Ansible マジック変数の一覧と内容

Ansible Playbookで利用できる、マジック変数たち。

公式docに一覧がなかったので、一覧にしてみた。


# マジック変数とは

Ansible Playbookには `gather_facts:False` でも使える変数がある。

「定義済み変数」であり、**マジック変数** と呼ばれる。

主に、インベントリに関する情報が得られる。

 - [Ansible doc | Magic Variables](http://docs.ansible.com/playbooks_variables.html#magic-variables-and-how-to-access-information-about-other-hosts)



# マジック変数の一覧

値は、Playbookを実行しているターゲットマシン毎にセットされる。

| 変数名 | 内容 |
|--------|-------|
| `group_names` | このターゲットマシンが属する全グループの一覧 |
| `groups` | グループ-to-ホストのマッピング |
| `inventory_hostname` | インベントリに書かれたホスト名 |
| `inventory_hostname_short` | `inventory_hostname`の最初の "." まで |
| `play_hosts` | Playbookが適用されるホスト一覧 |
| `inventory_dir` | インベントリファイルのあるディレクトリパス |
| `inventory_file` | インベントリファイルのベース名 |

`groups` は、グループ階層（グループのグループ）を平たく展開し

        グループX：[ ホスト1, ホスト2, ... ]
        グループY：[ ホスト1, ホスト2, ホスト3, ... ]

といった形式で、値を保持している。

`inventory_hostname_short` は、`inventory_hostname`をFQDNと見なしてドメイン部分を除いたホスト名。ただ、IPv4 アドレス xx.yy.zz.ww では "xx" となってしまう。思いもよらないバグとならないよう、使わない方がよいかもしれない。

`inventory_{dir,file}`は、`{{ inventory_dir ~ "/" ~ inventory_file }}` とすることで、フルパスを構成できる。


### Factsのためのマジック変数

重要なマジック変数。

| 変数名 | 内容 |
|--------|-------|
| `hostvars` | Playbookを適用する全マシンの facts の集合 |

`gather_facts:True` の場合、`hostvars` には、Playbook を実行する全マシンの facts が含まれる。

`gather_facts:False` だと、値は空になる（未定義ではない）。


★ hostvarsを`{{ hostvars["host名"] }}`で参照すると、参照時点で以下のメンバが追加される模様。

| 追加されるメンバ | 内容 |
|---------------|------|
| ansible_connection | sshやlocalなどコネクションタイプ |
| group_name | 前述のマジック変数 |
| inventory_hostname | 前述のマジック変数 |
| inventory_hostname_short | 前述のマジック変数 |

ただ、ドキュメントに記述を見つけられなかった。

公式仕様なのか、たまたまなのか、利用するなら念のため本家に質問したほうがよいかもしれない。


### 予約済みマジック変数

値は入っていない。ユーザが使ってはいけない変数らしい。

| 変数名 | 内容 |
|--------|-------|
| `environment` | 予約済み変数 |



# 実際に取得できた値

小さな Playbook を書いて、マジック変数の値を調べてみた。

`groups` の動きを見るため、インベントリに定義したグループは、無理やり階層化している。こんな感じ。

        test-servers
            +---- test-servers1
            |         +---- localhost1
            |
            +---- test-servers2
                      +---- localhost2


## 実行結果

まず、取得できた値から示す。

`gather_facts:False`で実行している。

```shell-session
[ansibleman@ponet ~]$ ansible-playbook -i hosts magic_vars.yml

PLAY [test-servers] ***********************************************************

TASK: [Facts | ansible_hostname] **********************************************
ok: [localhost1] => {
    "ansible_hostname": "{{ ansible_hostname }}"
}
ok: [localhost2] => {
    "ansible_hostname": "{{ ansible_hostname }}"
}

TASK: [Facts | ansible_default_ipv4] ******************************************
ok: [localhost1] => {
    "ansible_default_ipv4": "{{ ansible_default_ipv4 }}"
}
ok: [localhost2] => {
    "ansible_default_ipv4": "{{ ansible_default_ipv4 }}"
}

TASK: [Facts | ansible_env] ***************************************************
ok: [localhost1] => {
    "ansible_env": "{{ ansible_env }}"
}
ok: [localhost2] => {
    "ansible_env": "{{ ansible_env }}"
}

TASK: [Magic | group_names] ***************************************************
ok: [localhost1] => {
    "group_names": [
        "test-servers",
        "test-servers1"
    ]
}
ok: [localhost2] => {
    "group_names": [
        "test-servers",
        "test-servers2"
    ]
}

TASK: [Magic | groups] ********************************************************
ok: [localhost1] => {
    "groups": {
        "all": [
            "localhost1",
            "localhost2"
        ],
        "test-servers": [
            "localhost1",
            "localhost2"
        ],
        "test-servers1": [
            "localhost1"
        ],
        "test-servers2": [
            "localhost2"
        ],
        "ungrouped": []
    }
}
ok: [localhost2] => {
    "groups": {
        "all": [
            "localhost1",
            "localhost2"
        ],
        "test-servers": [
            "localhost1",
            "localhost2"
        ],
        "test-servers1": [
            "localhost1"
        ],
        "test-servers2": [
            "localhost2"
        ],
        "ungrouped": []
    }
}

TASK: [Magic | inventory_hostname] ********************************************
ok: [localhost1] => {
    "inventory_hostname": "localhost1"
}
ok: [localhost2] => {
    "inventory_hostname": "localhost2"
}

TASK: [Magic | inventory_hostname_short] **************************************
ok: [localhost1] => {
    "inventory_hostname_short": "localhost1"
}
ok: [localhost2] => {
    "inventory_hostname_short": "localhost2"
}

TASK: [Magic | play_hosts] ****************************************************
ok: [localhost1] => {
    "play_hosts": [
        "localhost1",
        "localhost2"
    ]
}
ok: [localhost2] => {
    "play_hosts": [
        "localhost1",
        "localhost2"
    ]
}

TASK: [Magic | inventory_dir] *************************************************
ok: [localhost1] => {
    "inventory_dir": "/home/ansibleman"
}
ok: [localhost2] => {
    "inventory_dir": "/home/ansibleman"
}

TASK: [Magic | inventory_file] ************************************************
ok: [localhost1] => {
    "inventory_file": "hosts"
}
ok: [localhost2] => {
    "inventory_file": "hosts"
}

TASK: [Magic for Facts | hostvars] ********************************************
ok: [localhost1] => {
    "hostvars": {
        "localhost1": {},
        "localhost2": {}
    }
}
ok: [localhost2] => {
    "hostvars": {
        "localhost1": {},
        "localhost2": {}
    }
}

TASK: [Reserved | environment] ************************************************
ok: [localhost1] => {
    "environment": {}
}
ok: [localhost2] => {
    "environment": {}
}

PLAY RECAP ********************************************************************
localhost1                 : ok=12   changed=0    unreachable=0    failed=0
localhost2                 : ok=12   changed=0    unreachable=0    failed=0
```

### 若干の説明

 - Factsは、`gather_facts:False` にしたので、未定義変数の状態。debugモジュールでは、未定義変数は文字列として処理される。

 - `group_name` では、localhost1 と localhost2 で、ちゃんと別々のグループ一覧を取得できている。

 - `play_hosts` では、Playbookを実行する全ターゲットマシン名を取得できている。

少し脇道にそれるが、`ansible_hostname`は、ターゲットマシンの本当のホスト名になる。Factsの一つなので、`gather_facts:True` にすると、ちゃんとターゲットマシン上で取得して、正しい値が入る。


## Playbook

上記の結果を得るために書いた Playbook は、こちら。

```magic_vars.yml
--- # file: magic_vars.yml
- hosts: test-servers

  gather_facts: "{{ check_env | default(False) }}"

  tasks:

    # Facts
    - name: Facts | ansible_hostname
      debug: var=ansible_hostname
    - name: Facts | ansible_default_ipv4
      debug: var=ansible_default_ipv4
    - name: Facts | ansible_env
      debug: var=ansible_env

    # Magic variables
    - name: Magic | group_names
      debug: var=group_names
    - name: Magic | groups
      debug: var=groups
    - name: Magic | inventory_hostname
      debug: var=inventory_hostname
    - name: Magic | inventory_hostname_short
      debug: var=inventory_hostname_short
    - name: Magic | play_hosts
      debug: var=play_hosts
    - name: Magic | inventory_dir
      debug: var=inventory_dir
    - name: Magic | inventory_file
      debug: var=inventory_file

    # Magic variable for Facts
    - name: Magic for Facts | hostvars
      debug: var=hostvars

    # Reserved magic variable
    - name: Reserved | environment
      debug: var=environment
```

`check_env` 変数をコマンドラインの `-e` オプションから与えると、Playbookを書き換えなくても、`gather_facts:`を制御できるようにしている。

以下のように実行する。

    ansible-playbook -i hosts magic_vars.yml

Factsを収集したいときには、こう。

    ansible-playbook -i hosts magic_vars.yml -e "check_env=True"


## インベントリと /etc/hosts

利用したインベントリファイルはこちら。

```ini:hosts
[test-servers1]
localhost1  ansible_connection=local

[test-servers2]
localhost2  ansible_connection=local

[test-servers:children]
test-servers1
test-servers2
```

/etc/hosts で、インベントリ上のホスト名とIPアドレスを対応付け。

```/etc/hosts
127.0.0.1   localhost localhost.localdomain
127.0.0.1   localhost1
127.0.0.1   localhost2
```



# 活用例｜ホスト名の一括設定

インベントリに書かれたホスト名を使って、全ターゲットマシンにホスト名を一括設定してみる。

```set_hostname.yml
--- # file: set_hostname.yml
- hosts: test-servers
  gather_facts: False
  tasks:
    - name: Set the hostname using the inventory
      hostname: name={{ inventory_hostname }}
      sudo: yes
```

インベントリに書いてあるホスト名は、/etc/hostsなどに登録し、IPアドレスと対応させておく。

こんな感じで実行。

        $ ansible-playbook -i hosts set_hostname.yml

沢山の仮想マシンを新規に立ち上げ、初期セットアップするときに便利かもしれない。`gather_facts:False`なので、動作も軽快。