エーピーコミュニケーションズ Advent Calendar 2021 11日目の記事です
Scrapliとは
ネットワーク機器へTelnetまたはSSHの接続するためのPythonライブラリです。
特徴としては、
- 簡単に始められる
- 速い
- 開発が簡単
- 拡張性がある(プラグ可能)
トランスポート部分のカスタマイズができるのも特徴です、ssh2やparamiko,telnetなど柔軟に変更ができるようです。しかし、トランスポートはよっぽどの要件がない限り変更しないで十分なようです。
Scrapliのメインリポジトリ github.com
サポートするドライブ(コア)を拡張し様々な機器に対応するためのscrapli_community github.com
Netconf対応すためのscrapli_netconf github.com
Nornirのプライグインとして利用するnornir_scrapli github.com
テストのために「実際の」ネットワークデバイスのように見えるセミインタラクティブSSHサーバーを作成するためのツールscrapli_replay github.com
NW機器の対応状況(2021/12/11時点)
scrapli
scrapli_community
Netmikoとの違い
似たようなライブラリにNetmikoがあります。メソッドの構成は似ています(send_command,send_commandsなど)
違いとしては、サポートしているデバイスの数、Netconfのサポートの有無でしょうか。 Netmikoはサポートしているデバイスの数がとても多いです。 github.com
scrapliではまだやり方が分かっていないだけなんですが、Netmikoではredispatchを使ってNW機器の踏み台アクセスもできます。 (SVを踏み台にするのはScrapliでも可能)
どちらも機能としてパーサーと連携もできるため、どちらを使うか?は利用しているデバイスの種類や設定変更によって決めることになりそうです。処理自体もScrapliのほうが速いようです。
手元で比較したときは、close(切断)の処理がNetmikoは数秒かかってしまうのでスクリプトの終了まではScrapliのほうが速いといった結果になりました。
Netmikoは公式ドキュメントがとてもおしゃれです。
環境
Python 3.9.2
scrapli==2021.7.30
インストール
最低限のインストール
pip install scrapli
他の機能を使う場合に、ライブラリをまとめてインストール
pip install scrapli[full] pip install scrapli[paramiko] pip install scrapli[ssh2] pip install scrapli[asyncssh] pip install scrapli[textfsm] pip install scrapli[genie] pip install scrapli[ttp]
使ってみる
ログイン/ログアウト
from scrapli import Scrapli device = { "host": "sandbox-iosxe-latest-1.cisco.com", "auth_username": "developer", "auth_password": "XXXXXXXXX", "auth_strict_key": False, "platform": "cisco_iosxe" } conn = Scrapli(**device) conn.open() print(conn.get_prompt()) conn.close()
platformを指定することで対応するDriverを選択してくれるようです。
platformを指定せずにIOSXEDriver()を利用しても、同じ挙動になります。
接続はopen()
でログアウトはclose()
で実施します。
get_prompt()
はその名の通りプロンプトを取得してくれます
Scrapli# python example.py csr1000v-1#
showコマンドの実行
続いて、ログイン後showコマンドを実行してみます。
send_command()/send_commands
を利用します。
ter len 0など表示に関する設定は自動的に行われるため、showコマンドのみを指定します。
send_command: 1コマンド実行
from scrapli import Scrapli import pprint device = { "host": "sandbox-iosxe-latest-1.cisco.com", "auth_username": "developer", "auth_password": "XXXXXXXXX", "auth_strict_key": False, "platform": "cisco_iosxe" } with Scrapli(**device) as conn: res = conn.send_command("show ip interface brief") print(res.start_time) print(res.result) print(res.finish_time)
resultに実行結果が格納されているので結果の確認はres.result
を表示させます。
# python show.py 2021-12-11 12:21:18.694178 Interface IP-Address OK? Method Status Protocol GigabitEthernet1 10.10.20.48 YES NVRAM up up GigabitEthernet2 192.168.1.1 YES other up up GigabitEthernet3 unassigned YES NVRAM administratively down down Loopback1 56.56.56.56 YES manual up up 2021-12-11 12:21:18.943480
send_commands: 複数コマンド実行
複数のコマンドを実行するにはsend_commands()
を利用します。
commands引数にリスト形式でshowコマンドを指定します。
from scrapli import Scrapli import pprint device = { "host": "sandbox-iosxe-latest-1.cisco.com", "auth_username": "developer", "auth_password": "XXXXXXXXX", "auth_strict_key": False, "platform": "cisco_iosxe" } with Scrapli(**device) as conn: res = conn.send_commands(commands=["show ip interface brief", "show version"]) print(res.result) # すべて表示 print(res[0].result) # 1つ目の要素(show ip interface brief)を表示 print(res[1].result) # 2つ目の要素(show version)を表示
実行結果も、リストになっているためX番目の結果を表示するにはres[X].result
と指定します。
python show2.py # すべて表示 show ip interface brief <- 実行したコマンドも含まれる Interface IP-Address OK? Method Status Protocol GigabitEthernet1 10.10.20.48 YES NVRAM up up GigabitEthernet2 192.168.1.1 YES other up up GigabitEthernet3 unassigned YES NVRAM administratively down down Loopback1 56.56.56.56 YES manual up upshow version <- 実行したコマンドも含まれる Cisco IOS XE Software, Version 17.03.01a Cisco IOS Software [Amsterdam], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.3.1a, RELEASE SOFTWARE (fc3) Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2020 by Cisco Systems, Inc. Compiled Wed 12-Aug-20 00:16 by mcpre Cisco IOS-XE software, Copyright (c) 2005-2020 by cisco Systems, Inc. All rights reserved. Certain components of Cisco IOS-XE software are licensed under the GNU General Public License ("GPL") Version 2.0. The software code licensed under GPL Version 2.0 is free software that comes with ABSOLUTELY NO WARRANTY. You can redistribute and/or modify such GPL code under the terms of GPL Version 2.0. For more details, see the documentation or "License Notice" file accompanying the IOS-XE software, or the applicable URL provided on the flyer accompanying the IOS-XE software. ROM: IOS-XE ROMMON csr1000v-1 uptime is 1 day, 3 hours, 57 minutes Uptime for this control processor is 1 day, 3 hours, 58 minutes System returned to ROM by reload System image file is "bootflash:packages.conf" Last reload reason: reload This product contains cryptographic features and is subject to United States and local country laws governing import, export, transfer and use. Delivery of Cisco cryptographic products does not imply third-party authority to import, export, distribute or use encryption. Importers, exporters, distributors and users are responsible for compliance with U.S. and local country laws. By using this product you agree to comply with applicable laws and regulations. If you are unable to comply with U.S. and local laws, return this product immediately. A summary of U.S. laws governing Cisco cryptographic products may be found at: http://www.cisco.com/wwl/export/crypto/tool/stqrg.html If you require further assistance please contact us by sending email to export@cisco.com. License Level: ax License Type: N/A(Smart License Enabled) Next reload license Level: ax The current throughput level is 1000 kbps Smart Licensing Status: UNREGISTERED/No Licenses in Use cisco CSR1000V (VXE) processor (revision VXE) with 715705K/3075K bytes of memory. Processor board ID 9ESGOBARV9D Router operating mode: Autonomous 3 Gigabit Ethernet interfaces 32768K bytes of non-volatile configuration memory. 3978420K bytes of physical memory. 6188032K bytes of virtual hard disk at bootflash:. Configuration register is 0x2102 # 1つ目の要素(show ip interface brief)を表示 Interface IP-Address OK? Method Status Protocol GigabitEthernet1 10.10.20.48 YES NVRAM up up GigabitEthernet2 192.168.1.1 YES other up up GigabitEthernet3 unassigned YES NVRAM administratively down down Loopback1 56.56.56.56 YES manual up up # 2つ目の要素(show version)を表示 Cisco IOS XE Software, Version 17.03.01a Cisco IOS Software [Amsterdam], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.3.1a, RELEASE SOFTWARE (fc3) Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2020 by Cisco Systems, Inc. Compiled Wed 12-Aug-20 00:16 by mcpre Cisco IOS-XE software, Copyright (c) 2005-2020 by cisco Systems, Inc. All rights reserved. Certain components of Cisco IOS-XE software are licensed under the GNU General Public License ("GPL") Version 2.0. The software code licensed under GPL Version 2.0 is free software that comes with ABSOLUTELY NO WARRANTY. You can redistribute and/or modify such GPL code under the terms of GPL Version 2.0. For more details, see the documentation or "License Notice" file accompanying the IOS-XE software, or the applicable URL provided on the flyer accompanying the IOS-XE software. ROM: IOS-XE ROMMON csr1000v-1 uptime is 1 day, 3 hours, 57 minutes Uptime for this control processor is 1 day, 3 hours, 58 minutes System returned to ROM by reload System image file is "bootflash:packages.conf" Last reload reason: reload This product contains cryptographic features and is subject to United States and local country laws governing import, export, transfer and use. Delivery of Cisco cryptographic products does not imply third-party authority to import, export, distribute or use encryption. Importers, exporters, distributors and users are responsible for compliance with U.S. and local country laws. By using this product you agree to comply with applicable laws and regulations. If you are unable to comply with U.S. and local laws, return this product immediately. A summary of U.S. laws governing Cisco cryptographic products may be found at: http://www.cisco.com/wwl/export/crypto/tool/stqrg.html If you require further assistance please contact us by sending email to export@cisco.com. License Level: ax License Type: N/A(Smart License Enabled) Next reload license Level: ax The current throughput level is 1000 kbps Smart Licensing Status: UNREGISTERED/No Licenses in Use cisco CSR1000V (VXE) processor (revision VXE) with 715705K/3075K bytes of memory. Processor board ID 9ESGOBARV9D Router operating mode: Autonomous 3 Gigabit Ethernet interfaces 32768K bytes of non-volatile configuration memory. 3978420K bytes of physical memory. 6188032K bytes of virtual hard disk at bootflash:. Configuration register is 0x2102
パーサーと組み合わせる
CLIのテキストデータを構造化するために、パースすることもできます。
- textfsm_parse_output
- ttp_parse_output
- genie_parse_output
from scrapli import Scrapli import pprint device = { "host": "sandbox-iosxe-latest-1.cisco.com", "auth_username": "developer", "auth_password": "XXXXXXXXX", "auth_strict_key": False, "platform": "cisco_iosxe" } with Scrapli(**device) as conn: res = conn.send_commands(commands=["show ip interface brief", "show version"]) [ pprint.pprint(i.textfsm_parse_output()) for i in res] [ pprint.pprint(i.genie_parse_output()) for i in res]
textfsmパーサとgenieパーサを試します。事前にntc-templates,genieのライブラリをインストールしておきます。 この2つのパーサはテンプレートを指定せずにすぐに利用が可能です。
TTPは利用するテンプレートを指定する必要があるためひと手間必要なので割愛します。
## textfsm(ntc-template): show interface brief [{'intf': 'GigabitEthernet1', 'ipaddr': '10.10.20.48', 'proto': 'up', 'status': 'up'}, {'intf': 'GigabitEthernet2', 'ipaddr': '192.168.1.1', 'proto': 'up', 'status': 'up'}, {'intf': 'GigabitEthernet3', 'ipaddr': 'unassigned', 'proto': 'down', 'status': 'administratively down'}, {'intf': 'Loopback1', 'ipaddr': '56.56.56.56', 'proto': 'up', 'status': 'up'}] ## textfsm(ntc-template): show version [{'config_register': '0x2102', 'hardware': ['CSR1000V'], 'hostname': 'csr1000v-1', 'mac': [], 'reload_reason': 'reload', 'restarted': '', 'rommon': 'IOS-XE', 'running_image': 'packages.conf', 'serial': ['9ESGOBARV9D'], 'uptime': '1 day, 4 hours, 36 minutes', 'uptime_days': '1', 'uptime_hours': '4', 'uptime_minutes': '36', 'uptime_weeks': '', 'uptime_years': '', 'version': '17.3.1a'}] ## genie: show interface brief {'interface': {'GigabitEthernet1': {'interface_is_ok': 'YES', 'ip_address': '10.10.20.48', 'method': 'NVRAM', 'protocol': 'up', 'status': 'up'}, 'GigabitEthernet2': {'interface_is_ok': 'YES', 'ip_address': '192.168.1.1', 'method': 'other', 'protocol': 'up', 'status': 'up'}, 'GigabitEthernet3': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'NVRAM', 'protocol': 'down', 'status': 'administratively down'}, 'Loopback1': {'interface_is_ok': 'YES', 'ip_address': '56.56.56.56', 'method': 'manual', 'protocol': 'up', 'status': 'up'}}} ## genie: show version {'version': {'chassis': 'CSR1000V', 'chassis_sn': '9ESGOBARV9D', 'compiled_by': 'mcpre', 'compiled_date': 'Wed 12-Aug-20 00:16', 'curr_config_register': '0x2102', 'disks': {'bootflash:.': {'disk_size': '6188032', 'type_of_disk': 'virtual hard disk'}}, 'hostname': 'csr1000v-1', 'image_id': 'X86_64_LINUX_IOSD-UNIVERSALK9-M', 'image_type': 'production image', 'label': 'RELEASE SOFTWARE (fc3)', 'last_reload_reason': 'reload', 'license_level': 'ax', 'license_type': 'N/A(Smart License Enabled)', 'main_mem': '715705', 'mem_size': {'non-volatile configuration': '32768', 'physical': '3978420'}, 'next_reload_license_level': 'ax', 'number_of_intfs': {'Gigabit Ethernet': '3'}, 'os': 'IOS-XE', 'platform': 'Virtual XE', 'processor_type': 'VXE', 'returned_to_rom_by': 'reload', 'rom': 'IOS-XE ROMMON', 'router_operating_mode': 'Autonomous', 'rtr_type': 'CSR1000V', 'system_image': 'bootflash:packages.conf', 'uptime': '1 day, 4 hours, 36 minutes', 'uptime_this_cp': '1 day, 4 hours, 38 minutes', 'version': '17.3.1a', 'version_short': '17.3', 'xe_version': '17.03.01a'}}
パースも簡単にできます。
設定変更
send_configs
で設定を送ることができます。
configureモードへの変更などはドライバー側で入力してくれるため、設定のみを指定します。
from scrapli import Scrapli import pprint device = { "host": "sandbox-iosxe-latest-1.cisco.com", "auth_username": "developer", "auth_password": "XXXXXXXXX", "auth_strict_key": False, "platform": "cisco_iosxe" } with Scrapli(**device) as conn: res_configure = conn.send_configs(["interface loopback100", "description configured by scrapli"]) print(res_configure.result)
resultには実行したコマンドと実行時の標準出力が格納されています。
python configure1.py interface loopback100 description configured by scrapli
存在しないリソースを消そうとした。などでエラーメッセージが出た場合もresultに格納されます
python configure_back.py # loopback100がない状態で削除をしようとした no interface loopback100 ^ % Invalid input detected at '^' marker.
ここまでの要素を組み合わせて、 参照→変更→参照の流れを実施します。 設定前と設定後のshow interfaceを取得しパースしてテーブルにします。 テーブル作成のため、numpyとtabulateを利用します。
from scrapli import Scrapli import tabulate import numpy as np device = { "host": "sandbox-iosxe-latest-1.cisco.com", "auth_username": "developer", "auth_password": "XXXXXXXXX", "auth_strict_key": False, "platform": "cisco_iosxe" } with Scrapli(**device) as conn: res_before = conn.send_command("show interfaces") res_configure = conn.send_configs(["interface loopback100", "description configured by scrapli"]) res_after = conn.send_command("show interfaces") parsed_before = res_before.genie_parse_output() parsed_after = res_after.genie_parse_output() ## NOTE: 出力結果をテーブルにする処理 after_interface_list = list() after_description_list = list() before_interface_list = list() before_description_list = list() [before_interface_list.append(i) for i in parsed_before] [before_description_list.append(parsed_after[i]['description']) for i in parsed_before] [after_interface_list.append(i) for i in parsed_after] [after_description_list.append(parsed_after[i]['description']) for i in parsed_after] headers = ["interface", "description"] table_before = [before_interface_list, before_description_list] table_after= [after_interface_list, after_description_list] res_table_before = tabulate.tabulate(np.array(table_before).transpose(), headers,tablefmt="grid") res_table_after = tabulate.tabulate(np.array(table_after).transpose(), headers,tablefmt="grid") ## NOTE: テーブルの表示 print("==BEFORE==") print(res_table_before) print("==AFTER==") print(res_table_after)
実行します
python configure.py ==BEFORE== +------------------+---------------------------------------+ | interface | description | +==================+=======================================+ | GigabitEthernet1 | MANAGEMENT INTERFACE - DON'T TOUCH ME | +------------------+---------------------------------------+ | GigabitEthernet2 | Configured by RESTCONF | +------------------+---------------------------------------+ | GigabitEthernet3 | Network Interface | +------------------+---------------------------------------+ | Loopback1 | This is a test | +------------------+---------------------------------------+ ==AFTER== +------------------+---------------------------------------+ | interface | description | +==================+=======================================+ | GigabitEthernet1 | MANAGEMENT INTERFACE - DON'T TOUCH ME | +------------------+---------------------------------------+ | GigabitEthernet2 | Configured by RESTCONF | +------------------+---------------------------------------+ | GigabitEthernet3 | Network Interface | +------------------+---------------------------------------+ | Loopback1 | This is a test | +------------------+---------------------------------------+ | Loopback100 | configured by scrapli | +------------------+---------------------------------------+
変更前と変更後のテーブルを表示してみました、Loopback100が追加されたことも確認できます。
Netconf
最後にScrapliの特徴の一つでもあるNetconfにドライバーも試してみます。
事前にscrapli-netconfをインストールしておきます。
pip install scrapli-netconf
スクリプト作成
from scrapli_netconf.driver import NetconfDriver device = { "host": "sandbox-iosxe-latest-1.cisco.com", "auth_username": "developer", "auth_password": "XXXXXXXXX", "auth_strict_key": False, "port": 830, } INTERFACE_FILTER = """ <if:interfaces xmlns:if="urn:ietf:params:xml:ns:yang:ietf-interfaces"> </if:interfaces> """ with NetconfDriver(**device) as nc_conn: response= nc_conn.get(filter_=INTERFACE_FILTER) print(response.result)
Interfaeの情報を取得します。
実行
python netconf_get.py <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101"> <data> <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> <interface> <name>GigabitEthernet1</name> <description>MANAGEMENT INTERFACE - DON'T TOUCH ME</description> <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type> <enabled>true</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"> <address> <ip>10.10.20.48</ip> <netmask>255.255.255.0</netmask> </address> </ipv4> <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface> <interface> <name>GigabitEthernet2</name> <description>Configured by RESTCONF</description> <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type> <enabled>true</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"> <address> <ip>192.168.1.1</ip> <netmask>255.255.255.252</netmask> </address> </ipv4> <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface> <interface> <name>GigabitEthernet3</name> <description>Network Interface</description> <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type> <enabled>false</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface> <interface> <name>Loopback1</name> <description>This is a test</description> <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type> <enabled>true</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"> <address> <ip>56.56.56.56</ip> <netmask>255.255.255.255</netmask> </address> </ipv4> <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface> <interface> <name>Loopback100</name> <description>configured by scrapli</description> <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type> <enabled>true</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface> <interface> <name>Loopback101</name> <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:softwareLoopback</type> <enabled>true</enabled> <ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"> <address> <ip>101.101.101.101</ip> <netmask>255.255.255.255</netmask> </address> </ipv4> <ipv6 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip"/> </interface> </interfaces> </data> </rpc-reply>
情報取得できることを確認できました。
設定変更(edit)やコンフィグ取得(get_config)などのメソッドも用意されています。
まとめ
Scrapliに入門してみました。Netmikoを触ったことがあったので抵抗なく始めることができました、それくらい似ていると思います。
DriverにはAsync用のものもあり非同期処理にも対応しているようです。
標準ライブラリconcurrent.futuresを使って平行処理も実装してみましたが、軽量でサクサク動くので便利です。
興味があればぜひ試してみてください。
Golangで書かれたScrapligoもあるようです。 github.com