|
| 1 | ++++ |
| 2 | +title = "A Basic introduction to Nornir (with lab!)" |
| 3 | +date = "2025-05-17" |
| 4 | +description = "Learn the Basics of Nornir with some hands on examples using containerlab" |
| 5 | +tags = [ |
| 6 | + "npa showcases", |
| 7 | + "containerlab", |
| 8 | + "nornir", |
| 9 | + "network automation", |
| 10 | + "python", |
| 11 | +] |
| 12 | +showComments = "true" |
| 13 | ++++ |
| 14 | + |
| 15 | +## Introduction |
| 16 | + |
| 17 | +In this article, we will learn the basics of [Nornir](https://nornir.readthedocs.io/en/latest/) along with how to use it to interact with Juniper and Arista devices. |
| 18 | + |
| 19 | +## What is Nornir? |
| 20 | + |
| 21 | +From the Nornir [documentation](https://nornir.readthedocs.io/en/latest/) |
| 22 | + |
| 23 | +> Nornir is an automation framework written in python to be used with python. Just imagine Nornir as the Flask of automation. Nornir will take care of dealing with the inventory where you have your host information, it will take care of dispatching the tasks to your devices and will provide a common framework to write “plugins”. |
| 24 | +
|
| 25 | +Where I think it shines compared to other products like Ansible is the "Pure Python" framework. If you can program in Python, then you can utilize Nornir to integrate with any product or API endpoint and create logic for any automation scenario. |
| 26 | + |
| 27 | +## Setup |
| 28 | + |
| 29 | +The ready to go example lab for this article is the [basic-nornir](https://github.com/commitconfirmed/npa-showcases/tree/main/examples/basic-nornir) topology. Navigate into this directory and execute `manage.sh build` & `manage.sh run` |
| 30 | + |
| 31 | +{{< alert >}} |
| 32 | +**Note:** If this is your first time here, see the [NPA Showcases](/npa-showcases) documentation for initial installation steps on your local machine or a GitHub codespace |
| 33 | +{{< /alert >}} |
| 34 | + |
| 35 | +## Topology |
| 36 | + |
| 37 | +The folder structure of our Nornir server is below |
| 38 | + |
| 39 | +| File | Description | |
| 40 | +| --- | --- | |
| 41 | +| `/app/config.yml` | Our basic Nornir config file | |
| 42 | +| `/app/inventory/defaults.yml` | Default inventory variables, applies to all hosts | |
| 43 | +| `/app/inventory/groups.yml` | Group inventory variables, applies to hosts in this group | |
| 44 | +| `/app/inventory/hosts.yml` | Host inventory variables, specific to the host | |
| 45 | +| `/app/tasks/task-inventory.py` | Example task showing inventory management | |
| 46 | +| `/app/tasks/task-napalm.py` | Example task to interact with devices using NAPALM plugin | |
| 47 | +| `/app/tasks/task-netmiko.py` | Example task to interact with devices using NETMIKO plugin | |
| 48 | +| `/app/tasks/task-scrapli.py` | Example task to interact with devices using SCRAPLI plugin | |
| 49 | +| `/app/templates/ceos1.j2` | Jinja2 template for our cEOS device | |
| 50 | +| `/app/templates/crpd1.j2` | Jinja2 template for our cRPD device | |
| 51 | + |
| 52 | +## Interacting with Devices |
| 53 | + |
| 54 | +Now the boilerplate is out of the way, let's jump right in! Assuming the manage.sh script worked, you should see the below output: |
| 55 | + |
| 56 | +```sh |
| 57 | +╭─────────────────┬───────────────────┬─────────┬────────────────╮ |
| 58 | +│ Name │ Kind/Image │ State │ IPv4/6 Address │ |
| 59 | +├─────────────────┼───────────────────┼─────────┼────────────────┤ |
| 60 | +│ clab-lab-ceos1 │ arista_ceos │ running │ 172.20.0.12 │ |
| 61 | +│ │ ceos:latest │ │ N/A │ |
| 62 | +├─────────────────┼───────────────────┼─────────┼────────────────┤ |
| 63 | +│ clab-lab-crpd1 │ juniper_crpd │ running │ 172.20.0.11 │ |
| 64 | +│ │ crpd:latest │ │ N/A │ |
| 65 | +├─────────────────┼───────────────────┼─────────┼────────────────┤ |
| 66 | +│ clab-lab-nornir │ linux │ running │ 172.20.0.100 │ |
| 67 | +│ │ lab-nornir:latest │ │ N/A │ |
| 68 | +╰─────────────────┴───────────────────┴─────────┴────────────────╯ |
| 69 | +Done. Sleeping for 5 seconds to allow the containers to fully boot |
| 70 | +``` |
| 71 | +From here, SSH (admin/admin) into our Nornir server: |
| 72 | + |
| 73 | +`ssh admin@clab-lab-nornir` |
| 74 | + |
| 75 | +### Inventory |
| 76 | + |
| 77 | +See the contents of the `/app/tasks/task-inventory.py` file below: |
| 78 | + |
| 79 | +<details> |
| 80 | +<summary>Expand / Collapse</summary> |
| 81 | +{{< codeimporter url="https://raw.githubusercontent.com/commitconfirmed/npa-showcases/main/examples/basic-nornir/configs/lab-nornir/tasks/task-inventory.py" type="python" >}} |
| 82 | +</details> |
| 83 | + |
| 84 | +This script shows you how to: |
| 85 | + |
| 86 | +- Initialize a Nornir runner and your inventory |
| 87 | +- Use the runner to execute a basic task |
| 88 | +- Utilize the nornir_rich plugin to enhance your output |
| 89 | + |
| 90 | +### NAPALM |
| 91 | + |
| 92 | +See the contents of the `/app/tasks/task-napalm.py` file below: |
| 93 | + |
| 94 | +<details> |
| 95 | +<summary>Expand / Collapse</summary> |
| 96 | +{{< codeimporter url="https://raw.githubusercontent.com/commitconfirmed/npa-showcases/main/examples/basic-nornir/configs/lab-nornir/tasks/task-napalm.py" type="python" >}} |
| 97 | +</details> |
| 98 | + |
| 99 | +This script shows you how to use [NAPALM](https://nornir.tech/nornir_napalm/html/) plugin to perform the below on EOS and JUNOS devices: |
| 100 | + |
| 101 | +- Retrieve the running configuration |
| 102 | +- Execute a CLI command (show version) |
| 103 | +- Render a Jinja2 configuration template and apply it |
| 104 | + |
| 105 | +### NETMIKO |
| 106 | + |
| 107 | +See the contents of the `/app/tasks/task-netmiko.py` file below: |
| 108 | + |
| 109 | +<details> |
| 110 | +<summary>Expand / Collapse</summary> |
| 111 | +{{< codeimporter url="https://raw.githubusercontent.com/commitconfirmed/npa-showcases/main/examples/basic-nornir/configs/lab-nornir/tasks/task-netmiko.py" type="python" >}} |
| 112 | +</details> |
| 113 | + |
| 114 | +This script shows you how to use [NETMIKO](https://github.com/ktbyers/nornir_netmiko) plugin to perform the below on EOS and JUNOS devices: |
| 115 | + |
| 116 | +- Execute a CLI command (show version) |
| 117 | +- Render a Jinja2 configuration template and apply it |
| 118 | + |
| 119 | +### SCRAPLI |
| 120 | + |
| 121 | +See the contents of the `/app/tasks/task-scrapli.py` file below: |
| 122 | + |
| 123 | +<details> |
| 124 | +<summary>Expand / Collapse</summary> |
| 125 | +{{< codeimporter url="https://raw.githubusercontent.com/commitconfirmed/npa-showcases/main/examples/basic-nornir/configs/lab-nornir/tasks/task-scrapli.py" type="python" >}} |
| 126 | +</details> |
| 127 | + |
| 128 | +This script shows you how to use [SCRAPLI](https://scrapli.github.io/nornir_scrapli) plugin to perform the below on EOS and JUNOS devices: |
| 129 | + |
| 130 | +- Execute a CLI command (show version) |
| 131 | +- Render a Jinja2 configuration template and apply it |
| 132 | + |
| 133 | +{{< alert >}} |
| 134 | +**Note:** The scrapli plugin requires a straight to CLI user (admin/clab123) on our cRPD device. This should be automatically applied but if not the commands are below |
| 135 | +{{< /alert >}} |
| 136 | + |
| 137 | +```sh |
| 138 | +set system login user admin uid 100 class super-user |
| 139 | +set system login user admin authentication plain-text-password |
| 140 | +``` |
| 141 | + |
| 142 | +## Final thoughts |
| 143 | + |
| 144 | +We have barely scratched the surface on what Nornir and its [plugins](https://nornir.readthedocs.io/en/latest/community/plugin_list.html) are capable of here but I hope this helps you get started with your Nornir journey. |
| 145 | + |
| 146 | +I've found that even in these basic examples, you can see that the code does vary a bit just to do the basics depending on your plugin of choice. A lot of basic documentation seems to be missing from some of the plugins as well, with the exception of the SCRAPLI plugin which I feel at moment is the best of the bunch, but YMMV. |
| 147 | + |
| 148 | +Please let me know in the comments below if you have any thoughts, or if any of these scripts aren't working and I will update this article. Thanks for reading! |
| 149 | + |
| 150 | + |
| 151 | + |
0 commit comments