てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] モジュールの探し方

はじめに

先日開催された「Ansibleもくもく会 ( サーバ編 ) 2019.12 in 札幌」に、リモートメンターとして参加させていただきました。

その際、参加者の方から以下のご質問をいただきました。

モジュールの数が多いためやりたいことに適したモジュールを探すのが大変です。よい方法はありますか?

Ansible 2.9 現在で 3,000 モジュール以上あるので、たしかに大変な面はあると思います。

その場でもドキュメントに書いてお答えしたのですが、改めてブログとして3つまとめておきたいと思います。

特に裏技があるわけでもないですが・・・


■ 方法1: 公式ドキュメントの Module Index のカテゴリから探す

おそらく一番王道パターンだと思います。

公式ドキュメントには、以下のようにモジュールをカテゴリごとにまとめられています。ここから探す方法です。

docs.ansible.com

例:


■ 方法2: 公式ドキュメントのすべてのモジュール一覧から探す

「いやぁ、そもそもどのカテゴリに属するかも分からないんだよねぇ・・」という方は、こちら。

以下の1ページに、すべての標準モジュールが掲載されています。モジュール名と合わせて概要の説明文もあるので、このページ内をブラウザの検索機能で探す方法です。

docs.ansible.com

なお、ansible-doc コマンドでも ansible-doc -lansible-doc -l -j-jは Ansible 2.9から)を実行することで、モジュールの一覧や概要を表示できます。

$ ansible-doc -l -j
{
    "a10_server": "Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' server object",
    "a10_server_axapi3": "Manage A10 Networks AX/SoftAX/Thunder/vThunder devices",
    "a10_service_group": "Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' service groups",
    "a10_virtual_server": "Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' virtual servers",
    "aci_aaa_user": "Manage AAA users (aaa:User)",
    "aci_aaa_user_certificate": "Manage AAA user certificates (aaa:UserCert)",
...(略)...
}


■ 方法3: 普通にググる

身もふたもないのですが、[ ansible ] などの言葉で検索して、サンプルを見つけて、使ってるモジュールを探す方法です。 そのモジュールが自分が使ってる Ansible のバージョンでも利用できるか確認する意味でも、公式ドキュメントでそのモジュールの情報を調べ直すのが吉です。


■ 方法4: Chrome 拡張を使う

[2019/12/23 追記]

@zaki_hmkcさんが、Ansible の調べ物をするときに便利な Chrome 拡張を作成されました。 chrome.google.com

qiita.com

  • 機能1: Ansible 公式ドキュメント検索機能
  • 機能2: モジュールやプラグインのページからコードへのリンクを追加する機能

このうち、機能1がモジュールを探すのに便利です。アドレスバーに usa 調べたい文字列 を入力すると、Ansible 公式ドキュメントの検索結果が表示されます。

もちろん、公式ドキュメントの検索ボックスに直接入力して検索するのもアリですね。

f:id:akira6592:20191223003103p:plain:w300
検索ボックス




公式ドキュメントの対象バージョンに注意

公式ドキュメントを閲覧するときは、目的の Ansible のバージョンのページなのかを確認しましょう。

f:id:akira6592:20191221174623p:plain
対象バージョン

最近のバージョンであれば、以下のコンボボックスで変更できます。devel は未リリースの開発中のものなので、普段は参照することはないと思います。

f:id:akira6592:20191221174700p:plain:w300
バージョンの切り替え

バージョンを切り替えると、URLの以下の #version# の部分が変わります。

https://docs.ansible.com/ansible/#version#/modules/list_of_all_modules.html

また、ググってたどりつた公式ドキュメントのページが古すぎて、バージョン切り替えのコンボボックスがないこともあります。結果の上の方に出てくることが多い印象です。

docs.ansible.com

f:id:akira6592:20191221174940p:plain
古すぎるドキュメント

その場合は、最新版公式ドキュメントのモジュールのページから探し直します。

[Ansible] NetBox をインベントリーとして利用する


これは Ansible 3 Advent Calendar 2019 の21日目の記事です。


■ はじめに

f:id:akira6592:20191215212622p:plain:w500
NetBox を インベントリーとして利用する

以前「[Ansible] NetBox モジュールで Site や Device を登録する(Collection モジュール編) - てくなべ (tekunabe)」で、Ansible から NetBox の Device を登録する方法をご紹介しました。

