pyATS/Genie: Genie API で遊んでみる with Robot Framework

(この記事はネットワーク自動化 Advent Calendar 2019 15日目として書いています)

Calendar page for Qiita Advent Calendar 2019 regarding ネットワーク自動化.

今回は pyATS/Genie の Genie API を紹介します。後半では Robot Framework と組み合わせた使い方を紹介しています。

Genie API とは?

pyATS/Genie を少しでも試したことのなる人には、pyATS/Genie では主に python コードを書いてネットワークの自動化をするというのは理解いただいているかと思います。

そのコーディングを手助けするために用意されているのが Genie API 、要はコーディングが楽になるライブラリです。

まずはどんなものがあるか見てみましょう。

The Genie (pyATS Library) Feature Browser. Easily find the right Trigger, Verification, Parser, API, or Model.

上の画面の左側のボタンにある APIS をクリックします。

すると既に用意されている API が一覧で表示され、OS 毎にフィルターしたり、キーワードを検索ボックスに入れて、例えば bgp とか ospf とかのキーワードで探すことができます。

Genie API の名前をクリックすると、API に渡すパラメータや戻り値などが確認できます。下記は get_ospf_neighbors という API で OSPFネイバーのリストが取得できるということが情報から分かります。

現時点(2019年12月)で 500 近くの API が既に存在します。また、Genie ライブラリの抽象化は API にも引き継がれており、同じ API で異なるプラットフォームを同じように設定したり、情報を取得することができます。

ネットワーク構成と testbed.yaml

それでは今回 Genie API を試すネットワーク構成です。 IOSXE(R1_xe)、IOS-XR(R2_xr)、NX-OS(R3_nx)を VIRL 上で起動して使います。

全ルータで OSPF が Area0 で設定されており、接続インタフェースとループバック(10.1.1.1/32, 10.2.2.2/32, 10.3.3.3/32)が OSPF 経路として見える状況です。

testbed.yaml は下記になります。

devices:
  R1_xe:
    alias: uut
    connections:
      defaults:
        via: ssh
      ssh:
        ip: 172.16.1.228
        protocol: ssh
        proxy: jump_host
    credentials:
      default:
        password: Cisc0123
        username: cisco
      enable:
        password: Cisc0123
    os: iosxe
    platform: iosxe
    type: CSR1000v
  R2_xr:
    alias: R2_xr
    connections:
      defaults:
        via: ssh
      ssh:
        ip: 172.16.1.229
        protocol: ssh
        proxy: jump_host
    credentials:
      default:
        password: Cisc0123
        username: admin
      enable:
        password: Cisc0123
    os: iosxr
    platform: iosxrv9k
    type: IOS XRv 9000
  R3_nx:
    alias: R3_nx
    connections:
      defaults:
        via: ssh
      ssh:
        ip: 172.16.1.230
        protocol: ssh
        proxy: jump_host
    credentials:
      default:
        password: Cisc0123
        username: admin
      enable:
        password: Cisc0123
    os: nxos
    platform: n9kv
    type: NX-OSv 9000
  jump_host:
    connections:
      cli:
        ip: 172.25.192.134
        port: 22
        protocol: ssh
    credentials:
      default:
        password: VIRL
        username: virl
    os: linux
    type: linux

Genie Shell から Genie API を使ってみる

それではお手軽に python コードが試せる Genie Shell から Genie API を試してみます。
※既に python virtualenv の作成、pyATS/Genie はインストール済みと想定しています。

genie shell コマンドに –testbed-file オプションで testbed.yaml を渡すだけで、testbed.yaml を読み込んだ testbed オブジェクトが用意されて python interpreter が起動されます。

(genie)$ genie shell --testbed-file testbed.yaml
Welcome to Genie Interactive Shell
==================================
Python 3.7.3 (default, Jun  5 2019, 11:26:12)
[Clang 10.0.1 (clang-1001.0.46.4)]

>>> from genie.testbed import load
>>> testbed = load('testbed.yaml')
-------------------------------------------------------------------------------
>>>

機器への接続 <device>.connect()

それではデバイス R1_xe を xe として testbed オブジェクトからセットします。

>>> testbed
<Testbed object 'testbed' at 0x10dc1e0f0>
>>> xe = testbed.devices['R1_xe']
>>> xe
<Device R1_xe at 0x10dc0d470>

xe がデバイスオブジェクトになります。このデバイスへの接続には connect() を下記のように実行します。

