てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] ディクショナリのキーを元に、残す、除外する、置き換えるフィルターをためしてみた

この記事は、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"
        }
    ]
}

残すキーとして namekind として指定しているので、該当しない 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"
        }
    ]
}

取り除くキーとして kinddescription として指定しているので、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 のようなイメージでしょうか。

出力結果は以下のとおりです。取り除くキーとして kinddescription として指定しているので、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"
        }
    ]
}

namename_changed に、kindkind_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 dataUsing filters to manipulate data のページのほか、ansible.utils コレクションを見るようにしています。