Grouping tasks

In this section we are going to see how we can group tasks. Grouping tasks might be useful for various reasons, for instance, for reusability purposes (as seen in a previous section) or even just for readability purposes.

We will also see very briefly how to use the functions functions.text.print_title and functions.text.print_result to make things look pretty.

As an objective in this tutorial we are going to be configuring the hostname and domain name of our network devices.

Let’s start with the basic imports and objects we will need:

[1]:
from nornir import InitNornir
from nornir.plugins.tasks import networking, text
from nornir.plugins.functions.text import print_title, print_result

nr = InitNornir(config_file="config.yaml", dry_run=True)
cmh = nr.filter(site="cmh", type="network_device")

You may have noticed that InitNornir got a new argument dry_run=True. That argument (which defaults to False) controls whether to apply the changes to the device or just simulate them. You can control that argument via the configuration as well. Some tasks might even allow you to override this behavior completely at the task level.

Now, let’s create task that is going to group what we want to do:

[2]:
def basic_configuration(task):
    # Transform inventory data to configuration via a template file
    r = task.run(task=text.template_file,
                 name="Base Configuration",
                 template="base.j2",
                 path=f"templates/{task.host.platform}")

    # Save the compiled configuration into a host variable
    task.host["config"] = r.result

    # Deploy that configuration to the device using NAPALM
    task.run(task=networking.napalm_configure,
             name="Loading Configuration on the device",
             replace=False,
             configuration=task.host["config"])

As you can see the task is basically doing two things:

  1. Render configuration from a jinja2 template and storing it into a host variable. Note we are using the host network operating system as part of the path so we can load the right template for the given device platform.
  2. Deploying that configuration to the device with NAPALM.

Quite straightforward. For reference let’s look at the templates:

[3]:
%cat templates/eos/base.j2
hostname {{ host }}
ip domain-name {{ host.site }}.{{ host.domain }}

[4]:
%cat templates/junos/base.j2
system {
  host-name {{ host }};
  domain-name {{ host.site }}.{{ host.domain }};
}

Now we can proceed with just a few lines of code:

[5]:
print_title("Playbook to configure the network")
result = cmh.run(task=basic_configuration)
print_result(result)
**** Playbook to configure the network *****************************************
basic_configuration*************************************************************
* leaf00.cmh ** changed : True *************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
hostname leaf00.cmh
ip domain-name cmh.acme.local
---- Loading Configuration on the device ** changed : True --------------------- INFO
@@ -7,6 +7,9 @@
    action bash sudo /mnt/flash/initialize_ma1.sh
 !
 transceiver qsfp default-mode 4x10G
+!
+hostname leaf00.cmh
+ip domain-name cmh.acme.local
 !
 spanning-tree mode mstp
 !
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* leaf01.cmh ** changed : True *************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
system {
  host-name leaf01.cmh;
  domain-name cmh.acme.local;
}
---- Loading Configuration on the device ** changed : True --------------------- INFO
[edit system]
-  host-name vsrx;
+  host-name leaf01.cmh;
+  domain-name cmh.acme.local;
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* spine00.cmh ** changed : True ************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
hostname spine00.cmh
ip domain-name cmh.acme.local
---- Loading Configuration on the device ** changed : True --------------------- INFO
@@ -7,6 +7,9 @@
    action bash sudo /mnt/flash/initialize_ma1.sh
 !
 transceiver qsfp default-mode 4x10G
+!
+hostname spine00.cmh
+ip domain-name cmh.acme.local
 !
 spanning-tree mode mstp
 !
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* spine01.cmh ** changed : True ************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
system {
  host-name spine01.cmh;
  domain-name cmh.acme.local;
}
---- Loading Configuration on the device ** changed : True --------------------- INFO
[edit system]
-  host-name vsrx;
+  host-name spine01.cmh;
+  domain-name cmh.acme.local;
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

As you can see, pretty straightforward. We just call the function we defined earlier grouping the tasks we wanted to execute and then we leverage on functions.text.print_title and tasks.text.print_result to make things look pretty.

However, this was a dry_run. Let’s set the dry_run variable to False so changes are committed and then run the code again:

Note: The dry_run value is shared between the main nornir objects and its children so in the snippet below nr.data.dry_run = False and cmh.data.dry_run = False are equivalent.
[6]:
nr.data.dry_run = False
print_title("Playbook to configure the network")
result = cmh.run(task=basic_configuration)
print_result(result)
**** Playbook to configure the network *****************************************
basic_configuration*************************************************************
* leaf00.cmh ** changed : True *************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
hostname leaf00.cmh
ip domain-name cmh.acme.local
---- Loading Configuration on the device ** changed : True --------------------- INFO
@@ -7,6 +7,9 @@
    action bash sudo /mnt/flash/initialize_ma1.sh
 !
 transceiver qsfp default-mode 4x10G
+!
+hostname leaf00.cmh
+ip domain-name cmh.acme.local
 !
 spanning-tree mode mstp
 !
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* leaf01.cmh ** changed : True *************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
system {
  host-name leaf01.cmh;
  domain-name cmh.acme.local;
}
---- Loading Configuration on the device ** changed : True --------------------- INFO
[edit system]
-  host-name vsrx;
+  host-name leaf01.cmh;
+  domain-name cmh.acme.local;
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* spine00.cmh ** changed : True ************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
hostname spine00.cmh
ip domain-name cmh.acme.local
---- Loading Configuration on the device ** changed : True --------------------- INFO
@@ -7,6 +7,9 @@
    action bash sudo /mnt/flash/initialize_ma1.sh
 !
 transceiver qsfp default-mode 4x10G
+!
+hostname spine00.cmh
+ip domain-name cmh.acme.local
 !
 spanning-tree mode mstp
 !
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* spine01.cmh ** changed : True ************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
system {
  host-name spine01.cmh;
  domain-name cmh.acme.local;
}
---- Loading Configuration on the device ** changed : True --------------------- INFO
[edit system]
-  host-name vsrx;
+  host-name spine01.cmh;
+  domain-name cmh.acme.local;
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

As the configuration should’ve been committed already, if we run it again the task “Loading Configuration on the device” should tell us that changed : False and should return an empty diff. Let’s see if that’s true:

[7]:
print_title("Playbook to configure the network")
result = cmh.run(task=basic_configuration)
print_result(result)
**** Playbook to configure the network *****************************************
basic_configuration*************************************************************
* leaf00.cmh ** changed : False ************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
hostname leaf00.cmh
ip domain-name cmh.acme.local
---- Loading Configuration on the device ** changed : False -------------------- INFO
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* leaf01.cmh ** changed : False ************************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
system {
  host-name leaf01.cmh;
  domain-name cmh.acme.local;
}
---- Loading Configuration on the device ** changed : False -------------------- INFO
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* spine00.cmh ** changed : False ***********************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
hostname spine00.cmh
ip domain-name cmh.acme.local
---- Loading Configuration on the device ** changed : False -------------------- INFO
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* spine01.cmh ** changed : False ***********************************************
vvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : False ------------------------------------- INFO
system {
  host-name spine01.cmh;
  domain-name cmh.acme.local;
}
---- Loading Configuration on the device ** changed : False -------------------- INFO
^^^^ END basic_configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^