Introduction
It is troublesome to create configs for network devices manually. In particular, when introducing a large amount of layer2 switches at once, it is easy to inadvertently forget to modify the unique settings.
In this post, I tried to automatically generate the config from a port management table and parameter sheet by rendering Jinja2 template.
Port Management Table
This time, I prepared the management table for Cisco Catalyst2960 switch with 8 + 2 ports.
port_list_hqaccess1.csv
The two uplink ports are connected to L3SW, and the eight downlink ports are connected to the terminal (status = o) or closed (status = x).
In addition, the uplink ports are connected by trunk VLANs, and downlink access ports use VLAN 100 or 101 with STP portfast setting.
Parameter Sheet
In this sheet, global settings unique to each device, such as the hostname, login information, and address, are defined in CSV format.
parameter_list_hqaccess1.csv
Jinja2 Template
Settings that are different for each device are defined in {{ var }}
.
Physical interface settings are enclosed by {%- for item in interfaces %}
and {%- endfor %}
to repeat the data in the port management table.
Also, the types of interface (description, switchport mode, speed, duplex, portfast, and status) are defined like {% if x == 'y' %}
-{% endif %}
.
catalyst2960_template.txt
!
no service pad
service timestamps debug datetime localtime
service timestamps log datetime localtime
service password-encryption
!
hostname {{ hostname }}
!
no logging console
enable secret {{ secret }}
!
username {{ username }} privilege 15 password {{ password }}
clock timezone JST 9
ip subnet-zero
no ip domain-lookup
ip domain-name {{ hostname }}
ip ssh version 2
!
spanning-tree mode pvst
no spanning-tree optimize bpdu transmission
spanning-tree extend system-id
!
!
{%- for item in interfaces %}
interface {{ item.port_no }}
{%- if item.description != '' %}
description << {{ item.description }} >>
{%- endif %}
{%- if item.mode == 'access' %}
switchport access {{ item.vlan }}
switchport mode access
{%- elif item.mode == 'trunk' %}
switchport mode trunk
{%- endif %}
{%- if item.duplex != 'auto' %}
duplex {{ item.duplex }}
{%- endif %}
{%- if item.speed != 'auto' %}
speed {{ item.speed }}
{%- endif %}
{%- if item.status == 'x' %}
shutdown
{%- endif %}
{%- if item.portfast == 'o' %}
spanning-tree portfast
{%- endif %}
!
{%- endfor %}
!
interface Vlan1
no ip address
no ip route-cache
shutdown
!
interface Vlan{{ vlan_num }}
description {{ vlan_desc }}
ip address {{ ip_address }} {{ subnet }}
no ip route-cache
!
ip default-gateway {{ default_gw }}
no ip http server
no ip http secure-server
!
logging 192.168.100.107
snmp-server community C1sc0 RO
snmp-server host 192.168.100.107 C1sc0
banner login ^C
============NOTICE==============
| This is test device for demo |
================================
^C
!
line con 0
line vty 0 4
login local
line vty 5 15
login local
!
ntp server {{ ntp_server }}
!
crypto key generate rsa modulus 2048
!
end
Python Script
config_generation.py
# -*- coding: utf-8 -*-
import jinja2
import csv
import re
# Define the path of each files
TEMPLATE = './catalyst2960_template.txt'
PARAMETER_LIST = './parameter_list_hqaccess1.csv'
PORT_LIST = './port_list_hqaccess1.csv'
CONFIG_FILENAME = './config_hqaccess1.txt'
def build_templates(template_file, parameter_list, port_list, config_filename):
templateLoader = jinja2.FileSystemLoader('./')
templateEnv = jinja2.Environment(loader=templateLoader)
template = templateEnv.get_template(template_file)
# Read the parameter sheet and convert it to a dictionary format
with open(parameter_list, 'rt') as fp:
reader_param = csv.DictReader(fp)
for dict_row1 in reader_param:
dict_param = dict(dict_row1)
# Read the port management table and convert it to a dictionary format
with open(port_list, 'rt') as fl:
reader_port = csv.DictReader(fl)
dict_port = {'interfaces':[]}
for dict_row2 in reader_port:
dict_port['interfaces'].append(dict(dict_row2))
# Combine the port table to the parameter sheet
dict_param.update(dict_port)
print(dict_param)
# Render the combined data to Jinja2 template and output the config
with open(config_filename, 'w') as cf:
outputText = template.render(dict_param)
cf.write(outputText)
print("Config File: %s" % config_filename)
if __name__ == "__main__":
build_templates(TEMPLATE, PARAMETER_LIST, PORT_LIST, CONFIG_FILENAME)
The point is that the port management table and parameter sheet are converted from CSV format to dictionary format when rendering to Jinja2 template.
The following is the contents of the dictionary variable dict_param
:
{'hostname': 'hqaccess1', 'hardware': 'Catalyst2960', 'secret': 'test', 'username': 'test', 'password': 'cisco', 'vlan_num': '100', 'vlan_desc': '<< Server Segment >>', 'ip_address': '192.168.100.47', 'subnet': '255.255.255.0', 'default_gw': '192.168.100.150', 'ntp_server': '192.168.100.44', 'interfaces': [{'port_no': 'FastEthernet0/1', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '100', 'portfast': 'o', 'status': 'o', 'description': 'To PC1'}, {'port_no': 'FastEthernet0/2', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '100', 'portfast': 'o', 'status': 'o', 'description': 'To PC2'}, {'port_no': 'FastEthernet0/3', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '100', 'portfast': 'o', 'status': 'x', 'description': ''}, {'port_no': 'FastEthernet0/4', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '100', 'portfast': 'o', 'status': 'x', 'description': ''}, {'port_no': 'FastEthernet0/5', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '101', 'portfast': 'o', 'status': 'o', 'description': 'To PC3'}, {'port_no': 'FastEthernet0/6', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '101', 'portfast': 'o', 'status': 'o', 'description': 'To PC4'}, {'port_no': 'FastEthernet0/7', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '101', 'portfast': 'o', 'status': 'x', 'description': ''}, {'port_no': 'FastEthernet0/8', 'speed': 'auto', 'duplex': 'auto', 'mode': 'access', 'vlan': '101', 'portfast': 'o', 'status': 'x', 'description': ''}, {'port_no': 'GigabitEthernet0/1', 'speed': '1000', 'duplex': 'full', 'mode': 'trunk', 'vlan': '', 'portfast': 'x', 'status': 'o', 'description': 'To hqdist1 Gi0/1'}, {'port_no': 'GigabitEthernet0/2', 'speed': '1000', 'duplex': 'full', 'mode': 'trunk', 'vlan': '', 'portfast': 'x', 'status': 'o', 'description': 'To hqdist2 Gi0/1'}]}
Config File: ./config_hqaccess1.txt
Generated Config
Jinja2 template (left) and generated config (right) are compared.
You can see that the expected values ββare embedded in the config.
Top comments (0)