はじめに
Ciscoが公開しているPythonベースのテストフレームワーク/フィーチャーライブラリー「pyATS/Genie」の内、Genie ParserとOpsを使ってみた結果をメモしておきます。
1-1. Genie Parserとは
NW機器のshowコマンド結果を構文解析し、テキストデータを「xの値はy」のようにプログラムが扱いやすい形式に変換します。2019年7月時点で1000を超えるパーサーが存在し、Cisco IOS、IOS-XE、IOS-XR、NX-OS、ASA、Junosの各種showコマンドに対応しています。
参考URL:
Parser一覧
GitHub - genieparser
1-2. Pythonコード例
Python環境のセットアップは前回の記事を参照願います。(Testbedも用意する必要あり。)
以下はCisco CSR1000Vのshow interfaces GigabitEthernet1をパースする例です。
from genie.conf import Genie
from pprint import pprint
testbed = Genie.init('testbed_devnet3.yaml')   # Testbedを指定
device = testbed.devices['csr1000v-1']   # ホスト名を指定
device.connect()
output = device.parse('show interfaces GigabitEthernet1')
pprint(output)
1-3. 実行結果
$ python genie8-1.py
# 途中の実行ログは省略
{'GigabitEthernet1': {'arp_timeout': '04:00:00',
                      'arp_type': 'arpa',
                      'auto_negotiate': True,
                      'bandwidth': 1000000,
                      'counters': {'in_broadcast_pkts': 0,
                                   'in_crc_errors': 0,
                                   'in_errors': 0,
                                   'in_frame': 0,
                                   'in_giants': 0,
                                   'in_ignored': 0,
                                   'in_mac_pause_frames': 0,
                                   'in_multicast_pkts': 0,
                                   'in_no_buffer': 0,
                                   'in_octets': 3984819,
                                   'in_overrun': 0,
                                   'in_pkts': 41999,
                                   'in_runts': 0,
                                   'in_throttles': 0,
                                   'in_watchdog': 0,
                                   'last_clear': 'never',
                                   'out_babble': 0,
                                   'out_buffer_failure': 0,
                                   'out_buffers_swapped': 0,
                                   'out_collision': 0,
                                   'out_deferred': 0,
                                   'out_errors': 0,
                                   'out_interface_resets': 0,
                                   'out_late_collision': 0,
                                   'out_lost_carrier': 0,
                                   'out_mac_pause_frames': 0,
                                   'out_no_carrier': 0,
                                   'out_octets': 414844,
                                   'out_pkts': 2545,
                                   'out_underruns': 0,
                                   'out_unknown_protocl_drops': 168,
                                   'rate': {'in_rate': 3000,
                                            'in_rate_pkts': 5,
                                            'load_interval': 300,
                                            'out_rate': 0,
                                            'out_rate_pkts': 0}},
                      'delay': 10,
                      'description': "MANAGEMENT INTERFACE - DON'T TOUCH ME",
                      'duplex_mode': 'full',
                      'enabled': True,
                      'encapsulations': {'encapsulation': 'arpa'},
                      'flow_control': {'receive': False, 'send': False},
                      'ipv4': {'10.10.20.48/24': {'ip': '10.10.20.48',
                                                  'prefix_length': '24'}},
                      'keepalive': 10,
                      'last_input': '00:00:00',
                      'last_output': '00:00:00',
                      'line_protocol': 'up',
                      'link_type': 'auto',
                      'mac_address': '0050.56ac.6cb1',
                      'media_type': 'Virtual',
                      'mtu': 1500,
                      'oper_status': 'up',
                      'output_hang': 'never',
                      'phys_address': '0050.56ac.6cb1',
                      'port_channel': {'port_channel_member': False},
                      'port_speed': '1000',
                      'queues': {'input_queue_drops': 0,
                                 'input_queue_flushes': 0,
                                 'input_queue_max': 375,
                                 'input_queue_size': 0,
                                 'output_queue_max': 40,
                                 'output_queue_size': 0,
                                 'queue_strategy': 'fifo',
                                 'total_output_drop': 0},
                      'reliability': '255/255',
                      'rxload': '1/255',
                      'txload': '1/255',
                      'type': 'CSR vNIC'}}
