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. [1]:
  1. from nornir import InitNornir
  2. from nornir.core.filter import F
  3. nr = InitNornir(config_file="advanced_filtering/config.yaml")
  1. [2]:
  1. %cat advanced_filtering/inventory/hosts.yaml
  1. ---
  2. cat:
  3. groups:
  4. - terrestrial
  5. - mammal
  6. data:
  7. domestic: true
  8. diet: omnivore
  9. additional_data:
  10. lifespan: 17
  11. famous_members:
  12. - garfield
  13. - felix
  14. - grumpy
  15. bat:
  16. groups:
  17. - terrestrial
  18. - mammal
  19. data:
  20. domestic: false
  21. fly: true
  22. diet: carnivore
  23. additional_data:
  24. lifespan: 15
  25. famous_members:
  26. - batman
  27. - count chocula
  28. - nosferatu
  29. eagle:
  30. groups:
  31. - terrestrial
  32. - bird
  33. data:
  34. domestic: false
  35. diet: carnivore
  36. additional_data:
  37. lifespan: 50
  38. famous_members:
  39. - thorondor
  40. - sam
  41. canary:
  42. groups:
  43. - terrestrial
  44. - bird
  45. data:
  46. domestic: true
  47. diet: herbivore
  48. additional_data:
  49. lifespan: 15
  50. famous_members:
  51. - tweetie
  52. caterpillaer:
  53. groups:
  54. - terrestrial
  55. - invertebrate
  56. data:
  57. domestic: false
  58. diet: herbivore
  59. additional_data:
  60. lifespan: 1
  61. famous_members:
  62. - Hookah-Smoking
  63. octopus:
  64. groups:
  65. - marine
  66. - invertebrate
  67. data:
  68. domestic: false
  69. diet: carnivore
  70. additional_data:
  71. lifespan: 1
  72. famous_members:
  73. - sharktopus
  1. [3]:
  1. %cat advanced_filtering/inventory/groups.yaml
  1. ---
  2. mammal:
  3. data:
  4. reproduction: birth
  5. fly: false
  6. bird:
  7. data:
  8. reproduction: eggs
  9. fly: true
  10. invertebrate:
  11. data:
  12. reproduction: mitosis
  13. fly: false
  14. terrestrial: {}
  15. 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:

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

We can also invert the F object by prepending ~:

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

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

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

As expected, you can combine all of the symbols:

  1. [8]:
  1. flying_not_carnivore = nr.filter(F(fly=True) & ~F(diet="carnivore"))
  2. print(flying_not_carnivore.inventory.hosts.keys())
  1. 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:

  1. [9]:
  1. long_lived = nr.filter(F(additional_data__lifespan__ge=15))
  2. print(long_lived.inventory.hosts.keys())
  1. 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:

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