我写了一个简化的示例,但作为最小的可重现代码应该足够了。我希望能够根据 2 条规则从我的库存文件生成主机列表。
cdsre
foo
值的属性 baz
OR 主机未定义属性 foo
我已经这样做了几个小时,并且可以通过一个冗长的 jinja2 字符串循环来实现这一点,该循环使用
if
表达式,其副作用是我认为非常丑陋。然而,我不禁认为这应该可以仅使用 jinja 过滤器来实现。
样品库存
all:
children:
cdsre:
children:
ovh_vm:
hosts:
ovh-vm[1:3]:
ovh-vm[6:7]:
foo: baz
oracle_vm:
hosts:
oracle-vm[1:3]:
foo: bar
oracle-vm[4:5]:
foo: baz
剧本
---
- hosts: localhost
gather_facts: false
tasks:
- set_fact:
some_servers: |
{% set servers = [] %}
{% for host in groups['cdsre'] %}
{% set foo = hostvars[host]['foo'] | default('baz', true) %}
{% if foo == 'baz' %}
{% if servers.append(hostvars[host]['inventory_hostname']) %}{% endif %}
{% endif %}
{% endfor %}
{{ servers }}
foo_matched_servers: "{{ groups['cdsre'] | map('extract', hostvars) | selectattr('foo', 'defined') | selectattr('foo', '==', 'baz') | map(attribute='inventory_hostname') | list}}"
- debug:
var: some_servers
- debug:
var: foo_matched_servers
输出
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [set_fact] ************************************************************************************************************************************************************************************************************************
Tuesday 10 January 2023 23:57:00 +0000 (0:00:00.073) 0:00:00.073 *******
ok: [localhost]
TASK [debug] ***************************************************************************************************************************************************************************************************************************
Tuesday 10 January 2023 23:57:01 +0000 (0:00:00.885) 0:00:00.958 *******
ok: [localhost] => {
"some_servers": [
"ovh-vm1",
"ovh-vm2",
"ovh-vm3",
"ovh-vm6",
"ovh-vm7",
"oracle-vm4",
"oracle-vm5"
]
}
TASK [debug] ***************************************************************************************************************************************************************************************************************************
Tuesday 10 January 2023 23:57:01 +0000 (0:00:00.061) 0:00:01.019 *******
ok: [localhost] => {
"foo_matched_servers": [
"ovh-vm6",
"ovh-vm7",
"oracle-vm4",
"oracle-vm5"
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Tuesday 10 January 2023 23:57:01 +0000 (0:00:00.062) 0:00:01.082 *******
===============================================================================
set_fact ---------------------------------------------------------------- 0.89s
debug ------------------------------------------------------------------- 0.12s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total ------------------------------------------------------------------- 1.01s
所以我可以生成匹配条件 1 的主机列表,但我似乎无法弄清楚如何在单个事实中捕获匹配条件 1 和条件 2 的主机。这可能吗?或者我是否需要编写一个额外的事实来捕获匹配条件 2 的主机,然后加入这两个事实(这也感觉有点啰嗦)
还有更多选择:
声明下面的变量
my_hosts_str: |
[
{% for host in groups.cdsre %}
{% if hostvars[host]['foo']|default('baz') == 'baz' %}
{{ host }},
{% endif %}
{% endfor %}
]
my_hosts: "{{ my_hosts_str|from_yaml }}"
在字符串 my_hosts_str
中给出有效的 YAML 列表 my_hosts_str: |-
[
ovh-vm1,
ovh-vm2,
ovh-vm3,
ovh-vm6,
ovh-vm7,
oracle-vm4,
oracle-vm5,
]
给出了预期的结果。将字符串转换为列表。
my_hosts:
- ovh-vm1
- ovh-vm2
- ovh-vm3
- ovh-vm6
- ovh-vm7
- oracle-vm4
- oracle-vm5
用于测试的剧本示例
- hosts: all
vars:
my_hosts_str: |
[
{% for host in groups.cdsre %}
{% if hostvars[host]['foo']|default('baz') == 'baz' %}
{{ host }},
{% endif %}
{% endfor %}
]
my_hosts: "{{ my_hosts_str|from_yaml }}"
tasks:
- block:
- debug:
var: my_hosts_str
- debug:
var: my_hosts
run_once: true
声明以下变量,例如在 group_vars/all
shell> cat group_vars/all
csdr_foo_und: "{{ groups.cdsre|map('extract', hostvars)|
selectattr('foo', 'undefined')|
map(attribute='inventory_hostname') }}"
csdr_foo_baz: "{{ groups.cdsre|map('extract', hostvars)|
selectattr('foo', 'defined')|
selectattr('foo', '==', 'baz')|
map(attribute='inventory_hostname') }}"
my_hosts: "{{ csdr_foo_baz + csdr_foo_und }}"
给出组 csdr 中未定义 foo
的主机列表 csdr_foo_und:
- ovh-vm1
- ovh-vm2
- ovh-vm3
给出组 csdr 中的主机列表,其中 foo 等于 baz
csdr_foo_baz:
- ovh-vm6
- ovh-vm7
- oracle-vm4
- oracle-vm5
给出了预期的结果。连接列表 csdr_foo_und 和 csdr_foo_baz
my_hosts:
- oracle-vm4
- oracle-vm5
- ovh-vm1
- ovh-vm2
- ovh-vm3
- ovh-vm6
- ovh-vm7
用于测试的剧本示例
- hosts: all
tasks:
- block:
- debug:
var: csdr_foo_und
- debug:
var: csdr_foo_baz
- debug:
var: my_hosts|sort
run_once: true
使用库存插件构建。见
shell> ansible-doc -t inventory ansible.builtin.constructed
例如创建下面的项目进行测试
shell> tree .
.
├── ansible.cfg
├── inventory
│ ├── 01-hosts.yml
│ └── 02-constructed.yml
└── pb.yml
1 directory, 4 files
shell> cat ansible.cfg
[defaults]
gathering = explicit
inventory = $PWD/inventory
retry_files_enabled = false
stdout_callback = yaml
shell> cat inventory/01-hosts.yml
all:
children:
cdsre:
children:
ovh_vm:
hosts:
ovh-vm[1:3]:
ovh-vm[6:7]:
foo: baz
oracle_vm:
hosts:
oracle-vm[1:3]:
foo: bar
oracle-vm[4:5]:
foo: baz
创建一个新组my_group
shell> cat inventory/02-constructed.yml
plugin: ansible.builtin.constructed
use_vars_plugins: true
use_extra_vars: true
groups:
# host belongs to group 'cdsre' and
# foo is either undefined or 'baz'
my_group: group_names is contains 'cdsre' and
foo|default('baz') == 'baz'
引用组 groups.my_group 或在 hosts
中使用它shell> cat pb.yml
- hosts: all
tasks:
- debug:
var: groups.my_group
run_once: true
- hosts: my_group
tasks:
- debug:
var: ansible_play_hosts_all
run_once: true
给予
shell> ansible-playbook pb.yml
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [ovh-vm1] =>
groups.my_group:
- ovh-vm1
- ovh-vm2
- ovh-vm3
- ovh-vm6
- ovh-vm7
- oracle-vm4
- oracle-vm5
PLAY [my_group] ******************************************************************************
TASK [debug] *********************************************************************************
ok: [ovh-vm1] =>
ansible_play_hosts_all:
- ovh-vm1
- ovh-vm2
- ovh-vm3
- ovh-vm6
- ovh-vm7
- oracle-vm4
- oracle-vm5
PLAY RECAP ***********************************************************************************
ovh-vm1: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
选项 1 对我有用。这个答案略有不同,展示了如何扩展该选项。不过,100% 的功劳都归功于弗拉基米尔。没有他就不可能想出这个。
就我而言,我的清单的主机名带有
this_host_is_manager_01.example.com:
os_node_roles: [ 'cluster_manager' ]
为某些主机定义。我想要一份上述主机的列表。这样做了:
os_manager_str: |
[
{% for host in ansible_play_hosts %} # I already had hosts selected
{% if 'cluster_manager' in hostvars[host]['os_node_roles'] %} # secret sauce
{{ host|regex_replace('\..*', '') }}, # convert FQDN to short hostname
{% endif %}
{% endfor %}
]
os_manager_nodes: "{{ os_manager_str|from_yaml }}"
此选择的秘诀是
in
条款。
非常感谢你,弗拉基米尔:你似乎对这类事情很感兴趣。