我正在尝试找到一种使用 Ansible 调试模块创建以下结果的方法。
期望的结果
{
"myserver01": {
"host_var_one": "foo",
"host_var_two": "bar"
},
"myserver02": {
"host_var_one": "biz",
"host_var_two": "baz"
},
"myserver03": {
"host_var_one": "fizz",
"host_var_two": "buzz"
}
}
库存示例
[my_servers]
myserver01 host_var_one=foo host_var_two=bar
myserver02 host_var_one=biz host_var_two=baz
myserver03 host_var_one=fizz host_var_two=buzz
我希望能够提供一个主机变量列表,并将它们显示在清单中每个主机下的字典中,其中键是主机变量名称,值是主机变量值。理想情况下,在结果中包含另一个主机变量应该只需要向列表中添加另一个变量名称即可。
例如,在任务中,我会为清单中的每个主机列出我想要的
["host_var_one", "host_var_two"]
,并获得上述所需的结果。
我有以下任务,它“在某种程度上”接近我想要的。我只是想不出一种方法以上述格式列出每个主机所需的所有变量。以下仅适用于一个变量,并且没有列出变量名称和值。
myplaybook.yml
---
- name: Test
hosts: all
gather_facts: yes
user: ansible
become: yes
tasks:
- name: Debug
debug:
msg: "{{ dict(query('inventory_hostnames', 'all') | zip(query('inventory_hostnames', 'all') | map('extract', hostvars, 'host_var_one'))) }}"
run_once: yes
$ ansible-playbook -i inventory test.yml --limit "myserver01"
PLAY [Test] **************************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************************
ok: [myserver01]
TASK [Debug] *************************************************************************************************************************************************
ok: [myserver01] => {
"msg": {
"myserver01": "foo",
"myserver02": "biz",
"myserver03": "fizz"
}
}
- name: Dict of host_vars
debug:
msg: "{{ dict( keys | zip(values) ) }}"
vars:
keys: "{{ hostvars | dict2items | map(attribute='key') }}"
values: "{{ hostvars | dict2items | map(attribute='value') | map('dict2items')
| map('selectattr', 'key', 'match', 'host_var_') | map('items2dict') }}"
run_once: yes
结果如下:
TASK [Host vars dict] **************************************************
ok: [myserver01] => {
"msg": {
"myserver01": {
"host_var_one": "foo",
"host_var_two": "bar"
},
"myserver02": {
"host_var_one": "biz",
"host_var_two": "baz"
},
"myserver03": {
"host_var_one": "fizz",
"host_var_two": "buzz"
}
}
}
逐步说明
keys
和
values
。keys
的列表是通过用
dict2items
转换字典来创建的,这样key
就可以提取属性map
。这样你就可以得到列表了:[ "myserver01", "myserver02", "myserver03" ]
对于
values
,其工作方式相同,即生成
value
属性列表。这些列表在每种情况下都是字典,因此必须通过 map('dict2items')
再次转换为列表。现在可以通过 key
在列表中过滤 selectattr
。这里 host_var_
被 match
(字符串的开头)过滤掉。然后,结果(列表中的列表)必须通过 map('items2dict')
转换回字典。[
{
"host_var_one": "foo",
"host_var_two": "bar"
},
{
"host_var_one": "biz",
"host_var_two": "baz"
},
{
"host_var_one": "fizz",
"host_var_two": "buzz"
}
]
最后,通过
zip
合并两个列表,并通过
dict
转换回一个列表。按前缀过滤的变量
例如库存:
[my_servers]
myserver01 host_var_one=foo host_var_two=bar
myserver02 host_var_one=biz host_var_two=baz host_var_three=boz host_var_four=buz
myserver03 host_var_one=fizz host_var_two=buzz host_var_six=dazz
返回以下结果:
TASK [Dict of host_vars] **********************************************
ok: [myserver01] => {
"msg": {
"myserver01": {
"host_var_one": "foo",
"host_var_two": "bar"
},
"myserver02": {
"host_var_four": "buz",
"host_var_one": "biz",
"host_var_three": "boz",
"host_var_two": "baz"
},
"myserver03": {
"host_var_one": "fizz",
"host_var_six": "dazz",
"host_var_two": "buzz"
}
}
}
selection: "{{ dict(ansible_play_hosts_all|
zip(hostvars|json_query(_query))) }}"
_query: '*.{host_var_one: host_var_one, host_var_two: host_var_two}'
鉴于上述库存,结果是
selection:
myserver01:
host_var_one: foo
host_var_two: bar
myserver02:
host_var_one: biz
host_var_two: baz
myserver03:
host_var_one: fizz
host_var_two: buzz
在字典中,所有主机都保留由公共_query
选择的相同变量。
- hosts: my_servers
gather_facts: false
vars:
selection: "{{ dict(ansible_play_hosts_all|
zip(hostvars|json_query(_query))) }}"
_query: '*.{host_var_one: host_var_one, host_var_two: host_var_two}'
tasks:
- debug:
var: selection
run_once: true
“列出清单中每个主机的变量。” A:例如,给定库存
shell> cat hosts
all:
children:
my_servers:
hosts:
myserver01:
host_var_one: foo
host_var_two: bar
host_var_three: baz
sel_vars: [host_var_one, host_var_two, host_var_three]
myserver02:
host_var_one: biz
host_var_two: baz
sel_vars: [host_var_one, host_var_two]
myserver03:
host_var_one: fiz
host_var_two: buz
sel_vars: [host_var_two]
下面的剧本
- hosts: my_servers
gather_facts: false
vars:
sel_vals: "{{ sel_vars|map('extract', vars)|list }}"
sel_dict: "{{ dict(sel_vars|zip(sel_vals)) }}"
sel_all: "{{ ansible_play_hosts_all|map('extract', hostvars, 'sel_dict') }}"
selection: "{{ dict(ansible_play_hosts_all|zip(sel_all)) }}"
tasks:
- set_fact:
sel_dict: "{{ sel_dict }}"
- debug:
var: selection
run_once: true
给出(删节)
selection:
myserver01:
host_var_one: foo
host_var_three: baz
host_var_two: bar
myserver02:
host_var_one: biz
host_var_two: baz
myserver03:
host_var_two: buz
任务set_fact
对于所有主机“实例化”(在 hostvars 中创建)变量 sel_dict 是必需的。 您可以动态选择变量。例如,将以下声明放入
vars
sel_vars: "{{ query('varnames', '.*_one') }}"
结果将会是
selection:
myserver01:
host_var_one: foo
myserver02:
host_var_one: biz
myserver03:
host_var_one: fiz
在处理顶级字典方面很烂,所以一点点
dict2items
将其旋转为一个字典数组, JMESPath 更擅长处理,将项目投影到与您想要的东西相同的
[{key: .key, value: ...}]
形状中,然后将其旋转回具有相反过滤器的字典 (items2dict
) - debug:
msg: '{{ hostvars | dict2items | json_query(jq) | items2dict }}'
delegate_to: localhost
run_once: true
vars:
jq: '[*].{key: key, value: {one: value.host_var_one, two: value.host_var_two}}'
产生
ok: [alpha -> localhost] => {
"msg": {
"alpha": {
"one": "a1",
"two": "a2"
},
"beta": {
"one": "b1",
"two": "b2"
}
}
}
zip
所做的事情,但据我所知,
hostvars
包含所有主机变量,因此不需要
zip
欺骗
或者,如果你有一个巨大的列表,或者一个未知的列表,要提取这些主机变量,我严重遗憾 ansible 的 Jinja2 中缺乏字典理解,但迭代版本很好:
- debug:
msg: >-
{%- set r = {} -%}
{%- for hv in hostvars.keys() -%}
{%- set d = {} -%}
{%- for f in the_facts -%}
{%- set _ = d.update({f: hostvars[hv][f]}) -%}
{%- endfor -%}
{%- set _ = r.update({hv: d}) -%}
{%- endfor -%}
{{ r }}
vars:
the_facts:
- host_var_one
- host_var_two