ネットワーク構成図の自動作成を目指す！（3）

　Fate/StayNight Heaven's Feel見てきました。桜可愛いよ、桜。
　その話はさておき、引き続きその3に話を進めていきたいと思います。この章では、前回MySQLに得られた情報をもとに、VLAN配下のホストの検出、ホスト名の取得、MySQLへの格納をどのように実装していくかを実験することを記載していきたいと思います。

# この章で書きたいこと

- VLAN配下の全IPアドレスに対してPingを打ち、応答のあるIPアドレスを検索する
- 応答のあるIPアドレスに対し、snmpwalkでホスト情報を得る
- 上記2つの情報をMySQLに格納する

# 前回までのあらすじ

　前回まででMySQLにCiscoスイッチのconfigから必要な情報をMySQLに格納しました。
　今考えたら、VLANとNW_ADDRESSは別テーブルにした方が使いやすかったですね。。

```
mysql> select * from config_data;
+-----------------+-------------------------------+-----------------+----------------------+------------------+
| IP_ADDRESS      | HOST                          | NETMASK         | VLAN                 | NW_ADDRESS       |
+-----------------+-------------------------------+-----------------+----------------------+------------------+
| 10.240.1.1      | tky-rt-01                     | 255.255.255.128 | GigabitEthernet9.901 | 10.240.1.0/25    |
| 10.1.1.252      | tky-rt-01                     | 255.255.255.0   | Vlan1                | 10.1.1.0/24      |
| 10.1.2.252      | tky-rt-01                     | 255.255.255.0   | Vlan2                | 10.1.2.0/24      |
| 10.1.3.252      | tky-rt-01                     | 255.255.255.0   | Vlan3                | 10.1.3.0/24      |
| 10.1.4.252      | tky-rt-01                     | 255.255.255.0   | Vlan4                | 10.1.4.0/24      |
+-----------------+-------------------------------+-----------------+----------------------+------------------+
5 rows in set (0.00 sec)
``` 

#実験
　今回は、慣れているPerlで実装します。Pythonに慣れてきたら、Pythonで再度書き直そうかと思います。

##実験①Pingスキャン
###Perlモジュール
　まずは、必要なPerlモジュールをインストールします。
　今回は、Pingスキャン用にNet::Ping::External、NWアドレスから対象のIPアドレスに変換するモジュールにNet::Netmask、SNMP用にNet::SNMP、以上3つをマシンにCPANを使ってインストールします。

```
cpan> install Net::Ping::External
cpan> install Net::Netmask
cpan> install Net::SNMP
```

###VLANから配下のIP抽出
　参考にしたのは、このページになります。
　http://goodnext.hatenadiary.jp/entry/2017/04/26/142437

　普通に$block->enumerateとするだけでは、ネットワークアドレスとブロードキャストアドレスが配列に混じってしまうので、shift関数で最初の値、pop関数で最後の値を削除するようにして、有効なIPアドレスのみを配列に格納しました。

```extract_iplist.pl
#!/usr/bin/perl

use strict;

use DBI;
use Net::Ping::External qw(ping);
use Net::Netmask;
use Net::SNMP;

my $vlan=$ARGV[0];

my $dsn = "DBI:mysql:database=nw_config;host=127.0.0.1;port=3306";
my $dbh = DBI->connect($dsn, "*****", "**********") || die("[ERROR] DB Connect Error");
my $sth = $dbh->prepare("select NW_ADDRESS from config_data where VLAN='$vlan'") || die("[ERROR] SQL statement is wrong.");
$sth->execute() || die("[ERROR] SQL cannot be executed.");

while( my $ary_ref = $sth->fetchrow_arrayref){
        my @target=&ip_list($ary_ref->[0]);
        for my $ip (@target){
        print "$ip\n";
        }
}

$dbh->disconnect;

sub ip_list{
        my @list=();
        my $block = new Net::Netmask ($_[0]);
        for my $IP ($block->enumerate)  {
                push(@list, $IP);
        }
        shift @list;
        pop @list;
        return @list;
}
```

```
# ./extract_iplist.pl Vlan1
10.1.1.1
10.1.1.2
10.1.1.3
10.1.1.4
10.1.1.5
10.1.1.6
10.1.1.7
10.1.1.8
10.1.1.9
10.1.1.10
10.1.1.11
10.1.1.12
10.1.1.13
10.1.1.14
10.1.1.15
10.1.1.16
10.1.1.17
10.1.1.18
~省略~
10.1.1.243
10.1.1.244
10.1.1.245
10.1.1.246
10.1.1.247
10.1.1.248
10.1.1.249
10.1.1.250
10.1.1.251
10.1.1.252
10.1.1.253
10.1.1.254

# ./extract_iplist.pl Vlan2
10.1.2.1
10.1.2.2
10.1.2.3
10.1.2.4
10.1.2.5
10.1.2.6
10.1.2.7
10.1.2.8
10.1.2.9
10.1.2.10
10.1.2.11
10.1.2.12
10.1.2.13
10.1.2.14
10.1.2.15
10.1.2.16
10.1.2.17
~省略~
10.1.2.242
10.1.2.243
10.1.2.244
10.1.2.245
10.1.2.246
10.1.2.247
10.1.2.248
10.1.2.249
10.1.2.250
10.1.2.251
10.1.2.252
10.1.2.253
10.1.2.254
```

