首页 > 解决方案 > Ansible 过滤器以查找具有指定名称的所有列表

问题描述

我有一个任务

  - name: Verify all app states
    shell: get_cluster_states {{item.name}}
    register: service_states
    until: <all services are in 'up' state, this is what I need>
    retries: 20
    delay: 3
    with_items: "{{ apps }}"

返回的“service_states”的内容格式如下:

"service_states": {
    "changed": true,
    "msg": "All items completed",
    "results": [
        {
            ......
            "stdout": "Service Status: up\nService Status: up\nService Status: up",
            "stdout_lines": [
                "Service Status: up",
                "Service Status: down",
                "Service Status: up"
            ]
        },
        {
            ......
            "stdout": "Service Status: up\nService Status: up\nService Status: up",
            "stdout_lines": [
                "Service Status: up",
                "Service Status: up",
                "Service Status: down"
            ]
        }
    ]
}

我需要将过滤器应用于 service_states 数据,因此我可以从所有“stdout_lines”中获得一个组合的简单列表。在这种情况下,来自 2 个列表的总共 6 个值 - 列表元素的数量不固定:

expected: [
                "Service Status: up",
                "Service Status: down",
                "Service Status: up",
                "Service Status: up",
                "Service Status: up",
                "Service Status: down"
          ]

如果我能做到这一点,我就可以使用唯一/计数/值来确定所有服务是否启动。我尝试使用 {{ service_states.results | subelements('stdout_lines') }} 来获取 6 个条目,但不知道如何进一步过滤以获得上述最终列表。

标签: ansible

解决方案


使用json_query,例如

- set_fact:
    expected: "{{ service_states.results|
                  json_query('[].stdout_lines')|flatten }}"

,或地图属性,例如

- set_fact:
    expected: "{{ service_states.results|
                  map(attribute='stdout_lines')|flatten }}"

问:确定所有服务是否启动。

答:例如

    - debug:
        msg: All services are up.
      when: _all == _up
      vars:
        _list: "{{ service_states.results|
                   map(attribute='stdout_lines')|flatten }}"
        _all: "{{ _list|length }}"
        _up: "{{ _list|select('match', 'Service Status: up')|list|length }}"
            
    - debug:
        msg: "{{ _all|int - _up|int }} services are down."
      vars:
        _list: "{{ service_states.results|
                   map(attribute='stdout_lines')|flatten }}"
        _all: "{{ _list|length }}"
        _up: "{{ _list|select('match', 'Service Status: up')|list|length }}"

TASK [debug] *************************************************************
skipping: [localhost]

TASK [debug] *************************************************************
ok: [localhost] => 
  msg: 2 services are down.

问:有没有办法打印出直到位置的 service_states 的样子?

A: 没有。没有打印中间结果的方法。该任务正在远程主机上运行。在任务完成注册变量后,我们的例子中的service_states将被发送回控制器。相反,您可以调试模块以详细了解发生了什么。请参阅调试模块


问:也许循环内部没有结果层

答:是的。循环内的结果是不同的。当循环完成时,注册变量必须包含所有结果的列表。请参阅循环任务直到成功


问:需要直到

A:没有循环直到文档中描述的工作,例如创建下面的脚本来生成随机数据

shell> cat random.sh
#!/bin/bash
printf "Service Status: "
[ "$((1 + $RANDOM % 10))" -lt "5" ] && echo up || echo down
printf "Service Status: "
[ "$((1 + $RANDOM % 10))" -lt "5" ] && echo up || echo down
printf "Service Status: "
[ "$((1 + $RANDOM % 10))" -lt "5" ] && echo up || echo down

shell> ./random.sh
Service Status: up
Service Status: up
Service Status: down

然后,剧本

shell> cat playbook.yml
- hosts: localhost
  vars:
    _pattern: "Service Status: up"
  tasks:
    - command: "{{ playbook_dir }}/random.sh"
      register: service_states
      until: service_states.stdout_lines|
             select('match', _pattern)|list|length == 3
      retries: 10
      delay: 1
    - debug:
        msg: "{{ service_states.stdout_lines }}"

shell> ansible-playbook playbook.yml

TASK [command] **********************************************************
FAILED - RETRYING: command (10 retries left).
FAILED - RETRYING: command (9 retries left).
FAILED - RETRYING: command (8 retries left).
changed: [localhost]

TASK [debug] ************************************************************
ok: [localhost] => 
  msg:
  - 'Service Status: up'
  - 'Service Status: up'
  - 'Service Status: up'

推荐阅读