Filtering Deep Dive

In this tutorial, we will explore the power of nornir filtering and demonstrate why nornir is a first-class inventory management framework.

This tutorial will demonstrate some common network use-cases and how nornir filtering can be target to target hosts or groups with precision.

Tutorial Inventory

Firstly, let’s start with initialising our nornir inventory.

[21]:
# Import modules
from nornir import InitNornir
from nornir.core.filter import F

# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")

Next, let’s look at our tutorial hosts.yaml file. Don’t be overwhelmed by the size of the inventory or the content within as we will explore this in further detail later in the tutorial:

[22]:
%cat filtering_deep_dive/inventory/hosts.yaml
---
lab-csr-011.lab.norn.local:
    hostname: lab-csr-011.lab.norn.local
    groups:
        - ios
        - lab
        - mel
    data:
        mgmt_ip: 10.0.0.16
        vendor: cisco
        device_type: router
        os_version: 16.6.4
        site_code: mel

dfjt-r001.lab.norn.local:
    hostname: dfjt-r001.lab.norn.local
    groups:
        - ios
        - lab
        - bcn
    data:
        mgmt_ip: 10.0.0.1
        vendor: cisco
        device_type: router
        os_version: 16.6.3
        site_code: bcn

lab-arista-01.lab.norn.local:
    hostname: lab-arista-01.lab.norn.local
    groups:
        - eos
        - lab
        - mtl
    data:
        mgmt_ip: 10.0.0.11
        vendor: arista
        device_type: switch
        os_version: 4.22.0F
        site_code: mtl

lab-arista-02.lab.norn.local:
    hostname: lab-arista-02.lab.norn.local
    groups:
        - eos
        - lab
        - mel
    data:
        mgmt_ip: 10.0.0.18
        vendor: arista
        device_type: switch
        os_version: 4.23.2F
        site_code: mel

lab-junos-01.lab.norn.local:
    hostname: lab-junos-01.lab.norn.local
    groups:
        - junos
        - lab
        - mtl
    data:
        mgmt_ip: 10.0.0.15
        vendor: juniper
        device_type: router
        os_version: 18.4R2-S5
        site_code: mtl

lab-nxos-01.lab.norn.local:
    hostname: lab-nxos-01.lab.norn.local
    groups:
        - nxos
        - lab
        - mtl
    data:
        mgmt_ip: 10.0.0.14
        vendor: cisco
        device_type: switch
        os_version: 9.3(6)
        site_code: mtl

lab-paloalto-01.lab.djft.local:
    hostname: lab-paloalto-01.lab.djft.local
    groups:
        - panos
        - lab
        - mel
    data:
        mgmt_ip: 10.0.0.21
        vendor: palo alto
        device_type: firewall
        os_version: 10.0.3
        site_code: mel

lab-paloalto-02.lab.norn.local:
    hostname: lab-paloalto-02.lab.norn.local
    groups:
        - panos
        - lab
        - bcn
    data:
        mgmt_ip: 10.0.0.22
        vendor: palo alto
        device_type: firewall
        os_version: 9.1.3-h1
        site_code: bcn

lab-junos-06.lab.norn.local:
    hostname: lab-junos-06.lab.norn.local
    groups:
        - junos
        - lab
        - mel
    data:
        mgmt_ip: 10.0.0.23
        vendor: juniper
        device_type: switch
        os_version: 18.4R2-S5
        site_code: mel

prd-csr-01.prd.norn.local:
    hostname: prd-csr-01.prd.norn.local
    groups:
        - ios
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.16
        vendor: cisco
        device_type: router
        os_version: 16.6.4
        site_code: mel

dfjt-r001.prd.norn.local:
    hostname: dfjt-r001.prd.norn.local
    groups:
        - ios
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.1
        vendor: cisco
        device_type: router
        os_version: 16.6.3
        site_code: mel

prd-arista-01.prd.norn.local:
    hostname: prd-arista-01.prd.norn.local
    groups:
        - eos
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.11
        vendor: arista
        device_type: switch
        os_version: 4.21.1F
        site_code: mel

prd-arista-02.prd.nron.local:
    hostname: prd-arista-02.prd.norn.local
    groups:
        - eos
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.18
        vendor: arista
        device_type: switch
        os_version: 4.23.1F
        site_code: mel

prd-junos-01.prd.norn.local:
    hostname: prd-junos-01.prd.norn.local
    groups:
        - junos
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.15
        vendor: juniper
        device_type: router
        os_version: 15.1R7-S6
        site_code: mel

prd-nxos-01.prd.norn.local:
    hostname: prd-nxos-01.prd.norn.local
    groups:
        - nxos_ssh
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.14
        vendor: cisco
        device_type: switch
        os_version: 7.0(3)
        site_code: mel

prd-paloalto-01.prd.norn.local:
    hostname: prd-paloalto-01.prd.norn.local
    groups:
        - panos
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.21
        vendor: palo alto
        device_type: firewall
        os_version: 10.0.3
        site_code: mel

prd-paloalto-02.prd.norn.local:
    hostname: prd-paloalto-02.prd.norn.local
    groups:
        - panos
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.22
        vendor: palo alto
        device_type: firewall
        os_version: 9.1.6
        site_code: mel

prd-junos-06.prd.norn.local:
    hostname: prd-junos-06.prd.norn.local
    groups:
        - junos
        - prod
        - mel
    data:
        mgmt_ip: 10.0.16.23
        vendor: juniper
        device_type: switch
        os_version: 12.1R3-S4
        site_code: mel

tst-csr-01.tst.norn.local:
    hostname: tst-csr-01.tst.norn.local
    groups:
        - ios
        - test
        - mel
    data:
        mgmt_ip: 10.0.32.16
        vendor: cisco
        device_type: router
        os_version: 16.6.4
        site_code: mel

dfjt-r001.tst.norn.local:
    hostname: dfjt-r001.tst.norn.local
    groups:
        - ios
        - test
        - mel
    data:
        mgmt_ip: 10.0.32.1
        vendor: cisco
        device_type: router
        os_version: 15.1.4
        site_code: mel

tst-arista-01.tst.norn.local:
    hostname: tst-arista-01.tst.norn.local
    groups:
        - eos
        - test
        - ptl
    data:
        mgmt_ip: 10.0.32.11
        vendor: arista
        device_type: switch
        os_version: 4.21.1F
        site_code: ptl

tstt-arista-02.tst.norn.local:
    hostname: tstt-arista-02.tst.norn.local
    groups:
        - eos
        - test
        - ptl
    data:
        mgmt_ip: 10.0.32.18
        vendor: arista
        device_type: switch
        os_version: 4.21.1F
        site_code: ptl

tst-junos-01.tst.norn.local:
    hostname: tst-junos-01.tst.norn.local
    groups:
        - junos
        - test
        - ptl
    data:
        mgmt_ip: 10.0.32.15
        vendor: juniper
        device_type: router
        os_version: 15.1R7-S6
        site_code: ptl

tst-nxos-01.tst.norn.local:
    hostname: tst-nxos-01.tst.norn.local
    groups:
        - nxos
        - test
        - chc
    data:
        mgmt_ip: 10.0.32.14
        vendor: cisco
        device_type: switch
        os_version: 7.0(4)
        site_code: chc

