pyATS を使ってルーティング情報の更新を確認する

pyATS とは

pyATS は Cisco が開発している、テストフレームワークです。python で書かれており、ネットワークのテストに必要な機能が実装されています。今回は、コードを書かずに pyATS に用意されている CLI ツールを使用し、ネットワーク機器の状態が変化した際の差分を確認していきます。

pyATS のインストール

Cisco DevNet: APIs, SDKs, Sandbox, and Community for Cisco Developers

公式ドキュメントの手順に従い、venv の環境に、pyats のパッケージをインストールします。

[root@localhost ~]# mkdir pyats
[root@localhost ~]# cd pyats
[root@localhost pyats]# python3.6 --version
Python 3.6.8
[root@localhost pyats]# python3.6 -m venv .
[root@localhost pyats]# ls
bin  include  lib  lib64  pyvenv.cfg
[root@localhost pyats]# source bin/activate
(pyats) [root@localhost pyats]# pip install --upgrade pip setuptools
(pyats) [root@localhost pyats]# pip install pyats[full]

構成

以下のようなマルチOSな環境で RT2 に変更を加えます。前後でどのように状態が変化するのか pyATS を使って確認します。

f:id:naoki029:20190914190418p:plain

testbed の準備

ネットワーク機器の情報を yaml ファイルに記載しておきます。

testbed:
  name: My topology
  credentials:
    default:
      username: admin
      password: password
    enable:
      password: password

devices:
  csr1kv-1:
    type: 'router'
    os: 'iosxe'
    platform: asr1k
    alias: 'RT1'
    connections:
      cli:
        protocol: ssh
        ip: "172.16.0.11"
  n9kv-1:
    type: 'router'
    os: 'nxos'
    platform: n9kv
    alias: 'RT2'
    connections:
      cli:
        protocol: ssh
        ip: "172.16.0.12"
  n9kv-2:
    type: 'router'
    os: 'nxos'
    platform: n9kv
    alias: 'RT3'
    connections:
      cli:
        protocol: ssh
        ip: "172.16.0.13"
  iosv-1:
    type: 'router'
    os: 'ios'
    platform: ios
    alias: 'RT4'
    connections:
      cli:
        protocol: ssh
        ip: "172.16.0.14"

pyATS learn の実行 【変更前】

ospfrouting の状態を pyATS を使って収集します。output1 ディレクトリに収集した情報が保存されます。

(pyats) [root@localhost pyATS_diff]# genie learn "ospf" "routing" --testbed-file testbed.yaml --output output1

Learning '['ospf', 'routing']' on devices '['csr1kv-1', 'n9kv-1', 'n9kv-2', 'iosv-1']'
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:14<00:00,  7.15s/it]
+==============================================================================+
| Genie Learn Summary for device csr1kv-1                                      |
+==============================================================================+
|  Connected to csr1kv-1                                                       |
|  -   Log: output1/connection_csr1kv-1.txt                                    |
|------------------------------------------------------------------------------|
|  Learnt feature 'ospf'                                                       |
|  -  Ops structure:  output1/ospf_iosxe_csr1kv-1_ops.txt                      |
|  -  Device Console: output1/ospf_iosxe_csr1kv-1_console.txt                  |
|------------------------------------------------------------------------------|
|  Learnt feature 'routing'                                                    |
|  -  Ops structure:  output1/routing_iosxe_csr1kv-1_ops.txt                   |
|  -  Device Console: output1/routing_iosxe_csr1kv-1_console.txt               |
|==============================================================================|


+==============================================================================+
| Genie Learn Summary for device n9kv-1                                        |
+==============================================================================+
|  Connected to n9kv-1                                                         |
|  -   Log: output1/connection_n9kv-1.txt                                      |
|------------------------------------------------------------------------------|
|  Learnt feature 'ospf'                                                       |
|  -  Ops structure:  output1/ospf_nxos_n9kv-1_ops.txt                         |
|  -  Device Console: output1/ospf_nxos_n9kv-1_console.txt                     |
|------------------------------------------------------------------------------|
|  Learnt feature 'routing'                                                    |
|  -  Ops structure:  output1/routing_nxos_n9kv-1_ops.txt                      |
|  -  Device Console: output1/routing_nxos_n9kv-1_console.txt                  |
|==============================================================================|


+==============================================================================+
| Genie Learn Summary for device n9kv-2                                        |
+==============================================================================+
|  Connected to n9kv-2                                                         |
|  -   Log: output1/connection_n9kv-2.txt                                      |
|------------------------------------------------------------------------------|
|  Learnt feature 'ospf'                                                       |
|  -  Ops structure:  output1/ospf_nxos_n9kv-2_ops.txt                         |
|  -  Device Console: output1/ospf_nxos_n9kv-2_console.txt                     |
|------------------------------------------------------------------------------|
|  Learnt feature 'routing'                                                    |
|  -  Ops structure:  output1/routing_nxos_n9kv-2_ops.txt                      |
|  -  Device Console: output1/routing_nxos_n9kv-2_console.txt                  |
|==============================================================================|


