最新のpyATS Blitz: 効率的なネットワーク自動化の実現方法

この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2024 一枚目の 5 日目として投稿しています。

https://qiita.com/advent-calendar/2024/cisco

過去のアドベントカレンダーも是非覗いてみてください。

2023年版: https://qiita.com/advent-calendar/2023/cisco
2022年版: https://qiita.com/advent-calendar/2022/cisco
2021年版: https://qiita.com/advent-calendar/2021/cisco
2021年版(2枚目): https://https://qiita.com/advent-calendar/2021/cisco2
2020年版: https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco2
2019年版: https://qiita.com/advent-calendar/2019/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2017年版: https://qiita.com/advent-calendar/2017/cisco

# はじめに

今回の記事は2019年に書いた下記記事の最新版になります。当初の pyATS Blitz から進化を遂げ、実装変更により内容が古くなってしまっていたので、書き直すことにしました。

https://qiita.com/tahigash/items/fa00872a1ced946da23b

内容が少し pyATS を知っている前提になっているため、私の下記過去記事もぜひ参考にしてください。

https://qiita.com/tahigash/items/753cffdbf6380afa823f

https://qiita.com/tahigash/items/2776b00e97a416b98e3d

https://qiita.com/tahigash/items/05a2cad4365ec1a47f55

https://qiita.com/tahigash/items/44a687f84ad3e689a35d

## pyATS Blitzとは？
ネットワーク運用の効率化や運用投入前の事前テストなどは、タスクの自動化によって大きく前進します。`pyATS Blitz`は、`Cisco Systems` が提供するネットワークテストおよび自動化フレームワーク `pyATS` の一部であり、柔軟性と直感的な記述を両立した自動化ツールです。

### pyATS Blitzの特徴

以下に、`pyATS Blitz`の主な特徴を簡潔にまとめました：

- **YAMLベースの簡潔な構文**: 読みやすく学習コストが低い
- **幅広い操作性**: CLI実行、API呼び出し、設定適用、トラブルシューティングが可能
- **構造化データ解析**: Cisco Genieライブラリを利用してCLI出力を簡単に処理
- **条件付き処理**: 問題発生時に追加アクションを自動実行（`run_condition`）
- **並列処理とループ**: 大規模ネットワークでも効率的な操作が可能
- **柔軟な条件判断**: `include`/`exclude`で文字列、正規表現、`Dq` を使った細かい条件判断が可能
- **対話的プロンプト対応**: 自動応答で完全な自動化を実現（`reply`）
- **ログ保存**: すべての結果が自動で記録される
- **スケーラブル**: 小規模から大規模ネットワークまで対応可能

これらの特徴により、`pyATS Blitz`はネットワーク自動化と効率化を強力にサポートします。

### この記事の目標

- `pyATS Blitz`の基本操作とアクションを詳しく解説
- 現場での運用に役立つ応用シナリオを紹介

# pyATS Blitzの概要

`pyATS Blitz`は、以下の主要な構成要素で動作します

1. **testbed**: 操作対象のネットワークデバイス情報を記載
2. **action**: 実行する操作の指示
3. **include/exclude**: アクション結果の判定条件

## Testbedの記述方法

`pyATS Blitz`を実行するためには、ネットワークデバイスの接続情報を記載した `testbed` ファイルが必要です。

### testbed.yaml の基本例:
```yaml:testbed.yaml
devices:
  switch1:
    os: ios
    connections:
      cli:
        protocol: ssh
        ip: 10.0.0.1
    credentials:
      default:
        username: admin
        password: adminpass
```

### 構成のポイント:

- **devices**: テスト対象のデバイスをリスト化
- **connections**: CLI接続の詳細（SSH/Telnetなど）
- **credentials**: デバイスへの認証情報

`testbed`ファイルは`pyaTS Blitz`スクリプトの全アクションに対する基盤として機能します。

# アクションの使い方

`pyATS Blitz`は、`execute` や `parse` など、複数のアクションをサポートしており、これらを組み合わせてテストや自動化タスクを記述できます。

