Advanced filtering

In this tutorial we are going to see how to use the F object to do advanced filtering of hosts. Let’s start by initiating nornir and looking at the inventory:

[1]:
from nornir import InitNornir
from nornir.core.filter import F

nr = InitNornir(config_file="advanced_filtering/config.yaml")
[2]:
%cat advanced_filtering/inventory/hosts.yaml
---
cat:
    groups:
        - terrestrial
        - mammal
    data:
        domestic: true
        diet: omnivore
        additional_data:
            lifespan: 17
            famous_members:
                - garfield
                - felix
                - grumpy

bat:
    groups:
        - terrestrial
        - mammal
    data:
        domestic: false
        fly: true
        diet: carnivore
        additional_data:
            lifespan: 15
            famous_members:
                - batman
                - count chocula
                - nosferatu

eagle:
    groups:
        - terrestrial
        - bird
    data:
        domestic: false
        diet: carnivore
        additional_data:
            lifespan: 50
            famous_members:
                - thorondor
                - sam

canary:
    groups:
        - terrestrial
        - bird
    data:
        domestic: true
        diet: herbivore
        additional_data:
            lifespan: 15
            famous_members:
                - tweetie

caterpillaer:
    groups:
        - terrestrial
        - invertebrate
    data:
        domestic: false
        diet: herbivore
        additional_data:
            lifespan: 1
            famous_members:
                - Hookah-Smoking

octopus:
    groups:
        - marine
        - invertebrate
    data:
        domestic: false
        diet: carnivore
        additional_data:
            lifespan: 1
            famous_members:
                - sharktopus
[3]:
%cat advanced_filtering/inventory/groups.yaml
---
mammal:
    data:
        reproduction: birth
        fly: false

bird:
    data:
        reproduction: eggs
        fly: true

invertebrate:
    data:
        reproduction: mitosis
        fly: false

terrestrial: {}
marine: {}

As you can see we have built ourselves a collection of animals with different properties. The F object let’s you access the magic methods of each types by just prepeding two underscores and the the name of the magic method. For instance, if you want to check if a list contains a particular element you can just prepend __contains. Let’s use this feature to retrieve all the animals that belong to the group bird:

[4]:
birds = nr.filter(F(groups__contains="bird"))
print(birds.inventory.hosts.keys())
dict_keys(['eagle', 'canary'])

We can also invert the F object by prepending ~:

[5]:
not_birds = nr.filter(~F(groups__contains="bird"))
print(not_birds.inventory.hosts.keys())
dict_keys(['cat', 'bat', 'caterpillaer', 'octopus'])

We can also combine F objects and perform AND and OR operations with the symbols & and | (pipe) respectively:

[6]:
domestic_or_bird = nr.filter(F(groups__contains="bird") | F(domestic=True))
print(domestic_or_bird.inventory.hosts.keys())
dict_keys(['cat', 'eagle', 'canary'])
[7]:
domestic_mammals = nr.filter(F(groups__contains="mammal") & F(domestic=True))
print(domestic_mammals.inventory.hosts.keys())
dict_keys(['cat'])

As expected, you can combine all of the symbols:

[8]:
flying_not_carnivore = nr.filter(F(fly=True) & ~F(diet="carnivore"))
print(flying_not_carnivore.inventory.hosts.keys())
dict_keys(['canary'])

You can also access nested data the same way you access magic methods, by appending two underscores and the data you want to access. You can keep building on this as much as needed and even access the magic methods of the nested data. For instance, let’s get the animals that have a lifespan greater or equal than 15:

[9]:
long_lived = nr.filter(F(additional_data__lifespan__ge=15))
print(long_lived.inventory.hosts.keys())
dict_keys(['cat', 'bat', 'eagle', 'canary'])

There are two extra facilities to help you working with lists; any and all. Those facilities let’s you send a list of elements and get the objects that has either any of the members or all of them. For instance:

[10]:
marine_and_invertebrates = nr.filter(F(groups__all=["marine", "invertebrate"]))
print(marine_and_invertebrates.inventory.hosts.keys())
dict_keys(['octopus'])
[11]:
bird_or_invertebrates = nr.filter(F(groups__any=["bird", "invertebrate"]))
print(bird_or_invertebrates.inventory.hosts.keys())
dict_keys(['eagle', 'canary', 'caterpillaer', 'octopus'])