tst-paloalto-01.tst.norn.local:
    hostname: tst-paloalto-01.tst.norn.local
    groups:
        - panos
        - test
        - chc
    data:
        mgmt_ip: 10.0.32.21
        vendor: palo alto
        device_type: firewall
        os_version: 10.0.3
        site_code: chc

tst-paloalto-02.tst.norn.local:
    hostname: tst-paloalto-02.tst.norn.local
    groups:
        - panos
        - test
        - chc
    data:
        mgmt_ip: 10.0.32.22
        vendor: palo alto
        device_type: firewall
        os_version: 8.0.8
        site_code: chc

tst-junos-06.tst.norn.local:
    hostname: tst-junos-06.tst.norn.local
    groups:
        - junos
        - test
        - chc
    data:
        mgmt_ip: 10.0.32.23
        vendor: juniper
        device_type: switch
        os_version: 12.1R3-S4
        site_code: chc

Next, let’s look at our tutorial groups.yaml file. There are a mixture of group types in here.

Some appear to be based on network operating systems, some on operating environments and others based on physical location.

nornir has no pre-conceived ideas or limits on group composition or structures:

[23]:
%cat filtering_deep_dive/inventory/groups.yaml
---
ios:
    platform: ios
    data:
        vendor: cisco
junos:
    platform: junos
    data:
        vendor: juniper
eos:
    platform: eos
    data:
        vendor: arista
nxos:
    platform: nxos
    data:
        vendor: cisco
nxos_ssh:
    platform: nxos_ssh
    data:
        vendor: cisco
panos:
    platform: paloalto_panos
    data:
        vendor: palo alto
lab:
    data:
        sla: 70
        production: false
prod:
    data:
        sla: 90
        production: true
test:
    data:
        sla: 80
        production: false
mel:
    data:
        full_name: Melbourne
        country: Australia
        region: apac
        hemisphere: southern
        site_type: primary
hbt:
    data:
        full_name: Hobart
        country: Australia
        region: apac
        hemisphere: southern
        site_type: tertiary
chc:
    data:
        full_name: Christchurch
        country: New Zealand
        region: apac
        hemisphere: southern
        site_type: secondary
ptl:
    data:
        full_name: Port Louis
        country: Mauritius
        region: amea
        hemisphere: southern
        site_type: primary
mtl:
    data:
        full_name: Montreal
        country: Canada
        region: amer
        hemisphere: northern
        site_type: primary
bcn:
    data:
        full_name: Barcelona
        country: Spain
        region: amea
        hemisphere: northern
        site_type: primary

Custom inventory data

Before we dive into filtering, it’s important to introduce some core concepts regarding custom data.

nornir allows you to populate custom data on hosts or groups objects under the data key, in whatever key/value data structure you choose. You can name these keys whatever you like to suit your business needs.

Firstly, let’s explore an extract of one host and one group in the initial inventory introduced hosts.yaml and groups.yaml file.

NOTE: These files have been cut down to only contain one entry each, and also have some comments dispersed for readability

[24]:
%cat filtering_deep_dive/inventory/hosts_extract.yaml
---
lab-csr-011.lab.norn.local:
    hostname: lab-csr-011.lab.norn.local
    groups:
        - ios
        - lab
        - mel
    data:  # Anything under this key is custom data
        mgmt_ip: 10.0.0.16  # This is custom data
        vendor: cisco  # So is this
        device_type: router  # Same as this
        os_version: 16.6.4  # Also this too
        site_code: mel  # Yes, and also this
[25]:
%cat filtering_deep_dive/inventory/groups_extract.yaml
---
mel:
    data:  # Anything under this key is custom data
        full_name: Melbourne  # This is custom data
        country: Australia  # So is this
        region: apac  # Same as this
        hemisphere: southern  # Also this too
        site_type: primary  # Yes, and also this

As you can see in the two examples above, you can use custom data in any manner you like to record any information you please. The examples above are a “flat” data structure under the data key, but you can nest your data structure in any key/value structure to suit your needs.

In the below example, we take the mgmt_ip value of 10.0.0.16, and reorient it under an alternate data structure to the key mgmt which could allow for future expansion:

[26]:
%cat filtering_deep_dive/inventory/hosts_extract_alternate.yaml
---
lab-csr-011.lab.norn.local:
    hostname: lab-csr-011.lab.norn.local
    groups:
        - ios
        - lab
        - mel
    data:  # Anything under this key is custom data
        ip_addresses:
            mgmt: 10.0.0.16  # Alternate way of managing mgmt_ip data
        vendor: cisco
        device_type: router
        os_version: 16.6.4
        site_code: mel

Naturally over time, you needs to store custom data might change and ideally you enrich your custom data with as much business information as possible.

In the example below, a new set of key/value pairs related to the location of the site are now stored under the location key. This means that any existing code leveraging the existing data structure doesn’t need to be refactored:

[27]:
%cat filtering_deep_dive/inventory/groups_extract_alternate.yaml
---
mel:
    data:  # Anything under this key is custom data
        full_name: Melbourne
        country: Australia
        region: apac
        hemisphere: southern
        site_type: primary
        location:  # New location data is stored about the site
            address: 1 Wurundjeri Street
            suburb: Northcote
            zip_code: 3070

Viewing host/group data

Before we revert back to filtering, we will show you how to access all data which is accessible or attributed to a host or group.

First, we will initialise nornir and filter the inventory on a single host lab-csr-011.lab.norn.local:

[28]:
# Import modules
from nornir import InitNornir
from nornir.core.filter import F

# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")
# Print the number of hosts in the inventory
print(f"Number of hosts in entire inventory: {len(nr.inventory.hosts)}")
# Access the host lab-csr-011.lab.norn.local using standard python key methods
single_host = nr.inventory.hosts['lab-csr-011.lab.norn.local']
# Print out the host
print(f"Single host: {single_host}")
Number of hosts in entire inventory: 27
Single host: lab-csr-011.lab.norn.local

We can now print out all the data associated to the host lab-csr-011.lab.norn.local by dumping the dictionary structure:

[29]:
# Import the JSON module
import json
# Dump the dictionary, assign to variable
single_host_data = json.dumps(single_host.dict(), indent=2)
# Print seperator
print("=" * 50)
# Print header and host data structure
print(f"Displaying information for host: {single_host}")
print(f"{single_host_data}")
# Print seperator
print("=" * 50)
==================================================
Displaying information for host: lab-csr-011.lab.norn.local
{
  "name": "lab-csr-011.lab.norn.local",
  "connection_options": {},
  "groups": [
    "ios",
    "lab",
    "mel"
  ],
  "data": {
    "mgmt_ip": "10.0.0.16",
    "vendor": "cisco",
    "device_type": "router",
    "os_version": "16.6.4",
    "site_code": "mel"
  },
  "hostname": "lab-csr-011.lab.norn.local",
  "port": null,
  "username": null,
  "password": null,
  "platform": null
}
==================================================

As you can see above, all our data related to the host is available for viewing and usage in any subsequent code. Whether we are using JSON or YAML, the data structure remains the same.

Below are some examples for displaying the values of some of the keys:

