老害のためのネットワーク自動化入門

# ネットワーク機器のCLIって最強のAPIだと思うんですｗ

今使ってるネットワーク機器がRESTful API対応してなくても、NetConfに対応してなくても、ナウいSDKとかライブラリとかメーカさんから提供されていなくても、

**CLIがあれば全然自動化できちゃいます！**

今回は、PerlのNet::Telnetモジュールを用いて、L2スイッチの自動設定を試してみようと思います。

**私のようにPerlしか書けない老害でも全然自動化できちゃいます！**

# Perl Net::Telnetモジュールとは？

Telnetを用いてネットワーク機器の対話型インターフェイス(CLI)を人間的に操作するのと同様、Perlスクリプトから機械的に同様の操作を実現することができます。

CPANのページが以下にあります。
http://search.cpan.org/~jrogers/Net-Telnet-3.04/lib/Net/Telnet.pm

Linux等で[expectコマンドを使って自動化されている方](http://qiita.com/ine1127/items/cd6bc91174635016db9b)も多いと思いますが、それと似たような感覚です。Net::TelnetはTelnetに特化した機能を備えています。

# Perl Net::Telnetモジュールのインストール

CentOS等であれば、**コマンド一発**で入ります。

`$ yum install -y perl-Net-Telnet`

インストール後、以下コマンドでマニュアルを表示できます。

`$ perldoc Net::Telnet`

# 例1. ネットワーク機器の設定を取得する

まず簡単な例として、ネットワーク機器に自動ログインしてrunning-configを取得してみます。ここでは試しにApresia15000を使ってます。

![apresia.png](https://qiita-image-store.s3.amazonaws.com/0/108990/4647d702-f147-9ff3-a5db-7422ce9ff21a.png)

このスクリプトを1日1回とか実行してgitに登録すれば、**設定の履歴管理ができちゃいますね！！**

```pl
#!/usr/bin/env perl

use Net::Telnet;

$remote = shift || die;
$telnet = new Net::Telnet(
    Timeout => 60,
    Input_log => 'telnet.log',
);

$telnet->open($remote) or die;

$telnet->waitfor('/login: /');
$telnet->print('adpro');

$telnet->waitfor('/Password:/');
$telnet->print('password');

$telnet->waitfor('/> /');

$telnet->print('enable');
$telnet->waitfor('/# /');

$telnet->print('conf term');
$telnet->waitfor('/# /');

$telnet->print('terminal length 0');
$telnet->waitfor('/# /');

$telnet->print('end');
$telnet->waitfor('/# /');

$telnet->print('show running-config');
($out) = $telnet->waitfor('/# /');
print $out;

$telnet->print('exit');

exit;
```

Apresiaちゃんは、configureモードに移行しないとページャをオフにできないので、ちょっとややこしいですね(terminal length 0 の部分)。

このスクリプトを実行すると、標準出力に設定が表示されると思います。

```
$ ./ap15k-telnet.pl 192.168.9.82
show running-config
!
! Configuration for AEOS 8.19.03
! Date: Thu Mar 03 15:01:44 2016
! Hardware Model: Apresia15000-32XL-PSR
!
hostname TEST-AP15K-F16
!
username adpro adpro
username user user
<snip>
```

# 例2. インターフェイスのVLAN設定をクリアする

次に、複数ポートに対してVLANの設定を削除しシャットダウンするまでを自動化してみます。**ある検証が終わって、設定をクリアするのに便利ですｗ**

以下のような感じです。

```pl
#!/usr/bin/env perl

use Net::Telnet;

$remote = shift || die;
$telnet = new Net::Telnet(
    Timeout => 60,
    Input_log => 'telnet.log',
);

$telnet->open($remote) or die;

$telnet->waitfor('/login: /');
$telnet->print('adpro');

$telnet->waitfor('/Password:/');
$telnet->print('password');

$telnet->waitfor('/> /');

$telnet->print('enable');
$telnet->waitfor('/# /');

$telnet->print('conf term');
$telnet->waitfor('/# /');

foreach $port (5..8) {
    $telnet->print("interface port 1/$port");
    $telnet->waitfor('/# /');

    $telnet->print("switchport mode trunk");
    $telnet->waitfor('/# /');

    $telnet->print("switchport trunk remove");
    $telnet->waitfor('/# /');

    $telnet->print("shutdown");
    $telnet->waitfor('/# /');
}

$telnet->print('end');
$telnet->waitfor('/# /');

$telnet->print('write mem');
$telnet->waitfor('/# /');

$telnet->print('exit');

exit;
```

以下のように実行します。

`$ ./ap15k-telnet.pl 192.168.9.82`

別ターミナルでtailコマンドでログを追えば進捗がわかります。

```
$ tail -F telnet.log
Ethernet Switch Apresia15000-32XL-PSR

login: adpro
Password:
No entry for terminal type "network";
using dumb terminal settings.
No entry for terminal type "network";
using dumb terminal settings.
TEST-AP15K-F16> enable
TEST-AP15K-F16# conf term
Enter configuration commands, one per line.  End with CNTL/Z.
TEST-AP15K-F16(config)# interface port 1/5
TEST-AP15K-F16(config-if-port)# switchport mode trunk
TEST-AP15K-F16(config-if-port)# switchport trunk remove
TEST-AP15K-F16(config-if-port)# shutdown
<snip>
TEST-AP15K-F16(config-if-port)# interface port 1/8
TEST-AP15K-F16(config-if-port)# switchport mode trunk
TEST-AP15K-F16(config-if-port)# switchport trunk remove
TEST-AP15K-F16(config-if-port)# shutdown
TEST-AP15K-F16(config-if-port)# end
TEST-AP15K-F16# write mem
Current running-config is saved to flash-config.
Writing to flash memory...
[OK]
Writing to SD memory...
ERROR: SD Memory card mount error

TEST-AP15K-F16#
```

とても便利ですね。ただし、**間違えて本番機に適用するとギャー！！**となりますのでご注意ください！！

注意事項：例示したコードはわかりやすさ優先のために、冗長だったりエラー処理がなかったりします。本番環境では、適宜追加してお使いください。