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(希望这样的“旧”版本不会成为解决方案的障碍)。
您可以控制输入数据的格式吗?如果您可以确保所有选项都包含在顶级部分中,如下所示:
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