2019/09/05 に、ネットワークプログラマビリティ勉強会 #18で、「show コマンド結果をパースする方法あれこれ」という発表をさせていだきました。サンプルコードが中心だったため、コピペしやすいように、ブログ記事として書きおこします。
[2020/09/25 追記]
ansible.netcommon
collection の 1.2.0 で、cli_parse
モジュールが追加されました。コマンドの実行と本記事で紹介するパーサー TextFSM、ntc-templates、pyATS などとの連携してパースデータを取得できます。
- ansible.netcommon/ansible.netcommon.cli_parse_module.rst at main · ansible-collections/ansible.netcommon · GitHub
- Parsing semi-structured text with Ansible — Ansible Documentation
[2021/01/29 追記]
先述の cli_parse
モジュールが、ansible.utils
collectionにも登場しました。今後はこちらでメンテナンスされているのかもしれません。
■ 1. はじめに
ネットワーク機器の通常の show コマンドの結果は、機械(プログラム)にとっては取り扱いにくくなりがちです。これを解決する、show コマンドの結果を構造化データ(JSONなど)にするパーサーをご紹介します。
具体的には、TextFSM、Genie Parser という2つのパーサーです。また、自動化ツール Netmiko、Ansible との組み合わせも、サンブルコードベースにご紹介します。すべて Python ベースのものです。
「そもそもネットワーク器機が構造化データを出力してくれればいいのでは」と思われるとも思いますが、運用中の機器にはそういった機能なまだまだ無かったりすることも多いのではないでしょうか。そんなときに、これらのパーサーが役に立ちます。
■ 2. パーサーとは
パーサーとは、show コマンドの結果を機械(プログラム)が取り扱いやすい構造化データに変換するものです。(今回の記事の文脈上の説明)
例えば、以下のような show ip interface brief
の結果
csr1000v#show ip interface brief Interface IP-Address OK? Method Status Protocol GigabitEthernet1 10.10.20.48 YES NVRAM up up GigabitEthernet2 unassigned YES NVRAM administratively down down GigabitEthernet3 unassigned YES NVRAM administratively down down
を、以下のような機械が取り扱いやすいデータに変換します。
{ "INTF": "GigabitEthernet1", "IPADDR": "10.10.20.48", "PROTO": "up", "STATUS": "up" }, { "INTF": "GigabitEthernet2", "IPADDR": "unassigned", "PROTO": "down", "STATUS": "administratively down" }, { "INTF": "GigabitEthernet3", "IPADDR": "unassigned", "PROTO": "down", "STATUS": "administratively down" }
今回、この記事でパースする、サンプルの show コマンド結果はこちらです。 Cisco DevNet Sandbox CSRV1000V IOS-XE (16.11.01a) の環境を利用させていただきました。
csr1000v#show ip interface brief Interface IP-Address OK? Method Status Protocol GigabitEthernet1 10.10.20.48 YES NVRAM up up GigabitEthernet2 unassigned YES NVRAM administratively down down GigabitEthernet3 unassigned YES NVRAM administratively down down Loopback0 unassigned YES unset up up Loopback1 unassigned YES unset up up Loopback2 unassigned YES unset up up
■ 3. TextFSM
3.1. TextFSM とは
TextFSM は、カスタマイズが容易なパーサーです。
- ntc-templates のテンプレートを利用可
- プラットフォーム数 23
- 1コマンド数 290
- (2019/09/03現在)
- テンプレートは追加、カスタマイズ可能
pip install textfsm
でインストール
テンプレートは以下のような形です。正規表現でパースする方法を定義します。
Value INTF (\S+) Value IPADDR (\S+) Value STATUS (up|down|administratively down) Value PROTO (up|down) Start ^${INTF}\s+${IPADDR}\s+\w+\s+\w+\s+${STATUS}\s+${PROTO} -> Record
3.2. TextFSM 単体の利用例
TextFSM 単体では、ネットワーク機器への接続機能はありません。そのためは何かしらの方法で取得したコマンド結果を用意しておきます。(コード内の raw_text_data
に相当するデータ)
コード
import textfsm from pprint import pprint # テンプレートファイルの読み込み template = open('./templates/cisco_ios_show_ip_interface_brief.template', 'r') re_table = textfsm.TextFSM(template) # raw_text_data は何かしらの方法で取得したコマンド結果 fsm_results = re_table.ParseText(raw_text_data) results = list() # ヘッダーとデータを結合 for item in fsm_results: results.append(dict(zip(re_table.header, item))) # 表示 pprint(results)
なお、公式のサンプルはこちらです。
実行結果
以下のようにパースされます。ここまで、構造化されていれば、コードから取り扱いしやすいですね。
[{'INTF': 'GigabitEthernet1', 'IPADDR': '10.10.20.48', 'PROTO': 'up', 'STATUS': 'up'}, {'INTF': 'GigabitEthernet2', 'IPADDR': 'unassigned', 'PROTO': 'down', 'STATUS': 'administratively down'}, {'INTF': 'GigabitEthernet3', 'IPADDR': 'unassigned', 'PROTO': 'down', 'STATUS': 'administratively down'}, {'INTF': 'Loopback0', 'IPADDR': 'unassigned', 'PROTO': 'up', 'STATUS': 'up'}, {'INTF': 'Loopback1', 'IPADDR': 'unassigned', 'PROTO': 'up', 'STATUS': 'up'}, {'INTF': 'Loopback2', 'IPADDR': 'unassigned', 'PROTO': 'up', 'STATUS': 'up'}]
3.3. TextFMS + Netmiko 利用例
ネットワーク自動化ライブラリ Netmikoでは、Netmiko 2.0.0 から TextFSM が同梱されていて、簡単に連携できます。
なお、Netmiko インストール時に TextFSM も一緒にインストールされます。
コード
send_command
の引数に、use_textfsm=True
を付加するのがポイントです。
from netmiko import Netmiko import os from pprint import pprint # 接続情報の定義 ios1 = { "host": "10.10.20.48", "device_type": "cisco_ios", "username": "testuser", "password": "testpass" } # テンプレートファイル格納ディレクトリを環境変数に指定 # デフォルトは ./ntc-templates/templates os.environ["NET_TEXTFSM"] = "/home/npstudy/templates" # 接続 net_connect = Netmiko(**ios1) # コマンド実行、パース result = net_connect.send_command("show ip interface brief", use_textfsm=True) # 表示 pprint(result)
実行結果
以下のようにパースされます。
[{'intf': 'GigabitEthernet1', 'ipaddr': '10.10.20.48', 'proto': 'up', 'status': 'up'}, {'intf': 'GigabitEthernet2', 'ipaddr': 'unassigned', 'proto': 'down', 'status': 'administratively down'}, {'intf': 'GigabitEthernet3', 'ipaddr': 'unassigned', 'proto': 'down', 'status': 'administratively down'}, {'intf': 'Loopback0', 'ipaddr': 'unassigned', 'proto': 'up', 'status': 'up'}, {'intf': 'Loopback1', 'ipaddr': 'unassigned', 'proto': 'up', 'status': 'up'}, {'intf': 'Loopback2', 'ipaddr': 'unassigned', 'proto': 'up', 'status': 'up'}]
3.4. TextFMS + Ansible 利用例
今度は Ansible と組み合わせた利用例です。
Ansible から TextFSM のテンプレートを利用して、パースするには Ansible 2.4 で追加された parse_cli_textfsmフィルターを利用します。
このフィルターを利用するには、あらかじめ TextFSM をインストールしておく必要があります。
$ pip install textfsm
Playbook
- hosts: ios gather_facts: no vars: template_file: "./templates/cisco_ios_show_ip_interface_brief.template" tasks: - name: show ios_command: commands: - show ip interface brief register: result - name: show parsed result debug: msg: "{{ result.stdout[0] | parse_cli_textfsm(template_file) }}"
実行結果
以下のようにパースされます。
ok: [ios1] => { "msg": [ { "INTF": "GigabitEthernet1", "IPADDR": "10.10.20.48", "PROTO": "up", "STATUS": "up" }, { "INTF": "GigabitEthernet2", "IPADDR": "unassigned", "PROTO": "down", "STATUS": "administratively down" }, { "INTF": "GigabitEthernet3", "IPADDR": "unassigned", "PROTO": "down", "STATUS": "administratively down" }, { "INTF": "Loopback0", "IPADDR": "unassigned", "PROTO": "up", "STATUS": "up" }, { "INTF": "Loopback1", "IPADDR": "unassigned", "PROTO": "up", "STATUS": "up" }, { "INTF": "Loopback2", "IPADDR": "unassigned", "PROTO": "up", "STATUS": "up" } ] }
■ 4. Genie Parser
4.1. Genie Parser とは
Genie Parser は、Cisco 機器に強いパーサーです。
- Python製テストフレームワーク/ライブラリ「pyATS/Genie」内のパーサー
- Cisco 社が開発をはじめたもの
- Cisco 機器への対応が充実
- プラットフォーム数 8(Cisco 7、Juniper 1)
- コマンド数 約 1300
- (2019/09/03現在)
pip install genie
でインストール
4.2. Genie Parser 単体の利用例
Genie にはネットワーク機器への接続機能があります。testdeb と呼ばれる接続情報を YMAL で定義し、コードから呼び出します。
コード
- testbed (接続情報)ファイル
testbed: name: "testbed1" devices: csr1000v: type: catalyst platform: iosxe os: "iosxe" alias: "ios1" tacacs: login_prompt: "login:" password_prompt: "Password:" username: testuser passwords: tacacs: testpass # enable: testpass # line: testpass connections: ssh: protocol: ssh ip: "10.10.20.48"
- コード
from genie.conf import Genie from pprint import pprint # testbed (接続情報)を読み込む testbed = Genie.init("./testbed.yml") device = testbed.devices["csr1000v"] # 機器への接続 device.connect() # コマンド実行、パース output = device.parse("show ip interface brief") # 表示 pprint(output)
実行結果
以下のようにパースされます。最初はログインや ter len 0
などのログが流れます。(device.connect(log_stdout=False)
とすることで非表示することも可能。@ccieojisanさん、情報ありがとうございます)
[2019-09-04 15:30:20,523] +++ csr1000v logfile /tmp/csr1000v-cli-20190904T153020523.log +++ [2019-09-04 15:30:20,524] +++ Unicon plugin iosxe +++ Password: [2019-09-04 15:30:28,022] +++ connection to spawn: ssh -l developer 10.10.20.48, id: 4458345528 +++ [2019-09-04 15:30:28,023] connection to csr1000v ...(略)... {'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': 'unassigned', 'method': 'NVRAM', 'protocol': 'down', 'status': 'administratively down'}, 'GigabitEthernet3': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'NVRAM', 'protocol': 'down', 'status': 'administratively down'}, 'Loopback0': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'unset', 'protocol': 'up', 'status': 'up'}, 'Loopback1': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'unset', 'protocol': 'up', 'status': 'up'}, 'Loopback2': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'unset', 'protocol': 'up', 'status': 'up'}}}
4.3. Genie Parser + Netmiko 利用例
Netmiko 2.4.1 で Genie Parser との連携機能が追加されました。
Netmiko から Genie Parser を利用するには、あらかじめンストールしておく必要があります。
$ pip install genie
コード
send_command
の引数に、use_genie=True
を付加するのがポイントです。
from netmiko import Netmiko import os from pprint import pprint # 接続情報の定義 ios1 = { "host": "10.10.20.48", "device_type": "cisco_ios", "username": "testuser", "password": "testpass" } # 接続 net_connect = Netmiko(**ios1) # コマンド実行、パース result = net_connect.send_command("show ip interface brief", use_genie=True) # 表示 pprint(result)
実行結果
以下のようにパースされます。
{'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': 'unassigned', 'method': 'NVRAM', 'protocol': 'down', 'status': 'administratively down'}, 'GigabitEthernet3': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'NVRAM', 'protocol': 'down', 'status': 'administratively down'}, 'Loopback0': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'unset', 'protocol': 'up', 'status': 'up'}, 'Loopback1': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'unset', 'protocol': 'up', 'status': 'up'}, 'Loopback2': {'interface_is_ok': 'YES', 'ip_address': 'unassigned', 'method': 'unset', 'protocol': 'up', 'status': 'up'}}}
4.4. Genie Parser + Ansible 利用例
今度は Ansible と組み合わせた利用例です。
Ansible から Genie Parser を利用するには、clay584.parse_genie
というロールが必要です。Genie Parser に加えて、本ロールもあらかじめインストールしておきます。
$ pip install genie $ ansible-galaxy install clay584.parse_genie
なお、本ロールは Ansible 2.7 以上が必要です。
Playbook
- hosts: ios gather_facts: no vars: # 実行 show コマンドの定義 show: show ip interface brief tasks: - name: Read in parse_genie role include_role: # clay584.parse_genie ロールを利用 name: clay584.parse_genie - name: ios command test ios_command: # コマンド実行 commands: - "{{ show }}" register: result # 実行結果が変数 result に入る - name: parsed result # parse_genie フィルターでパース debug: msg: "{{ result.stdout[0] | parse_genie(command=show, os='iosxe') }}"
実行結果
以下のようにパースされます。
ok: [ios1] => { "msg": { “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": "unassigned", "method": "NVRAM", "protocol": "down", "status": "administratively down" }, "GigabitEthernet3": { "interface_is_ok": "YES", "ip_address": "unassigned", "method": "NVRAM", "protocol": "down", "status": "administratively down" }, "Loopback0": { "interface_is_ok": "YES", "ip_address": "unassigned", "method": "unset", "protocol": "up", "status": "up" }, "Loopback1": { "interface_is_ok": "YES", "ip_address": "unassigned", "method": "unset", "protocol": "up", "status": "up" }, "Loopback2": { "interface_is_ok": "YES", "ip_address": "unassigned", "method": "unset", "protocol": "up", "status": "up" } } } }
■ 5. まとめ
- TextFSM: カスタマイズが容易
- Genie Parser: Cisco 機器に強い
- 使い分け(個人的主観)
- Cisco の機器が対象なら Genie Parser
- それ以外、容易にカスタマイズしたいなら TextFSM
- Netmiko や Ansible との連携も便利
参考資料
- TextFSM
- Genie Parser
- ansible-network.network-engine ロール(今回ご紹介できなかったもの)