Jinja2, iterations and accessing inventory

Hello,
just started learning python form scratch with focusing on network automation (I was learning c# seven years ago in pains at school and never used since). I want to create simple script for interface configuration using Nornir. My problem is accessing inventory data in jinja2 template.
My inventory file:

---
rtr1:
    hostname: 10.10.10.10
    port: 22
    username: adrian
    password: ccna
    platform: cisco_ios
    groups:
        - cmh
    data:
        site: gns3
        role: ASBR
        type: router
        interfaces:
            - loopback: 0
              ip_addr: 5.0.0.10
              mask: 255.255.255.255
              status: up
              protocol: up
            - FastEthernet: 0/0
              ip_addr: 10.10.10.10
              mask: 255.255.255.0
              status: up
              protocol: up
            - FastEthernet: 0/1
              ip_addr: None
              mask: None
              status: administratively down
              protocol: down
            - FastEthernet: 1/0
              ip_addr: None
              mask: None
              status: administratively down
              protocol: down
            - Serial: 0/0
              ip_addr: 10.100.100.1
              mask: 255.255.255.252
              status: up
              protocol: up
            - Serial: 0/1
              ip_addr: None
              mask: None
              status: administratively down
              protocol: down
            - Serial: 0/2
              ip_addr: None
              mask: None
              status: administratively down
              protocol: down
            - Serial: 0/3
              ip_addr: None
              mask: None
              status: administratively down
              protocol: down

Main python script:

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)

def interfaces_config(task):
    # Transform inventory data to configuration via a template file
    var = task.run(task=text.template_file,
                   name="Interface configuration",
                   template="interfaces_config.j2",
                   # current directory:
                   path=f"config_templates/")
    # Save the compiled configuration into a host variable
    task.host["config"] = var.result
    # task.run(task=networking.netmiko_send_config, config_commands=task.host["config"])

def main():
    print_title("Playbook to configure the network")
    task = nr.run(task=interfaces_config)
    print_result(task)


if __name__ == "__main__":
    main()

My Jinja2 template:

interface loopback{{host.data.interfaces[0].loopback}}
ip address {{host.data.interfaces[0].ip_addr}} {{host.data.interfaces[0].mask}}
no shut

Works only for one, single interface and I need to type interface name like FastEthernet and i have other types as well.

All what I want is to iterate through all interfaces and generate config for them but how to use/print key like loopback with value 0, then key FastEthernet with value 0/0. Can we print key name at all or just value? Exactly what i want to achieve:

interface loopback0
ip address 5.0.0.10 255.255.255.255
no shut

and for the same router, next interface:

interface FastEthernet0/0
ip address 10.10.10.10 255.255.255.255
no shut

Looks very simple but how “swap” jinja2 interface variable with key name? Something like this:

interface {{interface_variable_from_key_name}} {{host.data.interfaces[0].loopback}}

I’d suggest you to read: http://jinja.pocoo.org/docs/2.10/templates/#for (actually, the entire document) but basically something like:

{{ for interface in host["interfaces"] }}
interface {{ interface.name }}
   ...
{{ endfor }}

I’d suggest to change the first line of each element to “name”. For instance loopback: 0 -> name: Loopback0. You can add a “type” field if you need to know the type but variable keys is going to make your life harder.

Thank you very much. I changed my yaml keys to “slot_port” as you told me and added “type” key also, works like a charm now. That was so simple but i was stubborn and wanted to use yaml key name. Thanks for web link to. I will spent some time on Jinja2 resources.
Nornir is so cool, just need so time to get familiar with.

{% for interface in host["interfaces"] %}
 interface {{ interface.type }}{{ interface.slot_port }}
 ip address {{ interface.ip_addr }} {{ interface.mask }}
 no shut down
{% endfor %}

Result:

interface loopback0
 ip address 5.0.0.10 255.255.255.255
 no shut down
 interface FastEthernet0/0
 ip address 10.10.10.10 255.255.255.0
 no shut down
 interface FastEthernet0/1
 ip address None None
 no shut down
 interface FastEthernet1/0
 ip address None None
 no shut down
 interface Serial0/0
 ip address 10.100.100.1 255.255.255.252
 no shut down
 interface Serial0/1
 ip address None None
 no shut down
 interface Serial0/2
 ip address None None
 no shut down
 interface Serial0/3
 ip address None None
 no shut down

Just need to add some conditionals to filter not used ports.
Edit:
Conditionals for not used interfaces:

{% for interface in host["interfaces"] %}
{% if not interface.status == 'administratively down' %}
interface {{ interface.type }}{{ interface.slot_port }}
ip address {{ interface.ip_addr }} {{ interface.mask }}
no shutdown
{% else %}
interface {{ interface.type }}{{ interface.slot_port }}
shutdown
{% endif %}
{% endfor %}

Output:

interface loopback0
ip address 5.0.0.10 255.255.255.255
no shutdown
interface FastEthernet0/0
ip address 10.10.10.10 255.255.255.0
no shutdown
interface FastEthernet0/1
shutdown
interface FastEthernet1/0
shutdown
interface Serial0/0
ip address 10.100.100.1 255.255.255.252
no shutdown
interface Serial0/1
shutdown
interface Serial0/2
shutdown
interface Serial0/3
shutdown

Thanks again.

1 Like