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

Phi*_*ord 57 ansible

在Ansible中,我曾经register在变量中保存任务的结果people.省略我不需要的东西,它有这样的结构:

{
    "results": [
        {
            "item": {
                "name": "Bob"
            },
            "stdout": "male"
        },
        {
            "item": {
                "name": "Thelma"
            },
            "stdout": "female"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

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

{
    "Bob": "male",
    "Thelma": "female"
}
Run Code Online (Sandbox Code Playgroud)

我想这可能是可能的,但到目前为止我还没有运气.

Phi*_*ord 103

我想我最终到了那里.

任务是这样的:

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

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

{
    "Bob": "male",
    "Thelma": "female"
}
Run Code Online (Sandbox Code Playgroud)

它假设键(name在这种情况下)将是唯一的.


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

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

这使现有列表与包含单个字典的列表保持组合.我们最终得到一个genders像这样的数组:

[
    {'name': 'Bob', 'gender': 'male'},
    {'name': 'Thelma', 'gender': 'female'}
]
Run Code Online (Sandbox Code Playgroud)

  • 如果使用_default_ filter,则可以避免声明空字典.`genders:"{{genders | default({})| combine({item.item.name:item.stdout})}}` (9认同)
  • 谢谢@smicyk - 我姗姗来迟地在示例中添加了您的方便建议. (2认同)
  • 第二个示例应该是:性别:`“ {{性别|默认([])+ [{'名称':item.item.name,'性别':item.stdout}]}}”“`,而是使用默认数组默认对象。 (2认同)

buf*_*ufh 12

谢谢菲尔的解决方案; 如果有人遇到与我相同的情况,这里有一个(更复杂的)变体:

---
# 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
Run Code Online (Sandbox Code Playgroud)

这将给出以下输出(为示例剥离):

"postconf_d": {
    "alias_database": "hash:/etc/aliases", 
    "alias_maps": "hash:/etc/aliases, nis:mail.aliases",
    "allow_min_user": "no", 
    "allow_percent_hack": "yes"
}
Run Code Online (Sandbox Code Playgroud)

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

- 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
...
Run Code Online (Sandbox Code Playgroud)
"postconf_d": {
    "alias_database": "hash:/etc/aliases", 
    "alias_maps": [
        "hash:/etc/aliases", 
        "nis:mail.aliases"
    ], 
    "allow_min_user": "no", 
    "allow_percent_hack": "yes"
}
Run Code Online (Sandbox Code Playgroud)

有几点需要注意:

  • 在这种情况下,它需要"微调"一切(使用>-YAML-%}神社),否则你会得到这样的错误:

    FAILED! => {"failed": true, "msg": "|combine expects dictionaries, got u\"  {u'...
    
    Run Code Online (Sandbox Code Playgroud)
  • 显然这{% if ..远非防弹

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

    "|combine expects dictionaries, got u\"{u'...': <generator object do_map at ...
    
    Run Code Online (Sandbox Code Playgroud)

希望这可以提供帮助.

  • 这是一个很棒的提示!只有一个注释,Ansible 2.4.3至少除了最后一行之外的每一行都需要`{% - ... - %}`并且在开括号和右括号上都有短划线,否则dict将被解释为一个字符串 (2认同)