Cisco/Juniperルータのコンフィグを一元取得するツール

# 概要
ルータ群のコンフィグを一元取得するツールを作りました。作ったツールはgithubで公開しています。
https://github.com/taijiji/ConfigCollector

#作った動機
管理しているルータのコンフィグを全機器分を一気に取得してGit管理したかったので作りました。

元々の想定では、[ルータ一覧ファイルの読み込み]->[ルータコンフィグの一元取得]->[Gitリモートレポジトリにpush] までの一連の工程をツール化しようと考えていたのですが、Git管理は運用者が手動でするほうがよいという考えのもと、ルータコンフィグの一元取得する部分だけを切り出してツール化しました。
ルータコンフィグ取得だけにフォーカスしたことでツール自体がシンプルになったことに加えて、結果的に他のツールとの連携がしやすくなったように感じています。

# ツールのインストール
githubからダウンロードしてきます。

```
git clone git@github.com:taijiji/ConfigCollector.git
```

ダウンロードしたツールのディレクトリに移動します。

```
cd ConfigCollector
```

pipを使って関連モジュールをインストールします。
 - pip未使用の場合は、事前にpipをインストールしてください。

```
pip install -r requirements.txt
```

以上で準備完了です。

# ツールの使い方
"get_router_config.py"に対して、JSON形式で作成したルータ一覧情報をコマンドライン引数にして実行します。

```
python get_router_config.py [json file]
```

ルータ一覧情報のjson fileは以下のように記述します。
このように書くと、全ルータに対してSSH経由でコンフィグを取得します。

```my_router.json
[
    {
        "hostname" : "router1",
        "username" : "user1",
        "password" : "aaabbbccc",
        "ipv4"     : "192.168.0.1",
        "os"       : "JUNOS"
    },
    {
        "hostname" : "router2",
        "username" : "user2",
        "password" : "aaabbbccc",
        "ipv4"     : "192.168.0.2",
        "os"       : "IOS-XR"
    },
    {
        "hostname" : "router3",
        "username" : "user3",
        "password" : "aaabbbccc",
        "ipv4"     : "192.168.0.3",
        "os"       : "IOS"
    }
]
```

ルータOSは手元でテストできたものだけを対象としています。
- JUNOS
- IOS
- IOS-XE
- IOS-XR

# 出力されるもの
ツールを実行して成功すると、"router_config"ディレクトリ配下に各ルータごとのコンフィグが保存されます。ファイル名は「ルータホスト名.txt」になっています。

以下は、上記の"my_router.json"ファイルで実行させた場合の結果です。

```
$ ls router_config
router1.txt  router2.txt  router3.txt
```

```
$ less router_config/router1.txt

show configuration | no-more
## Last commit: 2015-05-01 17:00:00 JST by user1
version 10.x.x;
system {
    host-name router1;
    time-zone Asia/Tokyo;
(snip)
```

最後に管理者が手動で、"router_config"ディレクトリを丸ごと[git add]->[git commit]->[git push]を実施してGithubやGitlabなどで管理することで、ルータのコンフィグを一元的にバージョン管理することができます。

#コード
参考までにコードを記載しておきます。全体的にそこまで難しいことはしていません。

ルータへのSSH + 情報取得は[Exscript]( https://github.com/knipknap/exscript) というモジュールを使っています。
SSHツールでは[expect](http://pexpect.readthedocs.org/en/latest/)が有名ですが、ExscriptはLinux/Unixの他にも、IOS, IOS-XR, JunOS, VRP,などに対応されています。ルータを制御する際に別途ルータ用に構文解析する必要がないので便利です。

## JSONファイルを読み込み、ルータ操作を呼び出すファイル

```python:get_router_config.py
#! /usr/bin/env python
import sys
import traceback
import json

from ssh_router import Router

# Open json input file
try:
    # argument is routers' information with JSON formt.
    input_filename = sys.argv[1]
    input_file = open(input_filename ,'r')
    print 'Reading router info from "' + input_filename + '"...' 
except ( IOError, IndexError):
    print 'Cannot open JSON file.'
    print 'Please use bellow: " python get_router_config [JSON file] " '
    sys.exit()
else:
    router_info_json = input_file.read()
    input_file.close()

# Convert format from json to dictionary_type
try:
    router_info = json.loads(router_info_json)
except ValueError as error:
    print 'JSON format error : '
    print router_info_json
    print error
    sys.exit()

# Login and get config each routers using ssh
for num in range( len(router_info) ):
    router = Router( router_info[num] )
    
    # Login Router
    print 'Accessing router: ' + router_info[num]['hostname'] + '...'
    try:
        router.login()
        router_config = router.get_config()
    except:
        print 'Router login error'
        print router_info[num]
        router.logout()
        sys.exit()

    # Get config of Router
    try:
        router_config = router.get_config()
    except:
        print 'Router get configuration error'
        print router_info[num]
        router.logout()
        sys.exit()
    else:
        router.logout()
    
    # Create output file written config
    try:
        output_filename = 'router_config/' + router_info[num]['hostname'] + '.txt'
        print 'Writing output file "' + output_filename + '"...'
    except AtributeError:
        print 'cannot read dictionary of router_info[' + num + '][hostname]'
        sys.exit()

    try:
        output_file = open ( output_filename, 'w')
    except:
        print 'cannot open "' + output_filename + '"'
        output_file.close
        sys.exit()
    else:
        output_file.write( router_config )
        output_file.close

    print 'Success to create "'  + output_filename + '" !'
```

## ルータへの制御を構成するファイル
ここではほとんどExscriptを呼び出しているだけです。

```python:ssh_router.py
! /usr/bin/env python
# -*- coding: utf-8 -*-

from Exscript.protocols import SSH2
from Exscript.Account import Account

class Router:
    def __init__(self, router_info):
        self.hostname   = router_info['hostname']
        self.username   = router_info['username']
        self.password   = router_info['password']
        self.ipv4           = router_info['ipv4']
        self.os              = router_info['os']

    def login(self):
        self.session = SSH2()
        self.session.connect(self.ipv4)
        self.session.login( Account(name=self.username, password=self.password) )

    def logout(self):
        if self.session :
            self.session.send('exit\r')
            self.session.close()
        else:
            raise AttributeError( 'cannot find a living session.' )

    def get_config(self):
        if( self.os=='IOS-XR' or self.os=='IOS' or self.os=='IOS-XE'):
            self.session.execute('terminal length 0')
            self.session.execute('show running-config')
            result = self.session.response
        elif( self.os == 'JUNOS'):
            self.session.execute('show configuration | no-more')
            result = self.session.response
        else:
            raise ValueError( 'OS is unknown value. Please describe from  JUNOS / IOS / IOS-XE / IOS-XR.' )

        return result
```