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

Используете Ansible ini_file с вложенным dict?

Я пытаюсь достичь следующего. Предположим, у нас есть сценарий аналогичный к следующему:

---

- hosts: local
  tasks:
    - ini_file:
      path: test.ini
      create: yes
      section: "{{???}}"
      option: "{{???}}"
      value: "{{???}}"
    loop: "{{inidict ...???}}"
  vars:
    inidict:
      section1:
        option1: value1
        option2: value2
      section2:
        option1: value1
        option2: value2

Приведенный выше сценарий недействителен из-за строк с ??? в них.

Теперь моя цель - сопоставить этот dict с разделами / параметрами / значениями внутри файла INI и получить следующее содержимое INI:

[section1]
option1 = value1
option2 = value2

[section2]
option1 = value1
option2 = value2

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

Есть ли более элегантный или «доступный» (из-за отсутствия термина Ansible, соответствующего pythonic) способ добиться этого, чем путем обертывания одного уровня цикла в роли и повторения имен разделов, например, include_role, при передаче имени раздела и списка параметров / значений в качестве переменной?

Я полагаю, что одной из альтернатив могло бы быть «сгладить» словарь таким образом, чтобы получить что-то, что составляет (YAML):

  vars:
    inicontents:
      - section: section1
        key: option1
        value: value1
      - section: section1
        key: option2
        value: value2
      - section: section2
        key: option1
        value: value1
      - section: section2
        key: option2
        value: value2

Я просто хотел бы сохранить его СУХИМ и избежать дублирования имени раздела, когда очевидно, что dict будет идеально отображаться на «модель данных» INI-файла.

Я не знаю другого способа сделать это с помощью ansible по умолчанию, кроме вашей «плоской» версии. К счастью, у вас есть лучшее из обоих миров и вы можете написать свой собственный плагин фильтра для сглаживания слова за вас. Просто добавьте что-нибудь вроде следующего в filter_plugins каталог в каталоге вашего проекта или в ~/.ansible/plugins/filter_plugins и назовите это как-нибудь вроде filters.py:

class FilterModule(object):
    def filters(self):
        return { 'ini_dict_flatten': self.ini_dict_flatten }

    def ini_dict_flatten(self, arg):
        ret = []
        for section, subdict in arg.items():
            for key, value in subdict.items():
                ret.append(dict(section=section,
                                key=key,
                                value=value))
        return ret

После этого вы можете использовать новый фильтр в своем проекте следующим образом:

- hosts: localhost
  tasks:
    - ini_file:
        path: /tmp/test.ini
        create: yes
        section: "{{ item.section }}"
        option: "{{ item.key }}"
        value: "{{ item.value }}"
      loop: "{{ inidict | ini_dict_flatten }}"
  vars:
    inidict:
      section1:
        option1: value1
        option2: value2
      section2:
        option1: value1
        option2: value2

Иногда это помогает, если вы знаете Python, так как таким образом вы можете писать свои собственные плагины для ansible.

РЕДАКТИРОВАТЬ: Я также включу гораздо менее читаемое и не рекомендуемое решение. Поскольку loop: работает со строками в формате json, а jinja2 применяется к строкам в анзибле до того, как они будут обработаны, вы можете использовать логику jinja, чтобы создать для вас «плоский интерфейс» прямо в playbook. Это решение намного уродливее и менее пригодно для повторного использования, чем написание собственного фильтра, но технически все еще работает.

- hosts: localhost
  vars:
    inidict:
      section1:
        option1: value1
        option2: value2
      section2:
        option1: value1
        option2: value2
  tasks:
    - ini_file:
        path: /tmp/test.ini
        create: yes
        section: "{{ item.section }}"
        option: "{{ item.key }}"
        value: "{{ item.value }}"
      loop: >-
        [
        {% for section, subdict in inidict.items() %}
          {% for key, value in subdict.items() %}
            {'section': '{{ section }}',
             'key': '{{ key }}',
             'value': '{{ value }}'}
            {% if not loop.last %}
              ,
            {% endif %}
          {% endfor %}
          {% if not loop.last %}
            ,
          {% endif %}
        {% endfor %}
        ]