Table of Contents

In this section we will configure network devices using restconf protocol but through json based jinja2 configuration template and yaml based configuration data.

json based jinja2 configuration template code example

This is a figure that shows how jinja2 template works.

json based jinja2 configuration template example
json based jinja2 configuration template example

In addition to inventory files that list network devices, there are three other components to automate network device configuration.

json based configuration template

A configuration template through which we configure many network devices, but each device with it’s own configuration data.

In other words, with jinja2, network configuration of many network devices will be converted to a single configuration template using variable substitution, conditionals and looping.

This is exact the ospf configuration in the format of json that we have used in the previous section. I have replaced the configuration data with variables and also added looping to match ospf configuration of many network devices.

majid@ubuntu2204tls:~/devnet/pyhton_nornir/2023/13.restconf$ cat templates/ospf_.j2
{
    "Cisco-IOS-XE-ospf:ospf": {
        {% for ospf in host.hdata.ospf %}
        "process-id": [
            {
                "id": "{{ ospf.process_id }}",
                "network": [
                {% for networks in ospf.networks %}
                    {"ip": "{{ networks.network }}", "wildcard": "{{ networks.wildmask }}", "area": "{{ networks.area }}"},
                {% endfor %}
                ],
                "router-id": "{{ ospf.router_id }}"
            }
        ]
    {% endfor %}
    }
}

In the course, “CLI based Network Automation using Python Nornir”, lesson 24 and lesson 25 we have already discussed how to prepare jinja2 configuration template

In this course, we also implemented already jinja2 template in Netconf protocol.

yaml based configuration data

For each device there is configuration data file in the format of yaml, in which keep the data specific to that network devices.

majid@ubuntu2204tls:~/devnet/pyhton_nornir/2023/13.restconf$ cat host_vars/R1_.yaml
---
ospf:
  - process_id: 1
    router_id: 1.1.1.1
    networks:
      - network: 192.168.111.0
        wildmask: 0.0.0.255
        area: 0
      - network: 192.168.12.0
        wildmask: 0.0.0.255
        area: 0
      - network: 192.168.13.0
        wildmask: 0.0.0.255
        area: 1
      - network: 192.168.14.0
        wildmask: 0.0.0.255
        area: 1

In this example, for each device the data relate to ospf configuration, including process-id, network numbers and area numbers are stored inside yaml based configuration data.

automation script using json based jinja2 configuration template

And finally, the automation script.

 In the automation script, first we load data of each network device using “load_yaml”. The loaded data will be stored in a dictionary variable with the name of device, and a key. Key is “hdata” in our example.

majid@ubuntu2204tls:~/devnet/pyhton_nornir/2023/13.restconf$ cat 13.8.restconf_edit_config_with_template.py
import requests
import json
from nornir import InitNornir
from rich import print as rprint
from nornir_utils.plugins.functions import print_result
from nornir_jinja2.plugins.tasks import template_file
from nornir_utils.plugins.tasks.data import load_yaml


requests.packages.urllib3.disable_warnings()

nr = InitNornir(config_file="config.yaml")

headers = {
    "Accept": "application/yang-data+json",
    "Content-Type": "application/yang-data+json",
}


def load_data(task):
  hosts_data = task.run(task=load_yaml, file=f"./host_vars/{task.host}_.yaml")
  task.host["hdata"] = hosts_data.result


def restconf_edit_config_with_template(task):
    template_to_load = task.run(task=template_file, template="ospf_.j2", path="templates")
    payload = template_to_load.result
    module = "Cisco-IOS-XE-native:native/router/router-ospf/ospf"
    url = f"https://{task.host.hostname}:443/restconf/data/{module}"

    response = requests.put(url, headers=headers, data=payload, auth=(f"{task.host.username}", f"{task.host.password}"), verify=False)
    print(response)

nr.run(task=load_data)
results = nr.run(task=restconf_edit_config_with_template)
print_result(results)

Then we load the configuration template.

The configuration of each device is extracted from the configuration template, with replacing the variables with loaded device-specific data.

The variables start always with “host.hdata”. “host” keyword is always fixed. “hdata” is the name of the key in the dictionary structure, into which each device’s data is loaded.

The other part of the variable’s structure is extracted from the yaml file structure.

For example “host.hdata.ospf” is pointing to the root of ospf in yaml data file.

The variable “host.hdata.ospf.process_id” points to the process id of the ospf inside the yaml data file.

And finally, the configuration will be applied to the network devices using “requests.put” command.

analysing script result

If we run the script, you can see in the output that the ospf configuration in JSON format for each device is extracted from the jinja2 template. It is done with replacing the variables and parsing the loop logic in the configuration template.

But we receive the response code 400, which means the configuration is not successfully published to the network device.

To see what the problem is, I pushed the final configuration directly to the network device and found that in the list of networks, there shouldn’t be a “comma” on the last line.

json based jinja2 configuration template script result
json based jinja2 configuration template script result

Unfortunately, I could not find a solution for this problem.

Please inform me if you know any solution.

json nased jinja2 configuration template can be downloaded form this link.

Back to: YANG based Network Automation using NETCONF RESTCONF gNMI > Network Automation using RESTCONF

Leave a Reply

Your email address will not be published. Required fields are marked *


Post comment