:::note
本記事では代表的なアクションのみを紹介しております。実際にはもっと多くのアクションが用意されているので、[ドキュメント](https://pubhub.devnetcloud.com/media/genie-docs/docs/blitz/design/actions/actions.html#list-of-blitz-actions)も参照してください。
:::

## execute: コマンドの実行

`execute`はCLIコマンドを直接実行するための基本アクションです。

例: インターフェース情報の取得。

```yaml
test_sections:
  - check_interface
    - execute:
        device: PE1
        command: "show ip interface brief"
        alias: "interface_status"
```

解説:
- **command**: 実行するCLIコマンド
- **alias**: 結果にラベルを付けて後続のアクションで使用可能

## parse: 結果の解析

`parse`は、CLIコマンドの出力を構造化データとして解析します。`pyATS` の `genieparser` ライブラリを使用して、結果を簡単に扱える形式(Python Dictionary)に変換します。

例: バージョン情報の解析。

```yaml
test_sections:
  - check_version
    - parse:
        device: PE1
        command: "show version"
        alias: "version_output"
        include:
          - "17.12"
```

ポイント:

- **include**: 結果に特定の文字列が含まれているか確認
- **alias**: 結果を後続のアクションに渡すための識別子

:::note
pyATS の parser は Cisco 社内及び社外からライブラリに追加されており、数千の parser が存在します。こちらの[ページ](https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/parsers)にて parse アクションで使用できる parser を確認することができます。
:::

## configure: 設定の適用

configureは、ネットワークデバイスに新しい設定を適用します。

例: VLAN設定の追加。

```yaml
test_sections:
  - configure_vlan:
    - configure:
        device: PE1
        commands:
          - "vlan 200"
          - "name Accounting"
```
上記は設定の各行をリストで記載していますが、下記のように書くこともできます。私はこちらの方を好んで使います。
```yaml
test_sections:
  - configure_vlan:
    - configure:
        device: PE1
        commands: |
          vlan 200
           name Accounting
```

利点:
- CLIで設定を直接送信
- 設定履歴を自動的にログに保存

## api: デバイスAPIの呼び出し

apiを使用することで、デバイスのAPIを呼び出し、CLIよりも効率的な操作が可能です。

例: BGPネイバー情報の取得。

```yaml
test_sections:
  - get_bgp_neighbors
    - api:
        device: PE1
        function: "get_bgp_neighbors"
        alias: "bgp_neighbors"
```
特徴:

- 効率的なデータ取得
- CLIを直接介さずに構造化データを直接取得可能

:::note
pyATS のデバイスAPIは Cisco 社内及び社外からライブラリに追加されており、数千のAPIが存在します。こちらの[ページ](https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/apis)にて用意されているAPIを確認することができます。
:::

# include/excludeでの結果判定

Blitzの `include` / `exclude` は `execute`, `parse`, `api` アクションの結果を判定するのに使います。

:::note warn
`execute`, `parse`, `api` アクションで `include` / `exclude` を使っていない場合、意図しないエラーが発生した場合を除き、結果判定条件がないとして、デフォルトで結果は `passed` となります。
:::

**includeとexcludeによる絞り込み**
- **include**: 結果に含める文字列を指定
- **exclude**: 結果から除外する文字列を指定

例: インターフェースの状態を確認。

```yaml
test_sections:
  - check_interface
    - execute:
        device: PE1
        command: "show ip interface brief"
        alias: "interface_status"
        include:
          - "up"
        exclude:
          - "down"
```

上記の例だと、`show ip interface brief` コマンドの出力に `up` が含まれており、`down` が含まれない場合のみ、結果は `passed` となり、それ以外では `failed` となります。

また、`execute` アクションで show コマンドを実行して結果を `include` / `exclude` で判断する場合には、出力は単一の文字列となるため、下記のようにその行の全体を `include` に書く以外、特定のインタフェースのみを確認するといったことが困難です。

```yaml
        include:
          - "Ethernet0/2            unassigned      YES unset  up                    up"
```

そのため、`parser` が用意されている限り、通常は `parse` アクションと Dq を使った条件判断が推奨されます。

```yaml
test_sections:
  - check_interface
    - parse:
        device: PE1
        command: "show ip interface brief"
        alias: "interface_status"
        include:
         - contains_key_value('interface', 'Ethernet0/2').contains_key_value('protocol', 'up')
         - contains_key_value('interface', 'Ethernet0/2').contains_key_value('status', 'up')
```

上記のように `parse` を使うと show コマンドの結果が構造化された Python Dictionary となるため、構造体に沿って、Dq を用いて特定のインタフェースに対して、特定のキーに対する値を厳格にチェックすることができます。

:::note
`Dq` は python dictionary を効率良く簡単にアクセスするために非常に便利でパワフルな機能です。`Dq` の詳細については、こちらの[ドキュメント](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/utils/index.html?highlight=dq#dq)や私の[過去記事](https://qiita.com/tahigash/items/2776b00e97a416b98e3d)も参考にしてください。
:::

# 高度なアクション

複雑でスケールして自動化を実現するために高度なアクションも用意されています。

## parallel: 並列処理

複数のアクションを並列実行し、処理時間を短縮します。

例: 複数コマンドの同時実行。

```yaml
test_sections:
  - run_show_command_in_parallel:
    - parallel:
      - execute:
          device: PE1
          command: "show ip interface brief"
      - execute:
          device: PE2
          command: "show ip interface brief"
```

:::note
通常はデバイスへのコネクションは１つとなるため、異なるデバイスに対して並列実行を行います。
:::

## loop: 繰り返し処理
複数対象に対して同じアクションを実行します。

例: インターフェースごとのコンフィグを確認。

```yaml
test_sections:
  - check_interface_config
    - loop:
        loop_variable_name: interface
        value:
          - "GigabitEthernet0/0"
          - "GigabitEthernet0/1"
        actions:
          execute:
            device: PE1
            command: "show running-config interface %VARIABLES{interface}"
```

## run_condition: 条件付き処理
前のアクション結果に基づいて次のアクションを制御します。

例: 問題発生時の詳細なトラブルシューティング。


```yaml
test_sections:
  - verify_interface_up
    - parse:
        command: "show ip interface brief"
        alias: "cli"
        include:
          - contains_key_value('interface', 'Ethernet0/2').contains_key_value('protocol', 'up')
          - contains_key_value('interface', 'Ethernet0/2').contains_key_value('status', 'up')
    - run_condition:
        if: "%VARIABLES{cli} == passed"
        actions:
          - execute:
              device: PE1
              command: "show logging"
          - execute:
              device: PE1
              command: "show running-config interface GigabitEthernet0/2"
```

`Ethernet0/2` が `down` だった場合(parseアクションが`failed`だった場合)にのみ `show loggin`, `show running-config interface GigabitEthernet0/2` を実行する。

# 繰り返し処理: max_timeとcheck_interval

## 繰り返し処理の目的
`max_time`と`check_interval`は、アクションを繰り返し実行し、特定の条件(`include`/`exclude`)が満たされるまで待機する仕組みです。この機能により、動的なネットワーク環境における状態変化を監視し、効率的に対応できます。

## パラメータの詳細
- **max_time**: 最大待機時間（秒単位）
- **check_interval**: 繰り返し間隔（秒単位）
- **include/exclude**: 結果の条件フィルタリング

## 実用例: インターフェース状態の監視
目的: インターフェースがup状態になるまで繰り返し確認。

```yaml
test_sections:
  - verify_interface_status
    - parse:
        device: PE1
        command: "show ip interface brief"
        max_time: 60
        check_interval: 5
        include:
          - contains_key_value('interface', 'Ethernet0/2').contains_key_value('protocol', 'up')
          - contains_key_value('interface', 'Ethernet0/2').contains_key_value('status', 'up')
```
動作:
1. show ip interface briefを実行
2. Ethernet0/2 が up か確認
3. 含まれない場合は5秒間隔で再試行
4. 最大60秒間待機し、条件が満たされない場合は`failed`とする

## 応用例: BGPネイバーの確立監視
目的: BGPネイバーがEstablished状態になるまで監視。

```yaml
test_sections:
  - verify_bgp_neighbor
    - parse:
        command: "show bgp all neighbors"
        max_time: 90
        check_interval: 15
        include:
          - contains_key_value('neighbor', '10.1.11.1').contains_key_value('session_state', 'Established')
```
この例では、BGPネイバーが確立するまで最大90秒間監視し、15秒ごとに状態をチェックします。

:::note
状態の変化を待つための `sleep` アクションも用意されていますが、可能な限り早く検知し、テストを進めるために `sleep` アクションではなく `max_time`/`check_interval` の使用を推奨します。
:::

:::note warn
デフォルトでは `max_time`/`check_interval` は設定されないので、最初の試行で条件が満たされないと結果は `failed` になります。
:::

# Reply機能の活用
## Reply機能とは？
CLIで対話的なプロンプトが表示される場合、reply機能を使用して自動応答を送信できます。この機能により、ユーザー介入なしで操作を完了できます。

## 使用例: 再起動時の確認プロンプトへの応答
```yaml
test_sections:
  - device_reload
    - execute:
        device: PE1
        command: "reload"
        reply:
          - pattern: "Proceed with reload? [confirm]"
            action: sendline()  # Enterキーを送信
```
解説:
1. reloadコマンドを実行
2. "Proceed with reload?"のプロンプトが表示された場合に自動でEnterを送信
3. 対話的操作を自動化

ここでは上記 `reload` コマンドを `execute` アクションで実行する例としていますが、実際には同様のアクションを行うデバイスAPIが用意されている場合には、API を使うことで `reply` などが API 内で処理されており、簡素化できます。そのため、API がある場合はそちらを使うことを推奨します。

```yaml
test_sections:
  - device_reload
    - api:
        device : PE1
        function : execute_reload
```

# 応用シナリオ
ここでは、実際のネットワーク運用で役立つ応用例を紹介します。

### VLAN設定の一括適用と検証
目的: 複数のスイッチに対してVLANを一括設定し、正しく適用されているかを検証。

```yaml
test_sections:
  - configure_vlan
    - loop:
        loop_variable_name: device_name
        value:
          - PE1
          - PE2
        actions:
          - configure:
              device: "%VARIABLES{device_name}"
              commands: |
                vlan 100
                 name Marketing
          - parse:
              device: "%VARIABLES{device_name}"
              command: "show vlan brief"
              include:
                - contains_key_value('vlan', 'vlan101').contains_key_value('vlan_name', 'Marketing')
```
動作:
1. 各デバイスにVLAN 100を作成
2. show vlan briefで設定を確認
3. VLAN 100が存在し、適切な名前が含まれていない場合はエラーとしてログに記録

## BGPネイバーの問題診断
目的: BGPネイバーが非確立の場合、関連情報を収集して原因を診断。

```yaml
test_sections:
  - verify_bgp_neighbor
    - parse:
        alias: cli
        command: "show bgp all neighbors"
        max_time: 90
        check_interval: 15
        include:
          - contains_key_value('neighbor', '10.1.11.1').contains_key_value('session_state', 'Established')
    - run_condition:
        if: "%VARIABLES{cli} == passed"
        actions:
          - execute:
              device: PE1
              command: "show logging"
          - execute:
              device: PE1
              command: "show run | sec router bgp"
          - execute:
              device: PE1
              command: "ping 10.1.11.1"
```

## トラブルシューティングとベストプラクティス
## よくあるエラーと解決策
エラー1: Testbed構成ミス:

- **問題**: IPや認証情報が間違っている
- **解決策**: Testbedファイルを事前に検証

エラー2: YAMLフォーマットエラー:

- **問題**: インデントミスや構文エラー
- **解決策**: YAMLフォーマッタで整形確認

## ベストプラクティス
- **小さなスクリプトから始める**: 複雑な処理はステップごとにテスト
- **ログの活用**: アクションごとの結果を記録し、問題発生時にすぐ確認できるようにする
- **柔軟な条件指定**: run_conditionやmax_timeを使用して動的に処理を制御
- **柔軟な結果判定**: `parse`, `include`/`exclude`, `Dq` を使いこなす

# まとめ
pyATS Blitzは、ネットワーク運用を自動化により効率化し、作業効率を向上させるための強力なツールです。本記事で紹介した機能と応用例を参考に、より洗練された自動化を実現してください。

# 参考リンク
- [pyATS Document](https://developer.cisco.com/docs/pyats/api/)
- [Genie Document](https://developer.cisco.com/docs/genie-docs/)
  - [Blitz](https://pubhub.devnetcloud.com/media/genie-docs/docs/blitz/index.html)
  - [Genie CLI - Genie Run](https://pubhub.devnetcloud.com/media/genie-docs/docs/cli/genie_run.html)
  - [Genie API Browser](https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/)
  - [Genie Useful Libraries](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/utils/index.html#)
    - [Dq](https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/utils/index.html#dq)
  - [Genie Parser GitHub](https://github.com/CiscoTestAutomation/genieparser)
  - [Genie Libs GitHub](https://github.com/CiscoTestAutomation/genielibs)
- [pyATS community on Webex space](https://eurl.io/#r18UzrQVr)

# 免責事項

本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。