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


[2]:

%cat advanced_filtering/inventory/hosts.yaml

---
cat:
groups:
- terrestrial
- mammal
data:
domestic: true
diet: omnivore
lifespan: 17
famous_members:
- garfield
- felix
- grumpy

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

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

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

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

octopus:
groups:
- marine
- invertebrate
data:
domestic: false
diet: carnivore
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'])