Ansibleで他のplaybookをincludeした際にpathが通らないとき


#はじめに
Ansibleで、あるRole内から別のRoleを呼び出したいことがあります。
例えば、処理A→処理B→処理Cというplaybookを実行したいとき、処理Bについては既存Roleが利用できる場合です。

処理AもCも単独のRoleにして、playbookから

```
Roles:
  - RoleA
  - RoleB
  - RoleC
```
のように実行してもいいのですが、できれば分割したくない。
そんな時はRoleA+Cの中でRoleBを`include_role`モジュールで実行すればOKです。

しかし、

```Bash:ansible.cfg
[defaults]
roles_path = ./roles
```
で指定したroles_path以外に置いたRoleは`include_role`では呼び出せません。

その場合、Playbookから他のPlaybookを呼び出すモジュール`include`を使うことになります。

しかし、includeしたplaybookにtemplateモジュールなどが含まれていると少々面倒です。
実際に試してみましょう。

#sample
##ディレクトリ構成
```bash:ディレクトリ構成
ansible.cfg
site.yml
hosts/local
roles/
|--include_other_playbook/
|  |--tasks/
|  |  |--main.yml
other_roles/
|--template_role/
|  |--tasks/
|  |  |--main.yml
|  |--templates/
|  |  |--template.j2
```
## ansible.cfg
```Bash:ansible.cfg
[defaults]
roles_path = ./roles
```

## site.yml
```
---
- name: include_other_playbook
  hosts: targethost
  roles:
    - role: include_other_playbook
```

## include_other_playbook

```yaml:roles/include_other_playbook/tasks/main.yml
---
- debug: msg="1st"

- name: include template_role
  include: ../other_roles/template_role/tasks/main.yml

- debug: msg="2nd"
```

## template_role

```yaml:other_roles/template_role/tasks/main.yml
---
- name: template
  template:
    src: template.j2
    dest: /tmp/test
    owner: root
    group: root
    mode: '0644'
```


## 実行してみる

```bash:コマンド
$ ansible-playbook -i hosts/local -l targethost site.yml
```

```bash:結果
TASK [include_other_playbook : template dest=/tmp/test, src=template.j2, group=root, mode=0644, owner=root] ***
fatal: [targethost]: FAILED! => {"changed": false, "failed": true, "msg": "Unable to find 'template.j2' in expected paths."}
```

エラーになりました。テンプレートファイルが見つからないようです。
このtemplate_roleを単独実行すると成功するのに。。。

#対処法

Ansibleでは、Role内でのcopyモジュールやtemplateモジュールのsourceディレクトリの起点は、Roleディレクトリ配下のfiles,templatesに限定されてしまいます。

しかも、includeした場合でも、実行中のRoleディレクトリ配下が参照されます。
つまり、今回の場合、include_other_playbook/templatesにもtemplate.j2を配置しないと見つけてくれません。

```bash:ディレクトリ構成
ansible.cfg
site.yml
hosts/local
roles/
|--include_other_playbook/
|  |--tasks/
|  |  |--main.yml
|  |--templates/
|  |  |--template.j2
other_roles/
|--template_role/
|  |--tasks/
|  |  |--main.yml
|  |--templates/
|  |  |--template.j2
```
しかし、これだと何のためにRoleにしているのか分からなくなってしまいます。
そこで、単独実行に影響を与えず、includeした際にパスを通す方法をご紹介します。


# 修正後


## include_other_playbook

includeする際に、template_pathという変数を定義します。
[ここ](http://qiita.com/tomohiko_isobe/items/007e406d35428386c0d6)にも書きましたが、相対パスを定義するためには一工夫必要です。

```yaml:roles/include_other_playbook/tasks/main.yml
---
- debug: msg="1st"

- name: include template_role
  include: ../template_role/tasks/main.yml
  vars:
    template_path: "{{ inventory_dir }}/../roles/template_role/templates/template.j2"

- debug: msg="2nd"
```

## template_role
srcにはinclude側で指定した変数を適用します。
ただし、単独実行に影響を与えないよう、defaultフィルタでデフォルト値を設定しておきます。

```yaml:other_roles/template_role/tasks/main.yml
---
- name: template
  template:
    src: "{{ template_path | default('template.j2') }}"
    dest: /tmp/test
    owner: root
    group: root
    mode: '0644'
```

## 再度実行してみる

```bash:結果
TASK [include_other_playbook : debug msg=1st] **********************************
ok: [targethost] => {
    "msg": "1st"
}

TASK [include_other_playbook : template dest=/tmp/test, src={{ template_path | default('template.j2') }}, group=root, mode=0644, owner=root] ***
ok: [targethost]

TASK [include_other_playbook : debug msg=2nd] **********************************
ok: [targethost] => {
    "msg": "2nd"
}
```
成功しました。

# まとめ
Ansibleはpath周りで融通が利かないことが多いのが難点だと思いますが、今回の方法を使えばincludeがより柔軟にできると思います。