pyATS/TextFSMのテンプレートを使ってパースする

pyATS には Genie Parser がありますが、TextFSMntc-templates は昔からあり、知っている人やネットワークの自動化で既に使っている人もいるかと思います

TextFSM は単純にアウトプットを渡すだけなので、pyATS でネットワーク機器へ接続して出力をゲットすれば、簡単に使うことができます。

pyATS と TextFSM をインストール

genie を指定すると pyats と genie の両方インストールされます。

(pyats)$ pip install genie textfsm

ntc-template の取得

show コマンドのアウトプットを TextFSM でパースするために、ntc-template を git で取得します。

(pyats)$ cd $VIRTUAL_ENV
(pyats)$ git clone https://github.com/networktocode/ntc-templates.git
Cloning into 'ntc-templates'...
remote: Enumerating objects: 47, done.
remote: Counting objects: 100% (47/47), done.
remote: Compressing objects: 100% (43/43), done.
remote: Total 6762 (delta 17), reused 24 (delta 4), pack-reused 6715
Receiving objects: 100% (6762/6762), 1.70 MiB | 2.92 MiB/s, done.
Resolving deltas: 100% (3692/3692), done.

pyATS で ntc-template を使う例

それでは genie shell を使って、pyATS でネットワーク機器に接続し TextFSM でパースするまでの例です。

TextFSM のテンプレートを開いて、pyATS で取得した output を渡すというのを行なっています。

$ genie shell --testbed-file tb.yaml
Welcome to Genie Interactive Shell
==================================
Python 3.7.6 (default, Jan  3 2020, 14:59:27)
[Clang 10.0.1 (clang-110.1.46.4)]

