2019/09/05 に、ネットワークプログラマビリティ勉強会 #18 で、「show コマンド結果をパースする方法あれこれ 」という発表をさせていだきました。サンプルコードが中心だったため、コピペしやすいように、ブログ記事として書きおこします。
[2020/09/25 追記]
ansible.netcommon
collection の 1.2.0 で、cli_parse
モジュール が追加されました。コマンドの実行と本記事で紹介するパーサー TextFSM、ntc-templates、pyATS などとの連携してパースデータを取得できます。
[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)
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"
}
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 機器に強いパーサーです。
4.2. Genie Parser 単体の利用例
Genie にはネットワーク機器への接続機能があります。testdeb と呼ばれる接続情報を YMAL で定義し、コードから呼び出します。
コード
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
connections :
ssh :
protocol : ssh
ip : "10.10.20.48"
from genie.conf import Genie
from pprint import pprint
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 ip interface brief
tasks :
- name : Read in parse_genie role
include_role :
name : clay584.parse_genie
- name: ios command test
ios_command :
commands :
- "{{ show }}"
register : result
- name: parsed result
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
ansible-network.network-engine ロール(今回ご紹介できなかったもの)