これは エーピーコミュニケーションズ Advent Calendar 2019 の15日目の記事です。
■ はじめに
NetBox とは、IPAM、ラックやデバイス 管理機能を持ったWeb UI 付きのオープンソース のソフトウェアです。
Device 管理画面
API を備えていて、Ansible の NetBox モジュールから様々な操作ができます。
ここでは、NetBox の環境を構築した後に、Device を登録するのに必要な一連の作業(Manufacture や Device Role など含む)を Ansible から行う方法をご紹介します。
【目次】
■ NetBox モジュールの基本
Playbook を作成する前に、予め押さえておきたいポイントを説明します。
大きく2種類に分かれる
NetBox モジュール群は以下の2種類あります。
Collection とは、Ansible 2.9 から本格的(?)には始まった、新しいコンテンツ配布形式です。Ansible Galaxy 経由で入手する点ではロールと同じですが、Collection では、プラグイン 、モジュール、ロールなどがセットです。
Ansible のリリースサイクルに依存せずにモジュールをリリースするためなのか、現状は Collection モジュールの方が開発が進んでいます。
現時点で、標準モジュールが 5個なのに対して、Collection モジュール(v0.1.1)は 32個です。
今回は、Collection モジュール の方を利用します。
NetBox モジュールは、NetBox 本体に備わっている REST API を利用する仕組みになっています。
モジュールには、NetBox のホスト名またはIPアドレス や、API を利用するためのトーク ンを指定します。
具体的な指定方法は「接続情報の定義 」で後述します。
■ 環境の準備
Ansible 2.9.1
NetBox 2.6.7
pynetbox 4.2.2
Python 3.6
NetBox の準備
検証用の NetBox を準備します。ありがたいことに docker イメージ があるのでそれを利用します。(Play with Docker でも正常に起動しました。)
$ git clone -b release https://github.com/netbox-community/netbox-docker.git
$ cd netbox-docker
$ docker-compose pull
$ docker-compose up -d
[2020/02/16 更新] 公式手順で利用するブランチが、master
から release
に変更されたのでコマンドを修正
起動したら Web ブラウザ で http://localhost:32768/
にアクセスしてログインできることを確認します。デフォルトの docker-compose.yml
では、ホスト側のポートが固定されていないので、こちらの記事を参考にして固定する のも良いと思います。
初期ユーザー名、パスワードともに admin
です。起動に少し時間がかかるようなので、502 Bad Gateway
が表示されたらしばらく待ってから再度試します。
ログインは右上から
ユーザー名とパスワードを入力してログイン
ログイン成功
Ansible 環境の準備
NetBox モジュールを利用するには、NetBox API のクライアントライブラリである pynetbox
が必要なのでインストールします。
$ pip install pynetbox
Collecting pynetbox
...(略)...
venv で環境を分離している場合、どの venv にインストールしたのかをあとで Ansible 側に教えてあげる必要があるので、覚えておきましょう。
Collection モジュールのインストール
Collection モジュールは標準では入っていないので、ansible-galaxy collection install
コマンド でインストールします。
$ ansible-galaxy collection install fragmentedpacket.netbox_modules
Process install dependency map
Starting collection install process
Installing 'fragmentedpacket.netbox_modules:0.1.1' to '/home/vagrant/.ansible/collections/ansible_collections/fragmentedpacket/netbox_modules'
デフォルトでは、 ~/.ansible/collections
配下にインストールされます。
なお、一部で Collection モジュールは mazer
というツールでインストールするという説明しているページがありますが、mazer
の機能は ansible-galaxy
コマンドに統合されたので、mazer
はすでに非推奨扱いです 。
[2020/07/01 追記]
現在は netbox.netbox
という collection の方で開発が行われているようです。
■ 目指すゴール
管理用IPアドレス を持った device を登録します。
NetBox 上、以下の画面のように 4つの Device が登録された状態ががゴールです。
4つの Device を登録する
画面にもあるように Device には Site や Device Role、Device Type などの関連オブジェクト割り当てます。そのため、これらの関連オブジェクトを予め作成しておく必要があります。
関連オブジェクトは以下のようなものです。
NetBox では他にも、ラック管理 など、様々な機能がありますが今回は利用しません。
■ Playbook 類の作成
Playbook と、Playbook の実行に必要なファイルを作成します。
.
├── host_vars
│ └── netbox01.yml
├── inventory.ini
├── netbox.yml
└── object_vars.yml
インベントリファイルの作成
NetBox のホスト情報を定義する、インベントリファイルを作成します。
NetBox の API の URL は別途定義するので、ここで定義する名前は何でも構いません。ここでは netbox01
とします。
[ netbox]
netbox01
変数定義ファイルの作成
NetBox モジュールで利用する変数を定義するファイルを作成します。
接続情報の定義
NetBox ホスト netbox01
が利用する接続情報(url、トーク ン)をホスト変数として定義します。一応 netbox
というグループで囲って、あとで Playbook で hosts: netbox
と指定するすることにします。
---
netbox_url : http://localhost:32768
netbox_token : 0123456789abcdef0123456789abcdef01234567
netbox_url
:
NetBox のAPI に接続するためのURLを指定します。
netbox_token
NetBox のAPI に接続するためのトーク ンを指定します。詳細は、NetBox の公式ドキュメント を参照してください。上記ファイルで定義している 0123456789abcdef0123456789abcdef01234567
は、docker イメージを利用した場合のものです。
オブジェクト情報の定義
NetBox 上に登録したい device などの各オブジェクトを定義する変数定義ファイルを作成します。
---
site : my_site
manufactures :
- name : Cisco
- name : Arista
device_types :
- model : Catalyst
slug : Catalyst
manufacturer : Cisco
- model : veos
slug : veos
manufacturer : Arista
device_roles :
- name : core
slug : core
color : 4caf50
- name : aggregation
slug : aggregation
color : 2196f3
devices :
- name : cat1
device_type : Catalyst
device_role : core
management :
interface : GigabitEthernet0/0
address : 10.0.1.1/24
site : my_site
- name : cat2
device_type : Catalyst
device_role : core
management :
interface : GigabitEthernet0/0
address : 10.0.1.2/24
site : my_site
- name : veos1
device_type : veos
device_role : aggregation
management :
interface : Management1
address : 10.0.3.1/24
site : my_site
- name : veos2
device_type : veos
device_role : aggregation
management :
interface : Management1
address : 10.0.3.2/24
site : my_site
device_roles
モジュールの color
オプションで指定ているのは、16進で表した色指定です。NetBox の Device Roles 追加画面で色を選ぶ項目があるので、ここから適当に色を拾いました。
(参考)色の選択
Playbook の作成
いよいよ、実際の作業を定義する Playbook を作成します。
利用するモジュール
今回 Playbook で利用するモジュールは以下のとおりです。
これらのモジュールは Collection モジュールのため、公式ドキュメントには掲載されていません。そのため詳細情報は、モジュールのコードに埋め込まれているドキュメント用の部分を確認するか、ansible-doc
コマンドで名前空間を含めたモジュール名を指定して表示 させます。
$ ansible-doc fragmentedpacket.netbox_modules.netbox_manufacturer
なお、Primary adderss というのは Device の代表アドレスです。Primary adderss を設定すると、Ansible から NetBox をインベントリーとして利用 する際に、自動的に ansible_host
変数に割り当ててくれるので便利です。この件については別途ブログで取り上げる予定です。
[2019/12/21 追記]
投稿しました
[Ansible] NetBox をインベントリーとして利用する - てくなべ (tekunabe)
Playbook
role など、何かしらの方法で分割したほうがよさそうな分量ですが、説明の簡略化のため 1つの Playbook にまとめてしまいます。
冒頭の collection
で fragmentedpacket.netbox_modules
を指定することにより、モジュール利用時に Collection モジュールを名前空間 なしで指定できるようになります。例えば、名前空間 ありで指定する場合は fragmentedpacket.netbox_modules.netbox_manufacturer
とするところ、名前空間 なしでは単にnetbox_manufacturer
と指定できます。詳細は公式ドキュメント参照 。
- hosts : netbox
connection : local
gather_facts : no
collections :
- fragmentedpacket.netbox_modules
vars :
ansible_python_interpreter : ~/ansible291/bin/python
vars_files :
- object_vars.yml
tasks :
- name : site
netbox_site :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
name : "{{ site }}"
- name : add manufacturers
netbox_manufacturer :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
name : "{{ item.name }}"
loop : "{{ manufactures }}"
- name : add device role
netbox_device_role :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
name : "{{ item.name }}"
slug : "{{ item.slug }}"
color : "{{ item.color }}"
loop : "{{ device_roles }}"
- name : add device types
netbox_device_type :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
model : "{{ item.model }}"
slug : "{{ item.slug }}"
manufacturer : "{{ item.manufacturer }}"
loop : "{{ device_types }}"
- name : add devices
netbox_device :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
name : "{{ item.name }}"
device_type : "{{ item.device_type }}"
device_role : "{{ item.device_role }}"
site : "{{ item.site }}"
loop : "{{ devices }}"
- name : add management interfaces
netbox_device_interface :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
device : "{{ item.name }}"
name : "{{ item.management.interface }}"
loop : "{{ devices }}"
- name : assign ip addresses
netbox_ip_address :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
interface :
device : "{{ item.name }}"
name : "{{ item.management.interface }}"
address : "{{ item.management.address }}"
loop : "{{ devices }}"
- name : elect primary addresses
netbox_device :
netbox_url : "{{ netbox_url }}"
netbox_token : "{{ netbox_token }}"
data :
name : "{{ item.name }}"
primary_ip4 : "{{ item.management.address }}"
loop : "{{ devices }}"
■ Playbook の実行
それでは、 Playbook を実行します。
実行ログ(クリックして広げる)
$ ansible-playbook -i inventory.ini netbox.yml
PLAY [ netbox] **********************************************************************************************
TASK [ site] ************************************************************************************************
changed: [ netbox01]
TASK [ add manufacturers] ***********************************************************************************
changed: [ netbox01] => (item={ 'name' : 'Cisco' } )
changed: [ netbox01] => (item={ 'name' : 'Arista' } )
TASK [ add device role] *************************************************************************************
changed: [ netbox01] => (item={ 'name' : 'core' , 'slug' : 'core' , 'color' : '4caf50' } )
changed: [ netbox01] => (item={ 'name' : 'aggregation' , 'slug' : 'aggregation' , 'color' : '2196f3' } )
TASK [ add device types] ************************************************************************************
changed: [ netbox01] => (item={ 'model' : 'Catalyst' , 'slug' : 'Catalyst' , 'manufacturer' : 'Cisco' } )
changed: [ netbox01] => (item={ 'model' : 'veos' , 'slug' : 'veos' , 'manufacturer' : 'Arista' } )
TASK [ add devices] *****************************************************************************************
changed: [ netbox01] => (item={ 'name' : 'cat1' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'cat2' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.2/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos1' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos2' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.2/24' } , 'site' : 'my_site' } )
TASK [ add management interfaces] ***************************************************************************
changed: [ netbox01] => (item={ 'name' : 'cat1' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'cat2' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.2/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos1' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos2' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.2/24' } , 'site' : 'my_site' } )
TASK [ assign ip addresses] *********************************************************************************
changed: [ netbox01] => (item={ 'name' : 'cat1' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'cat2' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.2/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos1' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos2' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.2/24' } , 'site' : 'my_site' } )
TASK [ elect primary addresses] *****************************************************************************
changed: [ netbox01] => (item={ 'name' : 'cat1' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'cat2' , 'device_type' : 'Catalyst' , 'device_role' : 'core' , 'management' : { 'interface' : 'GigabitEthernet0/0' , 'address' : '10.0.1.2/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos1' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.1/24' } , 'site' : 'my_site' } )
changed: [ netbox01] => (item={ 'name' : 'veos2' , 'device_type' : 'veos' , 'device_role' : 'aggregation' , 'management' : { 'interface' : 'Management1' , 'address' : '10.0.3.2/24' } , 'site' : 'my_site' } )
PLAY RECAP *************************************************************************************************
netbox01 : ok=8 changed=8 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
なお、べき等性があるため、再度実行すると changed
ではなく ok
になります。
2回目の実行ログ(クリックして広げる)
$ ansible-playbook -i inventory.ini netbox.yml
TASK [ site] ************************************************************************************************
ok: [ netbox01]
TASK [ add manufacturers] ***********************************************************************************
ok: [ netbox01] => (item={ 'name' : 'Cisco' } )
ok: [ netbox01] => (item={ 'name' : 'Arista' } )
...(略)...
■ NetBox 側の確認
ちゃんとオブジェクトが作れたか確認します。
Site が登録された
Manufacturer が登録された
Device Type が登録された
Device Role が登録された
Device が登録された(完成!)
無事にすべて想定通りに登録できました。
■ まとめ
Ansible で NetBox の Device を登録できることを確認しました。
Device の登録の前にも、Site や Device Type などの登録も予め必要でしたが、Collection モジュール を利用することで、一連の作業が自動化できました。
前述のように、Ansible には NetBox をインベントリとして利用する機能 もあるので、NetBox と Ansible はなかなか相性がよいような印象です。