今回は NetBox に登録された Device を、Ansible のインベントリーとして利用する方法をご紹介します。 Ansible 2.8 で追加された、NetBox Inventory Plugin を利用します。 YAML で簡単な設定ファイルを書くだけで、動的なインベントリとして扱えるようになります。


■ NeBox上の状態

まず、今回インベントリとして利用する NetBox 上の Device 登録状態を示します。

前回の記事の「目指すゴール」で示した内容です。

f:id:akira6592:20191215212725p:plain
Device 一覧

念のため表でも示します。

Name Site Role Manufacture Type IP Address
cat1 my_site core Cisco Catalyst 10.0.1.1
cat2 my_site core Cisco Catalyst 10.0.1.2
veos1 my_site aggregation Arista veos 10.0.3.1
veos2 my_site aggregation Arista veos 10.0.3.2

ポイントは、IP Address の列で示されているアドレスです。これは 各 Device でPrimary Address として選出したアドレスです。このアドレスが、インベントリ変数の ansible_host に変換され、Ansible が接続する先の情報として利用されます。

f:id:akira6592:20191215212850p:plain
Device 登録画面の Primary IPv4 設定部分


■ 環境の準備

以下のものを利用します。

  • Ansible 2.9.1
  • NetBox 2.6.7
  • pynetbox 4.2.2
  • Python 3.6

NetBox は、以前の記事の「NetBox の準備」で構築済みです。

Ansible 側には pip install pynetboxpynetbox をインストールしておきます。

なお、Ansible で利用するのはあくまでも NetBox Inventory Plugin であるため、標準、Collection に関わらず NetBox モジュールは利用しません。


■ インベントリ設定ファイルの準備

NetBox をインベントリーとして利用するために、NetBox Inventory Plugin のお作法に従って簡単なインベントリ設定ファイルを準備します。この設定ファイルを ansible-playbook コマンドの -i オプションに指定するという流れです。

以下、簡単なサンプルです。ファイル名は任意です。

  • netbox.yml
---
plugin: netbox
api_endpoint: http://localhost:32768
token: 0123456789abcdef0123456789abcdef01234567
group_by:
  - manufacturers

各設定の意味は以下の通りです。

設定名 必須 概要
plugin 必須 NetBox をインベントリーとして利用する場合は netbox 固定
api_endpoint 必須 NetBox API のベースURL
token 必須 NetBox API の接続に必要なトーク
group_by グループ化する単位を指定、sitemanufacturers などを指定可

他にも、https で接続する際の証明書検証の有無を指定する validate_certs や、フィルターする query_filters などのオプションもあります。詳細は公式ドキュメントの NetBox Inventory Plugin を参照して下さい。

基本的な準備はここまでです。


ansible-inventory コマンドによる確認

正しく NetBox をインベントリーとして利用できるか確認します。

ここではいきなり Playbook で試さずに、インベントリー単体の確認するためのコマンド ansible-inventory を利用します。

ホストリストの確認

ansible-inventory コマンドに --list をプションをつけると、インベントリのリストを確認できます。 どのような変数が割り当てられているかも確認できます。

  • 実行例
$ ansible-inventory -i inventory_nb.yml --list
{
    "_meta": {
        "hostvars": {
            "cat1": {
                "ansible_host": "10.0.1.1",
                "device_roles": [
                    "core"
                ],
                "device_types": [
                    "Catalyst"
                ],
                "manufacturers": [
                    "Cisco"
                ],
                "primary_ip4": "10.0.1.1",
                "sites": [
                    "my_site"
                ]
            },
            "cat2": {
                "ansible_host": "10.0.1.2",
                "device_roles": [
                    "core"
                ],
                "device_types": [
                    "Catalyst"
                ],
                "manufacturers": [
                    "Cisco"
                ],
                "primary_ip4": "10.0.1.2",
                "sites": [
                    "my_site"
                ]
            },
            "veos1": {
                "ansible_host": "10.0.3.1",
                "device_roles": [
                    "aggregation"
                ],
                "device_types": [
                    "veos"
                ],
                "manufacturers": [
                    "Arista"
                ],
                "primary_ip4": "10.0.3.1",
                "sites": [
                    "my_site"
                ]
            },
            "veos2": {
                "ansible_host": "10.0.3.2",
                "device_roles": [
                    "aggregation"
                ],
                "device_types": [
                    "veos"
                ],
                "manufacturers": [
                    "Arista"
                ],
                "primary_ip4": "10.0.3.2",
                "sites": [
                    "my_site"
                ]
            }
        }
    },
    "all": {
        "children": [
            "manufacturers_Arista",
            "manufacturers_Cisco",
            "ungrouped"
        ]
    },
    "manufacturers_Arista": {
        "hosts": [
            "veos1",
            "veos2"
        ]
    },
    "manufacturers_Cisco": {
        "hosts": [
            "cat1",
            "cat2"
        ]
    }
}

