【Ansible】1台でもfailedになったhostがあったらplaybookを終了したい(ansible_play_hosts)

#### はじめに
<バージョン>
ansible 2.9.1

Ansibleで、以下のようなことは無いでしょうか？
（1）全てのhostがfailedになっていない状態じゃないと次のtaskに進みたくない
（2）taskの最後まで実行されたhostが何台あるか確認したい
そんな時は、ansible_play_hostsとansible_play_hosts_allという変数を使うと上手くいきます。
どうやら、[Ansible Documentation(Special Variables)](https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html)によると、
　ansible_play_hosts_all：最初に実行させようとしたhostのリスト
　ansible_play_hosts    ：現在実行しているhostのリスト
であるようです。

#### Playbook紹介1～ansible_play_hosts/ansible_play_hosts_allの中身の確認～
例として、junos_router_1とjunos_router_2の2台に対してplaybookを実行します。
2番目のtaskでjunos_router_2のみを強制的にfailedにして、ansible_play_hosts/ansible_play_hosts_allの変化を見てみます。

```yaml:play_hosts_test1.yml
---
- name: play hosts TEST
  hosts: all
  connection: local
  gather_facts: no
  vars:
    ansible_python_interpreter: /usr/bin/python3
  tasks:
    - name: 'debug1'
      debug:
        msg: >-
             ansible_play_hosts_all : {{ ansible_play_hosts_all }},
             ansible_play_hosts : {{ ansible_play_hosts }}

    - name: 'fail router_2'
      fail:
        msg: 'router_2 is failed'  # junos_router_2を強制的にエラーにする
      when:
        - ('router_2' in inventory_hostname)

    - name: 'debug2'
      debug:
        msg: >-
             ansible_play_hosts_all : {{ ansible_play_hosts_all }},
             ansible_play_hosts : {{ ansible_play_hosts }}
```

#### 実行結果1
2番目のtaskでjunos_router_2のみを強制的にfailedにしたことにより、その次のtaskにおいて
ansible_play_hostsの中身が、junos_router_1のみになっていることがわかります。 
※出力結果にu''(ユニコードエスケープ)が付いている点はご了承ください

```text:出力1
PLAY [play hosts TEST] *************************************************************************************************************************************************

TASK [debug1] **********************************************************************************************************************************************************
ok: [junos_router_2] => {
    "msg": "ansible_play_hosts_all : [u'junos_router_1', u'junos_router_2'], ansible_play_hosts : [u'junos_router_1', u'junos_router_2']"
}
ok: [junos_router_1] => {
    "msg": "ansible_play_hosts_all : [u'junos_router_1', u'junos_router_2'], ansible_play_hosts : [u'junos_router_1', u'junos_router_2']"  
}

TASK [fail router_2] ***************************************************************************************************************************************************
skipping: [junos_router_1]
fatal: [junos_router_2]: FAILED! => {"changed": false, "msg": "router_2 is failed"}

TASK [debug2] **********************************************************************************************************************************************************
ok: [junos_router_1] => {
    "msg": "ansible_play_hosts_all : [u'junos_router_1', u'junos_router_2'], ansible_play_hosts : [u'junos_router_1']"  
} # ansible_play_hostsの内容が変化

PLAY RECAP *************************************************************************************************************************************************************
junos_router_1             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
junos_router_2             : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```

#### Playbook紹介2～1台でもfailedがあれば途中でplaybookが終了する～
ansible_play_hostsとansible_play_hosts_allはリスト型になっているのでset_factで要素数を取得し、
hostにエラーが発生して要素数に違いが出ればtaskが終了するようにします。
___ポイント___
(1)set_factでリストの要素数を取得
(2)要素数が異なる場合はエラーになるようにする
(3)hostのうち、1台でもエラーが発生していればこのtaskは実行されない

```yaml:play_hosts_test2.yml
---
- name: play hosts TEST
  hosts: all
  connection: local
  gather_facts: no
  vars:
    ansible_python_interpreter: /usr/bin/python3
  tasks:
    - name: 'fail router_2'
      fail:
        msg: 'router_2 is failed'
      when:
        - ('router_2' in inventory_hostname)

    - block:
        - name: 'set_fact'
          set_fact:
            ansible_play_hosts_all_cnt : "{{ ansible_play_hosts_all | count }}"  #ポイント(1)
            ansible_play_hosts_cnt : "{{ ansible_play_hosts | count }}"

        - name: 'set_fact_result'
          debug:
            msg: >-
                 ansible_play_hosts_all_cnt: {{ ansible_play_hosts_all_cnt }},
                 ansible_play_hosts_cnt: {{ ansible_play_hosts_cnt }}

        - name: 'failed host'
          fail:
            msg: 'failed host exists'
          when:
            - (ansible_play_hosts_all_cnt != ansible_play_hosts_cnt)  #ポイント(2)

        - name: 'debug - all OK'  # ポイント(3)
          debug:
            msg: 'no failed host'
          when:
            - (ansible_play_hosts_all_cnt == ansible_play_hosts_cnt)
      run_once: true
```
#### 実行結果2
junos_router_2を強制的にfailedにしているので、最後のtask(task名：debug - all OK)が実行されていないことがわかります。

```text:出力2
PLAY [play hosts TEST] *************************************************************************************************************************************************

TASK [fail router_2] ***************************************************************************************************************************************************
skipping: [junos_router_1]
fatal: [junos_router_2]: FAILED! => {"changed": false, "msg": "router_2 is failed"}

TASK [set_fact] ********************************************************************************************************************************************************
ok: [junos_router_1]

TASK [set_fact_result] *************************************************************************************************************************************************
ok: [junos_router_1] => {
    "msg": "ansible_play_hosts_all_cnt: 2, ansible_play_hosts_cnt: 1"
}

TASK [failed host] *****************************************************************************************************************************************************
fatal: [junos_router_1]: FAILED! => {"changed": false, "msg": "failed host exists"}

NO MORE HOSTS LEFT *****************************************************************************************************************************************************

PLAY RECAP *************************************************************************************************************************************************************
junos_router_1             : ok=2    changed=0    unreachable=0    failed=1    skipped=1    rescued=0    ignored=0
junos_router_2             : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```