【问题标题】:Getting changed/failed hosts list from a previous task | Ansible从上一个任务中获取更改/失败的主机列表 | Ansible
【发布时间】:2021-07-18 20:14:40
【问题描述】:

全部,

示例:如果我有 20 台主机作为剧本并使用 Serial:10 运行它们,则下面的 shell 命令一次在 10 台主机上运行。调用完成后的处理程序任务,其中创建 dict (_dict) 的任务不提供字典输出,因此第二个任务 - 失败的主机 - 因提到的错误而失败。

      - name: Run some shell command
        shell: "echo 2 > /abcd/abcd.txt"
        when: random condition is satisfied
        register: update2
        ignore_errors: yes
        notify: abc_handler

   - handler:
      - name: abcd_handler
        set_fact:
           _dict: "{{ dict(ansible_play_hosts|zip(
                    ansible_play_hosts|map('extract', hostvars, 'update2'))) }}"
        run_once: true
  

      - name: Find failed hosts 
        set_fact:
           _failed:  "{{ _dict|dict2items|json_query('[?value.failed].key') }}"
        run_once: true         

Handler 第一个任务输出:

        "changed: false"
            "ansible_facts": {
                "_dict": "{u'host1': {'stderr_lines': [], u'changed': True,...u'host2':.....u'host10'}"

当 dict2items 针对上述值运行时,第二个处理程序任务会给出上述错误。

谢谢。

【问题讨论】:

标签: ansible


【解决方案1】:

您似乎想要获取command 任务(如问题所示)失败或更改的主机,然后将它们定位到其他任务。

这需要两件事:

  • 如果command 任务失败,playbook 执行将停止,因此以下任务都不会运行。所以我们需要在任务中添加ignore_errors标志

  • add_host 模块在任务失败或更改时创建新的主机组

所以最后像下面这样的任务应该可以解决问题:

- hosts: some_group
  serial: 1

    - name: update file count
      shell: "echo 2 > /home/ec2-user/abcd.txt"
      when:
        - count.stdout == "1"
      register: update1
      ignore_errors: true

    - name: conditionally add the hosts from current play hosts to a new group
      add_host:
        groups:
          - new_group
        host: "{{ ansible_hostname }}"
      when: >
        cmd_stat is failed or
        cmd_stat is changed

# Then have a play targeting the new group
- hosts: new_group
  tasks:
  # Tasks to be performed

虽然如果有很多主机,使用serial 可能会使整个剧本运行时间更长。

【讨论】:

  • 感谢您的指点。虽然上述方法的问题是当条件满足时,“ansible_play_hosts”会将整个主机列表添加到新组,而不是更改/失败的主机列表,因为同样不适用于循环。有什么办法吗?
  • 嗯。确实如此。我觉得玩主播不是必须的,有条件的直接加ansible_hostname之类的就可以了。我会更新答案。
  • 太好了,这行得通。谢谢 !!由于我的用例一次有 20-30 台服务器作为主机,因此 serial:1 会很痛苦。会想办法。
【解决方案2】:

问:某个任务执行、更改或失败的主机列表。

A:例如,该命令在 test_11 处不做任何更改,在 test_12 处更改文件并在 test_13

处失败
- hosts: test_11,test_12,test_13
  tasks:
    - shell:
        cmd: "echo 2 > /tmp/test/abcd.txt"
        creates: /tmp/test/abcd.txt
      register: update1
      ignore_errors: true
TASK [shell] ***********************************************************
changed: [test_12]
fatal: [test_13]: FAILED! => changed=true 
  cmd: echo 2 > /tmp/test/abcd.txt
  delta: '0:00:00.045992'
  end: '2021-04-25 23:22:31.623804'
  msg: non-zero return code
  rc: 2
  start: '2021-04-25 23:22:31.577812'
  stderr: '/bin/sh: cannot create /tmp/test/abcd.txt: Permission denied'
  stderr_lines: <omitted>
  stdout: ''
  stdout_lines: <omitted>
...ignoring
ok: [test_11]

让我们先用数据创建一个字典,例如

    - set_fact:
        _dict: "{{ dict(ansible_play_hosts|zip(
                        ansible_play_hosts|map('extract', hostvars, 'update1'))) }}"
      run_once: true

给予

  _dict:
    test_11:
      changed: false
      cmd: echo 2 > /tmp/test/abcd.txt
      failed: false
      rc: 0
      stdout: skipped, since /tmp/test/abcd.txt exists
      stdout_lines:
      - skipped, since /tmp/test/abcd.txt exists
    test_12:
      changed: true
      cmd: echo 2 > /tmp/test/abcd.txt
      delta: '0:00:00.032474'
      end: '2021-04-25 23:14:36.361510'
      failed: false
      rc: 0
      start: '2021-04-25 23:14:36.329036'
      stderr: ''
      stderr_lines: []
      stdout: ''
      stdout_lines: []
    test_13:
      changed: true
      cmd: echo 2 > /tmp/test/abcd.txt
      delta: '0:00:00.054980'
      end: '2021-04-25 23:14:35.565811'
      failed: true
      msg: non-zero return code
      rc: 2
      start: '2021-04-25 23:14:35.510831'
      stderr: '/bin/sh: cannot create /tmp/test/abcd.txt: Permission denied'
      stderr_lines:
      - '/bin/sh: cannot create /tmp/test/abcd.txt: Permission denied'
      stdout: ''
      stdout_lines: []

请注意 test_11 报告 ok 而不是 skipped 尽管注册变量显示“stdout: skipped, since / tmp/test/abcd.txt 存在”。

分析现在很简单,例如

    - set_fact:
        _failed:  "{{ _dict|dict2items|json_query('[?value.failed].key') }}"
      run_once: true

给出失败主机的列表

  _failed:
  - test_13

还有下一个任务

    - set_fact:
        _changed: "{{ (_dict|dict2items|json_query('[?value.changed].key'))|
                       difference(_failed) }}"
        _ok: "{{ _dict|dict2items|json_query('[?value.changed == `false`].key') }}"
      run_once: true

给予

  _changed:
  - test_12

  _ok:
  - test_11

注意

  1. 需要从更改的主机中减去故障主机,因为故障主机也报告已更改。

  2. 如果任务被跳过,当然不会有注册变量。


连续剧

如果使用 serial,则将剧本分成 2 个剧本。例如

shell> cat playbook.yml
- hosts: all
  serial: 10
  tasks:
    - shell:
        cmd: "echo 2 > /tmp/test/abcd.txt"
        creates: /tmp/test/abcd.txt
      register: update1
      ignore_errors: true

- hosts: all
  tasks:
    - set_fact:
        _dict: "{{ dict(ansible_play_hosts|zip(
                        ansible_play_hosts|map('extract', hostvars, 'update1'))) }}"
      run_once: true

【讨论】:

  • 获取“dict2items 需要字典,而不是得到 ”错误。所涉及的主机数大于 10,create dictionary 的输出也不是 dict 形式。已知案例?
  • 已知案例?不是我。是给你的吗? edit您的问题,如果您想分享,请发送minimal reproducible example
  • 更新问题
  • set_fact 任务在处理程序中,并在 shell 任务运行时调用它(使用通知)。那么如何让 set_fact 任务在所有主机上运行呢?
猜你喜欢
  • 2023-03-13
  • 2017-09-07
  • 2023-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-14
相关资源
最近更新 更多