はじめに
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
の結果はどうなるか、などを確認するのがよさそうです。