[30]:
print(f"Displaying data for host: {single_host}")
# Access the site_code value, nested under the data key
single_host_site_code = single_host.dict()["data"]["site_code"]
print(f"Site code is: {single_host_site_code}")
# Access the platform value
single_host_platform = single_host.dict()["platform"]
print(f"Platform is: {single_host_platform}")
# Access the os_version value, nested under the data key
single_host_os_version = single_host.dict()["data"]["os_version"]
print(f"OS Version is: {single_host_os_version}")
Displaying data for host: lab-csr-011.lab.norn.local
Site code is: mel
Platform is: None
OS Version is: 16.6.4

The same concept shown above with a host, also applies to a group.

As we have described how this works, we will show the same concepts as they apply to groups below, with comments:

[31]:
# Import modules
from nornir import InitNornir
from nornir.core.filter import F
import json

# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")
# Print the number of groups in the inventory
print(f"Number of groups in entire inventory: {len(nr.inventory.groups)}")
# Filter the entire group inventory on one group
single_group = nr.inventory.groups['ptl']
# Print out the group
print(f"Single group: {single_group}")

# Dump the dictionary, assign to variable
single_group_data = json.dumps(single_group.dict(), indent=2)
# Print seperator
print("=" * 50)
# Print header and host data structure
print(f"Displaying information for group: {single_group}")
print(f"{single_group_data}")
# Print seperator
print("=" * 50)

print(f"Displaying data for group: {single_group}")
# Access the full_name value, nested under the data key
single_group_site_code = single_group.dict()["data"]["full_name"]
print(f"Full Name is: {single_group_site_code}")
# Access the platform value
single_group_platform = single_group.dict()["platform"]
print(f"Platform is: {single_group_platform}")
# Access the os_version value, nested under the data key
single_group_region = single_group.dict()["data"]["region"]
print(f"Region is: {single_group_region}")
Number of groups in entire inventory: 15
Single group: ptl
==================================================
Displaying information for group: ptl
{
  "name": "ptl",
  "connection_options": {},
  "groups": [],
  "data": {
    "full_name": "Port Louis",
    "country": "Mauritius",
    "region": "amea",
    "hemisphere": "southern",
    "site_type": "primary"
  },
  "hostname": null,
  "port": null,
  "username": null,
  "password": null,
  "platform": null
}
==================================================
Displaying data for group: ptl
Full Name is: Port Louis
Platform is: None
Region is: amea
By now, you should have an understanding of the following regarding inventory custom data:
- What is nornir custom data?
- How can it be stored?
- How can I access it?
- How can I view and troubleshoot it?

In the next section, we will now revert back to nornir filtering, which will build off your understanding of these concepts.

Filtering Types

nornir offers three types of filtering capabilities:

  • basic/intermediate filtering using the filter method
  • advanced filtering using the F object
  • advanced filtering using filter functions

It should be noted that the F object filtering method can address all filtering requirements of the filter method, but the filter method cannot address all the filtering requirements of the F object. This tutorial will explore both filtering methods, so you can understand their potential use-cases, limitations and trade-offs.

Basic Filtering

Let’s explore the filter method, which can used for straight forward filtering requirements. We will reinitialise the same nornir inventory introduced in the top of this tutorial:

[32]:
# Import modules
from nornir import InitNornir
from nornir.core.filter import F

# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")
# Informational printouts
print(f"There are {len(nr.inventory.hosts)} hosts in this tutorial inventory.")
print(f"There are {len(nr.inventory.groups)} groups in this tutorial inventory.")
There are 27 hosts in this tutorial inventory.
There are 15 groups in this tutorial inventory.

The basic filter method allows to filter on any key attributable to a host or a group. In the example below, we will filter the inventory on a single host:

[33]:
# Assign filter value to a variable for usage
filter_query = "lab-arista-02.lab.norn.local"
# Filter inventory for all hostnames which equal the filter value
target_hosts = nr.filter(hostname=filter_query)
print(f"Filtering entire inventory on {filter_query} ...")
print(f"Total results found: {len(target_hosts.inventory.hosts)}")
# Iterate over filtered results and printout information
for host, data in target_hosts.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Hostname: {data.hostname}"
    )
Filtering entire inventory on lab-arista-02.lab.norn.local ...
Total results found: 1
Host: lab-arista-02.lab.norn.local - Hostname: lab-arista-02.lab.norn.local

Host-based basic filters

Let’s look at some more useful examples below whereby we filter on properties directly attributed to a host, such as:

  • filter hosts by vendor
  • filter hosts by device_type
  • filter hosts by mgmt_ip
[34]:
# Import modules
from nornir import InitNornir
# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")

# Use Case 1 - filter hosts by vendor
# Assign vendor to variable for later usage
vendor="palo alto"
# Execute filter based on vendor
target_hosts = nr.filter(vendor=vendor)
# Print seperator and header
print("=" * 50)
print(f"The hosts which have vendor {vendor} are:")
# Iterate over filtered results and printout information
for host, data in target_hosts.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Vendor: {data['vendor']}"
    )
# Print total and seperator
print(f"Total: {len(target_hosts.inventory.hosts.items())}")
print("=" * 50)

# Use Case 2 - filter hosts by device_type
# Assign device_type to variable for later usage
device_type="switch"
# Execute filter based on device_type
target_hosts = nr.filter(device_type=device_type)
# Print seperator and header
print("=" * 50)
print(f"The hosts which have device type {device_type} are:")
# Iterate over filtered results and printout information
for host, data in target_hosts.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Device Type: {data['device_type']}"
    )
# Print total and seperator
print(f"Total: {len(target_hosts.inventory.hosts.items())}")
print("=" * 50)


# Use Case 3 - filter hosts by mgmt_ip
# Assign mgmt_ip to variable for later usage
mgmt_ip="10.0.0.16"
# Execute filter based on mgmt_ip
target_hosts = nr.filter(mgmt_ip=mgmt_ip)
# Print seperator and header
print("=" * 50)
print(f"The hosts which have management IP address {mgmt_ip} are:")
# Iterate over filtered results and printout information
for host, data in target_hosts.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Management IP Address: {data['mgmt_ip']}"
    )
# Print total and seperator
print(f"Total: {len(target_hosts.inventory.hosts.items())}")
print("=" * 50)
==================================================
The hosts which have vendor palo alto are:
Host: lab-paloalto-01.lab.djft.local - Vendor: palo alto
Host: lab-paloalto-02.lab.norn.local - Vendor: palo alto
Host: prd-paloalto-01.prd.norn.local - Vendor: palo alto
Host: prd-paloalto-02.prd.norn.local - Vendor: palo alto
Host: tst-paloalto-01.tst.norn.local - Vendor: palo alto
Host: tst-paloalto-02.tst.norn.local - Vendor: palo alto
Total: 6
==================================================
==================================================
The hosts which have device type switch are:
Host: lab-arista-01.lab.norn.local - Device Type: switch
Host: lab-arista-02.lab.norn.local - Device Type: switch
Host: lab-nxos-01.lab.norn.local - Device Type: switch
Host: lab-junos-06.lab.norn.local - Device Type: switch
Host: prd-arista-01.prd.norn.local - Device Type: switch
Host: prd-arista-02.prd.nron.local - Device Type: switch
Host: prd-nxos-01.prd.norn.local - Device Type: switch
Host: prd-junos-06.prd.norn.local - Device Type: switch
Host: tst-arista-01.tst.norn.local - Device Type: switch
Host: tstt-arista-02.tst.norn.local - Device Type: switch
Host: tst-nxos-01.tst.norn.local - Device Type: switch
Host: tst-junos-06.tst.norn.local - Device Type: switch
Total: 12
==================================================
==================================================
The hosts which have management IP address 10.0.0.16 are:
Host: lab-csr-011.lab.norn.local - Management IP Address: 10.0.0.16
Total: 1
==================================================