+==============================================================================+
| Genie Learn Summary for device iosv-1                                        |
+==============================================================================+
|  Connected to iosv-1                                                         |
|  -   Log: output1/connection_iosv-1.txt                                      |
|------------------------------------------------------------------------------|
|  Learnt feature 'ospf'                                                       |
|  -  Ops structure:  output1/ospf_ios_iosv-1_ops.txt                          |
|  -  Device Console: output1/ospf_ios_iosv-1_console.txt                      |
|------------------------------------------------------------------------------|
|  Learnt feature 'routing'                                                    |
|  -  Ops structure:  output1/routing_ios_iosv-1_ops.txt                       |
|  -  Device Console: output1/routing_ios_iosv-1_console.txt                   |
|==============================================================================|


(pyats) [root@localhost pyATS_diff]# ls -1 output1/
connection_csr1kv-1.txt
connection_iosv-1.txt
connection_n9kv-1.txt
connection_n9kv-2.txt
ospf_ios_iosv-1_console.txt
ospf_ios_iosv-1_ops.txt
ospf_iosxe_csr1kv-1_console.txt
ospf_iosxe_csr1kv-1_ops.txt
ospf_nxos_n9kv-1_console.txt
ospf_nxos_n9kv-1_ops.txt
ospf_nxos_n9kv-2_console.txt
ospf_nxos_n9kv-2_ops.txt
routing_ios_iosv-1_console.txt
routing_ios_iosv-1_ops.txt
routing_iosxe_csr1kv-1_console.txt
routing_iosxe_csr1kv-1_ops.txt
routing_nxos_n9kv-1_console.txt
routing_nxos_n9kv-1_ops.txt
routing_nxos_n9kv-2_console.txt
routing_nxos_n9kv-2_ops.txt
(pyats) [root@localhost pyATS_diff]# 

_console.txt にはコンソール出力、_ops.txt には pyATS がコンソール出力を解析した結果を key-value 形式(json)で保存してくれます。

routing_iosxe_csr1kv-1_ops.txt