>>> from genie.testbed import load
>>> testbed = load('tb.yaml')
-------------------------------------------------------------------------------
>>> import textfsm
>>> template = open('ntc-templates/templates/cisco_ios_show_ip_interface_brief.textfsm', 'r')
>>> with open('ntc-templates/templates/cisco_ios_show_ip_interface_brief.textfsm', 'r') as f:
...     template = textfsm.TextFSM(f)
...
>>> dev = testbed.devices['R1_xe']
>>> dev.connect(via='cli')
(snip)
>>> output = dev.execute('show ip interface brief')
[2020-01-11 09:58:16,986] +++ R1_xe: executing command 'show ip interface brief' +++
show ip interface brief
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       172.16.1.91     YES TFTP   up                    up
GigabitEthernet2       unassigned      YES unset  up                    up
GigabitEthernet2.90    10.12.90.1      YES TFTP   up                    up
GigabitEthernet2.110   10.12.110.1     YES TFTP   up                    up
GigabitEthernet2.115   10.12.115.1     YES TFTP   up                    up
GigabitEthernet2.120   10.12.120.1     YES TFTP   up                    up
GigabitEthernet2.390   10.12.90.1      YES TFTP   up                    up
GigabitEthernet2.410   10.12.110.1     YES TFTP   up                    up
GigabitEthernet2.415   10.12.115.1     YES TFTP   up                    up
GigabitEthernet2.420   10.12.120.1     YES TFTP   up                    up
GigabitEthernet3       unassigned      YES unset  up                    up
GigabitEthernet3.90    10.13.90.1      YES TFTP   up                    up
GigabitEthernet3.110   10.13.110.1     YES TFTP   up                    up
GigabitEthernet3.115   10.13.115.1     YES TFTP   up                    up
GigabitEthernet3.120   10.13.120.1     YES TFTP   up                    up
GigabitEthernet3.390   10.13.90.1      YES TFTP   up                    up
GigabitEthernet3.410   10.13.110.1     YES TFTP   up                    up
GigabitEthernet3.415   10.13.115.1     YES TFTP   up                    up
GigabitEthernet3.420   10.13.120.1     YES TFTP   up                    up
GigabitEthernet4       unassigned      YES unset  up                    up
GigabitEthernet5       unassigned      YES unset  up                    up
GigabitEthernet6       unassigned      YES unset  up                    up
GigabitEthernet7       unassigned      YES unset  up                    up
Loopback0              10.4.1.1        YES TFTP   up                    up
Loopback300            10.4.1.1        YES TFTP   up                    up
Port-channel12         unassignd       YES unset  up                    up
Port-channel13         unassigned      YES unset  up                    up
Tunnel0                unassigned      YES unset  up                    up
Tunnel1                unassigned      YES unset  up                    up
Tunnel2                10.4.1.1        YES unset  up                    up
Tunnel3                10.4.1.1        YES unset  up                    up
Tunnel4                10.4.1.1        YES unset  up                    up
Tunnel5                10.4.1.1        YES unset  up                    up
Tunnel6                10.4.1.1        YES unset  up                    up
Tunnel7                10.4.1.1        YES unset  up                    up
Tunnel8                unassigned      YES unset  up                    up
Tunnel9                unassigned      YES unset  up                    up
R1_xe#
>>>
>>> parsed = template.ParseText(output)
>>> import json
>>> print(json.dumps(parsed, indent=2, sort_keys=2))
[
  [
    "GigabitEthernet1",
    "172.16.1.91",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2",
    "unassigned",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.90",
    "10.12.90.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.110",
    "10.12.110.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.115",
    "10.12.115.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.120",
    "10.12.120.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.390",
    "10.12.90.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.410",
    "10.12.110.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.415",
    "10.12.115.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet2.420",
    "10.12.120.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3",
    "unassigned",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.90",
    "10.13.90.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.110",
    "10.13.110.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.115",
    "10.13.115.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.120",
    "10.13.120.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.390",
    "10.13.90.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.410",
    "10.13.110.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.415",
    "10.13.115.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet3.420",
    "10.13.120.1",
    "up",
    "up"
  ],
  [
    "GigabitEthernet4",
    "unassigned",
    "up",
    "up"
  ],
  [
    "GigabitEthernet5",
    "unassigned",
    "up",
    "up"
  ],
  [
    "GigabitEthernet6",
    "unassigned",
    "up",
    "up"
  ],
  [
    "GigabitEthernet7",
    "unassigned",
    "up",
    "up"
  ],
  [
    "Loopback0",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Loopback300",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Port-channel12",
    "unassigned",
    "up",
    "up"
  ],
  [
    "Port-channel13",
    "unassigned",
    "up",
    "up"
  ],
  [
    "Tunnel0",
    "unassigned",
    "up",
    "up"
  ],
  [
    "Tunnel1",
    "unassigned",
    "up",
    "up"
  ],
  [
    "Tunnel2",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Tunnel3",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Tunnel4",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Tunnel5",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Tunnel6",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Tunnel7",
    "10.4.1.1",
    "up",
    "up"
  ],
  [
    "Tunnel8",
    "unassigned",
    "up",
    "up"
  ],
  [
    "Tunnel9",
    "unassigned",
    "up",
    "up"
  ]
]
>>>

上の結果を見ると全ての項目がパースされてはいませんが、主要な情報はパースされています。また、各インタフェースの情報はリストとして返ってきていることが分かります。

おまけ(genie parser編)

おまけとして genie parser のパース出力例も比較で載せておきます。ntc-templates とは異なり、全ての項目がパースされていることが分かります。