Host-based basic filters (from group inheritance)

As nornir uses an inheritance model, we filter on group-based attributes which are inherited down to the host.

In the examples below, we will filter the inventory based on some group-based attributes which inherited from their group values:

  • filter hosts by platform
  • filter hosts by production equals false
  • filter hosts by site_type of secondary
[35]:
# Import modules
from nornir import InitNornir
# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")

# Use Case 1 - filter hosts by platform
# Assign platform to variable for later usage
platform="junos"
# Execute filter based on platform
target_hosts = nr.filter(platform=platform)
# Print seperator and header
print("=" * 50)
print(f"The hosts which have platform {platform} are:")
# Iterate over filtered results and printout information
for host, data in target_hosts.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data.platform}"
    )
# Print total and seperator
print(f"Total: {len(target_hosts.inventory.hosts.items())}")
print("=" * 50)

# Use Case 2 - filter hosts by production equals false
# Assign production equals false to variable for later usage
production = False
# Execute filter based on production
target_hosts = nr.filter(production=production)
# Print seperator and header
print("=" * 50)
print(f"The hosts which have production boolean value of {production} are:")
# Iterate over filtered results and printout information
for host, data in target_hosts.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Production: {data['production']}"
    )
# Print total and seperator
print(f"Total: {len(target_hosts.inventory.hosts.items())}")
print("=" * 50)


# Use Case 3 - filter hosts by site_type of tertiary
# Assign site_type of secondary to variable for later usage
site_type="secondary"
# Execute filter based on site_type
target_hosts = nr.filter(site_type=site_type)
# Print seperator and header
print("=" * 50)
print(f"The hosts which have site_type of {site_type} are:")
# Iterate over filtered results and printout information
for host, data in target_hosts.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Site Type: {data['site_type']}"
    )
# Print total and seperator
print(f"Total: {len(target_hosts.inventory.hosts.items())}")
print("=" * 50)
==================================================
The hosts which have platform junos are:
Host: lab-junos-01.lab.norn.local - Platform: junos
Host: lab-junos-06.lab.norn.local - Platform: junos
Host: prd-junos-01.prd.norn.local - Platform: junos
Host: prd-junos-06.prd.norn.local - Platform: junos
Host: tst-junos-01.tst.norn.local - Platform: junos
Host: tst-junos-06.tst.norn.local - Platform: junos
Total: 6
==================================================
==================================================
The hosts which have production boolean value of False are:
Host: lab-csr-011.lab.norn.local - Production: False
Host: dfjt-r001.lab.norn.local - Production: False
Host: lab-arista-01.lab.norn.local - Production: False
Host: lab-arista-02.lab.norn.local - Production: False
Host: lab-junos-01.lab.norn.local - Production: False
Host: lab-nxos-01.lab.norn.local - Production: False
Host: lab-paloalto-01.lab.djft.local - Production: False
Host: lab-paloalto-02.lab.norn.local - Production: False
Host: lab-junos-06.lab.norn.local - Production: False
Host: tst-csr-01.tst.norn.local - Production: False
Host: dfjt-r001.tst.norn.local - Production: False
Host: tst-arista-01.tst.norn.local - Production: False
Host: tstt-arista-02.tst.norn.local - Production: False
Host: tst-junos-01.tst.norn.local - Production: False
Host: tst-nxos-01.tst.norn.local - Production: False
Host: tst-paloalto-01.tst.norn.local - Production: False
Host: tst-paloalto-02.tst.norn.local - Production: False
Host: tst-junos-06.tst.norn.local - Production: False
Total: 18
==================================================
==================================================
The hosts which have site_type of secondary are:
Host: tst-nxos-01.tst.norn.local - Site Type: secondary
Host: tst-paloalto-01.tst.norn.local - Site Type: secondary
Host: tst-paloalto-02.tst.norn.local - Site Type: secondary
Host: tst-junos-06.tst.norn.local - Site Type: secondary
Total: 4
==================================================

Intermediate filtering

We can chain or join filters together using the filter method to be narrow down results and end up with specific devices.

In the examples below, we will use output of a filter result and feed that into the next filter and filter incrementally to end up with a specific result of the Christchurch Cisco Switch.

[36]:
# Import modules
from nornir import InitNornir
# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")
# Filter all devices for Cisco devices
cisco_devices = nr.filter(vendor="cisco")
# Print seperator and header
print("=" * 50)
print("All cisco devices")
# Iterate over filtered results and printout information
for host, data in cisco_devices.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Vendor: {data['vendor']}"
    )
# Print total and seperator
print(f"Total: {len(cisco_devices.inventory.hosts.items())}")
print("=" * 50)
# Filter all Cisco devices for Cisco switches
cisco_switches = cisco_devices.filter(device_type="switch")
# Print seperator and header
print("=" * 50)
print("All cisco switches")
# Iterate over filtered results and printout information
for host, data in cisco_switches.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Device Type: {data['device_type']}"
    )
# Print total and seperator
print(f"Total: {len(cisco_switches.inventory.hosts.items())}")
print("=" * 50)
# Filter all Cisco switches for Christchurch switches
chc_cisco_switches = cisco_switches.filter(full_name="Christchurch")
# Print seperator and header
print("=" * 50)
print("All Christchuch cisco switches")
# Iterate over filtered results and printout information
for host, data in chc_cisco_switches.inventory.hosts.items():
    print(
        f"Host: {host} \n"
        + " " * 8 + f"- Vendor: {data['vendor']}\n"
        + " " * 8 + f"- Device Type: {data['device_type']}\n"
        + " " * 8 + f"- Site Name: {data['full_name']}"
    )
# Print total and seperator
print(f"Total: {len(chc_cisco_switches.inventory.hosts.items())}")
print("=" * 50)
==================================================
All cisco devices
Host: lab-csr-011.lab.norn.local - Vendor: cisco
Host: dfjt-r001.lab.norn.local - Vendor: cisco
Host: lab-nxos-01.lab.norn.local - Vendor: cisco
Host: prd-csr-01.prd.norn.local - Vendor: cisco
Host: dfjt-r001.prd.norn.local - Vendor: cisco
Host: prd-nxos-01.prd.norn.local - Vendor: cisco
Host: tst-csr-01.tst.norn.local - Vendor: cisco
Host: dfjt-r001.tst.norn.local - Vendor: cisco
Host: tst-nxos-01.tst.norn.local - Vendor: cisco
Total: 9
==================================================
==================================================
All cisco switches
Host: lab-nxos-01.lab.norn.local - Device Type: switch
Host: prd-nxos-01.prd.norn.local - Device Type: switch
Host: tst-nxos-01.tst.norn.local - Device Type: switch
Total: 3
==================================================
==================================================
All Christchuch cisco switches
Host: tst-nxos-01.tst.norn.local
        - Vendor: cisco
        - Device Type: switch
        - Site Name: Christchurch
Total: 1
==================================================

Advanced filtering

The final and most powerful method of filtering is using the F object. As mentioned in sections above, the F object can perform all forms of filtering.

In this section we will cover:
- F object filter operators
- F object filter operations
- filter functions using filter_func

