使用 Ansibleset_fact 根据寄存器结果创建字典

在 Ansible,我使用 register将任务的结果保存在变量 people中。除去我不需要的东西,它有这样的结构:

{
"results": [
{
"item": {
"name": "Bob"
},
"stdout": "male"
},
{
"item": {
"name": "Thelma"
},
"stdout": "female"
}
]
}

我想使用一个后续的 set_fact任务来生成一个新的变量,其字典如下:

{
"Bob": "male",
"Thelma": "female"
}

我想这也许是可能的,但我一直在兜圈子,到目前为止还没有什么好运气。

144148 次浏览

我想我最终还是到了那里。

任务是这样的:

- name: Populate genders
set_fact:
genders: "\{\{ genders|default({}) | combine( {item.item.name: item.stdout} ) }}"
with_items: "\{\{ people.results }}"

它循环遍历 people.results数组中的每个 dicts (item) ,每次创建一个新的 dicts,比如 {Bob: "male"},以及 genders数组中的 combine(),结果如下:

{
"Bob": "male",
"Thelma": "female"
}

它假设键(在本例中为 name)是唯一的。


然后我意识到我实际上需要一个字典列表,因为使用 with_items循环似乎更容易:

- name: Populate genders
set_fact:
genders: "\{\{ genders|default([]) + [ {'name': item.item.name, 'gender': item.stdout} ] }}"
with_items: "\{\{ people.results }}"

这将保持现有列表与包含单个 dict 的列表的组合。我们得到的 genders数组如下:

[
{'name': 'Bob', 'gender': 'male'},
{'name': 'Thelma', 'gender': 'female'}
]

谢谢你的解决方案,如果有人和我处于同样的情况,这里有一个(更复杂的)变体:

---
# this is just to avoid a call to |default on each iteration
- set_fact:
postconf_d: {}


- name: 'get postfix default configuration'
command: 'postconf -d'
register: command


# the answer of the command give a list of lines such as:
# "key = value" or "key =" when the value is null
- name: 'set postfix default configuration as fact'
set_fact:
postconf_d: >
\{\{
postconf_d |
combine(
dict([ item.partition('=')[::2]|map('trim') ])
)
with_items: command.stdout_lines

这将产生以下输出(为示例进行了剥离) :

"postconf_d": {
"alias_database": "hash:/etc/aliases",
"alias_maps": "hash:/etc/aliases, nis:mail.aliases",
"allow_min_user": "no",
"allow_percent_hack": "yes"
}

更进一步,解析“值”中的列表:

- name: 'set postfix default configuration as fact'
set_fact:
postconf_d: >-
{% set key, val = item.partition('=')[::2]|map('trim') -%}
{% if ',' in val -%}
{% set val = val.split(',')|map('trim')|list -%}
{% endif -%}
\{\{ postfix_default_main_cf | combine({key: val}) }}
with_items: command.stdout_lines
...
"postconf_d": {
"alias_database": "hash:/etc/aliases",
"alias_maps": [
"hash:/etc/aliases",
"nis:mail.aliases"
],
"allow_min_user": "no",
"allow_percent_hack": "yes"
}

有几点需要注意:

  • 在这种情况下,需要“修剪”所有内容(在 YAML中使用 >-,在 金贾中使用 -%}) ,否则您将得到如下错误:

    FAILED! => {"failed": true, "msg": "|combine expects dictionaries, got u\"  {u'...
    
  • obviously the {% if .. is far from bullet-proof

  • in the postfix case, val.split(',')|map('trim')|list could have been simplified to val.split(', '), but I wanted to point out the fact you will need to |list otherwise you'll get an error like:

    "|combine expects dictionaries, got u\"{u'...': <generator object do_map at ...
    

Hope this can help.