Ansible の Template 機能の紹介

```
 __________________
< template について書くよ >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

Ansible というかほぼ Jinja2 なのだけれども、ちょいちょいググってしまうものをまとめてみます（これをまとめるまで知らなかったものもあります、たまにはドキュメントを見直すのが良いです）。
template module だけでなく task 定義で使う YAML 内でも使われています。

(Qiita の syntax highlight は Jinja にも対応しているのですね)

## Ansible Managed

まずはこれ、つい使いたくなるけど罠があるので私は使わない。(Subversion の `$Id$` は便利なのに)
http://docs.ansible.com/ansible/intro_configuration.html#ansible-managed

テンプレートファイルに

```jinja
{{ ansible_managed }}
```

と書いておけば実行時に ansible.cfg などで設定したフォーマットにしたがって置換してくれます。いつ誰が更新したかとかを入れられます。

```jinja
ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
```

が、この機能は曲者で、ファイルに変更があったかどうかを比べるのにこの行まで含んでしまうから timestamp なんかを入れてしまった日には本来何も変更してないはずなのにファイルが入れ替えられてしまい。毎回 notify handler が発動してしまうという悲劇を産みます。気をつけましょう。


## default

ansible のいつしかの version からデフォルトでは未定義の変数を参照するとエラーで終了してしまいますが、指定しない場合のデフォルト値を指定しておけば大丈夫ということで、特に何も指定がなければこの値ですよというのを指定します。

`role` の `default/main.yml` を使うという手もありますが、参照すべきファイルが増えるのは嬉しくないので単純な値ならこれで。

```jinja
{{ some_variable | default('foo bar') }}
```

task の中で便利に使えるのが 1.8 で登場した default(ommit) ですね（と言ってもこれ書いてて知った）。
http://docs.ansible.com/ansible/playbooks_filters.html#omitting-undefined-variables-and-parameters に例がありますが、ファイルのパーミッションやオーナーなど、with_items でループ処理したい時、殆どは指定が不要なのにひとつだけ指定したいといった場合、それまでは全部の item に値をもたせるか、default でデフォルト値を設定する必要がありましたが、`ommit` を使えば未定義の場合はその項目の指定自体がないことにできます。便利（実は今知った...）

## リスト操作

### min, max

http://docs.ansible.com/ansible/playbooks_filters.html#list-filters
リストの中の最小値、最大値を取り出してくれます

### unique, union,...

http://docs.ansible.com/ansible/playbooks_filters.html#set-theory-filters
2つのリストの和とか差とか

### random

http://docs.ansible.com/ansible/playbooks_filters.html#random-number-filter
リストの中からランダムに選択されます。 数字内のランダムな値、ステップ数を指定したランダムな値（100のうち10刻みでのランダムな値）とかを取り出せます。
cron モジュールとセットで、複数台のサーバーで同時に実行されるのを避ける場合に便利そうです。ドキュメントにも cron の例があります。

```jinja
{{ 60 | random(step=5) }} * * * * /some/command
```

### shuffle

http://docs.ansible.com/ansible/playbooks_filters.html#shuffle-filter
シャッフル

### 動作確認

```yaml:test.yml
- hosts: all
  gather_facts: no
  vars:
    aaa:
      - A
      - B
      - C
    bbb:
      - A
      - B
      - C
      - X
      - Y
      - Z
    ccc: [1,2,3,4,5]
    ddd: [1,1,2,2,3,3,4,4,5,5]
    eee: [1,2,3]
    fff: [3,4,5]
  tasks:
    - debug: msg="[aaa] {{ aaa }}"
    - debug: msg="[bbb] {{ bbb }}"
    - debug: msg="[ccc] {{ ccc }}"
    - debug: msg="[ddd] {{ ddd }}"
    - debug: msg="[random] {{ aaa | random }}"
    - debug: msg="[unique] {{ ddd | unique }}"
    - debug: msg="[union1] {{ aaa | union(bbb) }}"
    - debug: msg="[union2] {{ aaa | union(ccc) }}"
    - debug: msg="[intersect] {{ bbb | intersect(aaa) }}"
    - debug: msg="[difference] {{ bbb | difference(aaa) }}"
    - debug: msg="[symmetric_difference] {{ eee | symmetric_difference(fff) }}"
    - debug: msg="[max] {{ ccc | max}}"
    - debug: msg="[min] {{ ccc | min}}"
    - debug: msg="[shuffle] {{ bbb | shuffle}}"
```

を用意して

```bash
ansible-playbook -i localhost, test.yml
```

とすれば動作確認ができます。

## 計算

http://docs.ansible.com/ansible/playbooks_filters.html#math
べき乗や平方根、対数が使えるようです

## その他のフィルター

IPアドレス関連やコメント用、ハッシュ処理などあるので確認してみてください。
http://docs.ansible.com/ansible/playbooks_filters.html

join は使う場面ありますよね

```jinja
{{ list | join(" ") }}
```

この join するリストがシンプルなリストならこれで良いのですが
ハッシュ/ディクショナリのリストだった場合にどうするかというと

```yaml
aaa:
  - en: apple
    ja: ringo
  - en: orange
    ja: mikan
  - en: grape
    ja: budou
```

という変数があって、`en` だけや `ja` だけを join したい場合は次のようにします
2つ繋げることもできるんです

```jinja
{{ aaa | map(attribute='en') | join(',') }}
{{ aaa | map(attribute='ja') | join(',') }}
```

結果

```
apple,orange,grape
ringo,mikan,budou
```

map() については http://jinja.pocoo.org/docs/dev/templates/#map を。


## Loop 処理

最後にループ処理です

task での Loop はこちらにいろいろ載ってます、ヘぇ、そんなのもあったんだぁという感じなので読んでみましょう。`task` の `with_xxx` には沢山の種類があって、`when` は `with_xxx` のそれぞれの値に対しても使えます。
http://docs.ansible.com/ansible/playbooks_loops.html

テンプレートファイル内での Loop 処理についてはこちら
http://jinja.pocoo.org/docs/dev/templates/

```jinja
{% for item in items %}
{% if item.key1 == 'AAA' %}
  {{ item.key1 }} {{ item.key2 }}
{% elif item.key1 == 'BBB' %}
  ...
{% else %}
  ...
{% endif %}
{% endfor %}
```

が基本でしょうか。`if`, `elif`, `else`, `endif` です。（いろんな言語使ってるといつも悩みますね、`elif` のところ。あれどれだっけ？と）

Loop のアイテムごとに番号を振りたいと思ったら `loop.index` (1から始まる), `loop.index0` (0から始まる) という変数が使えます
最初と最後を示す `loop.first`, `loop.last` という Boolean 変数もあります

```jinja
{% for item in items %}
{% if loop.first %}Begin{% endif %}
{{ loop.index }}: {{ item }}
{% if loop.last %}End{% endif %}
{% endfor %}
```

詳細はこちら
http://jinja.pocoo.org/docs/dev/templates/#list-of-control-structures


## エスケープ

template ファイル内に `{{` とか `{%` が含まれる場合は `raw` を使います

```jinja
{% raw %}
...
{% endraw %}
```

## まとめ

とりとめもなく書いてしまいましたが、また思いついたら書くことにします。
まだカレンダーに空きがあるようなので。