F object filter operators

There are three F filter operators which are described below:

Character Pattern Description
~ NOT
& AND
\| OR

In the proceeding code, we will show examples of these in use:

[37]:
# Import modules
from nornir import InitNornir
from nornir.core.filter import F
# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")
# Use Case 1 - NOT operator
# Filter for NOT equals platform of "junos"
not_junos = nr.filter(~F(platform__eq="junos"))
# Print seperator and header
print("=" * 50)
print("All non-junos devices - using NOT operator")
# Iterate over filtered results and printout information
for host, data in not_junos.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data.platform}"
    )
# Print total and seperator
print(f"Total: {len(not_junos.inventory.hosts.items())}")
print("=" * 50)
# Use Case 2 - AND operator
# Filter for platform equals "nxos" AND device_type equals "switch"
nxos_switches = nr.filter(F(platform__eq="nxos") & F(device_type__eq="switch"))
# Print seperator and header
print("=" * 50)
print("All nxos switches - using AND operator")
# Iterate over filtered results and printout information
for host, data in nxos_switches.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data.platform} "
        + f"- Device Type: {data['device_type']}"
    )
# Print total and seperator
print(f"Total: {len(nxos_switches.inventory.hosts.items())}")
print("=" * 50)
# Use Case 3 - OR operator
# Filter for site code equals "ptl" OR site code equals "chc"
ptl_or_chc_devices = nr.filter(
    F(site_code__eq="ptl") | F(site_code__eq="chc")
)
print("=" * 50)
print("All ptl or chc site code devices - using OR operator")
# Iterate over filtered results and printout information
for host, data in ptl_or_chc_devices.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Site Code: {data['site_code']}"
    )
# Print total and seperator
print(f"Total: {len(ptl_or_chc_devices.inventory.hosts.items())}")
print("=" * 50)
==================================================
All non-junos devices - using NOT operator
Host: lab-csr-011.lab.norn.local - Platform: ios
Host: dfjt-r001.lab.norn.local - Platform: ios
Host: lab-arista-01.lab.norn.local - Platform: eos
Host: lab-arista-02.lab.norn.local - Platform: eos
Host: lab-nxos-01.lab.norn.local - Platform: nxos
Host: lab-paloalto-01.lab.djft.local - Platform: paloalto_panos
Host: lab-paloalto-02.lab.norn.local - Platform: paloalto_panos
Host: prd-csr-01.prd.norn.local - Platform: ios
Host: dfjt-r001.prd.norn.local - Platform: ios
Host: prd-arista-01.prd.norn.local - Platform: eos
Host: prd-arista-02.prd.nron.local - Platform: eos
Host: prd-nxos-01.prd.norn.local - Platform: nxos_ssh
Host: prd-paloalto-01.prd.norn.local - Platform: paloalto_panos
Host: prd-paloalto-02.prd.norn.local - Platform: paloalto_panos
Host: tst-csr-01.tst.norn.local - Platform: ios
Host: dfjt-r001.tst.norn.local - Platform: ios
Host: tst-arista-01.tst.norn.local - Platform: eos
Host: tstt-arista-02.tst.norn.local - Platform: eos
Host: tst-nxos-01.tst.norn.local - Platform: nxos
Host: tst-paloalto-01.tst.norn.local - Platform: paloalto_panos
Host: tst-paloalto-02.tst.norn.local - Platform: paloalto_panos
Total: 21
==================================================
==================================================
All nxos switches - using AND operator
Host: lab-nxos-01.lab.norn.local - Platform: nxos - Device Type: switch
Host: tst-nxos-01.tst.norn.local - Platform: nxos - Device Type: switch
Total: 2
==================================================
==================================================
All ptl or chc site code devices - using OR operator
Host: tst-arista-01.tst.norn.local - Site Code: ptl
Host: tstt-arista-02.tst.norn.local - Site Code: ptl
Host: tst-junos-01.tst.norn.local - Site Code: ptl
Host: tst-nxos-01.tst.norn.local - Site Code: chc
Host: tst-paloalto-01.tst.norn.local - Site Code: chc
Host: tst-paloalto-02.tst.norn.local - Site Code: chc
Host: tst-junos-06.tst.norn.local - Site Code: chc
Total: 7
==================================================

F object operations

There are eleven F filter operations which are described below:

Character Pattern Description Type Usage
eq Equals string, integer
ge Greater than or equal to integer
gt Greater than integer
le Less than or equal to integer
lt Less than integer
contains Contains string
startswith Starts with string
endswith ends with string
any Any of the following string
has_parent_group Host has a parent group string
in In string
all All of list
In the proceeding code, we will show examples of these in use.
NOTE: These are spread out over multiple snippets of code to cut down the amount of interpreting you need to perform at once
[38]:
# Import modules
from nornir import InitNornir
from nornir.core.filter import F
# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")
# Use Case 1 - eq operation
# Filter for equals platform of "junos"
junos_devices = nr.filter(F(platform__eq="junos"))
# Print seperator and header
print("=" * 50)
print("All junos devices - using eq operation")
# Iterate over filtered results and printout information
for host, data in junos_devices.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data.platform}"
    )
# Print total and seperator
print(f"Total: {len(junos_devices.inventory.hosts.items())}")
print("=" * 50)
# Use Case 2 - ge operation
# Filter for sla greater than or equal to 80
sla_eighty_or_greater = nr.filter(F(sla__ge=80))
# Print seperator and header
print("=" * 50)
print("All devices with SLA greater than or equal to 80 - using gt operation")
# Iterate over filtered results and printout information
for host, data in sla_eighty_or_greater.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- SLA: {data['sla']}"
    )
# Print total and seperator
print(f"Total: {len(sla_eighty_or_greater.inventory.hosts.items())}")
print("=" * 50)
# Use Case 3 - gt operation
# Filter for sla greater than 79
sla_more_than_seventy_nine = nr.filter(F(sla__gt=79))
# Print seperator and header
print("=" * 50)
print("All devices with SLA greater than 79 - using gt operation")
# Iterate over filtered results and printout information
for host, data in sla_more_than_seventy_nine.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- SLA: {data['sla']}"
    )
# Print total and seperator
print(f"Total: {len(sla_more_than_seventy_nine.inventory.hosts.items())}")
print("=" * 50)
# Use Case 4 - le operation
# Filter for sla lesser than or equal to 80
sla_eighty_or_lesser = nr.filter(F(sla__le=80))
# Print seperator and header
print("=" * 50)
print("All devices with SLA less than or equal to 80 - using le operation")
# Iterate over filtered results and printout information
for host, data in sla_eighty_or_lesser.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- SLA: {data['sla']}"
    )
# Print total and seperator
print(f"Total: {len(sla_eighty_or_lesser.inventory.hosts.items())}")
print("=" * 50)
# Use Case 4 - lt operation
# Filter for sla lesser than 80
sla_less_than_eighty = nr.filter(F(sla__lt=80))
# Print seperator and header
print("=" * 50)
print("All devices with SLA less than 80 - using lt operation")
# Iterate over filtered results and printout information
for host, data in sla_less_than_eighty.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- SLA: {data['sla']}"
    )
