この記事は、Ansible Advent Calendar 2022 (Adventar 版) の 20日目の記事です。
偶然ですが、同日 Qiita 版に同じフィルターを扱った記事がアップされています。こちらも是非ご覧ください。
ansible.utilsコレクションのフィルタ(keep_keys/remove_keys/replace_keys)を試す - うさラボ
はじめに
ansible.utils
コレクションの 2.5.0 で、ディクショナリを操作する、以下の3つのフィルターが追加されました。
No. | フィルター名 | 概要 |
---|---|---|
1. | ansible.utils.keep_keys |
特定のキー配下のみ残す |
2. | ansible.utils.remove_keys |
特定のキー配下を取り除く |
3. | ansible.utils.replace_keys |
キーの名前を変更する |
ディクショナリのキーをもとにして、残す、除外する、置き換える、といったことができます。
おそらく、json_query
フィルター経由で JMESPath を使っても同様のことができると思います。(冒頭で紹介した別記事に例があります)
この記事では、上記3つのフィルターを試した結果をまとめます。
- 環境
- ansible 7.0.0
- anislb-core 2.14.0
- ansible.utils コレクション 2.7.0
1. 特定のキー配下のみ残す ansible.utils.keep_keys
フィルター
ansible.utils.keep_keys
フィルターは、target
で指定したキー配下のみを残す加工をします。
元データの定義と ansible.utils.keep_keys
フィルターをかけた結果を出力するサンプルの Playbook は以下のとおりです。
--- - name: Test manipulate keys hosts: localhost gather_facts: false connection: local vars: data: # 共通 - name: oikawa kind: fish description: kawaii - name: kingyo kind: fish description: totemo kawaii tasks: - name: Test keep_keys ansible.builtin.debug: msg: "{{ data | ansible.utils.keep_keys(target=target) }}" vars: target: - name # これらの - kind # キー配下のみを残す
SQL 文でいう、SELECT name, kind FROM data
のようなイメージでしょうか。
出力結果は以下のとおりです。
TASK [Test keep_keys] ********************************************************* ok: [localhost] => { "msg": [ { "kind": "fish", "name": "oikawa" }, { "kind": "fish", "name": "kingyo" } ] }
残すキーとして name
、 kind
として指定しているので、該当しない description
キー配下がなくなりました。
2. 特定のキー配下を取り除く ansible.utils.remove_keys
フィルター
ansible.utils.remove_keys
フィルターは、target
で指定したキー配下を取り除きます。
ちょうど、先程の ansible.utils.keep_keys
フィルターと逆です。
元データの定義(先ほどと同じ)と ansible.utils.keep_keys
フィルターをかけた結果を出力するサンプルの Playbook は以下のとおりです。
--- - name: Test manipulate keys hosts: localhost gather_facts: false connection: local vars: data: # 共通 - name: oikawa kind: fish description: kawaii - name: kingyo kind: fish description: totemo kawaii tasks: - name: Test remove_keys ansible.builtin.debug: msg: "{{ data | ansible.utils.remove_keys(target=target) }}" vars: target: - kind # これらの - description # キー配下を削除する
SQL 文でいう、SELECT * EXCEPT (kind, description) FROM data
のようなイメージでしょうか。
出力結果は以下のとおりです。
TASK [Test remove_keys] ******************************************************* ok: [localhost] => { "msg": [ { "name": "oikawa" }, { "name": "kingyo" } ] }
取り除くキーとして kind
、 description
として指定しているので、name
キー配下だけが残りました。
3. キーの名前を変更する ansible.utils.replace_keys
フィルター
先述の2つのフィルターとはやや性質が異なります。
ansible.utils.replace_keys
フィルターは、target
で指定したキーの名前を変更します。具体的には target
内の before
で指定したキーの名前を after
で指定した名前に変更します。
元データの定義(先ほどと同じ)と ansible.utils.replace_keys
フィルターをかけた結果を出力するサンプルの Playbook は以下のとおりです。
--- - name: Test manipulate keys hosts: localhost gather_facts: false connection: local vars: data: # 共通 - name: oikawa kind: fish description: kawaii - name: kingyo kind: fish description: totemo kawaii tasks: - name: Test replace_keys ansible.builtin.debug: msg: "{{ data | ansible.utils.replace_keys(target=target) }}" vars: target: - before: name # この名前のキーを after: name_changed # この名前に変更する - before: kind # この名前のキーを after: kind_changed # この名前に変更する
SQL 文でいう、SELECT * EXCEPT(xx,xx) FROM data
のようなイメージでしょうか。
出力結果は以下のとおりです。取り除くキーとして kind
、 description
として指定しているので、name
キー配下だけが残りました。
TASK [Test replace_keys] ****************************************************** ok: [localhost] => { "msg": [ { "description": "kawaii", "kind_changed": "fish", "name_changed": "oikawa" }, { "description": "totemo kawaii", "kind_changed": "fish", "name_changed": "kingyo" } ] }
name
が name_changed
に、kind
が kind_changed
に変更されました。特に指定していない description
はそのままです。
補足
ここまではかんたんなサンプルを掲載しましたが、いくつか補足します。
キーを指定する際のマッチ条件
各フィルターのドキュメントにも記載されていますが、キーを指定する際のマッチ条件を指定する matching_parameter
オプションがあります。
デフォルトは完全一致(大文字小文字も区別)です。他に選択できる値は以下のとおりです。
値 | 意味 |
---|---|
starts_with |
前方一致 |
ends_with |
後方一致 |
regex |
正規表現 |
例えば、matching_parameter: regex
にして、^desc.+tion$
という正規表現を指定した場合。
- name: Test keep_keys2 ansible.builtin.debug: msg: "{{ data | ansible.utils.keep_keys(target=target,matching_parameter='regex') }}" vars: target: - ^desc.+tion$
以下のように、^desc.+tion$
にマッチするキーを description
が残ります。
TASK [Test keep_keys] ********************************************************************** ok: [localhost] => { "msg": [ { "description": "kawaii" }, { "description": "totemo kawaii" } ] }
単純なディクショナリでもできた
公式ドキュメントの Examples は、いずれも元データがディクショナリのリスト(テーブルデータのイメージ)だったので、本記事でも同類のデータを扱いました。
試した限り、ただのディクショナリでも意図通りにフィルターできました。
--- - name: Test manipulate keys hosts: localhost gather_facts: false connection: local vars: data: # 単純なディクショナリ name: oikawa kind: fish description: kawaii tasks: - name: Test keep_keys ansible.builtin.debug: msg: "{{ data | ansible.utils.keep_keys(target=target) }}" vars: target: - name # これらの - kind # キー配下のみを残す
結果は以下のとおりです。
TASK [Test keep_keys] *********************************************************** ok: [localhost] => { "msg": { "kind": "fish", "name": "oikawa" } }
元データはそのまま
フィルターはあくまでフィルターなので、元のデータはそのままです。
今回はフィルター結果を debug
モジュールで表示するだけでしたが、変数に格納したい場合は ansible.builtin.set_fact
モジュールを使うなどします。
キーではなく中身を操作したい場合
ここまで取り上げたフィルターはあくまで、キーに対する操作です。
もし中身を操作したい場合は、公式ドキュメントのhttps://docs.ansible.com/ansible/latest/playbook_guide/playbooks_filters.html#combining-and-selecting-data が参考になります。
おわりに
ansible.utils
コレクションの、ディクショナリのキーをもとにして、残す、除外する、置き換えるフィルターをためしてみました。
ansible.utils
コレクションは、私の好きなコレクションの一つです。変数こねくり回す系の処理で、こんなことできないかな?と思ったら、Manipulating data
や Using filters to manipulate data
のページのほか、ansible.utils
コレクションを見るようにしています。