リストを結合、ただし未定義だったら何もしない(空リストを追加)、は、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.yml
にrequired_packages
として定義 - ホストグループ毎に異なる追加パッケージを
group_vars/***.yml
にoptional_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)は野菜も肉の両方を参照する動作ができる。
(参考) 単純なリスト結合
年末に書いたこちらをご参考
(参考)defaultのalias
d()
がaliasに定義されているので、短く記述することも可能。
前述の例であれば、↓のようにも書ける。
- name: curry debug: msg: "{{ item }}" loop: "{{ yasai + (niku | d([])) }}"
変数まわり緩い ansible (jinja2) では default filter はなくてはならない存在ですね。そして可読性等の観点で難ありという害はあるものの、僕は最近は default() の alias の d() を使うことがほとんどです: https://t.co/J6djimdrnL
— ssato (@satoru_satoh) 2020年5月10日