{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# test: jpath\n", "\n", "[jpath](../api/tasks.rst#nornir_tests.plugins.tests.jpath) uses jsonpath in conjunction with assertpy to validate task data.\n", "\n", "This module combines the power of jsonpath and assertpy. In order to use this module it is somewhat necessary to be familiar with both of those. Links to them can be found below.\n", "\n", "* `jsonpath_ng `\n", "* `assertpy `\n", "\n", "\n", "Examples:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "from nornir import InitNornir\n", "from nornir_napalm.plugins.tasks import napalm_get, napalm_cli\n", "\n", "from nornir_tests.plugins.functions import print_result\n", "from nornir_tests.plugins.tasks import wrap_task\n", "from nornir_tests.plugins.tests import jpath\n", "\n", "nr = InitNornir(\n", " inventory={\n", " \"plugin\": \"SimpleInventory\",\n", " \"options\": {\n", " \"host_file\": \"inventory/hosts.yaml\",\n", " \"group_file\": \"inventory/groups.yaml\",\n", " \"defaults_file\": \"inventory/defaults.yaml\",\n", " }\n", " },\n", " dry_run=True,\n", ")" ] }, { "source": [ "When running a task, validations usually if done in Nornir can be executed as additional logic implemented in python or with running of actual tasks. Using `nornir_tests` moves the logic into the task and provides a way to impact the success of a task based on its validations." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "results = nr.run(\n", " wrap_task(napalm_get), getters=['facts'],\n", " tests=[\n", " jpath(path='$..os_version', value='4.14.3-2329074.gaatlantarel')\n", " ]\n", ")" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "tags": [] }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[36mnapalm_get**********************************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr00 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv napalm_get ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'is_equal_to',\n 'path': '$..os_version',\n 'result_attr': 'result',\n 'value': '4.14.3-2329074.gaatlantarel'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['facts.os_version']}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c0714220>", "text/html": "
{\n  \"facts\": {\n    \"uptime\": 151005.57332897186,\n    \"vendor\": \"Arista\",\n    \"os_version\": \"4.14.3-2329074.gaatlantarel\",\n    \"serial_number\": \"SN0123A34AS\",\n    \"model\": \"vEOS\",\n    \"hostname\": \"eos-router\",\n    \"fqdn\": \"eos-router\",\n    \"interface_list\": [\n      \"Ethernet2\",\n      \"Management1\",\n      \"Ethernet1\",\n      \"Ethernet3\"\n    ]\n  }\n}\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END napalm_get ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr01 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv napalm_get ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'is_equal_to',\n 'path': '$..os_version',\n 'result_attr': 'result',\n 'value': '4.14.3-2329074.gaatlantarel'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['facts.os_version']}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c0714310>", "text/html": "
{\n  \"facts\": {\n    \"uptime\": 151005.57332897186,\n    \"vendor\": \"Arista\",\n    \"os_version\": \"4.14.3-2329074.gaatlantarel\",\n    \"serial_number\": \"SN0123A34AS\",\n    \"model\": \"vEOS\",\n    \"hostname\": \"eos-router2\",\n    \"fqdn\": \"eos-router2\",\n    \"interface_list\": [\n      \"Ethernet2\",\n      \"Management1\",\n      \"Ethernet1\",\n      \"Ethernet3\",\n      \"Ethernet4\"\n    ]\n  }\n}\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END napalm_get ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m{'facts': {'uptime': 151005.57332897186, 'vendor': 'Arista', 'os_version': '4.14.3-2329074.gaatlantarel', 'serial_number': 'SN0123A34AS', 'model': 'vEOS', 'hostname': 'eos-router', 'fqdn': 'eos-router', 'interface_list': ['Ethernet2', 'Management1', 'Ethernet1', 'Ethernet3']}}\u001b[0m\n\u001b[0m" } ], "source": [ "print_result(results, vars=['tests', 'highlit'])\n", "print(results['rtr00'][0].result)" ] }, { "source": [ "The first example was pretty simple but the next will have many tests run in validating interface data. It will also use @ decorator syntax." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 20, "metadata": { "tags": [] }, "outputs": [], "source": [ "@jpath(path='$..ipv6', assertion='contains', value=\"1::1\")\n", "@jpath(path='$.interfaces_ip', assertion='is_length', value=3)\n", "@jpath(path='$..FastEthernet8..prefix_length', value=22)\n", "def get_interface_ips(task):\n", " return napalm_get(task, getters=['interfaces_ip'])\n", "\n", "results = nr.run(get_interface_ips)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "tags": [] }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[36mget_interface_ips***************************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr00 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv get_interface_ips ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'is_equal_to',\n 'path': '$..FastEthernet8..prefix_length',\n 'result_attr': 'result',\n 'value': 22}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['interfaces_ip.FastEthernet8.ipv4.10.66.43.169.prefix_length']}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31mF JpathRecord - {'assertion': 'is_length',\n 'path': '$.interfaces_ip',\n 'result_attr': 'result',\n 'value': 3}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31m{'exception': TypeError("'str' object does not support item assignment"),\n 'matches': ['interfaces_ip']}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'contains',\n 'path': '$..ipv6',\n 'result_attr': 'result',\n 'value': '1::1'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['interfaces_ip.Loopback555.ipv6']}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c08d0a90>", "text/html": "
\"\\\"{\\\\n  \\\\\\\"interfaces_ip\\\\\\\": {\\\\n    \\\\\\\"FastEthernet8\\\\\\\": {\\\\n      \\\\\\\"ipv4\\\\\\\": {\\\\n  \n\\\\\\\"10.66.43.169\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": \\\\\\\"22\\\\\\\"\\\\n        }\\\\n      \n}\\\\n    },\\\\n    \\\\\\\"Loopback555\\\\\\\": {\\\\n      \\\\\\\"ipv4\\\\\\\": {\\\\n        \n\\\\\\\"192.168.1.1\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 24\\\\n        }\\\\n      },\\\\n      \n\\\\\\\"ipv6\\\\\\\": {\\\\n        \\\\\\\"1::1\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 64\\\\n        \n},\\\\n        \\\\\\\"2001:DB8:1::1\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 64\\\\n        },\\\\n  \n\\\\\\\"2::\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 64\\\\n        },\\\\n        \\\\\\\"FE80::3\\\\\\\": \n{\\\\n          \\\\\\\"prefix_length\\\\\\\": \\\\\\\"N/A\\\\\\\"\\\\n        }\\\\n      }\\\\n    },\\\\n    \n\\\\\\\"Tunnel0\\\\\\\": {\\\\n      \\\\\\\"ipv4\\\\\\\": {\\\\n        \\\\\\\"10.63.100.9\\\\\\\": {\\\\n          \n\\\\\\\"prefix_length\\\\\\\": 24\\\\n        }\\\\n      }\\\\n    }\\\\n  }\\\\n}\\\"\"\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END get_interface_ips ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr01 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv get_interface_ips ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'is_equal_to',\n 'path': '$..FastEthernet8..prefix_length',\n 'result_attr': 'result',\n 'value': 22}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['interfaces_ip.FastEthernet8.ipv4.10.66.43.170.prefix_length']}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31mF JpathRecord - {'assertion': 'is_length',\n 'path': '$.interfaces_ip',\n 'result_attr': 'result',\n 'value': 3}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31m{'exception': TypeError("'str' object does not support item assignment"),\n 'matches': ['interfaces_ip']}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31mF JpathRecord - {'assertion': 'contains',\n 'path': '$..ipv6',\n 'result_attr': 'result',\n 'value': '1::1'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31m{'exception': Exception(AssertionError("Expected <{'1::2': {'prefix_length': 64}, '2001:DB8:1::2': {'prefix_length': 64}, '2::99': {'prefix_length': 64}, 'FE80::5': {'prefix_length': 'N/A'}}> to contain key <1::1>, but did not."))}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c0b6fd60>", "text/html": "
\"\\\"{\\\\n  \\\\\\\"interfaces_ip\\\\\\\": {\\\\n    \\\\\\\"FastEthernet8\\\\\\\": {\\\\n      \\\\\\\"ipv4\\\\\\\": {\\\\n  \n\\\\\\\"10.66.43.170\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": \\\\\\\"22\\\\\\\"\\\\n        }\\\\n      \n}\\\\n    },\\\\n    \\\\\\\"Loopback555\\\\\\\": {\\\\n      \\\\\\\"ipv4\\\\\\\": {\\\\n        \n\\\\\\\"192.168.1.2\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 24\\\\n        }\\\\n      },\\\\n      \n\\\\\\\"ipv6\\\\\\\": {\\\\n        \\\\\\\"1::2\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 64\\\\n        \n},\\\\n        \\\\\\\"2001:DB8:1::2\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 64\\\\n        },\\\\n  \n\\\\\\\"2::99\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": 64\\\\n        },\\\\n        \n\\\\\\\"FE80::5\\\\\\\": {\\\\n          \\\\\\\"prefix_length\\\\\\\": \\\\\\\"N/A\\\\\\\"\\\\n        }\\\\n      }\\\\n   \n},\\\\n    \\\\\\\"Tunnel0\\\\\\\": {\\\\n      \\\\\\\"ipv4\\\\\\\": {\\\\n        \\\\\\\"10.63.100.10\\\\\\\": {\\\\n     \n\\\\\\\"prefix_length\\\\\\\": 24\\\\n        }\\\\n      }\\\\n    }\\\\n  }\\\\n}\\\"\"\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END get_interface_ips ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m" } ], "source": [ "print_result(results, vars=['tests', 'highlit'])" ] }, { "source": [ "The next example will show how a task can be set to failed based on the validation that is performed. If the task has fail_task set to true and ends up with passed=False in the test it will mark the overall task as failed." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 22, "metadata": { "tags": [] }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[36mcheck_bgp_neighbors*************************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr00 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv check_bgp_neighbors ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[31mF JpathRecord - {'assertion': 'is_equal_to',\n 'path': '$..remote_as',\n 'result_attr': 'result',\n 'value': '8121'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31m{'exception': Exception(AssertionError('Expected <8121> to be equal to <8121>, but was not.'))}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'is_equal_to',\n 'fail_task': True,\n 'path': '$..connection_state',\n 'result_attr': 'result',\n 'value': 'Established'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['bgp_neighbors_detail.global.8121.[0].connection_state']}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c039f2e0>", "text/html": "
\"{\\n  \\\"bgp_neighbors_detail\\\": {\\n    \\\"global\\\": {\\n      \\\"8121\\\": [\\n        {\\n         \n\\\"up\\\": true,\\n          \\\"local_as\\\": 13335,\\n          \\\"remote_as\\\": \\\"8121\\\",\\n          \n\\\"local_address\\\": \\\"172.101.76.1\\\",\\n          \\\"local_address_configured\\\": true,\\n        \n\\\"local_port\\\": 179,\\n          \\\"routing_table\\\": \\\"inet.0\\\",\\n          \\\"remote_address\\\":\n\\\"192.247.78.0\\\",\\n          \\\"remote_port\\\": 58380,\\n          \\\"multihop\\\": false,\\n       \n\\\"multipath\\\": true,\\n          \\\"remove_private_as\\\": true,\\n          \\\"import_policy\\\": \n\\\"4-NTT-TRANSIT-IN\\\",\\n          \\\"export_policy\\\": \\\"4-NTT-TRANSIT-OUT\\\",\\n          \n\\\"input_messages\\\": 123,\\n          \\\"output_messages\\\": 13,\\n          \\\"input_updates\\\": \n123,\\n          \\\"output_updates\\\": 5,\\n          \\\"messages_queued_out\\\": 23,\\n          \n\\\"connection_state\\\": \\\"Established\\\",\\n          \\\"previous_connection_state\\\": \n\\\"EstabSync\\\",\\n          \\\"last_event\\\": \\\"RecvKeepAlive\\\",\\n          \n\\\"suppress_4byte_as\\\": false,\\n          \\\"local_as_prepend\\\": false,\\n          \n\\\"holdtime\\\": 90,\\n          \\\"configured_holdtime\\\": 90,\\n          \\\"keepalive\\\": 30,\\n    \n\\\"configured_keepalive\\\": 30,\\n          \\\"active_prefix_count\\\": 132808,\\n          \n\\\"received_prefix_count\\\": 566739,\\n          \\\"accepted_prefix_count\\\": 566479,\\n          \n\\\"suppressed_prefix_count\\\": 0,\\n          \\\"advertised_prefix_count\\\": 0,\\n          \n\\\"flap_count\\\": 27\\n        }\\n      ]\\n    }\\n  }\\n}\"\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END check_bgp_neighbors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr01 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[31mvvvv check_bgp_neighbors ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ERROR\u001b[0m\n\u001b[0m\u001b[2m\u001b[31mF JpathRecord - {'assertion': 'is_equal_to',\n 'path': '$..remote_as',\n 'result_attr': 'result',\n 'value': '8121'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31m{'exception': Exception(AssertionError('Expected <8121> to be equal to <8121>, but was not.'))}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31mF JpathRecord - {'assertion': 'is_equal_to',\n 'fail_task': True,\n 'path': '$..connection_state',\n 'result_attr': 'result',\n 'value': 'Established'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31m{'exception': Exception(AssertionError('Expected <EstabSync> to be equal to <Established>, but was not.'))}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c039f370>", "text/html": "
\"{\\n  \\\"bgp_neighbors_detail\\\": {\\n    \\\"global\\\": {\\n      \\\"8121\\\": [\\n        {\\n         \n\\\"up\\\": true,\\n          \\\"local_as\\\": 13335,\\n          \\\"remote_as\\\": \\\"8121\\\",\\n          \n\\\"local_address\\\": \\\"172.101.77.1\\\",\\n          \\\"local_address_configured\\\": true,\\n        \n\\\"local_port\\\": 179,\\n          \\\"routing_table\\\": \\\"inet.0\\\",\\n          \\\"remote_address\\\":\n\\\"192.247.78.1\\\",\\n          \\\"remote_port\\\": 58381,\\n          \\\"multihop\\\": false,\\n       \n\\\"multipath\\\": true,\\n          \\\"remove_private_as\\\": true,\\n          \\\"import_policy\\\": \n\\\"4-NTT-TRANSIT-IN\\\",\\n          \\\"export_policy\\\": \\\"4-NTT-TRANSIT-OUT\\\",\\n          \n\\\"input_messages\\\": 123,\\n          \\\"output_messages\\\": 13,\\n          \\\"input_updates\\\": \n123,\\n          \\\"output_updates\\\": 5,\\n          \\\"messages_queued_out\\\": 23,\\n          \n\\\"connection_state\\\": \\\"EstabSync\\\",\\n          \\\"previous_connection_state\\\": \n\\\"Established\\\",\\n          \\\"last_event\\\": \\\"RecvKeepAlive\\\",\\n          \n\\\"suppress_4byte_as\\\": false,\\n          \\\"local_as_prepend\\\": false,\\n          \n\\\"holdtime\\\": 90,\\n          \\\"configured_holdtime\\\": 90,\\n          \\\"keepalive\\\": 30,\\n    \n\\\"configured_keepalive\\\": 30,\\n          \\\"active_prefix_count\\\": 132808,\\n          \n\\\"received_prefix_count\\\": 566739,\\n          \\\"accepted_prefix_count\\\": 566479,\\n          \n\\\"suppressed_prefix_count\\\": 0,\\n          \\\"advertised_prefix_count\\\": 0,\\n          \n\\\"flap_count\\\": 27\\n        }\\n      ]\\n    }\\n  }\\n}\"\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[31m^^^^ END check_bgp_neighbors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m" } ], "source": [ "@jpath(path='$..connection_state', value=\"Established\", fail_task=True)\n", "@jpath(path='$..remote_as', value='8121')\n", "def check_bgp_neighbors(task):\n", " return napalm_get(task, getters=['bgp_neighbors_detail'])\n", "\n", "results = nr.run(check_bgp_neighbors)\n", "print_result(results, vars=['tests', 'highlit'])" ] }, { "source": [ "The last example will use host_data in order to find some data specific to the host to validate against. The host_data is anything from inventory perhaps obviously in the data dictionary." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 23, "metadata": { "tags": [] }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[36mnapalm_get**********************************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr00 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv napalm_get ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'contains',\n 'host_data': '$.mgmt_port',\n 'path': '$.interfaces',\n 'result_attr': 'result',\n 'value': 'Management1'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['interfaces']}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c19007c0>", "text/html": "
{\n  \"interfaces\": {\n    \"Management1\": {\n      \"is_up\": false,\n      \"is_enabled\": false,\n      \"description\": \"\",\n      \"last_flapped\": -1.0,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:61\"\n    },\n    \"Ethernet1\": {\n      \"is_up\": true,\n      \"is_enabled\": true,\n      \"description\": \"foo\",\n      \"last_flapped\": 1429978575.1554043,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:62\"\n    },\n    \"Ethernet2\": {\n      \"is_up\": true,\n      \"is_enabled\": true,\n      \"description\": \"bla\",\n      \"last_flapped\": 1429978575.1555667,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:63\"\n    },\n    \"Ethernet3\": {\n      \"is_up\": false,\n      \"is_enabled\": true,\n      \"description\": \"bar\",\n      \"last_flapped\": -1.0,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:64\"\n    }\n  }\n}\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END napalm_get ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr01 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv napalm_get ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'contains',\n 'host_data': '$.mgmt_port',\n 'path': '$.interfaces',\n 'result_attr': 'result',\n 'value': 'Management2'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['interfaces']}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c052a970>", "text/html": "
{\n  \"interfaces\": {\n    \"Management2\": {\n      \"is_up\": false,\n      \"is_enabled\": false,\n      \"description\": \"\",\n      \"last_flapped\": -1.0,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:61\"\n    },\n    \"Ethernet1\": {\n      \"is_up\": true,\n      \"is_enabled\": true,\n      \"description\": \"foo\",\n      \"last_flapped\": 1429978575.1554043,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:62\"\n    },\n    \"Ethernet2\": {\n      \"is_up\": true,\n      \"is_enabled\": true,\n      \"description\": \"bla\",\n      \"last_flapped\": 1429978575.1555667,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:63\"\n    },\n    \"Ethernet3\": {\n      \"is_up\": false,\n      \"is_enabled\": true,\n      \"description\": \"bar\",\n      \"last_flapped\": -1.0,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:64\"\n    },\n    \"Ethernet4\": {\n      \"is_up\": false,\n      \"is_enabled\": true,\n      \"description\": \"bar\",\n      \"last_flapped\": -1.0,\n      \"speed\": 1000,\n      \"mtu\": 1500,\n      \"mac_address\": \"FA:16:3E:57:33:65\"\n    }\n  }\n}\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END napalm_get ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m" } ], "source": [ "nr.data.reset_failed_hosts()\n", "\n", "results = nr.run(\n", " wrap_task(napalm_get), getters=['interfaces'],\n", " tests=[\n", " jpath(path='$.interfaces', assertion='contains', host_data='$.mgmt_port')\n", " ]\n", ")\n", "print_result(results, vars=['tests', 'highlit'])" ] }, { "source": [ "In these examples 'contains', 'is_equal', and 'is_length' were used for assertions. Many other possibilities are available from the assertpy module. Not saying all of them make sense to use or that they all work as expected but they should. Too many to validate to be honest. Some other that would certainly work fine would be 'is_true', 'is_empty', etc." ], "cell_type": "markdown", "metadata": {} }, { "source": [ "A few more things about how it all works. The one_of argument isn't always needed but it could be. If the match was intended to turn up many of something and something like is_equal assertion is used, if one_of is not true then it will fail if all matches don't meet the assertion. This is kind of confusing and I should prob show an example here." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 24, "metadata": { "tags": [] }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[36mget_interface_ips***************************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[34m* rtr00 ** changed : False *****************************************************\u001b[0m\n\u001b[0m\u001b[1m\u001b[32mvvvv get_interface_ips ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n\u001b[0m\u001b[2m\u001b[32mP JpathRecord - {'assertion': 'contains',\n 'one_of': True,\n 'path': '$..ipv4',\n 'result_attr': 'result',\n 'value': '10.66.43.169'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[32m{'matches': ['interfaces_ip.FastEthernet8.ipv4']}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31mF JpathRecord - {'assertion': 'contains',\n 'path': '$..ipv4',\n 'result_attr': 'result',\n 'value': '10.66.43.169'}\u001b[0m\n\u001b[0m\u001b[2m\u001b[31m{'exception': Exception(AssertionError("Expected <{'192.168.1.1': {'prefix_length': 24}}> to contain key <10.66.43.169>, but did not.")),\n 'matches': ['interfaces_ip.FastEthernet8.ipv4']}\u001b[0m\n\u001b[0m" }, { "output_type": "display_data", "data": { "text/plain": "<rich.jupyter.JupyterRenderable at 0x7f61c0c634c0>", "text/html": "
\"{\\n  \\\"interfaces_ip\\\": {\\n    \\\"FastEthernet8\\\": {\\n      \\\"ipv4\\\": {\\n        \n\\\"10.66.43.169\\\": {\\n          \\\"prefix_length\\\": 22\\n        }\\n      }\\n    },\\n    \n\\\"Loopback555\\\": {\\n      \\\"ipv4\\\": {\\n        \\\"192.168.1.1\\\": {\\n          \n\\\"prefix_length\\\": 24\\n        }\\n      },\\n      \\\"ipv6\\\": {\\n        \\\"1::1\\\": {\\n         \n\\\"prefix_length\\\": 64\\n        },\\n        \\\"2001:DB8:1::1\\\": {\\n          \\\"prefix_length\\\":\n64\\n        },\\n        \\\"2::\\\": {\\n          \\\"prefix_length\\\": 64\\n        },\\n        \n\\\"FE80::3\\\": {\\n          \\\"prefix_length\\\": \\\"N/A\\\"\\n        }\\n      }\\n    },\\n    \n\\\"Tunnel0\\\": {\\n      \\\"ipv4\\\": {\\n        \\\"10.63.100.9\\\": {\\n          \\\"prefix_length\\\": \n24\\n        }\\n      }\\n    }\\n  }\\n}\"\n
\n" }, "metadata": {} }, { "output_type": "stream", "name": "stdout", "text": "\u001b[1m\u001b[32m^^^^ END get_interface_ips ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n\u001b[0m" } ], "source": [ "@jpath(path='$..ipv4', assertion='contains', value=\"10.66.43.169\")\n", "@jpath(path='$..ipv4', assertion='contains', value=\"10.66.43.169\", one_of=True)\n", "def get_interface_ips(task):\n", " return napalm_get(task, getters=['interfaces_ip'])\n", "\n", "rtr00 = nr.filter(name='rtr00')\n", "results = rtr00.run(get_interface_ips)\n", "print_result(results, vars=['tests', 'highlit'])" ] }, { "source": [ "So what happened is the first test passed as it found a bunch of paths that ended with 'ipv4' and it only needed one of them to contain the value of \"10.66.43.169\". The second one failed due to the fact that it wanted all the paths to contain that value and they did not." ], "cell_type": "markdown", "metadata": {} } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.2 64-bit ('.venv': venv)", "language": "python", "name": "python38264bitvenvvenv1c35bb63d8ff4e6aa6d1792b190afa1b" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.2-final" } }, "nbformat": 4, "nbformat_minor": 4 }