{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "nbsphinx": "hidden" }, "outputs": [], "source": [ "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", "%run ../highlighter.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inventory\n", "\n", "The Inventory is arguably the most important piece of nornir. Let's see how it works. To begin with the [inventory](../api/nornir/core/inventory.html#module-nornir.core.inventory) is comprised of [hosts](../api/nornir/core/inventory.html#nornir.core.inventory.Hosts), [groups](../api/nornir/core/inventory.html#nornir.core.inventory.Groups) and [defaults](../api/nornir/core/inventory.html#nornir.core.inventory.Defaults).\n", "\n", "In this tutorial we are using the [SimpleInventory](../api/nornir/plugins/inventory/simple.html#nornir.plugins.inventory.simple.SimpleInventory) plugin. This inventory plugin stores all the relevant data in three files. Let’s start by checking them:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
  1---\n",
       "  2host1.cmh:\n",
       "  3    hostname: 127.0.0.1\n",
       "  4    port: 2201\n",
       "  5    username: vagrant\n",
       "  6    password: vagrant\n",
       "  7    platform: linux\n",
       "  8    groups:\n",
       "  9        - cmh\n",
       " 10    data:\n",
       " 11        site: cmh\n",
       " 12        role: host\n",
       " 13        type: host\n",
       " 14        nested_data:\n",
       " 15            a_dict:\n",
       " 16                a: 1\n",
       " 17                b: 2\n",
       " 18            a_list: [1, 2]\n",
       " 19            a_string: "asdasd"\n",
       " 20\n",
       " 21host2.cmh:\n",
       " 22    hostname: 127.0.0.1\n",
       " 23    port: 2202\n",
       " 24    username: vagrant\n",
       " 25    password: vagrant\n",
       " 26    platform: linux\n",
       " 27    groups:\n",
       " 28        - cmh\n",
       " 29    data:\n",
       " 30        site: cmh\n",
       " 31        role: host\n",
       " 32        type: host\n",
       " 33        nested_data:\n",
       " 34            a_dict:\n",
       " 35                b: 2\n",
       " 36                c: 3\n",
       " 37            a_list: [1, 2]\n",
       " 38            a_string: "qwe"\n",
       " 39\n",
       " 40spine00.cmh:\n",
       " 41    hostname: 127.0.0.1\n",
       " 42    username: vagrant\n",
       " 43    password: vagrant\n",
       " 44    port: 12444\n",
       " 45    platform: eos\n",
       " 46    groups:\n",
       " 47        - cmh\n",
       " 48    data:\n",
       " 49        site: cmh\n",
       " 50        role: spine\n",
       " 51        type: network_device\n",
       " 52\n",
       " 53spine01.cmh:\n",
       " 54    hostname: 127.0.0.1\n",
       " 55    username: vagrant\n",
       " 56    password: ""\n",
       " 57    platform: junos\n",
       " 58    port: 12204\n",
       " 59    groups:\n",
       " 60        - cmh\n",
       " 61    data:\n",
       " 62        site: cmh\n",
       " 63        role: spine\n",
       " 64        type: network_device\n",
       " 65\n",
       " 66leaf00.cmh:\n",
       " 67    hostname: 127.0.0.1\n",
       " 68    username: vagrant\n",
       " 69    password: vagrant\n",
       " 70    port: 12443\n",
       " 71    platform: eos\n",
       " 72    groups:\n",
       " 73        - cmh\n",
       " 74    data:\n",
       " 75        site: cmh\n",
       " 76        role: leaf\n",
       " 77        type: network_device\n",
       " 78        asn: 65100\n",
       " 79\n",
       " 80leaf01.cmh:\n",
       " 81    hostname: 127.0.0.1\n",
       " 82    username: vagrant\n",
       " 83    password: ""\n",
       " 84    port: 12203\n",
       " 85    platform: junos\n",
       " 86    groups:\n",
       " 87        - cmh\n",
       " 88    data:\n",
       " 89        site: cmh\n",
       " 90        role: leaf\n",
       " 91        type: network_device\n",
       " 92        asn: 65101\n",
       " 93\n",
       " 94host1.bma:\n",
       " 95    groups:\n",
       " 96        - bma\n",
       " 97    platform: linux\n",
       " 98    data:\n",
       " 99        site: bma\n",
       "100        role: host\n",
       "101        type: host\n",
       "102\n",
       "103host2.bma:\n",
       "104    groups:\n",
       "105        - bma\n",
       "106    platform: linux\n",
       "107    data:\n",
       "108        site: bma\n",
       "109        role: host\n",
       "110        type: host\n",
       "111\n",
       "112spine00.bma:\n",
       "113    hostname: 127.0.0.1\n",
       "114    username: vagrant\n",
       "115    password: vagrant\n",
       "116    port: 12444\n",
       "117    platform: eos\n",
       "118    groups:\n",
       "119        - bma\n",
       "120    data:\n",
       "121        site: bma\n",
       "122        role: spine\n",
       "123        type: network_device\n",
       "124\n",
       "125spine01.bma:\n",
       "126    hostname: 127.0.0.1\n",
       "127    username: vagrant\n",
       "128    password: ""\n",
       "129    port: 12204\n",
       "130    platform: junos\n",
       "131    groups:\n",
       "132        - bma\n",
       "133    data:\n",
       "134        site: bma\n",
       "135        role: spine\n",
       "136        type: network_device\n",
       "137\n",
       "138leaf00.bma:\n",
       "139    hostname: 127.0.0.1\n",
       "140    username: vagrant\n",
       "141    password: vagrant\n",
       "142    port: 12443\n",
       "143    platform: eos\n",
       "144    groups:\n",
       "145        - bma\n",
       "146    data:\n",
       "147        site: bma\n",
       "148        role: leaf\n",
       "149        type: network_device\n",
       "150\n",
       "151leaf01.bma:\n",
       "152    hostname: 127.0.0.1\n",
       "153    username: vagrant\n",
       "154    password: wrong_password\n",
       "155    port: 12203\n",
       "156    platform: junos\n",
       "157    groups:\n",
       "158        - bma\n",
       "159    data:\n",
       "160        site: bma\n",
       "161        role: leaf\n",
       "162        type: network_device\n",
       "
\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# hosts file\n", "%highlight_file inventory/hosts.yaml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The hosts file is basically a map where the outermost key is the name of the host and then a `Host` object. You can see the schema of the object by executing:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"name\": \"str\",\n", " \"connection_options\": {\n", " \"$connection_type\": {\n", " \"extras\": {\n", " \"$key\": \"$value\"\n", " },\n", " \"hostname\": \"str\",\n", " \"port\": \"int\",\n", " \"username\": \"str\",\n", " \"password\": \"str\",\n", " \"platform\": \"str\"\n", " }\n", " },\n", " \"groups\": [\n", " \"$group_name\"\n", " ],\n", " \"data\": {\n", " \"$key\": \"$value\"\n", " },\n", " \"hostname\": \"str\",\n", " \"port\": \"int\",\n", " \"username\": \"str\",\n", " \"password\": \"str\",\n", " \"platform\": \"str\"\n", "}\n" ] } ], "source": [ "from nornir.core.inventory import Host\n", "import json\n", "print(json.dumps(Host.schema(), indent=4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `groups_file` follows the same rules as the `hosts_file`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
 1---\n",
       " 2global:\n",
       " 3    data:\n",
       " 4        domain: global.local\n",
       " 5        asn: 1\n",
       " 6\n",
       " 7eu:\n",
       " 8    data:\n",
       " 9        asn: 65100\n",
       "10\n",
       "11bma:\n",
       "12    groups:\n",
       "13        - eu\n",
       "14        - global\n",
       "15\n",
       "16cmh:\n",
       "17    data:\n",
       "18        asn: 65000\n",
       "19        vlans:\n",
       "20          100: frontend\n",
       "21          200: backend\n",
       "
\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# groups file\n", "%highlight_file inventory/groups.yaml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, the defaults file has the same schema as the `Host` we described before but without outer keys to denote individual elements. We will see how the data in the groups and defaults file is used later on in this tutorial." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
1---\n",
       "2data:\n",
       "3    domain: acme.local\n",
       "
\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# defaults file\n", "%highlight_file inventory/defaults.yaml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accessing the inventory\n", "\n", "You can access the [inventory](../api/nornir/core/inventory.html#module-nornir.core.inventory) with the `inventory` attribute:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'host1.cmh': Host: host1.cmh, 'host2.cmh': Host: host2.cmh, 'spine00.cmh': Host: spine00.cmh, 'spine01.cmh': Host: spine01.cmh, 'leaf00.cmh': Host: leaf00.cmh, 'leaf01.cmh': Host: leaf01.cmh, 'host1.bma': Host: host1.bma, 'host2.bma': Host: host2.bma, 'spine00.bma': Host: spine00.bma, 'spine01.bma': Host: spine01.bma, 'leaf00.bma': Host: leaf00.bma, 'leaf01.bma': Host: leaf01.bma}\n" ] } ], "source": [ "from nornir import InitNornir\n", "nr = InitNornir(config_file=\"config.yaml\")\n", "\n", "print(nr.inventory.hosts)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The inventory has two dict-like attributes `hosts` and `groups` that you can use to access the hosts and groups respectively:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'host1.cmh': Host: host1.cmh,\n", " 'host2.cmh': Host: host2.cmh,\n", " 'spine00.cmh': Host: spine00.cmh,\n", " 'spine01.cmh': Host: spine01.cmh,\n", " 'leaf00.cmh': Host: leaf00.cmh,\n", " 'leaf01.cmh': Host: leaf01.cmh,\n", " 'host1.bma': Host: host1.bma,\n", " 'host2.bma': Host: host2.bma,\n", " 'spine00.bma': Host: spine00.bma,\n", " 'spine01.bma': Host: spine01.bma,\n", " 'leaf00.bma': Host: leaf00.bma,\n", " 'leaf01.bma': Host: leaf01.bma}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nr.inventory.hosts" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'global': Group: global,\n", " 'eu': Group: eu,\n", " 'bma': Group: bma,\n", " 'cmh': Group: cmh}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nr.inventory.groups" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Host: leaf01.bma" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nr.inventory.hosts[\"leaf01.bma\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hosts and groups are also dict-like objects:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['site', 'role', 'type', 'asn', 'domain'])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "host = nr.inventory.hosts[\"leaf01.bma\"]\n", "host.keys()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "'bma'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "host[\"site\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Inheritance model\n", "\n", "Let's see how the inheritance models works by example. Let's start by looking again at the groups file:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
 1---\n",
       " 2global:\n",
       " 3    data:\n",
       " 4        domain: global.local\n",
       " 5        asn: 1\n",
       " 6\n",
       " 7eu:\n",
       " 8    data:\n",
       " 9        asn: 65100\n",
       "10\n",
       "11bma:\n",
       "12    groups:\n",
       "13        - eu\n",
       "14        - global\n",
       "15\n",
       "16cmh:\n",
       "17    data:\n",
       "18        asn: 65000\n",
       "19        vlans:\n",
       "20          100: frontend\n",
       "21          200: backend\n",
       "
\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# groups file\n", "%highlight_file inventory/groups.yaml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The host `leaf01.bma` belongs to the group `bma` which in turn belongs to the groups `eu` and `global`. The host `spine00.cmh` belongs to the group `cmh` which doesn't belong to any other group.\n", "\n", "Data resolution works by iterating recursively over all the parent groups and trying to see if that parent group (or any of it's parents) contains the data. For instance:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'global.local'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "leaf01_bma = nr.inventory.hosts[\"leaf01.bma\"]\n", "leaf01_bma[\"domain\"] # comes from the group `global`" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "65100" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "leaf01_bma[\"asn\"] # comes from group `eu`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Values in `defaults` will be returned if neither the host nor the parents have a specific value for it." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'acme.local'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "leaf01_cmh = nr.inventory.hosts[\"leaf01.cmh\"]\n", "leaf01_cmh[\"domain\"] # comes from defaults" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If nornir can't resolve the data you should get a KeyError as usual:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Couldn't find key: 'non_existent'\n" ] } ], "source": [ "try:\n", " leaf01_cmh[\"non_existent\"]\n", "except KeyError as e:\n", " print(f\"Couldn't find key: {e}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also try to access data without recursive resolution by using the `data` attribute. For example, if we try to access `leaf01_cmh.data[\"domain\"]` we should get an error as the host itself doesn't have that data:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Couldn't find key: 'domain'\n" ] } ], "source": [ "try:\n", " leaf01_cmh.data[\"domain\"]\n", "except KeyError as e:\n", " print(f\"Couldn't find key: {e}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Filtering the inventory\n", "\n", "So far we have seen that `nr.inventory.hosts` and `nr.inventory.groups` are dict-like objects that we can use to iterate over all the hosts and groups or to access any particular one directly. Now we are going to see how we can do some fancy filtering that will enable us to operate on groups of hosts based on their properties.\n", "\n", "The simpler way of filtering hosts is by `` pairs. For instance:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['host1.cmh', 'host2.cmh', 'spine00.cmh', 'spine01.cmh', 'leaf00.cmh', 'leaf01.cmh'])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nr.filter(site=\"cmh\").inventory.hosts.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also filter using multiple `` pairs:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['spine00.cmh', 'spine01.cmh'])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nr.filter(site=\"cmh\", role=\"spine\").inventory.hosts.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Filter is cumulative:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['spine00.cmh', 'spine01.cmh'])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nr.filter(site=\"cmh\").filter(role=\"spine\").inventory.hosts.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['spine00.cmh', 'spine01.cmh'])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cmh = nr.filter(site=\"cmh\")\n", "cmh.filter(role=\"spine\").inventory.hosts.keys()" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['leaf00.cmh', 'leaf01.cmh'])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cmh.filter(role=\"leaf\").inventory.hosts.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also grab the children of a group:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{Host: host1.bma,\n", " Host: host2.bma,\n", " Host: leaf00.bma,\n", " Host: leaf01.bma,\n", " Host: spine00.bma,\n", " Host: spine01.bma}" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nr.inventory.children_of_group(\"eu\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Advanced filtering\n", "\n", "Sometimes you need more fancy filtering. For those cases you have two options:\n", "\n", "1. Use a filter function.\n", "2. Use a filter object.\n", "\n", "##### Filter functions\n", "\n", "The ``filter_func`` parameter let's you run your own code to filter the hosts. The function signature is as simple as ``my_func(host)`` where host is an object of type [Host](../api/nornir/core/inventory.html#nornir.core.inventory.Host) and it has to return either ``True`` or ``False`` to indicate if you want to host or not." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['spine00.cmh', 'spine01.cmh', 'spine00.bma', 'spine01.bma'])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def has_long_name(host):\n", " return len(host.name) == 11\n", "\n", "nr.filter(filter_func=has_long_name).inventory.hosts.keys()" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['host1.cmh', 'host2.cmh', 'host1.bma', 'host2.bma'])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Or a lambda function\n", "nr.filter(filter_func=lambda h: len(h.name) == 9).inventory.hosts.keys()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Filter Object\n", "\n", "You can also use a filter objects to incrementally create a complex query objects. Let's see how it works by example:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "# first you need to import the F object\n", "from nornir.core.filter import F" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['host1.cmh', 'host2.cmh', 'spine00.cmh', 'spine01.cmh', 'leaf00.cmh', 'leaf01.cmh'])\n" ] } ], "source": [ "# hosts in group cmh\n", "cmh = nr.filter(F(groups__contains=\"cmh\"))\n", "print(cmh.inventory.hosts.keys())" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['host1.cmh', 'host2.cmh', 'spine00.cmh', 'leaf00.cmh', 'host1.bma', 'host2.bma', 'spine00.bma', 'leaf00.bma'])\n" ] } ], "source": [ "# devices running either linux or eos\n", "linux_or_eos = nr.filter(F(platform=\"linux\") | F(platform=\"eos\"))\n", "print(linux_or_eos.inventory.hosts.keys())" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['spine00.cmh', 'spine01.cmh'])\n" ] } ], "source": [ "# spines in cmh\n", "cmh_and_spine = nr.filter(F(groups__contains=\"cmh\") & F(role=\"spine\"))\n", "print(cmh_and_spine.inventory.hosts.keys())" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['host1.cmh', 'host2.cmh', 'leaf00.cmh', 'leaf01.cmh'])\n" ] } ], "source": [ "# cmh devices that are not spines\n", "cmh_and_not_spine = nr.filter(F(groups__contains=\"cmh\") & ~F(role=\"spine\"))\n", "print(cmh_and_not_spine.inventory.hosts.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also access nested data and even check if dicts/lists/strings contains elements. Again, let's see by example:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['host1.cmh'])\n" ] } ], "source": [ "nested_string_asd = nr.filter(F(nested_data__a_string__contains=\"asd\"))\n", "print(nested_string_asd.inventory.hosts.keys())" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['host2.cmh'])\n" ] } ], "source": [ "a_dict_element_equals = nr.filter(F(nested_data__a_dict__c=3))\n", "print(a_dict_element_equals.inventory.hosts.keys())" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys(['host1.cmh', 'host2.cmh'])\n" ] } ], "source": [ "a_list_contains = nr.filter(F(nested_data__a_list__contains=2))\n", "print(a_list_contains.inventory.hosts.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can basically access any nested data by separating the elements in the path with two underscores `__`. Then you can use `__contains` to check if an element exists or if a string has a particular substring." ] } ], "metadata": { "celltoolbar": "Edit Metadata", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.10" } }, "nbformat": 4, "nbformat_minor": 2 }