AnsibleによるSi-Rのネットワークテスト自動化

AnsibleとTextFSMパーサー、TextFSMのネットワーク用テンプレート集であるNTC Templatesを使用するとで、AnsibleからCLIコマンドの出力結果を分析することができます。今回はSi-Rルーターでのping疎通確認とファームウェアのバージョン確認を行ってみました。

# 環境構築

Ansibleがインストール済みであることを前提とします。
Ansibleのマシンと同セグメントに下記の機器を設置します。

- Si-R G120
    - version : V20.54 
    - IPv4アドレス : 192.168.50.21/24
    - デフォルトルート : 192.168.50.1
    - ProxyDNS : 有効

## Si-R用Ansibleモジュールのインストール

Si-Rには公式モジュールが存在しないため自作しました。Ansible galaxyからダウンロード可能です。
```
ansible-galaxy collection install caribouhy.sir
```

https://github.com/caribouHY/ansible_sir

## TextFSMのインストール

pipでTextFSMをインストールします。
```
pip install textfsm
```

## NTC-Templatesのダウンロード

今回使用するフォルダにNTC-Templatesをダウンロードします。なおダウンロードしたリポジトリ内の`ntc-templates`フォルダのみあればよいため、他は削除しています。使用するテンプレートのみをピックアップしてダウンロードしても問題ないです。
```
git clone https://github.com/networktocode/ntc-templates.git ntc

mv ntc/ntc_templates/ ./
rm -rf ntc
```

https://github.com/networktocode/ntc-templates

## (オプション)network-engineロールのインストール
`ansible-network.network-engine`というRoleを使用することでTextFSMによるパース処理を簡潔に記載することができます。

```
ansible-galaxy role install ansible-network.network-engine
```

# Playbook

下記の2点を実施します。
- ファームウェアバージョンが`V20.54`であることを確認する
- `192.168.50.1`と`www.google.co.jp`へpingを打つ

```yaml:inventory.yml
---
all:
  children:
    Si_R:
      hosts:
        G120-1:
          ansible_host: 192.168.50.21
      vars:
        ansible_user: admin
        ansible_password: password
        ansible_connection: network_cli
        ansible_network_os: caribouhy.sir.sir
```
インベントリでは`ansible_network_os`として`caribouhy.sir.sir`を指定してください。基本的にCisco等の他機種と同様の書き方です。

```yaml:playbook
---
- name: Si-R network test
  hosts: Si_R
  gather_facts: false

  tasks:
    # Si-R上でshow system informationコマンドを実行
    - name: Execute show system information
      caribouhy.sir.sir_command:
        commands:
          - show system information
      register: result

    # TextFSMによるパース
    - name: Parse show system information
      ansible.builtin.set_fact:
        sys_info: "{{ result.stdout[0] | parse_cli_textfsm('./ntc_templates/templates/fsas_sir_show_system_information.textfsm') }}"

    # ファームウェアバージョンの判定
    - name: Check firmware version
      ansible.builtin.assert:
        that:
          - sys_info[0].VERSION == "20.54"
        fail_msg: Firmware ver. is not V20.54

    # デフォゲへの疎通確認
    - name: Ping gateway
      caribouhy.sir.ping:
        dest: 192.168.50.1

    # インターネットサイトへの疎通確認
    - name: Ping Google
      caribouhy.sir.ping:
        dest: www.google.co.jp
        source: 192.168.50.21
        afi: ip
        count: 2
```

`caribouhy.sir.sir_command`モジュールでshowコマンドを実行します。`parse_cli_textfsm`フィルターでCLI出力結果のパースを行い、`set_fact`モジュールで変数に代入します。バージョンの判定は`assert`モジュールで実施します。
Pingは`caribouhy.sir.ping`モジュールを使用します。パラメータの設定により、送信回数やソースIP等を設定可能です。

# 実行結果
```
$ ansible-playbook -i inventory.yaml playbook.yml -v

PLAY [Si-R network test] 
************************************************************************
TASK [Execute show system information] 
************************************************************************
ok: [G120-1] => {
    "changed": false,
    "stdout": ["Current-time :(省略)\nUSB   : ------"],
    "stdout_lines": [[(省略)]]
}

TASK [Parse show system information] 
************************************************************************
ok: [G120-1] => {
    "ansible_facts": {
        "sys_info": [{
            "CONFIG_FILE": "config1",
            (省略),
            "SYSTEM": "Si-R G120",
            "VERSION": "20.54"
        }]
    }, 
    "changed": false
}

TASK [Check firmware version] 
***********************************************************************
ok: [G120-1] => {
    "changed": false,
    "msg": "All assertions passed"
}

TASK [Ping gateway] 
************************************************************************
ok: [G120-1] => {
    "changed": false,
    "commands": "ping 192.168.50.1 repeat 5",
    "packet_loss": "0%",
    "packets_rx": 5,
    "packets_tx": 5,
    "rtt": {"avg": 0, "max": 0, "min": 0}
}

TASK [Ping Google]
************************************************************************
ok: [G120-1] => {
    "changed": false,
    "commands": "ping www.google.co.jp v4 source 192.168.50.21 repeat 2",
    "packet_loss": "0%",
    "packets_rx": 2,
    "packets_tx": 2,
    "rtt": {"avg": 7, "max": 7, "min": 7}
}

PLAY RECAP 
************************************************************************
G120-1  : ok=5    changed=0    unreachable=0    failed=0
          skipped=0    rescued=0    ignored=0 
```

# (オプション) network-engineによるパース
`ansible-network.network-engine`の`textfsm_parser`を使用するこで、`set_fact`を使用せずにパースができます。下記のPlaybookで上記と同様の処理を行えます。
`set_fact`との違いは、パース結果が`ansible_facts`内に格納されます。

```yaml:playbook
---
- name: Si-R network test
  hosts: Si_R
  gather_facts: false
  roles:
    - ansible-network.network-engine

  tasks:
    # Si-R上でshow system informationコマンドを実行
    - name: Execute show system information
      caribouhy.sir.sir_command:
        commands:
          - show system information
      register: result

    # TextFSMによるパース
    - name: Parse show system information
      textfsm_parser:
        file: "./ntc_templates/templates/fsas_sir_show_system_information.textfsm"
        content: "{{ result.stdout[0] }}"
        name: sys_info

    # ファームウェアバージョンの判定
    - name: Check firmware version
      ansible.builtin.assert:
        that:
          - ansible_facts['sys_info'][0].VERSION == "20.54"
        fail_msg: Firmware ver. is not V20.54

    # --------- pingは省略 -------------
```