2-1. Genie Opsとは
デバイスの設定情報やオペレーション状態をフィーチャー単位(1つのshowコマンドではなく、例えばinterfaceに関連する複数のshowコマンド)で取得するPythonオブジェクトです。
以前ご紹介したGenie Confと同様、OSによるコマンドの違いを意識することなく、情報の取得が可能です。例えば、IOS-XEでinterface関連の情報を取得する場合、Genie Opsのlearnメソッドにて、以下のCLIコマンドが実行されます。
- show interfaces
- show vrf
- show interfaces accounting
- show ip interface
- show ipv6 interface
他にも、routing、bgp、ospf、mcast、acl、vlan、stp、platform等のフィーチャーをサポートしています(詳細は下記GitHubを参照)。
出力結果(info)は、Parserでパースしたものが出力されます。
参考URL:
Ops User Guide
DevNet Workshop (DEVWKS-2601)- pyATS Genie Ops and Parsers
GitHub - Genie Ops
2-2. Pythonコード例①(interface)
interface関連のフィーチャーを取得する例です。learnメソッド内の引数で、interfaceを指定することで、関連情報を取得できます。
from genie.conf import Genie
from pprint import pprint
testbed = Genie.init('testbed_devnet3.yaml')
device = testbed.devices['csr1000v-1']
device.connect()
interface = device.learn('interface')
pprint(interface.info)
2-3. 実行結果①(interface)
Genie Parserの結果と比較すると、accounting関連の情報が追加されています。また、interfaceのステータス情報は、エラーカウント内訳などが表示されずコンパクトになっています。
$ python genie8-2.py
# 途中の実行ログは省略
{'': {'accounting': {'arp': {'chars_in': 480,
                             'chars_out': 540,
                             'pkts_in': 8,
                             'pkts_out': 9},
                     'ip': {'chars_in': 4689480,
                            'chars_out': 566379,
                            'pkts_in': 50575,
                            'pkts_out': 3530},
                     'other': {'chars_in': 32820,
                               'chars_out': 634,
                               'pkts_in': 428,
                               'pkts_out': 10}},
      'switchport_enable': False},
 'GigabitEthernet1': {'auto_negotiate': True,
                      'bandwidth': 1000000,
                      'counters': {'in_broadcast_pkts': 0,
                                   'in_crc_errors': 0,
                                   'in_errors': 0,
                                   'in_mac_pause_frames': 0,
                                   'in_multicast_pkts': 0,
                                   'in_octets': 4873797,
                                   'in_pkts': 51634,
                                   'last_clear': 'never',
                                   'out_errors': 0,
                                   'out_mac_pause_frames': 0,
                                   'out_octets': 561534,
                                   'out_pkts': 3504,
                                   'rate': {'in_rate': 3000,
                                            'in_rate_pkts': 4,
                                            'load_interval': 300,
                                            'out_rate': 0,
                                            'out_rate_pkts': 0}},
                      'delay': 10,
                      'description': "MANAGEMENT INTERFACE - DON'T TOUCH ME",
                      'duplex_mode': 'full',
                      'enabled': True,
                      'encapsulation': {'encapsulation': 'arpa'},
                      'flow_control': {'receive': False, 'send': False},
                      'ipv4': {'10.10.20.48/24': {'ip': '10.10.20.48',
                                                  'prefix_length': '24',
                                                  'secondary': False}},
                      'mac_address': '0050.56ac.6cb1',
                      'mtu': 1500,
                      'oper_status': 'up',
                      'phys_address': '0050.56ac.6cb1',
                      'port_channel': {'port_channel_member': False},
                      'port_speed': '1000',
                      'switchport_enable': False,
                      'type': 'CSR vNIC'},
# GigabitEthernet2,3の結果は省略
                     }
2-4. Pythonコード例②(interface一部表示)
出力結果は辞書形式のため、特定のキーの値のみを表示できます。例えば、コード例①を以下の通り書き換えることで、GigabitEthernet1のオペレーション状態(up/down)のみ表示できます。
pprint(interface.info['GigabitEthernet1']['oper_status'])
2-5. 実行結果②(interface一部表示)
'up'
2-6. Pythonコード例③(interface一部取得)
コード例②は情報を一式取得した後、表示の段階で絞っていましたが、取得段階で対象を絞ることも可能です。例えば、各IFのBandwidthだけ取得できれば良い場合、learnメソッドの引数に、以下の通りattributesを指定します。
interface = device.learn('interface', attributes=['info[(.*)][bandwidth]'])
2-7. 実行結果③(interface一部取得)
実行中のログは省略していますが、取得コマンドはshow interfacesとshow vrfの2つに絞られていました。実行時間を短くしたい場合は、こちらの方が良いかも知れません。
{'GigabitEthernet1': {'bandwidth': 1000000, 'switchport_enable': False},
 'GigabitEthernet2': {'bandwidth': 1000000, 'switchport_enable': False},
 'GigabitEthernet3': {'bandwidth': 1000000, 'switchport_enable': False}}
2-8. Pythonコード例④(routing)
learnメソッド内の引数を、routingを変更することでrouting関連情報を取得できます。
from genie.conf import Genie
from pprint import pprint
testbed = Genie.init('testbed_devnet3.yaml')
device = testbed.devices['csr1000v-1']
device.connect()
routing = device.learn('routing')
pprint(routing.info)
2-9. 実行結果④(routing)
$ python genie8-4.py
# 途中の実行ログは省略
{'vrf': {'default': {'address_family': {'ipv4': {'routes': {'0.0.0.0/0': {'active': True,
                                                                          'metric': 0,
                                                                          'next_hop': {'next_hop_list': {1: {'index': 1,
                                                                                                             'next_hop': '10.10.20.254',
                                                                                                             'outgoing_interface': 'GigabitEthernet1'}}},
                                                                          'route': '0.0.0.0/0',
                                                                          'route_preference': 1,
                                                                          'source_protocol': 'static',
                                                                          'source_protocol_codes': 'S*'},
                                                            '10.10.20.0/24': {'active': True,
                                                                              'next_hop': {'outgoing_interface': {'GigabitEthernet1': {'outgoing_interface': 'GigabitEthernet1'}}},
                                                                              'route': '10.10.20.0/24',
                                                                              'source_protocol': 'connected',
                                                                              'source_protocol_codes': 'C'},
                                                            '10.10.20.48/32': {'active': True,
                                                                               'next_hop': {'outgoing_interface': {'GigabitEthernet1': {'outgoing_interface': 'GigabitEthernet1'}}},
                                                                               'route': '10.10.20.48/32',
                                                                               'source_protocol': 'local',
                                                                               'source_protocol_codes': 'L'}}}}}}}
所感
他のパーサーとして、textFSMとNTC-templateの組み合わせもあります。以下記事にも記載した通り、それぞれ良い所があるので、今後使い分けて行きたいです。