[小提示] 有两种方法可以正确地在Ansible中深度合并嵌套字典变量
在Ansible中如何合并变量?
在Ansible中,可以使用Jinja2作为变量过滤器。
有时候您是否希望将包含字典的变量进行漂亮的深度合并呢?
今天我将介绍两种在Ansible中实现变量深度合并的方法。它们之间的区别是,对于Ansible 2.0以上和更低版本,方法各有不同。
如果Ansible版本大于等于2.0的情况下
Ansible 2.0 中引入了 combine filter 功能。
请参考:http://docs.ansible.com/ansible/playbooks_filters.html#combining-hashes-dictionaries
- 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的内容已经完整地合并了。
顺便说一下,如果省略了“recursive”,则只会进行简单的更新合并。
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"dict_combine": {
"foo": {
"baz": 2
},
"qux": 2
}
}
顺便提一下,在Ansible 2.0以前的版本中,可以通过使用update()来实现类似于省略了递归的combine功能,如下所示。
- 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版本”<2.0″的情况
如果由于各种原因不得不使用Ansible 1.X版本,那么无法使用combine会有些不便。
当然,可以通过引入2.0版本中实现的combine源代码部分来解决,但我想趁机自己尝试开发一个独立的Filter插件。
http://docs.ansible.com/ansible/developing_plugins.html#filter-plugins
要使用插件,需要通過在ansible.cfg文件中設定路徑(filter_plugins=),或者直接將自定義文件放置在ansible本體的插件目錄中,以便在ansible執行時將其加載進來。
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}
那么,插件的创建就完成了。比我想象的要简单呢。接下来,我来进行测试。
- 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函数,我们可以获得类似的结果。
当然,它还可以帮助我们合并更深层次的字典。
如果您想在X系统中快速使用深度合并,不妨考虑一下这种方法。
其他
除了Filter插件外,Ansible还有一些可以自定义的插件。请参考http://docs.ansible.com/ansible/developing_plugins.html。
查找和回调插件是相对而言非常丰富且具有多种功能的插件。
让我们享受你的Ansible生活吧!