シスコサポートコミュニティ
キャンセル
次の結果を表示 
次の代わりに検索 
もしかして: 

Nexus: Python によるカスタムコマンドとデーモンの作成手法

Nexus は古いバージョンでなければ Python というスクリプト言語を利用することができます。

Python を使うことで

 - 複雑な処理をする新しいコマンド

 - 定期的な処理を繰り返すデーモン

を簡単に作成することができます。

ここでは以下のドキュメントの障害を防ぐためのコマンドやデーモンを作成することを通して作り方を紹介したいと思います。

 

N5K: VPC+, Fex 環境で Unknown Unicast が Flooding されない場合がある

https://supportforums.cisco.com/ja/document/12331506

 

まず、上記ドキュメントに目を通してもらえば分かりますが障害は「Nexus5500 が FEX の先にあるホストのエントリを学習していれば発生しない」ということがわかります。

そのため、ここでは「dynamic に学習した FEX のエントリを static に変換する」コマンドと、それを自動で行うデーモンを作成することとします。

 

Python スクリプトの起動方法

バージョンや機種により異なりますが、今回使用する Nexus5500 の バージョン 6.0(2)N2(5) では

python FILE_NAME

と指定します。

今回はファイル名を d2s.py とし、それを bootflash 直下に保存してあるとします。 その場合は以下のようになります。

NEXUS# python d2s.py
もしくは
NEXUS# python bootflash:d2s.py

 

コマンドの作成方法

実はコマンドを作成するのは非常に簡単であり、pythonで「特定の処理を一回走らせて終わり」というような実装をすればほぼ問題無く使えます。

ここでは「dynamic に学習した FEX の MAC エントリを static にする」というコマンドを以下の様な手順で作成します

  1. show mac address-table dynamic を発行
  2. その出力をパースし、FEX で dynamic に学習しているもののみ抽出
  3. static mac コマンドを生成
  4. static mac コマンドを発行

 

#出力例
"""
NEXUS# show mac address-table dynamic
Legend:
        * - primary entry, G - Gateway MAC, (R) - Routed MAC, O - Overlay MAC
        age - seconds since last seen,+ - primary entry using vPC Peer-Link
   VLAN     MAC Address      Type      age     Secure NTFY   Ports/SWID.SSID.LID
---------+-----------------+--------+---------+------+----+------------------
* 120      547f.ee48.fbbc    dynamic   0          F    F  Eth101/1/20 
* 119      547f.ee48.fbbc    dynamic   10         F    F  Eth101/1/19
* 118      547f.ee48.fbbc    dynamic   20         F    F  Eth101/1/18
* 1        6c9c.ed45.ec14    dynamic   0          F    F  Eth1/25
* 1        6c9c.ed45.ec15    dynamic   0          F    F  Eth1/13 
"""

# 全体の流れ
def dynamic2static():
	show_mac = cisco.cli('show mac address-table dynamic')[1]
	lines = show_mac.split('\n')
	dynamic_entries_on_fex = filter(is_dynamic_learn_on_fex, lines)
	static_mac_commands = map(get_static_mac_command, dynamic_entries_on_fex)
	cisco.cli("conf t")
	for command in static_mac_commands:
		cisco.cli(command)

# FEX で dynamic 学習しているエントリか判別
def is_dynamic_learn_on_fex(line):
	words = line.split()
	if(len(words) != 8):
		return False
	if(words[3] != 'dynamic'):
		return False
	num_of_slash = len(words[7].split('/')) - 1
	return num_of_slash == 2

# static で学習させるコマンドを作成
def get_static_mac_command(line):
	words = line.split()
	vlan = words[1]
	mac = words[2]
	interface = words[7]
	return 'mac address-table static ' + mac + ' vlan ' + vlan + ' interface ' + interface

上記の dynamic2static() 関数を呼ぶように python プログラムを起動してやればコマンドのように動きます。

clear するコマンドもほぼ同じ動きです。

 

デーモンの作成

先の学習方法は「一度動かしたら終わり」というような場合に使います。watchdog のような定期的な監視をしたいといった場合は「ずっと動き続ける」プログラムが必要です。そのようなプログラムはデーモンと呼ばれています。

デーモンを作ることは簡単でループ処理の中で「スリープ」と「処理の実行」を繰り返すことで実現できます。例えば、先の dynamic エントリを static エントリに変換するデーモンでしたら、以下のように作れます。

def start_daemon(interval):
	while True:
		time.sleep(interval)
		dynamic2static()

 

本来はデーモンを停止する方法や、デーモンがコンソールを奪わないようにするための方法が必要です。

後者については thread を使ったり Nexus 自体の機能を使ったりすることで実現できますが、今回は割愛します。

 

引数を使った起動

今回のスクリプトは一つのファイルに様々な機能がのっています。そのため、1タイムの学習、デーモンとしての起動、static エントリの削除などを使い分ける必要があります。

一般的にはプログラムの起動時に「引数」と呼ばれるオプションをつけることで、上記を使い分けます。

例えば

NEXUS# python d2p.py learn

という起動方法の場合 learn が引数です。

これをプログラム側で読み取り、与えられた内容に沿って挙動をかえます。

 

def print_usage():
	print('usage: python d2s.py <OPTION>')
	print('Options and arguments')
	print('help: show help message')
	print('learn: convert current dynamic mac entry to static entry on fex')
	print('daemon <INTERVAL_SEC>: periodically convert dynamic entry to static entry on fex')
	print('')