>>> xe.connect()
[2019-12-13 20:52:14,493] +++ R1_xe logfile /tmp/R1_xe-cli-20191213T205214492.log +++
[2019-12-13 20:52:14,494] +++ Unicon plugin iosxe +++
[2019-12-13 20:52:14,498] connection via proxy jump_host
[2019-12-13 20:52:14,502] +++ connection to spawn: ssh -l virl 172.25.192.134 -p 22, id: 4526263432 +++
[2019-12-13 20:52:14,503] connection to jump_host
virl@172.25.192.134's password:
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-116-generic x86_64)

  System information as of Sat Dec 14 01:52:15 GMT 2019

  System load:     21.22               IP address for eth0:    172.25.192.134
  Usage of /:      33.6% of 191.02GB   IP address for br3:     172.16.3.250
  Memory usage:    51%                 IP address for br1:     172.16.1.250
  Swap usage:      2%                  IP address for br2:     172.16.2.250
  Processes:       1013                IP address for br4:     172.16.10.250
  Users logged in: 0                   IP address for docker0: 172.17.0.1

  Graph this data and manage this system at:
    https://landscape.canonical.com/
New release '18.04.3 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Last login: Sat Dec 14 01:41:43 2019 from 10.24.58.107
virl@asg-ucs4-virl:~$
[2019-12-13 20:52:16,869] +++ initializing handle +++
[2019-12-13 20:52:17,011] connection to R1_xe
ssh -l cisco 172.16.1.228
Password:



R1_xe>
[2019-12-13 20:52:17,816] +++ initializing handle +++
enable
Password:
R1_xe#
[2019-12-13 20:52:18,102] +++ R1_xe: executing command 'term length 0' +++
term length 0
R1_xe#
[2019-12-13 20:52:18,364] +++ R1_xe: executing command 'term width 0' +++
term width 0
R1_xe#
[2019-12-13 20:52:18,621] +++ R1_xe: executing command 'show version' +++
show version
Cisco IOS XE Software, Version 16.09.01
Cisco IOS Software [Fuji], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.9.1, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2018 by Cisco Systems, Inc.
Compiled Tue 17-Jul-18 16:57 by mcpre


Cisco IOS-XE software, Copyright (c) 2005-2018 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

R1_xe uptime is 6 days, 11 hours, 25 minutes
Uptime for this control processor is 6 days, 11 hours, 26 minutes
System returned to ROM by reload
System image file is "bootflash:packages.conf"
Last reload reason: Reload Command



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:


If you require further assistance please contact us by sending email to
export@cisco.com.

License Level: ax
License Type: Default. No valid license found.
Next reload license Level: ax

cisco CSR1000V (VXE) processor (revision VXE) with 1217428K/3075K bytes of memory.
Processor board ID 9ZFZVAFKW4L
2 Gigabit Ethernet interfaces
32768K bytes of non-volatile configuration memory.
3018864K bytes of physical memory.
7774207K bytes of virtual hard disk at bootflash:.
0K bytes of WebUI ODM Files at webui:.

Configuration register is 0x2102

R1_xe#
[2019-12-13 20:52:18,896] +++ R1_xe: config +++
config term
Enter configuration commands, one per line.  End with CNTL/Z.
R1_xe(config)#no logging console
R1_xe(config)#line console 0
R1_xe(config-line)#exec-timeout 0
R1_xe(config-line)#end
R1_xe#
>>>

ここまでで、機器への接続ができましたので、Genie API を実行する準備ができました。

Genie API の実行 <device>.api.<api_name>()

Genie API はネットワークエンジニアに直感的に分かりやすく使えるようにデバイス中心に実行することができます。例えば、上記で見た get_ospf_neighbors はデバイスオブジェクトに対して xe.api. とした後に続けて下記のように実行できます。

>>> xe.api.get_ospf_neighbors()
Getting all ospf neighbors
[2019-12-13 20:55:55,018] +++ R1_xe: executing command 'show ip ospf neighbor' +++
show ip ospf neighbor

Neighbor ID     Pri   State           Dead Time   Address         Interface
172.16.12.2       1   FULL/DR         00:00:36    172.16.12.2     GigabitEthernet2
R1_xe#
['172.16.12.2']

最後の行に API の戻り値としてリストで OSPFネイバー が取得できていることが分かります。お気づきになったでしょうか? Genie Parser では OSPF Neighbor の State や Address など全ての情報を構造化データと返すので、OSPFネイバーのみのリストが欲しいという場合にはその構造化データから取得するコードを書く必要がありますが、API を使うとその部分が省略できます。

実際に API がどのような事を知りたければ、オープンソースなのでコードを見ることができまし、何か追加したいものがあれば開発してコントリビュートすることも可能です。

genie.libs contains libraries for configuring, retrieving and testing topologies - CiscoTestAutomation/genielibs

