使用一级索引移动嵌套的 Ansible 字典丰富项目?

问题描述 投票:0回答:1

TL;DR:我需要展平字典的 Ansible 字典,以便将子项移动到根级别,并将其原始父键添加到其中。

上下文:我需要使用 Ansible 填充 TOML 文件,其中配置选项来自 Ansible 变量/事实。我使用

community.general.ini_file
来填充
.toml
文件,因为它工作正常(就我而言)。

我的输入变量/事实很简单:

config:
  param0_1: value0_1 # This key-value pair has no section/table
  section1: # This dict contains all the key-value pairs for section/table "section1"
    param1_1: value1_1
  section2: # Section/table "section2"
    param2_1: value2_1
    param2_2: value2_2

我希望最终的 TOML 文件看起来像这样(= 有这些设置):

param0_1 = 'value0_1'

[section1]
param1_1 = 'value1_1'

[section2]
param2_1 = 'value2_1'
param2_2 = 'value2_2'

我设法使用此服务器错误“使用带有嵌套字典的Ansible ini_file?”答案获得了我想要的内容,它使用Jinja和JSON表示法来处理/转换所需输出中的输入。

这是包含此类解决方案的剧本:

# Usage: ansible-playbook playbook.yml -C -D
---
- hosts: 127.0.0.1
  connection: local
  gather_facts: false

  vars:
    config:
      param0_1: value0_1
      section1:
        param1_1: value1_1
      section2:
        param2_1: value2_1
        param2_2: value2_2
    config_flat_via_jinja_json: >-
      [
        {% for section, subdict in config.items() %}
          {% if subdict is mapping %}
            {% for key, value in subdict.items() %}
              {
                "section": "{{ section }}",
                "key": "{{ key }}",
                "value": "{{ value }}"
              }
              {% if not loop.last %}
                ,
              {% endif %}
            {% endfor %}
          {% else %}
            {
              "section": null,
              "key": "{{ section }}",
              "value": "{{ subdict }}"
            }
          {% endif %}
          {% if not loop.last %}
            ,
          {% endif %}
        {% endfor %}
      ]

  tasks:
  - set_fact:
      config:
        param0_1: value0_1
        section1:
          param1_1: value1_1
        section2:
          param2_1: value2_1
          param2_2: value2_2
  - set_fact:
      config_flat_via_jinja_json: >-
        [
          {% for section, subdict in config.items() %}
            {% if subdict is mapping %}
              {% for key, value in subdict.items() %}
                {
                  "section": "{{ section }}",
                  "key": "{{ key }}",
                  "value": "{{ value }}"
                }
                {% if not loop.last %}
                  ,
                {% endif %}
              {% endfor %}
            {% else %}
              {
                "section": null,
                "key": "{{ section }}",
                "value": "{{ subdict }}"
              }
            {% endif %}
            {% if not loop.last %}
              ,
            {% endif %}
          {% endfor %}
        ]
  - debug:
      var: config
  - debug:
      var: config_flat_via_jinja_json

  - name: loop on config_flat_via_jinja_json | list
    debug:
      var: item
    loop: "{{ config_flat_via_jinja_json | list }}"

  - name: Configure application
    # Using ini_file module to populates the TOML file. It works for our need, ini sections are like TOML tables.
    ini_file:
      path: config.toml
      section: '{{ item.section }}'
      option: '{{ item.key }}'
      value: '{{ item.value }}'
      # Will remove configuration line from config file if config value is empty
      state: '{{ "present" if item.value != "" else "absent" }}'
    loop: "{{ config_flat_via_jinja_json | list }}"

“config_flat_via_jinja_json”有以下输出:

TASK [debug] ******************************************************************************************************************************************************************
ok: [127.0.0.1] => {
    "config_flat_via_jinja_json": [
        {
            "key": "param0_1",
            "section": null,
            "value": "value0_1"
        },
        {
            "key": "param1_1",
            "section": "section1",
            "value": "value1_1"
        },
        {
            "key": "param2_1",
            "section": "section2",
            "value": "value2_1"
        },
        {
            "key": "param2_2",
            "section": "section2",
            "value": "value2_2"
        }
    ]
}

我可以工作,但它看起来像是对某些东西的黑客攻击我认为在本机 Ansible 中是可行的。

是吗?

注 1:由于 TOML 文件从 Ansible 外部更改,因此我无法在此处使用 Ansible 模板。除此之外,我认为这与我正在努力解决的主要问题无关。

注 2:我使用的是 Ansible v2.10(希望这样的“旧”版本不会成为解决方案的障碍)。

ansible
1个回答
0
投票

您可以控制输入数据的格式吗?如果您可以确保所有选项都包含在顶级部分中,如下所示:

config:
  default:
    param0_1: value0_1
  section1:
    param1_1: value1_1
  section2:
    param2_1: value2_1
    param2_2: value2_2

然后在 Ansible 中构建有用的数据结构就容易多了。我们可以做这样的事情:

- hosts: localhost
  gather_facts: false

  vars:
    config:
      default:
        param0_1: value0_1
      section1:
        param1_1: value1_1
      section2:
        param2_1: value2_1
        param2_2: value2_2
  tasks:
  # Transform the `config` dictionary into a list of dictionaries that we
  # can use with the `subelements` filter
  - loop: "{{ config.keys() }}"
    vars:
      _config: []
    set_fact:
      _config: "{{ _config + [{'name': item, 'items': config[item]|dictsort}] }}"

  - name: Configure application
    ini_file:
      path: config.toml
      section: '{{ (item.0.name == "default") | ternary("", item.0.name) }}'
      option: '{{ item.1.0 }}'
      value: '{{ item.1.1 }}'
      state: '{{ "present" if item.1.1 != "" else "absent" }}'
    loop: "{{ _config | subelements('items') }}"

上面的剧本产生以下

config.toml

param0_1 = value0_1

[section1]
param1_1 = value1_1
[section2]
param2_1 = value2_1
param2_2 = value2_2
© www.soinside.com 2019 - 2024. All rights reserved.