if(__name__ == '__main__'):
	num_of_arg = len(sys.argv)
	if(num_of_arg == 1):
		print_usage()
	elif(num_of_arg == 2 and sys.argv[1] == 'learn'):
		dynamic2static()
	elif(num_of_arg == 2 and sys.argv[1] == 'clear'):
		clear_static()
	elif(num_of_arg == 2 and sys.argv[1] == 'help'):
		print_usage()		
	elif(num_of_arg == 3 and sys.argv[1] == 'daemon'):
		interval = int(sys.argv[2])
		start_daemon(interval)
	else:
		print('** SYNTAX ERROR **')
		print_usage()

 

 

プログラムの挙動確認

 

事前に3つのエントリをFEXで動的に学習させています。

n5548_f09(config)# show mac address-table dynamic
Legend:
        * - primary entry, G - Gateway MAC, (R) - Routed MAC, O - Overlay MAC
        age - seconds since last seen,+ - primary entry using vPC Peer-Link
   VLAN     MAC Address      Type      age     Secure NTFY   Ports/SWID.SSID.LID
---------+-----------------+--------+---------+------+----+------------------
* 120      547f.ee48.fbbc    dynamic   0          F    F  Eth101/1/20  
* 119      547f.ee48.fbbc    dynamic   10         F    F  Eth101/1/19
* 118      547f.ee48.fbbc    dynamic   20         F    F  Eth101/1/18
* 1        6c9c.ed45.ec14    dynamic   0          F    F  Eth1/25
* 1        6c9c.ed45.ec15    dynamic   0          F    F  Eth1/13 

 

このエントリを static に変換します。

n5548_f09(config)# python d2s.py learn 

 

テーブルを見ると static になっていることがわかります。

n5548_f09(config)# show mac address-table static | inc Eth
* 120      547f.ee48.fbbc    static    20         F    F  Eth101/1/20 
* 119      547f.ee48.fbbc    static    30         F    F  Eth101/1/19
* 118      547f.ee48.fbbc    static    40         F    F  Eth101/1/18 

 

次にデーモンを使ってリアルタイムに dynamic エントリを static エントリに変換させます。チェックインターバルは5秒としています。

n5548_f09(config)# python d2s.py daemon 5 

 

動的に学習するようにpingをN5Kに打ってやると、それが static になっていることがわかります。

n5548_f09(config)# show mac address-table static | inc Eth
* 122      547f.ee48.fbbc    static    0          F    F  Eth101/1/22  
* 121      547f.ee48.fbbc    static    0          F    F  Eth101/1/21  
* 120      547f.ee48.fbbc    static    20         F    F  Eth101/1/20
* 119      547f.ee48.fbbc    static    30         F    F  Eth101/1/19
* 118      547f.ee48.fbbc    static    40         F    F  Eth101/1/18 

 

最後に static に学習しているエントリを消去します。

n5548_f09(config)# python d2s.py clear

n5548_f09(config)# show mac address-table static | inc Eth
n5548_f09(config)# 

 

プログラム全体

最後にプログラム全体を記載いたします。

#!/user/bin/python
# coding: utf-8

import sys, time, cisco

def is_dynamic_learn_on_fex(line):
	words = line.split()
	if(len(words) != 8):
		return False
	if(words[3] != 'dynamic'):
		return False
	num_of_slash = len(words[7].split('/')) - 1
	return num_of_slash == 2

def is_static_learn_command_on_fex(line):
	words = line.split()
	if(len(words) != 8):
		return False
	if(words[2] != 'static'):
		return False
	num_of_slash = len(words[7].split('/')) - 1
	return num_of_slash == 2

def get_static_mac_command(line):
	words = line.split()
	vlan = words[1]
	mac = words[2]
	interface = words[7]
	return 'mac address-table static ' + mac + ' vlan ' + vlan + ' interface ' + interface

def get_no_static_mac_command(line):
	words = line.split()
	mac = words[3]
	vlan = words[5]
	interface = words[7]
	return 'no mac address-table static ' + mac + ' vlan ' + vlan + ' interface ' + interface

def dynamic2static():
	show_mac = cisco.cli('show mac address-table dynamic')[1]
	lines = show_mac.split('\n')
	dynamic_entries_on_fex = filter(is_dynamic_learn_on_fex, lines)
	static_mac_commands = map(get_static_mac_command, dynamic_entries_on_fex)
	cisco.cli("conf t")
	for command in static_mac_commands:
		cisco.cli(command)

def clear_static():
	show_run_static_mac = cisco.cli('show run | inc address-table')[1]
	lines = show_run_static_mac.split('\n')
	static_entries_on_fex = filter(is_static_learn_command_on_fex, lines)
	no_static_mac_commands = map(get_no_static_mac_command, static_entries_on_fex)
	cisco.cli("conf t")
	for command in no_static_mac_commands:
		cisco.cli(command)	

def start_daemon(interval):
	while True:
		time.sleep(interval)
		dynamic2static()

def print_usage():
	print('usage: python d2s.py <OPTION>')
	print('Options and arguments')
	print('help: show help message')
	print('learn: convert current dynamic mac entry to static entry on fex')
	print('daemon <INTERVAL_SEC>: periodically convert dynamic entry to static entry on fex')
	print('')

if(__name__ == '__main__'):
	num_of_arg = len(sys.argv)
	if(num_of_arg == 1):
		print_usage()
	elif(num_of_arg == 2 and sys.argv[1] == 'learn'):
		dynamic2static()
	elif(num_of_arg == 2 and sys.argv[1] == 'clear'):
		clear_static()
	elif(num_of_arg == 2 and sys.argv[1] == 'help'):
		print_usage()		
	elif(num_of_arg == 3 and sys.argv[1] == 'daemon'):
		interval = int(sys.argv[2])
		start_daemon(interval)
	else:
		print('** SYNTAX ERROR **')
		print_usage()
バージョン履歴
改訂番号
1/1
最終更新:
‎10-29-2014 02:17 PM
更新者: