NetBoxからqueryプラグインを使って簡単に情報を抽出する方法について



##はじめに
NetBoxとは、IPAM/DCIMの両方の機能があり、かつWeb UI機能を持ったオープンソースのソフトウェアです。このソフトウェアを使う事で、サーバ/NW機器管理台帳、ラック台帳、IPアドレス管理表で管理していた情報を本ソフトウェアで管理することができます。
https://netbox.readthedocs.io/en/stable/

NetBoxは今年のAnsibleFest 2020に取り上げられたり、NetBox単体でカンファレンスが開催されるなど、いま非常に勢いがあると感じています。
https://wcollins.github.io/post/2020/01-automating-cloud-ipam/
https://www.youtube.com/playlist?list=PLjA0bhxgryJ3VcbLIZqY2F2QfUcdRhxZS

また、バージョンアップのリリースサイクルが早い事もひとつの特徴です。
https://github.com/netbox-community/netbox/releases

このNetBoxですが、Ansibleモジュールが提供されてます。
https://github.com/netbox-community/ansible_modules

今回は、提供されているAnsibleモジュールの中ででqueryプラグインという便利な情報取得コマンドの使い方についてご紹介します。

##queryプラグインの使い方
queryプラグインの使い方ですが、GitHubにあるAnsibleモジュールのソースコードの以下ページに記載されています。
https://github.com/netbox-community/ansible_modules/blob/devel/plugins/lookup/nb_lookup.py


基本的な使い方は以下です。

```
query('netbox.netbox.nb_lookup', '(Netboxのエンドポイント)', api_endpoint='http://(NetBoxのURL)', api_filter='(取得する項目の条件)'), token='APIトークン')
```

| 名称       |説明        |
|:-----------------|:------------------|
|Netboxのエンドポイント|デバイスやIPアドレスなど取得する項目の種別を指定します。*1 |
| NetBoxのURL           | NetBoxのWeb UIにアクセスするためのURLを指定します。 |
| 取得する項目の条件 | 特定の条件に満たした項目を取得したいとき条件をここで指定します。*2 |
| APIトークン               | NetBoxのWeb UIで取得できるAPIトークンを指定します。  |

*1
Netboxのエンドポイントで何があるかは以下URLをご参照ください。
https://github.com/netbox-community/ansible_modules/blob/devel/plugins/lookup/nb_lookup.py#L137-L202

*2
APIトークンの取得の仕方は、以下URLの『Setup And Prerequisites』をご参照ください。
https://ttl255.com/pynetbox-netbox-python-api-client-p1-getting-info/

*3 
指定できるフィルター条件は、各項目のAPIのGet系を見れば予測つきます。
例えば、デバイスの場合は、Get /dcim/devicesを参照します。
APIリファレンスは、`http://(NetBoxのURL)/api/docs` と指定すればSwaggerの画面で確認できます。


##実際の使用例
今回は以下のバージョンで試しています。

| 名前       |バージョン        |
|:-----------------|:------------------|
|ansible|2.10.3|
|NetBox(docker版)|2.9.10|
|NetBox Collection|1.1.0|


queryプラグインは、lookup pluginであるため、debugやyumモジュールの様に１taskオブジェクトとして指定する必要がありません。例えばdebugモジュールと組み合わせて以下の様に使う事ができます。
まず、NetBoxで以下IPアドレスのオブジェクトを作成します。
ちょっと見えずらいですが、タグで**dns**を指定しています。
![netbox_8.8.8.8.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192426/3026b703-00a6-70f2-62ea-0bec9b934c3c.jpeg)

この状態で以下Playbookを用意します。
内容はタグが**dns**で指定されているIPアドレスを表示するものです。
比較のため、1つ目のdebugではqueryの結果をそのまま、2つ目のdebugはIPアドレス部分のみ抽出できるように整形しています。

```Yaml:get_ntp.yml
- hosts: localhost
  gather_facts: False
  connection: local
  collections:
    - netbox.netbox
  vars:
    - netbox_url: http://xxxxxxxxxxxx:8000
    - netbox_token: 0123456789abcdef0123456789abcdef01234567
  tasks:
    #############################
    # queryの結果をそのまま出力
    #############################
    - name: show ip-address query(ntp)
      debug:
        msg: "{{ query('netbox.netbox.nb_lookup', 'ip-addresses', api_filter='tag=dns', 
                        api_endpoint=netbox_url, token=netbox_token) }}"

    #############################
    # queryの結果を整形してIPアドレス部分のみの出力
    #############################
    - name: show ip-address query(ntp) only ip address
      debug:
        msg: "{{ query('netbox.netbox.nb_lookup', 'ip-addresses', api_filter='tag=dns', 
                        api_endpoint=netbox_url, token=netbox_token)[0].value.address }}"
```

