はじめに
Ansible では、結果が JSON になるタスクを実行すると、(少なくとも表示上は)エスケープされた JSON が返ってくることがあります。
この場合、構造化データのように見えて文字列なので、ディクショナリ(構造化データ)として正しく扱えません。
from_json フィルターをかけると、うまくいきます。
この記事では簡単なサンプルをもとに説明します。
- 動作確認環境
- Ansible 2.9.7
■ エスケープされた JSON とは?
以下は、nclu モジュールを利用して、Cumulus Linux に対して、show bgp summary json コマンドを実行した結果を、debug モジュールで表示した結果です。
msg 内に \ によるエスケープが入っています。JSON のように見えてただの文字列です。
TASK [debug show bgp summary] *************************************************************************
ok: [leaf01] => {
"msg": {
"changed": false,
"failed": false,
"msg": "{\n \"ipv4 unicast\": {\n \"as\": 65011, \n \"bestPath\": {\n \"multiPathRelax\": \"true\"\n }, \n \"dynamicPeers\": 0, \n \"peerCount\": 2, \n \"peerGroupCount\": 1, \n \"peerGroupMemory\": 64, \n \"peerMemory\": 42352,
...(略)...
}
}
このままでは、result.msg['ipv4 unicast'] のように、内部のキーを指定しても、'dict object' has no attribute 'ipv4 unicast' のような、キーの参照エラーになってしまいます。
ちょっとハマりやすいのは、上記例でいうと result.msg を参照したときは、以下のように、いかにも正しい JSON っぽく表示される点です。
TASK [debug show bgp summary] ************************************************************************* ok: [leaf01] => { "msg": { "ipv4 unicast": { "as": 65011, "bestPath": { "multiPathRelax": "true" }, "dynamicPeers": 0, "peerCount": 2, ...(略)...
どう見てもディクショナリとして扱えそうですが、{{ result.msg | type_debug }} の結果は AnsibleUnsafeText です。どうして・・。
from_json フィルターでディクショナリに変換
正しくディクショナリとして扱うために、from_json フィルターをかけます。
"{{ result_bgp_summary_raw | from_json }}"
こでれで、JSON、ディクショナリになります。
TASK [debug show bgp summary] ************************************************************************* ok: [leaf01] => { "msg": { "ipv4 unicast": { "as": 65011, "bestPath": { "multiPathRelax": "true" }, "dynamicPeers": 0, "peerCount": 2, ...(略)...
"{{ result_bgp_summary_raw.msg | from_json | type_debug }}" の結果は dict です。
ここまでくれば、通常のディクショナリと同じく、特定のキーを参照したりするだけです。
タスク例
- name: debug show bgp summary debug: msg: "{{ bgp_summary['ipv4 unicast']['peerCount'] }}" vars: bgp_summary: "{{ result_bgp_summary_raw.msg | from_json }}"
(キーの指定を .ipv4 unicast ではなく ['ipv4 unicast'] としているのは、今回対象のデータに、たまたまスペース入りのキー名があったためです)
実行例
TASK [debug show bgp summary] ************************************************************************* ok: [leaf01] => { "msg": "2" }
■ おわりに
エスケープされた JSON に、from_json フィルターをかけて、ディクショナリにする方法をご紹介しました。
私だけかもしれませんが、Ansible で構造化データの扱いで悩むことが結構あります。
あるはずのキーが has no attribute となってハマったときは、エスケープされてないか、type_debugの結果はどうなるか、などを確認するのがよさそうです。