本記事は NetOpsCoding Advent Calendar 2015、23日目の記事です。
はじめまして。とある通信会社でネットワークの運用業務をしています。クリスマス・イヴイヴに「ルーターとお喋りする」という悲しいお話をします。
はじめに
世間では NETCONF/YANG などが話題ですが、Cisco 7206VXR、Catalyst 2950 といった生きた化石 歴史ある通信機器が現役な現場では未だ(そしてこれからも) Telnet によるCLI制御が主役です。Perl、Python、Ruby などの主要なLL言語では Telnet 用ライブラリが用意されていますが、PHP にはありません。
Packagist に Telnetクライアントライブラリ があるものの、プロンプト等の違いによりルータ類にログインできないため、今回はこれを拡張し PHP から Telnet でルータにログインするライブラリを作ったのでご紹介します。
Telnet ライブラリについて
条件
- 実行環境要件 :
- PHP 5.6以上(PHP 7.0対応)
- Composer
- 対象ホスト :
- Linux/UNIXサーバー全般
- Cisco IOS, IOS-XE, IOS-XR ルーター・スイッチ
- Juniper JUNOS ルーター・スイッチ
- AlaxalA, HITACHI ルーター・スイッチ
開発/検証環境
- PHP実行環境
- CentOS 7.2
- Apache 2.4
- PHP 7.0
- テスト済み接続先
- RHEL 5
- RHEL 6
- CentOS 7
- Cisco 7206VXR (IOS 12.3 / 12.4)
- Cisco Catalyst 2950 / 2960 / 3560 / 3750 (IOS 12.2)
- Cisco 7604 / 7609 (IOS 12.2)
- Cisco ASR 1001 / 1002 / 1006 (IOS-XE 15.1)
- Cisco CRS-1 / CRS-3 (IOS-XR 4.0)
- Juniper T640 / T1600 / T4000 (JUNOS 12.3)
- Juniper E120 / E320 (JUNOS 12.3)
- Juniper MX240 (JUNOS 12.3)
- HITACHI GR4000 (ROUTE-OS 9)
- AlaxalA AX7800R (OS-R 10.10)
ソース
- GitHub : https://github.com/miyahan/Telnet
- Packagist : https://packagist.org/packages/miyahan/telnet
インストール方法
Composer を使いますので、予めインストールしておきます → https://getcomposer.org/
まず composer.json を作り、以下のようにライブラリを記述します。
{
"require": {
"miyahan/telnet": "dev-master"
}
}
composer.json を作成したら、composer install
してライブラリをインストールします。
$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev)
- Installing miyahan/telnet (dev-master e1003c2)
Cloning e1003c27e422772962d849187c31f8ace38e8753
Writing lock file
Generating autoload files
ライブラリのインストールと同時にオートローダーが作成されますので、以後これを require して使います。
使い方:Juniper JUNOS ルータにTelnetし情報を取得する
<?php
require 'vendor/autoload.php';
try {
$telnet = new \miyahan\network\Telnet('10.0.0.1');
$telnet->connect();
$telnet->login('root', 'hogehoge', 'junos');
$telnet->exec('set cli screen-length 0');
$telnet->setPrompt('root>');
$result = $telnet->exec('show system uptime');
echo $result;
} catch (Exception $e) {
echo $e->getMessage();
}
$ php telnet-juniper.php
show system uptime
scc-re0:
--------------------------------------------------------------------------
Current time: 2015-12-16 03:26:55 JST
System booted: 2011-06-21 14:00:44 JST (234w0d 13:26 ago)
Protocols started: 2011-06-21 15:04:17 JST (234w0d 12:22 ago)
Last configured: 2015-10-06 11:16:17 JST (10w0d 16:10 ago) by root
3:26AM up 1638 days, 13:26, 1 user, load averages: 0.04, 0.23, 0.21
lcc0-re0:
--------------------------------------------------------------------------
Current time: 2015-12-16 03:26:55 JST
System booted: 2011-06-21 14:01:09 JST (234w0d 13:25 ago)
Last configured: 2015-10-06 11:16:11 JST (10w0d 16:10 ago) by root
3:26AM up 1638 days, 13:26, 0 users, load averages: 0.00, 0.02, 0.06
/// snip ///
取得できました。結果を一度に返すように set cli screen-length 0
コマンドを発行しておくことがポイントです。あとは preg_match
などの文字操作関数を使ってよしなに情報を抽出します。
使い方:Cisco IOS ルータにTelnetし情報を取得する
<?php
require 'vendor/autoload.php';
try {
$telnet = new \miyahan\network\Telnet('10.0.0.2');
$telnet->connect();
$telnet->login('root', 'hogehoge', 'ios');
$telnet->exec('terminal length 0');
$telnet->exec('enable'."\r\n".'hogehoge');
$telnet->setPrompt('router#');
$result = $telnet->exec('show environment all');
echo $result;
} catch (Exception $e) {
echo $e->getMessage();
}
$ php telnet-cisco.php
show environment all
Power Supplies:
Power Supply 1 is Zytek DC Power Supply. Unit is on.
Power Supply 2 is Zytek DC Power Supply. Unit is on.
Temperature readings:
chassis inlet measured at 28C/82F
chassis outlet 1 measured at 29C/84F
chassis outlet 2 measured at 31C/87F
chassis outlet 3 measured at 32C/89F
Voltage readings:
+3.45 V measured at +3.50 V
+5.15 V measured at +5.27 V
+12.15 V measured at +12.34 V
-11.95 V measured at -11.95 V
Envm stats saved 74822 time(s) since reload
取得できました。こちらでも予め terminal length 0
コマンドを発行しておくことがポイントです。
トラブルシュート
WebからアクセスするとTelnet接続に失敗する
SELinux が有効だとTelnet接続に失敗してしまいます。
$ curl http://localhost/telnet.php | more
Cannot connect to 10.0.0.1 on port 23
$ sudo tail /var/log/audit/audit.log
type=AVC msg=audit(1450217222.842:111): avc: denied { name_connect } for pid=2553 comm="httpd" dest=23 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:telnetd_port_t:s0 tclass=tcp_socket
この場合は SELinux を無効化するか、下記コマンドで httpd からのTCPソケット接続を許可してください。
$ sudo setsebool -P httpd_can_network_connect 1
遅いコマンドがタイムアウトしてしまう
デフォルトではプロンプトが返って来るまで10秒以上かかるとタイムアウトが発生してしまいます。
$ php telnet.php
Couldn't find the requested : 'router#' within 10 seconds
show tech-support
など、応答に時間がかかる処理を実行する場合は、コンストラクタの第4引数にタイムアウト時間(秒)を長めに指定してください。
$telnet = new \miyahan\network\Telnet('10.10.10.10', 23, 10, 300.0);
おわりに
PHP から Telnet するという誰得な内容でしたが、私の会社では PHP で書かれたWebアプリからルーターやスイッチにアクセスし、コマンドの応答結果を整形して表示するといった見える化系ツールで活躍しています。その事例も今後紹介できればなと思っています。
よろしければ使ってみてください。プルリクもお待ちしています。