Genie Harnessで実現するネットワークテスト自動化シナリオ

#初めに
DevNet Sandboxを使って簡単にpyATS/Genieを試すことができる非常に良い記事があるので、その記事を参考にしながらGenieのHarnessをCML2で稼働させているネットワーク機器で試してみました。

（参考記事）
[- DevNet Sandboxを使ってpyATS/XPRESSOをCML2と始めよう](https://qiita.com/tahigash/items/753cffdbf6380afa823f "DevNet Sandboxを使ってpyATS/XPRESSOをCML2と始めよう")

#Genieとは
Genieとは、pyATSフレームワークのモジュラーアーキテクチャにバンドルされているネットワークテスト自動化に必要なすべてのツールを含むpyATS用のPythonライブラリ。

#Genie Harness
Genie Harnessは、Common Setup、Triggers and Verifications、Common Cleanupの3つのセクションでテストシナリオを構成します。コアとなるのはTriggers and Verificationsです。
interfaceの`shutdown`、`no shutdown`やBPGの`unconfig`、`restore`などの__アクション__がTriggersで処理され、`Modelsのlearn object(Ops Object)`や`Parsers object`を駆使した__検証__がVerificationsで処理されます。VerificationsはTriggers前後に実行され、その取得した検証結果を比較し期待通りであるか判断します。
Genie Harnessは多くのモジュールが積み重ねられ、デバイスが更新されても対応できる将来性のある自動化ソリューションを作成することができます。
[こちら](https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/triggers)にOS毎にサポートされているTriggers、[こちら](https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/verifications)にVerificationsが掲載されています。

#testbed.yamlファイル
DevNet Sandboxの「Cisco pyATS XPRESSO」の環境でデモを実行します。
GenieのParsers、Models、Apisなどの各ライブラリを使用するためには、testbedというネットワーク構成を記載したyamlファイルが必要です。参考記事にもあるようにSandbox内にtestbed.yamlは準備されています。
また、前回記事にも掲載しています。
[- Genie Parsersで実現するDevices’ outputsの構造化](https://qiita.com/masanobu48154/items/d6572d8de6d50e11c6fb "Genie Parsersで実現するDevices’ outputsの構造化")

#データファイル
Genie Harnessは、[7つのdatafile](https://pubhub.devnetcloud.com/media/genie-docs/docs/cookbooks/harness.html#summary-of-each-datafile)を使用します。

__①testbed.yaml Datafile (Mandatory)__
testbed.yaml Datafileには、デバイス情報や、それらへの接続方法、サーバー、インターフェースなど、テスト対象のネットワーク構成が記述されています。

__②Mapping Datafile (Optional)__
Mapping Datafileは、ユーザーがtestbed.yaml上のどのデバイスのどの接続タイプを制御したいかなどをGenieに橋渡します。 Mapping Datafileが指定されない場合、Genieは全てのデバイスに接続します。

+ [Mapping datafile schema](https://pubhub.devnetcloud.com/media/genie-docs/docs/cookbooks/harness.html#mapping-datafile-optional)

__③Verification Datafile (Optional)__
Verification Datafileには、検証に関するすべての情報が含まれています。 Verification Datafileが指定されない場合は、デフォルトのデータファイル[$ VIRTUAL_ENV / genie_yamls / any os /verification_datafile.yaml](https://github.com/CiscoTestAutomation/genielibs/tree/master/pkgs/sdk-pkg/src/genie/libs/sdk/genie_yamls)が使用されます。
デフォルトのファイルを変更する場合にのみ、新しいVerification Datafileを指定する必要があります。

+ [Verification Datafile schema](https://pubhub.devnetcloud.com/media/genie-docs/docs/cookbooks/harness.html#verification-datafile-optional)

__④Trigger Datafile (Optional)__
Trigger Datafileには、トリガーに関するすべての情報が含まれています。 Trigger Datafileが指定されない場合は、デフォルトのデータファイル[$ VIRTUAL_ENV / genie_yamls / any os /trigger_datafile.yaml](https://github.com/CiscoTestAutomation/genielibs/tree/master/pkgs/sdk-pkg/src/genie/libs/sdk/genie_yamls)が使用されます。
デフォルトのファイルを変更する場合にのみ、新しいTrigger Datafileを指定する必要があります。

+ [Trigger Datafile schema](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/harness/user/datafile.html#trigger-datafile)

__⑤Subsection Datafile (Optional)__
Subsection Datafileには、サブセクションに関するすべての情報が含まれています。 Subsection Datafileが指定されていない場合は、デフォルトのデータファイル[$ VIRTUAL_ENV / genie_yamls /subsection_datafile.yaml](https://github.com/CiscoTestAutomation/genielibs/tree/master/pkgs/sdk-pkg/src/genie/libs/sdk/genie_yamls)が使用されます。
デフォルトのファイルを変更する場合にのみ、新しいSubsection Datafileを指定する必要があります。 それ以外の場合、必要なのはデフォルトのものだけです。

+ [Subsection datafile schema](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/harness/user/datafile.html#subsection-datafile)

Subsectionは、Common SetupとCommon Cleanupで実行され、トリガー間では実行されません。 そのため、Genieは、実行に影響を与えることなく、デバイスに設定されたすべてのFeaturesの完全なスナップショットの作成、クラウドへの設定のバックアップやレストアなど、様々なタスクを実行できます。

__⑥Configuration Datafile (Optional)__
Configuration Datafileには、デバイスのコンフィグ設定に関するすべての情報が含まれています。Configuration Datafileが提供されていない場合、デバイスにコンフィグを設定することはありません。

+ [Configuration schema](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/harness/user/datafile.html#configuration-datafile)

__⑦PTS Datafile (Optional)__
PTS(Profile The System) Datafileには、Common SetupとCommon Cleanupで実行されるOps objectを使用したFeature statusの情報収集に関するすべての情報が含まれています。 PTS Datafileが提供されていない場合は、デフォルトのデータファイル[$ VIRTUAL_ENV / genie_yamls /pts_datafile.yaml](https://github.com/CiscoTestAutomation/genielibs/tree/master/pkgs/sdk-pkg/src/genie/libs/sdk/genie_yamls)が使用されます。
デフォルトのファイルを変更する場合にのみ、新しいPTS Datafileを指定する必要があります。

+ [PTS Datafile schema](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/harness/user/datafile.html#pts-datafile)

PTSはOps objectを使用しFeature statusの情報収集をします。その点でVerificationに似ていますが、PTSはCommon SetupとCommon Cleanupでのみ実行されます。トリガー間では実行されません。PTSは2回しか実行されないため、Genie Harnessは、実行時間に影響を与えることなく、デバイスに構成されているすべての機能の完全なスナップショットを取得できます。PTSはOps objectを使用しFeature statusの情報を収集し比較することを目的としているので、Common SetupとCommon Cleanupで同じ処理をします。SubsectionのようにCommon SetupとCommon Cleanup時に異なる処理を実行しません。

PTSのもう1つの利点は、pts_golden_config機能です。 この機能により、Genieはデバイスに適用された構成の動作状態が、以前に作成された検証済みスナップショットと正確に一致することを検証できます。 これにより、同じ構成でのすべての実行で同じ動作状態が保証されます。

#Jobファイル
Genie Harnessを実行させるために必要なpythonファイルです。
実行するTriggers、Verificationsの選択や、参照するDatafilesの指定、PTSでSnapshotをとるFeatureの指定など、[Arguments](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/harness/user/reference.html#arguments)でオプションを指定しながら実行するテストシナリオを`gRun()`で構成、実行します。

```python
# Import Genie run
from genie.harness.main import gRun


def main():

    # Set job file path to current directory
    test_path = os.path.dirname(os.path.abspath(__file__))

    gRun(mapping_datafile=os.path.join(test_path, 'mapping.yaml'),
        runtime=runtime, config_datafile=os.path.join('configs.yaml'),
        pts_features=['ospf', 'hsrp'],
        pts_datafile=os.path.join(test_path, 'pts_datafile'),
        pts_golden_config='<path>/pts',
        verification_datafile=os.path.join(test_path, 'verification_datafile'),
        trigger_datafile=os.path.join(test_path, 'trigger_datafile'),
        trigger_uids=['TriggerShutNoShutOspf', 'TriggerClearOspf'])
```

#JobファイルのArguments

Genieは高度にカスタマイズ可能であり、テストの仕様と要件を満たすように調整できます。 すべての引数は、gRunの引数としてジョブファイルに指定できます。

すべての引数はオプションなので、オプションを指定せずgRun()のみで実行することも可能です。
オプションを指定せずgRun()を実行すれば、Common SetupとCommon CleanupをデフォルトのSubsection Datafileに従って処理します。VerificationsやTriggersは実行されません。

* __`pts_features`:__スナップショットをとる[Feature](https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/models)を指定します。

```python
gRun(pts_features=['ospf', 'bgp'])`
```

* __`pts_golden_config`:__PTS golden configuration fileの格納場所を指定します。

```python
gRun(pts_golden_config='/path/pts')`
```

* __`verification_uids`:__実行するverification uidのリストを指定します。

```python
gRun(verification_uids=['Verify_IpOspfNeighborDetail_vrf_all', 'Verify_IpRoute_protocol_bgp'])`
```
> verification名は、Verification Datafileに定義しているものを指定します。

* __`verification_groups`:__実行するverificationsのグループを指定します。

```python
# aetest.main() example using datastructure logic
from pyats.datastructures.logic import Or, And
gRun(verification_groups=And(Or('group1','group2'), 'group3'))
```

* __`trigger_uids`:__実行するverification uidのリストを指定します。

```python
gRun(trigger_uids=['TriggerUnconfigConfigBgp', 'TriggerShutNoShutEthernetInterface'])
```

* __`trigger_groups`:__実行するtriggersのグループを指定します。

```python
# aetest.main() example using datastructure logic
from pyats.datastructures.logic import Or, And
gRun(trigger_groups=And(Or('group1','group2'), 'group3'))
```

> trigger名は、Trigger Datafileに定義しているものを指定します。

* __`mapping_datafile`:__Mapping Datafileの格納場所を指定します。

```python
gRun(mapping_datafile='/path/mapping.pts')
```

* __`verification_datafile`:__Verification Datafileの格納場所を指定します。

```python
gRun(verification_datafile='/path/verificationdatafile.yaml')
```

* __`trigger_datafile`:__Trigger Datafileの格納場所を指定します。

```python
gRun(trigger_datafile='/path/triggerdatafile.yaml')
```

* __`config_datafile`:__Configuration Datafileの格納場所を指定します。

```python
gRun(config_datafile='/path/config_datafile.yaml')
```

* __`subsection_datafile`:__Subsection Datafileの格納場所を指定します。

```python
gRun(subsection_datafile='/path/subsection_datafile.yaml')
```

* __`pts_datafile`:__PTS Datafileの格納場所を指定します。

```python
gRun(pts_datafile='/path/pts_datafile.yaml')
```

* __`debug_plugin`:__debug-pluginの格納場所を指定します。

```python
# Inside a job file
# In case of single debug plugin in the run
gRun(debug_plugin='/path/debug_plugin')

# Inside a job file
# In case of multiple debug plugins in the run (differnet device types)
gRun(debug_plugin='/path/')
```

> 複数のdebug pluginを指定する場合は、下記のようにtestbed.yamlファイルの対応するdeviceのcustom sectionでそのdebug pluginを記載する必要があります。
custom:
　　debug_plugin: n7700-s2-debug-sh.8.4.1.gbin

* __`devices`:__Mapping Datafileを指定せず、ユーザーが複数のデバイスに接続したい場合に指定するデバイスのリスト。
`devices`を指定せず、かつ、Mapping Datafileを指定していない場合、Genieはtestbed.yamlファイル内のすべてのデバイスを接続します。

```python
gRun(devices=['N95_1', 'N95_2'])
```

* __`random`:__トリガーのランダム化を有効にすると、スクリプトのテストケースの順序をランダムにシャッフルできます。

```python
gRun(pts_features=['platform', 'bgp', 'interface'],
     verification_uids=['Verify_IpInterfaceBrief', 'Verify_IpRoute_vrf_all'],
     trigger_uids=['TriggerUnconfigConfigBgp.uut', 'TriggerShutNoShutBgpNeighbors', 'TriggerModifyLoopbackInterfaceIp.uut', 'TriggerShutNoShutEthernetInterface'],
     random=True)
```

#デモの概要
[Sandbox - Cisco DevNet](https://developer.cisco.com/site/sandbox/)の環境を使って、
Genie Harnessを試してみます。TriggersとVerificationsはいろんなテストの組み合わせをplug and playのテストケースとして実現することができます。このデモでは、そのTriggersとVerificationsを簡単に試してみます。処理は以下のように進みます。

* testbed.yaml Datafile、Mapping Datafileで定義されているデバイスに接続します。
* 最初のVerificationsラウンドを実行します。 Verificationsで実行されるコマンドのアウトプットからステータスのスナップショットを取ります。 後に同じように実行されるVerificationsラウンドが、この最初のスナップショットと比較されます。
* 最初のTriggerである`TriggerUnconfigConfigBgp`を実行します。
* 2回目のVerificationsラウンドを実行します。この結果は最初のラウンドと比較されます。
* 2番目のTriggerである`TriggerModifyLoopbackInterfaceIp`を実行します。
* すべてのTriggersが実行されるまで繰り返します。
* 実行を終了します。

他にもGenie Harness ExamplesがGit Hubで公開されており、下記からcloneすることができます。
[Genie Harness Examplesリポジトリ](https://github.com/CiscoTestAutomation/examples/tree/master/libraries)

#デモの手順

① CML2のルータへの追加設定
[Sandbox - Cisco DevNet](https://developer.cisco.com/site/sandbox/)の「dist-rtr01」と「dist-rtr02」にBGPの設定を追加します。

> 両ルータ間でMP-BGPとSR-MPLSでのL3VPNを構成するようにします。

② データファイルの準備
__testbed.yaml__と同じディレクトリに、__map.yaml__(Mapping Datafile)、
__verify.yaml__(Verification Datafile)、__trigger.yaml__(Trigger Datafile)、__pts.yaml__(PTS Datafile)を`touch`や`vim`でコピーします。Subsection Datafileは[Default Datafile](https://github.com/CiscoTestAutomation/genielibs/tree/master/pkgs/sdk-pkg/src/genie/libs/sdk/genie_yamls)を使用します。今回は追加コンフィグは直接手動で設定するので、Configuration Datafileは作成しません。

③Jobファイルの準備
__testbed.yaml__と同じディレクトリに、Jobファイルである__job.py__をコピーします。

④Jobの実行
`pyats run job`コマンドで、__job.py__を実行します。

⑤ログ参照
`pyats logs view`コマンドで、ブラウザからGUIでログを参照することができます。

#デモの実行

① __CML2のルータへの追加設定__
「dist-rtr01」へ__xe01.cfg__を、「dist-rtr02」へ__xe02.cfg__をcliで追加設定します。

```shell:xe01.cfg
vrf definition A
 rd 65432:1
 address-family ipv4
  route-target both 65432:1
 exit-address-family
vrf definition B
 rd 65432:2
 address-family ipv4
  route-target both 65432:2
 exit-address-family

interface loopback 0
 ip address 172.16.252.101 255.255.255.255
 no shutdown

interface loopback 1000
 vrf forwarding A
 ip address 10.0.0.101 255.255.255.255
 no shutdown

interface loopback 1001
 vrf forwarding B
 ip address 10.0.1.101 255.255.255.255
 no shutdown

segment-routing mpls
 !
 connected-prefix-sid-map
  address-family ipv4
   172.16.252.101/32 index 1 range 1
  exit-address-family

router ospf 1
 segment-routing area 0 mpls
 segment-routing mpls


router bgp 65432
 no bgp default ipv4-unicast
 neighbor 172.16.252.102 remote-as 65432
 neighbor 172.16.252.102 update-source Loopback0
 !
 address-family vpnv4
  neighbor 172.16.252.102 activate
  neighbor 172.16.252.102 send-community both
 exit-address-family
 !
 address-family ipv4 vrf A
  redistribute connected
 exit-address-family
 !
 address-family ipv4 vrf B
  redistribute connected
 exit-address-family
```

```shell:xe02.cfg
vrf definition A
 rd 65432:1
 address-family ipv4
  route-target both 65432:1
 exit-address-family
vrf definition B
 rd 65432:2
 address-family ipv4
  route-target both 65432:2
 exit-address-family

interface loopback 0
 ip address 172.16.252.102 255.255.255.255
 no shutdown

interface loopback 1000
 vrf forwarding A
 ip address 10.0.0.102 255.255.255.255
 no shutdown

interface loopback 1001
 vrf forwarding B
 ip address 10.0.1.102 255.255.255.255
 no shutdown

segment-routing mpls
 !
 connected-prefix-sid-map
  address-family ipv4
   172.16.252.102/32 index 2 range 1
  exit-address-family

router ospf 1
 segment-routing area 0 mpls
 segment-routing mpls


router bgp 65432
 no bgp default ipv4-unicast
 neighbor 172.16.252.101 remote-as 65432
 neighbor 172.16.252.101 update-source Loopback0
 !
 address-family vpnv4
  neighbor 172.16.252.101 activate
  neighbor 172.16.252.101 send-community both
 exit-address-family
 !
 address-family ipv4 vrf A
  redistribute connected
 exit-address-family
 !
 address-family ipv4 vrf B
  redistribute connected
 exit-address-family

```

② __データファイルの準備__
__testbed.yaml__と同じディレクトリに、__map.yaml__(Mapping Datafile)、
__verify.yaml__(Verification Datafile)、__trigger.yaml__(Trigger Datafile)、__pts.yaml__(PTS Datafile)をコピーします。

```yaml:map.yaml
devices:
    xe01:
        context: cli
        pool_size: 2
        mapping:
            cli: cli
    xe02:
        context: cli
        pool_size: 2
        mapping:
            cli: cli
```

> __testbed.yaml__の中から、検証対象のデバイスと、デバイスへの接続方式を__map.yaml__で指定してGenieに渡します。

```yaml:verify.yaml
extends: "%CALLABLE{genie.libs.sdk.genie_yamls.datafile(verification)}"

Verify_BgpAll:
  devices: ['xe01', 'xe02']
  exclude: []
Verify_BgpAllSummary:
  devices: ['xe01', 'xe02']
  exclude: []
  iteration:
    attempt: 10
    interval: 10
Verify_IpInterfaceBrief:
  devices: ['xe01', 'xe02']
  exclude: []
```

> Verification Datafileは、実行するVerificationsとその場所を示すYAMLファイルで、次の項目を記載します。

> - Verification名
- Verification source — Verificationクラスとそのパス
- テストを実行するデバイス(`-- device`で指定する。)
- その他のVerificationに引数として渡すパラメーター

```yaml:trigger.yaml
extends: "%CALLABLE{genie.libs.sdk.genie_yamls.datafile(trigger)}"

TriggerUnconfigConfigBgp:
    groups: ['unconfig-config', 'bgp', 'L3']
    method: 'checkpoint'
    timeout:
       max_time: 400
       interval: 10
    tgn_timeout: 300
    tgn_delay: 10
    devices: ['xe01', 'xe02']

TriggerModifyLoopbackInterfaceIp:
    groups: ['modify', 'interface', 'L3']
    method: 'checkpoint'
    timeout:
       max_time: 180
       interval: 10
    tgn_timeout: 300
    tgn_delay: 10
    devices: ['xe01', 'xe02']
```

> Trigger Datafileは、実行するテストケースとその場所を示すYAMLファイルで、次の項目を記載します。

> - Trigger名
- Trigger source — triggerクラスとそのパス
- テストを実行するデバイス(`-- device`で指定する。)
- その他のTriggerに引数として渡すパラメーター

```yaml:pts.yaml
platform:
    source:
        pkg: genie.libs.ops
        class: platform.platform.Platform
    devices: ['xe01', 'xe02']
    exclude: []

bgp:
    source:
        pkg: genie.libs.ops
        class: bgp.bgp.Bgp
    devices: ['xe01', 'xe02']
    exclude: []

interface:
    source:
        pkg: genie.libs.ops
        class: interface.interface.Interface
    devices: ['xe01', 'xe02']
    exclude: []
```

③__Jobファイルの準備__
testbed.yamlと同じディレクトリに、Jobファイルであるdemo2_harness_triggers_job.pyをコピーします。

```python:demo2_harness_triggers_job.py
import os

from genie.harness.main import gRun

def main():
    test_path = os.path.dirname(os.path.abspath(__file__))

    gRun(mapping_datafile=os.path.join(test_path, 'map.yaml'),
        pts_datafile=os.path.join(test_path, 'pts.yaml'),
        trigger_datafile=os.path.join(test_path, 'trigger.yaml'),
        verification_datafile=os.path.join(test_path, 'verify.yaml'),
        pts_features=['platform', 'bgp', 'interface'],
        verification_uids=['Verify_IpInterfaceBrief', 'Verify_BgpAllSummary'],
        trigger_uids=['TriggerUnconfigConfigBgp',  'TriggerModifyLoopbackInterfaceIp'])
```

④__Jobの実行__
`pyats run job`コマンドで、__job.py__を実行します。

```shell
pyats run job job.py --testbed-file testbed.yaml
```

⑤__ログ参照__
ログはリアルタイムにコンソールに表示され、最終的に下記のようなサマリーレポートが表示されます。ログは`~/.pyats/archive`に保存されます。

```shell

+------------------------------------------------------------------------------+
|                                Easypy Report                                 |
+------------------------------------------------------------------------------+
pyATS Instance   : /home/developer/py3venv
Python Version   : cpython-3.6.8 (64bit)
CLI Arguments    : /home/developer/py3venv/bin/pyats run job job.py --testbed-file testbed.yaml
User             : developer
Host Server      : devbox
Host OS Version  : CentOS Linux 7 Core (x86_64)
Job Information
    Name         : job
    Start time   : 2021-09-19 06:08:24.292997
    Stop time    : 2021-09-19 06:10:29.827424
    Elapsed time : 125.534427
    Archive      : /home/developer/.pyats/archive/21-Sep/job.2021Sep19_06:08:21.772769.zip
Total Tasks    : 1 
Overall Stats
    Passed     : 26
    Passx      : 0
    Failed     : 0
    Aborted    : 0
    Blocked    : 0
    Skipped    : 0
    Errored    : 0
    TOTAL      : 26
Success Rate   : 100.00 %
+------------------------------------------------------------------------------+
|                             Task Result Summary                              |
+------------------------------------------------------------------------------+
Task-1: genie_testscript.common_setup                                     PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe01.1                   PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe02.1                   PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe01.1                      PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe02.1                      PASSED
Task-1: genie_testscript.TriggerUnconfigConfigBgp.xe01                    PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe01.2                   PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe02.2                   PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe01.2                      PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe02.2                      PASSED
Task-1: genie_testscript.TriggerUnconfigConfigBgp.xe02                    PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe01.3                   PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe02.3                   PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe01.3                      PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe02.3                      PASSED
Task-1: genie_testscript.TriggerModifyLoopbackInterfaceIp.xe01            PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe01.4                   PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe02.4                   PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe01.4                      PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe02.4                      PASSED
Task-1: genie_testscript.TriggerModifyLoopbackInterfaceIp.xe02            PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe01.5                   PASSED
Task-1: genie_testscript.Verify_IpInterfaceBrief.xe02.5                   PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe01.5                      PASSED
Task-1: genie_testscript.Verify_BgpAllSummary.xe02.5                      PASSED
Task-1: genie_testscript.common_cleanup                                   PASSED
+------------------------------------------------------------------------------+
|                             Task Result Details                              |
+------------------------------------------------------------------------------+
Task-1: genie_testscript
|-- common_setup                                                          PASSED
|   |-- connect                                                           PASSED
|   |-- configure                                                        SKIPPED
|   |-- configuration_snapshot                                            PASSED
|   |-- save_bootvar                                                      PASSED
|   |-- learn_system_defaults                                             PASSED
|   |-- initialize_traffic                                               SKIPPED
|   |-- profile_platform_pre                                              PASSED
|   |-- profile_bgp_pre                                                   PASSED
|   |-- profile_interface_pre                                             PASSED
|   `-- PostProcessor-1                                                   PASSED
|-- Verify_IpInterfaceBrief.xe01.1                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_IpInterfaceBrief.xe02.1                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe01.1                                           PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe02.1                                           PASSED
|   `-- verify                                                            PASSED
|-- TriggerUnconfigConfigBgp.xe01                                         PASSED
|   |-- verify_prerequisite                                               PASSED
|   |   |-- STEP 1: Learning 'Bgp' Ops                                    PASSED
|   |   |-- STEP 2: Verifying requirements                                PASSED
|   |   `-- STEP 3: Merge requirements                                    PASSED
|   |-- save_configuration                                                PASSED
|   |-- unconfigure                                                       PASSED
|   |   `-- STEP 1: Unconfiguring 'Bgp'                                   PASSED
|   |-- verify_unconfigure                                                PASSED
|   |   `-- STEP 1: Verifying 'Bgp' state with ops.bgp.bgp.Bgp            PASSED
|   |-- restore_configuration                                             PASSED
|   `-- verify_initial_state                                              PASSED
|       `-- STEP 1: Verifying ops 'Bgp' is back to original state         PASSED
|-- Verify_IpInterfaceBrief.xe01.2                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_IpInterfaceBrief.xe02.2                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe01.2                                           PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe02.2                                           PASSED
|   `-- verify                                                            PASSED
|-- TriggerUnconfigConfigBgp.xe02                                         PASSED
|   |-- verify_prerequisite                                               PASSED
|   |   |-- STEP 1: Learning 'Bgp' Ops                                    PASSED
|   |   |-- STEP 2: Verifying requirements                                PASSED
|   |   `-- STEP 3: Merge requirements                                    PASSED
|   |-- save_configuration                                                PASSED
|   |-- unconfigure                                                       PASSED
|   |   `-- STEP 1: Unconfiguring 'Bgp'                                   PASSED
|   |-- verify_unconfigure                                                PASSED
|   |   `-- STEP 1: Verifying 'Bgp' state with ops.bgp.bgp.Bgp            PASSED
|   |-- restore_configuration                                             PASSED
|   `-- verify_initial_state                                              PASSED
|       `-- STEP 1: Verifying ops 'Bgp' is back to original state         PASSED
|-- Verify_IpInterfaceBrief.xe01.3                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_IpInterfaceBrief.xe02.3                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe01.3                                           PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe02.3                                           PASSED
|   `-- verify                                                            PASSED
|-- TriggerModifyLoopbackInterfaceIp.xe01                                 PASSED
|   |-- verify_prerequisite                                               PASSED
|   |   |-- STEP 1: Learning 'Interface' Ops                              PASSED
|   |   |-- STEP 2: Verifying requirements                                PASSED
|   |   `-- STEP 3: Merge requirements                                    PASSED
|   |-- save_configuration                                                PASSED
|   |-- modify_configuration                                              PASSED
|   |   `-- STEP 1: Configuring 'Interface'                               PASSED
|   |-- verify_modification                                               PASSED
|   |   `-- STEP 1: Verifying 'Interface' state with ops.interface....    PASSED
|   |-- restore_configuration                                             PASSED
|   `-- verify_initial_state                                              PASSED
|       `-- STEP 1: Verifying ops 'Interface' is back to original s...    PASSED
|-- Verify_IpInterfaceBrief.xe01.4                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_IpInterfaceBrief.xe02.4                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe01.4                                           PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe02.4                                           PASSED
|   `-- verify                                                            PASSED
|-- TriggerModifyLoopbackInterfaceIp.xe02                                 PASSED
|   |-- verify_prerequisite                                               PASSED
|   |   |-- STEP 1: Learning 'Interface' Ops                              PASSED
|   |   |-- STEP 2: Verifying requirements                                PASSED
|   |   `-- STEP 3: Merge requirements                                    PASSED
|   |-- save_configuration                                                PASSED
|   |-- modify_configuration                                              PASSED
|   |   `-- STEP 1: Configuring 'Interface'                               PASSED
|   |-- verify_modification                                               PASSED
|   |   `-- STEP 1: Verifying 'Interface' state with ops.interface....    PASSED
|   |-- restore_configuration                                             PASSED
|   `-- verify_initial_state                                              PASSED
|       `-- STEP 1: Verifying ops 'Interface' is back to original s...    PASSED
|-- Verify_IpInterfaceBrief.xe01.5                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_IpInterfaceBrief.xe02.5                                        PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe01.5                                           PASSED
|   `-- verify                                                            PASSED
|-- Verify_BgpAllSummary.xe02.5                                           PASSED
|   `-- verify                                                            PASSED
`-- common_cleanup                                                        PASSED
    |-- verify_configuration_snapshot                                     PASSED
    |-- stop_traffic                                                     SKIPPED
    |-- profile_platform_post                                             PASSED
    |-- profile_bgp_post                                                  PASSED
    |-- profile_interface_post                                            PASSED
```

また、`pyats logs view`コマンドでブラウザ経由でログを参照することができます。
下記コマンドを投入しブラウザで[http://10.10.20.50:8181](http://10.10.20.50:8181)にアクセスすればログがとても分かりやすく可視化されています。

```shell
pyats logs view --host 10.10.20.50 --port 8181
```

> DockerコンテナなどでpyATS/Genieの環境を作成している場合は、ブリッジネットワーク上で割り当てられたコンテナのアドレスと、内部ポート番号を指定します。

#まとめ
Genie Harnessを使えば多くのモジュールが積み重ねらた自動化ソリューションを、Verificationsや Triggers、PTSを組み合わせるだけで簡単に作成することができます。また、Common Setup、Testcases、Common CleanupなどpyATSのテストスクリプトの構造を理解するのにも適しています。

#参考リンク
[- DevNet Sandboxを使ってpyATS/XPRESSOをCML2と始めよう](https://qiita.com/tahigash/items/753cffdbf6380afa823f "DevNet Sandboxを使ってpyATS/XPRESSOをCML2と始めよう")
[- Genie Parsersで実現するDevices’ outputsの構造化](https://qiita.com/masanobu48154/items/d6572d8de6d50e11c6fb "Genie Parsersで実現するDevices’ outputsの構造化")
[- Genie Documentation](https://pubhub.devnetcloud.com/media/genie-docs/docs/index.html "Genie Documentation")
[- genie-feature-browser](https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/ "genie-feature-browser")