Ansible TowerまたはAWXでインベントリースクリプト(カスタムスクリプト)を使用する方法を説明します。Ansible Tower(AWX)にはvCenterやAWSと連携する機能が備わっていますが、スクリプトを書いて任意のホストやグループを動的に生成する事ができます。
インベントリースクリプトのユースケース
Ansible Tower(AWX)にはvCenterやAWSと連携する機能(ダイナミックインベントリ)が備わっていますが、以下のようなシナリオでは要件を満たす事ができません。
- ダイナミックインベントリのデフォルトの挙動が許容されない(例えば、vCenterと連携させた時にホスト名末尾に表示されるUUIDを削除するように「偉い人」に指示された)
- 設定のみで連携できないソフトウェアとの連携を作り込む必要がある(Cisco機器の管理製品, JP1ファミリーなど)
このようなシナリオが発生した時に有効になるのがインベントリースクリプト(カスタムスクリプト)です。
インベントリースクリプトの使い方
スクリプトの仕様
インベントリースクリプト(カスタムスクリプト)を作成するには、ホストやグループの一覧をJSON形式で標準出力するスクリプトを作成する必要があります。どのようなJSONフォーマットにすればよいかは、ansible-inventoryコマンドを使って調査すると良いでしょう。この辺りはマニュアルを読まなくても、ansible-inventoryコマンドの出力を見よう見真似でも充分実装できます。
まずはサンプルとなるインベントリースクリプトを作成します。
# cat <<EOF >inventory.ini [ios:vars] ansible_connection = network_cli ansible_network_os = 'ios' ansible_user = 'cisco' ansible_password = 'cisco' [eos:vars] ansible_connection = network_cli ansible_network_os = 'eos' ansible_user = 'admin' ansible_password = 'P@ssw0rd' [ios] ios171 ansible_host=192.168.1.171 ios172 ansible_host=192.168.1.172 [eos] eos173 ansible_host=192.168.1.173 eos174 ansible_host=192.168.1.174 EOF
以下のようにansible-inventoryコマンドを使用すると、ini形式のインベントリファイルをJSON形式に変換する事ができます。このようなJSON形式を見よう見真似で出力すれば、カスタムスクリプトの開発は可能です。
# ansible-inventory -i inventory.ini --list
{
"_meta": {
"hostvars": {
"eos173": {
"ansible_connection": "network_cli",
"ansible_host": "192.168.1.173",
"ansible_network_os": "eos",
"ansible_password": "P@ssw0rd",
"ansible_user": "admin"
},
"eos174": {
"ansible_connection": "network_cli",
"ansible_host": "192.168.1.174",
"ansible_network_os": "eos",
"ansible_password": "P@ssw0rd",
"ansible_user": "admin"
},
"ios171": {
"ansible_connection": "network_cli",
"ansible_host": "192.168.1.171",
"ansible_network_os": "ios",
"ansible_password": "cisco",
"ansible_user": "cisco"
},
"ios172": {
"ansible_connection": "network_cli",
"ansible_host": "192.168.1.172",
"ansible_network_os": "ios",
"ansible_password": "cisco",
"ansible_user": "cisco"
}
}
},
"all": {
"children": [
"eos",
"ios",
"ungrouped"
]
},
"eos": {
"hosts": [
"eos173",
"eos174"
]
},
"ios": {
"hosts": [
"ios171",
"ios172"
]
}
}
インベントリースクリプトの登録
「インベントリースクリプト」の設定画面に遷移し、「+(追加)」ボタンを押下します。

今回は動作確認ですので、実践的ではありませんが以下のようなJSONファイルを標準出力するのみの単純なスクリプトを用意します。実践ではperlやpythonで動的なJSONファイルを生成する事になるでしょう。
#!/bin/bash
cat << EOF
{
"ios_group": {
"hosts": [
"ios01",
"ios02"
],
"vars": {
"ansible_password": "admin",
"ansible_user": "admin"
}
}
}
EOF

CLIまたはAPIで操作する場合は以下の通りです。
インベントリーソースの登録
「インベントリー」「ソース」の順に画面遷移し、「+(追加)」ボタンを押下します。

必要な情報を入力し「保存」を押下します。インベントリースクリプトを使用する場合は、「ソース」を「カスタムスクリプト」にします。

CLIで操作する場合は以下の通りです。awxのCLIは殆どの場合はidではなく名前による指定が可能ですが、例外的にインベントリスクリプトはid(数字)で指定する必要があります。
名前で指定できないのは意図した挙動ではなく、将来的に改善される可能性もあります。
APIで操作する場合は以下の通りです。
インベントの同期
インベントリファイルの設定が終わったら、インベントリの「ソース」の設定画面に戻ります。設定直後に自動的に同期されるわけではありませんので、「同期」ボタンを押下しインベントリ情報を同期させます。

同期に成功した場合は、「雲(クラウド)」のアイコンが緑に変わります。

CLIで操作する場合は以下の通りです。inventory_source_idは環境に応じて適宜変更ください。また、–monitorオプションを付与すると同期の様子をリアルタイムで観察する事もできます。

APIで操作する場合は以下の通りです。同期の前後でstatusが”never updated”から”successful”に変わった事を確認できます。
実践的なインベントリースクリプトの例
静的な結果を返すインベントリースクリプトではなく、やや実践的な例としてCisco DevNet SandboxのCisco DNA Centerに登録されている機器一覧を返すインベントリースクリプトを紹介します。
インベントリスクリプトがpythonモジュールを使用する場合は、予めpip installの操作をしておきましょう。コンテナ環境ではホストOS側ではなく、コンテナawx_webのpythonが呼び出される事に留意ください。
pip3 install requests
docker container exec -i -t awx_web /bin/bash pip3 install requests
以下Cisco DevNet SandboxのCisco DNA Centerに登録された機器一覧を標準出力するスクリプトの実装例です。Cisco DevNet Sandboxへの接続パスワードなどは予告なしに変更される可能性がある事をご了承ください。
#!/usr/bin/python3
import json
import requests
requests.packages.urllib3.disable_warnings()
DNAC_HOST = 'sandboxdnac.cisco.com'
DNAC_USER = 'devnetuser'
DNAC_PASS = 'Cisco123!'
inventory = {
'ios_group': {
'hosts': [],
'vars': {
'ansible_user': 'cisco',
'ansible_password': 'cisco'
}
}
}
url_login = 'https://' + DNAC_HOST + '/dna/system/api/v1/auth/token'
result_login = requests.post(url=url_login, auth=(DNAC_USER, DNAC_PASS), verify=False)
token = result_login.json()['Token']
# print(token)
url_get = 'https://' + DNAC_HOST + '/dna/intent/api/v1/network-device'
headers_get = {'X-auth-token': token}
result_get = requests.get(url=url_get, headers=headers_get, verify=False)
# print(json.dumps(result_get.json(), indent=2))
for item in result_get.json()['response']:
inventory['iso_group']['hosts'].append(item['managementIpAddress'])
print(json.dumps(inventory, indent=2))

Cisco DevNet SandboxのCisco DNA Centerと連携できた事を確認します。

