Python
Network
Cisco
Jupyter

Jupyter NotebookでCiscoルータコンフィグ生成ツールの作成

Jupyter Notebookでipywidgetとjinja2を使い、Ciscoルータ(IOS)のコンフィグ生成ツールを作成します。

ipywidgetはJupyter Notebook上にテキストボックスやボタンを配置できるUIライブラリです。jinja2はpythonのテンプレートエンジンライブラリです。今回はコンフィグ作成に必要なパラメータをテキストボックスに入力し、ボタンを押すとCiscoルータのコンフィグ作成するようなツールを作ってみます。

早速ですが、Pythonコードと実行結果から確認していきます。

Pythonコード

python
from IPython.display import display, clear_output
from jinja2 import Template
from ipywidgets import Text, BoundedIntText, Button

global_template_txt = '''
hostname {{t_hostname}}
no ip domain-lookup
'''

recursive_template_txt = '''
int giga {{t_int_num}}
 no switchport
 ip address {{t_ip_address}} {{t_mask}}
 no shutdown
'''

global_template = Template(global_template_txt)
recursive_template = Template(recursive_template_txt)

tbox_hostname = Text (value='L3-SW1', description='hostname')
tbox_interface_num = BoundedIntText(value=0, min=0, max=5, step=1, description='Interface数:')
button = Button(description='generate config')

display(tbox_hostname)
display(tbox_interface_num)
display(button)

interface_boxes = []

def on_button_clicked(b) :
    global interface_boxes
    global_config = global_template.render(t_hostname = tbox_hostname.value)
    print(global_config)

    for i in range(tbox_interface_num.value) :
        recursive_config = recursive_template.render(t_int_num=interface_boxes[i].children[0].value,
                                                     t_ip_address=interface_boxes[i].children[1].value, 
                                                     t_mask=interface_boxes[i].children[2].value)
        print(recursive_config)

def on_value_change(change) :  
    global interface_boxes
    clear_output()
    display(tbox_hostname)
    display(tbox_interface_num)

    interface_boxes = []
    for i in range(change['new']) :
        interface_elements = [
            Text(value='1/0/1', description='I/F num'),
            Text(value='10.10.10.1',  description='I/F ip add'), 
            Text(value='255.255.255.0',  description='I/F mask')]
        interface_boxes.append(ipywidgets.Box(children = interface_elements))
        display(interface_boxes[i])

    display(button)
    button.on_click(on_button_clicked)

tbox_interface_num.observe(on_value_change, names='value')
button.on_click(on_button_clicked)

実行結果

初期画面

Screenshot from 2017-11-26 20-32-37.png

Interface数変更時

Screenshot from 2017-11-26 20-32-48.png

generate configボタン押下時

Screenshot from 2017-11-26 20-33-13.png

コンフィグの説明

実行順に見ていきます。

テンプレートの定義

python
global_template_txt = '''
hostname {{t_hostname}}
no ip domain-lookup
'''

recursive_template_txt = '''
int giga {{t_int_num}}
 no switchport
 ip address {{t_ip_address}} {{t_mask}}
 no shutdown
'''

global_template = Template(global_template_txt)
recursive_template = Template(recursive_template_txt)

ここでCiscoコンフィグのテンプレートを定義しています。グローバルコンフィグがglobal_templateに対応し、インターフェースコンフィグがrecursive_templateに対応しています。{{}}で囲まれた部分が変数になっており、あとから値を埋め込む箇所です。

初期画面の出力

python
tbox_hostname = Text (value='L3-SW1', description='hostname')
tbox_interface_num = BoundedIntText(value=0, min=0, max=5, step=1, description='Interface数:')
button = Button(description='generate config')

display(tbox_hostname)
display(tbox_interface_num)
display(button)

ここでホスト名とInterface数を入力するテキストボックス、及びコンフィグ作成ボタンを描画しています。

ipywidgetのイベント検出

python
tbox_interface_num.observe(on_value_change, names='value')
button.on_click(on_button_clicked)

一番最後の2行は、Interface数が変更された場合とボタンが押された場合に、それぞれon_value_change関数、on_button_clicked関数を呼び出すための記述です。

on_value_cange関数

python
def on_value_change(change) :  
    global interface_boxes
    clear_output()
    display(tbox_hostname)
    display(tbox_interface_num)

    interface_boxes = []
    for i in range(change['new']) :
        interface_elements = [
            Text(value='1/0/1', description='I/F num'),
            Text(value='10.10.10.1',  description='I/F ip add'), 
            Text(value='255.255.255.0',  description='I/F mask')]
        interface_boxes.append(ipywidgets.Box(children = interface_elements))
        display(interface_boxes[i])

    display(button)
    button.on_click(on_button_clicked)

on_value_change関数では、Interface数の変更に応じて画面に表示するインターフェースコンフィグ用のテキストボックス数を変えています。使うInterfaceが1つならテキストボックスも1行分、使うInterfaceが2つならテキストボックスも2行分出力させます。

on_button_clicked関数

python
def on_button_clicked(b) :
    global interface_boxes
    global_config = global_template.render(t_hostname = tbox_hostname.value)
    print(global_config)

    for i in range(tbox_interface_num.value) :
        recursive_config = recursive_template.render(t_int_num=interface_boxes[i].children[0].value,
                                                     t_ip_address=interface_boxes[i].children[1].value, 
                                                     t_mask=interface_boxes[i].children[2].value)
        print(recursive_config)

on_button_clicked関数では、コンフィグ作成・出力を行っています。コンフィグ作成ボタンが押された時に、global_templateは1回だけ、recursive_templateはInterface数分だけ繰り返し処理を行い、各テキストボックスの値をテンプレートの変数部分に埋め込んだあと、作成されたコンフィグを画面出力しています。

さいごに

正直なところ、自分が使う分にはipywidgetよりもテキストベースで直接パラメータ入力したほうが効率的です。
どちらかというと、プロジェクトやチーム内で共用するなど、他人に使ってもらうことを想定しています。