From 2b88119f236def78369f8c6ab413b84365aec341 Mon Sep 17 00:00:00 2001 From: Packet Netarch Date: Wed, 20 Nov 2019 14:46:50 -0600 Subject: [PATCH 01/19] Updated to handle failure when unsupported method is called --- tests/models/test_models.py | 66 ++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/tests/models/test_models.py b/tests/models/test_models.py index e27d7f5..1dd72d0 100644 --- a/tests/models/test_models.py +++ b/tests/models/test_models.py @@ -2,6 +2,8 @@ import pathlib from typing import Any, Dict, List, Tuple +from yangson.exceptions import InstanceValueError + import ntc_rosetta import pytest @@ -30,6 +32,40 @@ ], "exclude": [], }, + "openconfig-system": { + "include": [ + "/openconfig-system:system/clock/config", + "/openconfig-system:system/clock", + "/openconfig-system:system/dns/config", + "/openconfig-system:system/dns/servers/server/config", + "/openconfig-system:system/dns/servers/server", + "/openconfig-system:system/dns/servers", + "/openconfig-system:system/dns", + "/openconfig-system:system/ntp/config", + "/openconfig-system:system/ntp/servers/server/config", + "/openconfig-system:system/ntp/servers/server", + "/openconfig-system:system/ntp/servers", + "/openconfig-system:system/ntp", + "/openconfig-system:system/ssh-server/config", + "/openconfig-system:system/ssh-server", + "/openconfig-system:system/telnet-server/config", + "/openconfig-system:system/telnet-server", + "/openconfig-system:system/aaa/authentication/users/user/config", + "/openconfig-system:system/aaa/authentication/users/user", + "/openconfig-system:system/aaa/authentication/users", + "/openconfig-system:system/aaa/authentication", + "/openconfig-system:system/aaa/server-groups/server-group/servers/server/tacacs/config", + "/openconfig-system:system/aaa/server-groups/server-group/servers/server/tacacs", + "/openconfig-system:system/aaa/server-groups/server-group/servers/server", + "/openconfig-system:system/aaa/server-groups/server-group/servers", + "/openconfig-system:system/aaa/server-groups/server-group", + "/openconfig-system:system/aaa/server-groups", + "/openconfig-system:system/aaa", + "/openconfig-system:system/config", + "/openconfig-system:system", + ], + "exclude": [], + }, } @@ -84,12 +120,19 @@ def _test_translate( ) -> None: with open(test_case_path.joinpath("data.json"), "r") as f: candidate = json.load(f) - with open(test_case_path.joinpath(f"res_{mode}"), "r") as f: - expected = f.read() + try: + with open(test_case_path.joinpath(f"res_{mode}"), "r") as f: + expected = f.read() + except FileNotFoundError: + expected = None # type: ignore driver_class = ntc_rosetta.get_driver(driver, org) device = driver_class() - config = device.translate(candidate, replace=mode == "replace") - assert config == expected + if not expected: + with pytest.raises(InstanceValueError): + config = device.translate(candidate, replace=mode == "replace") + else: + config = device.translate(candidate, replace=mode == "replace") + assert config == expected @pytest.mark.parametrize(**get_test_cases("translate")) # type: ignore def test_translate_merge( @@ -110,12 +153,19 @@ def _test_merge( candidate = json.load(f) with open(test_case_path.joinpath("data_running.json"), "r") as f: running = json.load(f) - with open(test_case_path.joinpath(f"res_{mode}"), "r") as f: - expected = f.read() + try: + with open(test_case_path.joinpath(f"res_{mode}"), "r") as f: + expected = f.read() + except FileNotFoundError: + expected = None # type: ignore driver_class = ntc_rosetta.get_driver(driver, org) device = driver_class() - res_merge = device.merge(candidate, running, replace=mode == "replace") - assert res_merge == expected + if not expected: + with pytest.raises(InstanceValueError): + res_merge = device.merge(candidate, running, replace=mode == "replace") + else: + res_merge = device.merge(candidate, running, replace=mode == "replace") + assert res_merge == expected @pytest.mark.parametrize(**get_test_cases("merge")) # type: ignore def test_merge_merge( From 4de96dae7969aad56d7be969231e463e49e99b93 Mon Sep 17 00:00:00 2001 From: Packet Netarch Date: Wed, 20 Nov 2019 14:47:03 -0600 Subject: [PATCH 02/19] Update docs --- docs/tutorials/data/ios/config.txt | 15 ++ docs/tutorials/data/ios/new_config.txt | 15 ++ docs/tutorials/ios_merging.ipynb | 2 +- docs/tutorials/ios_navigating_data.ipynb | 255 +++++++++++++++++++---- docs/tutorials/ios_parsing.ipynb | 208 +++++++++++++----- docs/tutorials/ios_smart_diff.ipynb | 24 ++- docs/tutorials/ios_translate.ipynb | 2 +- 7 files changed, 421 insertions(+), 100 deletions(-) diff --git a/docs/tutorials/data/ios/config.txt b/docs/tutorials/data/ios/config.txt index 7828d74..6c004de 100644 --- a/docs/tutorials/data/ios/config.txt +++ b/docs/tutorials/data/ios/config.txt @@ -1,3 +1,18 @@ +hostname csr1000v-1 +! +ip name-server 8.8.8.8 +! +ntp server 2610:20:6F96:96::6 +ntp server 129.6.15.29 +! +ip ssh version 2 +ip ssh time-out 60 +! +username developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2 +username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls +username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI +username newuser80 password 0 Yu76_87AF +! interface FastEthernet1 description This is Fa1 shutdown diff --git a/docs/tutorials/data/ios/new_config.txt b/docs/tutorials/data/ios/new_config.txt index d02a209..43b0a97 100644 --- a/docs/tutorials/data/ios/new_config.txt +++ b/docs/tutorials/data/ios/new_config.txt @@ -1,3 +1,18 @@ +hostname csr1000v-1 +! +ip name-server 8.8.8.8 +! +ntp server 2610:20:6F96:96::6 +ntp server 129.6.15.29 +! +ip ssh version 2 +ip ssh time-out 60 +! +username developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2 +username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls +username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI +username newuser80 password 0 Yu76_87AF +! interface FastEthernet1 description This is Fa1 shutdown diff --git a/docs/tutorials/ios_merging.ipynb b/docs/tutorials/ios_merging.ipynb index e43c474..1c5da75 100644 --- a/docs/tutorials/ios_merging.ipynb +++ b/docs/tutorials/ios_merging.ipynb @@ -290,7 +290,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/ios_navigating_data.ipynb b/docs/tutorials/ios_navigating_data.ipynb index bf6f9cd..9a6fcaf 100644 --- a/docs/tutorials/ios_navigating_data.ipynb +++ b/docs/tutorials/ios_navigating_data.ipynb @@ -13,10 +13,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ + "import json\n", "from ntc_rosetta import get_driver\n", "\n", "ios = get_driver(\"ios\", \"openconfig\")\n", @@ -39,54 +40,216 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, + "execution_count": 5, + "metadata": { + "scrolled": false + }, "outputs": [ { - "data": { - "text/plain": [ - "{'openconfig-interfaces:interfaces': {'interface': [{'name': 'FastEthernet1',\n", - " 'config': {'name': 'FastEthernet1',\n", - " 'type': 'iana-if-type:ethernetCsmacd',\n", - " 'description': 'This is Fa1',\n", - " 'enabled': False},\n", - " 'subinterfaces': {'subinterface': [{'index': 1,\n", - " 'config': {'index': 1, 'description': 'This is Fa1.1'}},\n", - " {'index': 2, 'config': {'index': 2, 'description': 'This is Fa1.2'}}]}},\n", - " {'name': 'FastEthernet3',\n", - " 'config': {'name': 'FastEthernet3',\n", - " 'type': 'iana-if-type:ethernetCsmacd',\n", - " 'description': 'This is Fa3',\n", - " 'enabled': True},\n", - " 'openconfig-if-ethernet:ethernet': {'openconfig-vlan:switched-vlan': {'config': {'interface-mode': 'ACCESS',\n", - " 'access-vlan': 10}}}},\n", - " {'name': 'FastEthernet4',\n", - " 'config': {'name': 'FastEthernet4',\n", - " 'type': 'iana-if-type:ethernetCsmacd',\n", - " 'enabled': False},\n", - " 'openconfig-if-ethernet:ethernet': {'openconfig-vlan:switched-vlan': {'config': {'interface-mode': 'TRUNK',\n", - " 'trunk-vlans': [10, 20]}}}}]},\n", - " 'openconfig-network-instance:network-instances': {'network-instance': [{'name': 'default',\n", - " 'config': {'name': 'default'},\n", - " 'vlans': {'vlan': [{'vlan-id': 10,\n", - " 'config': {'vlan-id': 10, 'name': 'prod', 'status': 'ACTIVE'}},\n", - " {'vlan-id': 20,\n", - " 'config': {'vlan-id': 20, 'name': 'dev', 'status': 'SUSPENDED'}}]}}]}}" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"openconfig-interfaces:interfaces\": {\n", + " \"interface\": [\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa1\",\n", + " \"enabled\": false,\n", + " \"name\": \"FastEthernet1\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", + " },\n", + " \"name\": \"FastEthernet1\",\n", + " \"subinterfaces\": {\n", + " \"subinterface\": [\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa1.1\",\n", + " \"index\": 1\n", + " },\n", + " \"index\": 1\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa1.2\",\n", + " \"index\": 2\n", + " },\n", + " \"index\": 2\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa3\",\n", + " \"enabled\": true,\n", + " \"name\": \"FastEthernet3\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", + " },\n", + " \"name\": \"FastEthernet3\",\n", + " \"openconfig-if-ethernet:ethernet\": {\n", + " \"openconfig-vlan:switched-vlan\": {\n", + " \"config\": {\n", + " \"access-vlan\": 10,\n", + " \"interface-mode\": \"ACCESS\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"enabled\": false,\n", + " \"name\": \"FastEthernet4\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", + " },\n", + " \"name\": \"FastEthernet4\",\n", + " \"openconfig-if-ethernet:ethernet\": {\n", + " \"openconfig-vlan:switched-vlan\": {\n", + " \"config\": {\n", + " \"interface-mode\": \"TRUNK\",\n", + " \"trunk-vlans\": [\n", + " 10,\n", + " 20\n", + " ]\n", + " }\n", + " }\n", + " }\n", + " }\n", + " ]\n", + " },\n", + " \"openconfig-network-instance:network-instances\": {\n", + " \"network-instance\": [\n", + " {\n", + " \"config\": {\n", + " \"name\": \"default\"\n", + " },\n", + " \"name\": \"default\",\n", + " \"vlans\": {\n", + " \"vlan\": [\n", + " {\n", + " \"config\": {\n", + " \"name\": \"prod\",\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"name\": \"dev\",\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", + " }\n", + " ]\n", + " }\n", + " }\n", + " ]\n", + " },\n", + " \"openconfig-system:system\": {\n", + " \"aaa\": {\n", + " \"authentication\": {\n", + " \"users\": {\n", + " \"user\": [\n", + " {\n", + " \"config\": {\n", + " \"password-hashed\": \"$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2\",\n", + " \"role\": \"15\",\n", + " \"username\": \"developer\"\n", + " },\n", + " \"username\": \"developer\"\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"password-hashed\": \"$9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls\",\n", + " \"role\": \"15\",\n", + " \"username\": \"cisco\"\n", + " },\n", + " \"username\": \"cisco\"\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"password-hashed\": \"$9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI\",\n", + " \"role\": \"15\",\n", + " \"username\": \"root\"\n", + " },\n", + " \"username\": \"root\"\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"password\": \"Yu76_87AF\",\n", + " \"username\": \"newuser80\"\n", + " },\n", + " \"username\": \"newuser80\"\n", + " }\n", + " ]\n", + " }\n", + " }\n", + " },\n", + " \"config\": {\n", + " \"hostname\": \"csr1000v-1\"\n", + " },\n", + " \"dns\": {\n", + " \"servers\": {\n", + " \"server\": [\n", + " {\n", + " \"address\": \"8.8.8.8\",\n", + " \"config\": {\n", + " \"address\": \"8.8.8.8\",\n", + " \"port\": 53\n", + " }\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " \"ntp\": {\n", + " \"config\": {\n", + " \"enable-ntp-auth\": false,\n", + " \"enabled\": true\n", + " },\n", + " \"servers\": {\n", + " \"server\": [\n", + " {\n", + " \"address\": \"2610:20:6F96:96::6\",\n", + " \"config\": {\n", + " \"address\": \"2610:20:6F96:96::6\"\n", + " }\n", + " },\n", + " {\n", + " \"address\": \"129.6.15.29\",\n", + " \"config\": {\n", + " \"address\": \"129.6.15.29\"\n", + " }\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " \"ssh-server\": {\n", + " \"config\": {\n", + " \"enable\": true,\n", + " \"protocol-version\": \"V2\",\n", + " \"timeout\": 60\n", + " }\n", + " },\n", + " \"telnet-server\": {\n", + " \"config\": {\n", + " \"enable\": true\n", + " }\n", + " }\n", + " }\n", + "}\n" + ] } ], "source": [ "raw = parsed.raw_value()\n", - "raw" + "print(json.dumps(raw, sort_keys=True, indent=2))" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -112,7 +275,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -121,7 +284,7 @@ "'This is Fa1'" ] }, - "execution_count": 4, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -132,7 +295,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -141,7 +304,7 @@ "'This is Fa1.1'" ] }, - "execution_count": 5, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -152,7 +315,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -161,7 +324,7 @@ "{'config': {'interface-mode': 'ACCESS', 'access-vlan': 10}}" ] }, - "execution_count": 6, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -187,7 +350,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/ios_parsing.ipynb b/docs/tutorials/ios_parsing.ipynb index 581db58..3b6588b 100644 --- a/docs/tutorials/ios_parsing.ipynb +++ b/docs/tutorials/ios_parsing.ipynb @@ -51,6 +51,21 @@ "name": "stdout", "output_type": "stream", "text": [ + "hostname csr1000v-1\n", + "!\n", + "ip name-server 8.8.8.8\n", + "!\n", + "ntp server 2610:20:6F96:96::6\n", + "ntp server 129.6.15.29\n", + "!\n", + "ip ssh version 2\n", + "ip ssh time-out 60\n", + "!\n", + "username developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2\n", + "username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls\n", + "username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI\n", + "username newuser80 password 0 Yu76_87AF\n", + "!\n", "interface FastEthernet1\n", " description This is Fa1\n", " shutdown\n", @@ -131,56 +146,56 @@ " \"openconfig-interfaces:interfaces\": {\n", " \"interface\": [\n", " {\n", - " \"name\": \"FastEthernet1\",\n", " \"config\": {\n", - " \"name\": \"FastEthernet1\",\n", - " \"type\": \"iana-if-type:ethernetCsmacd\",\n", " \"description\": \"This is Fa1\",\n", - " \"enabled\": false\n", + " \"enabled\": false,\n", + " \"name\": \"FastEthernet1\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", " },\n", + " \"name\": \"FastEthernet1\",\n", " \"subinterfaces\": {\n", " \"subinterface\": [\n", " {\n", - " \"index\": 1,\n", " \"config\": {\n", - " \"index\": 1,\n", - " \"description\": \"This is Fa1.1\"\n", - " }\n", + " \"description\": \"This is Fa1.1\",\n", + " \"index\": 1\n", + " },\n", + " \"index\": 1\n", " },\n", " {\n", - " \"index\": 2,\n", " \"config\": {\n", - " \"index\": 2,\n", - " \"description\": \"This is Fa1.2\"\n", - " }\n", + " \"description\": \"This is Fa1.2\",\n", + " \"index\": 2\n", + " },\n", + " \"index\": 2\n", " }\n", " ]\n", " }\n", " },\n", " {\n", - " \"name\": \"FastEthernet3\",\n", " \"config\": {\n", - " \"name\": \"FastEthernet3\",\n", - " \"type\": \"iana-if-type:ethernetCsmacd\",\n", " \"description\": \"This is Fa3\",\n", - " \"enabled\": true\n", + " \"enabled\": true,\n", + " \"name\": \"FastEthernet3\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", " },\n", + " \"name\": \"FastEthernet3\",\n", " \"openconfig-if-ethernet:ethernet\": {\n", " \"openconfig-vlan:switched-vlan\": {\n", " \"config\": {\n", - " \"interface-mode\": \"ACCESS\",\n", - " \"access-vlan\": 10\n", + " \"access-vlan\": 10,\n", + " \"interface-mode\": \"ACCESS\"\n", " }\n", " }\n", " }\n", " },\n", " {\n", - " \"name\": \"FastEthernet4\",\n", " \"config\": {\n", + " \"enabled\": false,\n", " \"name\": \"FastEthernet4\",\n", - " \"type\": \"iana-if-type:ethernetCsmacd\",\n", - " \"enabled\": false\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", " },\n", + " \"name\": \"FastEthernet4\",\n", " \"openconfig-if-ethernet:ethernet\": {\n", " \"openconfig-vlan:switched-vlan\": {\n", " \"config\": {\n", @@ -198,32 +213,123 @@ " \"openconfig-network-instance:network-instances\": {\n", " \"network-instance\": [\n", " {\n", - " \"name\": \"default\",\n", " \"config\": {\n", " \"name\": \"default\"\n", " },\n", + " \"name\": \"default\",\n", " \"vlans\": {\n", " \"vlan\": [\n", " {\n", - " \"vlan-id\": 10,\n", " \"config\": {\n", - " \"vlan-id\": 10,\n", " \"name\": \"prod\",\n", - " \"status\": \"ACTIVE\"\n", - " }\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", " },\n", " {\n", - " \"vlan-id\": 20,\n", " \"config\": {\n", - " \"vlan-id\": 20,\n", " \"name\": \"dev\",\n", - " \"status\": \"SUSPENDED\"\n", - " }\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", " }\n", " ]\n", " }\n", " }\n", " ]\n", + " },\n", + " \"openconfig-system:system\": {\n", + " \"aaa\": {\n", + " \"authentication\": {\n", + " \"users\": {\n", + " \"user\": [\n", + " {\n", + " \"config\": {\n", + " \"password-hashed\": \"$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2\",\n", + " \"role\": \"15\",\n", + " \"username\": \"developer\"\n", + " },\n", + " \"username\": \"developer\"\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"password-hashed\": \"$9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls\",\n", + " \"role\": \"15\",\n", + " \"username\": \"cisco\"\n", + " },\n", + " \"username\": \"cisco\"\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"password-hashed\": \"$9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI\",\n", + " \"role\": \"15\",\n", + " \"username\": \"root\"\n", + " },\n", + " \"username\": \"root\"\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"password\": \"Yu76_87AF\",\n", + " \"username\": \"newuser80\"\n", + " },\n", + " \"username\": \"newuser80\"\n", + " }\n", + " ]\n", + " }\n", + " }\n", + " },\n", + " \"config\": {\n", + " \"hostname\": \"csr1000v-1\"\n", + " },\n", + " \"dns\": {\n", + " \"servers\": {\n", + " \"server\": [\n", + " {\n", + " \"address\": \"8.8.8.8\",\n", + " \"config\": {\n", + " \"address\": \"8.8.8.8\",\n", + " \"port\": 53\n", + " }\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " \"ntp\": {\n", + " \"config\": {\n", + " \"enable-ntp-auth\": false,\n", + " \"enabled\": true\n", + " },\n", + " \"servers\": {\n", + " \"server\": [\n", + " {\n", + " \"address\": \"2610:20:6F96:96::6\",\n", + " \"config\": {\n", + " \"address\": \"2610:20:6F96:96::6\"\n", + " }\n", + " },\n", + " {\n", + " \"address\": \"129.6.15.29\",\n", + " \"config\": {\n", + " \"address\": \"129.6.15.29\"\n", + " }\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " \"ssh-server\": {\n", + " \"config\": {\n", + " \"enable\": true,\n", + " \"protocol-version\": \"V2\",\n", + " \"timeout\": 60\n", + " }\n", + " },\n", + " \"telnet-server\": {\n", + " \"config\": {\n", + " \"enable\": true\n", + " }\n", + " }\n", " }\n", "}\n" ] @@ -231,7 +337,7 @@ ], "source": [ "import json\n", - "print(json.dumps(parsed.raw_value(), indent=4))" + "print(json.dumps(parsed.raw_value(), sort_keys=True, indent=4))" ] }, { @@ -295,20 +401,20 @@ " \"vlans\": {\n", " \"vlan\": [\n", " {\n", - " \"vlan-id\": 10,\n", " \"config\": {\n", - " \"vlan-id\": 10,\n", " \"name\": \"prod\",\n", - " \"status\": \"ACTIVE\"\n", - " }\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", " },\n", " {\n", - " \"vlan-id\": 20,\n", " \"config\": {\n", - " \"vlan-id\": 20,\n", " \"name\": \"dev\",\n", - " \"status\": \"SUSPENDED\"\n", - " }\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", " }\n", " ]\n", " }\n", @@ -327,7 +433,7 @@ " \"/openconfig-network-instance:network-instances/network-instance/vlans\", \n", " ]\n", ")\n", - "print(json.dumps(parsed_vlans.raw_value(), indent=4))" + "print(json.dumps(parsed_vlans.raw_value(), sort_keys=True, indent=4))" ] }, { @@ -350,27 +456,27 @@ " \"openconfig-network-instance:network-instances\": {\n", " \"network-instance\": [\n", " {\n", - " \"name\": \"default\",\n", " \"config\": {\n", " \"name\": \"default\"\n", " },\n", + " \"name\": \"default\",\n", " \"vlans\": {\n", " \"vlan\": [\n", " {\n", - " \"vlan-id\": 10,\n", " \"config\": {\n", - " \"vlan-id\": 10,\n", " \"name\": \"prod\",\n", - " \"status\": \"ACTIVE\"\n", - " }\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", " },\n", " {\n", - " \"vlan-id\": 20,\n", " \"config\": {\n", - " \"vlan-id\": 20,\n", " \"name\": \"dev\",\n", - " \"status\": \"SUSPENDED\"\n", - " }\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", " }\n", " ]\n", " }\n", @@ -390,7 +496,7 @@ " \"/openconfig-network-instance:network-instances/network-instance/vlans\", \n", " ]\n", ")\n", - "print(json.dumps(parsed_vlans.raw_value(), indent=4))" + "print(json.dumps(parsed_vlans.raw_value(), sort_keys=True, indent=4))" ] } ], @@ -410,7 +516,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/ios_smart_diff.ipynb b/docs/tutorials/ios_smart_diff.ipynb index e68bcaa..383108d 100644 --- a/docs/tutorials/ios_smart_diff.ipynb +++ b/docs/tutorials/ios_smart_diff.ipynb @@ -73,6 +73,21 @@ "name": "stdout", "output_type": "stream", "text": [ + "hostname csr1000v-1\t\t\t\t\t\thostname csr1000v-1\r\n", + "!\t\t\t\t\t\t\t\t!\r\n", + "ip name-server 8.8.8.8\t\t\t\t\t\tip name-server 8.8.8.8\r\n", + "!\t\t\t\t\t\t\t\t!\r\n", + "ntp server 2610:20:6F96:96::6\t\t\t\t\tntp server 2610:20:6F96:96::6\r\n", + "ntp server 129.6.15.29\t\t\t\t\t\tntp server 129.6.15.29\r\n", + "!\t\t\t\t\t\t\t\t!\r\n", + "ip ssh version 2\t\t\t\t\t\tip ssh version 2\r\n", + "ip ssh time-out 60\t\t\t\t\t\tip ssh time-out 60\r\n", + "!\t\t\t\t\t\t\t\t!\r\n", + "username developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..\tusername developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..\r\n", + "username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSk\tusername cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSk\r\n", + "username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEu\tusername root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEu\r\n", + "username newuser80 password 0 Yu76_87AF\t\t\t\tusername newuser80 password 0 Yu76_87AF\r\n", + "!\t\t\t\t\t\t\t\t!\r\n", "interface FastEthernet1\t\t\t\t\t\tinterface FastEthernet1\r\n", " description This is Fa1\t\t\t\t\t description This is Fa1\r\n", " shutdown\t\t\t\t\t\t\t shutdown\r\n", @@ -172,6 +187,13 @@ ")\n", "print(config)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -190,7 +212,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/ios_translate.ipynb b/docs/tutorials/ios_translate.ipynb index e37bec7..38ace48 100644 --- a/docs/tutorials/ios_translate.ipynb +++ b/docs/tutorials/ios_translate.ipynb @@ -230,7 +230,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.6.9" } }, "nbformat": 4, From 05731c7dcb3de8e0e943c2a485778ac68ea6fac0 Mon Sep 17 00:00:00 2001 From: Packet Netarch Date: Wed, 20 Nov 2019 14:47:16 -0600 Subject: [PATCH 03/19] Implement openconfig-system for IOS --- .../parsers/openconfig/ios/__init__.py | 2 + .../ios/openconfig_system/__init__.py | 0 .../ios/openconfig_system/system.py | 309 ++++++++++++++++++ .../translators/openconfig/ios/__init__.py | 2 + .../ios/openconfig_system/__init__.py | 0 .../ios/openconfig_system/system.py | 228 +++++++++++++ ntc_rosetta/yang/__init__.py | 20 +- ntc_rosetta/yang/openconfig.json | 70 ++++ .../ios/add_dns_server/data_candidate.json | 24 ++ .../ios/add_dns_server/data_running.json | 17 + .../merge/ios/add_dns_server/res_merge | 1 + .../ios/add_ntp_server/data_candidate.json | 26 ++ .../ios/add_ntp_server/data_running.json | 20 ++ .../merge/ios/add_ntp_server/res_merge | 1 + .../ios/add_ssh_user/data_candidate.json | 43 +++ .../merge/ios/add_ssh_user/data_running.json | 21 ++ .../merge/ios/add_ssh_user/res_merge | 3 + .../ios/remove_dns_server/data_candidate.json | 17 + .../ios/remove_dns_server/data_running.json | 24 ++ .../merge/ios/remove_dns_server/res_merge | 1 + .../ios/remove_ntp_server/data_candidate.json | 21 ++ .../ios/remove_ntp_server/data_running.json | 27 ++ .../merge/ios/remove_ntp_server/res_merge | 1 + .../ios/remove_ssh_user/data_candidate.json | 19 ++ .../ios/remove_ssh_user/data_running.json | 44 +++ .../merge/ios/remove_ssh_user/res_merge | 3 + .../parse/ios/config/dev_conf | 70 ++++ .../parse/ios/config/result.json | 98 ++++++ .../translate/ios/test_case_1/data.json | 98 ++++++ .../translate/ios/test_case_1/res_merge | 10 + 30 files changed, 1204 insertions(+), 16 deletions(-) create mode 100644 ntc_rosetta/parsers/openconfig/ios/openconfig_system/__init__.py create mode 100644 ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py create mode 100644 ntc_rosetta/translators/openconfig/ios/openconfig_system/__init__.py create mode 100644 ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_running.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/res_merge create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_running.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/res_merge create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_running.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/res_merge create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_running.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/res_merge create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_running.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/res_merge create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_running.json create mode 100644 tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/res_merge create mode 100644 tests/models/openconfig/data/openconfig-system/parse/ios/config/dev_conf create mode 100644 tests/models/openconfig/data/openconfig-system/parse/ios/config/result.json create mode 100644 tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/data.json create mode 100644 tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/res_merge diff --git a/ntc_rosetta/parsers/openconfig/ios/__init__.py b/ntc_rosetta/parsers/openconfig/ios/__init__.py index 9adaa1e..b2f076b 100644 --- a/ntc_rosetta/parsers/openconfig/ios/__init__.py +++ b/ntc_rosetta/parsers/openconfig/ios/__init__.py @@ -4,6 +4,7 @@ from ntc_rosetta.parsers.openconfig.ios.openconfig_network_instance.network_instances import ( NetworkInstances, ) +from ntc_rosetta.parsers.openconfig.ios.openconfig_system.system import System from yangify import parser from yangify.parser.text_tree import parse_indented_config @@ -24,3 +25,4 @@ def init(self) -> None: interfaces = Interfaces network_instances = NetworkInstances + system = System diff --git a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/__init__.py b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py new file mode 100644 index 0000000..37b558f --- /dev/null +++ b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py @@ -0,0 +1,309 @@ +from typing import Any, Dict, Iterator, Tuple + +from ntc_rosetta.helpers import json_helpers as jh + +from yangify.parser import Parser, ParserData + + +import re + + +class ClockConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/clock/config" + + def timezone_name(self) -> str: + v = jh.query('clock.timezone."#text"', self.yy.native) + if v is not None: + return str(v) + return None + + +class Clock(Parser): + config = ClockConfig + + class Yangify(ParserData): + path = "/openconfig-system:system/clock" + + +class DnsConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/dns/config" + + def search(self) -> str: + return None + + +class DnsServerConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/dns/servers/server/config" + + def address(self) -> str: + return str(self.yy.key) + + def port(self) -> int: + return 53 + + +class DnsServer(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/dns/servers/server" + + def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: + dns = jh.query('ip."name-server"', self.native) or {} + for k, v in dns.items(): + if k == "#text": + continue + yield k, v + + def address(self) -> str: + return str(self.yy.key) + + config = DnsServerConfig + + +class DnsServers(Parser): + server = DnsServer + + class Yangify(ParserData): + path = "/openconfig-system:system/dns/servers" + + +class Dns(Parser): + config = DnsConfig + servers = DnsServers + + class Yangify(ParserData): + path = "/openconfig-system:system/dns" + + +class NtpConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/ntp/config" + + def enabled(self) -> bool: + if jh.query("ntp", self.yy.native): + return True + return False + + def ntp_source_address(self) -> str: + return None + + def enable_ntp_auth(self) -> bool: + if jh.query("ntp.authenticate", self.yy.native): + return True + return False + + +class NtpServerConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/ntp/servers/server/config" + + def address(self) -> str: + return str(self.yy.key) + + +class NtpServer(Parser): + config = NtpServerConfig + + class Yangify(ParserData): + path = "/openconfig-system:system/ntp/servers/server" + + def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: + ntp_servers = jh.query("ntp.server", self.native) or {} + for k, v in ntp_servers.items(): + if k == "#text": + continue + yield k, v + + def address(self) -> str: + return str(self.yy.key) + + +class NtpServers(Parser): + server = NtpServer + + class Yangify(ParserData): + path = "/openconfig-system:system/ntp/servers" + + +class Ntp(Parser): + config = NtpConfig + servers = NtpServers + + class Yangify(ParserData): + path = "/openconfig-system:system/ntp" + + +class SshServerConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/ssh-server/config" + + def enable(self) -> bool: + # NOTE: This is always true + return True + + def protocol_version(self) -> str: + version = jh.query('ip.ssh.version."#text"', self.yy.native) + + if version and version == "2": + return "V2" + elif version and version == "1": + return "V1" + return "V1_V2" + + def timeout(self) -> int: + timeout = jh.query('ip.ssh."time-out"."#text"', self.yy.native) + if timeout: + return timeout + return None + + +class SshServer(Parser): + config = SshServerConfig + + class Yangify(ParserData): + path = "/openconfig-system:system/ssh-server" + + +class TelnetServerConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/telnet-server/config" + + def enable(self) -> bool: + # TODO: I believe this is always enabled, but need a way to check if it + # is listening on the tty ports + return True + + def timeout(self) -> int: + return None + + +class TelnetServer(Parser): + config = TelnetServerConfig + + class Yangify(ParserData): + path = "/openconfig-system:system/telnet-server" + + +class AaaAuthenticationUserConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/aaa/authentication/users/user/config" + + def username(self) -> str: + return str(self.yy.key) + + def role(self) -> str: + text = jh.query('"#text"', self.yy.native) + + role_RE = r"privilege\s(\d+)(?:\ssecret|\spassword)" + result = re.match(role_RE, text) + + if result: + return result[1] + return None + + def password(self) -> str: + text = jh.query('"#text"', self.yy.native) + password_RE = r".*password ((\d) )?(.*)" + result = re.match(password_RE, text) + + if result: + _, _, password = result.groups() + return password + return None + + def password_hashed(self) -> str: + text = jh.query('"#text"', self.yy.native) + password_RE = r".*secret ((\d) )?(.*)" + result = re.match(password_RE, text) + + if result: + _, _, secret = result.groups() + return secret + return None + + def ssh_key(self) -> str: + return None + + +class AaaAuthenticationUser(Parser): + config = AaaAuthenticationUserConfig + + class Yangify(ParserData): + path = "/openconfig-system:system/aaa/authentication/users/user" + + def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: + users = jh.query("username", self.native) or {} + for k, v in users.items(): + if k == "#text": + continue + yield k, v + + def username(self) -> str: + return str(self.yy.key) + + +class AaaAuthenticationUsers(Parser): + user = AaaAuthenticationUser + + class Yangify(ParserData): + path = "/openconfig-system:system/aaa/authentication/users" + + +class AaaAuthentication(Parser): + users = AaaAuthenticationUsers + + class Yangify(ParserData): + path = "/openconfig-system:system/aaa/authentication" + + +class Aaa(Parser): + authentication = AaaAuthentication + + class Yangify(ParserData): + path = "/openconfig-system:system/aaa" + + +class SystemConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/config" + + def hostname(self) -> str: + v = jh.query('hostname."#text"', self.yy.native) + if v is not None: + return str(v) + else: + return None + + def domain_name(self) -> str: + v = jh.query('ip."domain-name"."#text"', self.yy.native) + if v is not None: + return str(v) + else: + return None + + def login_banner(self) -> str: + # TODO + return None + + def motd_banner(self) -> str: + # TODO + return None + + +class System(Parser): + config = SystemConfig + clock = Clock + dns = Dns + ntp = Ntp + ssh_server = SshServer + telnet_server = TelnetServer + aaa = Aaa + + class Yangify(ParserData): + path = "/openconfig-system:system" + metadata = {"key": "dev_conf", "command": "show running-config all"} + + def pre_process(self) -> None: + self.native: Dict[str, Any] = self.root_native["dev_conf"] diff --git a/ntc_rosetta/translators/openconfig/ios/__init__.py b/ntc_rosetta/translators/openconfig/ios/__init__.py index e56d3e9..bd6b3b5 100644 --- a/ntc_rosetta/translators/openconfig/ios/__init__.py +++ b/ntc_rosetta/translators/openconfig/ios/__init__.py @@ -4,6 +4,7 @@ from ntc_rosetta.translators.openconfig.ios.openconfig_network_instance.network_instances import ( NetworkInstances, ) +from ntc_rosetta.translators.openconfig.ios.openconfig_system.system import System from yangify import translator from yangify.translator.config_tree import ConfigTree @@ -20,3 +21,4 @@ def post(self) -> None: interfaces = Interfaces network_instances = NetworkInstances + system = System diff --git a/ntc_rosetta/translators/openconfig/ios/openconfig_system/__init__.py b/ntc_rosetta/translators/openconfig/ios/openconfig_system/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py new file mode 100644 index 0000000..d17938a --- /dev/null +++ b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py @@ -0,0 +1,228 @@ +import re + +from yangify.translator import Translator, TranslatorData + +from yangson.exceptions import InstanceValueError + + +class DnsConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/dns/config" + + +class DnsServerConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/dns/servers/server/config" + + +class DnsServer(Translator): + config = DnsServerConfig + + class Yangify(TranslatorData): + path = "/openconfig-system:system/dns/servers/server" + + def pre_process_list(self) -> None: + if self.to_remove and not self.replace: + for element in self.to_remove: + self.result.add_command(f"no ip name-server {element['address']}") + + def address(self, value: str): + self.yy.result.add_command(f"ip name-server {value}") + + +class DnsServers(Translator): + server = DnsServer + + class Yangify(TranslatorData): + path = "/openconfig-system:system/dns/servers" + + def pre_process(self) -> None: + pass + + +class Dns(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/dns" + + config = DnsConfig + servers = DnsServers + + +class NtpServerConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp/servers/server/config" + + +class NtpServer(Translator): + config = NtpServerConfig + + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp/servers/server" + + def pre_process_list(self) -> None: + if self.to_remove: + for element in self.to_remove: + self.result.add_command(f"no ntp server {element['address']}") + + def address(self, value: str): + self.yy.result.add_command(f"ntp server {value}") + + +class NtpServers(Translator): + server = NtpServer + + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp/servers" + + +class NtpConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp/config" + + +class Ntp(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp" + + config = NtpConfig + servers = NtpServers + + +class SshServerConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ssh-server/config" + + def protocol_version(self, value: str) -> None: + if value == "V2": + self.yy.result.add_command(f"ip ssh version 2") + elif value == "V1": + self.yy.result.add_command(f"ip ssh version 1") + else: + self.yy.result.add_command(f"default ip ssh version") + + def timeout(self, value: int) -> None: + if value: + self.yy.result.add_command(f"ip ssh time-out {value}") + + +class SshServer(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ssh-server" + + config = SshServerConfig + + +class AaaAuthenticationUserConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/aaa/authentication/users/user/config" + + def pre_process(self) -> None: + self.extra = { + "username": "", + "password": "", + "password_hash": "", + "role": "", + "ssh_key": "", + } + + def post_process(self) -> None: + if not self.extra.get("username"): + return + role = self.extra.get("role") + role_str = f" privilege {role}" if role else "" + password = self.extra.get("password") + password_str = f"password 0 {password}" if password else "" + password_hash = self.extra.get("password_hash") + match = re.match(r"^\$(\d).*", password_hash) + password_hash_str = "" + if match: + hash_type = match.groups()[0] + password_hash_str = f"secret {hash_type} {password_hash}" + ssh_key = self.extra.get("ssh_key") + ssh_key_str = f"ssh_key {ssh_key}" if ssh_key else "" # noqa + self.result.add_command( + f"username {self.extra['username']}{role_str} {password_str or password_hash_str}" + ) + if ssh_key: + """ + ip ssh pubkey-chain + username test + key-string + some-rsa-ssh-key-string + exit + exit + """ + raise NotImplementedError + + def username(self, value: str) -> None: + self.yy.extra["username"] = value + + def role(self, value: str) -> None: + self.yy.extra["role"] = value + + def password(self, value: str) -> None: + self.yy.extra["password"] = value + + def password_hashed(self, value: str) -> None: + self.yy.extra["password_hash"] = value + + def ssh_key(self, value: str) -> None: + self.yy.extra["ssh_key"] = value + + +class AaaAuthenticationUser(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/aaa/authentication/users/user" + + def pre_process_list(self) -> None: + if self.to_remove: + for element in self.to_remove: + self.result.add_command(f"no username {element['username']}") + + config = AaaAuthenticationUserConfig + + +class AaaAuthenticationUsers(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/aaa/authentication/users" + + user = AaaAuthenticationUser + + +class AaaAuthentication(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/aaa/authentication" + + users = AaaAuthenticationUsers + + +class Aaa(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/aaa" + + authentication = AaaAuthentication + + +class SystemConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/config" + + def hostname(self, value: str) -> None: + self.yy.result.add_command(f"hostname {value}") + + +class System(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system" + + def pre_process(self): + if self.replace: + raise InstanceValueError( + self.path, "Replace is not a supported operation for this path" + ) + + config = SystemConfig + ssh_server = SshServer + aaa = Aaa + ntp = Ntp + dns = Dns diff --git a/ntc_rosetta/yang/__init__.py b/ntc_rosetta/yang/__init__.py index dab3f27..febd8b2 100644 --- a/ntc_rosetta/yang/__init__.py +++ b/ntc_rosetta/yang/__init__.py @@ -9,22 +9,10 @@ OPENCONFIG_PATH = [ BASEPATH.joinpath("YangModels/standard/ietf/RFC"), BASEPATH.joinpath("openconfig/release/models"), - BASEPATH.joinpath("openconfig/release/models/acl"), - BASEPATH.joinpath("openconfig/release/models/aft"), - BASEPATH.joinpath("openconfig/release/models/bgp"), - BASEPATH.joinpath("openconfig/release/models/interfaces"), - BASEPATH.joinpath("openconfig/release/models/isis"), - BASEPATH.joinpath("openconfig/release/models/mpls"), - BASEPATH.joinpath("openconfig/release/models/multicast"), - BASEPATH.joinpath("openconfig/release/models/network-instance"), - BASEPATH.joinpath("openconfig/release/models/local-routing"), - BASEPATH.joinpath("openconfig/release/models/ospf"), - BASEPATH.joinpath("openconfig/release/models/policy"), - BASEPATH.joinpath("openconfig/release/models/policy-forwarding"), - BASEPATH.joinpath("openconfig/release/models/rib"), - BASEPATH.joinpath("openconfig/release/models/segment-routing"), - BASEPATH.joinpath("openconfig/release/models/types"), - BASEPATH.joinpath("openconfig/release/models/vlan"), +] + [ + fname + for fname in BASEPATH.joinpath("openconfig/release/models").iterdir() + if fname.is_dir() ] diff --git a/ntc_rosetta/yang/openconfig.json b/ntc_rosetta/yang/openconfig.json index 54be4ec..bb55f50 100644 --- a/ntc_rosetta/yang/openconfig.json +++ b/ntc_rosetta/yang/openconfig.json @@ -387,6 +387,76 @@ "name": "openconfig-acl", "revision": "2018-11-21", "conformance-type": "implement" + }, + { + "name": "openconfig-system", + "revision": "2019-01-29", + "conformance-type": "implement" + }, + { + "name": "openconfig-aaa", + "revision": "2018-11-21", + "conformance-type": "implement", + "submodule": [ + { + "name": "openconfig-aaa-radius", + "revision": "2018-11-21" + }, + { + "name": "openconfig-aaa-tacacs", + "revision": "2018-11-21" + } + ] + }, + { + "name": "openconfig-aaa-types", + "revision": "2018-11-21", + "conformance-type": "implement" + }, + { + "name": "openconfig-system-logging", + "revision": "2018-11-21", + "conformance-type": "implement" + }, + { + "name": "openconfig-system-management", + "revision": "2018-11-21", + "conformance-type": "implement" + }, + { + "name": "openconfig-system-terminal", + "revision": "2018-11-21", + "conformance-type": "implement" + }, + { + "name": "openconfig-procmon", + "revision": "2018-11-21", + "conformance-type": "implement" + }, + { + "name": "openconfig-alarms", + "revision": "2018-11-21", + "conformance-type": "implement" + }, + { + "name": "openconfig-alarm-types", + "revision": "2018-11-21", + "conformance-type": "implement" + }, + { + "name": "openconfig-messages", + "revision": "2018-08-13", + "conformance-type": "implement" + }, + { + "name": "openconfig-platform", + "revision": "2019-04-16", + "conformance-type": "implement" + }, + { + "name": "openconfig-platform-types", + "revision": "2018-11-21", + "conformance-type": "implement" } ] } diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_candidate.json b/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_candidate.json new file mode 100644 index 0000000..74f3b3a --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_candidate.json @@ -0,0 +1,24 @@ +{ + "openconfig-system:system": { + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + }, + { + "address": "8.8.4.4", + "config": { + "address": "8.8.4.4", + "port": 53 + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_running.json b/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_running.json new file mode 100644 index 0000000..fab71ea --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_running.json @@ -0,0 +1,17 @@ +{ + "openconfig-system:system": { + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/res_merge b/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/res_merge new file mode 100644 index 0000000..87a8f0a --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/res_merge @@ -0,0 +1 @@ +ip name-server 8.8.4.4 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_candidate.json b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_candidate.json new file mode 100644 index 0000000..a826dcf --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_candidate.json @@ -0,0 +1,26 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + }, + { + "address": "129.6.15.29", + "config": { + "address": "129.6.15.29" + } + } + ] + } + } + } +} diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_running.json b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_running.json new file mode 100644 index 0000000..f82e715 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_running.json @@ -0,0 +1,20 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + } + ] + } + } + } +} diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/res_merge b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/res_merge new file mode 100644 index 0000000..1f122d2 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/res_merge @@ -0,0 +1 @@ +ntp server 129.6.15.29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_candidate.json b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_candidate.json new file mode 100644 index 0000000..3970ea4 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_candidate.json @@ -0,0 +1,43 @@ +{ + "openconfig-system:system": { + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + "password-hashed": "$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2", + "role": "15" + } + }, + { + "username": "cisco", + "config": { + "username": "cisco", + "password-hashed": "$9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls", + "role": "15" + } + }, + { + "username": "root", + "config": { + "username": "root", + "password-hashed": "$9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI", + "role": "15" + } + }, + { + "username": "newuser80", + "config": { + "username": "newuser80", + "password": "Yu76_87AF" + } + } + ] + } + } + } + } + } diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_running.json b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_running.json new file mode 100644 index 0000000..2d68d20 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_running.json @@ -0,0 +1,21 @@ +{ + "openconfig-system:system": { + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + "password-hashed": "$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2", + "role": "15" + } + } + ] + } + } + } + } + } + diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/res_merge b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/res_merge new file mode 100644 index 0000000..5caf6dd --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/res_merge @@ -0,0 +1,3 @@ +username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls +username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI +username newuser80 password 0 Yu76_87AF diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_candidate.json b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_candidate.json new file mode 100644 index 0000000..fab71ea --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_candidate.json @@ -0,0 +1,17 @@ +{ + "openconfig-system:system": { + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_running.json b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_running.json new file mode 100644 index 0000000..0230940 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_running.json @@ -0,0 +1,24 @@ +{ + "openconfig-system:system": { + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + }, + { + "address": "8.8.4.4", + "config": { + "address": "8.8.4.4", + "port": 53 + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/res_merge b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/res_merge new file mode 100644 index 0000000..290b8f8 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/res_merge @@ -0,0 +1 @@ +no ip name-server 8.8.4.4 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_candidate.json b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_candidate.json new file mode 100644 index 0000000..1537c93 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_candidate.json @@ -0,0 +1,21 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + } + ] + } + } + } +} + diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_running.json b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_running.json new file mode 100644 index 0000000..7dd6bde --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_running.json @@ -0,0 +1,27 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + }, + { + "address": "129.6.15.29", + "config": { + "address": "129.6.15.29" + } + } + ] + } + } + } +} + diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/res_merge b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/res_merge new file mode 100644 index 0000000..6955822 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/res_merge @@ -0,0 +1 @@ +no ntp server 129.6.15.29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_candidate.json b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_candidate.json new file mode 100644 index 0000000..e4a84e8 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_candidate.json @@ -0,0 +1,19 @@ +{ + "openconfig-system:system": { + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "newuser80", + "config": { + "username": "newuser80", + "password": "Yu76_87AF" + } + } + ] + } + } + } + } + } diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_running.json b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_running.json new file mode 100644 index 0000000..3b82e3f --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_running.json @@ -0,0 +1,44 @@ +{ + "openconfig-system:system": { + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + "password-hashed": "$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2", + "role": "15" + } + }, + { + "username": "cisco", + "config": { + "username": "cisco", + "password-hashed": "$9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls", + "role": "15" + } + }, + { + "username": "root", + "config": { + "username": "root", + "password-hashed": "$9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI", + "role": "15" + } + }, + { + "username": "newuser80", + "config": { + "username": "newuser80", + "password": "Yu76_87AF" + } + } + ] + } + } + } + } + } + diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/res_merge b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/res_merge new file mode 100644 index 0000000..9a5d094 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/res_merge @@ -0,0 +1,3 @@ +no username developer +no username cisco +no username root diff --git a/tests/models/openconfig/data/openconfig-system/parse/ios/config/dev_conf b/tests/models/openconfig/data/openconfig-system/parse/ios/config/dev_conf new file mode 100644 index 0000000..82caac4 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/parse/ios/config/dev_conf @@ -0,0 +1,70 @@ +version 16.11 +! +hostname csr1000v-1 +! +ip name-server 8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844 +no ip domain lookup recursive +no ip domain lookup +ip domain lookup nsap +ip domain name cisco.com +ip domain multicast in-addr.arpa +ip domain recursive retry 10 +no ip domain recursive allow-soa +! +ip ssh time-out 60 +ip ssh authentication-retries 3 +ip ssh window-size 8192 +ip ssh rsa keypair-name ssh-key +ip ssh break-string ~break +ip ssh logging events +ip ssh version 2 +ip ssh dh min size 2048 +no ip ssh rekey time +no ip ssh rekey volume +ip ssh server authenticate user publickey +ip ssh server authenticate user keyboard +ip ssh server authenticate user password +no ip ssh server peruser session limit +! +ip ssh server algorithm mac hmac-sha2-256 hmac-sha2-512 hmac-sha1 hmac-sha1-96 +ip ssh server algorithm encryption aes128-ctr aes192-ctr aes256-ctr +ip ssh server algorithm kex diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 +ip ssh server algorithm hostkey x509v3-ssh-rsa ssh-rsa +ip ssh server algorithm authentication publickey keyboard password +ip ssh server algorithm publickey x509v3-ssh-rsa ssh-rsa +ip ssh client algorithm mac hmac-sha2-256 hmac-sha2-512 hmac-sha1 hmac-sha1-96 +ip ssh client algorithm encryption aes128-ctr aes192-ctr aes256-ctr +ip ssh client algorithm kex diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 +! +ntp max-associations 100 +! +ntp allow mode control 0 +ntp leap-handle +ntp mindistance 1 +ntp maxdistance 8 +ntp server 2610:20:6F96:96::6 maxpoll 10 minpoll 6 version 4 burst iburst +ntp peer 2610:20:6F15:15::27 maxpoll 10 minpoll 6 version 4 burst iburst +ntp peer 129.6.15.28 maxpoll 10 minpoll 6 version 4 burst iburst +ntp server 129.6.15.29 maxpoll 10 minpoll 6 version 4 burst iburst +! +clock timezone CST -6 0 +! +username developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2 +username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls +username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI +username newuser80 password 0 Yu76_87AF +! +tacacs-server host 192.0.0.43 +tacacs-server key 7 PLAINTEXTPASS +! +banner login ^ +Login banner +^ +! +banner motd ^ +Dummy MOTD +Have a nice day ;) +^ +! +interface GigabitEthernet1/1 + no shutdown \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-system/parse/ios/config/result.json b/tests/models/openconfig/data/openconfig-system/parse/ios/config/result.json new file mode 100644 index 0000000..f3086b4 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/parse/ios/config/result.json @@ -0,0 +1,98 @@ +{ + "openconfig-system:system": { + "config": { + "hostname": "csr1000v-1" + }, + "clock": { + "config": { + "timezone-name": "CST -6 0" + } + }, + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + } + ] + } + }, + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + }, + { + "address": "129.6.15.29", + "config": { + "address": "129.6.15.29" + } + } + ] + } + }, + "ssh-server": { + "config": { + "enable": true, + "protocol-version": "V2", + "timeout": 60 + } + }, + "telnet-server": { + "config": { + "enable": true + } + }, + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + "password-hashed": "$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2", + "role": "15" + } + }, + { + "username": "cisco", + "config": { + "username": "cisco", + "password-hashed": "$9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls", + "role": "15" + } + }, + { + "username": "root", + "config": { + "username": "root", + "password-hashed": "$9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI", + "role": "15" + } + }, + { + "username": "newuser80", + "config": { + "username": "newuser80", + "password": "Yu76_87AF" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/data.json b/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/data.json new file mode 100644 index 0000000..f302383 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/data.json @@ -0,0 +1,98 @@ +{ + "openconfig-system:system": { + "config": { + "hostname": "csr1000v-1" + }, + "clock": { + "config": { + "timezone-name": "CST -6 0" + } + }, + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + } + ] + } + }, + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + }, + { + "address": "129.6.15.29", + "config": { + "address": "129.6.15.29" + } + } + ] + } + }, + "ssh-server": { + "config": { + "enable": true, + "protocol-version": "V2", + "timeout": 60 + } + }, + "telnet-server": { + "config": { + "enable": true + } + }, + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + "password-hashed": "$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2", + "role": "15" + } + }, + { + "username": "cisco", + "config": { + "username": "cisco", + "password-hashed": "$9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls", + "role": "15" + } + }, + { + "username": "root", + "config": { + "username": "root", + "password-hashed": "$9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI", + "role": "15" + } + }, + { + "username": "newuser80", + "config": { + "username": "newuser80", + "password": "Yu76_87AF" + } + } + ] + } + } + } + } + } \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/res_merge b/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/res_merge new file mode 100644 index 0000000..c14097a --- /dev/null +++ b/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/res_merge @@ -0,0 +1,10 @@ +hostname csr1000v-1 +ip name-server 8.8.8.8 +ntp server 2610:20:6F96:96::6 +ntp server 129.6.15.29 +ip ssh version 2 +ip ssh time-out 60 +username developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2 +username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls +username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI +username newuser80 password 0 Yu76_87AF From 07e0023f0448f76b6737e34f68c909c1838ff756 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Tue, 18 Feb 2020 18:24:30 -0600 Subject: [PATCH 04/19] Refactor tests for openconfig-system/ios --- docs/tutorials/ios_navigating_data.ipynb | 174 +++++++++++++----- docs/tutorials/ios_parsing.ipynb | 127 +++++++------ docs/tutorials/ios_smart_diff.ipynb | 2 +- .../ios/openconfig_system/system.py | 21 ++- .../openconfig_system/merge/ios/__init__.py | 0 .../merge/ios/add_dns_server/__init__.py | 0 .../ios/add_dns_server/data_candidate.json | 0 .../ios/add_dns_server/data_running.json | 0 .../merge/ios/add_dns_server/res_merge | 0 .../merge/ios/add_dns_server/test_case.py | 10 + .../merge/ios/add_ntp_server/__init__.py | 0 .../ios/add_ntp_server/data_candidate.json | 0 .../ios/add_ntp_server/data_running.json | 0 .../merge/ios/add_ntp_server/res_merge | 0 .../merge/ios/add_ntp_server/test_case.py | 10 + .../merge/ios/add_ssh_user/__init__.py | 0 .../ios/add_ssh_user/data_candidate.json | 0 .../merge/ios/add_ssh_user/data_running.json | 0 .../merge/ios/add_ssh_user/res_merge | 0 .../merge/ios/add_ssh_user/test_case.py | 10 + .../merge/ios/remove_dns_server/__init__.py | 0 .../ios/remove_dns_server/data_candidate.json | 0 .../ios/remove_dns_server/data_running.json | 0 .../merge/ios/remove_dns_server/res_merge | 0 .../merge/ios/remove_dns_server/test_case.py | 10 + .../merge/ios/remove_ntp_server/__init__.py | 0 .../ios/remove_ntp_server/data_candidate.json | 0 .../ios/remove_ntp_server/data_running.json | 0 .../merge/ios/remove_ntp_server/res_merge | 0 .../merge/ios/remove_ntp_server/test_case.py | 10 + .../merge/ios/remove_ssh_user/__init__.py | 0 .../ios/remove_ssh_user/data_candidate.json | 0 .../ios/remove_ssh_user/data_running.json | 0 .../merge/ios/remove_ssh_user/res_merge | 0 .../merge/ios/remove_ssh_user/test_case.py | 10 + .../openconfig_system/parse/ios/__init__.py | 0 .../parse/ios/config/__init__.py | 0 .../parse/ios/config/dev_conf | 0 .../parse/ios/config/result.json | 0 .../parse/ios/config/test_case.py | 7 + .../data/openconfig_system/test_ios_system.py | 15 ++ .../translate/ios/__init__.py | 0 .../translate/ios/test_case_1/__init__.py | 0 .../translate/ios/test_case_1/data.json | 0 .../translate/ios/test_case_1/res_merge | 0 .../translate/ios/test_case_1/test_case.py | 10 + 46 files changed, 307 insertions(+), 109 deletions(-) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/__init__.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_dns_server/data_candidate.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_dns_server/data_running.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_dns_server/res_merge (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_ntp_server/data_candidate.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_ntp_server/data_running.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_ntp_server/res_merge (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_ssh_user/data_candidate.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_ssh_user/data_running.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/add_ssh_user/res_merge (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_dns_server/data_candidate.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_dns_server/data_running.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_dns_server/res_merge (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_ntp_server/data_candidate.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_ntp_server/data_running.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_ntp_server/res_merge (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_ssh_user/data_candidate.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_ssh_user/data_running.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/merge/ios/remove_ssh_user/res_merge (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/parse/ios/__init__.py create mode 100644 tests/models/openconfig/data/openconfig_system/parse/ios/config/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/parse/ios/config/dev_conf (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/parse/ios/config/result.json (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/parse/ios/config/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/test_ios_system.py create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/__init__.py create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/__init__.py rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/translate/ios/test_case_1/data.json (100%) rename tests/models/openconfig/data/{openconfig-system => openconfig_system}/translate/ios/test_case_1/res_merge (100%) create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py diff --git a/docs/tutorials/ios_navigating_data.ipynb b/docs/tutorials/ios_navigating_data.ipynb index c2e1a5a..80d5101 100644 --- a/docs/tutorials/ios_navigating_data.ipynb +++ b/docs/tutorials/ios_navigating_data.ipynb @@ -43,53 +43,139 @@ "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "{'openconfig-interfaces:interfaces': {'interface': [{'name': 'FastEthernet1',\n", - " 'config': {'name': 'FastEthernet1',\n", - " 'type': 'iana-if-type:ethernetCsmacd',\n", - " 'loopback-mode': False,\n", - " 'description': 'This is Fa1',\n", - " 'enabled': False},\n", - " 'subinterfaces': {'subinterface': [{'index': 1,\n", - " 'config': {'index': 1,\n", - " 'description': 'This is Fa1.1',\n", - " 'enabled': True}},\n", - " {'index': 2,\n", - " 'config': {'index': 2,\n", - " 'description': 'This is Fa1.2',\n", - " 'enabled': True}}]}},\n", - " {'name': 'FastEthernet3',\n", - " 'config': {'name': 'FastEthernet3',\n", - " 'type': 'iana-if-type:ethernetCsmacd',\n", - " 'loopback-mode': False,\n", - " 'description': 'This is Fa3',\n", - " 'enabled': True},\n", - " 'openconfig-if-ethernet:ethernet': {'openconfig-vlan:switched-vlan': {'config': {'interface-mode': 'ACCESS',\n", - " 'access-vlan': 10}}}},\n", - " {'name': 'FastEthernet4',\n", - " 'config': {'name': 'FastEthernet4',\n", - " 'type': 'iana-if-type:ethernetCsmacd',\n", - " 'loopback-mode': True,\n", - " 'enabled': False},\n", - " 'openconfig-if-ethernet:ethernet': {'openconfig-vlan:switched-vlan': {'config': {'interface-mode': 'TRUNK',\n", - " 'trunk-vlans': [10, 20]}}}}]},\n", - " 'openconfig-network-instance:network-instances': {'network-instance': [{'name': 'default',\n", - " 'config': {'name': 'default'},\n", - " 'vlans': {'vlan': [{'vlan-id': 10,\n", - " 'config': {'vlan-id': 10, 'name': 'prod', 'status': 'ACTIVE'}},\n", - " {'vlan-id': 20,\n", - " 'config': {'vlan-id': 20, 'name': 'dev', 'status': 'SUSPENDED'}}]}}]}}" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"openconfig-interfaces:interfaces\": {\n", + " \"interface\": [\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa1\",\n", + " \"enabled\": false,\n", + " \"loopback-mode\": false,\n", + " \"name\": \"FastEthernet1\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", + " },\n", + " \"name\": \"FastEthernet1\",\n", + " \"subinterfaces\": {\n", + " \"subinterface\": [\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa1.1\",\n", + " \"enabled\": true,\n", + " \"index\": 1\n", + " },\n", + " \"index\": 1\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa1.2\",\n", + " \"enabled\": true,\n", + " \"index\": 2\n", + " },\n", + " \"index\": 2\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"description\": \"This is Fa3\",\n", + " \"enabled\": true,\n", + " \"loopback-mode\": false,\n", + " \"name\": \"FastEthernet3\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", + " },\n", + " \"name\": \"FastEthernet3\",\n", + " \"openconfig-if-ethernet:ethernet\": {\n", + " \"openconfig-vlan:switched-vlan\": {\n", + " \"config\": {\n", + " \"access-vlan\": 10,\n", + " \"interface-mode\": \"ACCESS\"\n", + " }\n", + " }\n", + " }\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"enabled\": false,\n", + " \"loopback-mode\": true,\n", + " \"name\": \"FastEthernet4\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", + " },\n", + " \"name\": \"FastEthernet4\",\n", + " \"openconfig-if-ethernet:ethernet\": {\n", + " \"openconfig-vlan:switched-vlan\": {\n", + " \"config\": {\n", + " \"interface-mode\": \"TRUNK\",\n", + " \"trunk-vlans\": [\n", + " 10,\n", + " 20\n", + " ]\n", + " }\n", + " }\n", + " }\n", + " }\n", + " ]\n", + " },\n", + " \"openconfig-network-instance:network-instances\": {\n", + " \"network-instance\": [\n", + " {\n", + " \"config\": {\n", + " \"name\": \"default\"\n", + " },\n", + " \"name\": \"default\",\n", + " \"vlans\": {\n", + " \"vlan\": [\n", + " {\n", + " \"config\": {\n", + " \"name\": \"prod\",\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", + " },\n", + " {\n", + " \"config\": {\n", + " \"name\": \"dev\",\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", + " }\n", + " ]\n", + " }\n", + " }\n", + " ]\n", + " },\n", + " \"openconfig-system:system\": {\n", + " \"ntp\": {\n", + " \"config\": {\n", + " \"enable-ntp-auth\": false,\n", + " \"enabled\": false\n", + " }\n", + " },\n", + " \"ssh-server\": {\n", + " \"config\": {\n", + " \"enable\": true,\n", + " \"protocol-version\": \"V1_V2\"\n", + " }\n", + " },\n", + " \"telnet-server\": {\n", + " \"config\": {\n", + " \"enable\": true\n", + " }\n", + " }\n", + " }\n", + "}\n" + ] } ], "source": [ + "import json\n", "raw = parsed.raw_value()\n", - "raw" + "print(json.dumps(raw, sort_keys=True, indent=4))" ] }, { @@ -195,7 +281,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/ios_parsing.ipynb b/docs/tutorials/ios_parsing.ipynb index 17b076e..5c24c9e 100644 --- a/docs/tutorials/ios_parsing.ipynb +++ b/docs/tutorials/ios_parsing.ipynb @@ -132,61 +132,61 @@ " \"openconfig-interfaces:interfaces\": {\n", " \"interface\": [\n", " {\n", - " \"name\": \"FastEthernet1\",\n", " \"config\": {\n", - " \"name\": \"FastEthernet1\",\n", - " \"type\": \"iana-if-type:ethernetCsmacd\",\n", - " \"loopback-mode\": false,\n", " \"description\": \"This is Fa1\",\n", - " \"enabled\": false\n", + " \"enabled\": false,\n", + " \"loopback-mode\": false,\n", + " \"name\": \"FastEthernet1\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", " },\n", + " \"name\": \"FastEthernet1\",\n", " \"subinterfaces\": {\n", " \"subinterface\": [\n", " {\n", - " \"index\": 1,\n", " \"config\": {\n", - " \"index\": 1,\n", " \"description\": \"This is Fa1.1\",\n", - " \"enabled\": true\n", - " }\n", + " \"enabled\": true,\n", + " \"index\": 1\n", + " },\n", + " \"index\": 1\n", " },\n", " {\n", - " \"index\": 2,\n", " \"config\": {\n", - " \"index\": 2,\n", " \"description\": \"This is Fa1.2\",\n", - " \"enabled\": true\n", - " }\n", + " \"enabled\": true,\n", + " \"index\": 2\n", + " },\n", + " \"index\": 2\n", " }\n", " ]\n", " }\n", " },\n", " {\n", - " \"name\": \"FastEthernet3\",\n", " \"config\": {\n", - " \"name\": \"FastEthernet3\",\n", - " \"type\": \"iana-if-type:ethernetCsmacd\",\n", - " \"loopback-mode\": false,\n", " \"description\": \"This is Fa3\",\n", - " \"enabled\": true\n", + " \"enabled\": true,\n", + " \"loopback-mode\": false,\n", + " \"name\": \"FastEthernet3\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", " },\n", + " \"name\": \"FastEthernet3\",\n", " \"openconfig-if-ethernet:ethernet\": {\n", " \"openconfig-vlan:switched-vlan\": {\n", " \"config\": {\n", - " \"interface-mode\": \"ACCESS\",\n", - " \"access-vlan\": 10\n", + " \"access-vlan\": 10,\n", + " \"interface-mode\": \"ACCESS\"\n", " }\n", " }\n", " }\n", " },\n", " {\n", - " \"name\": \"FastEthernet4\",\n", " \"config\": {\n", - " \"name\": \"FastEthernet4\",\n", - " \"type\": \"iana-if-type:ethernetCsmacd\",\n", + " \"enabled\": false,\n", " \"loopback-mode\": true,\n", - " \"enabled\": false\n", + " \"name\": \"FastEthernet4\",\n", + " \"type\": \"iana-if-type:ethernetCsmacd\"\n", " },\n", + " \"name\": \"FastEthernet4\",\n", " \"openconfig-if-ethernet:ethernet\": {\n", " \"openconfig-vlan:switched-vlan\": {\n", " \"config\": {\n", @@ -204,32 +204,51 @@ " \"openconfig-network-instance:network-instances\": {\n", " \"network-instance\": [\n", " {\n", - " \"name\": \"default\",\n", " \"config\": {\n", " \"name\": \"default\"\n", " },\n", + " \"name\": \"default\",\n", " \"vlans\": {\n", " \"vlan\": [\n", " {\n", - " \"vlan-id\": 10,\n", " \"config\": {\n", - " \"vlan-id\": 10,\n", " \"name\": \"prod\",\n", - " \"status\": \"ACTIVE\"\n", - " }\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", " },\n", " {\n", - " \"vlan-id\": 20,\n", " \"config\": {\n", - " \"vlan-id\": 20,\n", " \"name\": \"dev\",\n", - " \"status\": \"SUSPENDED\"\n", - " }\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", " }\n", " ]\n", " }\n", " }\n", " ]\n", + " },\n", + " \"openconfig-system:system\": {\n", + " \"ntp\": {\n", + " \"config\": {\n", + " \"enable-ntp-auth\": false,\n", + " \"enabled\": false\n", + " }\n", + " },\n", + " \"ssh-server\": {\n", + " \"config\": {\n", + " \"enable\": true,\n", + " \"protocol-version\": \"V1_V2\"\n", + " }\n", + " },\n", + " \"telnet-server\": {\n", + " \"config\": {\n", + " \"enable\": true\n", + " }\n", + " }\n", " }\n", "}\n" ] @@ -237,7 +256,7 @@ ], "source": [ "import json\n", - "print(json.dumps(parsed.raw_value(), indent=4))" + "print(json.dumps(parsed.raw_value(), indent=4, sort_keys=True))" ] }, { @@ -301,20 +320,20 @@ " \"vlans\": {\n", " \"vlan\": [\n", " {\n", - " \"vlan-id\": 10,\n", " \"config\": {\n", - " \"vlan-id\": 10,\n", " \"name\": \"prod\",\n", - " \"status\": \"ACTIVE\"\n", - " }\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", " },\n", " {\n", - " \"vlan-id\": 20,\n", " \"config\": {\n", - " \"vlan-id\": 20,\n", " \"name\": \"dev\",\n", - " \"status\": \"SUSPENDED\"\n", - " }\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", " }\n", " ]\n", " }\n", @@ -333,7 +352,7 @@ " \"/openconfig-network-instance:network-instances/network-instance/vlans\", \n", " ]\n", ")\n", - "print(json.dumps(parsed_vlans.raw_value(), indent=4))" + "print(json.dumps(parsed_vlans.raw_value(), indent=4, sort_keys=True))" ] }, { @@ -356,27 +375,27 @@ " \"openconfig-network-instance:network-instances\": {\n", " \"network-instance\": [\n", " {\n", - " \"name\": \"default\",\n", " \"config\": {\n", " \"name\": \"default\"\n", " },\n", + " \"name\": \"default\",\n", " \"vlans\": {\n", " \"vlan\": [\n", " {\n", - " \"vlan-id\": 10,\n", " \"config\": {\n", - " \"vlan-id\": 10,\n", " \"name\": \"prod\",\n", - " \"status\": \"ACTIVE\"\n", - " }\n", + " \"status\": \"ACTIVE\",\n", + " \"vlan-id\": 10\n", + " },\n", + " \"vlan-id\": 10\n", " },\n", " {\n", - " \"vlan-id\": 20,\n", " \"config\": {\n", - " \"vlan-id\": 20,\n", " \"name\": \"dev\",\n", - " \"status\": \"SUSPENDED\"\n", - " }\n", + " \"status\": \"SUSPENDED\",\n", + " \"vlan-id\": 20\n", + " },\n", + " \"vlan-id\": 20\n", " }\n", " ]\n", " }\n", @@ -396,7 +415,7 @@ " \"/openconfig-network-instance:network-instances/network-instance/vlans\", \n", " ]\n", ")\n", - "print(json.dumps(parsed_vlans.raw_value(), indent=4))" + "print(json.dumps(parsed_vlans.raw_value(), indent=4, sort_keys=True))" ] } ], @@ -416,7 +435,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/ios_smart_diff.ipynb b/docs/tutorials/ios_smart_diff.ipynb index 95c7364..2394a80 100755 --- a/docs/tutorials/ios_smart_diff.ipynb +++ b/docs/tutorials/ios_smart_diff.ipynb @@ -191,7 +191,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py index d17938a..c2e7f3d 100644 --- a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py @@ -143,16 +143,17 @@ def post_process(self) -> None: self.result.add_command( f"username {self.extra['username']}{role_str} {password_str or password_hash_str}" ) - if ssh_key: - """ - ip ssh pubkey-chain - username test - key-string - some-rsa-ssh-key-string - exit - exit - """ - raise NotImplementedError + # TODO: implement ssh_key + # if ssh_key: + # """ + # ip ssh pubkey-chain + # username test + # key-string + # some-rsa-ssh-key-string + # exit + # exit + # """ + # raise NotImplementedError def username(self, value: str) -> None: self.yy.extra["username"] = value diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/data_candidate.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_candidate.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/data_candidate.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/data_running.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/data_running.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/data_running.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/res_merge similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_dns_server/res_merge rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/res_merge diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/add_dns_server/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/data_candidate.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_candidate.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/data_candidate.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/data_running.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/data_running.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/data_running.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/res_merge similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_ntp_server/res_merge rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/res_merge diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/data_candidate.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_candidate.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/data_candidate.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/data_running.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/data_running.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/data_running.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/res_merge similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/add_ssh_user/res_merge rename to tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/res_merge diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ssh_user/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/data_candidate.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_candidate.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/data_candidate.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/data_running.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/data_running.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/data_running.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/res_merge similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_dns_server/res_merge rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/res_merge diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_dns_server/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/data_candidate.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_candidate.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/data_candidate.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/data_running.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/data_running.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/data_running.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/res_merge similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_ntp_server/res_merge rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/res_merge diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/data_candidate.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_candidate.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/data_candidate.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/data_running.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/data_running.json rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/data_running.json diff --git a/tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/res_merge similarity index 100% rename from tests/models/openconfig/data/openconfig-system/merge/ios/remove_ssh_user/res_merge rename to tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/res_merge diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ssh_user/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/parse/ios/__init__.py b/tests/models/openconfig/data/openconfig_system/parse/ios/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig_system/parse/ios/config/__init__.py b/tests/models/openconfig/data/openconfig_system/parse/ios/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/parse/ios/config/dev_conf b/tests/models/openconfig/data/openconfig_system/parse/ios/config/dev_conf similarity index 100% rename from tests/models/openconfig/data/openconfig-system/parse/ios/config/dev_conf rename to tests/models/openconfig/data/openconfig_system/parse/ios/config/dev_conf diff --git a/tests/models/openconfig/data/openconfig-system/parse/ios/config/result.json b/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/parse/ios/config/result.json rename to tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json diff --git a/tests/models/openconfig/data/openconfig_system/parse/ios/config/test_case.py b/tests/models/openconfig/data/openconfig_system/parse/ios/config/test_case.py new file mode 100644 index 0000000..33cb979 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/parse/ios/config/test_case.py @@ -0,0 +1,7 @@ +from tests.models.test_models import parse_path, parser + + +def test_parser() -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + parser(test_data.org, test_data.model, test_data.driver, test_data.path) diff --git a/tests/models/openconfig/data/openconfig_system/test_ios_system.py b/tests/models/openconfig/data/openconfig_system/test_ios_system.py new file mode 100644 index 0000000..8440992 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/test_ios_system.py @@ -0,0 +1,15 @@ +import ntc_rosetta +from yangson.exceptions import InstanceValueError + +import pytest + + +def test_ios_system_replace_fail() -> None: + """IOS does not support configuration replacement for all sections of the config. + + Test that the appropriate error is raised on system replace. + """ + driver_class = ntc_rosetta.get_driver("ios", "openconfig") + device = driver_class() + with pytest.raises(InstanceValueError): + device.merge({"openconfig-system:system": {}}, {}, replace=True) diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/__init__.py b/tests/models/openconfig/data/openconfig_system/translate/ios/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/__init__.py b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/data.json b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/data.json similarity index 100% rename from tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/data.json rename to tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/data.json diff --git a/tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/res_merge b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge similarity index 100% rename from tests/models/openconfig/data/openconfig-system/translate/ios/test_case_1/res_merge rename to tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py new file mode 100644 index 0000000..9815340 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, translate + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_translate(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + translate(test_data.org, test_data.driver, test_data.path, action) From 3f17a8144b645d3fbdea3c37678df11a200e2af8 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Thu, 20 Feb 2020 16:39:31 -0600 Subject: [PATCH 05/19] remove spurious replace check --- .../translators/openconfig/ios/openconfig_system/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py index c2e7f3d..a72c491 100644 --- a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py @@ -22,7 +22,7 @@ class Yangify(TranslatorData): path = "/openconfig-system:system/dns/servers/server" def pre_process_list(self) -> None: - if self.to_remove and not self.replace: + if self.to_remove: for element in self.to_remove: self.result.add_command(f"no ip name-server {element['address']}") From 6eaa4344c97ff5939a5e570795fee8d1b5e9b593 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Thu, 20 Feb 2020 16:39:44 -0600 Subject: [PATCH 06/19] Fix DNS server parsing --- .../ios/openconfig_system/system.py | 12 +++++------ .../parse/ios/config/result.json | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py index 37b558f..c18ecf9 100644 --- a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py @@ -46,21 +46,19 @@ def port(self) -> int: class DnsServer(Parser): + config = DnsServerConfig + class Yangify(ParserData): path = "/openconfig-system:system/dns/servers/server" def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: - dns = jh.query('ip."name-server"', self.native) or {} - for k, v in dns.items(): - if k == "#text": - continue - yield k, v + dns = jh.query('ip."name-server"."#text"', self.native) or "" + for ns in dns.split(' '): + yield ns, {} def address(self) -> str: return str(self.yy.key) - config = DnsServerConfig - class DnsServers(Parser): server = DnsServer diff --git a/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json b/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json index f3086b4..1b4fe4a 100644 --- a/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json +++ b/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json @@ -17,6 +17,27 @@ "address": "8.8.8.8", "port": 53 } + }, + { + "address": "8.8.4.4", + "config": { + "address": "8.8.4.4", + "port": 53 + } + }, + { + "address": "2001:4860:4860::8888", + "config": { + "address": "2001:4860:4860::8888", + "port": 53 + } + }, + { + "address": "2001:4860:4860::8844", + "config": { + "address": "2001:4860:4860::8844", + "port": 53 + } } ] } From 4e1e580bcab331ca83dea68f782dcfefaacbab58 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Thu, 20 Feb 2020 16:40:32 -0600 Subject: [PATCH 07/19] black --- ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py index c18ecf9..9fb332d 100644 --- a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py @@ -53,7 +53,7 @@ class Yangify(ParserData): def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: dns = jh.query('ip."name-server"."#text"', self.native) or "" - for ns in dns.split(' '): + for ns in dns.split(" "): yield ns, {} def address(self) -> str: From 814fb53e4cf4503d8ac474acaf32530d83b4b19d Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Wed, 26 Feb 2020 11:29:06 -0600 Subject: [PATCH 08/19] Update yangify to 0.1.2 --- poetry.lock | 891 ++++++++++++++++++++++++++++++++++++++++--------- pyproject.toml | 2 +- 2 files changed, 731 insertions(+), 162 deletions(-) diff --git a/poetry.lock b/poetry.lock index dc7b109..466406c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -9,7 +9,7 @@ version = "1.4.3" [[package]] category = "dev" description = "Disable App Nap on OS X 10.9" -marker = "python_version >= \"3.3\" and sys_platform == \"darwin\" or sys_platform == \"darwin\"" +marker = "sys_platform == \"darwin\" or platform_system == \"Darwin\" or python_version >= \"3.3\" and sys_platform == \"darwin\"" name = "appnope" optional = false python-versions = "*" @@ -29,7 +29,13 @@ description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.1.0" +version = "19.3.0" + +[package.extras] +azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] +dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] +docs = ["sphinx", "zope.interface"] +tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] [[package]] category = "dev" @@ -53,13 +59,16 @@ attrs = ">=18.1.0" click = ">=6.5" toml = ">=0.9.4" +[package.extras] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] + [[package]] category = "dev" description = "An easy safelist-based HTML-sanitizing tool." name = "bleach" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.1.0" +version = "3.1.1" [package.dependencies] six = ">=1.9.0" @@ -76,19 +85,22 @@ version = "7.0" [[package]] category = "dev" description = "Cross-platform colored terminal text." -marker = "python_version >= \"3.3\" and sys_platform == \"win32\" or sys_platform == \"win32\"" +marker = "sys_platform == \"win32\" and python_version != \"3.4\" or python_version >= \"3.3\" and sys_platform == \"win32\" or sys_platform == \"win32\"" name = "colorama" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.4.1" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.3" [[package]] category = "dev" description = "Code coverage measurement for Python" name = "coverage" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" -version = "4.5.3" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "5.0.3" + +[package.extras] +toml = ["toml"] [[package]] category = "main" @@ -101,11 +113,11 @@ version = "0.6" [[package]] category = "dev" -description = "Better living through Python with decorators" +description = "Decorators for Humans" name = "decorator" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.4.0" +version = "4.4.1" [[package]] category = "dev" @@ -135,27 +147,47 @@ version = "0.18.1" pycodestyle = "*" setuptools = "*" +[[package]] +category = "dev" +description = "Read metadata from Python packages" +marker = "python_version < \"3.8\"" +name = "importlib-metadata" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "1.5.0" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "rst.linker"] +testing = ["packaging", "importlib-resources"] + [[package]] category = "dev" description = "IPython Kernel for Jupyter" name = "ipykernel" optional = false python-versions = ">=3.4" -version = "5.1.1" +version = "5.1.4" [package.dependencies] +appnope = "*" ipython = ">=5.0.0" jupyter-client = "*" tornado = ">=4.2" traitlets = ">=4.1.0" +[package.extras] +test = ["pytest", "pytest-cov", "flaky", "nose"] + [[package]] category = "dev" description = "IPython: Productive Interactive Computing" name = "ipython" optional = false -python-versions = ">=3.5" -version = "7.5.0" +python-versions = ">=3.6" +version = "7.12.0" [package.dependencies] appnope = "*" @@ -165,11 +197,22 @@ decorator = "*" jedi = ">=0.10" pexpect = "*" pickleshare = "*" -prompt-toolkit = ">=2.0.0,<2.1.0" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" setuptools = ">=18.5" traitlets = ">=4.2" +[package.extras] +all = ["ipyparallel", "requests", "notebook", "qtconsole", "ipywidgets", "pygments", "nbconvert", "testpath", "Sphinx (>=1.3)", "nbformat", "numpy (>=1.14)", "ipykernel", "nose (>=0.10.1)"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["notebook", "ipywidgets"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] + [[package]] category = "dev" description = "Vestigial utilities from IPython" @@ -184,47 +227,57 @@ description = "IPython HTML widgets for Jupyter" name = "ipywidgets" optional = false python-versions = "*" -version = "7.4.2" +version = "7.5.1" [package.dependencies] ipykernel = ">=4.5.1" nbformat = ">=4.2.0" traitlets = ">=4.3.1" -widgetsnbextension = ">=3.4.0,<3.5.0" +widgetsnbextension = ">=3.5.0,<3.6.0" [package.dependencies.ipython] python = ">=3.3" version = ">=4.0.0" +[package.extras] +test = ["pytest (>=3.6.0)", "pytest-cov", "mock"] + [[package]] category = "dev" description = "An autocompletion tool for Python that can be used for text editors." name = "jedi" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.3" +version = "0.16.0" [package.dependencies] -parso = ">=0.3.0" +parso = ">=0.5.2" + +[package.extras] +qa = ["flake8 (3.7.9)"] +testing = ["colorama (0.4.1)", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] category = "dev" -description = "A small but fast and easy to use stand-alone template engine written in pure python." +description = "A very fast and expressive template engine." name = "jinja2" optional = false -python-versions = "*" -version = "2.10.1" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.11.1" [package.dependencies] MarkupSafe = ">=0.23" +[package.extras] +i18n = ["Babel (>=0.8)"] + [[package]] category = "main" description = "JSON Matching Expressions" name = "jmespath" optional = false python-versions = "*" -version = "0.9.4" +version = "0.9.5" [[package]] category = "dev" @@ -232,7 +285,7 @@ description = "An implementation of JSON Schema validation for Python" name = "jsonschema" optional = false python-versions = "*" -version = "3.0.1" +version = "3.2.0" [package.dependencies] attrs = ">=17.4.0" @@ -240,6 +293,14 @@ pyrsistent = ">=0.14.0" setuptools = "*" six = ">=1.11.0" +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + +[package.extras] +format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] +format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] + [[package]] category = "dev" description = "Jupyter metapackage. Install all the Jupyter components in one go." @@ -261,40 +322,47 @@ category = "dev" description = "Jupyter protocol implementation and client libraries" name = "jupyter-client" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "5.2.4" +python-versions = ">=3.5" +version = "6.0.0" [package.dependencies] -jupyter-core = "*" +jupyter-core = ">=4.6.0" python-dateutil = ">=2.1" pyzmq = ">=13" tornado = ">=4.1" traitlets = "*" +[package.extras] +test = ["ipykernel", "ipython", "mock", "pytest"] + [[package]] category = "dev" description = "Jupyter terminal console" name = "jupyter-console" optional = false python-versions = ">=3.5" -version = "6.0.0" +version = "6.1.0" [package.dependencies] ipykernel = "*" ipython = "*" jupyter-client = "*" -prompt-toolkit = ">=2.0.0,<2.1.0" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" +[package.extras] +test = ["pexpect"] + [[package]] category = "dev" description = "Jupyter core package. A base package on which Jupyter projects rely." name = "jupyter-core" optional = false -python-versions = "*" -version = "4.4.0" +python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" +version = "4.6.3" [package.dependencies] +pywin32 = ">=1.0" traitlets = "*" [[package]] @@ -302,8 +370,14 @@ category = "main" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." name = "lxml" optional = false -python-versions = "*" -version = "4.3.3" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +version = "4.5.0" + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["beautifulsoup4"] +source = ["Cython (>=0.29.7)"] [[package]] category = "dev" @@ -335,8 +409,8 @@ description = "More routines for operating on iterables, beyond itertools" marker = "python_version > \"2.7\"" name = "more-itertools" optional = false -python-versions = ">=3.4" -version = "7.0.0" +python-versions = ">=3.5" +version = "8.2.0" [[package]] category = "dev" @@ -350,13 +424,16 @@ version = "0.701" mypy-extensions = ">=0.4.0,<0.5.0" typed-ast = ">=1.3.1,<1.4.0" +[package.extras] +dmypy = ["psutil (>=5.4.0,<5.5.0)"] + [[package]] category = "dev" description = "Experimental type system extensions for programs checked with the mypy typechecker." name = "mypy-extensions" optional = false python-versions = "*" -version = "0.4.1" +version = "0.4.3" [[package]] category = "dev" @@ -364,7 +441,7 @@ description = "Converting Jupyter Notebooks" name = "nbconvert" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.5.0" +version = "5.6.1" [package.dependencies] bleach = "*" @@ -372,20 +449,27 @@ defusedxml = "*" entrypoints = ">=0.2.2" jinja2 = ">=2.4" jupyter-core = "*" -mistune = ">=0.8.1" +mistune = ">=0.8.1,<2" nbformat = ">=4.4" pandocfilters = ">=1.4.1" pygments = "*" testpath = "*" traitlets = ">=4.2" +[package.extras] +all = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "mock"] +docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "jupyter-client (>=5.3.1)"] +execute = ["jupyter-client (>=5.3.1)"] +serve = ["tornado (>=4.0)"] +test = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "mock"] + [[package]] category = "dev" description = "The Jupyter Notebook format" name = "nbformat" optional = false -python-versions = "*" -version = "4.4.0" +python-versions = ">=3.5" +version = "5.0.4" [package.dependencies] ipython-genutils = "*" @@ -393,13 +477,16 @@ jsonschema = ">=2.4,<2.5.0 || >2.5.0" jupyter-core = "*" traitlets = ">=4.1" +[package.extras] +test = ["testpath", "pytest", "pytest-cov"] + [[package]] category = "dev" description = "A py.test plugin to validate Jupyter notebooks" name = "nbval" optional = false python-versions = "*" -version = "0.9.1" +version = "0.9.5" [package.dependencies] coverage = "*" @@ -414,24 +501,39 @@ category = "dev" description = "A web-based notebook environment for interactive computing" name = "notebook" optional = false -python-versions = "*" -version = "5.7.8" +python-versions = ">=3.5" +version = "6.0.3" [package.dependencies] Send2Trash = "*" ipykernel = "*" ipython-genutils = "*" jinja2 = "*" -jupyter-client = ">=5.2.0" -jupyter-core = ">=4.4.0" +jupyter-client = ">=5.3.4" +jupyter-core = ">=4.6.1" nbconvert = "*" nbformat = "*" prometheus-client = "*" pyzmq = ">=17" terminado = ">=0.8.1" -tornado = ">=4.1,<7" +tornado = ">=5.0" traitlets = ">=4.2.1" +[package.extras] +test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "nose-exclude"] + +[[package]] +category = "dev" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.1" + +[package.dependencies] +pyparsing = ">=2.0.2" +six = "*" + [[package]] category = "dev" description = "Utilities for writing pandoc filters in python" @@ -445,8 +547,11 @@ category = "dev" description = "A Python Parser" name = "parso" optional = false -python-versions = "*" -version = "0.4.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.6.1" + +[package.extras] +testing = ["docopt", "pytest (>=3.0.7)"] [[package]] category = "dev" @@ -455,7 +560,7 @@ marker = "python_version >= \"3.3\" and sys_platform != \"win32\" or sys_platfor name = "pexpect" optional = false python-versions = "*" -version = "4.7.0" +version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" @@ -474,7 +579,15 @@ description = "plugin and hook calling mechanisms for python" name = "pluggy" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.11.0" +version = "0.13.1" + +[package.dependencies] +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +dev = ["pre-commit", "tox"] [[package]] category = "dev" @@ -482,18 +595,20 @@ description = "Python client for the Prometheus monitoring system." name = "prometheus-client" optional = false python-versions = "*" -version = "0.6.0" +version = "0.7.1" + +[package.extras] +twisted = ["twisted"] [[package]] category = "dev" description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" optional = false -python-versions = "*" -version = "2.0.9" +python-versions = ">=3.6" +version = "3.0.3" [package.dependencies] -six = ">=1.9.0" wcwidth = "*" [[package]] @@ -511,7 +626,7 @@ description = "library with cross-python path, ini-parsing, io, code, log facili name = "py" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.8.0" +version = "1.8.1" [[package]] category = "dev" @@ -526,11 +641,10 @@ category = "dev" description = "Python docstring style checker" name = "pydocstyle" optional = false -python-versions = "*" -version = "3.0.0" +python-versions = ">=3.5" +version = "5.0.2" [package.dependencies] -six = "*" snowballstemmer = "*" [[package]] @@ -547,7 +661,7 @@ description = "Pygments is a syntax highlighting package written in Python." name = "pygments" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.4.0" +version = "2.5.2" [[package]] category = "dev" @@ -563,13 +677,21 @@ pycodestyle = ">=2.3.1" pydocstyle = ">=2.0.0" pyflakes = ">=1.5.0" +[[package]] +category = "dev" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.6" + [[package]] category = "dev" description = "Persistent/Functional/Immutable data structures" name = "pyrsistent" optional = false python-versions = "*" -version = "0.15.2" +version = "0.15.7" [package.dependencies] six = "*" @@ -579,35 +701,48 @@ category = "dev" description = "pytest: simple powerful testing with Python" name = "pytest" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "4.5.0" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "4.6.9" [package.dependencies] atomicwrites = ">=1.0" attrs = ">=17.4.0" -colorama = "*" -pluggy = ">=0.9,<0.10 || >0.10,<1.0" +packaging = "*" +pluggy = ">=0.12,<1.0" py = ">=1.5.0" -setuptools = "*" six = ">=1.10.0" wcwidth = "*" +[package.dependencies.colorama] +python = "<3.4.0 || >=3.5.0" +version = "*" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + [package.dependencies.more-itertools] python = ">=2.8" version = ">=4.0.0" +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] + [[package]] category = "dev" description = "Pytest plugin for measuring coverage." name = "pytest-cov" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.7.1" +version = "2.8.1" [package.dependencies] coverage = ">=4.4" pytest = ">=3.6" +[package.extras] +testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "virtualenv"] + [[package]] category = "dev" description = "pytest plugin for adding to the PYTHONPATH from command line or configs." @@ -624,12 +759,21 @@ category = "dev" description = "Extensions to the standard Python datetime module" name = "python-dateutil" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.8.0" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" [package.dependencies] six = ">=1.5" +[[package]] +category = "dev" +description = "Python for Window Extensions" +marker = "sys_platform == \"win32\"" +name = "pywin32" +optional = false +python-versions = "*" +version = "227" + [[package]] category = "dev" description = "Python bindings for the winpty library" @@ -637,7 +781,7 @@ marker = "os_name == \"nt\"" name = "pywinpty" optional = false python-versions = "*" -version = "0.5.5" +version = "0.5.7" [[package]] category = "main" @@ -652,8 +796,8 @@ category = "dev" description = "Python bindings for 0MQ" name = "pyzmq" optional = false -python-versions = ">=2.7,!=3.0*,!=3.1*,!=3.2*" -version = "18.0.1" +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" +version = "19.0.0" [[package]] category = "dev" @@ -661,7 +805,7 @@ description = "Jupyter Qt console" name = "qtconsole" optional = false python-versions = "*" -version = "4.4.4" +version = "4.6.0" [package.dependencies] ipykernel = ">=4.1" @@ -671,6 +815,10 @@ jupyter-core = "*" pygments = "*" traitlets = "*" +[package.extras] +doc = ["Sphinx (>=1.3)"] +test = ["pytest", "mock"] + [[package]] category = "dev" description = "Send file to trash natively under Mac OS X, Windows and Linux." @@ -684,16 +832,16 @@ category = "dev" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.12.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.14.0" [[package]] category = "dev" -description = "This package provides 16 stemmer algorithms (15 + Poerter English stemmer) generated from Snowball algorithms." +description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." name = "snowballstemmer" optional = false python-versions = "*" -version = "1.2.1" +version = "2.0.0" [[package]] category = "dev" @@ -701,7 +849,7 @@ description = "Terminals served to xterm.js using Tornado websockets" name = "terminado" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.8.2" +version = "0.8.3" [package.dependencies] ptyprocess = "*" @@ -714,7 +862,10 @@ description = "Test utilities for code working with files and commands" name = "testpath" optional = false python-versions = "*" -version = "0.4.2" +version = "0.4.4" + +[package.extras] +test = ["pathlib2"] [[package]] category = "dev" @@ -730,7 +881,7 @@ description = "Tornado is a Python web framework and asynchronous networking lib name = "tornado" optional = false python-versions = ">= 3.5" -version = "6.0.2" +version = "6.0.3" [[package]] category = "dev" @@ -738,13 +889,16 @@ description = "Traitlets Python config system" name = "traitlets" optional = false python-versions = "*" -version = "4.3.2" +version = "4.3.3" [package.dependencies] decorator = "*" ipython-genutils = "*" six = "*" +[package.extras] +test = ["pytest", "mock"] + [[package]] category = "dev" description = "a fork of Python 2 and 3 ast modules with type comment support" @@ -759,7 +913,7 @@ description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" optional = false python-versions = "*" -version = "0.1.7" +version = "0.1.8" [[package]] category = "dev" @@ -775,7 +929,7 @@ description = "IPython HTML widgets for Jupyter" name = "widgetsnbextension" optional = false python-versions = "*" -version = "3.4.2" +version = "3.5.1" [package.dependencies] notebook = ">=4.4.1" @@ -786,10 +940,10 @@ description = "Library to help parsing/translating YANG models from/to native te name = "yangify" optional = false python-versions = ">=3.6,<4.0" -version = "0.1.1" +version = "0.1.2" [package.dependencies] -yangson = ">=1.3,<2.0" +yangson = "1.3.45" [package.dependencies.dataclasses] python = ">=3.6,<3.7" @@ -801,88 +955,503 @@ description = "Library for working with data modelled in YANG" name = "yangson" optional = false python-versions = "*" -version = "1.3.39" +version = "1.3.45" [package.dependencies] PyXB = "*" +[[package]] +category = "dev" +description = "Backport of pathlib-compatible object wrapper for zip files" +marker = "python_version < \"3.8\"" +name = "zipp" +optional = false +python-versions = ">=3.6" +version = "3.0.0" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["jaraco.itertools", "func-timeout"] + [metadata] -content-hash = "c46335faab848a55f56b358ccc88f03cd8142e51622e89b7efb809bf5a1b3359" +content-hash = "711f44a1c270e24c00dde0b31ed4653fb2057ebd8dffb4f37e55c7ea14f26263" python-versions = "^3.6" -[metadata.hashes] -appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] -appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] -atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"] -attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"] -backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] -black = ["09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", "68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"] -bleach = ["213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", "3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"] -click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] -colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] -coverage = ["0c5fe441b9cfdab64719f24e9684502a59432df7570521563d7b1aff27ac755f", "2b412abc4c7d6e019ce7c27cbc229783035eef6d5401695dccba80f481be4eb3", "3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9", "39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74", "3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390", "42692db854d13c6c5e9541b6ffe0fe921fe16c9c446358d642ccae1462582d3b", "465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8", "48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe", "4ec30ade438d1711562f3786bea33a9da6107414aed60a5daa974d50a8c2c351", "5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf", "5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e", "6899797ac384b239ce1926f3cb86ffc19996f6fa3a1efbb23cb49e0c12d8c18c", "68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741", "6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09", "7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd", "7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034", "839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420", "8e679d1bde5e2de4a909efb071f14b472a678b788904440779d2c449c0355b27", "8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c", "932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab", "93f965415cc51604f571e491f280cff0f5be35895b4eb5e55b47ae90c02a497b", "988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba", "998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e", "9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609", "9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2", "a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49", "a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b", "a9abc8c480e103dc05d9b332c6cc9fb1586330356fc14f1aa9c0ca5745097d19", "aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d", "bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce", "bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9", "c22ab9f96cbaff05c6a84e20ec856383d27eae09e511d3e6ac4479489195861d", "c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4", "c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773", "c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723", "ca58eba39c68010d7e87a823f22a081b5290e3e3c64714aac3c91481d8b34d22", "df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c", "f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f", "f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1", "f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260", "fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a"] -dataclasses = ["454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f", "6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"] -decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"] -defusedxml = ["6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", "f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"] -entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] -flake8-import-order = ["90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543", "a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"] -ipykernel = ["346189536b88859937b5f4848a6fd85d1ad0729f01724a411de5cae9b618819c", "f0e962052718068ad3b1d8bcc703794660858f58803c3798628817f492a8769c"] -ipython = ["54c5a8aa1eadd269ac210b96923688ccf01ebb2d0f21c18c3c717909583579a8", "e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26"] -ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] -ipywidgets = ["0f2b5cde9f272cb49d52f3f0889fdd1a7ae1e74f37b48dac35a83152780d2b7b", "a3e224f430163f767047ab9a042fc55adbcab0c24bbe6cf9f306c4f89fdf0ba3"] -jedi = ["2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b", "2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"] -jinja2 = ["065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", "14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"] -jmespath = ["3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6", "bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c"] -jsonschema = ["0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d", "a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a"] -jupyter = ["3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", "5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", "d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"] -jupyter-client = ["b5f9cb06105c1d2d30719db5ffb3ea67da60919fb68deaefa583deccd8813551", "c44411eb1463ed77548bc2d5ec0d744c9b81c4a542d9637c7a52824e2121b987"] -jupyter-console = ["308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5", "cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"] -jupyter-core = ["927d713ffa616ea11972534411544589976b2493fc7e09ad946e010aa7eb9970", "ba70754aa680300306c699790128f6fbd8c306ee5927976cbe48adacf240c0b7"] -lxml = ["03984196d00670b2ab14ae0ea83d5cc0cfa4f5a42558afa9ab5fa745995328f5", "0815b0c9f897468de6a386dc15917a0becf48cc92425613aa8bbfc7f0f82951f", "175f3825f075cf02d15099eb52658457cf0ff103dcf11512b5d2583e1d40f58b", "30e14c62d88d1e01a26936ecd1c6e784d4afc9aa002bba4321c5897937112616", "3210da6f36cf4b835ff1be853962b22cc354d506f493b67a4303c88bbb40d57b", "40f60819fbd5bad6e191ba1329bfafa09ab7f3f174b3d034d413ef5266963294", "43b26a865a61549919f8a42e094dfdb62847113cf776d84bd6b60e4e3fc20ea3", "4a03dd682f8e35a10234904e0b9508d705ff98cf962c5851ed052e9340df3d90", "62f382cddf3d2e52cf266e161aa522d54fd624b8cc567bc18f573d9d50d40e8e", "7b98f0325be8450da70aa4a796c4f06852949fe031878b4aa1d6c417a412f314", "846a0739e595871041385d86d12af4b6999f921359b38affb99cdd6b54219a8f", "a3080470559938a09a5d0ec558c005282e99ac77bf8211fb7b9a5c66390acd8d", "ad841b78a476623955da270ab8d207c3c694aa5eba71f4792f65926dc46c6ee8", "afdd75d9735e44c639ffd6258ce04a2de3b208f148072c02478162d0944d9da3", "b4fbf9b552faff54742bcd0791ab1da5863363fb19047e68f6592be1ac2dab33", "b90c4e32d6ec089d3fa3518436bdf5ce4d902a0787dbd9bb09f37afe8b994317", "b91cfe4438c741aeff662d413fd2808ac901cc6229c838236840d11de4586d63", "bdb0593a42070b0a5f138b79b872289ee73c8e25b3f0bea6564e795b55b6bcdd", "c4e4bca2bb68ce22320297dfa1a7bf070a5b20bcbaec4ee023f83d2f6e76496f", "cec4ab14af9eae8501be3266ff50c3c2aecc017ba1e86c160209bb4f0423df6a", "e83b4b2bf029f5104bc1227dbb7bf5ace6fd8fabaebffcd4f8106fafc69fc45f", "e995b3734a46d41ae60b6097f7c51ba9958648c6d1e0935b7e0ee446ee4abe22", "f679d93dec7f7210575c85379a31322df4c46496f184ef650d3aba1484b38a2d", "fd213bb5166e46974f113c8228daaef1732abc47cb561ce9c4c8eaed4bd3b09b", "fdcb57b906dbc1f80666e6290e794ab8fb959a2e17aa5aee1758a85d1da4533f", "ff424b01d090ffe1947ec7432b07f536912e0300458f9a7f48ea217dd8362b86"] -markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] -mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] -mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"] -more-itertools = ["2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7", "c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"] -mypy = ["2afe51527b1f6cdc4a5f34fc90473109b22bf7f21086ba3e9451857cf11489e6", "56a16df3e0abb145d8accd5dbb70eba6c4bd26e2f89042b491faa78c9635d1e2", "5764f10d27b2e93c84f70af5778941b8f4aa1379b2430f85c827e0f5464e8714", "5bbc86374f04a3aa817622f98e40375ccb28c4836f36b66706cf3c6ccce86eda", "6a9343089f6377e71e20ca734cd8e7ac25d36478a9df580efabfe9059819bf82", "6c9851bc4a23dc1d854d3f5dfd5f20a016f8da86bcdbb42687879bb5f86434b0", "b8e85956af3fcf043d6f87c91cbe8705073fc67029ba6e22d3468bfee42c4823", "b9a0af8fae490306bc112229000aa0c2ccc837b49d29a5c42e088c132a2334dd", "bbf643528e2a55df2c1587008d6e3bda5c0445f1240dfa85129af22ae16d7a9a", "c46ab3438bd21511db0f2c612d89d8344154c0c9494afc7fbc932de514cf8d15", "f7a83d6bd805855ef83ec605eb01ab4fa42bcef254b13631e451cbb44914a9b0"] -mypy-extensions = ["37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812", "b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e"] -nbconvert = ["138381baa41d83584459b5cfecfc38c800ccf1f37d9ddd0bd440783346a4c39c", "4a978548d8383f6b2cfca4a3b0543afb77bc7cb5a96e8b424337ab58c12da9bc"] -nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"] -nbval = ["3f18b87af4e94ccd073263dd58cd3eebabe9f5e4d6ab535b39d3af64811c7eda", "74ff5e2c90a50b1ddf7edd02978c4e43221b1ee252dc14fcaa4230aae4492eda"] -notebook = ["573e0ae650c5d76b18b6e564ba6d21bf321d00847de1d215b418acb64f056eb8", "f64fa6624d2323fbef6210a621817d6505a45d0d4a9367f1843b20a38a4666ee"] -pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"] -parso = ["17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33", "2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"] -pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] -pickleshare = ["87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", "9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"] -pluggy = ["25a1bc1d148c9a640211872b4ff859878d422bccb59c9965e04eed468a0aa180", "964cedd2b27c492fbf0b7f58b3284a09cf7f99b0f715941fb24a439b3af1bd1a"] -prometheus-client = ["1b38b958750f66f208bcd9ab92a633c0c994d8859c831f7abc1f46724fcee490"] -prompt-toolkit = ["11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", "2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", "977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"] -ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] -py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] -pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"] -pydocstyle = ["2258f9b0df68b97bf3a6c29003edc5238ff8879f1efb6f1999988d934e432bd8", "5741c85e408f9e0ddf873611085e819b809fca90b619f5fd7f34bd4959da3dd4", "ed79d4ec5e92655eccc21eb0c6cf512e69512b4a97d215ace46d17e4990f2039"] -pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"] -pygments = ["31cba6ffb739f099a85e243eff8cb717089fdd3c7300767d9fc34cb8e1b065f5", "5ad302949b3c98dd73f8d9fcdc7e9cb592f120e32a18e23efd7f3dc51194472b"] -pylama = ["9bae53ef9c1a431371d6a8dca406816a60d547147b60a4934721898f553b7d8f", "fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"] -pyrsistent = ["16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a"] -pytest = ["1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24", "b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6"] -pytest-cov = ["2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6", "e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a"] -pytest-pythonpath = ["63fc546ace7d2c845c1ee289e8f7a6362c2b6bae497d10c716e58e253e801d62"] -python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"] -pywinpty = ["0e01321e53a230233358a6d608a1a8bc86c3882cf82769ba3c62ca387dc9cc51", "333e0bc5fca8ad9e9a1516ebedb2a65da38dc1f399f8b2ea57d6cccec1ff2cc8", "3ca3123aa6340ab31bbf9bd012b92e72f9ec905e4c9ee152cc997403e1778cd3", "44a6dddcf2abf402e22f87e2c9a341f7d0b296afbec3d28184c8de4d7f514ee4", "53d94d574c3d4da2df5b1c3ae728b8d90e4d33502b0388576bbd4ddeb4de0f77", "c3955f162c53dde968f3fc11361658f1d83b683bfe601d4b6f94bb01ea4300bc", "cec9894ecb34de3d7b1ca121dd98433035b9f8949b5095e84b103b349231509c", "dcd45912e2fe2e6f72cee997a4da6ed1ad2056165a277ce5ec7f7ac98dcdf667", "f2bcdd9a2ffd8b223752a971b3d377fb7bfed85f140ec9710f1218d760f2ccb7"] -pyxb = ["2a00f38dd1d87b88f92d79bc5a09718d730419b88e814545f472bbd5a3bf27b4"] -pyzmq = ["1651e52ed91f0736afd6d94ef9f3259b5534ce8beddb054f3d5ca989c4ef7c4f", "5ccb9b3d4cd20c000a9b75689d5add8cd3bce67fcbd0f8ae1b59345247d803af", "5e120c4cd3872e332fb35d255ad5998ebcee32ace4387b1b337416b6b90436c7", "5e2a3707c69a7281a9957f83718815fd74698cba31f6d69f9ed359921f662221", "63d51add9af8d0442dc90f916baf98fdc04e3b0a32afec4bfc83f8d85e72959f", "65c5a0bdc49e20f7d6b03a661f71e2fda7a99c51270cafe71598146d09810d0d", "66828fabe911aa545d919028441a585edb7c9c77969a5fea6722ef6e6ece38ab", "7d79427e82d9dad6e9b47c0b3e7ae5f9d489b1601e3a36ea629bb49501a4daf3", "824ee5d3078c4eae737ffc500fbf32f2b14e6ec89b26b435b7834febd70120cf", "89dc0a83cccec19ff3c62c091e43e66e0183d1e6b4658c16ee4e659518131494", "8b319805f6f7c907b101c864c3ca6cefc9db8ce0791356f180b1b644c7347e4c", "90facfb379ab47f94b19519c1ecc8ec8d10813b69d9c163117944948bdec5d15", "a0a178c7420021fc0730180a914a4b4b3092ce9696ceb8e72d0f60f8ce1655dd", "a7a89591ae315baccb8072f216614b3e59aed7385aef4393a6c741783d6ee9cf", "ba2578f0ae582452c02ed9fac2dc477b08e80ce05d2c0885becf5fff6651ccb0", "c69b0055c55702f5b0b6b354133e8325b9a56dbc80e1be2d240bead253fb9825", "ca434e1858fe222380221ddeb81e86f45522773344c9da63c311d17161df5e06", "d4b8ecfc3d92f114f04d5c40f60a65e5196198b827503341521dda12d8b14939", "d706025c47b09a54f005953ebe206f6d07a22516776faa4f509aaff681cc5468", "d8f27e958f8a2c0c8ffd4d8855c3ce8ac3fa1e105f0491ce31729aa2b3229740", "dbd264298f76b9060ce537008eb989317ca787c857e23cbd1b3ddf89f190a9b1", "e926d66f0df8fdbf03ba20583af0f215e475c667fb033d45fd031c66c63e34c9", "efc3bd48237f973a749f7312f68062f1b4ca5c2032a0673ca3ea8e46aa77187b", "f59bc782228777cbfe04555707a9c56d269c787ed25d6d28ed9d0fbb41cb1ad2", "f8da5322f4ff5f667a0d5a27e871b560c6637153c81e318b35cb012b2a98835c"] -qtconsole = ["a667558c7b1e1442a2e5bcef1686c55e096efd0b58d8b2a0a8415f4579991ee3", "fdfc6002d9d2834c88f9c92e0f6f590284ff3740fa53016f188a62d58bcca6d8"] -send2trash = ["60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", "f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"] -six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] -snowballstemmer = ["919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128", "9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"] -terminado = ["d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460", "de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"] -testpath = ["46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109", "b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"] -toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] -tornado = ["1174dcb84d08887b55defb2cda1986faeeea715fff189ef3dc44cce99f5fca6b", "2613fab506bd2aedb3722c8c64c17f8f74f4070afed6eea17f20b2115e445aec", "44b82bc1146a24e5b9853d04c142576b4e8fa7a92f2e30bc364a85d1f75c4de2", "457fcbee4df737d2defc181b9073758d73f54a6cfc1f280533ff48831b39f4a8", "49603e1a6e24104961497ad0c07c799aec1caac7400a6762b687e74c8206677d", "8c2f40b99a8153893793559919a355d7b74649a11e59f411b0b0a1793e160bc0", "e1d897889c3b5a829426b7d52828fb37b28bc181cd598624e65c8be40ee3f7fa"] -traitlets = ["9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", "c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"] -typed-ast = ["132eae51d6ef3ff4a8c47c393a4ef5ebf0d1aecc96880eb5d6c8ceab7017cc9b", "18141c1484ab8784006c839be8b985cfc82a2e9725837b0ecfa0203f71c4e39d", "2baf617f5bbbfe73fd8846463f5aeafc912b5ee247f410700245d68525ec584a", "3d90063f2cbbe39177e9b4d888e45777012652d6110156845b828908c51ae462", "4304b2218b842d610aa1a1d87e1dc9559597969acc62ce717ee4dfeaa44d7eee", "4983ede548ffc3541bae49a82675996497348e55bafd1554dc4e4a5d6eda541a", "5315f4509c1476718a4825f45a203b82d7fdf2a6f5f0c8f166435975b1c9f7d4", "6cdfb1b49d5345f7c2b90d638822d16ba62dc82f7616e9b4caa10b72f3f16649", "7b325f12635598c604690efd7a0197d0b94b7d7778498e76e0710cd582fd1c7a", "8d3b0e3b8626615826f9a626548057c5275a9733512b137984a68ba1598d3d2f", "8f8631160c79f53081bd23446525db0bc4c5616f78d04021e6e434b286493fd7", "912de10965f3dc89da23936f1cc4ed60764f712e5fa603a09dd904f88c996760", "b010c07b975fe853c65d7bbe9d4ac62f1c69086750a574f6292597763781ba18", "c908c10505904c48081a5415a1e295d8403e353e0c14c42b6d67f8f97fae6616", "c94dd3807c0c0610f7c76f078119f4ea48235a953512752b9175f9f98f5ae2bd", "ce65dee7594a84c466e79d7fb7d3303e7295d16a83c22c7c4037071b059e2c21", "eaa9cfcb221a8a4c2889be6f93da141ac777eb8819f077e1d09fb12d00a09a93", "f3376bc31bad66d46d44b4e6522c5c21976bf9bca4ef5987bb2bf727f4506cbb", "f9202fa138544e13a4ec1a6792c35834250a85958fde1251b6a22e07d1260ae7"] -wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] -webencodings = ["a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", "b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"] -widgetsnbextension = ["14b2c65f9940c9a7d3b70adbe713dbd38b5ec69724eebaba034d1036cf3d4740", "fa618be8435447a017fd1bf2c7ae922d0428056cfc7449f7a8641edf76b48265"] -yangify = ["53a6c0a0c99c822028d59b69f4dbe25366ba824a8c23f198340e5a284d676d70", "775aa5203426acd96cd98cd0c8d4b63b4de07db036546d4da623bc7b51a3286c"] -yangson = ["70f54af13295cfd56b8762c8ffe193ffcdaf209ef53bfcb6e3d2ca231c41824d", "f80cfbeb25235ba86ae6fa3ed8a12ba9075c76f56b693070c002316d5845e3df"] +[metadata.files] +appdirs = [ + {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, + {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, +] +appnope = [ + {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, + {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, +] +atomicwrites = [ + {file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, + {file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, +] +attrs = [ + {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, + {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, +] +backcall = [ + {file = "backcall-0.1.0.tar.gz", hash = "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4"}, + {file = "backcall-0.1.0.zip", hash = "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"}, +] +black = [ + {file = "black-19.3b0-py36-none-any.whl", hash = "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf"}, + {file = "black-19.3b0.tar.gz", hash = "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"}, +] +bleach = [ + {file = "bleach-3.1.1-py2.py3-none-any.whl", hash = "sha256:44f69771e2ac81ff30d929d485b7f9919f3ad6d019b6b20c74f3b8687c3f70df"}, + {file = "bleach-3.1.1.tar.gz", hash = "sha256:aa8b870d0f46965bac2c073a93444636b0e1ca74e9777e34f03dd494b8a59d48"}, +] +click = [ + {file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"}, + {file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"}, +] +colorama = [ + {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, + {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, +] +coverage = [ + {file = "coverage-5.0.3-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f"}, + {file = "coverage-5.0.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc"}, + {file = "coverage-5.0.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a"}, + {file = "coverage-5.0.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52"}, + {file = "coverage-5.0.3-cp27-cp27m-win32.whl", hash = "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c"}, + {file = "coverage-5.0.3-cp27-cp27m-win_amd64.whl", hash = "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73"}, + {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68"}, + {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691"}, + {file = "coverage-5.0.3-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301"}, + {file = "coverage-5.0.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf"}, + {file = "coverage-5.0.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3"}, + {file = "coverage-5.0.3-cp35-cp35m-win32.whl", hash = "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"}, + {file = "coverage-5.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0"}, + {file = "coverage-5.0.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2"}, + {file = "coverage-5.0.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894"}, + {file = "coverage-5.0.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf"}, + {file = "coverage-5.0.3-cp36-cp36m-win32.whl", hash = "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477"}, + {file = "coverage-5.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc"}, + {file = "coverage-5.0.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8"}, + {file = "coverage-5.0.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987"}, + {file = "coverage-5.0.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea"}, + {file = "coverage-5.0.3-cp37-cp37m-win32.whl", hash = "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc"}, + {file = "coverage-5.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e"}, + {file = "coverage-5.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb"}, + {file = "coverage-5.0.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37"}, + {file = "coverage-5.0.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d"}, + {file = "coverage-5.0.3-cp38-cp38m-win32.whl", hash = "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954"}, + {file = "coverage-5.0.3-cp38-cp38m-win_amd64.whl", hash = "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e"}, + {file = "coverage-5.0.3-cp39-cp39m-win32.whl", hash = "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40"}, + {file = "coverage-5.0.3-cp39-cp39m-win_amd64.whl", hash = "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af"}, + {file = "coverage-5.0.3.tar.gz", hash = "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef"}, +] +dataclasses = [ + {file = "dataclasses-0.6-py3-none-any.whl", hash = "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f"}, + {file = "dataclasses-0.6.tar.gz", hash = "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"}, +] +decorator = [ + {file = "decorator-4.4.1-py2.py3-none-any.whl", hash = "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"}, + {file = "decorator-4.4.1.tar.gz", hash = "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce"}, +] +defusedxml = [ + {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, + {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, +] +entrypoints = [ + {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, + {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, +] +flake8-import-order = [ + {file = "flake8-import-order-0.18.1.tar.gz", hash = "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"}, + {file = "flake8_import_order-0.18.1-py2.py3-none-any.whl", hash = "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543"}, +] +importlib-metadata = [ + {file = "importlib_metadata-1.5.0-py2.py3-none-any.whl", hash = "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b"}, + {file = "importlib_metadata-1.5.0.tar.gz", hash = "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302"}, +] +ipykernel = [ + {file = "ipykernel-5.1.4-py3-none-any.whl", hash = "sha256:ba8c9e5561f3223fb47ce06ad7925cb9444337ac367341c0c520ffb68ea6d120"}, + {file = "ipykernel-5.1.4.tar.gz", hash = "sha256:7f1f01df22f1229c8879501057877ccaf92a3b01c1d00db708aad5003e5f9238"}, +] +ipython = [ + {file = "ipython-7.12.0-py3-none-any.whl", hash = "sha256:f6689108b1734501d3b59c84427259fd5ac5141afe2e846cfa8598eb811886c9"}, + {file = "ipython-7.12.0.tar.gz", hash = "sha256:d9459e7237e2e5858738ff9c3e26504b79899b58a6d49e574d352493d80684c6"}, +] +ipython-genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] +ipywidgets = [ + {file = "ipywidgets-7.5.1-py2.py3-none-any.whl", hash = "sha256:13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516"}, + {file = "ipywidgets-7.5.1.tar.gz", hash = "sha256:e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"}, +] +jedi = [ + {file = "jedi-0.16.0-py2.py3-none-any.whl", hash = "sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2"}, + {file = "jedi-0.16.0.tar.gz", hash = "sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5"}, +] +jinja2 = [ + {file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"}, + {file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"}, +] +jmespath = [ + {file = "jmespath-0.9.5-py2.py3-none-any.whl", hash = "sha256:695cb76fa78a10663425d5b73ddc5714eb711157e52704d69be03b1a02ba4fec"}, + {file = "jmespath-0.9.5.tar.gz", hash = "sha256:cca55c8d153173e21baa59983015ad0daf603f9cb799904ff057bfb8ff8dc2d9"}, +] +jsonschema = [ + {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, + {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, +] +jupyter = [ + {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"}, + {file = "jupyter-1.0.0.tar.gz", hash = "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"}, + {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"}, +] +jupyter-client = [ + {file = "jupyter_client-6.0.0-py3-none-any.whl", hash = "sha256:ed2490c65f7e0987d1e7b2c4146371d58112489e558b3a835aefb86b7283f930"}, + {file = "jupyter_client-6.0.0.tar.gz", hash = "sha256:1fac6e3be1e797aea33d5cd1cfa568ff1ee71e01180bc89f64b24ee274f1f126"}, +] +jupyter-console = [ + {file = "jupyter_console-6.1.0-py2.py3-none-any.whl", hash = "sha256:b392155112ec86a329df03b225749a0fa903aa80811e8eda55796a40b5e470d8"}, + {file = "jupyter_console-6.1.0.tar.gz", hash = "sha256:6f6ead433b0534909df789ea64f0a14cdf9b6b2360757756f08182be4b9e431b"}, +] +jupyter-core = [ + {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, + {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, +] +lxml = [ + {file = "lxml-4.5.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0701f7965903a1c3f6f09328c1278ac0eee8f56f244e66af79cb224b7ef3801c"}, + {file = "lxml-4.5.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:06d4e0bbb1d62e38ae6118406d7cdb4693a3fa34ee3762238bcb96c9e36a93cd"}, + {file = "lxml-4.5.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5828c7f3e615f3975d48f40d4fe66e8a7b25f16b5e5705ffe1d22e43fb1f6261"}, + {file = "lxml-4.5.0-cp27-cp27m-win32.whl", hash = "sha256:afdb34b715daf814d1abea0317b6d672476b498472f1e5aacbadc34ebbc26e89"}, + {file = "lxml-4.5.0-cp27-cp27m-win_amd64.whl", hash = "sha256:585c0869f75577ac7a8ff38d08f7aac9033da2c41c11352ebf86a04652758b7a"}, + {file = "lxml-4.5.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:8a0ebda56ebca1a83eb2d1ac266649b80af8dd4b4a3502b2c1e09ac2f88fe128"}, + {file = "lxml-4.5.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:fe976a0f1ef09b3638778024ab9fb8cde3118f203364212c198f71341c0715ca"}, + {file = "lxml-4.5.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7bc1b221e7867f2e7ff1933165c0cec7153dce93d0cdba6554b42a8beb687bdb"}, + {file = "lxml-4.5.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d068f55bda3c2c3fcaec24bd083d9e2eede32c583faf084d6e4b9daaea77dde8"}, + {file = "lxml-4.5.0-cp35-cp35m-win32.whl", hash = "sha256:e4aa948eb15018a657702fee0b9db47e908491c64d36b4a90f59a64741516e77"}, + {file = "lxml-4.5.0-cp35-cp35m-win_amd64.whl", hash = "sha256:1f2c4ec372bf1c4a2c7e4bb20845e8bcf8050365189d86806bad1e3ae473d081"}, + {file = "lxml-4.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:5d467ce9c5d35b3bcc7172c06320dddb275fea6ac2037f72f0a4d7472035cea9"}, + {file = "lxml-4.5.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:95e67224815ef86924fbc2b71a9dbd1f7262384bca4bc4793645794ac4200717"}, + {file = "lxml-4.5.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ebec08091a22c2be870890913bdadd86fcd8e9f0f22bcb398abd3af914690c15"}, + {file = "lxml-4.5.0-cp36-cp36m-win32.whl", hash = "sha256:deadf4df349d1dcd7b2853a2c8796593cc346600726eff680ed8ed11812382a7"}, + {file = "lxml-4.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f2b74784ed7e0bc2d02bd53e48ad6ba523c9b36c194260b7a5045071abbb1012"}, + {file = "lxml-4.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fa071559f14bd1e92077b1b5f6c22cf09756c6de7139370249eb372854ce51e6"}, + {file = "lxml-4.5.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:edc15fcfd77395e24543be48871c251f38132bb834d9fdfdad756adb6ea37679"}, + {file = "lxml-4.5.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd52e796fee7171c4361d441796b64df1acfceb51f29e545e812f16d023c4bbc"}, + {file = "lxml-4.5.0-cp37-cp37m-win32.whl", hash = "sha256:90ed0e36455a81b25b7034038e40880189169c308a3df360861ad74da7b68c1a"}, + {file = "lxml-4.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:df533af6f88080419c5a604d0d63b2c33b1c0c4409aba7d0cb6de305147ea8c8"}, + {file = "lxml-4.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4b2c63cc7963aedd08a5f5a454c9f67251b1ac9e22fd9d72836206c42dc2a72"}, + {file = "lxml-4.5.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e5d842c73e4ef6ed8c1bd77806bf84a7cb535f9c0cf9b2c74d02ebda310070e1"}, + {file = "lxml-4.5.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:63dbc21efd7e822c11d5ddbedbbb08cd11a41e0032e382a0fd59b0b08e405a3a"}, + {file = "lxml-4.5.0-cp38-cp38-win32.whl", hash = "sha256:4235bc124fdcf611d02047d7034164897ade13046bda967768836629bc62784f"}, + {file = "lxml-4.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5b3c4b7edd2e770375a01139be11307f04341ec709cf724e0f26ebb1eef12c3"}, + {file = "lxml-4.5.0.tar.gz", hash = "sha256:8620ce80f50d023d414183bf90cc2576c2837b88e00bea3f33ad2630133bbb60"}, +] +markupsafe = [ + {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] +more-itertools = [ + {file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"}, + {file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"}, +] +mypy = [ + {file = "mypy-0.701-cp35-cp35m-macosx_10_6_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:6a9343089f6377e71e20ca734cd8e7ac25d36478a9df580efabfe9059819bf82"}, + {file = "mypy-0.701-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6c9851bc4a23dc1d854d3f5dfd5f20a016f8da86bcdbb42687879bb5f86434b0"}, + {file = "mypy-0.701-cp35-cp35m-win_amd64.whl", hash = "sha256:b8e85956af3fcf043d6f87c91cbe8705073fc67029ba6e22d3468bfee42c4823"}, + {file = "mypy-0.701-cp36-cp36m-macosx_10_6_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:56a16df3e0abb145d8accd5dbb70eba6c4bd26e2f89042b491faa78c9635d1e2"}, + {file = "mypy-0.701-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5bbc86374f04a3aa817622f98e40375ccb28c4836f36b66706cf3c6ccce86eda"}, + {file = "mypy-0.701-cp36-cp36m-win_amd64.whl", hash = "sha256:bbf643528e2a55df2c1587008d6e3bda5c0445f1240dfa85129af22ae16d7a9a"}, + {file = "mypy-0.701-cp37-cp37m-macosx_10_6_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:b9a0af8fae490306bc112229000aa0c2ccc837b49d29a5c42e088c132a2334dd"}, + {file = "mypy-0.701-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c46ab3438bd21511db0f2c612d89d8344154c0c9494afc7fbc932de514cf8d15"}, + {file = "mypy-0.701-cp37-cp37m-win_amd64.whl", hash = "sha256:2afe51527b1f6cdc4a5f34fc90473109b22bf7f21086ba3e9451857cf11489e6"}, + {file = "mypy-0.701-py3-none-any.whl", hash = "sha256:f7a83d6bd805855ef83ec605eb01ab4fa42bcef254b13631e451cbb44914a9b0"}, + {file = "mypy-0.701.tar.gz", hash = "sha256:5764f10d27b2e93c84f70af5778941b8f4aa1379b2430f85c827e0f5464e8714"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +nbconvert = [ + {file = "nbconvert-5.6.1-py2.py3-none-any.whl", hash = "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee"}, + {file = "nbconvert-5.6.1.tar.gz", hash = "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523"}, +] +nbformat = [ + {file = "nbformat-5.0.4-py3-none-any.whl", hash = "sha256:f4bbbd8089bd346488f00af4ce2efb7f8310a74b2058040d075895429924678c"}, + {file = "nbformat-5.0.4.tar.gz", hash = "sha256:562de41fc7f4f481b79ab5d683279bf3a168858268d4387b489b7b02be0b324a"}, +] +nbval = [ + {file = "nbval-0.9.5-py2.py3-none-any.whl", hash = "sha256:10aced0f9a28893e608c2704b2e8bdcbc57635db9640d454f85fe0545fda5002"}, + {file = "nbval-0.9.5.tar.gz", hash = "sha256:e8727bb54ae844368fd17a40c15f3bf2ae64be6ec3a9ac1433e0155ddeb902f6"}, +] +notebook = [ + {file = "notebook-6.0.3-py3-none-any.whl", hash = "sha256:3edc616c684214292994a3af05eaea4cc043f6b4247d830f3a2f209fa7639a80"}, + {file = "notebook-6.0.3.tar.gz", hash = "sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48"}, +] +packaging = [ + {file = "packaging-20.1-py2.py3-none-any.whl", hash = "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73"}, + {file = "packaging-20.1.tar.gz", hash = "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"}, +] +pandocfilters = [ + {file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"}, +] +parso = [ + {file = "parso-0.6.1-py2.py3-none-any.whl", hash = "sha256:951af01f61e6dccd04159042a0706a31ad437864ec6e25d0d7a96a9fbb9b0095"}, + {file = "parso-0.6.1.tar.gz", hash = "sha256:56b2105a80e9c4df49de85e125feb6be69f49920e121406f15e7acde6c9dfc57"}, +] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +prometheus-client = [ + {file = "prometheus_client-0.7.1.tar.gz", hash = "sha256:71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, + {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, +] +ptyprocess = [ + {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, + {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, +] +py = [ + {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, + {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, +] +pycodestyle = [ + {file = "pycodestyle-2.5.0-py2.py3-none-any.whl", hash = "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56"}, + {file = "pycodestyle-2.5.0.tar.gz", hash = "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"}, +] +pydocstyle = [ + {file = "pydocstyle-5.0.2-py3-none-any.whl", hash = "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586"}, + {file = "pydocstyle-5.0.2.tar.gz", hash = "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5"}, +] +pyflakes = [ + {file = "pyflakes-2.1.1-py2.py3-none-any.whl", hash = "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0"}, + {file = "pyflakes-2.1.1.tar.gz", hash = "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"}, +] +pygments = [ + {file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"}, + {file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"}, +] +pylama = [ + {file = "pylama-7.7.1-py2.py3-none-any.whl", hash = "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"}, + {file = "pylama-7.7.1.tar.gz", hash = "sha256:9bae53ef9c1a431371d6a8dca406816a60d547147b60a4934721898f553b7d8f"}, +] +pyparsing = [ + {file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"}, + {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"}, +] +pyrsistent = [ + {file = "pyrsistent-0.15.7.tar.gz", hash = "sha256:cdc7b5e3ed77bed61270a47d35434a30617b9becdf2478af76ad2c6ade307280"}, +] +pytest = [ + {file = "pytest-4.6.9-py2.py3-none-any.whl", hash = "sha256:c77a5f30a90e0ce24db9eaa14ddfd38d4afb5ea159309bdd2dae55b931bc9324"}, + {file = "pytest-4.6.9.tar.gz", hash = "sha256:19e8f75eac01dd3f211edd465b39efbcbdc8fc5f7866d7dd49fedb30d8adf339"}, +] +pytest-cov = [ + {file = "pytest-cov-2.8.1.tar.gz", hash = "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b"}, + {file = "pytest_cov-2.8.1-py2.py3-none-any.whl", hash = "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"}, +] +pytest-pythonpath = [ + {file = "pytest-pythonpath-0.7.3.tar.gz", hash = "sha256:63fc546ace7d2c845c1ee289e8f7a6362c2b6bae497d10c716e58e253e801d62"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +pywin32 = [ + {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"}, + {file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"}, + {file = "pywin32-227-cp35-cp35m-win32.whl", hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"}, + {file = "pywin32-227-cp35-cp35m-win_amd64.whl", hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"}, + {file = "pywin32-227-cp36-cp36m-win32.whl", hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"}, + {file = "pywin32-227-cp36-cp36m-win_amd64.whl", hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"}, + {file = "pywin32-227-cp37-cp37m-win32.whl", hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"}, + {file = "pywin32-227-cp37-cp37m-win_amd64.whl", hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"}, + {file = "pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"}, + {file = "pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"}, + {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"}, + {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"}, +] +pywinpty = [ + {file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"}, + {file = "pywinpty-0.5.7-cp27-cp27m-win_amd64.whl", hash = "sha256:1e525a4de05e72016a7af27836d512db67d06a015aeaf2fa0180f8e6a039b3c2"}, + {file = "pywinpty-0.5.7-cp35-cp35m-win32.whl", hash = "sha256:2740eeeb59297593a0d3f762269b01d0285c1b829d6827445fcd348fb47f7e70"}, + {file = "pywinpty-0.5.7-cp35-cp35m-win_amd64.whl", hash = "sha256:33df97f79843b2b8b8bc5c7aaf54adec08cc1bae94ee99dfb1a93c7a67704d95"}, + {file = "pywinpty-0.5.7-cp36-cp36m-win32.whl", hash = "sha256:e854211df55d107f0edfda8a80b39dfc87015bef52a8fe6594eb379240d81df2"}, + {file = "pywinpty-0.5.7-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd838de92de1d4ebf0dce9d4d5e4fc38d0b7b1de837947a18b57a882f219139"}, + {file = "pywinpty-0.5.7-cp37-cp37m-win32.whl", hash = "sha256:5fb2c6c6819491b216f78acc2c521b9df21e0f53b9a399d58a5c151a3c4e2a2d"}, + {file = "pywinpty-0.5.7-cp37-cp37m-win_amd64.whl", hash = "sha256:dd22c8efacf600730abe4a46c1388355ce0d4ab75dc79b15d23a7bd87bf05b48"}, + {file = "pywinpty-0.5.7-cp38-cp38-win_amd64.whl", hash = "sha256:8fc5019ff3efb4f13708bd3b5ad327589c1a554cb516d792527361525a7cb78c"}, + {file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"}, +] +pyxb = [ + {file = "PyXB-1.2.6.tar.gz", hash = "sha256:2a00f38dd1d87b88f92d79bc5a09718d730419b88e814545f472bbd5a3bf27b4"}, +] +pyzmq = [ + {file = "pyzmq-19.0.0-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:3f12ce1e9cc9c31497bd82b207e8e86ccda9eebd8c9f95053aae46d15ccd2196"}, + {file = "pyzmq-19.0.0-cp27-cp27m-win32.whl", hash = "sha256:e8e4efb52ec2df8d046395ca4c84ae0056cf507b2f713ec803c65a8102d010de"}, + {file = "pyzmq-19.0.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f5b6d015587a1d6f582ba03b226a9ddb1dfb09878b3be04ef48b01b7d4eb6b2a"}, + {file = "pyzmq-19.0.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:bb10361293d96aa92be6261fa4d15476bca56203b3a11c62c61bd14df0ef89ba"}, + {file = "pyzmq-19.0.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4557d5e036e6d85715b4b9fdb482081398da1d43dc580d03db642b91605b409f"}, + {file = "pyzmq-19.0.0-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:84b91153102c4bcf5d0f57d1a66a0f03c31e9e6525a5f656f52fc615a675c748"}, + {file = "pyzmq-19.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6aaaf90b420dc40d9a0e1996b82c6a0ff91d9680bebe2135e67c9e6d197c0a53"}, + {file = "pyzmq-19.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ad48865a29efa8a0cecf266432ea7bc34e319954e55cf104be0319c177e6c8f5"}, + {file = "pyzmq-19.0.0-cp35-cp35m-win32.whl", hash = "sha256:32234c21c5e0a767c754181c8112092b3ddd2e2a36c3f76fc231ced817aeee47"}, + {file = "pyzmq-19.0.0-cp35-cp35m-win_amd64.whl", hash = "sha256:f37c29da2a5b0c5e31e6f8aab885625ea76c807082f70b2d334d3fd573c3100a"}, + {file = "pyzmq-19.0.0-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:1e076ad5bd3638a18c376544d32e0af986ca10d43d4ce5a5d889a8649f0d0a3d"}, + {file = "pyzmq-19.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f4d558bc5668d2345773a9ff8c39e2462dafcb1f6772a2e582fbced389ce527f"}, + {file = "pyzmq-19.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f562dab21c03c7aa061f63b147a595dbe1006bf4f03213272fc9f7d5baec791"}, + {file = "pyzmq-19.0.0-cp36-cp36m-win32.whl", hash = "sha256:7f7e7b24b1d392bb5947ba91c981e7d1a43293113642e0d8870706c8e70cdc71"}, + {file = "pyzmq-19.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:75238d3c16cab96947705d5709187a49ebb844f54354cdf0814d195dd4c045de"}, + {file = "pyzmq-19.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb3b7156ef6b1a119e68fbe3a54e0a0c40ecacc6b7838d57dd708c90b62a06dc"}, + {file = "pyzmq-19.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:a99ae601b4f6917985e9bb071549e30b6f93c72f5060853e197bdc4b7d357e5f"}, + {file = "pyzmq-19.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:242d949eb6b10197cda1d1cec377deab1d5324983d77e0d0bf9dc5eb6d71a6b4"}, + {file = "pyzmq-19.0.0-cp37-cp37m-win32.whl", hash = "sha256:a49fd42a29c1cc1aa9f461c5f2f5e0303adba7c945138b35ee7f4ab675b9f754"}, + {file = "pyzmq-19.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5f10a31f288bf055be76c57710807a8f0efdb2b82be6c2a2b8f9a61f33a40cea"}, + {file = "pyzmq-19.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26f4ae420977d2a8792d7c2d7bda43128b037b5eeb21c81951a94054ad8b8843"}, + {file = "pyzmq-19.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:944f6bb5c63140d76494467444fd92bebd8674236837480a3c75b01fe17df1ab"}, + {file = "pyzmq-19.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b08e425cf93b4e018ab21dc8fdbc25d7d0502a23cc4fea2380010cf8cf11e462"}, + {file = "pyzmq-19.0.0-cp38-cp38-win32.whl", hash = "sha256:a1f957c20c9f51d43903881399b078cddcf710d34a2950e88bce4e494dcaa4d1"}, + {file = "pyzmq-19.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:bd1a769d65257a7a12e2613070ca8155ee348aa9183f2aadf1c8b8552a5510f5"}, + {file = "pyzmq-19.0.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:0bbc1728fe4314b4ca46249c33873a390559edac7c217ec7001b5e0c34a8fb7f"}, + {file = "pyzmq-19.0.0-pp36-pypy36_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5e071b834051e9ecb224915398f474bfad802c2fff883f118ff5363ca4ae3edf"}, + {file = "pyzmq-19.0.0.tar.gz", hash = "sha256:5e1f65e576ab07aed83f444e201d86deb01cd27dcf3f37c727bc8729246a60a8"}, +] +qtconsole = [ + {file = "qtconsole-4.6.0-py2.py3-none-any.whl", hash = "sha256:4de25b8895957d23ceacf2526b6f0a76da4e60e60115611930d387c853f3cb08"}, + {file = "qtconsole-4.6.0.tar.gz", hash = "sha256:654f423662e7dfe6a9b26fac8ec76aedcf742c339909ac49f1f0c1a1b744bcd1"}, +] +send2trash = [ + {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"}, + {file = "Send2Trash-1.5.0.tar.gz", hash = "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2"}, +] +six = [ + {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, + {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, +] +snowballstemmer = [ + {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, + {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, +] +terminado = [ + {file = "terminado-0.8.3-py2.py3-none-any.whl", hash = "sha256:a43dcb3e353bc680dd0783b1d9c3fc28d529f190bc54ba9a229f72fe6e7a54d7"}, + {file = "terminado-0.8.3.tar.gz", hash = "sha256:4804a774f802306a7d9af7322193c5390f1da0abb429e082a10ef1d46e6fb2c2"}, +] +testpath = [ + {file = "testpath-0.4.4-py2.py3-none-any.whl", hash = "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4"}, + {file = "testpath-0.4.4.tar.gz", hash = "sha256:60e0a3261c149755f4399a1fff7d37523179a70fdc3abdf78de9fc2604aeec7e"}, +] +toml = [ + {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, + {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, + {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, +] +tornado = [ + {file = "tornado-6.0.3-cp35-cp35m-win32.whl", hash = "sha256:c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"}, + {file = "tornado-6.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60"}, + {file = "tornado-6.0.3-cp36-cp36m-win32.whl", hash = "sha256:4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281"}, + {file = "tornado-6.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c"}, + {file = "tornado-6.0.3-cp37-cp37m-win32.whl", hash = "sha256:559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5"}, + {file = "tornado-6.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7"}, + {file = "tornado-6.0.3.tar.gz", hash = "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9"}, +] +traitlets = [ + {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, + {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, +] +typed-ast = [ + {file = "typed-ast-1.3.5.tar.gz", hash = "sha256:5315f4509c1476718a4825f45a203b82d7fdf2a6f5f0c8f166435975b1c9f7d4"}, + {file = "typed_ast-1.3.5-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:eaa9cfcb221a8a4c2889be6f93da141ac777eb8819f077e1d09fb12d00a09a93"}, + {file = "typed_ast-1.3.5-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:4304b2218b842d610aa1a1d87e1dc9559597969acc62ce717ee4dfeaa44d7eee"}, + {file = "typed_ast-1.3.5-cp34-cp34m-win32.whl", hash = "sha256:912de10965f3dc89da23936f1cc4ed60764f712e5fa603a09dd904f88c996760"}, + {file = "typed_ast-1.3.5-cp34-cp34m-win_amd64.whl", hash = "sha256:6cdfb1b49d5345f7c2b90d638822d16ba62dc82f7616e9b4caa10b72f3f16649"}, + {file = "typed_ast-1.3.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4983ede548ffc3541bae49a82675996497348e55bafd1554dc4e4a5d6eda541a"}, + {file = "typed_ast-1.3.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ce65dee7594a84c466e79d7fb7d3303e7295d16a83c22c7c4037071b059e2c21"}, + {file = "typed_ast-1.3.5-cp35-cp35m-win32.whl", hash = "sha256:2baf617f5bbbfe73fd8846463f5aeafc912b5ee247f410700245d68525ec584a"}, + {file = "typed_ast-1.3.5-cp35-cp35m-win_amd64.whl", hash = "sha256:c94dd3807c0c0610f7c76f078119f4ea48235a953512752b9175f9f98f5ae2bd"}, + {file = "typed_ast-1.3.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d90063f2cbbe39177e9b4d888e45777012652d6110156845b828908c51ae462"}, + {file = "typed_ast-1.3.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f3376bc31bad66d46d44b4e6522c5c21976bf9bca4ef5987bb2bf727f4506cbb"}, + {file = "typed_ast-1.3.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c908c10505904c48081a5415a1e295d8403e353e0c14c42b6d67f8f97fae6616"}, + {file = "typed_ast-1.3.5-cp36-cp36m-win32.whl", hash = "sha256:8f8631160c79f53081bd23446525db0bc4c5616f78d04021e6e434b286493fd7"}, + {file = "typed_ast-1.3.5-cp36-cp36m-win_amd64.whl", hash = "sha256:7b325f12635598c604690efd7a0197d0b94b7d7778498e76e0710cd582fd1c7a"}, + {file = "typed_ast-1.3.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:132eae51d6ef3ff4a8c47c393a4ef5ebf0d1aecc96880eb5d6c8ceab7017cc9b"}, + {file = "typed_ast-1.3.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18141c1484ab8784006c839be8b985cfc82a2e9725837b0ecfa0203f71c4e39d"}, + {file = "typed_ast-1.3.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8d3b0e3b8626615826f9a626548057c5275a9733512b137984a68ba1598d3d2f"}, + {file = "typed_ast-1.3.5-cp37-cp37m-win32.whl", hash = "sha256:f9202fa138544e13a4ec1a6792c35834250a85958fde1251b6a22e07d1260ae7"}, + {file = "typed_ast-1.3.5-cp37-cp37m-win_amd64.whl", hash = "sha256:b010c07b975fe853c65d7bbe9d4ac62f1c69086750a574f6292597763781ba18"}, +] +wcwidth = [ + {file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"}, + {file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +widgetsnbextension = [ + {file = "widgetsnbextension-3.5.1-py2.py3-none-any.whl", hash = "sha256:bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd"}, + {file = "widgetsnbextension-3.5.1.tar.gz", hash = "sha256:079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7"}, +] +yangify = [ + {file = "yangify-0.1.2-py3-none-any.whl", hash = "sha256:c1f3e546faf93338183e6fdb59d15064676026005bd68d996a100021e1d805b4"}, + {file = "yangify-0.1.2.tar.gz", hash = "sha256:1ac34229c0c1df4bf5de1293fb55b20adb1c8e2cc9504292cef2c365e5686104"}, +] +yangson = [ + {file = "yangson-1.3.45-py3-none-any.whl", hash = "sha256:84c48709cd18e595d9eaa61fc3910b0d73b51f0724fc6dec9d57e7c6aff63a44"}, + {file = "yangson-1.3.45.tar.gz", hash = "sha256:a4a879e1ac0e9871e29e17b123570018712a41d6aa6f560435cbbf6dc199f7bc"}, +] +zipp = [ + {file = "zipp-3.0.0-py3-none-any.whl", hash = "sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2"}, + {file = "zipp-3.0.0.tar.gz", hash = "sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a"}, +] diff --git a/pyproject.toml b/pyproject.toml index b1fd255..5af5c6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ python = "^3.6" jmespath = "^0.9.3" lxml = "^4.3" click = "^7.0" -yangify = "^0.1.1" +yangify = "^0.1.2" [tool.poetry.dev-dependencies] pytest = "^4.2" From 405834ec67c52f1f0dad7ed4b0ca10d43f283dd9 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Wed, 26 Feb 2020 11:29:37 -0600 Subject: [PATCH 09/19] Add jupyter var to allow multiple containers on the same host during testing --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4a6ed9e..c51db90 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ ifeq (${PYTHON}, ) override PYTHON=3.6 endif -DOCKER=docker run -it --rm -p 8888:8888 -v ${PWD}:/ntc_rosetta ntc_rosetta-${PYTHON}:latest +DOCKER=docker run -it --rm -v ${PWD}:/ntc_rosetta ntc_rosetta-${PYTHON}:latest +JUPYTER=docker run -it --rm -p 8888:8888 -v ${PWD}:/ntc_rosetta ntc_rosetta-${PYTHON}:latest YANG_VENDORED_BASE_PATH=ntc_rosetta/yang @@ -77,7 +78,7 @@ docs: .PHONY: jupyter jupyter: - ${DOCKER} \ + ${JUPYTER} \ jupyter notebook --allow-root --ip=0.0.0.0 --NotebookApp.token='' .PHONY: tests From 58ec5811587eafd2aa5c9dd2d1af28eca3525d4c Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Wed, 26 Feb 2020 11:30:23 -0600 Subject: [PATCH 10/19] Correct DNS tests --- .../translate/ios/test_case_1/data.json | 11 ++- .../translate/ios/test_case_1/res_merge | 6 +- .../translate/ios/test_case_1/test_case.py | 37 +++++++++- .../translate/ios/test_case_2/__init__.py | 0 .../translate/ios/test_case_2/data.json | 69 +++++++++++++++++++ .../translate/ios/test_case_2/res_merge | 6 ++ .../translate/ios/test_case_2/test_case.py | 14 ++++ 7 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/__init__.py create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/data.json create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/test_case.py diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/data.json b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/data.json index f302383..54a3954 100644 --- a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/data.json +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/data.json @@ -17,6 +17,13 @@ "address": "8.8.8.8", "port": 53 } + }, + { + "address": "8.8.4.4", + "config": { + "address": "8.8.4.4", + "port": 53 + } } ] } @@ -63,7 +70,7 @@ "username": "developer", "config": { "username": "developer", - "password-hashed": "$9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2", + "password-hashed": "$1$pdQG$blOvq4Ey0SJCv3n001vnj.", "role": "15" } }, @@ -71,7 +78,7 @@ "username": "cisco", "config": { "username": "cisco", - "password-hashed": "$9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls", + "password-hashed": "$8$FiJQlYEnyjCvkk$GkMYqqmLNP2YNoAFBcod4Tg7/Sy6e58Q3a8umokmlJI", "role": "15" } }, diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge index c14097a..947955b 100644 --- a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge @@ -1,10 +1,10 @@ hostname csr1000v-1 -ip name-server 8.8.8.8 +ip name-server 8.8.8.8 8.8.4.4 ntp server 2610:20:6F96:96::6 ntp server 129.6.15.29 ip ssh version 2 ip ssh time-out 60 -username developer privilege 15 secret 9 $9$fhUXi6Xg438iAE$..VhXRCHiDQy3K2zmZUl9iZLbQJ9wpUtQZwQxSRESc2 -username cisco privilege 15 secret 9 $9$COf3Q4xfzT.JxE$L3hvSkDv874Qrh8Hzdv/rPQjLNOjreYG2ocffHG7rls +username developer privilege 15 secret 5 $1$pdQG$blOvq4Ey0SJCv3n001vnj. +username cisco privilege 15 secret 8 $8$FiJQlYEnyjCvkk$GkMYqqmLNP2YNoAFBcod4Tg7/Sy6e58Q3a8umokmlJI username root privilege 15 secret 9 $9$FfUDIPBFcs03AE$MyLIWEuiRle8p3yGflAGTcrJA6BUUh/oWtyyfoMQXSI username newuser80 password 0 Yu76_87AF diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py index 9815340..46b7fe5 100644 --- a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/test_case.py @@ -1,5 +1,11 @@ +""" +- Multiple DNS servers +- Multiple users +- Check bad ssh hash +""" +import ntc_rosetta from tests.models.test_models import parse_path, translate - +from yangson.exceptions import UnexpectedInput import pytest @@ -8,3 +14,32 @@ def test_translate(action: str) -> None: """Run a merge test with the provided action.""" test_data = parse_path(__file__) translate(test_data.org, test_data.driver, test_data.path, action) + + +def test_ios_translate_ssh_hash_fail() -> None: + """Assert that a supported hash type is present""" + fail = { + "openconfig-system:system": { + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + # $7 is an in valid value + "password-hashed": "$7$foobar", + "role": "15", + }, + } + ] + } + } + } + } + } + driver_class = ntc_rosetta.get_driver("ios", "openconfig") + device = driver_class() + with pytest.raises(UnexpectedInput): + device.translate(fail) diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/__init__.py b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/data.json b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/data.json new file mode 100644 index 0000000..2b54b69 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/data.json @@ -0,0 +1,69 @@ +{ + "openconfig-system:system": { + "config": { + "hostname": "csr1000v-1" + }, + "clock": { + "config": { + "timezone-name": "CST -6 0" + } + }, + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + } + ] + } + }, + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + } + ] + } + }, + "ssh-server": { + "config": { + "enable": true, + "protocol-version": "V2", + "timeout": 60 + } + }, + "telnet-server": { + "config": { + "enable": true + } + }, + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + "password-hashed": "$1$pdQG$blOvq4Ey0SJCv3n001vnj.", + "role": "15" + } + } + ] + } + } + } + } + } \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge new file mode 100644 index 0000000..c308f87 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge @@ -0,0 +1,6 @@ +hostname csr1000v-1 +ip name-server 8.8.8.8 +ntp server 2610:20:6F96:96::6 +ip ssh version 2 +ip ssh time-out 60 +username developer privilege 15 secret 5 $1$pdQG$blOvq4Ey0SJCv3n001vnj. diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/test_case.py b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/test_case.py new file mode 100644 index 0000000..6a79f46 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/test_case.py @@ -0,0 +1,14 @@ +""" +- Single DNS server +- Single user +""" + +from tests.models.test_models import parse_path, translate +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_translate(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + translate(test_data.org, test_data.driver, test_data.path, action) From 740dbf45c14b8e418259367087730baf62ac865c Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Wed, 26 Feb 2020 11:30:43 -0600 Subject: [PATCH 11/19] Add IOS helper module --- ntc_rosetta/helpers/ios.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ntc_rosetta/helpers/ios.py diff --git a/ntc_rosetta/helpers/ios.py b/ntc_rosetta/helpers/ios.py new file mode 100644 index 0000000..3c62093 --- /dev/null +++ b/ntc_rosetta/helpers/ios.py @@ -0,0 +1,4 @@ +"""IOS helper consts and methods""" + +# Map hash string to hash type, see PR https://github.com/networktocode/ntc-rosetta/pull/40 +HASH_MAP = {"1": 5, "8": 8, "9": 9} From 9d8edf07866db2ccb13b6a8532b9aad2016ec6e2 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Wed, 26 Feb 2020 11:31:00 -0600 Subject: [PATCH 12/19] Correct DNS translation --- .../ios/openconfig_system/system.py | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py index a72c491..2ec575c 100644 --- a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py @@ -1,8 +1,10 @@ import re +from ntc_rosetta.helpers.ios import HASH_MAP + from yangify.translator import Translator, TranslatorData -from yangson.exceptions import InstanceValueError +from yangson.exceptions import InstanceValueError, UnexpectedInput class DnsConfig(Translator): @@ -14,20 +16,27 @@ class DnsServerConfig(Translator): class Yangify(TranslatorData): path = "/openconfig-system:system/dns/servers/server/config" + def address(self, value: str): + self.yy.extra["dns_servers"].append(value) -class DnsServer(Translator): - config = DnsServerConfig +class DnsServer(Translator): class Yangify(TranslatorData): path = "/openconfig-system:system/dns/servers/server" def pre_process_list(self) -> None: + self.extra = {"dns_servers": []} if self.to_remove: for element in self.to_remove: self.result.add_command(f"no ip name-server {element['address']}") - def address(self, value: str): - self.yy.result.add_command(f"ip name-server {value}") + def post_process_list(self): + if self.extra["dns_servers"]: + self.result.add_command( + f"ip name-server {' '.join(self.extra['dns_servers'])}" + ) + + config = DnsServerConfig class DnsServers(Translator): @@ -36,9 +45,6 @@ class DnsServers(Translator): class Yangify(TranslatorData): path = "/openconfig-system:system/dns/servers" - def pre_process(self) -> None: - pass - class Dns(Translator): class Yangify(TranslatorData): @@ -136,7 +142,10 @@ def post_process(self) -> None: match = re.match(r"^\$(\d).*", password_hash) password_hash_str = "" if match: - hash_type = match.groups()[0] + hash_type_str = match.groups()[0] + hash_type = HASH_MAP.get(hash_type_str) + if not hash_type: + raise UnexpectedInput("Unsupported hash type") password_hash_str = f"secret {hash_type} {password_hash}" ssh_key = self.extra.get("ssh_key") ssh_key_str = f"ssh_key {ssh_key}" if ssh_key else "" # noqa From 9e5669900609e71aab5f389ea3a03dc234a93a4b Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Wed, 26 Feb 2020 14:48:58 -0600 Subject: [PATCH 13/19] Add system/clock parse/translate --- .../ios/openconfig_system/system.py | 29 +++++++++++++++---- .../ios/openconfig_system/system.py | 17 +++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py index 9fb332d..88af16d 100644 --- a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Iterator, Tuple +from typing import Any, Dict, Iterator, Optional, Tuple from ntc_rosetta.helpers import json_helpers as jh @@ -274,12 +274,13 @@ def hostname(self) -> str: else: return None - def domain_name(self) -> str: + def domain_name(self) -> Optional[str]: + # Cisco has used domain-name with and without a dash v = jh.query('ip."domain-name"."#text"', self.yy.native) - if v is not None: + if not v: + v = jh.query('ip.domain.name."#text"', self.yy.native) + if v: return str(v) - else: - return None def login_banner(self) -> str: # TODO @@ -290,6 +291,23 @@ def motd_banner(self) -> str: return None +class ClockConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/clock/config" + + def timezone_name(self) -> str: + v = jh.query('clock.timezone."#text"', self.yy.native) + if v: + return str(v) + + +class Clock(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/clock" + + config = ClockConfig + + class System(Parser): config = SystemConfig clock = Clock @@ -298,6 +316,7 @@ class System(Parser): ssh_server = SshServer telnet_server = TelnetServer aaa = Aaa + clock = Clock class Yangify(ParserData): path = "/openconfig-system:system" diff --git a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py index 2ec575c..8e7929a 100644 --- a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py @@ -221,6 +221,22 @@ def hostname(self, value: str) -> None: self.yy.result.add_command(f"hostname {value}") +class ClockConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/clock/config" + + def timezone_name(self, value: str) -> None: + self.yy.result.add_command(f"clock timezone {value}") + + + +class Clock(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/clock" + + config = ClockConfig + + class System(Translator): class Yangify(TranslatorData): path = "/openconfig-system:system" @@ -236,3 +252,4 @@ def pre_process(self): aaa = Aaa ntp = Ntp dns = Dns + clock = Clock From c671e83eac0e96c21350acafdc907d3503b398a3 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Wed, 26 Feb 2020 14:49:08 -0600 Subject: [PATCH 14/19] Fix system/clock tests --- .../data/openconfig_system/parse/ios/config/result.json | 3 ++- .../data/openconfig_system/translate/ios/test_case_1/res_merge | 1 + .../data/openconfig_system/translate/ios/test_case_2/res_merge | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json b/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json index 1b4fe4a..005fbc0 100644 --- a/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json +++ b/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json @@ -1,7 +1,8 @@ { "openconfig-system:system": { "config": { - "hostname": "csr1000v-1" + "hostname": "csr1000v-1", + "domain-name": "cisco.com" }, "clock": { "config": { diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge index 947955b..5a768e6 100644 --- a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_1/res_merge @@ -1,4 +1,5 @@ hostname csr1000v-1 +clock timezone CST -6 0 ip name-server 8.8.8.8 8.8.4.4 ntp server 2610:20:6F96:96::6 ntp server 129.6.15.29 diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge index c308f87..ba5d732 100644 --- a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_2/res_merge @@ -1,4 +1,5 @@ hostname csr1000v-1 +clock timezone CST -6 0 ip name-server 8.8.8.8 ntp server 2610:20:6F96:96::6 ip ssh version 2 From c1631ab9d6685f6a6f55b21a176d866f0f93a3ec Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Thu, 27 Feb 2020 11:40:54 -0600 Subject: [PATCH 15/19] Update IOS docs --- docs/tutorials/ios_merging.ipynb | 20 ++-- docs/tutorials/ios_navigating_data.ipynb | 4 +- docs/tutorials/ios_parsing.ipynb | 12 ++- docs/tutorials/ios_smart_diff.ipynb | 16 +-- docs/tutorials/ios_translate.ipynb | 118 +++++++++++++++++++++-- 5 files changed, 140 insertions(+), 30 deletions(-) diff --git a/docs/tutorials/ios_merging.ipynb b/docs/tutorials/ios_merging.ipynb index e43c474..438b1ed 100644 --- a/docs/tutorials/ios_merging.ipynb +++ b/docs/tutorials/ios_merging.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -163,7 +163,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -180,7 +180,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -190,7 +190,7 @@ " 'config': {'vlan-id': 20, 'name': 'dev', 'status': 'SUSPENDED'}}" ] }, - "execution_count": 5, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -208,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -232,7 +232,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -248,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -290,7 +290,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/ios_navigating_data.ipynb b/docs/tutorials/ios_navigating_data.ipynb index 80d5101..9bf0070 100644 --- a/docs/tutorials/ios_navigating_data.ipynb +++ b/docs/tutorials/ios_navigating_data.ipynb @@ -40,7 +40,9 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [ { "name": "stdout", diff --git a/docs/tutorials/ios_parsing.ipynb b/docs/tutorials/ios_parsing.ipynb index 5c24c9e..a947ef8 100644 --- a/docs/tutorials/ios_parsing.ipynb +++ b/docs/tutorials/ios_parsing.ipynb @@ -45,7 +45,9 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [ { "name": "stdout", @@ -106,7 +108,9 @@ { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ "parsed = ios_driver.parse(native={\"dev_conf\": config})" @@ -122,7 +126,9 @@ { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [ { "name": "stdout", diff --git a/docs/tutorials/ios_smart_diff.ipynb b/docs/tutorials/ios_smart_diff.ipynb index 2394a80..783fc4f 100755 --- a/docs/tutorials/ios_smart_diff.ipynb +++ b/docs/tutorials/ios_smart_diff.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -128,8 +128,10 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, + "execution_count": 11, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ "parsed_candidate = ios_processor.parse(native={\"dev_conf\": candidate_config})\n", @@ -145,7 +147,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 12, "metadata": {}, "outputs": [ { diff --git a/docs/tutorials/ios_translate.ipynb b/docs/tutorials/ios_translate.ipynb index 03f7492..df837f2 100644 --- a/docs/tutorials/ios_translate.ipynb +++ b/docs/tutorials/ios_translate.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -32,11 +32,94 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ - "data = {\n", + "system = {\n", + " \"openconfig-system:system\": {\n", + " \"config\": {\n", + " \"hostname\": \"csr1000v-1\"\n", + " },\n", + " \"clock\": {\n", + " \"config\": {\n", + " \"timezone-name\": \"CST -6 0\"\n", + " }\n", + " },\n", + " \"dns\": {\n", + " \"servers\": {\n", + " \"server\": [\n", + " {\n", + " \"address\": \"8.8.8.8\",\n", + " \"config\": {\n", + " \"address\": \"8.8.8.8\",\n", + " \"port\": 53\n", + " }\n", + " },\n", + " {\n", + " \"address\": \"8.8.4.4\",\n", + " \"config\": {\n", + " \"address\": \"8.8.4.4\",\n", + " \"port\": 53\n", + " }\n", + " },\n", + " {\n", + " \"address\": \"2620:119:35::35\",\n", + " \"config\": {\n", + " \"address\": \"2620:119:35::35\",\n", + " \"port\": 53\n", + " }\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " \"ntp\": {\n", + " \"config\": {\n", + " \"enabled\": True,\n", + " \"enable-ntp-auth\": False\n", + " },\n", + " \"servers\": {\n", + " \"server\": [\n", + " {\n", + " \"address\": \"2610:20:6F96:96::6\",\n", + " \"config\": {\n", + " \"address\": \"2610:20:6F96:96::6\"\n", + " }\n", + " }\n", + " ]\n", + " }\n", + " },\n", + " \"ssh-server\": {\n", + " \"config\": {\n", + " \"enable\": True,\n", + " \"protocol-version\": \"V2\",\n", + " \"timeout\": 60\n", + " }\n", + " },\n", + " \"telnet-server\": {\n", + " \"config\": {\n", + " \"enable\": True\n", + " }\n", + " },\n", + " \"aaa\": {\n", + " \"authentication\": {\n", + " \"users\": {\n", + " \"user\": [\n", + " {\n", + " \"username\": \"developer\",\n", + " \"config\": {\n", + " \"username\": \"developer\",\n", + " \"password-hashed\": \"$1$pdQG$blOvq4Ey0SJCv3n001vnj.\",\n", + " \"role\": \"15\"\n", + " }\n", + " }\n", + " ]\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "interfaces = {\n", " \"openconfig-interfaces:interfaces\": {\n", " \"interface\": [\n", " {\n", @@ -106,7 +189,9 @@ " }\n", " }\n", " ]\n", - " },\n", + " }\n", + "}\n", + "nw_instance = {\n", " \"openconfig-network-instance:network-instances\": {\n", " \"network-instance\": [\n", " {\n", @@ -144,16 +229,20 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "There is one \"gotcha\" to translating. The backing library, Yangson, doesn't consider key order when building the data model. To ensure order of operations, it is best to be deterministic.\n", + "\n", "Once we have the data, translating it to native is very simple:" ] }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, + "execution_count": 23, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ - "native = ios_processor.translate(candidate=data)" + "native = str.join(\"\\n\", [ios_processor.translate(candidate=data) for data in [system, interfaces, nw_instance]])" ] }, { @@ -165,13 +254,23 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, + "execution_count": 22, + "metadata": { + "scrolled": false + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "hostname csr1000v-1\n", + "clock timezone CST -6 0\n", + "ip name-server 8.8.8.8 8.8.4.4 2620:119:35::35\n", + "ntp server 2610:20:6F96:96::6\n", + "ip ssh version 2\n", + "ip ssh time-out 60\n", + "username developer privilege 15 secret 5 $1$pdQG$blOvq4Ey0SJCv3n001vnj.\n", + "\n", "interface FastEthernet1\n", " no loopback mac\n", " description This is Fa1\n", @@ -201,6 +300,7 @@ " switchport trunk allowed vlan 10,20\n", " exit\n", "!\n", + "\n", "vlan 10\n", " name prod\n", " no shutdown\n", From 4359f8cdda9887cb69e73961b4d4c148d06de73b Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Thu, 27 Feb 2020 11:41:10 -0600 Subject: [PATCH 16/19] Allow interfaces to not be defined --- .../parsers/openconfig/ios/openconfig_interfaces/interfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntc_rosetta/parsers/openconfig/ios/openconfig_interfaces/interfaces.py b/ntc_rosetta/parsers/openconfig/ios/openconfig_interfaces/interfaces.py index abde401..84bca6b 100644 --- a/ntc_rosetta/parsers/openconfig/ios/openconfig_interfaces/interfaces.py +++ b/ntc_rosetta/parsers/openconfig/ios/openconfig_interfaces/interfaces.py @@ -93,7 +93,7 @@ class Yangify(ParserData): path = "/openconfig-interfaces:interfaces/interface" def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: - for k, v in self.native["interface"].items(): + for k, v in jh.query("interface", self.native, default={}).items(): if k == "#text" or "." in k: continue yield k, v From 04bdf718bc9ec40b0a76a688844ff6182994b14c Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Thu, 27 Feb 2020 11:41:27 -0600 Subject: [PATCH 17/19] Add NTP auth --- .../ios/openconfig_system/system.py | 55 ++++++++++++++++--- .../ios/openconfig_system/system.py | 54 +++++++++++++++++- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py index 88af16d..5b19021 100644 --- a/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/parsers/openconfig/ios/openconfig_system/system.py @@ -53,6 +53,9 @@ class Yangify(ParserData): def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: dns = jh.query('ip."name-server"."#text"', self.native) or "" + # str.split will fire once on an empty string + if not dns: + raise StopIteration for ns in dns.split(" "): yield ns, {} @@ -125,8 +128,53 @@ class Yangify(ParserData): path = "/openconfig-system:system/ntp/servers" +class NtpKeyConfig(Parser): + class Yangify(ParserData): + path = "/openconfig-system:system/ntp/ntp-keys/ntp-key/config" + + def key_id(self) -> str: + return str(self.yy.key) + + def key_type(self) -> str: + # md5 is the only type declared in the model and the only type in the cisco docs + # ntc_rosetta/yang/openconfig/release/models/system/openconfig-system.yang#L116 + return "NTP_AUTH_MD5" + + def key_value(self) -> str: + for k, v in self.yy.native.items(): + if k == "#text": + continue + # return the whole md5 string + return str(v["#text"]) + + +class NtpKey(Parser): + config = NtpKeyConfig + + class Yangify(ParserData): + path = "/openconfig-system:system/ntp/ntp-keys/ntp-key" + + def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]: + ntp_auth = jh.query('ntp."authentication-key"', self.native) or {} + for k, v in ntp_auth.items(): + if k == "#text": + continue + yield k, v + + def key_id(self) -> str: + return str(self.yy.key) + + +class NtpKeys(Parser): + ntp_key = NtpKey + + class Yangify(ParserData): + path = "/openconfig-system:system/ntp/ntp-keys" + + class Ntp(Parser): config = NtpConfig + ntp_keys = NtpKeys servers = NtpServers class Yangify(ParserData): @@ -301,13 +349,6 @@ def timezone_name(self) -> str: return str(v) -class Clock(Parser): - class Yangify(ParserData): - path = "/openconfig-system:system/clock" - - config = ClockConfig - - class System(Parser): config = SystemConfig clock = Clock diff --git a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py index 8e7929a..7710d9d 100644 --- a/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py +++ b/ntc_rosetta/translators/openconfig/ios/openconfig_system/system.py @@ -70,7 +70,7 @@ def pre_process_list(self) -> None: for element in self.to_remove: self.result.add_command(f"no ntp server {element['address']}") - def address(self, value: str): + def address(self, value: str) -> None: self.yy.result.add_command(f"ntp server {value}") @@ -85,6 +85,55 @@ class NtpConfig(Translator): class Yangify(TranslatorData): path = "/openconfig-system:system/ntp/config" + def enable_ntp_auth(self, value: bool) -> None: + if value: + self.yy.result.add_command("ntp authenticate") + + +class NtpKeyConfig(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp/ntp-keys/ntp-key/config" + + def pre_process(self) -> None: + self.extra = {"key-id": "", "key-type": "", "key-value": ""} + + def post_process(self) -> None: + self.result.add_command( + f"ntp authentication-key {self.extra['key-id']} " + f"{self.extra['key-type']} {self.extra['key-value']}" + ) + + def key_id(self, value: str) -> None: + self.yy.extra["key-id"] = value + + def key_type(self, value: str) -> None: + # md5 is our only choice for now + self.yy.extra["key-type"] = "md5" + + def key_value(self, value: str) -> None: + self.yy.extra["key-value"] = value + + +class NtpKey(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp/ntp-keys/ntp-key" + + def pre_process_list(self) -> None: + if self.to_remove: + for element in self.to_remove: + self.result.add_command( + f"no ntp authentication-key {element['key-id']}" + ) + + config = NtpKeyConfig + + +class NtpKeys(Translator): + class Yangify(TranslatorData): + path = "/openconfig-system:system/ntp/ntp-keys" + + ntp_key = NtpKey + class Ntp(Translator): class Yangify(TranslatorData): @@ -92,6 +141,7 @@ class Yangify(TranslatorData): config = NtpConfig servers = NtpServers + ntp_keys = NtpKeys class SshServerConfig(Translator): @@ -162,7 +212,6 @@ def post_process(self) -> None: # exit # exit # """ - # raise NotImplementedError def username(self, value: str) -> None: self.yy.extra["username"] = value @@ -229,7 +278,6 @@ def timezone_name(self, value: str) -> None: self.yy.result.add_command(f"clock timezone {value}") - class Clock(Translator): class Yangify(TranslatorData): path = "/openconfig-system:system/clock" From fbae14135092b002175fa943ac822315b11a6cee Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Thu, 27 Feb 2020 11:41:48 -0600 Subject: [PATCH 18/19] Update openconfig-system for IOS tests --- .../merge/ios/add_ntp_server_key/__init__.py | 0 .../add_ntp_server_key/data_candidate.json | 30 +++++++ .../ios/add_ntp_server_key/data_running.json | 22 +++++ .../merge/ios/add_ntp_server_key/res_merge | 1 + .../merge/ios/add_ntp_server_key/test_case.py | 10 +++ .../ios/remove_ntp_server_key/__init__.py | 0 .../remove_ntp_server_key/data_candidate.json | 22 +++++ .../remove_ntp_server_key/data_running.json | 30 +++++++ .../merge/ios/remove_ntp_server_key/res_merge | 1 + .../ios/remove_ntp_server_key/test_case.py | 10 +++ .../parse/ios/config/dev_conf | 4 + .../parse/ios/config/result.json | 22 ++++- .../translate/ios/test_case_3/__init__.py | 0 .../translate/ios/test_case_3/data.json | 81 +++++++++++++++++++ .../translate/ios/test_case_3/res_merge | 9 +++ .../translate/ios/test_case_3/test_case.py | 13 +++ 16 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/__init__.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_running.json create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/res_merge create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/__init__.py create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_candidate.json create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_running.json create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/res_merge create mode 100644 tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/test_case.py create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/__init__.py create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/data.json create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/res_merge create mode 100644 tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/test_case.py diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_candidate.json new file mode 100644 index 0000000..36cd0ac --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_candidate.json @@ -0,0 +1,30 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "ntp-keys": { + "ntp-key": [ + { + "key-id": 41, + "config": { + "key-id": 41, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "047A070F0C2465403E160B1317190005242F 7" + } + }, + { + "key-id": 42, + "config": { + "key-id": 42, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "1108350C14172202332525202D2139231D03 7" + } + } + ] + } + } + } +} diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_running.json new file mode 100644 index 0000000..cb02506 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/data_running.json @@ -0,0 +1,22 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "ntp-keys": { + "ntp-key": [ + { + "key-id": 42, + "config": { + "key-id": 42, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "1108350C14172202332525202D2139231D03 7" + } + } + ] + } + } + } +} diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/res_merge new file mode 100644 index 0000000..c13fdd0 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/res_merge @@ -0,0 +1 @@ +ntp authentication-key 41 md5 047A070F0C2465403E160B1317190005242F 7 diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/add_ntp_server_key/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/__init__.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_candidate.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_candidate.json new file mode 100644 index 0000000..cb02506 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_candidate.json @@ -0,0 +1,22 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "ntp-keys": { + "ntp-key": [ + { + "key-id": 42, + "config": { + "key-id": 42, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "1108350C14172202332525202D2139231D03 7" + } + } + ] + } + } + } +} diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_running.json b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_running.json new file mode 100644 index 0000000..36cd0ac --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/data_running.json @@ -0,0 +1,30 @@ +{ + "openconfig-system:system": { + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": false + }, + "ntp-keys": { + "ntp-key": [ + { + "key-id": 41, + "config": { + "key-id": 41, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "047A070F0C2465403E160B1317190005242F 7" + } + }, + { + "key-id": 42, + "config": { + "key-id": 42, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "1108350C14172202332525202D2139231D03 7" + } + } + ] + } + } + } +} diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/res_merge b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/res_merge new file mode 100644 index 0000000..a33e097 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/res_merge @@ -0,0 +1 @@ +no ntp authentication-key 41 diff --git a/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/test_case.py b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/test_case.py new file mode 100644 index 0000000..651289f --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/merge/ios/remove_ntp_server_key/test_case.py @@ -0,0 +1,10 @@ +from tests.models.test_models import parse_path, merge + +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_merge(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + merge(test_data.org, test_data.driver, test_data.path, action) diff --git a/tests/models/openconfig/data/openconfig_system/parse/ios/config/dev_conf b/tests/models/openconfig/data/openconfig_system/parse/ios/config/dev_conf index 82caac4..8503a10 100644 --- a/tests/models/openconfig/data/openconfig_system/parse/ios/config/dev_conf +++ b/tests/models/openconfig/data/openconfig_system/parse/ios/config/dev_conf @@ -42,6 +42,10 @@ ntp allow mode control 0 ntp leap-handle ntp mindistance 1 ntp maxdistance 8 +ntp authenticate +ntp authentication-key 41 md5 047A070F0C2465403E160B1317190005242F 7 +ntp authentication-key 42 md5 1108350C14172202332525202D2139231D03 7 +ntp trusted-key 42 ntp server 2610:20:6F96:96::6 maxpoll 10 minpoll 6 version 4 burst iburst ntp peer 2610:20:6F15:15::27 maxpoll 10 minpoll 6 version 4 burst iburst ntp peer 129.6.15.28 maxpoll 10 minpoll 6 version 4 burst iburst diff --git a/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json b/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json index 005fbc0..4ff5971 100644 --- a/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json +++ b/tests/models/openconfig/data/openconfig_system/parse/ios/config/result.json @@ -46,7 +46,27 @@ "ntp": { "config": { "enabled": true, - "enable-ntp-auth": false + "enable-ntp-auth": true + }, + "ntp-keys": { + "ntp-key": [ + { + "key-id": 41, + "config": { + "key-id": 41, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "047A070F0C2465403E160B1317190005242F 7" + } + }, + { + "key-id": 42, + "config": { + "key-id": 42, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "1108350C14172202332525202D2139231D03 7" + } + } + ] }, "servers": { "server": [ diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/__init__.py b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/data.json b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/data.json new file mode 100644 index 0000000..aa8124e --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/data.json @@ -0,0 +1,81 @@ +{ + "openconfig-system:system": { + "config": { + "hostname": "csr1000v-1" + }, + "clock": { + "config": { + "timezone-name": "CST -6 0" + } + }, + "dns": { + "servers": { + "server": [ + { + "address": "8.8.8.8", + "config": { + "address": "8.8.8.8", + "port": 53 + } + } + ] + } + }, + "ntp": { + "config": { + "enabled": true, + "enable-ntp-auth": true + }, + "ntp-keys": { + "ntp-key": [ + { + "key-id": 42, + "config": { + "key-id": 42, + "key-type": "openconfig-system:NTP_AUTH_MD5", + "key-value": "1108350C14172202332525202D2139231D03 7" + } + } + ] + }, + "servers": { + "server": [ + { + "address": "2610:20:6F96:96::6", + "config": { + "address": "2610:20:6F96:96::6" + } + } + ] + } + }, + "ssh-server": { + "config": { + "enable": true, + "protocol-version": "V2", + "timeout": 60 + } + }, + "telnet-server": { + "config": { + "enable": true + } + }, + "aaa": { + "authentication": { + "users": { + "user": [ + { + "username": "developer", + "config": { + "username": "developer", + "password-hashed": "$1$pdQG$blOvq4Ey0SJCv3n001vnj.", + "role": "15" + } + } + ] + } + } + } + } + } \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/res_merge b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/res_merge new file mode 100644 index 0000000..94044f7 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/res_merge @@ -0,0 +1,9 @@ +hostname csr1000v-1 +clock timezone CST -6 0 +ip name-server 8.8.8.8 +ntp authenticate +ntp authentication-key 42 md5 1108350C14172202332525202D2139231D03 7 +ntp server 2610:20:6F96:96::6 +ip ssh version 2 +ip ssh time-out 60 +username developer privilege 15 secret 5 $1$pdQG$blOvq4Ey0SJCv3n001vnj. diff --git a/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/test_case.py b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/test_case.py new file mode 100644 index 0000000..155d3e0 --- /dev/null +++ b/tests/models/openconfig/data/openconfig_system/translate/ios/test_case_3/test_case.py @@ -0,0 +1,13 @@ +""" +- NTP authentication key +""" + +from tests.models.test_models import parse_path, translate +import pytest + + +@pytest.mark.parametrize("action", ["merge"]) # type: ignore +def test_translate(action: str) -> None: + """Run a merge test with the provided action.""" + test_data = parse_path(__file__) + translate(test_data.org, test_data.driver, test_data.path, action) From d69b232ab8f114b7f2c6084f06346db8bd8cc0e1 Mon Sep 17 00:00:00 2001 From: Daniel Justice Date: Fri, 28 Feb 2020 10:52:08 -0600 Subject: [PATCH 19/19] Renumber cells from '1' --- docs/tutorials/ios_merging.ipynb | 18 +++++++-------- docs/tutorials/ios_smart_diff.ipynb | 12 +++++----- docs/tutorials/ios_translate.ipynb | 8 +++---- docs/tutorials/junos_parsing.ipynb | 23 +++++++------------ docs/tutorials/junos_translate.ipynb | 17 ++++---------- .../rosetta_and_yangson_walkthrough.ipynb | 20 ++++++++-------- 6 files changed, 42 insertions(+), 56 deletions(-) diff --git a/docs/tutorials/ios_merging.ipynb b/docs/tutorials/ios_merging.ipynb index 438b1ed..1c5da75 100644 --- a/docs/tutorials/ios_merging.ipynb +++ b/docs/tutorials/ios_merging.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -163,7 +163,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -180,7 +180,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -190,7 +190,7 @@ " 'config': {'vlan-id': 20, 'name': 'dev', 'status': 'SUSPENDED'}}" ] }, - "execution_count": 29, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -208,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -232,7 +232,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -248,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 8, "metadata": {}, "outputs": [ { diff --git a/docs/tutorials/ios_smart_diff.ipynb b/docs/tutorials/ios_smart_diff.ipynb index 783fc4f..6a376c4 100755 --- a/docs/tutorials/ios_smart_diff.ipynb +++ b/docs/tutorials/ios_smart_diff.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -128,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 5, "metadata": { "scrolled": false }, @@ -147,7 +147,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 6, "metadata": {}, "outputs": [ { diff --git a/docs/tutorials/ios_translate.ipynb b/docs/tutorials/ios_translate.ipynb index df837f2..d348f3e 100644 --- a/docs/tutorials/ios_translate.ipynb +++ b/docs/tutorials/ios_translate.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -236,7 +236,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 3, "metadata": { "scrolled": false }, @@ -254,7 +254,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 4, "metadata": { "scrolled": false }, diff --git a/docs/tutorials/junos_parsing.ipynb b/docs/tutorials/junos_parsing.ipynb index e29bbe8..f5d99d6 100644 --- a/docs/tutorials/junos_parsing.ipynb +++ b/docs/tutorials/junos_parsing.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -34,7 +34,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 3, "metadata": { "scrolled": false }, @@ -216,7 +216,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -232,7 +232,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 5, "metadata": { "scrolled": false }, @@ -559,7 +559,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -596,7 +596,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -678,7 +678,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -754,13 +754,6 @@ ")\n", "print(json.dumps(parsed_vlans.raw_value(), sort_keys=True, indent=4))" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/docs/tutorials/junos_translate.ipynb b/docs/tutorials/junos_translate.ipynb index 29567cd..d593b60 100644 --- a/docs/tutorials/junos_translate.ipynb +++ b/docs/tutorials/junos_translate.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -162,7 +162,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -235,13 +235,6 @@ "source": [ "print(native)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -260,7 +253,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.6.9" } }, "nbformat": 4, diff --git a/docs/tutorials/rosetta_and_yangson_walkthrough.ipynb b/docs/tutorials/rosetta_and_yangson_walkthrough.ipynb index 278085f..b44aab2 100644 --- a/docs/tutorials/rosetta_and_yangson_walkthrough.ipynb +++ b/docs/tutorials/rosetta_and_yangson_walkthrough.ipynb @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 1, "metadata": { "scrolled": false }, @@ -205,7 +205,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 2, "metadata": { "scrolled": false }, @@ -417,7 +417,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -502,7 +502,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -592,7 +592,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -622,7 +622,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 6, "metadata": { "scrolled": false }, @@ -776,7 +776,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -851,7 +851,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -896,7 +896,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -998,7 +998,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 10, "metadata": {}, "outputs": [ {