# Print total and seperator
print(f"Total: {len(sla_less_than_eighty.inventory.hosts.items())}")
print("=" * 50)
==================================================
All junos devices - using eq operation
Host: lab-junos-01.lab.norn.local - Platform: junos
Host: lab-junos-06.lab.norn.local - Platform: junos
Host: prd-junos-01.prd.norn.local - Platform: junos
Host: prd-junos-06.prd.norn.local - Platform: junos
Host: tst-junos-01.tst.norn.local - Platform: junos
Host: tst-junos-06.tst.norn.local - Platform: junos
Total: 6
==================================================
==================================================
All devices with SLA greater than or equal to 80 - using gt operation
Host: prd-csr-01.prd.norn.local - SLA: 90
Host: dfjt-r001.prd.norn.local - SLA: 90
Host: prd-arista-01.prd.norn.local - SLA: 90
Host: prd-arista-02.prd.nron.local - SLA: 90
Host: prd-junos-01.prd.norn.local - SLA: 90
Host: prd-nxos-01.prd.norn.local - SLA: 90
Host: prd-paloalto-01.prd.norn.local - SLA: 90
Host: prd-paloalto-02.prd.norn.local - SLA: 90
Host: prd-junos-06.prd.norn.local - SLA: 90
Host: tst-csr-01.tst.norn.local - SLA: 80
Host: dfjt-r001.tst.norn.local - SLA: 80
Host: tst-arista-01.tst.norn.local - SLA: 80
Host: tstt-arista-02.tst.norn.local - SLA: 80
Host: tst-junos-01.tst.norn.local - SLA: 80
Host: tst-nxos-01.tst.norn.local - SLA: 80
Host: tst-paloalto-01.tst.norn.local - SLA: 80
Host: tst-paloalto-02.tst.norn.local - SLA: 80
Host: tst-junos-06.tst.norn.local - SLA: 80
Total: 18
==================================================
==================================================
All devices with SLA greater than 79 - using gt operation
Host: prd-csr-01.prd.norn.local - SLA: 90
Host: dfjt-r001.prd.norn.local - SLA: 90
Host: prd-arista-01.prd.norn.local - SLA: 90
Host: prd-arista-02.prd.nron.local - SLA: 90
Host: prd-junos-01.prd.norn.local - SLA: 90
Host: prd-nxos-01.prd.norn.local - SLA: 90
Host: prd-paloalto-01.prd.norn.local - SLA: 90
Host: prd-paloalto-02.prd.norn.local - SLA: 90
Host: prd-junos-06.prd.norn.local - SLA: 90
Host: tst-csr-01.tst.norn.local - SLA: 80
Host: dfjt-r001.tst.norn.local - SLA: 80
Host: tst-arista-01.tst.norn.local - SLA: 80
Host: tstt-arista-02.tst.norn.local - SLA: 80
Host: tst-junos-01.tst.norn.local - SLA: 80
Host: tst-nxos-01.tst.norn.local - SLA: 80
Host: tst-paloalto-01.tst.norn.local - SLA: 80
Host: tst-paloalto-02.tst.norn.local - SLA: 80
Host: tst-junos-06.tst.norn.local - SLA: 80
Total: 18
==================================================
==================================================
All devices with SLA less than or equal to 80 - using le operation
Host: lab-csr-011.lab.norn.local - SLA: 70
Host: dfjt-r001.lab.norn.local - SLA: 70
Host: lab-arista-01.lab.norn.local - SLA: 70
Host: lab-arista-02.lab.norn.local - SLA: 70
Host: lab-junos-01.lab.norn.local - SLA: 70
Host: lab-nxos-01.lab.norn.local - SLA: 70
Host: lab-paloalto-01.lab.djft.local - SLA: 70
Host: lab-paloalto-02.lab.norn.local - SLA: 70
Host: lab-junos-06.lab.norn.local - SLA: 70
Host: tst-csr-01.tst.norn.local - SLA: 80
Host: dfjt-r001.tst.norn.local - SLA: 80
Host: tst-arista-01.tst.norn.local - SLA: 80
Host: tstt-arista-02.tst.norn.local - SLA: 80
Host: tst-junos-01.tst.norn.local - SLA: 80
Host: tst-nxos-01.tst.norn.local - SLA: 80
Host: tst-paloalto-01.tst.norn.local - SLA: 80
Host: tst-paloalto-02.tst.norn.local - SLA: 80
Host: tst-junos-06.tst.norn.local - SLA: 80
Total: 18
==================================================
==================================================
All devices with SLA less than 80 - using lt operation
Host: lab-csr-011.lab.norn.local - SLA: 70
Host: dfjt-r001.lab.norn.local - SLA: 70
Host: lab-arista-01.lab.norn.local - SLA: 70
Host: lab-arista-02.lab.norn.local - SLA: 70
Host: lab-junos-01.lab.norn.local - SLA: 70
Host: lab-nxos-01.lab.norn.local - SLA: 70
Host: lab-paloalto-01.lab.djft.local - SLA: 70
Host: lab-paloalto-02.lab.norn.local - SLA: 70
Host: lab-junos-06.lab.norn.local - SLA: 70
Total: 9
==================================================
[39]:
# Use Case 5 - contains operation
# Filter for a platform that contains "nos" in it.
# i.e match "paloalto_panos" or "junos" but not "nxos"
network_nos = nr.filter(F(platform__contains="nos"))
# Print seperator and header
print("=" * 50)
print("All devices contain 'nos' in platform - using contain operation")
# Iterate over filtered results and printout information
for host, data in network_nos.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data.platform}"
    )
# Print total and seperator
print(f"Total: {len(network_nos.inventory.hosts.items())}")
print("=" * 50)
# Use Case 6 - startswith operation
# Filter for a platform that starts with "am".
# i.e match "amer", "amea" but not "apac"
am_devs = nr.filter(F(region__startswith="am"))
# Print seperator and header
print("=" * 50)
print("All devices starts with 'am' in region - using startswith operation")
# Iterate over filtered results and printout information
for host, data in am_devs.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data['region']}"
    )
# Print total and seperator
print(f"Total: {len(am_devs.inventory.hosts.items())}")
print("=" * 50)
# Use Case 7 - startswith operation
# Filter for a platform that ends with "l".
# i.e match "mtl", "mel" or "ptl", but not "chc", "hbt" or "bcn"
l_site_code_devs = nr.filter(F(site_code__endswith="l"))
# Print seperator and header
print("=" * 50)
print("All devices ends with 'l' in site code - using endswith operation")
# Iterate over filtered results and printout information
for host, data in l_site_code_devs.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data['site_code']}"
    )
# Print total and seperator
print(f"Total: {len(l_site_code_devs.inventory.hosts.items())}")
print("=" * 50)
# Use Case 8 - startswith operation
# Filter for a platform that is any of elements in our cisco_platforms
# list
cisco_platforms = ["catos", "ios", "iosxe", "iosxr", "nxos"]
cisco_devs = nr.filter(F(platform__any=cisco_platforms))
# Print seperator and header
print("=" * 50)
print(f"All devices which contains {cisco_platforms} - using contains operation")
# Iterate over filtered results and printout information
for host, data in cisco_devs.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data.platform}"
    )
# Print total and seperator
print(f"Total: {len(cisco_devs.inventory.hosts.items())}")
print("=" * 50)
# Use Case 9 - has_parent_group operation
# Filter for any hosts which have the parent group of test
test_devs  = nr.filter(F(has_parent_group="test"))
# Print seperator and header
print("=" * 50)
print(f"All devices which has parent group of test - using has_parent_group operation")
# Iterate over filtered results and printout information
for host, data in test_devs.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Production: {data['production']}"
    )