cat1cat2veos1veos2 という 4つの Device があり、それぞれ ansible_hostdevice_roles などの経数が割り当てられていることが確認できます。ansible_host 変数の値が、Ansible が接続に利用する宛先です。

また、インベントリー設定ファイルの group_bymanufacturers を指定したことから、manufacturers_Aristamanufacturers_Cisco グループと所属ホストの定義がされていることも確認できます。

グループ化の確認

ansible-inventory コマンドに --graph をプションをつけると、グループと所属ホストを確認できます。

  • 実行例
$ ansible-inventory -i inventory_nb.yml --graph
@all:
  |--@manufacturers_Arista:
  |  |--veos1
  |  |--veos2
  |--@manufacturers_Cisco:
  |  |--cat1
  |  |--cat2

スタティックなインベントリファイルで表すと

イメージを湧きやすくするため、NetBox で定義されて動的なインベントリを、スタティックなインベントリファイル(ini形式)で表してみます。変数は抜粋してます。

[manufacturers_Arista]
veos1 ansible_host=10.0.3.1
veos2 ansible_host=10.0.3.2

[manufacturers_Cisco]
cat1 ansible_host=10.0.1.1
cat2 ansible_host=10.0.1.2

ここまでで、ansible-inventory コマンドによって、正常にインベントリとして認識できていることが確認できました。


ansible-playbook コマンドによる利用

今度は、インベントリを Playbook 実行時に利用します。

変数ファイルによる補足

ここまでの作業で、インベントリ名や ansible_host 変数などは NetBox 上の情報を利用できようになしました。しかし、まだパスワードなど接続に必要な情報がありません。NetBox 本体や、NetBox Inventory Plugin を活用すれば、NetBox 上に情報を集約できるかもしれません。ただ、現時点で実現方法がまだ分からなかったので、Ansible 側に別途変数ファイルを準備して補足することにします。

ここでは、manufacturers_Cisco グループに所属するホストを対象とします。

  • group_vars/manufacturers_Cisco.yml
---
ansible_network_os: ios
ansible_connection: network_cli
ansible_user: testuserxxx
ansible_password: testpaswordxxx

Playbook の作成

manufacturers_Cisco グループを対象にした簡単な Playbook を作成します。

  • iso_show.yml
- hosts: manufacturers_Cisco
  gather_facts: no

  tasks:
    - name: show test
      ios_command:
        commands:
          - show version    # show version を実行
      register: result

    - name: debug test
      debug:
        msg: "{{ result }}"  # コマンド実行結果を表示

Playbook の実行

それでは、Playbook を実行します。

$ ansible-playbook -i inventory_nb.yml ios_show.yml 

PLAY [manufacturers_Cisco] ******************************************************************************************

TASK [show test] ****************************************************************************************************
ok: [cat2]
ok: [cat1]

TASK [debug test] ***************************************************************************************************
ok: [cat1] => {
    "msg": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "failed": false,
        "stdout": [
            "Cisco IOS XE Software, Version 16.11.01a\n...(略)...
        ],
        "stdout_lines": [
            [
                "Cisco IOS XE Software, Version 16.11.01a",
                "Cisco IOS Software [Gibraltar], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.11.1a, RELEASE SOFTWARE (fc1)",
                "Technical Support: http://www.cisco.com/techsupport",
                "Copyright (c) 1986-2019 by Cisco Systems, Inc.",
                "Compiled Thu 11-Apr-19 23:59 by mcpre",
                ...(略)...
            ]
        ],
        "warnings": [
            "Platform darwin on host cat1 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information."
        ]
    }
}
ok: [cat2] => {
    "msg": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "failed": false,
        "stdout": [
            "Cisco IOS XE Software, Version 16.11.01a\n...(略)...
        ],
        "stdout_lines": [
            [
                "Cisco IOS XE Software, Version 16.11.01a",
                "Cisco IOS Software [Gibraltar], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.11.1a, RELEASE SOFTWARE (fc1)",
                "Technical Support: http://www.cisco.com/techsupport",
                "Copyright (c) 1986-2019 by Cisco Systems, Inc.",
                "Compiled Thu 11-Apr-19 23:59 by mcpre",
                ...(略)...
            ]
        ],
        "warnings": [
            "Platform darwin on host cat2 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information."
        ]
    }
}

