Cisco VIRLをpyATSから操作する

# はじめに

以下の記事で、virlutilsの`virl generate ansible`コマンドからAnsible Inventoryファイルを自動生成し、簡単なVIRL環境のテストを行いました。
[Cisco VIRLをAnsibleから操作する(トポロジーファイル作成編)](https://qiita.com/tech_kitara/items/1c134e748d1db5558b67)

virlutilsには`virl generate pyats`コマンドもあり、pyATSのtestbedを自動生成することも可能です。
今回は、testbed生成と簡単な動作確認を行ってみます。


# testbedの生成

以下コマンドでtestbed「testbed_virl1.yml」を生成します。

```
$ virl generate pyats --output testbed_virl1.yaml TEST4
Writing testbed_virl1.yaml
```

Ansibleの場合、SSH接続情報の指定が必要ですが、pyATSはTelnet接続もサポートしています。
実際に生成されたファイルを見ると、コンソール経由のTelnet接続情報が使用されています。
ユーザ名、パスワードは、Linuxで定義した環境変数から取得する形になっています。
また、トポロジー情報も生成されています。

```yaml:testbed_virl1.yaml
testbed:

  name: venv_TEST4_XCF52W

  tacacs:
    username: "%ENV{PYATS_USERNAME}"
  passwords:
    tacacs: "%ENV{PYATS_PASSWORD}"
    enable: "%ENV{PYATS_AUTH_PASS}"
    line: "%ENV{PYATS_PASSWORD}"

devices:

  asav-1:
    alias: asav-1
    os: asa
    type: ASAv
    platform: ASAv

    connections:

      defaults:
        class: unicon.Unicon
      console:
        protocol: telnet
        ip: 10.10.20.160
        port: 17001
    custom:
      abstraction:
        order: [os, type]

  iosv-1:
    alias: iosv-1
    os: ios
    type: IOSv
    platform: IOSv

    connections:

      defaults:
        class: unicon.Unicon
      console:
        protocol: telnet
        ip: 10.10.20.160
        port: 17002
    custom:
      abstraction:
        order: [os, type]

  nx-osv9000-1:
    alias: nx-osv9000-1
    os: nxos
    type: NX-OSv 9000
    platform: NX-OSv 9000

    connections:

      defaults:
        class: unicon.Unicon
      console:
        protocol: telnet
        ip: 10.10.20.160
        port: 17004
    custom:
      abstraction:
        order: [os, type]

topology:
  asav-1:
    interfaces:
      GigabitEthernet0/0:
        link: asav-1-to-iosv-1
        type: ethernet
      GigabitEthernet0/1:
        link: asav-1-to-nx-osv9000-1
        type: ethernet
      Management0/0:
        ipv4: 172.16.30.108/24
        link: flat
        type: ethernet
  iosv-1:
    interfaces:
      GigabitEthernet0/1:
        link: asav-1-to-iosv-1
        type: ethernet
      GigabitEthernet0/0:
        ipv4: 172.16.30.109/24
        link: flat
        type: ethernet
  nx-osv9000-1:
    interfaces:
      Ethernet1/1:
        link: asav-1-to-nx-osv9000-1
        type: ethernet
      mgmt0:
        ipv4: 172.16.30.110/24
        link: flat
        type: ethernet
```

VIRLのAutoNetkitでユーザ名、パスワードを自動設定した場合は、すべて「cisco」になるため、環境変数の設定内容は以下になります。

```
$ export PYATS_USERNAME=cisco
$ export PYATS_PASSWORD=cisco
$ export PYATS_AUTH_PASS=cisco
```
ただし、`virl generate pyats`コマンド実行中に、デフォルトのログイン情報を使って特権モードまで移行してくれるようなので、環境変数を定義しなくてもpyATSのスクリプト実行は可能でした。

**＜補足：コンソール接続のタイムアウトについて＞**
IOSvやNX-OSv9000のタイムアウト時間はデフォルトで10分のため、`virl generate pyats`コマンド実行後、10分以上経ってからpyATSスクリプトを実行する場合、ログイン情報を再入力する必要があります。ASAvの場合、AutoNetkitでコマンド「console timeout 0」が適用されるため、手動でログインして特権モードから抜けない限り、再入力は不要でした。

自動設定以外のユーザ名/パスワードを使っている場合は、スクリプト実行に環境変数の定義は必須です。

# Pythonスクリプト
各機器の「show users」を取得してみます。

```python:pyats_virl1.py
from pyats.topology import loader

testbed = loader.load('testbed_virl1.yaml')
testbed.devices

# get show command on remote devices
host_list = ['asav-1', 'iosv-1', 'nx-osv9000-1']

for host in host_list:
    device = testbed.devices[host]
    device.connect()
    print(device.execute('show users'))
    device.disconnect()
```

# 実行結果
ASAvはコマンド非サポートのため取得できていませんが、スクリプト自体は問題なく動きました。

```
$ python pyats_virl1.py
[2019-09-21 13:41:07,716] +++ asav-1 logfile /tmp/asav-1-default-20190921T134107714.log +++
[2019-09-21 13:41:07,716] +++ Unicon plugin asa +++
[2019-09-21 13:41:19,741] Timeout occurred : Timeout value : 12.0
 Target: asav-1
 Command sent: telnet 10.10.20.160 17001
 Pattern: '.+'
 Got: ''
[2019-09-21 13:41:19,742] +++ connection to spawn: telnet 10.10.20.160 17001, id: 139787161782648 +++
[2019-09-21 13:41:19,742] connection to asav-1


Trying 10.10.20.160...
Connected to 10.10.20.160.
Escape character is '^]'.


asav-1# 
asav-1# 
[2019-09-21 13:41:30,101] +++ initializing handle +++
[2019-09-21 13:41:30,102] +++ asav-1: executing command 'terminal pager 0' +++

asav-1# 
asav-1# 
asav-1# 
asav-1# 
asav-1# 
asav-1# 
[2019-09-21 13:41:30,188] +++ asav-1: executing command 'show users' +++

asav-1# 
asav-1# 
asav-1#
[2019-09-21 13:41:43,611] +++ iosv-1 logfile /tmp/iosv-1-default-20190921T134143610.log +++
[2019-09-21 13:41:43,611] +++ Unicon plugin ios +++
[2019-09-21 13:41:55,623] Timeout occurred : Timeout value : 12.0
 Target: iosv-1
 Command sent: telnet 10.10.20.160 17002
 Pattern: '.+'
 Got: ''
[2019-09-21 13:41:55,624] +++ connection to spawn: telnet 10.10.20.160 17002, id: 139787575753192 +++
[2019-09-21 13:41:55,624] connection to iosv-1


Trying 10.10.20.160...
Connected to 10.10.20.160.
Escape character is '^]'.


iosv-1#
iosv-1#
[2019-09-21 13:42:06,079] +++ initializing handle +++
[2019-09-21 13:42:06,079] +++ iosv-1: executing command 'term length 0' +++

iosv-1#
iosv-1#
iosv-1#
iosv-1#
iosv-1#
iosv-1#
[2019-09-21 13:42:06,113] +++ iosv-1: executing command 'term width 0' +++

iosv-1#
iosv-1#
[2019-09-21 13:42:06,952] +++ iosv-1: executing command 'show version' +++
term length 0
iosv-1#term width 0
iosv-1#show version
(省略: show versionの出力結果)

iosv-1#
[2019-09-21 13:42:11,912] +++ iosv-1: config +++
config term
Enter configuration commands, one per line.  End with CNTL/Z.
iosv-1(config)#no logging console
iosv-1(config)#line console 0
iosv-1(config-line)#exec-timeout 0
iosv-1(config-line)#end
iosv-1#
[2019-09-21 13:42:18,365] +++ iosv-1: executing command 'show users' +++
show users
    Line       User       Host(s)              Idle       Location
*  0 con 0                idle                 00:00:00   

  Interface    User               Mode         Idle     Peer Address

iosv-1#
    Line       User       Host(s)              Idle       Location
*  0 con 0                idle                 00:00:00   

  Interface    User               Mode         Idle     Peer Address
[2019-09-21 13:42:33,574] +++ nx-osv9000-1 logfile /tmp/nx-osv9000-1-default-20190921T134233573.log +++
[2019-09-21 13:42:33,574] +++ Unicon plugin nxos +++
[2019-09-21 13:42:45,589] Timeout occurred : Timeout value : 12.0
 Target: nx-osv9000-1
 Command sent: telnet 10.10.20.160 17004
 Pattern: '.+'
 Got: ''
[2019-09-21 13:42:45,590] +++ connection to spawn: telnet 10.10.20.160 17004, id: 139787097647144 +++
[2019-09-21 13:42:45,590] connection to nx-osv9000-1


Trying 10.10.20.160...
Connected to 10.10.20.160.
Escape character is '^]'.



nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
nx-osv9000-1# 
[2019-09-21 13:43:01,750] +++ initializing handle +++
[2019-09-21 13:43:01,752] +++ nx-osv9000-1: executing command 'term length 0' +++
term length 0
nx-osv9000-1# 
[2019-09-21 13:43:05,650] +++ nx-osv9000-1: executing command 'term width 511' +++
term width 511
nx-osv9000-1# 
[2019-09-21 13:43:08,392] +++ nx-osv9000-1: executing command 'terminal session-timeout 0' +++
terminal session-timeout 0
nx-osv9000-1# 
[2019-09-21 13:43:09,835] +++ nx-osv9000-1: config +++
config term
Enter configuration commands, one per line. End with CNTL/Z.
nx-osv9000-1(config)# no logging console
nx-osv9000-1(config)# line console
nx-osv9000-1(config-console)# exec-timeout 0
nx-osv9000-1(config-console)# terminal width 511
nx-osv9000-1(config-console)# end
nx-osv9000-1# 
[2019-09-21 13:43:20,828] +++ nx-osv9000-1: executing command 'show users' +++
show users
NAME     LINE         TIME         IDLE          PID COMMENT
admin    ttyS0        Sep 26 15:41   .          1449 *
nx-osv9000-1# 
NAME     LINE         TIME         IDLE          PID COMMENT
admin    ttyS0        Sep 26 15:41   .          1449 *

```

# 最後に
VIRL+Ansibleと比較すると、VIRL+pyATSはマネジメントネットワーク（SSH接続環境）の作成や、ユーザ名/パスワードの指定が基本的に不要であり、便利だと感じました。それぞれ良い点があるので、CI/CDのパイプラインに組み込む時は、うまく使い分けて行きたいです。