# Print total and seperator
print(f"Total: {len(test_devs.inventory.hosts.items())}")
print("=" * 50)
# Use Case 10 - in operation
# Filter for a platform that is in one of elements in our non_cisco_platforms
# list
# NOTE: Not sure how different this is to the "any" operation in case you
# were wondering too.
non_cisco_platforms = ["eos", "paloalto_panos", "junos"]
non_cisco_devs = nr.filter(F(platform__in=non_cisco_platforms))
# Print seperator and header
print("=" * 50)
print(f"All devices which are in the list {non_cisco_platforms} - using in operation")
# Iterate over filtered results and printout information
for host, data in non_cisco_devs.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Platform: {data.platform}"
    )
# Print total and seperator
print(f"Total: {len(non_cisco_devs.inventory.hosts.items())}")
print("=" * 50)
# Use Case 11 - all operation
# Filter for a device which has all groups
all_groups = ["eos","prod", "mel"]
eos_prod_mel = nr.filter(F(groups__all=all_groups))
# Print seperator and header
print("=" * 50)
print(f"All devices which all the groups {all_groups} - using all operation")
# Iterate over filtered results and printout information
for host, data in eos_prod_mel.inventory.hosts.items():
    print(
        f"Host: {host} "
        + f"- Groups: {data.groups}"
    )
# Print total and seperator
print(f"Total: {len(eos_prod_mel.inventory.hosts.items())}")
print("=" * 50)
==================================================
All devices contain 'nos' in platform - using contain operation
Host: lab-junos-01.lab.norn.local - Platform: junos
Host: lab-paloalto-01.lab.djft.local - Platform: paloalto_panos
Host: lab-paloalto-02.lab.norn.local - Platform: paloalto_panos
Host: lab-junos-06.lab.norn.local - Platform: junos
Host: prd-junos-01.prd.norn.local - Platform: junos
Host: prd-paloalto-01.prd.norn.local - Platform: paloalto_panos
Host: prd-paloalto-02.prd.norn.local - Platform: paloalto_panos
Host: prd-junos-06.prd.norn.local - Platform: junos
Host: tst-junos-01.tst.norn.local - Platform: junos
Host: tst-paloalto-01.tst.norn.local - Platform: paloalto_panos
Host: tst-paloalto-02.tst.norn.local - Platform: paloalto_panos
Host: tst-junos-06.tst.norn.local - Platform: junos
Total: 12
==================================================
==================================================
All devices starts with 'am' in region - using startswith operation
Host: dfjt-r001.lab.norn.local - Platform: amea
Host: lab-arista-01.lab.norn.local - Platform: amer
Host: lab-junos-01.lab.norn.local - Platform: amer
Host: lab-nxos-01.lab.norn.local - Platform: amer
Host: lab-paloalto-02.lab.norn.local - Platform: amea
Host: tst-arista-01.tst.norn.local - Platform: amea
Host: tstt-arista-02.tst.norn.local - Platform: amea
Host: tst-junos-01.tst.norn.local - Platform: amea
Total: 8
==================================================
==================================================
All devices ends with 'l' in site code - using endswith operation
Host: lab-csr-011.lab.norn.local - Platform: mel
Host: lab-arista-01.lab.norn.local - Platform: mtl
Host: lab-arista-02.lab.norn.local - Platform: mel
Host: lab-junos-01.lab.norn.local - Platform: mtl
Host: lab-nxos-01.lab.norn.local - Platform: mtl
Host: lab-paloalto-01.lab.djft.local - Platform: mel
Host: lab-junos-06.lab.norn.local - Platform: mel
Host: prd-csr-01.prd.norn.local - Platform: mel
Host: dfjt-r001.prd.norn.local - Platform: mel
Host: prd-arista-01.prd.norn.local - Platform: mel
Host: prd-arista-02.prd.nron.local - Platform: mel
Host: prd-junos-01.prd.norn.local - Platform: mel
Host: prd-nxos-01.prd.norn.local - Platform: mel
Host: prd-paloalto-01.prd.norn.local - Platform: mel
Host: prd-paloalto-02.prd.norn.local - Platform: mel
Host: prd-junos-06.prd.norn.local - Platform: mel
Host: tst-csr-01.tst.norn.local - Platform: mel
Host: dfjt-r001.tst.norn.local - Platform: mel
Host: tst-arista-01.tst.norn.local - Platform: ptl
Host: tstt-arista-02.tst.norn.local - Platform: ptl
Host: tst-junos-01.tst.norn.local - Platform: ptl
Total: 21
==================================================
==================================================
All devices which contains ['catos', 'ios', 'iosxe', 'iosxr', 'nxos'] - using contains operation
Host: lab-csr-011.lab.norn.local - Platform: ios
Host: dfjt-r001.lab.norn.local - Platform: ios
Host: lab-nxos-01.lab.norn.local - Platform: nxos
Host: prd-csr-01.prd.norn.local - Platform: ios
Host: dfjt-r001.prd.norn.local - Platform: ios
Host: tst-csr-01.tst.norn.local - Platform: ios
Host: dfjt-r001.tst.norn.local - Platform: ios
Host: tst-nxos-01.tst.norn.local - Platform: nxos
Total: 8
==================================================
==================================================
All devices which has parent group of test - using has_parent_group operation
Host: tst-csr-01.tst.norn.local - Production: False
Host: dfjt-r001.tst.norn.local - Production: False
Host: tst-arista-01.tst.norn.local - Production: False
Host: tstt-arista-02.tst.norn.local - Production: False
Host: tst-junos-01.tst.norn.local - Production: False
Host: tst-nxos-01.tst.norn.local - Production: False
Host: tst-paloalto-01.tst.norn.local - Production: False
Host: tst-paloalto-02.tst.norn.local - Production: False
Host: tst-junos-06.tst.norn.local - Production: False
Total: 9
==================================================
==================================================
All devices which are in the list ['eos', 'paloalto_panos', 'junos'] - using in operation
Host: lab-arista-01.lab.norn.local - Platform: eos
Host: lab-arista-02.lab.norn.local - Platform: eos
Host: lab-junos-01.lab.norn.local - Platform: junos
Host: lab-paloalto-01.lab.djft.local - Platform: paloalto_panos
Host: lab-paloalto-02.lab.norn.local - Platform: paloalto_panos
Host: lab-junos-06.lab.norn.local - Platform: junos
Host: prd-arista-01.prd.norn.local - Platform: eos
Host: prd-arista-02.prd.nron.local - Platform: eos
Host: prd-junos-01.prd.norn.local - Platform: junos
Host: prd-paloalto-01.prd.norn.local - Platform: paloalto_panos
Host: prd-paloalto-02.prd.norn.local - Platform: paloalto_panos
Host: prd-junos-06.prd.norn.local - Platform: junos
Host: tst-arista-01.tst.norn.local - Platform: eos
Host: tstt-arista-02.tst.norn.local - Platform: eos
Host: tst-junos-01.tst.norn.local - Platform: junos
Host: tst-paloalto-01.tst.norn.local - Platform: paloalto_panos
Host: tst-paloalto-02.tst.norn.local - Platform: paloalto_panos
Host: tst-junos-06.tst.norn.local - Platform: junos
Total: 18
==================================================
==================================================
All devices which all the groups ['eos', 'prod', 'mel'] - using all operation
Host: prd-arista-01.prd.norn.local - Groups: [Group: eos, Group: prod, Group: mel]
Host: prd-arista-02.prd.nron.local - Groups: [Group: eos, Group: prod, Group: mel]
Total: 2
==================================================