PLAY RECAP **********************************************************************************************************
cat1                       : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
cat2                       : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

$

無事に、NetBox 上の Device 情報をインベントリとして利用して Playbook を実行できました。


■ おわりに

NetBox Inventory Plugin を利用することで、NetBox をインベントリとして利用できました。

今回の私のやり方ですが、情報を NetBox に集約しきれなかったのでいまいちかもしれませんが、もっと活用すると良い感じになるかも知れません。

参考 (2020/12/24 追記)

以下の記事では、インベントリに加えて、Config Context を活用してホスト変数も扱う方法が紹介されています。

qiita.com

[Ansible] 今年もよくつまづいたねランキング2019


これは Ansible 3 Advent Calendar 201920日目の記事です。


はじめに

f:id:akira6592:20191219150202p:plain:w300

みなさま、Playbook を書いて一発で意図通り正常に動かすことはできますでしょうか?

私は、そうそうできません。

今年もよくいろいろなところでつまづきました。この記事では、個人的によくつまづいたことをランキング形式で上位5位をご紹介します。どハマリというよりは、ちょっとしたことだしすぐ気がつくけど良くつまづく、といったものです。

つまづいた回数をカウントしていたたわけではなく、完全に感覚です。

なお、タイトルは以下の記事からインスパイヤされました。




■ 第5位: debug のスペル間違えがち

debug モジュールを使用した以下の Playbook、どこが間違えているかわかりますでしょうか。

- hosts: localhost
  gather_facts: no

  tasks:
    - name: debug test
      dbeug:
        msg: Hello, kingyo

debug モジュールのスペルが違いますね。なぜかわからないのですが、良く間違えてしまいます。

ERROR! no action detected in task. This often indicates a misspelled module name, or incorrect module path.

というエラーが表示されるのですぐに気がつくのですが、本当にしょうもないですね・・




■ 第4位: with_items を安直に loop に置き換えがち

with_itemsloop はネストの扱いが異なります。同じだと思って安直に loop に置き換えると、参照エラーになるケースがあります。

詳細は以下の記事を参照して下さい。

Ansible の loop は flatten されない(with_items ではなく with_list と同じ) - てくなべ (tekunabe)

・・と、記事を書いておきながらつまづくわけです。




■ 第3位: register してないのにresult 参照しがち

こういうケースです。

- hosts: iosallatest
  gather_facts: no

  tasks:
    - name: show version
      ios_command:
        commands:
          - show version
      
    - name: debug test
      debug:
        msg: "{{ result }}"

ios_command モジュールのタスクの結果を register で変数に登録してないのに、debug モジュールのタスクで参照しようとしています。

The task includes an option with an undefined variable. The error was: 'result' is undefined

というエラーが表示されるのですぐ気が付きます。

以下のように修正すると動きます。

    - name: show version
      ios_command:
        commands:
          - show version
      register: result    # 追加

手癖のように変数名を result としていますが、本当は任意です。




■ 第2位 --only-changed--changed-only に間違えがち

Playbook ではないですがランクイン。

Ansible 2.4 以上には ansible-configという、設定項目を表示するコマンドがあります。

dump サブコマンドの --only-changed オプションで、デフォルトから変更されている設定項目だけを表示できて便利です。

$ ansible-config dump --only-changed
HOST_KEY_CHECKING(/home/ansible/ansible/ansible.cfg) = False

なのですが、--only-changed だったか --changed-only だったかなかなか覚えられず、しょっちゅう間違えます。 ヘルプを見れば良い話なのですが、できればノールックでシュッとコマンドを打ちたいものです。何かいい覚え方があれば @akira6592 まで教えていただけると嬉しいです。

参考




■ 第1位: ansible_python_interpreter 変数定義し忘れがち

堂々の第1位です。

