【目次】
■ はじめに
Ansible は Cisco ACI にも対応して、多数のモジュールがあります。ほとんどのモジュールには、どのような状態であるべきかを指定する state オプションがあります。このオプションは present、absent のような「状態」に加えて、query という検索する「操作」も指定できます。
この記事では、簡単なサンプルを元に state: query での情報取得の方法と、assert による値の確認などを説明します。
(私自身は調査中の部分もありますが、記録として残しておきます)
■ 使い方
基本的な使い方
基本的には、state オプションに query を指定するだけです。
特定の Tenant 情報を取得

まず単純に、Tenant の情報を取得する Playbook です。オブジェクトの階層の上のほうなので、検索条件の指定方法は単純です。
ここでは、aci_tenant モジュールを利用し、tenant1 という名前の Tenant の情報を取得します。
query の結果を register で指定した変数 resutlt に格納して、debug モジュールで表示します。
- Playbook
--- - name: query hosts: apic gather_facts: no tasks: - name: tenant aci_tenant: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no tenant: tenant1 # Tenant 名を指定 state: query # query する output_level: debug register: result - name: debug debug: var: result
- 実行結果(debug抜粋)
TASK [debug] *********************************************************************************** ok: [apic01] => { "result": { ...(略)... "current": [ { "fvTenant": { "attributes": { "annotation": "", "childAction": "", "descr": "", "dn": "uni/tn-tenant1", "extMngdBy": "", "lcOwn": "local", "modTs": "2019-11-03T12:25:26.882+00:00", "monPolDn": "uni/tn-common/monepg-default", "name": "tenant1", "nameAlias": "", "ownerKey": "", "ownerTag": "", "status": "", "uid": "15374" } } } ], ...(略)... }
もし、指定した名前の Tenant がない場合は、current が空のリスト(current: [])になります。
補足: query の条件に使用される値と使用されない値がある
少し注意が必要なのは「query の条件に使用される値と使用されない値がある」という点です。例えば、tenant オプションを指定すれば、「この名前のオプションはあるか」という query になります。
一方で、 description オプションは query 時は無視されます。
具体的には
- aci_tenant: # ...(略)... tenant: tenant1 description: hogehoge state: query
という指定した場合、単に
- Tenant 名が
tenant1であること
のみが query 条件(SQLでいう where 句のようなもの)になります。
- Tenant 名が
tenant1、かつ description がhogehogeであること
ではありません。
どのようなオプションが query の条件に使用されるか、一言で説明したものは見つけられていません。いくつか試して「オブジェクトの階層を示すオプション」は条件に使用される、という感触はあります。
たとえば、aci_epg モジュールの場合、EPG は Tenant > Application Profile > EPG というオブジェクトの階層なので、tenant や ap オプションは query 条件に使用され、 desicription はオブジェクトの階層には関係ない属性値なので query 条件に使用されない、とった具合です。
全 Tenant 情報を取得

aci_tenant モジュールで、tenant オプションを指定しないで query すると、全 Tenant の情報を取得します。
- Playbook
--- tasks: - name: tenant aci_tenant: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no state: query output_level: debug register: result - name: debug debug: var: result
- 実行結果(debug抜粋)
TASK [debug] *********************************************************************************** ok: [apic01] => { "result": { ...(略)... "current": [ ...(略)... { "fvTenant": { "attributes": { "annotation": "", "childAction": "", "descr": "", "dn": "uni/tn-tenant1", ...(略)... } } }, { "fvTenant": { "attributes": { "annotation": "", "childAction": "", "descr": "", "dn": "uni/tn-tenant2", ...(略)... } } }, ...(略)... }
少し応用的な使い方
階層を伴うオブジェクトのモジュールでは、階層を示すオプション(例えば aci_epg モジュール)の tenant や ap オプション)の有無によって、検索パターンがあります。
ここでは、EPG(階層は Tenant > Application Profile > EPG)を扱う aci_epg モジュールを例にして、パターンごとに説明します。
パターン1. 何も指定なし

何も指定しないパターンです。
tasks: - name: epg aci_epg: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no state: query # query する output_level: debug register: result - name: debug debug: var: result
全 Tenant、全 AP、全 EPG の情報を取得できます。dn を見るとオブジェクトの階層が分かります。
- 実行結果(debug抜粋)
TASK [debug] ******************************************************************* ok: [apic01] => { "result": { ...(略)... "current": [ { "fvAEPg": { "attributes": { ...(略)... "dn": "uni/tn-tenant2/ap-ap1/epg-epg1", ...(略)... { "fvAEPg": { "attributes": { ...(略)... "dn": "uni/tn-tenant2/ap-ap2/epg-epg2", ...(略)... { "fvAEPg": { "attributes": { ...(略)... "dn": "uni/tn-tenant1/ap-ap1/epg-epg1", ...(略)... { "fvAEPg": { "attributes": { ...(略)... "dn": "uni/tn-tenant1/ap-ap2/epg-epg2", ...(略)... }
パターン2. Tenant のみ指定

Tenant のみ指定しないパターンです。
tasks: - name: epg aci_epg: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no tenant: tenant1 # Tenant の指定 state: query # query する output_level: debug register: result - name: debug debug: var: result
指定した Tenant 内の、全 AP、全 EPG の情報を取得できます。
- 実行結果(debug抜粋)
TASK [debug] *********************************************************************************** ok: [apic01] => { "result": { ...(略)... "current": [ { "fvTenant": { "attributes": { ...(略)... "name": "tenant1", ...(略)... }, "children": [ { "fvAp": { "attributes": { ...(略)... "name": "ap2", ...(略)... }, "children": [ { "fvAEPg": { ...(略)... "name": "epg2", ...(略)... } } ] } }, { "fvAp": { "attributes": { ...(略)... "name": "ap1", ...(略)... }, "children": [ { "fvAEPg": { "attributes": { "name": "epg1", ...(略)... }
- リクエストURL
パターン3. Tenant、AP を指定

tasks: - name: epg aci_epg: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no tenant: tenant1 # Tenant の指定 ap: ap1 # AP の指定 state: query # query する output_level: debug register: result - name: debug debug: var: result
指定した Tenant、AP 内の、全 EPG の情報を取得できます。
- 実行結果(debug抜粋)
TASK [debug] ******************************************************************* ok: [apic01] => { "result": { ...(略)... "current": [ { "fvAp": { "attributes": { "annotation": "", "childAction": "", "descr": "", "dn": "uni/tn-tenant1/ap-ap1", ...(略)... }, "children": [ { "fvAEPg": { "attributes": { ...(略)... "name": "epg1", ...(略)... }
- リクエストURL
パターン4. Tenant、AP、EPG を指定

tasks: - name: epg aci_epg: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no tenant: tenant1 # Tenant の指定 ap: ap1 # AP の指定 epg: epg1 # EPG の指定 state: query # query する output_level: debug register: result - name: debug debug: var: result
指定した Tenant、AP、EPG の情報を取得できます。
- 実行結果(debug抜粋)
TASK [debug] ******************************************************************* ok: [apic01] => { "result": { ...(略)... "current": [ { "fvAEPg": { "attributes": { "annotation": "", "childAction": "", "configIssues": "", "configSt": "applied", "descr": "", "dn": "uni/tn-tenant1/ap-ap1/epg-epg1", ...(略)... }
- リクエストURL
パターン5. Tenant、EPG を指定

tasks: - name: epg aci_epg: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no tenant: tenant1 # Tenant の指定 epg: epg1 # EPG の指定 state: query # query する output_level: debug register: result - name: debug debug: var: result
指定した Tenant、内の、全 EPG の情報を取得できます。
- 実行結果(debug抜粋)
TASK [debug] ******************************************************************* ok: [apic01] => { "result": { ...(略)... "current": [ { "fvTenant": { "attributes": { ...(略)... "dn": "uni/tn-tenant1", ...(略)... }, "children": [ { "fvAp": { "attributes": { ...(略)... "name": "ap1", ...(略)... }, "children": [ { "fvAEPg": { "attributes": { ...(略)... "name": "epg1", ...(略)... }
- リクエストURL
- https://apic01/api/mo/uni/tn-tenant1.json?rsp-subtree-filter=eq(fvAEPg.name, \"epg1\")&rsp-subtree-class=fvAEPg,fvRsBd&rsp-subtree=full
■ 戻り値について
各モジュールの説明ページの Return Values に戻り値の説明があります。
例: aci_epg モジュールの Return Values
ここまでの Playbook の実行結果でも示されていますが、regsiter で指定した変数の中に query 結果が格納されます。ここでは register: result とした場合で補足説明します。
0件の場合は空のリストの current になる
result.current に query 結果そのものがリストとして格納されます。query 結果が 0 件の場合は、空のリスト result.current[] になります。そのため、result.current 自体の有無では、結果の有無の確認はできないので注意です。もしかしたら、エラーになるケースもあるかもしれません。
output_level: debug しておくとリクエストした URL も分かる
output_level: debug しておくと、result.url には、モジュールが APIC にリクエストした URL が、result.filter にはフィルター文字(クエリストリング)が格納されます。「この指定方法で、実際どんなリクエストをしたんだろう?」と知りたいときに非常に便利です。
ファイル出力時の整形には to_nice_json が便利
結果の内容をファイル場合は、copy モジュールで content: {{ result }} のように指定します。
ただ、そのままですと、平ぺったい JSON
{"status": 200, "proposed": {}, "url": "https://apic01/api/mo/uni/tn-tenant1.json", ...(略)...
のように、人間には読みにくい形になっていまいます。そこで to_nice_json フィルターを利用すると、読みやすく nice になります。
- name: file output copy: content: "{{ result | to_nice_json }}" dest: result_tenant.json
$ cat result_tenant.json { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "current": [ { "fvTenant": { "attributes": { "annotation": "", "childAction": "", "descr": "", "dn": "uni/tn-tenant1", ...(略)... }
assert でオブジェクトの有無や値をチェックする
情報を取得できるということは、assert モジュールを利用して、期待した状態とあっているかどうかのチェックもできます。
ここでは、オブジェクトの有無を assert する例と、オブジェクトが持っている値を assert する例を紹介します。
オブジェクトの有無を assert する
query した結果の current のリストの長さが 1 以上であることを確認することで、オブジェクトの有無を確認できます。
ここでは、指定した Tenant、AP 内に、指定した EPG があることを確認します。
tasks: - name: assert test aci_epg: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no tenant: tenant1 # Tenant の指定 ap: ap1 # AP の指定 epg: epg1 # EPG の指定 state: query # query する output_level: debug register: result - name: assert assert: that: - (result.current | length) >= 1
他にも色々方法はあると思います。
- 実行結果(assert抜粋)
TASK [assert] ****************************************************************** ok: [apic01] => { "changed": false, "msg": "All assertions passed" }
assert が OK であれば All assertions passed と表示されます。
値を assert する
query した結果の current の中の ssert したい値を指定して assert します。
ここでは、指定した Tenant、AP 内に、指定した EPG の Description が test であることを確認します。

tasks: - name: assert test aci_epg: host: "{{ ansible_host }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no tenant: tenant1 # Tenant の指定 ap: ap1 # AP の指定 epg: epg1 # EPG の指定 state: query # query する output_level: debug register: result - name: assert assert: that: - result.current[0].fvAEPg.attributes.descr == "test"
(current[0] と指定しているあたりが、あまりスマートではないですね・・)
- 実行結果(assert抜粋)
TASK [assert] ****************************************************************** ok: [apic01] => { "changed": false, "msg": "All assertions passed" }
assert が OK であれば All assertions passed と表示されます。
■ オブジェクト特化モジュールで対応できないなら aci_rest モジュールの出番
これまで紹介したように、aci_tenant や aci_epg など、各オブジェクト用のモジュールで state: query を指定することで、query を実行できますが、なかには思うような検索条件を指定できないこともあるかもしれません。
そんなときには、APIC の REST API を叩くことに特化した、aci_rest モジュールを利用します。Playbook を書く側が REST API の仕様を意識する必要がある文、柔軟な処理を指定できます。
詳細は、公式ドキュメントの aci_rest モジュール の説明ページを参照してください。
■ さいごに
Cisco ACI モジュール の state: query でオブジェクト情報を取得したり、assert する方法をご紹介しました。
設定系のタスクよりも query のほうが、よりオブジェクトの階層を意識する必要があるきがしました。