はじめに
この記事はシスコの同志による Advent Calendar の一部として投稿しています
- 2017年版: https://qiita.com/advent-calendar/2017/cisco
- 2018年版: https://qiita.com/advent-calendar/2018/cisco
昨年の Advent Calendar でネットワーク自動化開発についての連載を宣言したものの、1年ぶりの投稿となってしまいました。不覚です。
気を取り直して、連載第2回では Python でのルータ操作を取り上げます。第1回では、自動化プログラミングのためのサンドボックスとしてネットワークシミュレータ VIRL を準備しました。今回は、VIRL 上で稼働させたルータを自在に操作するお話です。
IOS ルータを Python で操作する
普段、IOS ルータの操作には何を使うでしょうか? 最近の IOS-XE では GUI, REST API, NetConf/YANG など様々な実装が進んできていますが、いまだに Telnet/SSH での CLI 操作が多いのではないでしょうか?
ところが Telnet/SSH はそのままだとプログラミングには不向きです。そこで、Python で Telnet/SSH を使って IOS ルータに接続する方法を考えてみます。
1. paramiko を使う
paramiko は Python の SSH ライブラリです。これを使った、ルータに接続してコマンドを実行する Python コードは以下のような感じになります。
#!/usr/bin/env python
import paramiko
import time
host = "10.x.x.x"
port = 22
# login details
username = "admin"
password = "admin"
# Create a new Paramiko SSH connection object
conn = paramiko.SSHClient()
# Automatically add SSH hosts keys
conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
print("--------------------",host,"--------------------")
# create a shell session for multiple commands
conn.connect(host, port, username, password, look_for_keys=False, allow_agent=False)
remote_shell = conn.invoke_shell()
time.sleep(2)
# receive remote host shell output
output = remote_shell.recv(65535)
# display the output
print(output)
# send the command
remote_shell.send("show version\n")
time.sleep(1)
output = remote_shell.recv(65535)
print(output)
このコードでは host に対して ssh でログインし、show version コマンドを実行しています。ちなみに実行結果は以下の様な感じです。
$ ./enable_feature_cli.py
-------------------- 10.x.x.x --------------------
b'\r\n\r\n\r\ncsr1kv-eta#'
b'show version\r\nCisco IOS XE Software, Version 16.06.02\r\nCisco IOS Software [Everest], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.6.2, RELEASE SOFTWARE (fc2)\r\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2017 by Cisco Systems, Inc.\r\nCompiled Wed 01-Nov-17 07:24 by mcpre\r\n\r\n\r\nCisco IOS-XE software, Copyright (c) 2005-2017 by cisco Systems, Inc.\r\nAll rights reserved. Certain components of Cisco IOS-XE software are\r\nlicensed under the GNU General Public License ("GPL") Version 2.0. The\r\nsoftware code licensed under GPL Version 2.0 is free software that comes\r\nwith ABSOLUTELY NO WARRANTY. You can redistribute and/or modify such\r\nGPL code under the terms of GPL Version 2.0. For more details, see the\r\ndocumentation or "License Notice" file accompanying the IOS-XE software,\r\nor the applicable URL provided on the flyer accompanying the IOS-XE\r\nsoftware.\r\n\r\n\r\nROM: IOS-XE ROMMON\r\n\r\ncsr1kv-eta uptime is 1 year, 2 weeks, 22 hours, 34 minutes\r\nUptime for this control processor is 1 year, 2 weeks, 22 hours, 36 minutes\r\n --More-- '
コードを見て、「たった1行のコマンドを打つのにこんなことしないといけないの?」 と思われたことと思います。特に、セッション確立後に sleep を使って CLI 応答を待ったり、応答を recv でバッファから取り出したりと、いかにも面倒なことをしています。しかし、これをしないと正常に出力を取り出せないのです! 試しに sleep なしでコードを何度も実行すると、タイミングによっては output に何も出力されないことがあったりします。
このように、単にルータの操作に SSH ライブラリを使うだけではコードが長くなるだけで、本当に取り組みたい自動化のためのプログラミングをやる気が無くなってしまいます。
そこで、ルータの操作をケアしてくれる別のライブラリを使うことを考えてみます
2. Netmiko を使う
netmiko は、 paramiko をベースに開発されている、ネットワークデバイスの操作をシンプルに書くことができるライブラリです。Cisco IOS だけでなく様々なデバイスに対応しています。
この netmiko を使った Python コードの例は以下です
#!/usr/bin/env python
from netmiko import ConnectHandler
import time # used to insert pauses in the script
# a list of the hosts we wish to access
cisco_ios = {
'device_type': 'cisco_ios',
'ip': '10.x.x.x',
'username': 'admin',
'password': 'admin',
'port' : 22, # optional, defaults to 22
'secret': 'admin', # optional, defaults to ''
'verbose': False, # optional, defaults to False
}
# Create a new Paramiko SSH connection object
net_connect = ConnectHandler(**cisco_ios)
# Issue commands
print("--------------------",cisco_ios['ip'],"--------------------")
output = net_connect.send_command('show version')
print(output)
ずいぶんシンプルになりました。このように、デバイスの情報をディクショナリでまとめて ConnectHandler に渡すと、OS の種類に合わせて最適なやりとりをしてくれます。
さらに、netmiko は TextFSM と連携する機能があります。TextFSM は、対応しているコマンドであれば、出力をパースして構造化データにしてくれるスグレモノです。
たとえば、上記コードを以下のように変更すると、Raw テキストでなく json 形式で結果が帰ってきます。
output = net_connect.send_command('show version', use_textfsm=True)
出力例:
$ ./netmiko_example.py
-------------------- 10.71.158.154 --------------------
[{'uptime': '1 year, 2 weeks, 23 hours, 28 minutes', 'serial': ['90SFJB41MY5'], 'rommon': 'IOS-XE', 'reload_reason': 'Unknown reason', 'running_image': 'packages.conf', 'config_register': '0x2102', 'hardware': ['CSR1000V'], 'version': '16.6.2', 'hostname': 'csr1kv-eta'}]
このように、Python コード側でテキストをパースする手間が減り、シリアル番号や OS バージョンなど必要な情報を簡単に取り出すことができます。TextFSM テンプレートのリスト を見ると、すべてのコマンドに対応しているわけではありませんが、執筆時の時点で、Cisco IOS の場合 50 個程度のコマンドをパースできるようです。また、自分で TextFSM テンプレートを追加することもできます。
Google で検索しても、netmiko は自動化の取り組みで広く使われているようです。
これで良い気がしますが、もう少し、自動化に必要なものを考えてみます。ルータ操作の自動化プログラミングで必要となるのは、show コマンドの実行のほかにコンフィグの投入があります。たとえば、ルータの動作テストを行うとしたら、何らかのトリガーとなるコンフィグの投入を行い、その前後で show コマンドを打って状態の差分を見たりするはずです。さらに、そのログを取得したり、レポートを作成したりする必要もあります。こういった操作をもう少し便利にする仕組みがあると嬉しいところです。
(ちょっと強引だったかもしれませんが)そういう期待に応えてくれるのが次に紹介する pyATS / Genie / ROBOT Framework です。
3. pyATS / Genie / ROBOT Framework を使う
pyATS は Python3 で書かれたテスト自動化フレームワークです。実はもともと Cisco の IOS 開発部隊が実際に使っているフレームワークだったのですが、昨年、広く活用してもらうべく Apache 2.0 ライセンスで公開されました。
pyATS やそのライブラリである Genie, テスト記述フレームワークである ROBOT Framework についての解説は、私の ONIC 2018 発表用の github レポジトリ をぜひご参照いただけると嬉しいです。
pyATS/Genie を使った Python コードは、テストシナリオの記述が目的ということもあり、paramiko や netmiko と比べて複雑になってしまいますので、今回の記事では取り上げません。「テストの自動化」については、今回のベーシックレベルの連載とは別に、改めて記事にしたいと思います。
まとめ
今回の記事では、ネットワーク機器の CLI 操作が可能な Python ライブラリをいくつか紹介しました。今回取り上げたもの以外にも、Github などで公開されているものがたくさんあります。
これらを活用すると、ルータを Python で簡単に操作することができるようになります。さらに、TextFSM などを活用してコマンド出力をパースし構造化データとして取り出すこともできます。
今回の記事で紹介したコードはとても基本的なものですが、応用すれば複雑な操作手順を記述することもできると思います。ぜひ参考にして、いろいろとカスタマイズするところから始めていただけたら幸いです。
目次
前回: 第1回: ネットワーク自動化開発実践 準備編 - Cisco VIRL
次回: 第3回: ネットワーク自動化開発実践 - Jupyter Notebook で Cool なデモをしよう
免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。