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.
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.
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.