Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
29
Help us understand the problem. What is going on with this article?
@tech_kitara

ポート管理表+パラメータ表+Jinja2テンプレートから、L2SWのConfigを自動生成してみた

More than 1 year has passed since last update.

はじめに

NW機器のConfig作成、手動でちまちまやるのって面倒くさいですよね?
特にL2SWは、まとめて大量に導入することが多く、コピペで作成すると、固有の設定をうっかり修正し忘れたりしがちです。
ポート管理表を個別に作成するケースも多く、何となく二度手間だと思っているのは自分だけではないはずです。

そこで今回は、ポート管理表と、その他のパラメータ表を元データとし、これにConfigのテンプレート(Jinja2テンプレート)をレンダリングすることで、L2SWの設定Configを自動生成してみました。

Jinja2とは

Python用のテンプレートエンジンの一つです。
詳しくは、下記のテンプレートファイルを見て頂くと分かると思いますが、テキストデータの中に、例えば{{ var }}と記載すると、そこに別で定義した変数varの値を埋め込むことができます。

他にも、‘{% for a in b %}{% endfor %}で繰り返し、{% if x == 'y' %}{% endif %}で条件分岐させることも可能です。

ポート管理表

今回は、8+2ポートのCisco Catalystスイッチをイメージして、CSV形式の管理表を作成しました。
port_list_hqaccess1.csv
port2.png

アップリンクの2ポートはL3SWに接続し、ダウンリンクの8ポートは端末に接続するか閉塞(status=x)しておくものとします。
また、アップリンクはトランクで接続し、ダウンリンクのアクセスポートは用途毎にVLAN100と101を使い分け、かつPortfastを適用するものとします。

パラメータ表

同じくCSV形式で、ホスト名、ログイン情報、アドレス等、各機器固有のパラメータを定義しました。
parameter_list_hqaccess1.csv
param.png

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

Pythonスクリプト

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に埋め込まれていることが分かります。
compare_result.png

最後に

今回はサンプルとして、1台のConfig生成を行いましたが、別機種のテンプレートも用意し、複数機器をループ処理で回せるようにすれば、かなりの効率化、品質向上が見込めるのではないでしょうか。

29
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
29
Help us understand the problem. What is going on with this article?