[python]Filter inventory – Nornir

Introduction

Nornir is a framework for network automation, it gels netmiko, paramiko, ansible, yaml, ansible, napalm, netconf together, this is a must have package for doing network automation and it is not too difficult to learn.

There will be time when specific configuration is for specific device in the inventory, in order to push to the correct device in the inventory you can use the filter feature made in nornir.

Simple filter

This post records filter which I need, there are many filtering methods and examples in the documentation, but those examples are not what i was looking for, my hosts.yaml is very simple, there are no groups and sites, only list of hosts and their nested data.

Simple hosts.yaml inventory file

This is my simple yaml file:

fw01:
  hostname: 192.168.100.20
  password: null
  platform: cisco_asa
  port: '22'
  username: null
fw02:
  hostname: 192.168.100.30
  password: null
  platform: cisco_asa
  port: '22'
  username: null

Inventory posted to api

I have made my own api gateway to interact with the network devices and hashicorp vault, the below is my sample using insomnia api client. Insomnia is less complicated than postman and is suffice for me to do simple test, of course if you are a cli person you can use curl, but to me I will use whichever is straightforward and easy.

nor9

The api gateway removes the username and password of the devices, the actual username and password are created in the hashicorp vault, and the output shown in insomnia is stored in hosts.yaml.

nor10

Filter specific device and push configuration

The filter method has an argument filter_func which accepts a function, lambda function is used to match the device which I need to configure.
fw01 = nr.filter(filter_func=lambda h: h.name == "fw01")

The entire test code:

from nornir import InitNornir
from nornir.plugins.functions.text import print_result
from network.ciscoasa import add_acl, get_asa_credential, change_asa_host_data
from nornir.core.filter import F


if __name__ == "__main__":
    credential = get_asa_credential(hostname="fw01")
    nr = InitNornir(
        inventory={
            "plugin": "nornir.plugins.inventory.simple.SimpleInventory",
            "options": {
                "host_file": "network/inventory/hosts.yaml",
            }
        }
    )
# modify the username and password fields in the hosts.yaml file.
    nr.inventory.hosts["fw01"].username = credential["username"]
    nr.inventory.hosts["fw01"].password = credential["password"]
    payload = {
            "acl_name": "abc",
            "action": "permit",
            "protocol": "tcp",
            "src_fw_object_type": "host",
            "src_object": "10.20.1.90",
            "dst_fw_object_type": "host",
            "dst_object": "192.168.90.1",
            "svc_object_group_type": "eq",
            "svc_object_group": "8200"
        }
    fw01 = nr.filter(filter_func=lambda h: h.name == "fw01")
    r = fw01.run(task=add_acl, **payload)
    print_result(r)

This is the output after executed the test code:
nor11

This is the ACL in the fw01 firewall:
nor12

Self made python module – network.ciscoasa

As shown in my test code I am also using my own network.ciscoasa package. The pyvault2 can be found in my own repository. Here’s the code:

from nornir.plugins.tasks.networking import netmiko_send_config, netmiko_save_config
from nornir.plugins.tasks.text import template_file
from pyvault2.vault.hvault2 import get_kv2_secret


def get_asa_credential(hostname=None):
    """
    Get asa's device credentials.
    :param hostname:
    :return: credential in json
    """
    return get_kv2_secret(mount_path="cisco_asa", path=hostname, find="data")


def change_asa_host_data(host, **kwargs):
    host.username = kwargs["username"]
    host.password = kwargs["password"]
    host.hostname = kwargs["ip"]
    host.platform = "cisco_asa"


def add_acl(task, **kwargs):
    cmd = task.run(task=template_file,
                   name="Generating template",
                   template="asa_acl.j2",
                   path="network/templates/",
                   acl_attr=kwargs
                   )
    task.host["config"] = cmd.result
    task.run(task=netmiko_send_config,
             name="Sending configuration",
             config_commands=task.host["config"])
    task.run(task=netmiko_save_config,
             name="Saving configuration",
             cmd="write memory")

ACL configuration jinja2 template

{% if acl_attr.line_number is defined %}
access-list {{ acl_attr.acl_name }} line {{acl_attr.line_number}} extended {{acl_attr.action}} {{acl_attr.protocol}} {{acl_attr.src_fw_object_type}} {{acl_attr.src_object}} {{acl_attr.dst_fw_object_type}} {{acl_attr.dst_object}} {{acl_attr.svc_object_group_type}} {{acl_attr.svc_object_group}} log
{% else %}
access-list {{acl_attr.acl_name}} extended {{acl_attr.action}} {{acl_attr.protocol}} {{acl_attr.src_fw_object_type}} {{acl_attr.src_object}} {{acl_attr.dst_fw_object_type}} {{acl_attr.dst_object}} {{acl_attr.svc_object_group_type}} {{acl_attr.svc_object_group}} log
{% endif %}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s