zaki work log

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

[Ansible] リスト連結時にdefaultを使って未定義の場合は空リストとして処理する

リストを結合、ただし未定義だったら何もしない(空リストを追加)、は、default()フィルタを使えばOK

記述例

nikuというリストを+で連結しようとしているが、nikuリストは未定義の場合もあり、その場合はdefault([])で空リストを連結する。

  tasks:
    - name: curry
      debug:
        msg: "{{ item }}"
      loop: "{{ yasai + (niku | default([])) }}"

これで、yasaiリスト(これは必須/未定義はエラーという前提)に、nikuリストがあれば連結・未定義であればdefault([])によって空リスト=yasaiリストのまま、というloop処理となる。

動作確認

---
- hosts: localhost
  gather_facts: false
  vars:
    yasai:
      - carrot
      - potato
  become: false
  tasks:
    - name: curry
      debug:
        msg: "{{ item }}"
      loop: "{{ yasai + (niku | default([])) }}"

↑の、yasaiしかないのにnikuも参照しようとしてるplaybookを実行すると以下の通り。

[zaki@manager-dev sample-ansible]$ ansible-playbook list.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

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

TASK [curry] *******************************************************************
ok: [localhost] => (item=carrot) => 
  msg: carrot
ok: [localhost] => (item=potato) => 
  msg: potato

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

nikuのリスト定義がないので、default([])のフィルタが無ければ以下のようにundefinedエラーとなってしまうが、default([])によって↑のようにyasaiリストだけでちゃんと実行できている。

  msg: |-
    The task includes an option with an undefined variable. The error was: 'item' is undefined

で、これ使って何するの?

例えば

  • 全ホストでYumで入れる必須パッケージをgroup_vars/all.ymlrequired_packagesとして定義
  • ホストグループ毎に異なる追加パッケージをgroup_vars/***.ymloptional_packagesとして定義
    • 定義の無いグループも有り
  • これらを一つのYumモジュールを使ったtaskでインストール

みたいな、playbookは共通でinventoryの変数定義部分のみで条件分けすることができる。

curryの例でmsgモジュールだと、こんな感じ。

  • inventory.ini
[carnivore]
192.168.0.121

[vegetarian]
192.168.0.125
  • playbook.yml
---
- hosts: all
  gather_facts: false
  become: false
  tasks:
    - name: curry
      debug:
        msg: "{{ item }}"
      loop: "{{ yasai + (niku | default([])) }}"
  • group_vars/all.yml
yasai:
  - carrot
  - potato
  • group_vars/carnivore.yml
niku:
  - beef
  - pork
  • 実行結果
[zaki@manager-dev sample-ansible]$ ansible-playbook playbook.yaml -i inventory.ini 

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

TASK [curry] *******************************************************************
ok: [192.168.0.125] => (item=carrot) => 
  msg: carrot
ok: [192.168.0.125] => (item=potato) => 
  msg: potato
ok: [192.168.0.121] => (item=carrot) => 
  msg: carrot
ok: [192.168.0.121] => (item=potato) => 
  msg: potato
ok: [192.168.0.121] => (item=beef) => 
  msg: beef
ok: [192.168.0.121] => (item=pork) => 
  msg: pork

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

[zaki@manager-dev sample-ansible]$ 

この通り、allに対するtaskのみで、vegetarian(192.168.0.125)は野菜のみ、carnivore(192.168.0.121)は野菜も肉の両方を参照する動作ができる。


(参考) 単純なリスト結合

年末に書いたこちらをご参考

qiita.com


(参考)defaultのalias

d()がaliasに定義されているので、短く記述することも可能。
前述の例であれば、↓のようにも書ける。

    - name: curry
      debug:
        msg: "{{ item }}"
      loop: "{{ yasai + (niku | d([])) }}"