CLIベースのNW自動化バッドノウハウのあれこれ

はじめに

ネットワークプログラマビリティ勉強会 #1 に行って知人と話をしたところ、みんな同じような落とし穴にはまっててあああああってなってるし、そういう界隈に来る人なら一度は何かしら似たようなことをやっているんじゃないか? というあたりがきっかけ。SDN だ NFV だ Infrastructure as a Code だみたいな話もいろんなところで聞かれるようになった気がしますが、そもそもこういう話が出る以前って、ネットワークの管理や自動化というと何をどうしてたんだっけ……という話を振り返ってみてもいいんじゃないかなと思ってきました。

最近、SDN Layers and Architecture Terminology を読んでみた 結果をまとめて書いたり、前述のような勉強会行ってみたり、直後に 知ったかぶりしない NETCONF - LGTM という記事が回ってきたり、いくつかフラグが重なった感*1があるので、勢いでまとめてみます。

[2014-10-29 補足をかきました]

ネットワークの管理

複数ベンダ・複数製品が混在するヘテロな環境で運用管理・オペレーション・自動化……を考えるときに、機器ごとに異なる処理を吸収・隠蔽して、統一的な操作ができる抽象化レイヤを作ってやろうよ、というのは割とみんな前からやってるはず。

その抽象化レイヤの中身の処理として、すべてを統一したインタフェース・統一したデータモデルで操作するというのが非常に難しいので、やっぱりどうしても CLI に頼らざるを得ないという話があると思います。SNMP でやれる操作には制約や限界がどうしてもあるし(ルーティングプロトコルの設定や機器状態の把握などの詳細な操作が何でもかんでも SNMP でやれるわけでもない)。人がやる作業を元にオペレーションを組み立てて自動化をやる…という方向性だと、人がやる操作 → CLI ベースオペレーションというのになってしまう、という話もあるかな。

さて。

CLI 操作の自動化ってなにをどうするのか、というと、対話処理の自動化をやるツールを使って、人の代わりにプログラム(プロセス)が応答する処理を記述することになります。有名どころは Expect ですが、現在は元々の tcl な Expect というよりはそれと同等の実装を使うことになるでしょう。個人的には過去こういうのを書いています。

まあほかにもツール探すといろいろ出てくるんですが。ちょっと探すと、こういう素の操作をやると言うよりは、もうちょっと特定の製品とか向けに(それこそCisco機器向けに、とか)機能を追加したライブラリとかがいろいろ見つかると思います。

CLI自動化の落とし穴

この手の処理を作ったことのあるほとんどの人が、これから列挙していくようなところではまったことがあると思います*2。そういうバッドノウハウ系の話をいつまでもみんなで繰り返すのって不毛だよね。まだ CLI ベース自動化に頼らざるを得ないところはあるだろうけど、これからやる人のために、こういうところがつらいから気をつけてね、というのをまとめます。

あまりこういう処理をやったことがない人は、これを見て、「あ、これだめなやつや」と思ってもらえればいいです。そして、NETCONF なり SDN 関連の話題でマネジメントプレーンのプロトコルや新しい標準、それを使った自動化、という話がなぜいろいろ出ているのか、理解の助けになればいいなあと思います。

正規表現マッチの調整の煩雑さ

Expect は、会話対象のプロセス(NW機器)が返してくる文字列を比較(正規表現マッチ)して、応答を返すべきかどうかを判断します。すなわち、人間側が入力を入れるべきところがどこなのか、をあらかじめ知っておく必要があります。通常は何かしらの「プロンプト」です。

  • プロンプトの種類が多い上にデバイスやOSバージョンや特定のコマンドごとにまちまち。
    • ログインプロンプト(username, password)
    • コマンドプロンプト(しかも configuration mode ごとにプロンプトが変化する)
    • Yes/No の応答(これも yes/no だったり y/n だったり ok? (y/n) だったりモノにより確認プロンプトがまちまち)
    • ホスト名などの考慮
      • 通常のコマンドプロンプトは "ホスト名+モード" とかだったりするので、その辺考慮しつつプロンプトマッチ正規表現を組み立てる必要があったり。

手順ベースなので順序を知っておかなければいけない。

  • save したら yes/no が出るのかでないのかで処理実装を変えないといけない。その前の操作履歴で保存していなければ出るし、保存済みなら出ないし、とか、機材によってはそもそも聞かないものがいたりする。
    • コマンドごとに応答待ちプロンプト処理とかが必要
  • たとえば Cisco IOS の拡張 ping みたいに複数のプロンプトを順に出してくるようなものは、特定コマンドに対するサブプロンプトマッチ、しかも多段のものが処理できないといけない。
