如何在没有错误的情况下退出Ansible playbook

jre*_*ger 33 ansible

当我遇到某种情况时,我想在没有错误的情况下退出(我知道断言失败模块).以下代码退出但失败:

  tasks:

    - name: Check if there is something to upgrade
      shell: if apt-get --dry-run upgrade | grep -q "0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded"; then echo "no"; else echo "yes"; fi
      register: upgrading

    - name: Exit if nothing to upgrade
      fail: msg="Nothing to upgrade"
      when: upgrading.stdout == "no"
Run Code Online (Sandbox Code Playgroud)

sna*_*fla 59

在Ansible 2.2,可以使用end_play模块:

- meta: end_play
Run Code Online (Sandbox Code Playgroud)

您还可以指定when有条件地结束游戏:

- meta: end_play
  when: upgrading.stdout == "no"
Run Code Online (Sandbox Code Playgroud)

但请注意,无论播放是否实际结束,任务都未列在ansible-playbook的输出中.此外,该任务不计入回顾中.所以,你可以这样做:

- block:
    - name: "end play if nothing to upgrade"
      debug:
        msg: "nothing to upgrade, ending play"

    - meta: end_play
  when: upgrading.stdout == "no"
Run Code Online (Sandbox Code Playgroud)

只有在满足条件时才会在结束前宣布比赛结束.如果不满足条件,您将看到名称被end play if nothing to upgrade适当跳过的任务,这将为用户提供有关游戏结束或不结束的更多信息.

当然,这只会结​​束当前的比赛而不是剧本中所有剩余的比赛.

  • 很棒的发现!不幸的是,`end_play` 会结束整个 playbook,即使在使用例如 `import_tasks` 从主 playbook 调用的子 playbook 中使用时也是如此。 (2认同)
  • @ssc您正在混淆任务和剧本导入。使用“import_tasks”执行仍保持相同的状态,因此应该以该命令结束。要导入单独的剧本,请使用“import_playbook”语句。 (2认同)
  • 重要提示:end_play 似乎停止了所有主机上的执行,而不仅仅是条件匹配的主机!https://github.com/ansible/ansible/issues/27973 (2认同)
  • 这很好,但如果在“角色”内使用“end_host”,并且您有一个包含“roles:”的剧本,那么它将结束主机的整个剧本。是否可以直接跳到列表中的下一个角色?这值得提出一个新问题吗?:) (2认同)

小智 7

只是一点注意:meta: end_play结束只是戏剧,而不是剧本。所以这个剧本:

---
- name: 1st play with end play
  hosts: localhost
  connection: local
  gather_facts: no
  tasks:
    - name: I'll always be printed
      debug:
        msg: next task terminates first play

    - name: Ending the 1st play now
      meta: end_play

    - name: I want to be printed!
      debug:
        msg: However I'm unreachable so this message won't appear in the output

- name: 2nd play
  hosts: localhost
  connection: local
  gather_facts: no
  tasks:
    - name: I will also be printed always
      debug:
        msg: "meta: end_play ended just the 1st play. This is 2nd one."
Run Code Online (Sandbox Code Playgroud)

将产生这个输出:

$ ansible-playbook -i localhost, playbooks/end_play.yml 

PLAY [1st play with end play] **************************************************

TASK [I'll always be printed] **************************************************
ok: [localhost] => {
    "msg": "next task terminates first play"
}

PLAY [2nd play] ****************************************************************

TASK [I will also be printed always] *******************************************
ok: [localhost] => {
    "msg": "meta: end_play ended just the 1st play. This is 2nd one."
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Run Code Online (Sandbox Code Playgroud)

  • 你如何让整个剧本结束? (2认同)

Tym*_*aul 5

解决它的更好、更合乎逻辑的方法可能是反过来做,而不是在没有任何升级的情况下失败(这是一个单独的步骤,仅执行此操作)您可以根据upgrade变量附加所有升级任务的条件. 本质上只是添加

when: upgrading.changed
Run Code Online (Sandbox Code Playgroud)

只应在升级期间执行的任务。

这是更多的工作,但它也带来了清晰度,并且 self 包含影响给定任务的逻辑,而不是依赖于上面可能会或可能不会提前终止的某种方式。

  • 当在决定是否继续的任务之后有很多任务时,这很不方便:-/ (3认同)
  • 好吧,这也可以与块和 when 语句结合使用,有效地对要运行或跳过的任务进行分组。这应该会大大减少不便。 (2认同)
  • 这种方法也可以与“include_tasks”或“import_tasks”配合使用,以保持输出干净,因此每次都不会跳过一堆任务。 (2认同)

jhu*_*tar 5

让我们使用 Tymoteusz 建议的角色:

Split your play into two roles where first role will execute the check (and sets some variable holding check's result) and second one will act based on result of the check.

I have created aaa.yaml with this content:

---
- hosts: all
  remote_user: root
  roles:
    - check
    - { role: doit, when: "check.stdout == '0'" }
...
Run Code Online (Sandbox Code Playgroud)

then role check in roles/check/tasks/main.yaml:

---
- name: "Check if we should continue"
  shell:
    echo $(( $RANDOM % 2 ))
  register: check
- debug:
    var: check.stdout
...
Run Code Online (Sandbox Code Playgroud)

and then role doit in roles/doit/tasks/main.yaml:

---
- name: "Do it only on systems where check returned 0"
  command:
    date
...
Run Code Online (Sandbox Code Playgroud)

And this was the output:

TASK [check : Check if we should continue] *************************************
Thursday 06 October 2016  21:49:49 +0200 (0:00:09.800)       0:00:09.832 ****** 
changed: [capsule.example.com]
changed: [monitoring.example.com]
changed: [satellite.example.com]
changed: [docker.example.com]

TASK [check : debug] ***********************************************************
Thursday 06 October 2016  21:49:55 +0200 (0:00:05.171)       0:00:15.004 ****** 
ok: [monitoring.example.com] => {
    "check.stdout": "0"
}
ok: [satellite.example.com] => {
    "check.stdout": "1"
}
ok: [capsule.example.com] => {
    "check.stdout": "0"
}
ok: [docker.example.com] => {
    "check.stdout": "0"
}

TASK [doit : Do it only on systems where check returned 0] *********************
Thursday 06 October 2016  21:49:55 +0200 (0:00:00.072)       0:00:15.076 ****** 
skipping: [satellite.example.com]
changed: [capsule.example.com]
changed: [docker.example.com]
changed: [monitoring.example.com]
Run Code Online (Sandbox Code Playgroud)

It is not perfect: looks like you will keep seeing skipping status for all tasks for skipped systems, but might do the trick.