ろーてくxyz blog

ローテクを駆使してIT関係でサヴァイヴしようとする人による備忘録

Network機器のconfigを自動取得してgitで管理してみる

久々の記事を書いてみる
最近Gitlabのサーバを立てて作ったツール類の管理をしていたのだが、運用する機器のconfigも管理したいなーと思っていた。
手始めにNetwork機器のconfigを取得してgitで管理してみる事に。
ネットで調べたところPythonモジュール類でいくつか良さそうながありました。

ネットで見つけたPythonモジュール類のお話

Cisco/Juniperルータのコンフィグを一元取得するツール #Python - Qiita
netmikoでネットワーク機器の種類を自動検出してコマンドを実行する #Python - Qiita

今回は以下の前提もありPythonで標準モジュールのみで一旦作ってみた。
次回はnetmikoあたりを使ってみたい・・・。
あとはAnsibleで叩いてからgitへとかもできそうな気もしている。

前提

  • RHEL系の標準モジュールでうごかす(Pythonも2.X系)
  • 歴史的経緯でtelnetsshが混ざっている環境(・・・)

みなさんの環境で環境構築に融通が利くと思うのでこんな感じにする必要はない気もしますが。
また、今回はGitlabへの反映はMaster branchに直接pushしてしまっている。

Pythonコード

SampleとしてCisco機器にtelnet、A10 Networks機器にSSHで入ってexpectモドキでsh runしてconfigをテキストで書き込むようなものを書いてみた。(無理矢理感もありますが)
destlistというtextファイルに

ipaddress,hostname,os type

をカンマ区切りでリスト化したものを読み込んで動かす形。

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import telnetlib
import datetime
import subprocess
import time
import paramiko
import swpass.password

"""
settings from
"""

home_dir = '/home/'
destlist = home_dir + 'destlist'

"""
settings end
"""

date = datetime.datetime.now().strftime("%Y%m%d")
loginp = swpass.password.password()


def cisco_get(ipaddr, sw_name):
    try:
        con = telnetlib.Telnet(ipaddr, '23')
        con.read_until('Password: ')
        con.write(loginp + '\n')
        time.sleep(2)
        login_result = con.read_some()
        if sw_name + '>' in login_result:
            con.write('en\n')
            con.write(loginp + '\n')
            con.write('terminal length 0\n')
            con.write('sh run\n')
            time.sleep(2)
            con.write('exit\n')
            result = con.read_all()
            time.sleep(10)
            print('Finish.')
            con.close()
            return result
        else:
	    message = 'LOGIN Error: ' + sw_name
	    print(message)
            con.close()
	    return message

    except Exception as e:
        print(sw_name + ' telnet error : ' + str(e.args))


def aten_ssh_get(ipaddr, sw_name):
    con = paramiko.SSHClient()
    con.load_system_host_keys()
    con.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        con.connect(ipaddr, port=22, username='admin',  
                    password=loginp,
                    look_for_keys=False, allow_agent=False)

        ssh_con = con.invoke_shell()
        time.sleep(2)
        result = ssh_con.recv(65535)
        if sw_name in result:
            ssh_con.send('en\n')
            time.sleep(1)
            ssh_con.send(loginp+ '\n')
            time.sleep(1)
            ssh_con.send('terminal length 0\n')
            time.sleep(1)
            ssh_con.send('sh run\n')
            time.sleep(5)
            ssh_con.send('exit\n')
            time.sleep(1)
            ssh_con.send('exit\n')
            time.sleep(1)
            ssh_con.send('y\n')
            time.sleep(1)
            command_result = ssh_con.recv(65535)
            print('Finish. MODE:ssh')
            con.close()
            return command_result
        else:
            message = 'LOGIN Error: ' + sw_name
            print(message)
            con.close()
            return message

    except Exception as e:
        print(sw_name + ' ssh error : ' + str(e.args))
       

def config_get():
    with open(destlist) as f:
        for lists in f:
            dest = lists.rstrip()
            ipaddr = dest.split(',')[0]
            sw_name = dest.split(',')[1]
            ostype = dest.split(',')[2]
            print('Start : ' + sw_name)
            with open(home_dir + sw_name + '-config', 'w') as result_log:
                if 'cisco' == ostype:
                    result = cisco_get(ipaddr, sw_name)
                    result_log.write(str(result))
                elif 'a10_ssh' == ostype:
                    result = aten_ssh_get(ipaddr, sw_name)
                    result_log.write(str(result))


def main():
    config_get()
    subprocess.call('sed -i -e \'/clock-period/d\' *config ', shell=True)
    subprocess.call('/usr/bin/git pull origin master', shell=True)
    subprocess.call('/usr/bin/git add --a', shell=True)
    subprocess.call('/usr/bin/git commit -m "[auto update] ' + date + '."', shell=True)
    subprocess.call('/usr/bin/git push -u origin master', shell=True)


if __name__ == '__main__':
    main()

機器ログイン時のパスワードは、Pythonのモジュール化させて外部から読み込ませています。
あと、Ciscoでsh runをするとntp clock-periodの行でgit diffが出てしまうので思い切って行の削除をsedでしてます。
expectモドキなので、前半でご紹介したモジュール類で機器が対応してない場合は、柔軟に対応できるのがメリットといったところでしょうか。


その後の運用メインの話はこちら
rotek.hateblo.jp