Playbookの実行結果は以下です。1つ目のdebugの結果はリスト形式で出力されます。今回は**dns**のタグが設定されたIPアドレスがNetBox上に1つしか存在しなかったため、1要素(1IPアドレス)しか出力されません。条件に合致するオブジェクトが複数ある場合は複数の要素で出力されます。また1要素の中のvalueの中に実際の値が存在します。今回はアドレス(8.8.8.8)を取得したいため、addressの値が対象になります。
2つ目のdebugの結果で、queryの結果に**[0].value.address**を追加することでピンポイントで"8.8.8.8/32"が取得できていることが分かります。

```
$ ansible-playbook -i localhost, get_ntp.yml 

PLAY [localhost] *******************************************************************************************************************************************************************************

TASK [show ip-address query(ntp)] **************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "key": 182,
            "value": {
                "address": "8.8.8.8/32",
                "assigned_object": null,
                "assigned_object_id": null,
                "assigned_object_type": null,
                "created": "2020-12-04",
                "custom_fields": {},
                "description": "",
                "dns_name": "",
                "family": {
                    "label": "IPv4",
                    "value": 4
                },
                "id": 182,
                "last_updated": "2020-12-04T12:44:23.065254Z",
                "nat_inside": null,
                "nat_outside": null,
                "role": null,
                "status": {
                    "label": "Active",
                    "value": "active"
                },
                "tags": [
                    {
                        "color": "9e9e9e",
                        "id": 40,
                        "name": "dns",
                        "slug": "dns",
                        "url": "http://xxxxxxxxxx:8000/api/extras/tags/40/"
                    }
                ],
                "tenant": null,
                "url": "http://xxxxxxxxxx:8000/api/ipam/ip-addresses/182/",
                "vrf": null
            }
        }
    ]
}

TASK [show ip-address query(ntp) only ip address] **********************************************************************************************************************************************
ok: [localhost] => {
    "msg": "8.8.8.8/32"
}

PLAY RECAP *************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

また、queryプラグインを他のAnsibleモジュールを使うときにパラメータの一部として使用することもできます。
以下のPlaybookでは、CiscoのルーターにDNSサーバの設定としてqueryプラグインで取得したDNSのIPアドレスを設定します。
queryプラグインで取得した結果を整形しても**/32**の部分がどうしても残ってしまうため、末尾に**| ipaddr('address')**をつけることでホストアドレスのみに整形します。

```Yaml:get_ntp.yml
- hosts: cisco
  gather_facts: False
  collections:
    - netbox.netbox
    - cisco.ios
  vars:
    - netbox_url: http://xxxxxxxxxx:8000
    - netbox_token: 0123456789abcdef0123456789abcdef01234567
  tasks:
    #############################
    # コマンド「ip name-server 8.8.8.8」を設定
    #############################
    - name: set dns
      cisco.ios.ios_config:
        lines: "ip name-server {{ query('netbox.netbox.nb_lookup', 'ip-addresses', api_filter='tag=dns', 
                                api_endpoint=netbox_url, token=netbox_token)[0].value.address | ipaddr('address')}}"

    #############################
    # コマンド「show ip name-server」の結果を出力
    #############################
    - name: show dns
      cisco.ios.ios_command:
        commands: "show ip name-server"
      register: result

    - name: show dns result
      debug:
        msg: "{{ result.stdout_lines }}"
```

Playbookの実行結果です。最後のDebugモジュールの結果でqueryプラグインで取得したIPアドレス「8.8.8.8」が設定されていることが分かります。

```
$ ansible-playbook -i hosts.ini set_ntp_to_router.yml 

PLAY [cisco] ***********************************************************************************************************************************************************************************

TASK [set dns] *********************************************************************************************************************************************************************************
changed: [172.31.22.137]

TASK [show dns] ********************************************************************************************************************************************************************************
ok: [172.31.22.137]

TASK [show dns result] *************************************************************************************************************************************************************************
ok: [172.31.22.137] => {
    "msg": [
        [
            "172.31.0.2",
            "8.8.8.8"
        ]
    ]
}

PLAY RECAP *************************************************************************************************************************************************************************************
172.31.22.137              : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```



##まとめ
簡単ではありますが、queryプラグインの使い方について説明させて頂きました。
queryプラグインを使えば、NetBoxに登録したデバイス情報、IPアドレス情報など情報をダイレクトにPlaybookのパラメータに渡す事ができます。

今回はひとつの事例で説明しましたが、使いようによっては空いているIPアドレスを割り振ったり、デバイスのインターフェースのステータスに従って、実機のインターフェースのステータスを変更したりと様々な事が可能になると思います。ネットワーク機器の運用者でIPアドレスやVlan ID、インターフェース情報をもっと効率的に管理したいと考えている方にとって手助けになれば幸いです。