実際にネイバーを取得しているコードが下記のようになっています。

    try:
        out = device.parse("show ip ospf neighbor")
    except SchemaEmptyParserError:
        return neighbor_addresses

    if out and "interfaces" in out and interface in out["interfaces"]:

        for neighbor in out["interfaces"][interface].get("neighbors", {}):
            neighbor_addresses.append(neighbor)

    return neighbor_addresses

見てみると、show ip ospf neighbor をパースして、内容が空でないかをチェックし、また構造化データに沿って for 文でループしたりしていることが分かります。

そういうコードを書く手間がなくなるだけでも、かなり楽にコーディングができるようになります。

get_ API は情報取得に使う

もう一つ get_ospf_neighbor_address_in_state を試して見ます。引数として state を渡すことで条件に合致した OSPFネイバーのみを取得することができます。

>>> xe.api.get_ospf_neighbor_address_in_state(state='FULL/DR')
[2019-12-13 21:18:15,955] +++ R1_xe: executing command 'show ip ospf neighbor' +++
show ip ospf neighbor

Neighbor ID     Pri   State           Dead Time   Address         Interface
172.16.12.2       1   FULL/DR         00:00:33    172.16.12.2     GigabitEthernet2
R1_xe#
['172.16.12.2']
>>>

試しに違う state で試してみると、条件に合致しないのでネイバーが取得できません。

>>> xe.api.get_ospf_neighbor_address_in_state(state='FULL/BDR')
[2019-12-13 21:20:12,544] +++ R1_xe: executing command 'show ip ospf neighbor' +++
show ip ospf neighbor

Neighbor ID     Pri   State           Dead Time   Address         Interface
172.16.12.2       1   FULL/DR         00:00:39    172.16.12.2     GigabitEthernet2
R1_xe#
[]

configure_ API で設定もできる

次は configure_ で始まる Genie API を試してみます。これは名前から分かるように設定を行うことができます。引数に OSPFプロセスID と対象のインタフェースを渡して passive を投入する API です。

>>> xe.api.configure_ospf_passive_interface(ospf_process_id='1', interface=['GigabitEthernet2'])
[2019-12-13 21:24:40,290] +++ R1_xe: config +++
config term
Enter configuration commands, one per line.  End with CNTL/Z.
R1_xe(config)#router ospf 1
R1_xe(config-router)#passive-interface GigabitEthernet2
R1_xe(config-router)#end
R1_xe#
>>>

verify_ API は確認に使う

verify_ で始まる Genie API は get_ に似ていますが、大きな相違点は確認用としてリトライを行います。下記のように APIガイド に沿って引数を渡すと、指定した最大時間とインターバルの範囲で繰り返し確認を行います。

>>> xe.api.verify_ospf_neighbor_address_in_state(addresses=['172.16.12.2'], state='FULL/DR', max_time=60, check_interval=30)
Starting a timeout with maximum time of 60 seconds with interval of 30 seconds, Test case will fail after the maximum time set
[2019-12-13 21:30:36,747] +++ R1_xe: executing command 'show ip ospf neighbor' +++
show ip ospf neighbor
R1_xe#
The following addresses are not in state FULL/DR: {'172.16.12.2'}
59.71 seconds remaining; Sleeping for 30.00 seconds. '1' attempt(s) have been performed
[2019-12-13 21:31:07,041] +++ R1_xe: executing command 'show ip ospf neighbor' +++
show ip ospf neighbor
R1_xe#
The following addresses are not in state FULL/DR: {'172.16.12.2'}
29.41 seconds remaining; Sleeping for 29.41 seconds. '2' attempt(s) have been performed
Performing the last attempt
[2019-12-13 21:31:36,751] +++ R1_xe: executing command 'show ip ospf neighbor' +++
show ip ospf neighbor
R1_xe#
The following addresses are not in state FULL/DR: {'172.16.12.2'}
0 second remaining; Will not sleep
Ran out of time for this timeout
False
>>>

例えば、インタフェースの shutdown / no shutdown の後でネイバーがアップになるまで時間がかかる場合などに verify_ でリトライでネイバーアップになるまで確認を繰り返すことができます。

Genie API を使ってコーディングが簡単になるだろうことは分かりましたが、まだコーディング必要です。次はさらに簡単になる方法を紹介します。

Genie API を Robot Framework で使う

前述までの説明で Genie API を使うことでコーディング楽になることが分かりましたが、実は Robot Framework と組み合わせることでキーワードベースで分かり易い記述でネットワークの自動化を実現することが可能です。

Genie API は開発されると同時に Robot キーワードが自動的に追加されるという仕組みになっています。その為、Genie API で存在するものと同様の Robot キーワードは基本的に別途開発する必要はありません。

本記事ではどういう感じで使えるかというのをお伝えするのを目的としている為、Robot Framework の記述についての詳細は割愛します。

