포트 관리 테이블 + 파라미터 테이블 + Jinja2 템플릿에서 L2SW의 Config를 자동 생성 해 보았습니다.

소개



NW 기기의 Config 작성, 수동으로 찢어지는 것은 귀찮아요?
특히 L2SW는 일괄적으로 대량으로 도입하는 경우가 많고, copipe로 작성하면 고유의 설정을 실수로 수정하는 것을 잊기 쉽습니다.
포트 관리표를 개별적으로 작성하는 경우도 많고, 왠지 두 번 번거롭다고 생각하는 것은 자신만이 아닐 것입니다.

그래서 이번에는 포트 관리표와 그 외의 파라미터표를 원데이터로 하고, 이것에 Config의 템플릿(Jinja2 템플릿)을 렌더링 하는 것으로, L2SW의 설정 Config를 자동 생성해 보았습니다.

Jinja2란?



파이썬 용 템플릿 엔진 중 하나입니다.
자세한 것은, 이하의 템플릿 파일을 봐 주시는 것을 알겠지만, 텍스트 데이터안에, 예를 들면 {{ var }} 라고 기재하면, 거기에 별로 정의한 변수 var 의 값을 포함할 수 있습니다 .

그 외에도 ‘ {% for a in b %}{% endfor %} 로 반복하고 {% if x == 'y' %}{% endif %} 로 조건 분기시킬 수도 있습니다.

포트 관리 테이블



이번에는 8+2 포트 Cisco Catalyst 스위치를 이미지화하여 CSV 형식 관리 테이블을 만들었습니다.
port_list_hqaccess1.csv


업링크의 2포트는 L3SW에 접속하고, 다운링크의 8포트는 단말에 접속하거나 폐색(status=x) 해 두는 것으로 합니다.
또한 업링크는 트렁크로 연결되며, 다운링크 액세스 포트는 용도별로 VLAN 100과 101을 구분하며 Portfast를 적용해야 합니다.

파라미터 테이블



마찬가지로 CSV 형식으로 호스트명, 로그인 정보, 주소 등 각 기기 고유의 파라미터를 정의했습니다.
parameter_list_hqaccess1.csv


Jinja2 템플릿



각 장비마다 다른 설정은 {{ var }}에 정의되어 있습니다.
물리적 인터페이스 설정은 포트 관리 테이블의 데이터를 반복적으로 처리하기 위해 ‘{% for a in b %} ~ {% endfor %}로 묶여 있습니다.
또한 인터페이스의 종류(Description 유무, 액세스/트렁크, Speed/Duplex, 개방/폐색, Portfast 유무)에 따라 {% 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

파이썬 스크립트



config_generation.py
# -*- coding: utf-8 -*-
import jinja2
import csv
import re

# 各種ファイルのパスを定義
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)

    # パラメータ表を読み込み、辞書形式に変換
    with open(parameter_list, 'rt') as fp:
        reader_param = csv.DictReader(fp)
        for dict_row1 in reader_param:
            dict_param = dict(dict_row1)

    # ポート表を読み込み、辞書形式に変換
    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))

    # ポート表をパラメータ表へ結合
    dict_param.update(dict_port)
    print(dict_param)

    # 結合データをJinja2テンプレートにレンダリングし、Configを出力
    with open(config_filename, 'w') as cf:
        outputText = template.render(dict_param)
        cf.write(outputText)

    print("Configファイル生成: %s" % config_filename)


if __name__ == "__main__":
    build_templates(TEMPLATE, PARAMETER_LIST, PORT_LIST, CONFIG_FILENAME)

처리내용은 스크립트내에 기재하고 있습니다만, 포인트는, Jinja2 템플릿에 렌더링 함에 있어서, 포트 관리 테이블과 파라미터 테이블을 CSV 형식에서 사전 데이터 형식으로 변환하고 있는 점입니다.
다음은 사전 데이터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ファイル生成: ./config_hqaccess1.txt

생성된 Config



Jinja2 템플릿(왼쪽)과 생성된 Config(오른쪽)를 비교하고 있습니다.
예상대로 값이 Config에 포함되어 있음을 알 수 있습니다.


마지막으로



이번은 샘플로서, 1대의 Config 생성을 실시했습니다만, 다른 기종의 템플릿도 준비해, 복수 기기를 루프 처리로 돌릴 수 있도록 하면, 상당한 효율화, 품질 향상을 전망할 수 있는 것은 아닐까요? .

좋은 웹페이지 즐겨찾기