pythonでルータ・スイッチにログインして作業したい。とにかくしたい。

## でっかい自動化、ちっさい自動化
世は大自動化時代！
なわけですけど、一部巨大ネットワークオペレータはさておき、小規模なネットワークにおいては「自動化を 発注したら コスト増」なんて事態も大いにありうるわけです。
とはいえ、「ちょっとした自動化はしたいよなあ」「あっちょっと急ぎでここだけ確認したい！」という人向けのシンプルなクラスを作っておきました。
ssh平文コマンドの自動化なので、学習コスト極小、コマンドわかれば使える、最小限のクラス（なんならこっそり踏み台から、、）を志向しています。
ネットワークエンジニアが辞書片手にpython書く想定で丁寧に書いています。

## paramiko？
paramikoってのはpythonのsshライブラリです。pipでインストールできます。これだけは必須です。

```shell:とりあえずparamikoはインストール
pip install paramiko
もしくは
pip３ install paramiko
```

## node.py
クラスというほどでもないかもしれないクラスです。
paramikoってちょっとネットワーク機器には微妙なところがあって、そのへんをいなしてくれます。以下をコピペして実行ファイルと同じディレクトリに置いておいてください。
```python:node.py
from __future__ import print_function
import sys
import time
import paramiko
import warnings

class node(object):
    ip              = None
    shell           = None
    client          = None

    def __init__(self, ip = ""):
        self.ip         = str(ip)
        self.client     = paramiko.SSHClient()


    def login(self, user, passw):
        warnings.filterwarnings('ignore')
        self.client.set_missing_host_key_policy(paramiko.WarningPolicy())
        self.client.connect(self.ip, username = user, password = passw)
        self.shell = self.client.invoke_shell()
        self.exec_cmd("")
        return self.shell

    def logout(self):
        self.client.close() 

    def exec_cmd(self, cmdStr, tprompt = "#", time_out = 30):
        cmdline       = cmdStr + '\n'

        #Clear buffer
        if (self.shell.recv_ready()):
            now = self.shell.recv(100000)
            self.printMsg(now)
            now = ""

        self.shell.settimeout(time_out)
        self.shell.send(cmdline)
        revive = ''
        result = False

        for i in range(1, time_out * 10):
            time.sleep(0.1)

            if (self.shell.recv_ready()):
                now = self.shell.recv(100000)
                sys.stdout.write(now)

                revive += now
    
                if tprompt in revive:
                    result = True
                    time.sleep(0.5) #念の為、バッファのクリア
                    if (self.shell.recv_ready()):
                        now = self.shell.recv(100000)
                        sys.stdout.write(now)
                        revive += now
    
                    break

            else:
                continue

        if(not result):
            raise ValueError("COMMAND TIME OUT")

        return revive
```

### 各メソッドの使い方
```python:class node
__init__(self, ip = "")
    ip : ログイン対象のip、名前解決できるならホスト名でもOKです

login(self, user, passw)
    user : ログイン時のユーザー名
    passw: ログイン時のパスワード

exec_cmd(self, cmdStr, tprompt = "#", time_out = 30)
    cmdStr　　 : 実行するコマンド
    tprompt :　　コマンドの終了を判断する文字、hostname#みたいにしておけばいいです。
    time_out:　　コマンドのタイムアウトを判断する秒数

logout(self)
    終わったらログアウトお忘れなく

```


### node.pyのちょっと細かい話
sshってそもそも対話型なプロトコルなんだと思うんですけど、ルータって必ずしも対話ではないってのが苦戦ポイント。 monitor系のコマンドなんて典型ですが、１のコマンドに対して１のリターンがあるわけでもない。あと時間のかかるコマンドがある。そんな状態なので、ssh受信のためのコマンドがうまく動かずにparamikoが固まってしまうことも多いです。

そこでこのクラスでは、以下のループで出力を回収しています。こうすることでルーターの不思議な挙動に左右されずログを回収します。
**受信バッファが貯まってるか→バッファがあれば回収→リターンプロンプト（switch#みたいなやつ）があるか確認**

## sample.py
そんじゃ使ってみましょうということで以下簡単なサンプル。
**ログイン→コマンド打つ→結果判断→終了**

```python:sample.py
from __future__ import print_function
import node

def main():
    #生成
    router = node.node("192.168.0.1")

    #ログイン
    router.login("tarou.yamada", "password")

    #コマンド実行して結果を取得
    cmd = router.exec_cmd("show interface Gi 0/0/0/1", tprompt = "hostname#", time_out = 30)

    #結果を分析
    if "up" in cmd:
        print("interface up")
    else :
        print("interface down")

    #ログアウト
    router.logout()

if __name__ == "__main__":
    main()  

```

## 最後に
本格的に実行するなら例外処理とか、リスト実行とか、マルチスレッド実行とか、結果のファイル出力とか入れたほうがいいです。ご要望あれば追加します。
なによりssh平文コマンドよりnetconfとかansibleの時代ですし、nornirなんて便利なフレームワークもあります。
ただ小さな自動化に関しては、ssh平文コマンドが自動化のハードルを下げるのではないかと思っています。
ネットワークエンジニアが自動化に取り組むことに意義があると思うんです。ネットワークエンジニアが自動化を身につければ、ツール部隊に出力パターンと読み取りポイントを事細かに説明する必要もないし、フローチャートもいらないし、なんなら使い捨ての簡単なツール程度は検証込みで２-３時間でリリースできます。
いつの日かネットワークエンジニアがサクサクっとツールを作る未来を夢見ています！