(pyats) [root@localhost pyATS_diff]# cat output1/routing_iosxe_csr1kv-1_ops.txt 
{
  "_exclude": [],
  "attributes": null,
  "commands": null,
  "connections": null,
  "context_manager": {},
  "info": {
    "vrf": {
      "default": {
        "address_family": {
          "ipv4": {
            "routes": {
              "1.1.1.1/32": {
                "active": true,
                "next_hop": {
                  "outgoing_interface": {
                    "Loopback0": {
                      "outgoing_interface": "Loopback0"
                    }
                  }
                },
                "route": "1.1.1.1/32",
                "source_protocol": "connected",
                "source_protocol_codes": "C"
              },
              "1.1.1.2/32": {
                "active": true,
                "metric": 2,
                "next_hop": {
                  "next_hop_list": {
                    "1": {
                      "index": 1,
                      "next_hop": "10.0.0.1",
                      "outgoing_interface": "GigabitEthernet2",
                      "updated": "02:10:21"
                    }
                  }
                },
--- snip ---

routing_iosxe_csr1kv-1_console.txt

+++ csr1kv-1: executing command 'show vrf detail' +++
show vrf detail

--- snip ---

+++ csr1kv-1: executing command 'show ip route' +++
show ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      1.0.0.0/32 is subnetted, 4 subnets
C        1.1.1.1 is directly connected, Loopback0
O        1.1.1.2 [110/2] via 10.0.0.1, 02:10:21, GigabitEthernet2
O        1.1.1.3 [110/2] via 10.0.0.3, 02:10:21, GigabitEthernet3
O        1.1.1.4 [110/42] via 10.0.0.3, 02:10:21, GigabitEthernet3
                 [110/42] via 10.0.0.1, 02:10:21, GigabitEthernet2
      10.0.0.0/8 is variably subnetted, 6 subnets, 2 masks
C        10.0.0.0/31 is directly connected, GigabitEthernet2
L        10.0.0.0/32 is directly connected, GigabitEthernet2
C        10.0.0.2/31 is directly connected, GigabitEthernet3
L        10.0.0.2/32 is directly connected, GigabitEthernet3
O        10.0.0.4/31 [110/41] via 10.0.0.1, 02:10:21, GigabitEthernet2
O        10.0.0.6/31 [110/41] via 10.0.0.3, 02:10:21, GigabitEthernet3
csr1kv-1#
+++ csr1kv-1: executing command 'show ipv6 route vrf mgmt updated' +++
show ipv6 route vrf mgmt updated
% IPv6 routing table mgmt does not exist
csr1kv-1#

--- snip ---

RT2 の設定変更

RT2 (n9kv-1v) の Eth 1/3 に IP を設定して、OSPF でアドレスを広告させます。

n9kv-1(config)# int eth 1/3
n9kv-1(config-if)# no switchport 
n9kv-1(config-if)# ip address 192.168.0.254/24
n9kv-1(config-if)# ip router ospf 1 area 0
n9kv-1(config-if)# no shut

pyATS learn の実行 【変更後】

再度 genie learn を実行します。出力結果を output2 ディレクトリに保存するように指定しています。

(pyats) [root@localhost pyATS_diff]# genie learn "ospf" "routing" --testbed-file testbed.yaml --output output2

pyATS を使って差分を確認

genine diff コマンドを使うことで、pyATS で得られた2つの状態の差分を比較することができます。OSPF は新しくインターフェースを設定した、RT2(n9kv-1)のみに変更があったことがわかります。一方でルーティング情報に関しては、全てのデバイスで変更があったことがわかります。

(pyats) [root@localhost pyATS_diff]# genie diff output1 output2
1it [00:00, 65.76it/s]
+==============================================================================+
| Genie Diff Summary between directories output1/ and output2/                 |
+==============================================================================+
|  File: ospf_nxos_n9kv-1_ops.txt                                              |
|   - Diff can be found at ./diff_ospf_nxos_n9kv-1_ops.txt                     |
|------------------------------------------------------------------------------|
|  File: ospf_nxos_n9kv-2_ops.txt                                              |
|   - Identical                                                                |
|------------------------------------------------------------------------------|
|  File: ospf_iosxe_csr1kv-1_ops.txt                                           |
|   - Identical                                                                |
|------------------------------------------------------------------------------|
|  File: ospf_ios_iosv-1_ops.txt                                               |
|   - Identical                                                                |
|------------------------------------------------------------------------------|
|  File: routing_nxos_n9kv-1_ops.txt                                           |
|   - Diff can be found at ./diff_routing_nxos_n9kv-1_ops.txt                  |
|------------------------------------------------------------------------------|
|  File: routing_nxos_n9kv-2_ops.txt                                           |
|   - Diff can be found at ./diff_routing_nxos_n9kv-2_ops.txt                  |
|------------------------------------------------------------------------------|
|  File: routing_ios_iosv-1_ops.txt                                            |
|   - Diff can be found at ./diff_routing_ios_iosv-1_ops.txt                   |
|------------------------------------------------------------------------------|
|  File: routing_iosxe_csr1kv-1_ops.txt                                        |
|   - Diff can be found at ./diff_routing_iosxe_csr1kv-1_ops.txt               |
|------------------------------------------------------------------------------|

変更の詳細は各ファイルに出力されています。

(pyats) [root@localhost pyATS_diff]# ls -1
diff_ospf_nxos_n9kv-1_ops.txt
diff_routing_ios_iosv-1_ops.txt
diff_routing_iosxe_csr1kv-1_ops.txt
diff_routing_nxos_n9kv-1_ops.txt
diff_routing_nxos_n9kv-2_ops.txt
output1
output2
testbed.yaml
(pyats) [root@localhost pyATS_diff]# 

RT1(csr1kv-1) のルーティング情報の差分を確認すると、新しいルートが追加されていることが確認できます。その他、何時間前にOSPF でルート受け取っているのかを確認することができます。

(pyats) [root@localhost diff12]# cat diff_routing_iosxe_csr1kv-1_ops.txt 
--- output1/routing_iosxe_csr1kv-1_ops.txt
+++ output2/routing_iosxe_csr1kv-1_ops.txt
info:
 vrf:
  default:
   address_family:
    ipv4:
     routes:
+      192.168.0.0/24: 
+       active: True
+       metric: 41
+       next_hop: 
+        next_hop_list: 
+         1: 
+          index: 1
+          next_hop: 10.0.0.1
+          outgoing_interface: GigabitEthernet2
+          updated: 00:01:55
+       route: 192.168.0.0/24
+       route_preference: 110
+       source_protocol: ospf
+       source_protocol_codes: O
      1.1.1.2/32:
       next_hop:
        next_hop_list:
         1:
+          updated: 02:13:59
-          updated: 02:10:21
      1.1.1.3/32:
       next_hop:
        next_hop_list:
         1:
+          updated: 02:13:59
-          updated: 02:10:21
      1.1.1.4/32:
       next_hop:
        next_hop_list:
         1:
+          updated: 02:13:59
-          updated: 02:10:21
         2:
+          updated: 02:13:59
-          updated: 02:10:21
      10.0.0.4/31:
       next_hop:
        next_hop_list:
         1:
+          updated: 02:13:59
-          updated: 02:10:21
      10.0.0.6/31:
       next_hop:
        next_hop_list:
         1:
+          updated: 02:13:59
-          updated: 02:10:21

まとめ

pyATS を使って、ネットワーク機器の状態の差分を確認しました。pyATS の一番のメリットは show コマンドの情報を機械が読みやすい key-value 形式で出力してくれることだと思います。pyATS と Ansible を組み合わせれば、かなり実用的な自動化環境が構築できそうな印象を受けました。