ansible_python_interpreter 変数は、Ansible が生成した Python スクリプトを実行する際に利用する Python Interpreter を指定する変数です。 指定がない場合は、(Ansible 2.8 以降)Interpreter Discoveryという機能によって見つけたパスになります。例えば、RHEL/CentOS 7であれば、/usr/bin/pythonRHEL/CentOS 8 であれば、/usr/libexec/platform-python が見つけられます。

特に指定しなくても正常に実行できることも多いですが、条件によっては以下のことが起こりえます。

  • Python 3 で実行してほしいのに Python 2 で実行されてしまう
  • 必要な Python パケージをインストールしたはずなのに hogehoge is not installed でエラーになってします。

スペルミスにも注意です・・・。



さいごに

来年は、ここにあげたつまづきポイントにつまづかないといいですね(希望)。

[Ansible] 今日のラッキーモジュールを表示する(Ansible占い)

■ はじめに

Ansible には 3,000 を超えるモジュールが標準で含まれています。

ここまで増えると「今日は何のモジュールを触ろうかな?今日のラッキーモジュールはなんだろう?」と気になりますよね。

この記事では、そんなときにぴったりで極めて実用的な Playbook をご紹介します。

※ ただし、結果に対して当方は何ら責任を負いません

  • 動作環境
    • Ansible 2.9.1


■ Playbook

ansible-doc コマンド-l でモジュールの一覧が、-j オプションで JSON 形式で表示されます(-j はAnsible 2.9 からの機能)。

この結果を受け取って、from_json フィルターで、Ansible のディクショナリにパースし、その結果をdict2items フィルターでリストに変換します。

そのリストを、with_random_choice にかけて、ランダムに1つのモジュールを選択します。

- hosts: localhost
  gather_facts: no
  connection: local

  tasks:
    - name: get module list
      command: "ansible-doc -l -j"
      changed_when: no
      register: result

    - name: do uranai
      debug:
        msg:
          - "今日のラッキーモジュールは・・・・"
          - ""
          - ""
          - "    {{ item.key }}    です!!"
          - ""
          - ""
          - "概要: {{ item.value }}"
          - "今すぐアクセス!! >> https://docs.ansible.com/ansible/latest/modules/{{ item.key }}_module.html"
      with_random_choice: "{{ result.stdout | from_json | dict2items }}"
      loop_control:
        label: "{{ item.key }}"


■ 実行例

実行例です。インベントリファイルは不要です。

$ ansible-playbook -i localhost, uranai.yml 

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

TASK [get module list] **********************************************************************************************
ok: [localhost]

TASK [do uranai] ****************************************************************************************************
ok: [localhost] => (item=fortios_system_replacemsg_spam) => {
    "msg": [
        "今日のラッキーモジュールは・・・・",
        "",
        "",
        "    fortios_system_replacemsg_spam    です!!",
        "",
        "",
        "概要: Replacement messages in Fortinet's FortiOS and FortiGate",
        "今すぐアクセス!! >> https://docs.ansible.com/ansible/latest/modules/fortios_system_replacemsg_spam_module.html"
    ]
}

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

ちなみに今日の私のラッキーモジュールは fortios_system_replacemsg_spamでした。

fortios_* モジュールは 400個以上もあるので、出現頻度は高いと思います。

もう一回くらいやってみましょう。

ok: [localhost] => (item=slxos_config) => {
    "msg": [
        "今日のラッキーモジュールは・・・・",
        "",
        "",
        "    slxos_config    です!!",
        "",
        "",
        "概要: Manage Extreme Networks SLX-OS configuration sections",
        "今すぐアクセス!! >> https://docs.ansible.com/ansible/latest/modules/slxos_config_module.html"
    ]
}

slxos_config モジュールでした。ネットワークモジュールが続いて、なんだか嬉しいです。

よく考えたら、同じ日に実行したら同じ結果になるべきですかね。おいておきましょう・・。


■ さいごに

表示の仕方はもっといろいろカスタマイズできますし、組み合わせ次第でチャットツールに送ることもできます。ぜひご活用ください。

[Ansible] NetBox モジュールで Site や Device を登録する(Collection モジュール編)


これは エーピーコミュニケーションズ Advent Calendar 2019 の15日目の記事です。


■ はじめに

NetBox とは、IPAM、ラックやデバイス管理機能を持ったWeb UI 付きのオープンソースのソフトウェアです。

