Назад | Перейти на главную страницу

Добавление к спискам или добавление ключей к словарям в Ansible

(Относится к Обратные вызовы или хуки, а также многоразовые серии задач в ролях Ansible):

Есть ли лучший способ добавить в список или добавить ключ в словарь в Ansible, чем (ab), используя выражение шаблона jina2?

Я знаю, что вы можете сделать что-то вроде:

- name: this is a hack
  shell: echo "{% originalvar.append('x') %}New value of originalvar is {{originalvar}}"

но разве для этого не существует мета-задачи или помощника?

Он кажется хрупким, недокументированным и основан на множестве предположений о том, как переменные работают в Ansible.

Мой вариант использования - несколько ролей (расширения сервера базы данных), каждая из которых должна предоставить некоторую конфигурацию базовой роли (серверу базы данных). Это не так просто, как добавить строку в файл конфигурации сервера db; каждое изменение применяется к та же линия, например расширения bdr и pg_stat_statements оба должны появиться в целевой строке:

shared_preload_libaries = 'bdr, pg_stat_statements'

Можно ли с помощью Ansible обработать файл конфигурации несколько раз (один раз для каждого расширения) с помощью регулярного выражения, которое извлекает текущее значение, анализирует его и затем перезаписывает? Если да, то как сделать этот идемпотент в нескольких прогонах?

Что, если конфигурацию сложнее проанализировать, и это не так просто, как добавить другое значение, разделенное запятыми? Подумайте о файлах конфигурации XML.

Начиная с Ansible v2.x вы можете делать это:

# use case I: appending to LIST variable:

      - name: my appender
        set_fact:
          my_list_var: '{{my_list_myvar + new_items_list}}'

# use case II: appending to LIST variable one by one:

      - name: my appender
        set_fact:
          my_list_var: '{{my_list_var + [item]}}'
        with_items: '{{my_new_items|list}}'

# use case III: appending more keys DICT variable in a "batch":

      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine(my_new_keys_in_a_dict)}}'

# use case IV: appending keys DICT variable one by one from tuples
      - name: setup list of tuples (for 2.4.x and up
        set_fact:
          lot: >
            [('key1', 'value1',), ('key2', 'value2',), ..., ('keyN', 'valueN',)],
      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine({item[0]: item[1]})}}'
        with_items: '{{lot}}'
# use case V: appending keys DICT variable one by one from list of dicts (thanks to @ssc)

  - name: add new key / value pairs to dict
    set_fact:
      my_dict_var: "{{ my_dict_var | combine({item.key: item.value}) }}"
    with_items:
    - { key: 'key01', value: 'value 01' }
    - { key: 'key02', value: 'value 03' }
    - { key: 'key03', value: 'value 04' }

все вышесказанное задокументировано в: http://docs.ansible.com/ansible/playbooks_filters.html

Вы можете объединить два списка в переменной с помощью +. Скажите, что у вас есть group_vars файл с таким содержанием:

---
# group_vars/all
pgsql_extensions:
  - ext1
  - ext2
  - ext3

И он используется в шаблоне pgsql.conf.j2 лайк:

# {{ ansible_managed }}
pgsql_extensions={% for item in pgsql_extensions %}{{ item }}, {% endfor %}

Затем вы можете добавить расширения к серверам тестовой базы данных следующим образом:

---
# group_vars/testing_db
append_exts:
  - ext4
  - ext5
pgsql_extensions: "{{ pgsql_extensions + append_exts }}"

Когда роль запускается на любом из тестовых серверов, будут добавлены дополнительные расширения.

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

вам нужно разбить петлю на 2

--- 
- hosts: localhost
  tasks: 
    - include_vars: stacks
    - set_facts: roles={{stacks.Roles | split(' ')}}
    - include: addhost.yml
      with_items: "{{roles}}"

и addhost.yml

- set_facts: groupname={{item}}
- set_facts: ips={{stacks[item]|split(' ')}}
- local_action: add_host hostname={{item}} groupname={{groupname}}
  with_items: {{ips}}

Почти все ответы здесь требуют изменения задач, но мне нужно было динамически объединять словари в определении vars, а не во время выполнения.