Genie API を使った Robot ファイルのサンプル

前の項目で使った Genie API を利用して Robot Framework で使う robot ファイルを作成してみました。ファイルの拡張子は .robot になります。

# Genie API example with Robot

*** Settings ***
Library        Collections
Library        ats.robot.pyATSRobot
Library        genie.libs.robot.GenieRobot
Library        genie.libs.robot.GenieRobotApis

*** Variables ***
${testbed}     testbed.yaml

*** Test Cases ***
Connect to Devices
    use genie testbed "${testbed}"
    connect to all devices

Check Ospf and Shut/Noshut Interface
    @{ospf_neighbors} =  Get Ospf Neighbors  device=R1_xe
    :For  ${ospf_neighbor}  IN  @{ospf_neighbors}
        \  @{neighbors} =  Create List  ${ospf_neighbor}
        \  Verify Ospf Neighbor Address In State    addresses=@{neighbors}   state=FULL/DR    device=R1_xe
        \  Shut Interface    interface=GigabitEthernet2    device=R1_xe
        \  Unshut Interface    interface=GigabitEthernet2    device=R1_xe
        \  ${result} =  Verify Ospf Neighbor Address In State    addresses=@{neighbors}   state=FULL/DR    device=R1_xe
        \  Should Be True  ${result}

Settings には pyATS/Genie のライブラリを指定する必要があることに注意してください。

実際の自動化テストの内容は Test Cases に記載していきます。上記のファイルのテスト内容をざっくり書くとこんな感じです。

  1. 機器へ接続
  2. インターフェースの shut/no shut 及び OSPF ネイバー確認
    1. OSPFネイバーの一覧取得
    2. 取得ネイバーで下記を繰り返し
      1. OSPFネイバーの状態チェック ‘FULL/DR’ か
      2. インタフェースの shutdown
      3. インタフェースの no shutdown
      4. OSPFネイバーの状態チェック ‘FULL/DR’ か
      5. 4 の結果判定

実際には各キーワードの成否判定などを充実させるべきですが、サンプルとして見ていただければと思います。

Robot キーワードと Genie API の比較

下記の部分が Genie API を Robot キーワードとして書いている部分になります。

       \  Verify Ospf Neighbor Address In State    addresses=@{neighbors}   state=FULL/DR    device=R1_xe
        \  Shut Interface    interface=GigabitEthernet2    device=R1_xe

これは python で書く場合の下記と同意となります。

xe.api.verify_ospf_neighbor_address_in_state(addresses=['<address>'], state='FULL/DR')
xe.api.shut_interface(interface='GigabitEthernet2')

比べると分かるかと思いますが、API の名前がそのまま Robot キーワード(_ を space に変換) に対応し、引数も同じようにキーワードに続けて書くだけです。
Robot で書く場合は device は追加で指定する必要があります。

Robot ファイルの実行

それでは上記の robot ファイルを実行してみます。robot コマンドに続けてファイルを指定するだけです。

(genie)$ robot genie_api.robot
==============================================================================
Genie Api
==============================================================================
Connect to Devices                                                    | PASS |
------------------------------------------------------------------------------
Check Ospf and Shut/Noshut Interface                                  | PASS |
------------------------------------------------------------------------------
Genie Api                                                             | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed
==============================================================================
Output:  /Users/ccieojisan/.pyenv/versions/3.7.3/envs/genie_pkg/output.xml
Log:     /Users/ccieojisan/.pyenv/versions/3.7.3/envs/genie_pkg/log.html
Report:  /Users/ccieojisan/.pyenv/versions/3.7.3/envs/genie_pkg/report.html

実際のコンソールでは下記のように PASS や結果にはカラー付けがされる為、非常に結果が見易くなっています。

上記 Log の項目にある log.html は HTML ファイルになっており、ブラウザで開くと結果とログの詳細を確認することができます。

機器の接続からインタフェースの shut/no shut、OSPF ネイバーの確認部分はこんな感じです。

各項目を展開して詳細を開くと、実際のコンソールが画面が確認できます。具体的にどのような確認を行なっているか、設定を行なったが確認できます。

Genie API を Robot Framework の組み合わせも非常に強力なことが分かるかと思います。Genie API が豊富なネットワーク機器の設定、確認を提供し、Robot Framework の記述でキーワード対話形式で自動化テストを記述することが可能になります。

まとめ

今回は Genie API を紹介しました。Python コーディングを手助けするものとして有益なこと、Robot Framework と組み合わせることで、コーディング無しでテスト自動化ができることも紹介しました。ネットワーク自動化の選択肢としては有力な組み合わせではないかと思います。

スポンサーリンク