core#ping
Protocol [ip]:
Target IP address: 192.168.0.1
Repeat count [5]: 20
Datagram size [100]:
Timeout in seconds [2]:
Extended commands [n]:
Sweep range of sizes [n]:
Type escape sequence to abort.
Sending 20, 100-byte ICMP Echos to 192.168.0.1, timeout is 2 seconds:
!!!!!!!!!!!!!!!!!!!!
Success rate is 100 percent (20/20), round-trip min/avg/max = 1/2/9 ms
core#

エラーの把握、エラーメッセージ処理

  • コマンドを入力したらエラーが返ることがあるわけですが、そのエラーすらもコマンド実行時の出力を見て正規表現マッチかけて把握する必要があります。
    • 機器やOSバージョンごとにエラーメッセージやフォーマットが違う。実際同じ製品ラインでもバージョン違うとエラーメッセージやメッセージフォーマットが変わってるとかあるからねえ…。

予期しないプロンプトマッチ

  • show config とかやったら、コンフィグ中の特定の文字列がプロンプト正規表現にマッチしてしまって、プロンプトが出ていないところで次のコマンドを送ってしまう。
    • ログインバナーや各設定の description など、任意の文字列が設定できるところに #> などのプロンプト固有の文字を使っていて予期せぬプロンプトマッチおきるとか…
    • 処理シーケンスがずれてしまう…
  • 問題はコンフィグ中に直接プロンプト文字列が指定されている(正規のプロンプト文字列が、コマンドの出力に含まれる)ような例や、特定のログ中にコマンドプロンプト相当の文字列が含まれているようなケース。
    • show log みたいなコマンドでログを表示させたりしたときに、過去ログの中に怪しい文字列が含まれていて止まる…とかは、なかなかテストでも見つからないです…。
    • show techshow log みたいに、どういう出力が返るか読めないコマンドの処理は非常に難しいです。
  • 仕方がないので特定コマンドでプロンプトマッチを一時的に変更するとかスルーするような処理を実装したり…

文字コードエスケープシーケンスの処理

  • 最近あまり見かけないけど、エスケープシーケンスを使って文字に色をつける、プロンプト位置を固定する、等の処理を行う機材では、expect 系プロセスが拾った文字列に(人間可読な文字としては見えない)エスケープシーケンスが挟まっており、正規表現マッチにひっかからないことがあります。
    • 事前に取得した文字からエスケープシーケンスを消すためのクリンナップ処理を入れたり…
  • 改行コード問題
  • 自動化すると、パラメータだけ別に(人が)入力するようなことがあるわけですが、まあ中には全角文字列とかいれるひともいてですね…
処理の成否判定の難しさ・順序依存性とエラー対応の難しさ

まず、通信自体の正常性の判断ですよ。telnet なり ssh なりでリモートログインした上でコマンドを送って応答をもらって、というのをやっているのだけど、どこで何が起きているのかが非常につかみにくい。

  • 相手 (NW 機器) は、こちらが送ったコマンドを正しく受信できているのか? (トランスポートの問題)
  • コマンドは送受信できているけど、コマンド自体に間違いがあるとかパラメータがおかしいとかの問題があるのか? (アプリケーションやオペレーションとしての問題)

