【目次】
■ はじめに
Ansible は Cisco ACI にも対応して、多数のモジュール があります。ほとんどのモジュールには、どのような状態であるべきかを指定する state
オプションがあります。このオプションは present
、absent
のような「状態」に加えて、query
という検索する「操作」も指定できます。
この記事では、簡単なサンプルを元に state: query
での情報取得の方法と、assert
による値の確認などを説明します。
(私自身は調査中の部分もありますが、記録として残しておきます)
環境
Cisco DevNet Sandbox (APIC 4.1)
Ansible 2.9.0
■ 使い方
基本的な使い方
基本的には、state
オプションに query
を指定するだけです。
特定の Tenant 情報を取得
特定の Tenant 情報を取得
まず単純に、Tenant の情報を取得する Playbook です。オブジェクトの階層の上のほうなので、検索条件の指定方法は単純です。
ここでは、aci_tenant
モジュール を利用し、tenant1
という名前の Tenant の情報を取得します。
query の結果を register
で指定した変数 resutlt
に格納して、debug
モジュールで表示します。
---
- name : query
hosts : apic
gather_facts : no
tasks :
- name : tenant
aci_tenant :
host : "{{ ansible_host }}"
username : "{{ username }}"
password : "{{ password }}"
validate_certs : no
tenant : tenant1
state : query
output_level : debug
register : result
- name : debug
debug :
var : result
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
という指定した場合、単に
のみが query 条件(SQL でいう where 句のようなもの)になります。
Tenant 名が tenant1
、かつ description が hogehoge
であること
ではありません。
どのようなオプションが query の条件に使用されるか、一言で説明したものは見つけられていません。いくつか試して「オブジェクトの階層を示すオプション」は条件に使用される、という感触はあります。
たとえば、aci_epg
モジュール の場合、EPG は Tenant > Application Profile > EPG というオブジェクトの階層なので、tenant
や ap
オプションは query 条件に使用され、 desicription
はオブジェクトの階層には関係ない属性値なので query 条件に使用されない、とった具合です。
全 Tenant 情報を取得
全 Tenant 情報を取得
aci_tenant
モジュール で、tenant
オプションを指定しないで query すると、全 Tenant の情報を取得します。
---
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
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
output_level : debug
register : result
- name : debug
debug :
var : result
全 Tenant、全 AP、全 EPG の情報を取得できます。dn
を見るとオブジェクトの階層が分かります。
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 のみ指定
Tenant のみ指定しないパターンです。
tasks :
- name : epg
aci_epg :
host : "{{ ansible_host }}"
username : "{{ username }}"
password : "{{ password }}"
validate_certs : no
tenant : tenant1
state : query
output_level : debug
register : result
- name : debug
debug :
var : result
指定した Tenant 内の、全 AP、全 EPG の情報を取得できます。
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 ",
...(略)...
}
パターン3. Tenant、AP を指定
Tenant、AP を指定
tasks :
- name : epg
aci_epg :
host : "{{ ansible_host }}"
username : "{{ username }}"
password : "{{ password }}"
validate_certs : no
tenant : tenant1
ap : ap1
state : query
output_level : debug
register : result
- name : debug
debug :
var : result
指定した Tenant、AP 内の、全 EPG の情報を取得できます。
TASK [ debug ] *******************************************************************
ok : [ apic01 ] => {
"result ": {
...(略)...
"current ": [
{
"fvAp ": {
"attributes ": {
"annotation ": "",
"childAction ": "",
"descr ": "",
"dn ": "uni/tn-tenant1/ap-ap1 ",
...(略)...
} ,
"children ": [
{
"fvAEPg ": {
"attributes ": {
...(略)...
"name ": "epg1 ",
...(略)...
}
パターン4. Tenant、AP、EPG を指定
Tenant、AP、EPG を指定
tasks :
- name : epg
aci_epg :
host : "{{ ansible_host }}"
username : "{{ username }}"
password : "{{ password }}"
validate_certs : no
tenant : tenant1
ap : ap1
epg : epg1
state : query
output_level : debug
register : result
- name : debug
debug :
var : result
指定した Tenant、AP、EPG の情報を取得できます。
TASK [ debug ] *******************************************************************
ok : [ apic01 ] => {
"result ": {
...(略)...
"current ": [
{
"fvAEPg ": {
"attributes ": {
"annotation ": "",
"childAction ": "",
"configIssues ": "",
"configSt ": "applied ",
"descr ": "",
"dn ": "uni/tn-tenant1/ap-ap1/epg-epg1 ",
...(略)...
}
パターン5. Tenant、EPG を指定
Tenant、EPG を指定
tasks :
- name : epg
aci_epg :
host : "{{ ansible_host }}"
username : "{{ username }}"
password : "{{ password }}"
validate_certs : no
tenant : tenant1
epg : epg1
state : query
output_level : debug
register : result
- name : debug
debug :
var : result
指定した Tenant、内の、全 EPG の情報を取得できます。
TASK [ debug ] *******************************************************************
ok : [ apic01 ] => {
"result ": {
...(略)...
"current ": [
{
"fvTenant ": {
"attributes ": {
...(略)...
"dn ": "uni/tn-tenant1 ",
...(略)...
} ,
"children ": [
{
"fvAp ": {
"attributes ": {
...(略)...
"name ": "ap1 ",
...(略)...
} ,
"children ": [
{
"fvAEPg ": {
"attributes ": {
...(略)...
"name ": "epg1 ",
...(略)...
}
■ 戻り値について
各モジュールの説明ページの 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
ap : ap1
epg : epg1
state : query
output_level : debug
register : result
- name : assert
assert :
that :
- (result.current | length) >= 1
他にも色々方法はあると思います。
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
であることを確認します。
EPG の Description
tasks :
- name : assert test
aci_epg :
host : "{{ ansible_host }}"
username : "{{ username }}"
password : "{{ password }}"
validate_certs : no
tenant : tenant1
ap : ap1
epg : epg1
state : query
output_level : debug
register : result
- name : assert
assert :
that :
- result.current[0 ].fvAEPg.attributes.descr == "test"
(current[0]
と指定しているあたりが、あまりスマートではないですね・・)
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 のほうが、よりオブジェクトの階層を意識する必要があるきがしました。