netmikoをYamahaに対応させてみた


はじめに

netmokoというNW機器向けのライブラリがあります。

※おなじみ某お魚さんの記事。ありがたや。
https://qiita.com/akira6592/items/feff470e0093d0b7ca43
https://tekunabe.hatenablog.jp/entry/2019/01/08/network_automation_tools_selection

NAPALMほど抽象化されていないのですが、ログインやログアウト、configモードになる、設定して保存するといった操作がメソッドとして提供されているので、expectコマンドやteratermのマクロを使って自動化するよりかは、共通的なインターフェイスで操作できるというメリットがあると思います。

そんなnetmikoが対応している機器はこちらです。
https://www.slideshare.net/akira6592/20170818-78977651

そう!Yamahaがないのである!!
自宅のRTXを操作できないではないですか!!

ソースを見た感じ、yamahaってフォルダにyamaha_ssh.pyを作ればいけんじゃね?(ど素人感)っと思ったのでやってみました。

実装

ひとまず開発環境(ラズパイw)でvenvを作成して、netmikoに必要なパッケージをインストールしました。

pi@raspberrypi:~ $ python3 -m venv yamaha-dev
pi@raspberrypi:~ $ cd yamaha-dev/
pi@raspberrypi:yamaha-dev $ git clone https://github.com/sat0ken/netmiko
pi@raspberrypi:yamaha-dev $ source bin/activate
(yamaha-dev) pi@raspberrypi:yamaha-dev $ cd netmiko/
(yamaha-dev) pi@raspberrypi:yamaha-dev $ python3 -m pip install -r requirements.txt

パッケージを入れたら、forkしたnetmikoにソースを追加します。
netmikoフォルダにyamahaを作成して、init.pyと他のソースを参考にyamaha_ssh.pyを作成しました。

initはこんな感じ

__init__.py
from __future__ import unicode_literals
from netmiko.yamaha.yamaha_ssh import YamahaSSH

__all__ = ["YamahaSSH"]

yamaha_ssh.pyはこんな感じで、CiscoSSHConnectionをimportしてYamahaSSHクラスを作成しました。
各機器のソースを見た感じ、ここで機器に応じてメソッドをオーバライドして対応するようです。

yamaha_ssh.py
from __future__ import print_function
from __future__ import unicode_literals
import time
from netmiko.cisco_base_connection import CiscoSSHConnection

class YamahaSSH(CiscoSSHConnection):
    def session_preparation(self):
        self._test_channel_read(pattern=r"[>#]")
        self.set_base_prompt()
        self.disable_paging(command="console lines infinity")
        time.sleep(0.3 * self.global_delay_factor)
        self.clear_buffer()

    def exit_enable_mode(self, exit_command="exit"):
        """Exit enable mode."""
        return super(YamahaSSH, self).exit_enable_mode(exit_command=exit_command)

    def save_config(self, cmd="save", confirm=False):
        """Saves configuration."""
        return super(YamahaSSH, self).save_config(cmd=cmd, confirm=confirm)

作成したらインストールします。

(yamaha-dev) pi@raspberrypi:netmiko $ python3 setup.py install

インストールしたら、netmikoのexampleフォルダのファイルを編集して実行してみます。

show_command.py
#!/usr/bin/env python
from __future__ import print_function, unicode_literals

# Netmiko is the same as ConnectHandler
from netmiko import Netmiko
from getpass import getpass

my_device = {
    "host": "10.0.0.1",
    "username": "yamaha",
    "password": getpass(),
    #device_typeでcisco_ios → yamahaを指定
    "device_type": "yamaha",
}

net_connect = Netmiko(**my_device)

#コマンドはYamahaのコマンドに
output = net_connect.send_command("show config")
print(output)

net_connect.disconnect()

で実行してみます。

(yamaha-dev) pi@raspberrypi:yamaha-dev $ python3 show_command.py 
Password: 
# RTX1100 Rev.8.03.91 (build 6) NTT-Com patch #1 (Mon Apr 25 13:46:36 2011)
# MAC Address : 00:a0:de:68:37:a1, 00:a0:de:68:37:a2, 00:a0:de:68:37:a3, 
# Memory 32Mbytes, 3LAN, 1BRI
# main:  RTX1100 ver=f0 serial=N1A171749 MAC-Address=00:a0:de:68:37:a1 MAC-Addr
ess=00:a0:de:68:37:a2 MAC-Address=00:a0:de:68:37:a3
# Reporting Date: Jun 24 17:20:36 1980
login password *
administrator password *
login user yamaha *
timezone +09:00
console character ascii
console lines infinity
login timer clear
ip route default gateway 192.168.0.1
ip lan1 address 10.0.0.1/20
ip lan3 address 192.168.0.254/24
ip lan3 nat descriptor 1
nat descriptor type 1 nat
nat descriptor address outer 1 192.168.0.250-192.168.0.253
nat descriptor address inner 1 10.0.0.1-10.0.0.254
tftp host any
telnetd service on
dhcp service server
dhcp scope 1 10.0.0.2-10.0.0.254/24
dns service recursive
dns server 192.168.0.1
httpd service on
httpd host lan3
sshd service on
sshd host key generate *
(yamaha-dev) pi@raspberrypi:yamaha-dev $ 

RTX1100にコマンドが通りました!!

今後

以下のスクリプトは同じコマンド(show config)をparamikoで書いてみたものですが、netmikoで書いた↑の方がスッキリしてますね。
device_typeの指定を変えればCiscoなどでも使い回しが聞きますし、paramikoで頑張るよりもnetmikoを拡張させたほうがよさそうです。

rtx1100_ssh.py
#!/usr/bin/env/python3

import paramiko
import time

host = "10.0.0.1"
user = "yamaha"
password = "xxxxxxxx"

client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

client.connect(hostname=host,username=user,password=password)

command = client.invoke_shell()
command.settimeout(10)

command.send("show config\n")
time.sleep(1)
output = command.recv(65535)

client.close()

lines = output.decode().split('\r\n')
for i in lines:
    print(i)

というわけでnetmikoを改造してYamahaの機器にもコマンドを打つことは出来ましたが、設定して保存なんかはまだ対応できてないので引き続き取り組んでみようと思います。

最後にリポジトリを
https://github.com/sat0ken/netmiko