f:id:akira6592:20191213162158p:plain:w400
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 の REST API を利用する

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 が表示されたらしばらく待ってから再度試します。

f:id:akira6592:20191213163800p:plain:w400
ログインは右上から

f:id:akira6592:20191213163831p:plain:w400
ユーザー名とパスワードを入力してログイン

f:id:akira6592:20191213163855p:plain:w400
ログイン成功

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 が登録された状態ががゴールです。

f:id:akira6592:20191213162225p:plain:w400
4つの Device を登録する

画面にもあるように Device には Site や Device Role、Device Type などの関連オブジェクト割り当てます。そのため、これらの関連オブジェクトを予め作成しておく必要があります。

関連オブジェクトは以下のようなものです。

オブジェクト名
Site 地理的な管理単位
Manufactures Cisco、Juniper、Aristaなど
Device Types catalyst など
Device Roles core、distribution、aggretation など

NetBox では他にも、ラック管理など、様々な機能がありますが今回は利用しません。


■ Playbook 類の作成

Playbook と、Playbook の実行に必要なファイルを作成します。

  • 今回作成するファイル一覧
.
├── host_vars
│   └── netbox01.yml
├── inventory.ini
├── netbox.yml
└── object_vars.yml

インベントリファイルの作成

NetBox のホスト情報を定義する、インベントリファイルを作成します。

NetBox の API の URL は別途定義するので、ここで定義する名前は何でも構いません。ここでは netbox01 とします。

  • inventory.ini
[netbox]
netbox01

変数定義ファイルの作成

NetBox モジュールで利用する変数を定義するファイルを作成します。

接続情報の定義

NetBox ホスト netbox01 が利用する接続情報(url、トークン)をホスト変数として定義します。一応 netbox というグループで囲って、あとで Playbook で hosts: netbox と指定するすることにします。

  • host_vars/netbox01.yml
---
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
  • netbox_url:
    • NetBox のAPI に接続するためのURLを指定します。
  • netbox_token
    • NetBox のAPI に接続するためのトークンを指定します。詳細は、NetBox の公式ドキュメントを参照してください。上記ファイルで定義している 0123456789abcdef0123456789abcdef01234567 は、docker イメージを利用した場合のものです。

オブジェクト情報の定義

NetBox 上に登録したい device などの各オブジェクトを定義する変数定義ファイルを作成します。

  • object_vars.yml
---
# Site の定義
site: my_site

# Manufacture の定義
manufactures:
  - name: Cisco
  - name: Arista

# Device Type の定義
device_types:
  - model: Catalyst
    slug: Catalyst
    manufacturer: Cisco
  - model: veos
    slug: veos
    manufacturer: Arista

# Device Role の定義
device_roles:
  - name: core
    slug: core
    color: 4caf50 # green
  - name: aggregation
    slug: aggregation
    color: 2196f3 # blue

# Device の定義
devices:
  # Cisco
  - 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
  # Arista
  - 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 追加画面で色を選ぶ項目があるので、ここから適当に色を拾いました。

f:id:akira6592:20191213162958p:plain:w200
(参考)色の選択

Playbook の作成

いよいよ、実際の作業を定義する Playbook を作成します。

利用するモジュール

今回 Playbook で利用するモジュールは以下のとおりです。

モジュール名 概要 ドキュメント(v0.1.1)
netbox_manufacturer Manufacture (Cisco、Juniper、Aristaなど) を管理する 埋め込みドキュメント
netbox_device_role Device Role (switch、firewall など) を管理する 埋め込みドキュメント
netbox_device_type Device Type (catalyst など) を管理する 埋め込みドキュメント
netbox_device Device を管理する。今回は作成と、Primary adderss の選出に利用 埋め込みドキュメント
netbox_device_interface Device のインターフェースを管理する 埋め込みドキュメント
netbox_ip_address IPアドレスを管理する。今回は Device のインターフェースの割当に利用 埋め込みドキュメント

これらのモジュールは Collection モジュールのため、公式ドキュメントには掲載されていません。そのため詳細情報は、モジュールのコードに埋め込まれているドキュメント用の部分を確認するか、ansible-doc コマンドで名前空間を含めたモジュール名を指定して表示させます。

  • 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 にまとめてしまいます。

