経緯
スイッチ機器の状態を取りたかったが、いちいちSSHしてコマンド打ってメモして・・・がすごいめんどくさかったので、自動化したかったのです。
んで、対話型コマンドを自動化するのであればexpectだよね、ってことで使ってみたのですが、ファイルを読み込んで1つずつ処理する、というものを実装するのに以外と時間がかかったのでメモです。
例のごとく備忘です。まただよ。
参考URL
Linuxの対話がめんどくさい?そんな時こそ自動化だ!-expect編-
やりたいこと
対象スイッチにSSH → Enable → show int **(任意のeth)
で、show interfaces **の部分をファイルに指定したethの数繰り返す
って感じにしたいです。
expectファイルの作成
前提として、bashでたたくのではなく、expectをたたくようにします。
というわけで、ファイルの一番上に書くおまじないは
#!/usr/bin/expect
です。
引数の指定
まずはファイルを読み込むために引数の指定です。
別に直接ファイルにパスを書いてもいいのですが、どうせなのであれば汎用性を高くしたかったのでコマンドラインから引数に指定します。
というわけで書き方は以下。
set HOST [lindex $argv 0]
set ETHFILE [lindex $argv 1]
1つ目はSSH対象のスイッチを引数に渡します。
2つ目はスイッチのshow interfaces **に食わせるための**の部分の記載があるリストファイルです。
↑のリストファイルのパスを渡してあげます。
コマンドのイメージは以下。
expect [expectスクリプト] [ホスト名] [リストファイルパス]
SSHするためのパスワードも渡すようにしようか迷ったのですが、どうせ一元的に管理しててサーバごとに違ったパスワードにはしていないので、expectスクリプトに平文で書いてあげることにしました。
セキュリティ?知らん!
SSHログインする部分
まずはSSHしないと何もできないので、SSHする部分を書きましょう。
set PW "password"
spawn env LANG=C /usr/bin/ssh admin@${HOST}
expect {
"Password:" {
send "${PW}\n"
}
}
こんな感じ。
adminユーザーでログインします。
HOSTは引数で渡したホスト、パスワードは平文「password」と書いている部分です。
spawnっていうのは、超簡単に言えば一番初めのコマンドですね。
というか私もよくわかってません。動けばいいんじゃい!
LANGとかのあたりはおまじないです。参考URL先を参考にどうぞ。
んで、SSHすると最初にパスワードを聞かれると思います。
その時に出力されるメッセージが「Password:」なので、この文字が表示されたら自動的にパスワードを入力するようにします。
今回はexpectディレクティブ以下でその作業をやっています。
書き方は見ればなんとなくわかるはず。ここは本題ではないのですっ飛ばします。
Enableする部分
expect {
"\>" {
send "enable\n"
}
}
expect {
"Password:" {
send "${PW}\n"
}
}
とはいえ、SSHの部分とほぼ同じなため飛ばします。
ファイルを読み込んで繰り返し処理
ここからが本題です。
2つ目の引数で指定したリストファイルを読み込んで、その行分繰り返し処理させます。
set fileID [open $ETHFILE]
while {! [eof $fileID]} {
set ethline [gets $fileID]
expect {
"\#" {
send "show interfaces ethernet ${ethline}\n"
}
}
}
上から見ていきましょう。
set fileID [open $ETHFILE]
まず、1行目の「set fileID [open $ETHFILE]」ですが、$ETHFILE変数に格納されているファイルを読み込んで、fileIDという変数に入れる感じ。
$ETHFILEはただのファイルパスが格納されている変数のため、ファイルの中身を読み込ませる必要があります。
それがこの部分です。
while {! [eof $fileID]} {
ここで繰り返し処理をするように書いています。
この場合、$fileIDを1行ずつ読み込んで、$fileIDのEOFまでディレクティブ内の処理をしまーすと書いています。
EOFとはファイルの終わりのことです。
set ethline [gets $fileID]
$fileIDの現在読み込んでいる行をethlineという変数に代入します。
これ、いちいち変数に入れなくてもコマンド打つ時に[gets $fileID]で値を呼び出せるんかな?
まあ、変数に入れたほうが可読性もいいし・・・ってことで入れています。
expect {
"\#" {
send "show interfaces ethernet ${ethline}\n"
}
}
繰り返したい処理の部分です。
↑で入れた$ethlineというものを使ってshow interfacesをしています。
ここの書き方は通常と同じなので割愛。普通に書けばよいです。
んで、後はディレクティブをすべて閉じて終わりです。
スクリプト内容
というわけでスクリプト全容です。
$cat checkInterface.exp
#!/usr/bin/expect
set PW "password"
set HOST [lindex $argv 0]
set ETHFILE [lindex $argv 1]
set timeout 10
spawn env LANG=C /usr/bin/ssh admin@${HOST}
expect {
"Password:" {
send "${PW}\n"
}
}
expect {
"\>" {
send "enable\n"
}
}
expect {
"Password:" {
send "${PW}\n"
}
}
set fileID [open $ETHFILE]
while {! [eof $fileID]} {
set ethline [gets $fileID]
expect {
"\#" {
send "show interfaces ethernet ${ethline}\n"
}
}
}
expect {
"\#" {
exit 0
}
}
理解できれば単純でした。
ちなみに、ethのリストファイルは以下な感じ。
$cat ethlist.txt
1
2
3
4
5
6
7
8
・・・いやこれseqで食わせればよくね・・・?
実際にコマンドで打つ時は以下。
$ expect checkInterface.exp [スイッチのホスト名] ethlist.txt
たんじゅーん
まとめ
というわけで、備忘ですがまとめました。
SSHで入って情報をとって・・・というのは現在はおそらくansibleが主流だとは思いますが、まだまだexpectも使えますね。
場面場面で使い分けるのがよさそうです。
個人的にansibleのPlaybookは読みにくいから嫌い
それでは!
コメント