使用Ansible set_fact从寄存器结果创建字典


69

在Ansible中,我曾经register将任务的结果保存在变量中people。省略了我不需要的东西,它具有以下结构:

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

我想使用后续set_fact任务来生成一个具有这样的字典的新变量:

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

我想这可能是可行的,但到目前为止我还没有走运。


请注意,从ansible v2.2开始,with_items需要显式的jinja2包装。因此,第一个示例将是:<pre>-名称:填充性别set_fact:性别:“ {{性别|默认({})| Combine({item.item.name:item.stdout}}}}}” with_items:“ {{people.results}}“ </ pre>
sarraz

Answers:


127

我想我最终到了那里。

任务是这样的:

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

它循环通过每个类型的字典(的item中)people.results阵列,每个时间创建像一个新的字典{Bob: "male"},并且combine()s表示中新的dictgenders阵列,其中相同的结束:

{
    "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 }}"

这样可以将现有列表与包含单个字典的列表结合在一起。我们最终得到一个genders像这样的数组:

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

11
如果使用默认过滤器,则可以避免声明空字典。genders: "{{ genders | default({}) | combine( {item.item.name: item.stdout}) }}
smicyk,2016年

2
谢谢@smicyk-我迟来将您的方便建议添加到示例中。
菲尔·吉福德

2
第二个示例应该是:sexs:,"{{ genders|default([]) + [ {'name': item.item.name, 'gender': item.stdout} ] }}"使用默认数组而不是默认对象。
Olivier Lecrivain '16

谢谢@OlivierLecrivain-我已修复了示例。
菲尔·吉福德

无论如何要在Ansible 2.0版之前执行此操作?
兰迪

16

谢谢Phil的解决方案;万一有人遇到与我相同的情况,这是一个(更复杂的)变体:

---
# 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'...
    
  • 显然,{% if ..这远非防弹

  • 在后缀的情况下,本val.split(',')|map('trim')|list可以简化为val.split(', '),但是我想指出一个事实,|list否则您将需要得到以下错误:

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

希望这会有所帮助。


1
备受-%}注目的坦克!我已经为此战斗了好几个星期。
ceving

3
这是一个很好的提示!请注意,Ansible 2.4.3至少每行,最后一行除外,最后一行都需要{%- ... -%}在破折号上加上破折号,否则该字典将被解释为字符串
Michael

1
谢谢您的提示,找出这些>--%}奇数将非常耗时。我欠你。谢谢。
vjt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.