冒頭の collectionfragmentedpacket.netbox_modules を指定することにより、モジュール利用時に Collection モジュールを名前空間なしで指定できるようになります。例えば、名前空間ありで指定する場合は fragmentedpacket.netbox_modules.netbox_manufacturer とするところ、名前空間なしでは単にnetbox_manufacturer と指定できます。詳細は公式ドキュメント参照

  • netbox.yml
- hosts: netbox
  connection: local
  gather_facts: no
  # Collection モジュールの読み込み
  collections:
    - fragmentedpacket.netbox_modules

  vars:
    # pynetbox をインストールした venv 環境の python インタープリターを指定
    ansible_python_interpreter: ~/ansible291/bin/python
  
  # オブジェクト定義ファイルの読み込み
  vars_files:
    - object_vars.yml

  tasks:
    # site
    - name: site
      netbox_site:
        netbox_url: "{{ netbox_url }}"
        netbox_token: "{{ netbox_token }}"
        data:
          name: "{{ site }}"

    # manufacturer 追加
    - name: add manufacturers
      netbox_manufacturer:
        netbox_url: "{{ netbox_url }}"
        netbox_token: "{{ netbox_token }}"
        data:
          name: "{{ item.name }}"
      loop: "{{ manufactures }}"

    # device role 追加
    - 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 }}"
          # vm_role: true
      loop: "{{ device_roles }}"

    # device type 追加
    - 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 }}"
          # vm_role: true
      loop: "{{ device_types }}"

    # device 追加
    - 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 }}"

    # device に interface 追加 
    - 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 }}"

    # device interface に IP addresses 割り当て
    - 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 }}"

    # Primary adderss を選出
    - 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 側の確認

ちゃんとオブジェクトが作れたか確認します。

f:id:akira6592:20191213162350p:plain:w500
Site が登録された

f:id:akira6592:20191213162418p:plain:w500
Manufacturer が登録された

f:id:akira6592:20191213162448p:plain:w500
Device Type が登録された

f:id:akira6592:20191213162512p:plain:w500
Device Role が登録された

f:id:akira6592:20191213162543p:plain
Device が登録された(完成!)

無事にすべて想定通りに登録できました。

■ まとめ

Ansible で NetBox の Device を登録できることを確認しました。

Device の登録の前にも、Site や Device Type などの登録も予め必要でしたが、Collection モジュールを利用することで、一連の作業が自動化できました。

前述のように、Ansible には NetBox をインベントリとして利用する機能もあるので、NetBox と Ansible はなかなか相性がよいような印象です。

2020年2月開始の Cisco DevNet 認定「DevNet Associate (DEVASC 200-901)」のリンクまとめ

はじめに

2020/02/24 に、Cisco 技術者認定試験の大きな改定が予定されています。

CCNA が1本化されたり、いきなり上位試験を受けられるようになったりなど、なかなかの大改訂です。

その中でも私が一番気になっているのが、ネットワークプログラマビリティや自動化が範囲の DevNet 認定です。ネットワークの基礎知識だけでなく、Python や Git、API、構造化データや NETCONF などの知識が求められます。

レベルは 3つあって、一番やさしいのが「DevNet Associate (DEVASC 200-901)」です。

この記事では、「DevNet Associate (DEVASC 200-901)」の概要を知ったり、試験対策のとっかかりになりそうなリンクをまとめます。

大改訂の概要

itjinzai-lab.jp

DevNet 認定の概要

learningnetwork.cisco.com www.cisco.com itjinzai-lab.jp

DevNet Associate (DEVASC 200-901) の概要

www.cisco.com www.cisco.com

developer.cisco.com

試験範囲

PDF

https://www.cisco.com/c/dam/global/ja_jp/training-events/training-certifications/exam-topics/200-901-DEVASC.pdf

見出しのみ抜粋

15% 1.0 ソフトウェア開発と設計
20% 2.0 API の理解と使用
15% 3.0 シスコ プラットフォームと開発
15% 4.0 アプリケーションの展開とセキュリティ
20% 5.0 インフラストラクチャと自動化
15% 6.0 ネットワークの基礎

対応コンテンツ

試験用というわけではなないですが、試験範囲に該当する既存の DevNet 上のコンテンツの対応表が掲載されています。 developer.cisco.com

こちらも参考 https://learningnetwork.cisco.com/docs/DOC-36629learningnetwork.cisco.com