ということすら expect プロセスの中では判断しにくい。NETCONF みたいな Transport protocol だとその辺はフォローされるし、REST API ならエラーコードとかですぐわかるんだけど、リモートログイン + CLI な自動化だと実装者側で頑張らないといけない。

  • そもそも CLI オペレーションに対しての成否、終了ステータスとかの考え方がないので、設定 → エラーメッセージなどが表示されていないことの確認 → show コマンドを実行して、望んだ状態になっているかどうかの確認、というのをワンセットで入れていかなければいけない。
  • それも各機器・OS・バージョン・オペレーションごとに(以下略

次に。上の正規表現処理の煩雑さ、という話ともかぶるんですが…。結局、show コマンド系とか状態確認しようとした場合とか、コマンドに対して出力された文字列*3をどうにかする処理なんですよね。それでこれ、すべては文字列なんですよ。したがって、得られた情報(=文字列)に対する意味づけを、実装者側が正規表現マッチですべてパースすることになる。

  • コマンドの出力自体はもうフリーフォーマットなので、ベンダやOSやバージョンによる違いがいろいろある。あるいは Cisco であれば、exec prompt timestamp とか入れておくと、コマンド入力に対して常に時刻表示してくれるという機能があるんですが、そういう機能の有無が不統一だったりすると、予期せぬ文字列マッチを招いて処理がうまくいかなくなったりする。
  • 返ってきた「文字列」をみて、状態が正しいのか、エラーが起きてるのか、必要な値はどの行のどのフィールドなのか、そもそもフィールド区切りはどう判断するのか……を、自力でどうにかしないといけない。

もうひとつ。NW 機器の設定って依存関係があるので、決まった順序が必要になったりします。

  • 一連のコマンドシーケンスを入力する間でエラーが出た・何かしらの失敗やトラブルが発生したとしたらどうすべき?
  • 処理を取り消そうにも、そこまで入力したコマンドはすでに反映されちゃってる。取り消そうと思うと、いったん消した上で前のコマンドを再入力しなければいけないけど…単純な上書きでやり直せないし、前のコマンドの再入力ってどうやってやるんだ? というのが問題になる。
    • Juniper(JUNOS) みたいに candidate config を持っていて commit/rollback できるとこうしたエラー処理は非常に楽になるんだけどね。(NETCONF は candidate config capability があります。)
    • データベースみたいにトランザクション処理みたいなことがやれるかというとそんなことないわけで、しかも周辺の状況に応じて動的に状態が変化していくし…。イベントに対応して何かをやる、というのも、実際どういう情報を集めてどう判断するのかとか考え始めるとすごく難しい。
タイムアウト処理の難しさ

期待している応答を拾えなかった、何かしらの形で処理が中断してしまった、等の場合に、リモートセッション張りっぱなしになってしまうと困るので、タイムアウト処理を入れます。が、これも往々にして機材や処理個別のチューニングが必要になり、非常に煩雑です。

  • --More-- とか pager 制御をオフにするのを忘れていて中断してはまる、とか。
    • ターミナル横幅を超えた出力を削るか改行して送ってくるような機材もあった気がする。
    • pager 系はセッション開始時に全部オフにしておく等の処理が必要。
  • コマンドごとの実行時間の長さの吸収
    • 長めの ping の実行ってどうする? traceroute したらホップ数が多くて取得に時間がかかる? show tech したら出力まだ終わってないのにタイムアウトしちゃった…とか。
    • 結局これも、コマンドごとにタイムアウト設定ができるように実装する必要が出たりする。

CLIベース自動化のつらさ

結局、各機器に対するオペレーションすべてについて、全部(に近いほとんど)の入出力とその応答がどういう「文字列」として返ってくるかがわかってないと、自動化しにくいんですよね。ベンダ、機器、OS、バージョンによっても違いがある。自動化処理として実装するもの(コード)の量は、そうした構成要素のパターン数と、各機器に対してやりたい操作とのかけ算で増えていくので、もう事前情報調査もテストも膨大なパターン必要になる。まあ、全部が全部ばらばらなわけじゃないし、共通する操作はある程度作っておいて…というのができないわけじゃないのだけど、傾向としては圧倒的にパターンが多く再利用性が低いんだよね。

しかもこれ、「特定のデバイスに対して、デバイスが返す応答に従って特定のコマンドを送る」を自動化する…という基本的なレベルのオペレーションに対してこういう問題が出てくるんですよ。じゃ、複数の製品が連動するオーケストレーションになるとどうなるの、という話ですね。そうなるとつぎは、排他制御ってどうする…とかそういう話が出てくるんですよね。特定の機器の設定をいじっている間は、ほかのプロセスからの処理を受けてほしくないのです。が、NW機器側でそうした機能を持ってくれているかというと一般的にはないので、オーケストレータ側がどうにかしなければいけない。(NETCONF だと config の lock/unlock の仕掛けが標準の中に含まれているはず。)

やったことがある人だと多分感じたことがあると思うんですが、やればやるほど「俺がやりたかったことってコレジャナイ」感がすごいです。もう全然本題と関係ないところで行き詰まっちゃうし時間食いつぶしちゃうし。やりたかったのは「オペレーションを自動化すること」であって謎の正規表現チューニングやらなんやらは本題ではないわけです。

おわりに

まだほかにも聞いたような気がするけど思い出せない…。まあいいや。

CLI ベースの処理でネットワークを自動化する (複数の NW 機器に対する処理を自動化する) のはかなりやっかいである、という話でした。まだ CLI ベースの話をせざるを得ないところもあると思うのですが、そういう方にはこういうノウハウを共通しつつ、無駄な時間をつぶすリスクを下げてもらえるとよいかな…と。こういう話をやったことがない人には、なぜ SDN 界隈でインタフェースの統一やマネジメントプロトコルの提案などが行われているのか、という理由の一端を垣間見てもらえるとよいかなと思います。

*1:NETCONF の何がこの辺の話題と絡むのか…は SDN Layers and Architecture Terminology 読んでみてくださいせっかく書いたので。

*2:少なくとも勉強会帰りの雑談ではそうでした

*3:Expect だと プロンプト → コマンド送信 → 出力される文字列を記録して、次のプロンプトが出たら待機となるので、プロンプトからプロンプトまでに帰ってきた文字列に対して、必要な情報を拾っていく処理になる