Например. Я хочу определить несколько общих варов в all group_vars а затем я хочу расширить их на другие group или host_vars. Очень полезно при работе с ролями.

Если вы попытаетесь использовать combine или union фильтры, перезаписывающие исходную переменную в файлах var, вы закончите бесконечный цикл во время создания шаблона, поэтому я создал этот обходной путь (это не решение).

Вы можете определить несколько переменных на основе некоторого шаблона имени, а затем автоматически загрузить их в роли.

group_vars/all.yml

dictionary_of_bla:
  - name: blabla
    value1 : blabla
    value2 : blabla

group_vars/group1.yml

dictionary_of_bla_group1:
  - name: blabla2
    value1 : blabla2
    value2 : blabla2

фрагмент кода роли

tasks:
  - name: Run for all dictionary_of_bla.* variations
    include_tasks: do_some_stuff.yml
    with_items: "{{ lookup('varnames','dictionary_of_bla.*').split(',') }}"
    loop_control:
      loop_var: _dictionary_of_bla

do_some_stuff.yml

- name: do magic
  magic:
    trick_name: item.name
    trick_value1: item.value1
    trick_value2: item.value2
  with_items: "{{ vars[_dictionary_of_bla] }}"

Это всего лишь фрагмент, но вы должны понять, как он работает. примечание: поиск ('varnames', '') доступен начиная с ansible 2.8

Я думаю, можно было бы объединить все переменные dictionary_of_bla.* в один словарь во время выполнения, используя тот же поиск.

Преимущество этого подхода в том, что вам не нужно задавать точные списки имен переменных, а только шаблон и пользователь могут устанавливать их динамически.

Точно сказать не могу когда они добавили это, но, по крайней мере, для словарей / хэшей (НЕ списков / массивов) вы можете установить переменную hash_behaviour, вот так: hash_behaviour = merge в твоем ansible.cfg.

Мне потребовалось несколько часов, чтобы случайно наткнуться на этот параметр: S

Ansible это система автоматизации, и, что касается управления файлами конфигурации, она не сильно отличается от apt. Причина, по которой все больше и больше программ предлагает функцию чтения фрагментов конфигурации из conf.d Каталог предназначен для того, чтобы позволить таким системам автоматизации иметь различные пакеты / роли, добавляющие конфигурацию в программное обеспечение. Я считаю, что это не философия Ansible делать то, что задумано, но вместо этого использовать conf.d уловка. Если конфигурируемое программное обеспечение не предлагает эту функцию, у вас могут быть проблемы.

Поскольку вы упомянули файлы конфигурации XML, я пользуюсь возможностью, чтобы немного поплакать. Есть причина для традиции Unix использовать файлы конфигурации в виде простого текста. Двоичные файлы конфигурации плохо поддаются автоматизации системы, поэтому любой вид двоичного формата доставит вам проблемы и, вероятно, потребует от вас создания программы для обработки конфигурации. (Если кто-то думает, что XML - это простой текстовый формат, ему следует проверить свой мозг.)

Теперь о вашем конкретном PostgreSQL проблема. PostgreSQL поддерживает conf.d уловка. Сначала я бы проверил, shared_preload_libraries можно указывать несколько раз. Я не нашел в документации намека на то, что это возможно, но все же попробовал бы. Если его нельзя указать несколько раз, я объясню свою проблему PostgreSQL ребята, если у них есть идеи; это PostgreSQL проблема, а не Ansible вопрос. Если решения нет и я действительно не могу объединить разные роли в одну, я бы реализовал систему для компиляции конфигурации на управляемом хосте. В этом случае я бы, вероятно, создал сценарий /usr/local/sbin/update_postgresql_config который скомпилирует /etc/postgresql/postgresql.conf.jinja в /etc/postgresql/9.x/main/postgresql.conf. Скрипт будет читать общие библиотеки предварительной загрузки из /etc/postgresql/shared_preload_libraries.txt, по одной библиотеке на строку, и предоставьте их jinja.

Это не редкость для систем автоматизации. Примером является Debian exim4 пакет.