DevNet には他にも様々なコンテンツが揃っています。

developer.cisco.com

日本語

DevNet へのログインからていねいに解説されています。 qiita.com

DevNet Learning Labsで学習してみよう

developer.cisco.com

書籍

公式

2020年6月発売予定

大改訂に対応するその他の書籍はこちら www.ciscopress.com

fullstack networker

[2019/12/28 追記]

後述のオンライン学習コンテンツの fullstack networker 内の API の章を詳しくしたような書籍です。

オンライン学習コンテンツ(有料)

DevNet Associate Fundamentals Course

[2020/02/22 追記]

developer.cisco.com 公式のコンテンツです。

fullstack networker

有料ですが、対策のコンテンツがありました。一部は無料で見れます。 www.fullstacknetworker.com

CBT Nuggets

[2019/12/28 追記]

CBT ベースのコンテンツ

www.cbtnuggets.com

動画

勉強の過程を動画に撮ってアップされている方もいらっしゃいます。

www.youtube.com


おわりに

このブログでも関連技術を扱っていきたいと思います。

[Ansible] 【別解】 Ansibleの呼吸 肆ノ型 フィルター芸「0,1反転」


これは Ansible 3 Advent Calendar 2019 の6日目の記事です。(さかのぼり投稿)


■ はじめに

アドベントカレンダーの 5日目の記事は、既存の 0,1 が書かれているファイルを反転させて別のファイルを生成するという記事でした。

qiita.com

おもわず別解を考えてみたいネタで、「是非 :)」とのことだったので考えました。


■ 使用するファイル

元の記事と同じファイルを用意しおきます。

$ cat before_number.txt
001010101010101010010101
111000101010100111010011
100001010100001010101010
110101010101010010101010
101010100101010101010000
111111100000111010000000


■ Playbook

処理の概要は以下のとおりです。

Playbook は以下のとおりです。

  • main.yml
- hosts: localhost
  connection: local
  gather_facts: no

  tasks:
    # まずそのままコピー
    - name: copy
      copy:
        src: before_number.txt
        dest: after_number.txt

    # 0 と 1 をそれぞれ仮の文字に置換
    - name: replace to temporary chars
      replace:
        path: after_number.txt
        regexp: "{{ item.regexp }}"
        replace: "{{ item.replace }}"
      loop:
        - regexp: 0
          replace: "x"
        - regexp: 1
          replace: "y"  # True にならないようにクォーテーションが必要

    # 仮の文字を反転した 0/1に置換
    - name: replace to 0/1
      replace:
        path: after_number.txt
        regexp: "{{ item.regexp }}"
        replace: "{{ item.replace }}"
      loop:
        - regexp: "x"   # もともと 0 だったところ
          replace: 1    # 1 に置換
        - regexp: "y"   # もともと 1 だったところ
          replace: 0    # 0 に置換


■ Playbook実行

Playbook を実行します

$ ansible-playbook -i localhost, main.yml

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

TASK [copy] *******************************************************************************************************
changed: [localhost]

TASK [replace to temporary chars] *********************************************************************************
changed: [localhost] => (item={'regexp': 0, 'replace': 'x'})
changed: [localhost] => (item={'regexp': 1, 'replace': 'y'})

TASK [replace to 0/1] *********************************************************************************************
changed: [localhost] => (item={'regexp': 'x', 'replace': 1})
changed: [localhost] => (item={'regexp': 'y', 'replace': 0})

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

$

作成されたファイルの中身を見てみます。

$ cat after_number.txt
110101010101010101101010
000111010101011000101100
011110101011110101010101
001010101010101101010101
010101011010101010101111
000000011111000101111111

無事に before_number.txt と 0,1 が反転したファイルが生成されました。

おまけ的ですが、after_number.txt の内容が書き換わっていく様子のです。


■ その他考えたこと

この方法に至るまでに考えたことです。

  • せっかく2進数なので、Pythonビット反転の ~ を使えたら良いなと思ったけど、うまく 組み込めなかった
  • replace モジュールを使ってみよう
  • 01 に、10 にいっぺんに置換できればいいけど、できなさそうだ
  • ternayフィルターを使えばできただろうか・・)
  • 2段階の置換でどうにかできた
  • ただ、この方法だと冪等性がなく、毎回 changed になってしまう
  • 中間ファイルの生成処理を挟めばできるかな、まぁいいか。