NetBox と Batfish を利用したネットワーク運用管理・テストを試す

# 目的

社内含め、ネットワークを構成するためには多くのネットワーク機器が必要である。
機器には適切なアクセス制御やルーティングを実現するために適切な設定をしなければならず、機器の台数が増えるごとに管理・運用のコストが増加する。
台数増加に伴い Ansible 等を活用したネットワーク運用の自動化も取り入れられている。
ただ、Ansible ではノード単体を対象とするため、システム全体として正しい設定ができているか検証することは難しい。

このような課題を解決する手法として下記のような記事が掲載されていた。

- [ネットワークをモデルとして抽象化しオペレーションを高度化するチャレンジ](https://engineers.ntt.com/entry/2022/03/31/090000)

こちらの記事ではネットワークを含めた全体をモデル化し、モデルデータで各種の検証やテストを行うことが可能なソフトウェア群の実装を目指している。
こちらの記事を参考にさせて頂きつつ、ネットワーク事業者の運用状況を想定しながら実機のネットワーク設定をベースに検証やテストができる実装を目指した。
具体的には、ネットワーク機器の設定すべてを NetBox 上で一元管理し、NetBox 上でその設定を変更することで機器の設定を論理的にテスト可能なシステムの実装した。

# 構成

下記のような構成を目指す。
Ansible, NetBox, Batfish はすべて Ubuntu 20.04 上に動作させる。

![architecture.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735085/0d797531-624d-1ad3-9820-b905e0bfbd54.png)

また、運用中物理ネットワーク (Config テスト用仮想ネットワーク) は下記のような構成である。

- demo-edge[1-4]: Ubuntu20.04 マシン
- demo-router[1-4]: vjunos-switch-23.1R1.8 VM

![ip.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735085/77e39f89-bc84-3c8a-f308-120cbd36a9de.png)

# インストール

## Docker/docker-compose

NetBox/Batfish を動作させるために Docker, docker-compose が必要なため、インストールする。

Docker Install する

```
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
sudo apt install -y docker-ce docker-compose-plugin
```

Docker のインストール結果を確認する。

```
$ docker --version
Docker version 24.0.5, build ced0996
```

ユーザが docker コマンドを利用できるようにする。

```
sudo usermod -aG docker $USER
```

docker-compose をインストールする。

```
wget  https://github.com/docker/compose/releases/download/1.27.0/docker-compose-`uname -s`-`uname -m`
chmod 755 docker-compose-Linux-x86_64
sudo mv docker-compose-Linux-x86_64  /usr/local/bin/docker-compose
```

docker-compose のインストール結果を確認する。

```
$ docker-compose --version
docker-compose version 1.27.0, build 980ec85b
```

## NetBox

機器の管理をするツールとして、NetBox をインストールする。

```
git clone https://github.com/netbox-community/netbox-docker.git
cd netbox-docker
```

外部接続用の設定を override する。

```
cat << _EOF_ > docker-compose.override.yml
version: '3.4'
services:
  netbox:
    ports:
      - 8000:8080
_EOF_
docker compose pull
```

起動する。

```
docker-compose up -d
```

動作状況を確認する。

```
$ docker ps -a
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS                    PORTS                                       NAMES
7fc85f130326   netboxcommunity/netbox:v3.5-2.6.1   "/usr/bin/tini -- /o…"   15 minutes ago   Up 15 minutes (healthy)                                               netbox-docker_netbox-housekeeping_1
aba76ade7194   netboxcommunity/netbox:v3.5-2.6.1   "/usr/bin/tini -- /o…"   15 minutes ago   Up 15 minutes (healthy)                                               netbox-docker_netbox-worker_1
ea56e95be77f   netboxcommunity/netbox:v3.5-2.6.1   "/usr/bin/tini -- /o…"   16 minutes ago   Up 16 minutes (healthy)   0.0.0.0:8000->8080/tcp, :::8000->8080/tcp   netbox-docker_netbox_1
7caf476a9f38   redis:7-alpine                      "docker-entrypoint.s…"   16 minutes ago   Up 16 minutes             6379/tcp                                    netbox-docker_redis_1
401aa46d567e   postgres:15-alpine                  "docker-entrypoint.s…"   16 minutes ago   Up 16 minutes             5432/tcp                                    netbox-docker_postgres_1
de8e12848b1f   redis:7-alpine                      "docker-entrypoint.s…"   16 minutes ago   Up 16 minutes             6379/tcp                                    netbox-docker_redis-cache_1
```

管理者権限作成する。

```
$ docker-compose exec netbox /opt/netbox/netbox/manage.py createsuperuser
🧬 loaded config '/etc/netbox/config/configuration.py'
🧬 loaded config '/etc/netbox/config/extra.py'
🧬 loaded config '/etc/netbox/config/logging.py'
🧬 loaded config '/etc/netbox/config/plugins.py'
Username (leave blank to use 'unit'): admin
Email address: admin@example.com
Password:
Password (again):
Superuser created successfully.
```

ブラウザで下記にアクセスできる。

- \<NetBox Machine IP>:8000
  - username/password: admin/password

## Batfish

ネットワークを仮想的にシミュレーションし、テストするツールとして Batfish をインストールする。
Batfish は 2 種類のインストール手段がある。

- batfish/batfish のイメージ利用  
config と batfish のみが動作。pybatfish などを自分で導入し、制御する。
- batfish/allinone のイメージ利用  
  - http://\<IP>:8888/ にアクセスし、token を利用してログインする。  
Jupyter Notebook, pybatfish なども一緒に動作するため、何も設定することなくブラウザ上で動作を試すことができる。

今回は batfish/batfish のイメージを利用する。

batfish server をインストールし、起動する。

```
docker pull batfish/batfish
docker run -d --rm --name batfish -p 9996:9996 -p 9997:9997 batfish/batfish
```

Batfish 自体はクライアント・サーバ型で使用する。
Batfish を扱うための REST API の python wrapper として batfish/pybatfish があるため、インストールする。

```
sudo apt install python3-pip
pip install pybatfish
pip install pybatfish==2021.11.4.1095

$ pip list | grep batfish
pybatfish              2021.11.4.1095
```

インストール後は下記のサイトにサンプルコンフィグが準備されているため、動作確認も可能である。

- https://github.com/corestate55/batfish-test-topology

## Ansible

Ansible をインストールする。

```
sudo apt-get install ansible
sudo apt-get install sshpass
```

# システム構築

[構成] に記載した内容を構築する。

## ネットワーク機器の接続情報を NetBox に登録 (手動)

Ansible から利用するための接続情報を NetBox に登録する。
登録は NetBox のブラウザ画面から実施する。

### [Devices] > [Interfaces]

ネットワーク機器の持つインターフェースを登録する。
下記は登録例である。(記載箇所以外は空白)

- [Interface]
  - Device: demo-router1
    - デバイスを未作成の場合は先行して [Devices] > [Devices] からデバイスを作成する
  - Name: fxp0 (demo-router1)
  - Type: Virtual
- [Operation]
  - [x] Enabled

### [IPAM] > [IP Addresses]

ネットワーク機器の IP アドレスを登録する。
下記は登録例である。(記載箇所以外は空白)

- [IP Address]
  - Address: 10.100.2.74/22
  - Status: Active
- [Interface Assignment]
  - [Device] を選択
    - Interface: fxp0 (demo-router1) 
    - [x] Make this the primary IP for the device/VM

### [Customization] > [Custom Fields]

Ansible で利用するための認証用パラメータを登録する。
下記の 3 種を登録する。

- ansible_network_os
- ansible_password
- ansible_user

下記は登録例である。(記載箇所以外は空白)

- [Custom Field]
  - Content types: DCIM > Device
  - Name: ansible_network_os
  - Type: Text
- [Behavior]
  - Search weight: 1000
  - Filter logic: Loose
  - UI visibility: Read/Write
  - Display weight: 100

### [Devices] > [Devices]

デバイス情報を登録する。
下記は登録例である。(記載箇所以外は空白)

- [Device]
  - Name: demo-router1
  - Device role: demo-router1
    - 別途 [Devices] > [Device Roles] で作成しておく
- [Hardware]
  - Device type: demo
    - 別途 [Devices] > [Device Types] で作成しておく
- [Location]
  - Site: demo
    - 別途 [Organization] > [Sites] で作成しておく
- [Management]
  - Status: Active
  - Primary IPv4: 10.100.2.74 (fxp0 (demo-router1)) を選択する
- [Custom Fields]
  - Ansible network os: junos
  - Ansible password: xxxxx
  - Ansible user: xxxxx

## ネットワーク機器の基本情報を NetBox に登録 (Ansible)

登録した接続情報を利用し、ネットワーク機器の基本情報を Ansible から NetBox に登録する。
ネットワーク機器の基本情報として `show configuration` 等で取得可能な情報を取得する。
また、取得した情報は NetBox の [Devices] > [Devices] で登録した各デバイスの Local Config Context Data (Local Context)に登録する。
この時、NetBox では JSON フォーマットでないと設定できないため、Playbook で整形する。

登録した接続情報を利用するためにダイナミックインベントリを利用する。

```
$ cat netbox_get.py
#!/usr/bin/env python

import json
import os
import re
import requests
import urllib3

urllib3.disable_warnings()

netbox_url = "{}".format(os.environ.get("NETBOX_API_ENDPOINT"))
netbox_token = os.environ.get("NETBOX_TOKEN")

headers = {"Content-Type": "application/json", "Authorization": f"Token {netbox_token}"}

device_res = requests.get(f"{netbox_url}/api/dcim/devices/", headers=headers)

target_network_os = ["junos"]

inventory = {}

for res in device_res.json()["results"]:
    ip_addresses = []

    ip_addresses.append(res["primary_ip4"]["address"].split("/")[0])

    ansible_connection = ""
    custom = res["custom_fields"]

    if custom["ansible_network_os"] in target_network_os:
        ansible_connection = "network_cli"
    else:
        ansible_connection = "ssh"

    if ip_addresses:
        inventory['{}'.format(res['name'])] = {
                "hosts": ip_addresses,
                "vars": {
                    "name": res["name"],
                    "ansible_host_key_checking": False,
                    "ansible_user": custom["ansible_user"],
                    "ansible_password": custom["ansible_password"],
                    "ansible_network_os": custom["ansible_network_os"],
                    "ansible_connection": ansible_connection
                }
            }

print(json.dumps(inventory, indent=2))
```

Playbook を作成する。

```
$ cat junos-netbox.yaml
---
- hosts: demo-router1 (デイバス名を指定する)
  gather_facts: false
  roles:
    - junos-netbox

$ cat roles/junos-netbox/tasks/main.yaml
---
- name: worker vm create
  include: get_junos_config.yaml
  when: ansible_network_os == 'junos'

$ cat roles/junos-netbox/tasks/get_junos_config.yaml
---
- name: Get committed configuration
  junos_command:
    commands: "show configuration"
    display: set
  register: response

- set_fact:
    config_context:
      configuration: "{{ response.stdout_lines[0] }}"

- name: Create Netbox config-context
  netbox.netbox.netbox_device:
    netbox_url: "{{ lookup('env', 'NETBOX_API_ENDPOINT') }}"
    netbox_token: "{{ lookup('env', 'NETBOX_TOKEN') }}"
    data:
      name: "{{ host }}"
      local_context_data: "{{ config_context }}"
```

下記を実行することで対象ネットワーク機器の基本情報を NetBox に登録できる。

```
export NETBOX_API_ENDPOINT=http://<NetBox Machine IP>:8000
export NETBOX_TOKEN=<NetBox Token>

ansible-playbook -i netbox_get.py junos-netbox.yaml
```

## ネットワーク機器の設定を変更

ブラウザから NetBox にアクセスし、ネットワーク機器の設定を変更する。
前項がうまく動作すると、各デバイスの Local Context に下記のようなネットワーク機器の情報が登録されている。

```
{
    "configuration": [
        "set version 23.1R1.8",
        "set system host-name demo-router1",
        "set interfaces fxp0 unit 0 family inet address 10.100.2.74/22",
        ... (省略) ...
    ]
}
```

上記の Local Context を直接修正し、テストしたいネットワーク機器の設定を登録する。

## NetBox から登録されている設定の抜き出しと整形

Local Context に登録されたネットワーク機器の設定を抜き出し、Batfish が利用できるように整形する。

[構成] のような環境を Batfish で再現する場合、下記のようにディレクトリやコンフィグファイルを準備する必要がある。

```
demo/
├── configs (ネットワーク機器のコンフィグ)
│   ├── demo-router1.conf
│   ├── demo-router2.conf
│   ├── demo-router3.conf
│   └── demo-router4.conf
└── hosts (ホスト定義ファイルを格納)
    ├── demo-edge1.json
    ├── demo-edge2.json
    ├── demo-edge3.json
    └── demo-edge4.json
```

NetBox に登録された情報を利用して demo/configs 以下にデバイスの設定ファイルを準備する。
この時、Batfish では set xxx の情報しか読み込めないため、設定を取得しつつファイルへの整形を行う。

```
export NETBOX_API_ENDPOINT=http://<NetBox Machine IP>:8000
export NETBOX_TOKEN=<NetBox Token>

# NetBox に登録されている抜き出し対象デバイス名列挙ファイル
$ cat target_device_list.txt
demo-router1
demo-router2
demo-router3
demo-router4

# 実行するファイル生成プログラム
$ cat batfish_test_network.py
#!/usr/bin/env python
import os
import requests
import shutil
import urllib3
import json

urllib3.disable_warnings()

def device_config_save_to_netbox():
    netbox_url = os.environ.get("NETBOX_API_ENDPOINT")
    netbox_token = os.environ.get("NETBOX_TOKEN")

    headers = {"Content-Type": "application/json",
               "Authorization": f"Token {netbox_token}"}

    device_res = requests.get(f"{netbox_url}/api/dcim/devices/", headers=headers)

    target_config_dir = "demo/configs/"
    target_file = "target_device_list.txt"
    target_device_list = []

    # Target device list read
    with open(target_file, "r", encoding="utf-8") as f:
        target_device_list = f.read().splitlines()

    # Refresh config directory
    shutil.rmtree(target_config_dir)
    os.mkdir(target_config_dir)

    # Get device's local context from netbox
    print("# Start to get device's config from NetBox")
    for res in device_res.json()["results"]:
        if res["name"] in target_device_list:
            config = res["local_context_data"]["configuration"]
            o_filepath = '{}.conf'.format(target_config_dir)
            with open('{}{}.conf'.format(target_config_dir, res["name"]), mode='w', encoding='utf-8', newline='\n') as f:
                print("Save file ({}.conf)".format(res["name"]))
                for line in config:
                    f.write(line + '\n')
    print("# End to get device's config from NetBox")

def main():
    # Device config file save
    device_config_save_to_netbox()

if __name__ == "__main__":
    main()
```

実行することで、例に示したように demo/configs 以下にネットワーク機器のコンフィグファイルが生成される。

```
$ cat demo/configs/demo-router1.conf
set version 23.1R1.8
set system host-name demo-router1
set interfaces fxp0 unit 0 family inet address 10.100.2.74/22
... (省略) ...
```

また、demo/hosts 以下のファイルは下記のような内容で手動準備した。

```
$ cat demo/hosts/demo-edge1.json
{
    "hostname" : "demo-edge1",
    "hostInterfaces" : {
        "eth0" : {
            "name": "eth0",
            "prefix" : "192.168.51.10/24",
            "gateway": "192.168.51.1"
        }
    }
}
```

## Batfish でテスト

Batfish を利用したテストを実施するためのプログラムを作成する。

今回は demo-edge 間の疎通性を確認するためのプログラムを作成した。

```
# 疎通性を確認する demo-edge 列挙ファイル
$ cat reachability_target.txt
demo-edge1
demo-edge2
demo-edge3
demo-edge4

# 疎通性を確認するプロトコル列挙ファイル
$ cat ipprotocols_target.txt
ICMP

# 実行するテストプログラム
$ cat batfish_test_network.py
import urllib3
import json

from pybatfish.client.session import Session
from pybatfish.datamodel import *
from pybatfish.datamodel.answer import *
from pybatfish.datamodel.flow import *

urllib3.disable_warnings()

def batfish_reachability_test():
    bf = Session(host='localhost')
    bf.set_network("batfish-test-topology")
    snapname="test_scenario"
    bf.init_snapshot('./demo', name=snapname, overwrite=True)

    reachability_test_target_file = "reachability_target.txt"
    ipprotocols_test_target_file = "ipprotocols_target.txt"
    target_hosts_dir = "demo/hosts/"

    # Target test list read
    with open(reachability_test_target_file, "r", encoding="utf-8") as f:
        target_test_list = f.read().splitlines()

    with open(ipprotocols_test_target_file, "r", encoding="utf-8") as f:
        ipprotocols_test_target_list = f.read().splitlines()

    print("# Start edge to edge reachable test")
    for src_target in target_test_list:
        print("# Source Device: {} #".format(src_target))
        for dst_target in target_test_list:
            with open('{}{}.json'.format(target_hosts_dir, src_target), mode='r', encoding='utf-8') as f:
                src_json = json.load(f)
                src_ips = src_json["hostInterfaces"]["eth0"]["prefix"].split('/')[0]

            with open('{}{}.json'.format(target_hosts_dir, dst_target), mode='r', encoding='utf-8') as f:
                dst_json = json.load(f)
                dst_ips = dst_json["hostInterfaces"]["eth0"]["prefix"].split('/')[0]

            for protocol_target in ipprotocols_test_target_list:
                bdtr_dev_check = bf.q.traceroute(
                    startLocation='{}[eth0]'.format(src_target),
                    headers=HeaderConstraints(
                        srcIps=src_ips,
                        dstIps=dst_ips,
                        ipProtocols=[protocol_target]
                    )).answer().frame()

                check = bdtr_dev_check.Traces[0][0].disposition

                if check == "ACCEPTED":
                    print("{} -> {} ({}): OK".format(src_target, dst_target, protocol_target))
                else:
                    print("{} -> {} ({}): NG".format(src_target, dst_target, protocol_target))
        print("")
    print("# End edge to edge reachable test")

    bf.delete_snapshot(name=snapname)

def main():
    # Reachability Test
    batfish_reachability_test()

if __name__ == "__main__":
    main()
```

実行すると reachability_target.txt に記載した demo-edge 間で ipprotocols_target.txt で指定したプロトコルの疎通性があるかをテストする。
上記のプログラム実行結果は下記のようになる。

```
$ python3 batfish_test_network.py

# Start edge to edge reachable test
# Source Device: demo-edge1 #
demo-edge1 -> demo-edge1 (ICMP): OK
demo-edge1 -> demo-edge2 (ICMP): OK
demo-edge1 -> demo-edge3 (ICMP): OK
demo-edge1 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge2 #
... (省略) ...
```

実施したいテスト次第で Batfish のテスト内容を変更することで、任意のテストを実機を使うことなく実施できる。

# 到達性確認テスト

下記の構成でテスト用のネットワークを構成する。
demo-router 間は OSPF で接続できており、各 demo-edge 間は疎通可能な状態である。
今回は demo-dege4 から demo-edge1 へのパケットのみを拒否する設定が正しくできているかテストする。

- demo-edge[1-4]: Ubuntu20.04 マシン
- demo-router[1-4]: vjunos-switch-23.1R1.8 VM

![ip.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735085/77e39f89-bc84-3c8a-f308-120cbd36a9de.png)

## 既存の設定の疎通性を確認

デモ構成で最初の OSPF 設定の疎通性を確認する。  
すべての Edge 間で OK となっていることがわかる。

```
$ python3 batfish_test_network.py
# Start to get device's config from NetBox
Save file (demo-router1.conf)
Save file (demo-router2.conf)
Save file (demo-router3.conf)
Save file (demo-router4.conf)
# End to get device's config from NetBox


# Start edge to edge reachable test
# Source Device: demo-edge1 #
demo-edge1 -> demo-edge1 (ICMP): OK
demo-edge1 -> demo-edge2 (ICMP): OK
demo-edge1 -> demo-edge3 (ICMP): OK
demo-edge1 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge2 #
demo-edge2 -> demo-edge1 (ICMP): OK
demo-edge2 -> demo-edge2 (ICMP): OK
demo-edge2 -> demo-edge3 (ICMP): OK
demo-edge2 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge3 #
demo-edge3 -> demo-edge1 (ICMP): OK
demo-edge3 -> demo-edge2 (ICMP): OK
demo-edge3 -> demo-edge3 (ICMP): OK
demo-edge3 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge4 #
demo-edge4 -> demo-edge1 (ICMP): OK
demo-edge4 -> demo-edge2 (ICMP): OK
demo-edge4 -> demo-edge3 (ICMP): OK
demo-edge4 -> demo-edge4 (ICMP): OK

# End edge to edge reachable test
```

## 設定を間違えた場合の到達性を確認

NetBox の demo-router1 の Local Context に下記を追記する。

```
"set firewall filter filter1 term blcok_demo_edge4 from source-address 192.168.54.0/24",
"set firewall filter filter1 term blcok_demo_edge4 from protocol icmp",
"set firewall filter filter1 term blcok_demo_edge4 then discard",
"set interfaces ge-0/0/1 unit 0 family inet filter input filter1",
"set interfaces ge-0/0/2 unit 0 family inet filter input filter1"
```

上記の設定のみを追加する場合、設定した filter にマッチしたパケット以外はすべて削除される。
そのため、想定外の demo-edge1 から demo-edge2/3 間の疎通も NG となることがわかる。

```
$ python3 batfish_test_network.py
# Start to get device's config from NetBox
Save file (demo-router1.conf)
Save file (demo-router2.conf)
Save file (demo-router3.conf)
Save file (demo-router4.conf)
# End to get device's config from NetBox


# Start edge to edge reachable test
# Source Device: demo-edge1 #
demo-edge1 -> demo-edge1 (ICMP): OK
demo-edge1 -> demo-edge2 (ICMP): OK
demo-edge1 -> demo-edge3 (ICMP): OK
demo-edge1 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge2 #
demo-edge2 -> demo-edge1 (ICMP): NG
demo-edge2 -> demo-edge2 (ICMP): OK
demo-edge2 -> demo-edge3 (ICMP): OK
demo-edge2 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge3 #
demo-edge3 -> demo-edge1 (ICMP): NG
demo-edge3 -> demo-edge2 (ICMP): OK
demo-edge3 -> demo-edge3 (ICMP): OK
demo-edge3 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge4 #
demo-edge4 -> demo-edge1 (ICMP): NG
demo-edge4 -> demo-edge2 (ICMP): OK
demo-edge4 -> demo-edge3 (ICMP): OK
demo-edge4 -> demo-edge4 (ICMP): OK

# End edge to edge reachable test
```

## 正常な設定をした場合の到達性を確認

NetBox の demo-router1 の Local Context に不足していた下記の設定を追加する。

```
"set firewall filter filter1 term accept_everything_else then accept"
```

その結果、下記のように demo-edge4 から demo-edge1 の疎通のみ NG となった。

```
$ python3 batfish_test_network.py
# Start to get device's config from NetBox
Save file (demo-router1.conf)
Save file (demo-router2.conf)
Save file (demo-router3.conf)
Save file (demo-router4.conf)
# End to get device's config from NetBox


# Start edge to edge reachable test
# Source Device: demo-edge1 #
demo-edge1 -> demo-edge1 (ICMP): OK
demo-edge1 -> demo-edge2 (ICMP): OK
demo-edge1 -> demo-edge3 (ICMP): OK
demo-edge1 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge2 #
demo-edge2 -> demo-edge1 (ICMP): OK
demo-edge2 -> demo-edge2 (ICMP): OK
demo-edge2 -> demo-edge3 (ICMP): OK
demo-edge2 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge3 #
demo-edge3 -> demo-edge1 (ICMP): OK
demo-edge3 -> demo-edge2 (ICMP): OK
demo-edge3 -> demo-edge3 (ICMP): OK
demo-edge3 -> demo-edge4 (ICMP): OK

# Source Device: demo-edge4 #
demo-edge4 -> demo-edge1 (ICMP): NG
demo-edge4 -> demo-edge2 (ICMP): OK
demo-edge4 -> demo-edge3 (ICMP): OK
demo-edge4 -> demo-edge4 (ICMP): OK

# End edge to edge reachable test
```

# まとめ

今回は NetBox と Batfish を利用し、ネットワーク機器の設定や情報を一元管理しつつ、ネットワーク機器の設定テストを Batfish 上で実施することで、実機を操作することなくネットワーク機器の設定を検証可能なシステムを構築した。
本記事の環境では Ansible をそのまま利用しているが、Ansible Tower (AWX) を利用することで API 越しに Playbook の実行も可能となる。
これにより、Playbook の実行の定期実行化や別ソフトウェアやプログラムから実行呼び出しが容易になる。

例えば、今回作成したものを活用すると実際に実機に設定を導入する前の事前検証の自動実施を導入できると考えている。
具体的には既存のシステムとして自動でネットワーク機器の設定をするようなオーケストレータが存在するのであれば、実際に機器に設定を投入する前に今回作成したテストを実行するように API を呼び出す処理を追加する。
このようにすることで既存のオーケストレータがいきなり設定することなく、事前検証も自動化フローに追加可能と考えている。

また、試験環境が用意できない場合、有識者による机上レビューで代替するケースも多い。
人によるチェックは見落としが発生しがちなため、代替手段として活用できるとも考えている。

課題としてはネットワーク機器の認証情報管理がある。
今回は NetBox の Custom Field を利用しているが、このままでは平文での認証情報管理となる。
そのため、NetBox はセキュアな環境かつ閲覧者の制限が重要になる。
また、Batfish で論理的にシミュレーション可能なデバイス数には限りがあるため、大規模なネットワーク構成をそのままシミュレーションできるかどうかは別途検証する必要がある。