##実験②ホスト情報を入手して、MySQLにinsertする
　では先ほど作ったスクリプトにPingスキャン機能、ホスト情報取得機能、MySQLへの登録機能を実装していきます。ソースコードは下記になります。

```extract_iplist.pl
#!/usr/bin/perl

use strict;

use DBI;
use Net::Ping::External qw(ping);
use Net::Netmask;
use Net::SNMP;

my $vlan=$ARGV[0];

my $dsn = "DBI:mysql:database=nw_config;host=127.0.0.1;port=3306";
my $dbh = DBI->connect($dsn, "*****", "*******") || die("[ERROR] DB Connect Error");
my $a_sth = $dbh->prepare("select NW_ADDRESS from config_data where VLAN='$vlan'") || die("[ERROR] SQL statement is wrong.");
$a_sth->execute() || die("[ERROR] SQL cannot be executed.");

while( my $ary_ref = $a_sth->fetchrow_arrayref){
        my $nwaddr = $ary_ref->[0];
        my @target=&ip_list($nwaddr);
        my $mask=&netmask($nwaddr);
        for my $ip (@target){
                my $alive = ping(
                        host=> $ip,
                        timeout=> "1",
                );
                if($alive){
                        print "$ip: Pingable\n";
                        my $hostname=&snmpwalk_host($ip);
                        my $b_sth=$dbh->prepare("insert into config_data (IP_ADDRESS, HOST, NETMASK, VLAN, NW_ADDRESS ) values (?, ?, ?, ?, ?)") || die("[ERROR] SQL statement is wrong.");
                        $b_sth->execute($ip,$hostname,$mask,$vlan,$nwaddr) || die("[ERROR] SQL(insert) cannot be executed.")
                } else {
                        print "$ip: NO response\n";
                }
        }
}

$dbh->disconnect;

exit 0;

sub ip_list{
        my @list=();
        my $block = new Net::Netmask ($_[0]);
        for my $IP ($block->enumerate)  {
                push(@list, $IP);
        }
        shift @list;
        pop @list;

        return @list;
}

sub netmask{
        my $block = new Net::Netmask ($_[0]);
        my $netmask = $block->mask();
        return $netmask;
}

sub snmpwalk_host{
        my ($session,$error) = Net::SNMP->session(
                -hostname => $_[0],
                -community=> "public",
                -port     => "161"
        );
        my $host_oid=".1.3.6.1.2.1.1.5.0";
        my $request=$session->get_request(
                -varbindlist => [$host_oid]
        );
        my $result=$request->{$host_oid};
        return $result;

        $session->close;
}
```

　相変わらず、判定機能は作ってません‼（商用ならダメダメな仕様ですね）
　今のところ実験できる環境がないので、エラーを吐かないから一応は動作するといったところですね…いつかは実験できるような環境を自宅に構築したいですね。
　/24のセグメントに対して実験すると、体感254×5秒=1270秒くらい掛かってますね。20分間は待たなければいけなさそう。
　コミュニティ名は今のところ、publicのみの環境を想定していますが、様々なポリシーがある場合はコミュニティ名が複数ある場合もあるので、それにも対応しないといけなさそうですね。多分、ポートへのアクセスが許可されていなかったり、コミュニティ名が間違ってるとHOSTのカラムにnullで入ると思います。

```
#  ./extract_iplist.pl Vlan2
10.1.2.1:NO response
10.1.2.2:NO response
10.1.2.3:NO response
10.1.2.4:NO response
```
##結果
　ネットワーク機器のConfigから抽出した情報をもとに、そのVLAN配下のVLAN情報についてPingスキャンとホスト情報の取得することができました。MySQLへのinsertまでは、自宅環境では再現できなかったので今後の課題ですね。
　これを調べたいVLANを列挙してシェルスクリプトで記載しておき、cronで1日1回起動させて情報の差分がないようにしたりとか出来そうですね。これも別の章で記載していきたいと思います。
　では、次回はとうとうnwdiagのconfigを自動生成して構成図を出力する機能を作っていこうと思います。

#参考資料
http://www.kurobuti.com/blog/?p=6510
http://search.cpan.org/dist/Net-Ping-External/External.pm
http://www2u.biglobe.ne.jp/~MAS/perl/waza/adel.html