使用带有嵌套字典的 Ansible ini_file?

使用带有嵌套字典的 Ansible ini_file?

我想要实现的目标如下。假设我们有一本剧本相似的更改为以下代码:

---

- 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

上述剧本因???其中含有以下内容而无效。

现在我的目标是将这个字典映射到 INI 文件内的sections/options/values并获取以下 INI 内容:

[section1]
option1 = value1
option2 = value2

[section2]
option1 = value1
option2 = value2

不幸的是,这似乎失败了,因为我需要为循环添加第二级嵌套不知何故在外循环中循环遍历各个部分,然后在内循环中循环遍历选项/值。

有没有一种更优雅或“ansible”(因为缺少与 pythonic 相对应的 Ansible 术语)的方法来实现这一点,而不是通过在角色中包装一个循环级别并使用迭代部分名称,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

我只是想保持它DRY并避免重复部分名称,当字典显然可以完美地映射到INI文件的“数据模型”时。

答案1

除了使用“扁平化”版本之外,我不知道默认情况下使用 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 应用于 ansible 中的字符串,因此您可以使用 jinja 逻辑直接在剧本中为您创建“扁平化”布局。与编写自己的过滤器相比,此解决方案更丑陋且可重用性较差,但从技术上讲仍然有效。

- 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 %}
        ]

相关内容