24
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AnsibleAdvent Calendar 2016

Day 12

[小ネタ] Ansibleでnestedなdict変数を正しくdeep mergeする方法2つ

Last updated at Posted at 2016-06-11

Ansibleで変数のmergeをするには?

Ansibleでは変数のFilterにJinja2を使用します。

時として辞書を含む変数を綺麗にdeep mergeしたい時ってないですか?
今日はAnsibleで変数のdeep mergeを実現させる方法を2つ紹介します。
何が違うかというと、Ansible 2.0以上 とそれ以下のバージョンでそれぞれ方法が変わってきます。

Ansible version ">=2.0" の場合

Ansible 2.0から新たに combine filter が実装されました。
http://docs.ansible.com/ansible/playbooks_filters.html#combining-hashes-dictionaries

test.yml
- hosts: localhost
  gather_facts: no
  vars:
    dict:
      foo:
        bar: 1
    dict2:
      foo:
        baz: 2
      qux: 2
    # combining hashes/dictionaries (new in version 2.0)
    dict_combine: "{{ dict | combine(dict2, recursive=True) }}"
  tasks:
    - debug:
        var: dict_combine

上記を実行すると下記の結果が得られると思います。

$ ansible-playbook -i localhost, test.yml

PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "dict_combine": {
        "foo": {
            "bar": 1,
            "baz": 2
        },
        "qux": 2
    }
}

非常に便利ですね。
きちんとdict->fooの中身がmergeされています。
ちなみに「recursive」を省略すると単なる update mergeになります。

recursive=false
$ ansible-playbook -i localhost, test.yml

PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "dict_combine": {
        "foo": {
            "baz": 2
        },
        "qux": 2
    }
}

ちなみにrecursiveを省略したcombine相当の機能はAnsible 2.0以前のバージョンでもupdate()を使うことで下記のように実現することができました。

test.yml
- hosts: localhost
  gather_facts: no
  vars:
    dict:
      foo:
        bar: 1
    dict2:
      foo:
        baz: 2
      qux: 2
    # **[Note]**
    # jinja2 'do' tag need expression-statement extension
    # please set below to [default] section in ansible.cfg
    # jinja2_extensions=jinja2.ext.do
    dict_update: |
      {% do dict.update(dict2) %}
      {{ dict }}
  tasks:
    - debug:
        var: dict_update

Ansible version "<2.0" の場合

色々な理由でAnsible 1.X台を使わざるを得ない場合、combineが使えずに多少不便です。
無論2.0で実装されたcombineのソース部分を取り込めばよいのですが、せっかくなので独自にFilter pluginを作ってみたいと思います。
http://docs.ansible.com/ansible/developing_plugins.html#filter-plugins

pluginを使うにはansible.cfgにパス(filter_plugins=)を通すか、ansible本体のプラグインディレクトリに直接カスタムファイルを置くことでansible実行時に読み込んでくれます。

/path-to/ansible/filter_plugins/dict_merge.py
from copy import deepcopy


def dict_merge(a, b):
    if not isinstance(b, dict):
        return b
    result = deepcopy(a)
    for k, v in b.iteritems():
        if k in result and isinstance(result[k], dict):
                result[k] = dict_merge(result[k], v)
        else:
            result[k] = deepcopy(v)
    return result


class FilterModule(object):
    def filters(self):
        return {'dict_merge': dict_merge}

これでプラグインの作成は終了です。思ったより簡単ですね。
ではテストしてみます。

test.yml
- hosts: localhost
  gather_facts: no
  vars:
    dict:
      foo:
        bar: 1
    dict2:
      foo:
        baz: 2
      qux: 2
    # custom filter plugin
    dict_merged: "{{ dict | dict_merge(dict2) }}"
  tasks:
    - debug:
        var: dict_merged

実行結果は下記の通りです。

$ ansible-playbook -i localhost, test.yml

PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "dict_merged": {
        "foo": {
            "bar": 1,
            "baz": 2
        },
        "qux": 2
    }
}

combine同様の結果を得ることが出来ました。
もちろん更に深い階層のdictもmergeしてくれます。
1.X 系でサクッとdeep mergeを使いたい場合はこういう手もあるので、検討されてみてはいかがでしょうか。

その他

Filterプラグインの他にもAnsibleには独自に作ることのできるプラグインがいくつかあります。
http://docs.ansible.com/ansible/developing_plugins.html

Lookupやcallbacksプラグインは比較的情報も多く色んな事をしている方の情報がありますね。

Let's enjoy your Ansible life!

24
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?