zaki work log

作業ログやら生活ログやらなんやら

[Ansible] metaモジュールを使ってPlayの処理を途中で終了する

プログラミングだとreturnexit(0)などで、以降の処理が不要な場合に、その時点で処理を終了させることができる。
Ansibleのplaybookでも似たようなことができ、metaモジュールを使用し、任意のタイミングで処理を終了させるタスクを作成できる。

docs.ansible.com

このモジュールはパラメタに指定するワードによっていろいろな動作を指せることができ、本エントリではend_playend_hostについて説明する。

以下、ansible-core 2.12.2で確認。

end_play

Playの実行をその時点で終了する。

---
- hosts: localhost
  gather_facts: false

  tasks:
  - name: task1
    debug:
      msg: task1

  - name: task2
    debug:
      msg: task2

  - name: end play
    ansible.builtin.meta: end_play

  - name: task3
    debug:
      msg: task3

このplaybookの内容でAnsibleを実行すると、end playタスクで処理が終了し、task3は実行されない。

PLAY [localhost] ****************************************************************

TASK [task1] ********************************************************************
ok: [localhost] => 
  msg: task1

TASK [task2] ********************************************************************
ok: [localhost] => 
  msg: task2

TASK [end play] *****************************************************************

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

条件によって以降の全ての処理を丸ごとスキップしたい場合に使える。

end_host

end_playが全処理を終了するのに対し、end_hostは指定ホストを対象とした処理のみその時点で終了する。
たとえば処理対象ホストにRHELホストとDebianホストがあり、Debianの場合は後続の処理が何もないので処理終了させて、RHELは続きの処理を行う、みたいなことができる。

---
- hosts: all
  gather_facts: true
  gather_subset:
    - min

  tasks:
  - name: task1
    debug:
      msg: task1

  - name: end host
    ansible.builtin.meta: end_host
    when: ansible_os_family == 'Debian'

  - name: task2
    debug:
      msg: task2

  - name: task3
    debug:
      msg: task3

このplaybookの内容でAnsibleを実行すると、facts変数のansible_os_familyの値がDebianのホストはend hostタスク以降の処理が行われず、それ以外のホストはtask2task3と処理が継続する。

実行例の処理対象ホストは以下の通り。

host distro
cheddar Debian
cloud-dev CentOS

これでansible-playbookを実行すると以下の通り。

PLAY [all] **********************************************************************

TASK [Gathering Facts] **********************************************************
ok: [cheddar]
ok: [cloud-dev]

TASK [task1] ********************************************************************
ok: [cloud-dev] => 
  msg: task1
ok: [cheddar] => 
  msg: task1

TASK [end host] *****************************************************************
skipping: [cloud-dev]

TASK [end host] *****************************************************************

TASK [task2] ********************************************************************
ok: [cloud-dev] => 
  msg: task2

TASK [task3] ********************************************************************
ok: [cloud-dev] => 
  msg: task3

PLAY RECAP **********************************************************************
cheddar                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
cloud-dev                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(追記) 複数のPlayの場合

end_playend_hostでPlayが終了しても、後続のPlayがある場合は、そこからは処理が始まる。

---
- name: play1
  hosts: localhost
  gather_facts: false

  tasks:
  - name: task1
    debug:
      msg: task1

  - name: task2
    debug:
      msg: task2

  - name: end play
    ansible.builtin.meta: end_play

  - name: task3
    debug:
      msg: task3

- name: play2
  hosts: localhost
  gather_facts: false

  tasks:
  - name: task1
    debug:
      msg: task1

このようにplay1のPlayがend_playで終了した場合、task3は処理されないがplay2は処理が始まる。
実行結果は以下。

PLAY [play1] ********************************************************************

TASK [task1] ********************************************************************
ok: [localhost] => 
  msg: task1

TASK [task2] ********************************************************************
ok: [localhost] => 
  msg: task2

TASK [end play] *****************************************************************

PLAY [play2] ********************************************************************

TASK [task1] ********************************************************************
ok: [localhost] => 
  msg: task1

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

end_hostも同様。

---
- name: play1
  hosts: all
  gather_facts: true
  gather_subset:
    - min

  tasks:
  - name: task1
    debug:
      msg: task1

  - name: end host
    ansible.builtin.meta: end_host
    when: ansible_os_family == 'Debian'

  - name: task2
    debug:
      msg: task2

  - name: task3
    debug:
      msg: task3

- name: play2
  hosts: all
  gather_facts: false

  tasks:
  - name: task1
    debug:
      msg: task1

この内容のPlaybookで実行すると、play2は全ホストで処理が開始する。

PLAY [play1] ********************************************************************

TASK [Gathering Facts] **********************************************************
ok: [cheddar]
ok: [cloud-dev]

TASK [task1] ********************************************************************
ok: [cloud-dev] => 
  msg: task1
ok: [cheddar] => 
  msg: task1

TASK [end host] *****************************************************************
skipping: [cloud-dev]

TASK [end host] *****************************************************************

TASK [task2] ********************************************************************
ok: [cloud-dev] => 
  msg: task2

TASK [task3] ********************************************************************
ok: [cloud-dev] => 
  msg: task3

PLAY [play2] ********************************************************************

TASK [task1] ********************************************************************
ok: [cloud-dev] => 
  msg: task1
ok: [cheddar] => 
  msg: task1

PLAY RECAP **********************************************************************
cheddar                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
cloud-dev                  : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

後続のPlayは実行されるので、イメージとしてはexit()ではなくreturnだね。


whenを使ってスキップしてももちろん良いが、実行しない後続のタスク定義数が多い場合はend_hsotでスパッと抜けるという実装もできる。
え、MISRA-C準拠で実装してる?playbookを?まさかそんな。。