#スクリプト言語でtelnet使うときの(略) (NetOpsCoding Advent Calendar 2015)
@s1061123と申します。
本記事はNetOpsCoding Advent Calendar 2015の24日目の記事となります。メリークリスマスイブ!!
昨日は@miyahanさんのPHPでもTelnetがしたい!でした。
さて本日は先日お話しした"スクリプト言語でtelnet使うときの落とし穴"をふまえた形でちょっとした話をしてみようと思います。
前回のお話ではコマンドをスクリプト言語で実行する話をしました。ルータのちょっとした情報(インターフェースのUp/Down等)を得るためにコマンド1つ打つことをスクリプト言語で実行するというのもありますが、たとえば設備メンテナンス的な形で複数のコマンドをまとめてスクリプト言語等で自動化するケースというのもあります。
本日はそういった複数のコマンドを自動化する際のお話です。
##スクリプトによる処理の自動化の一例
例えば、ネットワーク装置の情報を収集するために以下のようなコマンドの出力結果を、複数装置(例えば5台)から入手することを考えてみましょう。
(注:ここでは特定ベンダーの話ではないので適当に僕が想像した嘘showコマンドで書いてます)
show interface (想定完了時間1分)
show hyper-tech-support (想定完了時間20分)
show running-config (コンフィグが長いため、完了時間は10分を想定)
これを、そのままスクリプトにしてみましょう。ここではPythonっぽい嘘コードでちょっと書いてみようと思います。
for i in [rtr1, rtr2, rtr3, rtr4, rtr5]:
i = <ルータiにTelnetでログイン>
i.exec_command("show interface")
i.exec_command("show hyper-long-tech-support")
i.exec_command("show running-config")
<ルータiからログアウト>
このスクリプトの場合、一台あたり1+20+10=31分、5台で155分(=2.5時間)かかることになります。映画一本分です。スター・ウォーズ/フォースの覚醒は136分なので、見終わってコーヒーを飲むことだって可能です。もっと短かくならないでしょうか。
Threadによる並列化
まずスレッドを使うことで各ルータの実行を並列にすることができます。Python,Ruby等最近のスクリプト言語では通常Thread機構は存在しますので、それをそのまま利用することが可能です。
もし可能ならば同時並列実行を調整できるようにスレッドプールやワーク・キュー(参考: Javaの理論と実践: スレッド・プールとワーク・キュー)を利用すると便利です。
さて、スレッドを使うと上の嘘コードは以下のようになります。
def get_show_commands(name)
i = <ルータnameにTelnetでログイン>
i.exec_command("show interface")
i.exec_command("show hyper-long-tech-support")
i.exec_command("show running-config")
<ルータiからログアウト>
for i in [rtr1, rtr2, rtr3, rtr4, rtr5]:
t = Thread(target=i, args=(i))
これをやることで並列に取得することで、5台分の情報が1台分の時間(31分)で可能になりました。
さて、最適化としてはこれでも十分ですが更に最適化することは可能でしょうか?
ちょっと強引とも言えますが、もう一つ高速化できる事が可能です。
更なる並列化
先ほどの3つの'show ...'コマンドですが、'show'コマンドは情報収集のコマンドであるので、装置に副作用を与えない可能性があります。もし、実際にその場合、つまり各コマンド毎に独立したコマンドで依存関係が無い場合、3つのスクリプトを同時に平行で走らせることで更に最適化が可能です。
つまり、上のスクリプトでは1ルータで1スレッドを割り当てていたのを、1コマンドに1スレッドを割り合てるといった形にします。
スクリプト的にはこんな感じになります。
def get_show_commands(name, cmd)
i = <ルータnameにTelnetでログイン>
i.exec_command(cmd)
<ルータiからログアウト>
for i in [rtr1, rtr2, rtr3, rtr4, rtr5]:
for c in ["show interface", "show hyper-long-tech-support", "show running-config"]:
t = Thread(target=i, args=(i, c))
こうすることにより、5台分の情報が1コマンド分の時間(20分)で可能になります。ですが、この場合1ルータへの負荷も上がってしまうので、それぞれの環境でそういった最適化が本当にシステム的に納得できるかも考える必要があります。また、コマンド毎にtelnetをするのでルータに設定されている最大telnetセッション数にも注意する必要があります。まさに諸刃の剣ってやつですね。注意して使いましょう。
ということで本日は"スクリプト言語でtelnetを使う際の高速化とその副作用"についてお話しました。
ちなみにこれを使うとかなり並列化することが可能ですが、30台を越えるルータでの場合に躓いてしまいました。どこに躓いてしまったのか、どう解決したのかについてはまた別の機会にでもお話できたらと思います(スマートな解決ではないことだけは告白しておきますww)。
明日はついに最終日ですね。最終日は@junpei-yoshinoさんの"pull requestベースの設定変更"です。設定変更履歴って案外忘れがちなのですが、結構なタイミングで重要な意味を持つケースがあるのでgitを使っての変更の話は楽しみです。