循环不适用于 Ansible 中的条件数据类型测试

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

当我使用此命令和额外变量执行 ansible playbook 时

ansible-playbook controller_config/windows_domain_names.yml -e "target_win_host=testserver.testdomain.com"

其中包含一项任务

- name: Find if the variable target_win_host is a list
  debug:
    msg: "{{ item }} is list data type"
  loop: "{{ target_win_host}}"
  when: target_win_host | type_debug == 'list'

我得到的错误是

“传递到“循环”的数据无效,它需要一个列表,改为:testserver.testdomain.com。提示:如果您传递了仅包含一个元素的列表/字典,请尝试将wantlist=True添加到您的查找调用中或使用q/查询而不是查找。”

我希望应该跳过上面的块,因为它不满足变量是列表并且它是标量(字符串)的条件。

我尝试了以下不同的条件

- name: Find if the variable target_win_host is a list
  debug:
    msg: "{{ item }} is list data type"
  loop: "{{ target_win_host}}"
  when: target_win_host is not string and target_win_host is iterable and target_win_host is sequence

但与上面相同的错误。

ansible
2个回答
2
投票

您的方法不起作用,因为对于给定的任务,首先评估

loop:
表达式,提取第一项,然后才评估
when:
条件(每次循环迭代一次)。

换句话说,为了您的工作方法,您需要以某种方式将条件嵌入到

loop:
表达式中,并使其结果为 0 长度列表。以下任务实现了该目标。

    - name: Loop over target_win_host it is a list
      debug:
          msg: "{{ item }} is part of a list of hosts"
      loop: "{{ target_win_host if target_win_host | type_debug == 'list' else [] }}"

同时,如果您希望对所有主机执行相同的任务,无论它们是作为列表还是作为单个主机字符串给出,这可能会更好:

    - name: Loop over hosts in target_win_host whether it is a list or a single host string
      debug:
        msg: "{{ item }} was given as one the hosts"
      loop: "{{ [target_win_host] | flatten }}"

这是测试这两个选项的完整剧本:

---
- name: Conditionnal and loops
  hosts: localhost
  gather_facts: false

  vars:
    # default value as a list. Override with extra vars
    target_win_host:
      - host1.example.com
      - host2.example.com

  tasks:
    - name: Loop over target_win_host it is a list
      debug:
          msg: "{{ item }} is part of a list of hosts"
      loop: "{{ target_win_host if target_win_host | type_debug == 'list' else [] }}"


    - name: Loop over hosts in target_win_host whether it is a list or a single host string
      debug:
        msg: "{{ item }} was given as one of the hosts"
      loop: "{{ [target_win_host] | flatten }}"

这给出了:

$ ansible-playbook test.yml 

PLAY [Conditionnal and loops] *************************************************************************************************************************************************************************************

TASK [Loop over target_win_host it is a list] *********************************************************************************************************************************************************************
ok: [localhost] => (item=host1.example.com) => {
    "msg": "host1.example.com is part of a list of hosts"
}
ok: [localhost] => (item=host2.example.com) => {
    "msg": "host2.example.com is part of a list of hosts"
}

TASK [Loop over hosts in target_win_host whether it is a list or a single host string] *****************************************************************************************************************************
ok: [localhost] => (item=host1.example.com) => {
    "msg": "host1.example.com was given as one of the hosts"
}
ok: [localhost] => (item=host2.example.com) => {
    "msg": "host2.example.com was given as one of the hosts"
}

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ ansible-playbook test.yml -e target_win_host=a.single.host

PLAY [Conditionnal and loops] *************************************************************************************************************************************************************************************

TASK [Loop over target_win_host it is a list] *********************************************************************************************************************************************************************
skipping: [localhost]

TASK [Loop over hosts in target_win_host whether it is a list or a single host string] *****************************************************************************************************************************
ok: [localhost] => (item=a.single.host) => {
    "msg": "a.single.host was given as one of the hosts"
}

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

0
投票

创建一个开关。例如,

    - debug:
        msg: "{{ item }}"
      loop: "{{ switch }}"
      vars:
        target: "{{ target_hosts | d([]) }}"
        type: "{{ target | type_debug }}"
        switch: |
          {% filter from_yaml %}
          {% if type == 'str' %}
          {{ target | split(',') }}
          {% elif type == 'list' %}
          {{ target }}
          {% elif type == 'dict' %}
          {{ target.keys() }}
          {% else %}
          []
          {% endif %}
          {% endfilter %}
  • 当未定义 target_hosts 时,循环会跳过默认的空列表
  target: "{{ target_hosts | d([]) }}"
  • target_hosts 是单个字符串时
shell> ansible-playbook pb.yml -e target_hosts=host1

给予

  msg: host1
  • 逗号分隔的字符串也按预期工作
shell> ansible-playbook pb.yml -e target_hosts=host1,host2

给予

  msg: host1
  msg: host2
  • 您可以提供一个列表作为额外变量。将其放入文件中。例如,
shell> cat target_hosts_list.yml
target_hosts:
  - host1
  - host2

该列表也按预期工作

shell> ansible-playbook pb.yml -e @target_hosts_list.yml

给出相同的结果

  msg: host1
  msg: host2
  • 如果您愿意,您甚至可以提供一本字典
shell> cat target_hosts_dict.yml
target_hosts:
  host1: whatever you want 1st
  host2: whatever you want 2nd

这给出了相同的结果

shell> ansible-playbook pb.yml -e @target_hosts_dict.yml

用于测试的完整剧本示例

- hosts: localhost

  tasks:

    - debug:
        var: target_hosts | d([]) | type_debug

    - debug:
        msg: "{{ item }}"
      loop: "{{ switch }}"
      vars:
        target: "{{ target_hosts | d([]) }}"
        type: "{{ target | type_debug }}"
        switch: |
          {% filter from_yaml %}
          {% if type == 'str' %}
          {{ target | split(',') }}
          {% elif type == 'list' %}
          {{ target }}
          {% elif type == 'dict' %}
          {{ target.keys() }}
          {% else %}
          []
          {% endif %}
          {% endfilter %}
© www.soinside.com 2019 - 2024. All rights reserved.