見出し画像

PythonでTelnet

仕事でLinuxやネットワーク機器に触れる方なら、Teratermはご存じかと思います。
あれがないと仕事にならない、と断言してもいいのではないかと思えるほど、秀逸なソフトですよね。

TeratermのマクロをPythonに移植

Teratermの便利なところは、マクロが組めて、自動実行が可能なところです。
私も同じ設定を複数のネットワーク機器に投入する時は、マクロを作って一気に流し込んでいます。

今回はTeratermの簡単なマクロを、Pythonに移植してみました。
わざわざ移植する理由は、Pythonの勉強です。

移植するマクロの要件

移植するマクロの要件としては、以下4点です。

  1. 接続はTelnetとする

  2. コマンドは複数発行できる

  3. コマンドの実行結果は、個別に保存する

  4. 対象はciscoルーターとする

  5. ユーザーモードと特権モードへのパスワードは同じとする(4/18追記)

今時Telnet?と思われるかもしれませんが、昔ながらの機器も多数あるため私の周りではTelnetが現役です。
その内、最低でもSSHに置き換わっていくでしょうが。

Pythonでのプログラム

from telnetlib import Telnet 
import datetime
import os

#メインルーチン
def main():

    #実行コマンド取得
    exec_cmd = read_exec_cmd()

    #Telnetログイン
    client = do_login()
    
    for cmd in exec_cmd:
        client.write(cmd.encode()+b"\n")
        save_cmd_result(cmd, client.read_until(b"#").decode('ascii'))

    #ログアウト
    do_logout(client)

#Telnetログイン
def do_login() -> Telnet:
    HOST = "192.168.1.1"
    password = "password"

    client = Telnet(HOST)
    
    client.read_until(b"Password: ")
    client.write(password.encode('ascii') + b"\n")

    #enable
    client.write(b"en\n")
    client.read_until(b"Password: ")
    client.write(password.encode('ascii') + b"\n")
    client.read_until(b"#")
    client.write(b"ter len 0\n")
    client.read_until(b"#")

    return client

#Telnetログアウト
def do_logout(client : Telnet):
    client.write(b"exit")
    client.close()

#実行するコマンドを取得する
def read_exec_cmd():
    root = os.path.dirname(os.path.abspath(__file__))
    filepath = os.path.join(root, "exec_cmd.txt")

    with open(filepath, "r", encoding="utf-8") as f:
        cmdlist = f.read().splitlines()

    return cmdlist

#コマンドの結果をログに保存
def save_cmd_result(cmd : str, txt : str):
    #現在日時をファイル名に付与
    nowstr = format(datetime.datetime.now(), "%Y%m%d-%H%M")

    curdir = os.path.dirname(os.path.abspath(__file__))
    #コマンド内の空白を「_」に
    filename = f"{nowstr}_{cmd.replace(' ', '_')}.txt"
    filepath = os.path.join(curdir, filename)

    with open(filepath, "w", encoding="utf-8") as f:
        f.write(txt.replace('\r',''))

if __name__ == "__main__":
    main()

接続先は特定1台に限定し、パスワードもコードに埋め込み。
本当は暗号化して外部ファイルとして保存しようと思ったのですが、少してこずりそうなため、今回はコードに埋め込んでいます。

上記プログラムと同階層に、実行するコマンドを一行一命令で記述した、「exec_cmd.txt」というコマンドファイルをutf-8で作成します。
内容は以下の感じです。

sh int status
sh congig

後はプログラムを実行すると、コマンドごとに「yyyymmdd-hhmm_コマンド.txt」という書式で結果が保存されます。
コマンド内の空白は「_」に置換しています。

てこずったのが、コマンドの実行結果を個別にログ出力する部分です。
サンプルの多くがread_all()メソッドを利用していたので、そのまま流用すると、プログラムが途中で止まったり、すべてのコマンドの結果が1ファイルとして作成されたりしました。

read_allは下記の説明の通り、接続開始から終了までを取得する時に有効なようです。

EOFに到達するまでの全てのデータをバイト列として読み込みます; 接続が閉じられるまでブロックします

https://docs.python.org/ja/3/library/telnetlib.html

今回はコマンドごとに結果が欲しいので、read_untilメソッドを使いました。

上記のプログラムで要望を満たした結果を得られることは確認済みです。
が、改善点が多々あります。

改善点

  1. 接続先とパスワードを別ファイルで管理する

  2. パスワードは暗号化する

  3. ciscoルーターも機種やOSによって、プロンプトに差があるため、その違いを吸収する

  4. エラー処理を実装する

今すぐではありませんが、上記については実装していきたいと思います。

それと、もっとPythonについて勉強が必要だと痛感しました。
今は必要な(やりたい)ことだけを調べながら、プログラムを書いているので、Pythonのお作法に反していると思います。

一度どこかでPythonの公式ドキュメントを読み込まないと。。。

いいなと思ったら応援しよう!