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

时间:2016-02-24 15:00:01

标签: ansible

在Ansible中,我使用const mongodb = require('mongodb'); const MongoClient = mongodb.MongoClient; function randomIndex() { return Math.ceil(Math.random() * 2100); } MongoClient.connect('mongodb://localhost/somedb').then(db => { let collection = db.collection('somecollection'); db.on('close', err => console.log('close', err)); db.on('reconnect', () => console.trace('reconnect')); setInterval(() => { collection.find({ index: randomIndex() }).limit(1).next() .then(console.log) .catch(console.log); }, 1000); }); 将任务的结果保存在变量register中。省略我不需要的东西,它有这样的结构:

people

我想使用后续{ "results": [ { "item": { "name": "Bob" }, "stdout": "male" }, { "item": { "name": "Thelma" }, "stdout": "female" } ] } 任务生成一个带有这样字典的新变量:

set_fact

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

2 个答案:

答案 0 :(得分:83)

我想我最终到了那里。

任务是这样的:

- 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新的词典, genders数组,最终结果如下:

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

答案 1 :(得分:11)

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

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

- 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中的>-Jinja中的-%},否则您将得到一个错误如:

    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 ...
    

希望这可以提供帮助。