Filter functions

Filter functions are a powerful method of filtering your nornir inventory through another python function.

Using this method, you can write complex filtering logic and manage that in another function, then simply call them as needed.

In this example, we will introduce a few examples using regex to perform some analysis based on the hostnames, then call those from another function. This is just one idea of many that this can be used for so hopefully it helps spark your imagination.

[40]:
# Import modules
from nornir import InitNornir
from nornir.core.filter import F
import re
# Initialise nornir
nr = InitNornir(config_file="filtering_deep_dive/config.yaml")
"""
Below are some example filter functions, which we will call in later functions
"""

def even_device_naming_convention(host):
    """
    Helper filter function to filter hosts based targeting
    host names which have even number names.
    Examples:
        - lab-junos-08.prd.norn.local
        - lab-arista-22.tst.norn.local
        - lab-nxos-64.lab.norn.local
    :param host: The host you want to filter on
    :return bool: True if it matches, False if it doesn't match
    """
    # Perform regex match on host name and return boolean
    if re.match(".+\-[0-9][2,4,6,8,0].+", host.name):
        return True
    else:
        return False


def device_name_convention(host):
    """
    Helper filter function to filter hosts based targeting
    host names which a specified naming convention
    Examples:
        - lab-junos-08.tst.norn.local
        - lab-arista-22.prd.norn.local
        - lab-nxos-01.lab.norn.local
    :param host: The host you want to filter on
    :return bool: True if it matches, False if it doesn't match
    """
    # Perform regex match on host name and return boolean
    if re.match("\w{3}\-\w+\-\d{2}.\w{3}.norn.local", host.name):
        return True
    else:
        return False


def non_device_name_convention(host):
    """
    Helper filter function to filter hosts based targeting
    host names which do NOT match a specified naming convention
    Examples:
        - lab-junos-08.tstt.norn.local
        - dfjt-arista-22.prd.norn.local
        - lab-nxos-001.lab.nron.local
    :param host: The host you want to filter on
    :return bool: True if does not match, False if it matches the convention
    """
    # Perform regex match on host name and return boolean
    if re.match("\w{3}\-\w+\-\d{2}.\w{3}.norn.local", host.name):
        return False
    else:
        return True


def filter_functions(nr):
    """
    An example of wrapping filter functions to retrieve some more complex
    filtering queries.

    Three use cases are shown:
        - Use Case 1 - Filter inventory for "even" numbered devices.
        - Use Case 2 - Filter inventory for device which meet
        the naming convention.
        - Use Case 3 - Filter inventory for device which DO NOT meet
        the naming convention.

    :param nr: An initialised Nornir inventory, used for processing.
    """
    # We use the 'filter_func' option to run our entire inventory through the python
    # function called 'even_device_naming_convention'
    even_device_name_hosts = nr.filter(filter_func=even_device_naming_convention)
    # Print seperator and header
    print("=" * 50)
    print("The hosts which match the even device naming convention are:")
    # Iterate over filtered results and printout information
    for host, data in even_device_name_hosts.inventory.hosts.items():
        print(f"Host: {host} ")
    # Print total and seperator
    print(f"Total: {len(even_device_name_hosts.inventory.hosts.items())}")
    print("=" * 50)
    # Use Case 2 - Devices which match the naming convention
    # We use the 'filter_func' option to run our entire inventory through the python
    # function called 'device_name_convention'
    compliant_naming_convention_hosts = nr.filter(filter_func=device_name_convention)
    # Print seperator and header
    print("=" * 50)
    print("The hosts which match the device naming convention are:")
    # Iterate over filtered results and printout information
    for host, data in compliant_naming_convention_hosts.inventory.hosts.items():
        print(f"Host: {host} ")
    # Print total and seperator
    print(f"Total: {len(compliant_naming_convention_hosts.inventory.hosts.items())}")
    print("=" * 50)
    # Use Case 2 - Devices which DO NOT match the naming convention
    # We use the 'filter_func' option to run our entire inventory through the python
    # function called 'non_device_name_convention'
    non_compliant_naming_convention_hosts = nr.filter(filter_func=non_device_name_convention)
    # Print seperator and header
    print("=" * 50)
    print("The hosts which DO NOT match the device naming convention are:")
    # Iterate over filtered results and printout information
    for host, data in non_compliant_naming_convention_hosts.inventory.hosts.items():
        print(f"Host: {host} ")
    # Print total and seperator
    print(f"Total: {len(non_compliant_naming_convention_hosts.inventory.hosts.items())}")
    print("=" * 50)


# Call wrapper function
filter_functions(nr)
==================================================
The hosts which match the even device naming convention are:
Host: lab-arista-02.lab.norn.local
Host: lab-paloalto-02.lab.norn.local
Host: lab-junos-06.lab.norn.local
Host: prd-arista-02.prd.nron.local
Host: prd-paloalto-02.prd.norn.local
Host: prd-junos-06.prd.norn.local
Host: tstt-arista-02.tst.norn.local
Host: tst-paloalto-02.tst.norn.local
Host: tst-junos-06.tst.norn.local
Total: 9
==================================================
==================================================
The hosts which match the device naming convention are:
Host: lab-arista-01.lab.norn.local
Host: lab-arista-02.lab.norn.local
Host: lab-junos-01.lab.norn.local
Host: lab-nxos-01.lab.norn.local
Host: lab-paloalto-02.lab.norn.local
Host: lab-junos-06.lab.norn.local
Host: prd-csr-01.prd.norn.local
Host: prd-arista-01.prd.norn.local
Host: prd-junos-01.prd.norn.local
Host: prd-nxos-01.prd.norn.local
Host: prd-paloalto-01.prd.norn.local
Host: prd-paloalto-02.prd.norn.local
Host: prd-junos-06.prd.norn.local
Host: tst-csr-01.tst.norn.local
Host: tst-arista-01.tst.norn.local
Host: tst-junos-01.tst.norn.local
Host: tst-nxos-01.tst.norn.local
Host: tst-paloalto-01.tst.norn.local
Host: tst-paloalto-02.tst.norn.local
Host: tst-junos-06.tst.norn.local
Total: 20
==================================================
==================================================
The hosts which DO NOT match the device naming convention are:
Host: lab-csr-011.lab.norn.local
Host: dfjt-r001.lab.norn.local
Host: lab-paloalto-01.lab.djft.local
Host: dfjt-r001.prd.norn.local
Host: prd-arista-02.prd.nron.local
Host: dfjt-r001.tst.norn.local
Host: tstt-arista-02.tst.norn.local
Total: 7
==================================================

Conclusion

Thanks for following along with this tutorial. If you have got to this point, I’m sure you would agree that nornir filtering is powerful and limitless in it’s possibilities.

We have covered basic, intermediate and advanced filtering using network-automation specific use-cases. Hopefully this will give you some ideas and insipiration on how to leverage this feature.

There is also a helpful filtering cheatsheet which has been created for your reference.