2014-01-22 03:23 PM
2014年現在、比較的新しいバージョンのNexusにおいては Python を利用することができます。
Python を利用することで 「EEM をより柔軟に強力に利用する」「複雑なログを簡単な操作で取得する」といったことが可能です。
本ドキュメントでは後者の Python Script を利用したログの取得方法について取り扱います。
なお、python のプログラミング自体に関する説明は必要最低限に留めます。
本ドキュメントは Nexus 6000 上で動作確認をしております。
Nexus 3000, 5000, 6000, 7000, 9000 で Python が利用できます。
ただし、古いバージョンのOSを利用している場合は利用できません。
以下のように python コマンドを受け入れれば利用可能なバージョンです。
SWITCH(config)# python
Python 2.7.2 (default, Nov 27 2012, 17:50:33)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Loaded cisco NxOS lib!
>>> 1+1
2
>>> exit()
SWITCH(config)#
どの型番のNexusにおいても Python の挙動自体に大差はございません。
ただ、スクリプトの呼び出し方などが若干異なっております。この点に関しては後述いたします。
以下の様なシナリオでログを取得するスクリプトを作成いたします。
- FabricPath の障害が発生している
- 多数の FabricPath の show コマンドを発行する
- 特定インタフェースの通信レートを定期的に確認する
- 任意のファイルに結果を出力する
※ 本スクリプトは実際に弊社検証で利用したものからお客様情報を排し、簡素化した(取得コマンド数を減らした)ものとなります。
Python で Cisco 機器のコマンドを発行する方法
パッケージ cisco をインポートする必要があります。
パッケージ cisco の関数 cli() に実行したいコマンドを文字列として渡すことで、コマンドが実行されます。
関数 cli() の返り値はタプル型であり、2つめのエレメントがコマンドの実行結果(文字列)となります。
SWITCH(config)# python
Python 2.7.2 (default, Nov 27 2012, 17:50:33)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Loaded cisco NxOS lib!
>>> import cisco
>>> showVer = cisco.cli("show version")
>>> type(showVer)
<type 'tuple'>
>>> showVer[1]
'Cisco Nexus Operating System (NX-OS) Software\nTAC support: http://www.cisco.com/tac\n <以下省略>
本ドキュメントでは関数 cli() に関してこれ以上言及いたしませんが、この返り値を解析するコードを書くことで「状態に応じた取得コマンドの変更」や、「機器の挙動に影響を与えるコマンドを発行する」ことが可能です。
実はPythonシェルではこの cisco パッケージの import はデフォルトで行われているので上記の import はなくても動きます。ただ、python Script を呼び出す際は自動で import されないので、明示的に import する必要があります。
Nexus 7000 は Python スクリプトを script コマンドで呼び出します。
スクリプトファイルは bootflash:source/ 配下に配置する必要があります。
呼び出しコマンドは以下のようになります。
SWITCH# script test.py
Nexus 6000 は python コマンドでスクリプトを呼び出します。
Script ファイルは好きなところに置いて構いませんが、呼び出す際にパスの指定が必要です。
SWITCH# python bootflash:test.py
スクリプトに対しパラメータを渡すには、ファイル名の後にパラメータを指定します。
以下の例では 1, 2, 3 がパラメータとなります。
SWITCH# python bootflash:test.py 1 2 3
以下の様なシンタックスでコマンドの結果をファイルに出力を行いたいとします。
コマンドの発行時刻
# 発行コマンド
コマンドの結果(複数行)
....
<改行>
このシンタックスはログを確認する上で効率のよいものです。
コマンドの発行時刻は「いつ」どうなっていたかということを知る上で必要です。
また、コマンド名は「コマンドの出力結果に記載されていない」ので、別途書き出す必要があります。
コマンド名がないと出力がどのコマンドか把握しづらく、またコマンドの境界が不明瞭になります。
コマンド名前にハッシュ(#)をつけているのはテキスト検索でコマンド部分を見つけやすくするためです。
最後の改行は次のコマンドとの間に間隔を開けて見栄えをよくするためにいれています。
必要であれば改良するなりしてご自分が使いやすい形式で書き出してください。
以下のような関数を定義することで、このシンタックスで書きだすコマンドを生成できます。
def makeEcho(message, fileName):
return 'echo \"{}\" >> bootflash:{}'.format(message, fileName)
def makeShow(command, fileName):
return '{} >> bootflash:{}'.format(command, fileName)
def makeCombi(command, fileName):
showClock = makeShow('show clock', fileName)
echoCommand = makeEcho('#' + command, fileName)
showCommand = makeShow(command, fileName)
echoNL = makeEcho('', fileName)
return '{} ;{} ;{} ;{}'.format(showClock, echoCommand, showCommand, echoNL)
上記の1つめの関数 makeEcho() では、特定文字列をそのままファイルに書き出すコマンド(文字列)を作成します。
2つめの関数 makeShow() は、コマンドの実行結果をファイルに書き出すためのコマンド(文字列)を作成します。
なお、生成されるコマンドを見て分かるように、ファイルへの書き出し処理は python の機能ではなくNexus のリダイレクト処理を利用しています。
3つめの関数 makeCombi() は上記2つの関数を使うことで、先に提示したシンタックスを作成するラッパ関数です。
この3つめの関数 makeCombi() に適切な show コマンドを与えて書き出し用のコマンドを生成し、それを cli() 関数に渡すことにより実際にファイルに対して書き出し処理がなされます。
from cisco import *
cli(makeCombi('show fabricpath isis route', 'test.log'))
書きだされたファイルを開くと、"show fabricpath isis route" が定義されたシンタックスに基づいて出力されていることが確認できます。
SWITCH(config)# show file bootflash:test.log
14:25:39.998 UTC Tue Jan 21 2014 # コマンド発行時刻
#show fabricpath isis route # コマンド名
Fabricpath IS-IS domain: default MT-0 # コマンド出力(複数行)
Topology 0, Tree 0, Swid routing table
<省略>
SWITCH(config)
Python のループとスリープ機能を使うと、簡単にコマンドを定期的に実行することができます。
ここでは、3秒間隔で3回、port-channel 1 の通信レートを取得しています。
from cisco import *
import sys, time
def writeRateLogs(fileName, interval, n):
for i in range(0, n):
cli(makeCombi('show interface port-channel 1 | grep rate', fileName))
time.sleep(interval)
writeRateLogs("test.log", 3, 3)
弊社機器のコマンドには細かいパラメータのみ若干異なるコマンドが多数あります。
例えば Table 情報や Neighbor 情報などを指定するコマンドを思い浮かべて頂けると分かりやすいと思います。
これを全てベタ書きで取得することも可能ですが、変化がある場所のみ変更させてループを回したほうがコードの保守性が高くなります。
以下に fabricPath のコマンドで ftag と switch-id を変更してコマンド文を生成し、それを実行するコードを記載いたします。
def writeFPLogs(fileName):
ftaglist = [1,2]
swidlist = [1,2,3,4]
for swid in swidlist:
for ftag in ftaglist:
showFtagSwid = 'show platform fwm info l2mp route ftag {} swid {} hw'.format(str(ftag), str(swid))
cli(makeCombi(showFtagSwid, fileName))
上記コードでは FabricPath の Switch ID 1,2,3,4 の機器において ftag 1,2 それぞれの転送情報を取得するコマンドを生成しています。
ループを回すと以下のようなコマンドが生成され、個別に makeCombi() 及び cli() 関数で実行されます。
#show platform fwm info l2mp route ftag 1 swid 1 hw
#show platform fwm info l2mp route ftag 2 swid 1 hw
#show platform fwm info l2mp route ftag 1 swid 2 hw
<以下省略>
今回は全部で8つのコマンドのみ生成しておりますが、規模が大きくなるほどベタ書きのコードは保守性が低くなります。
また、強いて言えば ftaglist, swidlist も定数などとして関数外に定義したほうがよいかもしれません。
今回のスクリプトは内部で関数 sleep() を呼び出しているため、実行から終了までに時間がかかります。
そのため、現在のスクリプトの進捗状況をコンソールに表示したほうがプログラムの実行者が「今、スクリプトが何を実行しているのか」が分かって安心できます。
この進捗状況の通知はコンソールへの出力が一般的ですが、python ではこれを関数 print() を使うことで実現できます。
ただ、いくつかの Nexus においてはコンソールへの標準出力が内部でバッファリングされてしまい、スクリプトが終了するまで出力が得られないことがございます。
関数 print() を以下のように flush させる関数 sys.stdout.flush() とともにラップして使用すると、リアルタイムでコンソールへ出力を行うことが可能となります。
import sys
def printConsole(message):
print(message)
sys.stdout.flush() # to avoid IO delay problem on some Nexus.
fileName = 'test.log'
sleepInterval = int('3')
sleepCount = int('3')
printConsole('taking Rate logs')
writeRateLogs(fileName, sleepInterval, sleepCount)
printConsole('taking FP logs')
writeFPLogs(fileName)
printConsole('taking Rate logs')
writeRateLogs(fileName, sleepInterval, sleepCount)
最後に本ドキュメントのもとになった Python のスクリプトと、呼び出しコマンド及び出力を記載いたします。
ソースコード
from cisco import *
import sys, time
SHOW_CLOCK = 'show clock'
SHOW_RATE_PO1 = 'show interface port-channel 1 | grep rate'
SHOW_RATE_E1_1 = 'show interface ethernet 1/1 | grep rate'
SHOW_FP_ISIS_ROUTE = 'show fabricpath isis route'
SHOW_FWM_L2MP_ALL = 'show platform fwm info l2mp topo all hw'
def printConsole(message):
print(message)
sys.stdout.flush() # to avoid IO delay problem on some Nexus.
def makeEcho(message, fileName):
return 'echo \"{}\" >> bootflash:{}'.format(message, fileName)
def makeShow(command, fileName):
return '{} >> bootflash:{}'.format(command, fileName)
def makeCombi(command, fileName):
showClock = makeShow(SHOW_CLOCK, fileName)
echoCommand = makeEcho('#' + command, fileName)
showCommand = makeShow(command, fileName)
echoNL = makeEcho('', fileName)
return '{} ;{} ;{} ;{}'.format(showClock, echoCommand, showCommand, echoNL)
def writeRateLogs(fileName, interval, n):
for i in range(0, n):
cli(makeCombi(SHOW_RATE_PO1, fileName))
cli(makeCombi(SHOW_RATE_E1_1, fileName))
time.sleep(interval)
def writeFPLogs(fileName):
cli(makeCombi(SHOW_FP_ISIS_ROUTE, fileName))
cli(makeCombi(SHOW_FWM_L2MP_ALL, fileName))
ftaglist = [1,2]
swidlist = [1,2,3,4]
for swid in swidlist:
for ftag in ftaglist:
showFtagSwid = 'show platform fwm info l2mp route ftag {} swid {} hw'.format(str(ftag), str(swid))
cli(makeCombi(showFtagSwid, fileName))
if __name__ == '__main__':
if(len(sys.argv) == 4):
fileName = sys.argv[1]
sleepInterval = int(sys.argv[2])
sleepCount = int(sys.argv[3])
printConsole('taking Rate logs')
writeRateLogs(fileName, sleepInterval, sleepCount)
printConsole('taking FP logs')
writeFPLogs(fileName)
printConsole('taking Rate logs')
writeRateLogs(fileName, sleepInterval, sleepCount)
else:
print('Syntax: python test.py FILENAME SLEEP_INTERVAL SLEEP_COUNT')
先に解説しておりませんが、if __name__ == '__main__' は呼び出し起点の場合のみ実行されるコードを記載いたします。
その内部にある sys.argv[X] はパラメータを取得するために利用しています。
コードの中にパラメータを埋め込むのではなく、コマンドで指定するほうが融通性が増します。
mac アドレスや ip アドレス、インタフェースの番号を指定するように設計してもよいかもしれません。
呼び出しコマンド
SWITCH(config)# python bootflash:test.py test.log 3 3
得られた出力ファイルの中身
SWITCH(config)# show file bootflash:test.log
14:25:30.473 UTC Tue Jan 21 2014
#show interface port-channel 1 | grep rate
30 seconds input rate 4889344 bits/sec, 1173 packets/sec
30 seconds output rate 1824 bits/sec, 0 packets/sec
input rate 4.89 Mbps, 1.17 Kpps; output rate 1.21 Kbps, 0 pps
14:25:30.556 UTC Tue Jan 21 2014
#show interface ethernet 1/1 | grep rate
30 seconds input rate 10006432 bits/sec, 2412 packets/sec
30 seconds output rate 10001184 bits/sec, 2404 packets/sec
input rate 10.01 Mbps, 2.41 Kpps; output rate 10.00 Mbps, 2.40 Kpps
14:25:33.628 UTC Tue Jan 21 2014
#show interface port-channel 1 | grep rate
30 seconds input rate 4889344 bits/sec, 1173 packets/sec
30 seconds output rate 1824 bits/sec, 0 packets/sec
input rate 4.89 Mbps, 1.17 Kpps; output rate 1.21 Kbps, 0 pps
14:25:33.709 UTC Tue Jan 21 2014
#show interface ethernet 1/1 | grep rate
30 seconds input rate 10006432 bits/sec, 2412 packets/sec
30 seconds output rate 10001184 bits/sec, 2404 packets/sec
input rate 10.01 Mbps, 2.41 Kpps; output rate 10.00 Mbps, 2.40 Kpps
<省略>
14:25:39.998 UTC Tue Jan 21 2014
#show fabricpath isis route
Fabricpath IS-IS domain: default MT-0
Topology 0, Tree 0, Swid routing table
<省略>
Python Script を使うことで日々の業務の負担を減らすことが可能です。
もしご興味があれば一度利用してみて頂くことを推奨いたします。
Nexus での Python の取り扱いに関しては以下のドキュメントが参考になります。
Cisco Nexus 7000 Series NX-OS Fundamentals Configuration Guide, Release 6.x
Python API
本当に役に立つドキュメントです、共有していただきありがとうございます。
検索バーにキーワード、フレーズ、または質問を入力し、お探しのものを見つけましょう
シスコ コミュニティをいち早く使いこなしていただけるよう役立つリンクをまとめました。みなさんのジャーニーがより良いものとなるようお手伝いします
下記より関連するコンテンツにアクセスできます