Lab 02 - Exploring the Nornir Inventory¶
This lab will guide you on how to build a configuration file and initialize Nornir. After initializing Nornir, there are a few modules and functions that allow you to extract inventory data so it can be consumed as needed.
Task 1 - Create Configuration Files¶
In Nornir a configuration file needs to be created so that it knows where to find the inventory file and other files like groups and defaults so it can consume the data. To be able to combine the inventory file with the configuration file the InitNornir object needs to be used in the Python code. With InitNornir you can initialize Nornir with a configuration file in YAML or with code. The examples below will walk through the process on how to do that.
Step 1¶
Make sure you are in the /home/ntc/nornir directory.
Step 2¶
Create a new directory named nornir_configuration and inside the directory add two new YAML files called napalm_configuration.yml and netmiko_configuration.yml.
Note: Like mentioned before, two different configuration files will be used here so that testing can be done using both NAPALM modules and Netmiko modules.
The folder structure should look like the following:
ntc@ntc-training:nornir$ touch nornir_configuration/napalm_configuration.yml
ntc@ntc-training:nornir$ touch nornir_configuration/netmiko_configuration.yml
ntc@ntc-training:nornir$ tree
.
├── inventory
│ ├── defaults
│ │ └── inventory_defaults.yml
│ ├── groups
│ │ ├── napalm_groups.yml
│ │ └── netmiko_groups.yml
│ └── inventory_file.yml
└── nornir_configuration
├── napalm_configuration.yml
└── netmiko_configuration.yml
4 directories, 6 files
Step 3¶
Inside the napalm_configuration.yml and netmiko_configuration.yml files define what inventory plugin is going to be used and the path to find the host_file (inventory file), group_file and defaults_file. These files should look exactly the same except for the group files path since one belongs to NAPALM and the other to Netmiko.
For the Netmiko configuration file add the following:
---
inventory:
plugin: SimpleInventory
options:
host_file: "inventory/inventory_file.yml"
group_file: "inventory/groups/netmiko_groups.yml"
defaults_file: "inventory/defaults/inventory_defaults.yml"
For the NAPALM configuration file add the following:
---
inventory:
plugin: SimpleInventory
options:
host_file: "inventory/inventory_file.yml"
group_file: "inventory/groups/napalm_groups.yml"
defaults_file: "inventory/defaults/inventory_defaults.yml"
Task 2 - Explore the Inventory¶
Once the configuration file is created and saved, the InitNornir object can be used to initialize and combine the configuration file with the inventory, groups, and defaults files.
Step 1¶
To start exploring inventory data, access the Python interpreter by using the command python.
ntc@ntc-training:nornir$ python
Python 3.6.8 (default, Jun 11 2019, 01:16:11)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
Step 2¶
Once in the Python interpreter, import the needed libraries to initialize Nornir and start exploring inventory data. Import the InitNornir object inside the nornir library and the rich.print methods to help display complex data in a more readable format (the rich package provides a drop-in replacement for the print function).
>>> from nornir import InitNornir
>>> from rich import print
>>>
>>> nr = InitNornir(config_file="./nornir_configuration/napalm_configuration.yml")
>>>
Step 3¶
Now that Nornir has been initialized, use the dir() function to view available modules and functions inside the nr.inventory object.
>>> print(dir(nr.inventory))
[
'__class__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__len__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'children_of_group',
'defaults',
'dict',
'filter',
'groups',
'hosts',
'schema'
]
>>>
The main inventory objects this lesson is going to focus on are: * hosts * groups * dict * schema * defaults
Step 4¶
Print out the nr.inventory.hosts object using the print function. The output of the hosts object will return a dictionary of all the available hosts inside the nr object.
>>> print(nr.inventory.hosts)
{
'csr1': Host: csr1,
'csr2': Host: csr2,
'csr3': Host: csr3,
'nxos-spine1': Host: nxos-spine1,
'nxos-spine2': Host: nxos-spine2,
'eos-leaf1': Host: eos-leaf1,
'eos-leaf2': Host: eos-leaf2,
'eos-spine1': Host: eos-spine1,
'eos-spine2': Host: eos-spine2
}
>>>
Step 5¶
Use the print function again except on the nr.inventory.groups object and it will return a dictionary of all the available groups inside the nr object.
>>> print(nr.inventory.groups)
{
'iosxe': Group: iosxe,
'nxos': Group: nxos,
'eos-leaves': Group: eos-leaves,
'eos-spines': Group: eos-spines,
'cisco': Group: cisco,
'arista': Group: arista
}
>>>
Step 6¶
Using the schema() method shows how the data structure should look like and what data type each value should represent.
>>> print(nr.inventory.schema())
{
'hosts': {
'$name': {
'name': 'str',
'connection_options': {
'$connection_type': {
'extras': {'$key': '$value'},
'hostname': 'str',
'port': 'int',
'username': 'str',
'password': 'str',
'platform': 'str'
}
},
'groups': ['$group_name'],
'data': {'$key': '$value'},
'hostname': 'str',
'port': 'int',
'username': 'str',
'password': 'str',
'platform': 'str'
}
},
'groups': {
'$group': {
'name': 'str',
'connection_options': {
'$connection_type': {
'extras': {'$key': '$value'},
'hostname': 'str',
'port': 'int',
'username': 'str',
'password': 'str',
'platform': 'str'
}
},
'groups': ['$group_name'],
'data': {'$key': '$value'},
'hostname': 'str',
'port': 'int',
'username': 'str',
'password': 'str',
'platform': 'str'
}
},
'defaults': {
'data': {'$key': '$value'},
'connection_options': {
'$connection_type': {
'extras': {'$key': '$value'},
'hostname': 'str',
'port': 'int',
'username': 'str',
'password': 'str',
'platform': 'str'
}
},
'hostname': 'str',
'port': 'int',
'username': 'str',
'password': 'str',
'platform': 'str'
}
}
>>>
Step 7¶
The nr.inventory.dict() function will return all the data that belongs to the inventory file. You can also use it on individual hosts and groups.
>>> print(nr.inventory.dict())
{
'hosts': {
'csr1': {
'name': 'csr1',
'connection_options': {},
'groups': ['iosxe'],
'data': {},
'hostname': 'csr1',
'port': None,
'username': None,
'password': None,
'platform': None
},
'csr2': {
'name': 'csr2',
'connection_options': {},
'groups': ['iosxe'],
'data': {},
'hostname': 'csr2',
'port': None,
'username': None,
'password': None,
'platform': None
},
'csr3': {
'name': 'csr3',
'connection_options': {},
'groups': ['iosxe'],
'data': {},
'hostname': 'csr3',
'port': None,
'username': None,
'password': None,
'platform': None
},
'nxos-spine1': {
'name': 'nxos-spine1',
'connection_options': {},
'groups': ['nxos'],
'data': {},
'hostname': 'nxos-spine1',
'port': None,
'username': None,
'password': None,
'platform': None
},
'nxos-spine2': {
'name': 'nxos-spine2',
'connection_options': {},
'groups': ['nxos'],
'data': {},
'hostname': 'nxos-spine2',
'port': None,
'username': None,
'password': None,
'platform': None
},
'eos-leaf1': {
'name': 'eos-leaf1',
'connection_options': {},
'groups': ['eos-leaves'],
'data': {},
'hostname': 'eos-leaf1',
'port': None,
'username': None,
'password': None,
'platform': None
},
'eos-leaf2': {
'name': 'eos-leaf2',
'connection_options': {},
'groups': ['eos-leaves'],
'data': {},
'hostname': 'eos-leaf2',
'port': None,
'username': None,
'password': None,
'platform': None
},
'eos-spine1': {
'name': 'eos-spine1',
'connection_options': {},
'groups': ['eos-spines'],
'data': {},
'hostname': 'eos-spine1',
'port': None,
'username': None,
'password': None,
'platform': None
},
'eos-spine2': {
'name': 'eos-spine2',
'connection_options': {},
'groups': ['eos-spines'],
'data': {},
'hostname': 'eos-spine2',
'port': None,
'username': None,
'password': None,
'platform': None
}
},
'groups': {
'iosxe': {
'name': 'iosxe',
'connection_options': {},
'groups': ['cisco'],
'data': {},
'hostname': None,
'port': None,
'username': None,
'password': None,
'platform': 'ios'
},
'nxos': {
'name': 'nxos',
'connection_options': {},
'groups': ['cisco'],
'data': {},
'hostname': None,
'port': None,
'username': None,
'password': None,
'platform': 'nxos_ssh'
},
'cisco': {
'name': 'cisco',
'connection_options': {},
'groups': [],
'data': {},
'hostname': None,
'port': None,
'username': None,
'password': None,
'platform': None
},
'eos-leaves': {
'name': 'eos-leaves',
'connection_options': {},
'groups': ['arista'],
'data': {},
'hostname': None,
'port': None,
'username': None,
'password': None,
'platform': None
},
'eos-spines': {
'name': 'eos-spines',
'connection_options': {},
'groups': ['arista'],
'data': {},
'hostname': None,
'port': None,
'username': None,
'password': None,
'platform': None
},
'arista': {
'name': 'arista',
'connection_options': {},
'groups': [],
'data': {},
'hostname': None,
'port': None,
'username': None,
'password': None,
'platform': 'eos'
}
},
'defaults': {
'data': {},
'connection_options': {},
'hostname': None,
'port': None,
'username': 'ntc',
'password': 'ntc123',
'platform': None
}
}
>>>
Step 8¶
Use the dir() method to view all the available built-in methods that can be used inside the defaults object.
>>> print(dir(nr.inventory.defaults))
[
'__class__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'connection_options',
'data',
'dict',
'hostname',
'password',
'platform',
'port',
'schema',
'username'
]
>>>
Step 9¶
Within the defaults object it will allow you to view what has been stored in the inventory/defaults/inventory_defaults.yml file. Currently the only thing stored in that file is the username and password that can be used to access all devices.
>>> print(nr.inventory.defaults.dict())
{
'data': {},
'connection_options': {},
'hostname': None,
'port': None,
'username': 'ntc',
'password': 'ntc123',
'platform': None
}
Step 10¶
Print the username and password using the username and password variables stored in the defaults object.
Step 11¶
Print the groups of the csr2 host from the inventory.
Step 12¶
Print the extended groups of the csr2 host from the inventory.
Step 13¶
Now print the platform of the csr2 and eos-spine1 hosts.
>>> print(nr.inventory.hosts['csr2'].platform)
ios
>>> print(nr.inventory.hosts['eos-spine1'].platform)
eos
Where is this data coming from? Hint: check the groups membership and explore the group attributes. Do it only with Nornir objects via Python without looking at the YAML files!