使用ansible过滤器根据条件和默认值生成主机列表

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

我写了一个简化的示例,但作为最小的可重现代码应该足够了。我希望能够根据 2 条规则从我的库存文件生成主机列表。

  1. 主持人属于一个名为
    cdsre
  2. 的群组
  3. 主机具有定义为
    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 的主机,然后加入这两个事实(这也感觉有点啰嗦)

ansible ansible-inventory ansible-facts
2个回答
4
投票

还有更多选择:

  1. 使用 Jinja 创建具有有效 YAML 列表的字符串并将该字符串转换为列表

声明下面的变量

  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

  1. 使用过滤器创建变量my_hosts

声明以下变量,例如在 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_undcsdr_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

  1. 创建一个新组my_group

使用库存插件构建。见

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

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
条款。

非常感谢你,弗拉基米尔:你似乎对这类事情很感兴趣。

© www.soinside.com 2019 - 2024. All rights reserved.