[Python] pexpectを改めて勉強してみる

RTXのコンフィグもバックアップを取りたい

現在、Netmikoを使ったPythonコードで以下の機器の設定を定期的にバックアップしています。

  • コアL3スイッチ(Cisco)
  • IHANet用ルーター(Cisco)
  • 外接L3スイッチ(H3C)
  • コアL2スイッチ(Juniper)

で、これに加えて前々からバックアップを取りたかったのがYAMAHAのVPNルータ。
自宅と災対拠点にそれぞれ1台ずついます。

NetmikoやNAPALMでは対応しておらず、取れうる手段としては以下のものが考えられました。

  • AnsibleのRAWモジュールでコマンドを実行して取得
  • Python+pexpectでコマンドを実行して取得

当初、前者のAnsibleパターンを狙い、プレイブックを作成して動かしてみたのですが、なんとまぁYAMAHA RTXのSSHって、exec(sshコマンドでコマンド実行をさせるもの)がサポートされてないんですね。こんなメッセージが出力されてしまいました。

Received disconnect from XXX.XXX.XXX.XXX : 2: Channel request 'exec' is not supported.

そこで、後者のパターンでコンフィグ取得処理を書くことになりました。
かなり力技なので、コードを公開する気はないんですが、この時を通じて久々にPython触ったのと、pexpectちゃんと調べたのと、それを忘れないようにするために記事としてUPしました。

pexpectの大まかな使い方

pexpectの実装方法については、過去の記事に書いているのでそこを参照いただければと思います。
その時ともあまり変わらないんですが、だいたい使う命令群は以下のような感じでした。

最初は「pexpect.spawn」で接続を行います。例えばこんな感じ。IPアドレスを「ipaddr」と言う変数で、引数等から入手し、「telnet 」と言う文字列と結合させることで、TELNET接続を試みる感じになります。

child = pexpect.spawn("telnet " + ipaddr)

で、その結果をオブジェクトに引き渡すことで、そのオブジェクトに紐付く命令を実行できます。基本的な使い方としては、「expect」で期待する結果が来るまで待ち受け、「sendline」で命令を投げるという感じになります。

 child.expect("[A-Z]\#")
 child.sendline('exit')
 child.expect('>')
 child.sendline('exit')

上記の場合だと、

  • 「英大文字」+「#」の文字列応答が帰ってきたら次へ進む
  • 「exit」と言う文字列を送信する
  • 「>」という文字列応答が帰ってきたら次へ進む
  • 「exit」という文字列を送信する
  • 結果、セッションが切れる。めでたしめでたし

と言う感じになります。
なお、今回YAMAHAのルーターではコンフィグ取得を行った場合に、先頭にコメントが付与されるため、単純に「#」だけを待ち受けてしまうと、コンフィグ出力一行目で処理が先に進んでしまうため、『「英大文字」+「#」』と言う条件を設定しています。

結果の取得

コンフィグバックアップ処理の流れは、簡単に書くとこんな感じです。

  • TELNETでセッションを開く
  • administratorモードへ移行
  • 「show config」を叩く
  • 結果を取得し、コンフィグファイルとして吐き出す
  • セッションをクローズする

この時、コンフィグの取得を行うには「before」を使用します。これは「expect」でキャッチする直前の出力を取り出す処理です。childオブジェクトに対してこの命令を使うことで、「show config」の実行結果が取得できるというわけでして。

 cf_file = open(conf_file,'wb')
 child.sendline('show config')
 child.expect("[A-Z]\#")
 cf_file.write(child.before)
 cf_file.close()

注意点としては、上記で取得される「child.before」の中身はバイナリデータになっていますので、ファイルオープンする際は「w(文字列書き込み)」ではなく「wb(バイナリ書き込み)」とする必要があります。

これでめでたくなんとかコンフィグ取得ができるようになりました。よかった。
あとはRouterboardぐらいかな。取得が必要なアプライアンスは。

意外とYAMAHAルーターって扱いづらい

私が産まれて初めて触ったルーターは、YAMAHA RTX-1000でした。それ故、コマンドラインなどの扱いに関してはYAMAHAがとっつきやすかった・・・・はずなんですが、ここ最近は逆に扱いにくく感じています。経路の再配布周りの処理が書きづらいのと、今回の対応でも以下の点が難しいなぁと感じました。

  • 文字コードがSJIS。メッセージが基本的に日本語。
  • TELNETログインする際のログインプロンプトとadministratorモード移行時のログインプロンプトが同一文字列

今回この処理はあくまで個人向けに作ったので、エラーハンドリング等も考慮していませんが、応答文字列によってエラーハンドリングしたいと考えた時、文字コードSJIS、しかもメッセージが日本語というのは中々につらいです。

SJIS⇒UTF8に変換して、その上で日本語ベースの突合をするのかなぁ?とか。
とは言え、んまぁちゃんとコンフィグは取れるようになりましたので、良いのかなーとおもったりもするのですが、ちゃんと仕事でこの手のことをやってる人は対策取れてるのだろうなぁーともおもったり。単に私が未熟なだけなんだろうなぁともおもったりします。
ただ・・うーん、スクリプト運用等をする際、このあたりをもしご存じないようなら、RTX設定の自動化とかはあまり安請け合いしないほうが良いのかもしれません・・・

コツコツ情報見つけながら、引き続き精進しますです。