Ansible - Как сохранить новые ключи в словаре при использовании модуля set_fact с with_items?

Я хочу добавить ключи к словарю при использовании set_fact с with_items. Это небольшой POC, который поможет мне выполнить некоторые другие работы. Я попытался обобщить POC, чтобы удалить из него все несущественные детали.

Когда я выполняю следующий код, он показывает словарь с одним ключом, который соответствует последнему элементу with_items. Кажется, что он воссоздает новый словарь или может переопределять существующий словарь для каждого элемента in_items. Я хочу один словарь со всеми ключами.

код:

---
- hosts: localhost
  connection: local
  vars:
      some_value: 12345
      dict: {}
  tasks:
     - set_fact: {
          dict: "{
             {{ item }}: {{ some_value }}
             }"
            }
       with_items:
          - 1
          - 2
          - 3
     - debug: msg="{{ dict }}"

Ответ 1

Используйте плагин фильтра.

Во-первых, создайте новый файл в вашей доступной директории под названием filter_plugins/makedict.py.

Теперь создайте новую функцию с именем "makedict" (или что угодно), которая принимает значение и список и возвращает новый словарь, ключи которого являются элементами списка, а значение всегда одинаково.

class FilterModule(object):
     def filters(self):
         return { 'makedict': lambda _val, _list: { k: _val for k in _list }  }

Теперь вы можете использовать новый фильтр в Playbook для достижения желаемого результата:

- hosts: 127.0.0.1
  connection: local
  vars:
      my_value: 12345
      my_keys: [1, 2, 3]
  tasks:
    - set_fact: my_dict="{{ my_value | makedict(my_keys) }}"
    - debug: msg="{{ item.key }}={{ item.value }}"
      with_dict: "{{my_dict}}"

Вы можете настроить местоположение плагина фильтра, используя опцию filter_plugins в ansible.cfg.

Ответ 2

Это также можно сделать, не прибегая к плагинам, проверенным в Ansible 2.2.

---
- hosts: localhost
  connection: local
  vars:
    some_value: 12345
    dict: {}
  tasks:
  - set_fact:
      dict: "{{ dict | combine( { item: some_value } ) }}"
    with_items:
      - 1
      - 2
      - 3
  - debug: msg="{{ dict }}"

В качестве альтернативы это можно записать без сложного однострочного файла с включенным файлом.

  tasks:
  - include: append_dict.yml
    with_items: [1, 2, 3]

append_dict.yml:

- name: "Append dict: define helper variable"
  set_fact:
    _append_dict: "{ '{{ item }}': {{ some_value }} }"

- name: "Append dict: execute append"
  set_fact:
    dict: "{{ dict | combine( _append_dict ) }}"

Вывод:

TASK [debug]
*******************************************************************
ok: [localhost] => {
    "msg": {
        "1": "12345",
        "2": "12345",
        "3": "12345"
    }
}

Одиночные кавычки ' вокруг {{ some_value }} необходимы для хранения строковых значений явно.

Этот синтаксис также можно использовать для добавления из dict по элементам с помощью with_dict, ссылаясь на item.key и item.value.

Манипуляции, такие как добавление pre- и postfixes или хешей, могут выполняться на одном и том же этапе, например

    set_fact:
      dict: "{{ dict | combine( { item.key + key_postfix: item.value + '_' +  item.value | hash('md5') } ) }}"

Ответ 3

кажется, это не работает больше на ANSIB 2.5

---
- hosts: localhost
  connection: local
  vars:
    some_value: 12345
    dict: {}
  tasks:
  - set_fact:
      dict: "{{ dict | combine( { item: some_value } ) }}"
    with_items:
      - 1
      - 2
      - 3
  - debug: msg="{{ dict }}"

возвращает только последнее значение {"dict":{"3": "some value"}}

Я предлагаю вам сделать это:

- set_fact:
    __dict: |
        {% for item in  [1,2,3] %}
        {{item}}: "value"
        {% endfor %}

- set_fact:
    final_dict: "{{__dict|from_yaml}}"

- debug: 
  var: final_dict