>>> parsed = dev.parse('show ip interface brief')
[2020-01-11 10:24:31,256] +++ R1_xe: executing command 'show ip interface brief' +++
show ip interface brief
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       172.16.1.91     YES TFTP   up                    up
GigabitEthernet2       unassigned      YES unset  up                    up
GigabitEthernet2.90    10.12.90.1      YES TFTP   up                    up
GigabitEthernet2.110   10.12.110.1     YES TFTP   up                    up
GigabitEthernet2.115   10.12.115.1     YES TFTP   up                    up
GigabitEthernet2.120   10.12.120.1     YES TFTP   up                    up
GigabitEthernet2.390   10.12.90.1      YES TFTP   up                    up
GigabitEthernet2.410   10.12.110.1     YES TFTP   up                    up
GigabitEthernet2.415   10.12.115.1     YES TFTP   up                    up
GigabitEthernet2.420   10.12.120.1     YES TFTP   up                    up
GigabitEthernet3       unassigned      YES unset  up                    up
GigabitEthernet3.90    10.13.90.1      YES TFTP   up                    up
GigabitEthernet3.110   10.13.110.1     YES TFTP   up                    up
GigabitEthernet3.115   10.13.115.1     YES TFTP   up                    up
GigabitEthernet3.120   10.13.120.1     YES TFTP   up                    up
GigabitEthernet3.390   10.13.90.1      YES TFTP   up                    up
GigabitEthernet3.410   10.13.110.1     YES TFTP   up                    up
GigabitEthernet3.415   10.13.115.1     YES TFTP   up                    up
GigabitEthernet3.420   10.13.120.1     YES TFTP   up                    up
GigabitEthernet4       unassigned      YES unset  up                    up
GigabitEthernet5       unassigned      YES unset  up                    up
GigabitEthernet6       unassigned      YES unset  up                    up
GigabitEthernet7       unassigned      YES unset  up                    up
Loopback0              10.4.1.1        YES TFTP   up                    up
Loopback300            10.4.1.1        YES TFTP   up                    up
Port-channel12         unassigned      YES unset  up                    up
Port-channel13         unassigned      YES unset  up                    up
Tunnel0                unassigned      YES unset  up                    up
Tunnel1                unassigned      YES unset  up                    up
Tunnel2                10.4.1.1        YES unset  up                    up
Tunnel3                10.4.1.1        YES unset  up                    up
Tunnel4                10.4.1.1        YES unset  up                    up
Tunnel5                10.4.1.1        YES unset  up                    up
Tunnel6                10.4.1.1        YES unset  up                    up
Tunnel7                10.4.1.1        YES unset  up                    up
Tunnel8                unassigned      YES unset  up                    up
Tunnel9                unassigned      YES unset  up                    up
R1_xe#
>>> print(json.dumps(parsed, indent=2, sort_keys=True))
{
  "interface": {
    "GigabitEthernet1": {
      "interface_is_ok": "YES",
      "ip_address": "172.16.1.91",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.110": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.110.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.115": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.115.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.120": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.120.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.390": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.90.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.410": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.110.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.415": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.115.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.420": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.120.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet2.90": {
      "interface_is_ok": "YES",
      "ip_address": "10.12.90.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.110": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.110.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.115": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.115.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.120": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.120.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.390": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.90.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.410": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.110.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.415": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.115.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.420": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.120.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet3.90": {
      "interface_is_ok": "YES",
      "ip_address": "10.13.90.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet4": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet5": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet6": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "GigabitEthernet7": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Loopback0": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "Loopback300": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "TFTP",
      "protocol": "up",
      "status": "up"
    },
    "Port-channel12": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Port-channel13": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel0": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel1": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel2": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel3": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel4": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel5": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel6": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel7": {
      "interface_is_ok": "YES",
      "ip_address": "10.4.1.1",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel8": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    },
    "Tunnel9": {
      "interface_is_ok": "YES",
      "ip_address": "unassigned",
      "method": "unset",
      "protocol": "up",
      "status": "up"
    }
  }
}
>>>

Genie Parser のパースデータでは、interface をキーとして階層化されているので、interface 名を知っている場合には下記のように簡単にアクセスできるというメリットがあります。

>>> output['interface']['GigabitEthernet1']
{'ip_address': '172.16.1.91', 'interface_is_ok': 'YES', 'method': 'TFTP', 'status': 'up', 'protocol': 'up'}

ntc-templates のようにリストになっていると対象のインタフェースを見つけるまでループする必要が生じてしまいます。そのため、Genie Parser では基本的にリストは使わず、ネストされた構造体になっています。

スポンサーリンク