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, 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 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(