Я пытаюсь достичь следующего. Предположим, у нас есть сценарий аналогичный к следующему:
---
- 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 %}
]