ansible-consoleを使おう

# ansible-consoleの概要

ansible-consoleコマンドはAnsible 2.1で追加された、これまでアドホックコマンド（ansibleコマンド）で行っていた作業をインタラクティブに実行することができるものである。
https://github.com/dominis/ansible-shell からAnsible本体に取り込まれ、コマンド名がansible-shellからansible-consoleに変更されている。

# ansible-consoleの起動

ansible-consoleコマンドは通常インベントリファイルを指定して起動する。
インベントリファイルを指定するオプションはアドホックコマンドやansible-playbookコマンドと同じく-iである。
その他、アドホックコマンドと大体同じオプションが指定できる。例えばAnsible Vaultのパスワードを指定する--ask-vault-passなど。

```
$ ansible-console -i hosts
Welcome to the ansible console.
Type help or ? to list commands.

user@all (3)[f:5]$
```

この通りansible-consoleを起動すると`user@all (3)[f:5]$`というプロンプトのインタラクティブシェルになる。
このプロンプトは`<接続先のユーザ>@<処理対象とするグループかホスト> (<処理対象のホスト数>)[f:<並列で処理されるホスト数>]$`を表している。

今サンプルとしてインベントリファイルは以下の中身としている。

```txt:hosts
[a-group]
a1

[b-group]
b1
b2
```

# モジュールの実行

モジュールの実行は簡単である。
例えばpingと入力してエンターを押すと、処理対象ホストに対してpingモジュールが実行される。

```
user@all (3)[f:5]$ ping
b2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
a1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
b1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
```

引数が必要なモジュールは、ちょうどplaybookで1行で指定する時のように、ただしモジュール名の後のコロンなしで入力する。
以下の例のようにAnsible内の変数も使用できる。

```
user@all (3)[f:5]$ copy src=file dest=~/{{ inventory_hostname }}.txt
a1 | SUCCESS => {
    "changed": true,
    "checksum": "7afb6961cef6e8bc0876224c46bc5692e0d25a48",
    "dest": "/home/user/a1.txt",
    "gid": 1000,
    "group": "user",
    "md5sum": "1c3b6b8100773f62601b7b94cf400077",
    "mode": "0664",
    "owner": "user",
    "secontext": "unconfined_u:object_r:user_home_t:s0",
    "size": 17,
    "src": "/home/user/.ansible/tmp/ansible-tmp-1464266411.86-130941408708345/source",
    "state": "file",
    "uid": 1000
}
b2 | SUCCESS => {
    "changed": true,
    "checksum": "7afb6961cef6e8bc0876224c46bc5692e0d25a48",
    "dest": "/home/user/b2.txt",
    "gid": 1000,
    "group": "user",
    "md5sum": "1c3b6b8100773f62601b7b94cf400077",
    "mode": "0664",
    "owner": "user",
    "secontext": "unconfined_u:object_r:user_home_t:s0",
    "size": 17,
    "src": "/home/user/.ansible/tmp/ansible-tmp-1464266411.86-11663736480472/source",
    "state": "file",
    "uid": 1000
}
b1 | SUCCESS => {
    "changed": true,
    "checksum": "7afb6961cef6e8bc0876224c46bc5692e0d25a48",
    "dest": "/home/user/b1.txt",
    "gid": 1000,
    "group": "user",
    "md5sum": "1c3b6b8100773f62601b7b94cf400077",
    "mode": "0664",
    "owner": "user",
    "secontext": "unconfined_u:object_r:user_home_t:s0",
    "size": 17,
    "src": "/home/user/.ansible/tmp/ansible-tmp-1464266411.86-116685457447402/source",
    "state": "file",
    "uid": 1000
}
```

lookupプラグインも当然使用できる。

```
user@all (3)[f:5]$ echo {{ lookup('pipe', 'date') }}
a1 | SUCCESS | rc=0 >>
Thu May 26 23:13:02 JST 2016

b1 | SUCCESS | rc=0 >>
Thu May 26 23:13:02 JST 2016

b2 | SUCCESS | rc=0 >>
Thu May 26 23:13:02 JST 2016

```

ここでechoなんて使っているが、存在しないモジュール名（※）を指定した場合はshellモジュールの引数として扱われる。
あるいは存在するモジュール名（※）であっても最初に!を付けるとshellモジュールの引数として扱ってくれる。
（※正確には、モジュール名、および後で紹介するansible-console用のいくつかのコマンド名）

```
user@all (3)[f:5]$ !hostname
b2 | SUCCESS | rc=0 >>
b2

a1 | SUCCESS | rc=0 >>
a1

b1 | SUCCESS | rc=0 >>
b1
```

set_factで変数に値をセットしてから使用するなんてことも可能。

```
user@all (3)[f:5]$ set_fact a=1
a1 | SUCCESS => {
    "ansible_facts": {
        "a": "1"
    },
    "changed": false
}
b1 | SUCCESS => {
    "ansible_facts": {
        "a": "1"
    },
    "changed": false
}
b2 | SUCCESS => {
    "ansible_facts": {
        "a": "1"
    },
    "changed": false
}
user@all (3)[f:5]$ echo {{ a }}
a1 | SUCCESS | rc=0 >>
1

b1 | SUCCESS | rc=0 >>
1

b2 | SUCCESS | rc=0 >>
1

```

嬉しい点として、ansible-consoleのインタラクティブシェル内ではTABキーやCtrl+iでの補完が効くことが挙げられる。（モジュールのパラメタが何種類の値からの選択の場合までは補完してくれなかったが）
また、コマンドの実行履歴は~/.ansible-console_historyに保存されており、Ctrl+rやCtrl+sの履歴検索やCtrl+p（↑キー）やCtrl+n（↓キー）の履歴の1つ移動も使用できる。

# その他の機能

ansible-consoleはモジュール以外のいくつかのコマンドを受け付ける。

cdコマンドは処理対象のグループあるいはホストを絞り込む。

```
user@all (3)[f:5]$ cd a-group
user@a-group (1)[f:5]$ cd b1
user@b1 (1)[f:5]$
```

プロンプトの`<処理対象とするグループかホスト> (<処理対象のホスト数>)`の部分が変化したことがわかる。

listコマンドは処理対象のホストの一覧を返す。

```
user@b1 (1)[f:5]$ list
b1
user@b1 (1)[f:5]$ cd b-group
user@b-group (2)[f:5]$ list
b1
b2
```

list groupsコマンドは選択可能なグループ名の一覧を返す。

```
user@b1 (1)[f:5]$ list groups
a-group
all
b-group
ungrouped
```

remote_userコマンドは接続先のユーザを変更する。

```
user@b1 (1)[f:5]$ remote_user root
root@b1 (1)[f:5]$
```

forksコマンドあるいはserialコマンドは並列で処理されるホスト数を変更する。

```
root@b1 (1)[f:5]$ forks 10
root@b1 (1)[f:10]$
```

verbosityコマンドはverboseモードのレベルを変更する。

```
root@b1 (1)[f:10]$ ping
b1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
root@b1 (1)[f:10]$ verbosity 3
verbosity level set to 3
root@b1 (1)[f:10]$ ping
<b1> ESTABLISH SSH CONNECTION FOR USER: root
<b1> SSH: EXEC sshpass -d12 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o User=root -o ConnectTimeout=10 -o ControlPath=/home/user/.ansible/cp/ansible-ssh-%h-%p-%r b1 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1464270265.16-274013824904817 `" && echo ansible-tmp-1464270265.16-274013824904817="` echo $HOME/.ansible/tmp/ansible-tmp-1464270265.16-274013824904817 `" ) && sleep 0'"'"''
<b1> PUT /tmp/tmpiHTfg6 TO /root/.ansible/tmp/ansible-tmp-1464270265.16-274013824904817/ping
<b1> SSH: EXEC sshpass -d12 sftp -o BatchMode=no -b - -C -o ControlMaster=auto -o ControlPersist=60s -o User=root -o ConnectTimeout=10 -o ControlPath=/home/user/.ansible/cp/ansible-ssh-%h-%p-%r '[b1]'
<b1> ESTABLISH SSH CONNECTION FOR USER: root
<b1> SSH: EXEC sshpass -d12 ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o User=root -o ConnectTimeout=10 -o ControlPath=/home/user/.ansible/cp/ansible-ssh-%h-%p-%r -tt b1 '/bin/sh -c '"'"'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /root/.ansible/tmp/ansible-tmp-1464270265.16-274013824904817/ping; rm -rf "/root/.ansible/tmp/ansible-tmp-1464270265.16-274013824904817/" > /dev/null 2>&1 && sleep 0'"'"''
b1 | SUCCESS => {
    "changed": false,
    "invocation": {
        "module_args": {
            "data": null
        },
        "module_name": "ping"
    },
    "ping": "pong"
}
```

他にsudoなどを使用する環境のためにbecome系コマンドも使用できる。

# ansible-consoleの終了

exitを実行するか、Ctrl+cあるいはCtrl+dでansible-consoleのインタラクティブシェルは終了する。

# インベントリファイルを指定しない場合

おまけ。
ansible-consoleをインベントリファイルを指定せずに単体で起動した場合、localhostだけが使用できるとのメッセージが出力され、`cd localhost`するとlocal接続でlocalhostだけを対象に処理することができるようになる。

```
$ ansible-console
 [WARNING]: provided hosts list is empty, only localhost is available

Welcome to the ansible console.
Type help or ? to list commands.

user@all (0)[f:5]$ cd localhost
user@localhost (1)[f:5]$ ping
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
```

というわけで昔書いた[Ansibleをインベントリファイルなしで使おう](http://qiita.com/yunano/items/061645a66b3df7e477a2)のようなこともできる。

インベントリファイルの代わりにコンマ区切りの一連のホスト名も受け付ける。この辺りはアドホックコマンドやansible-playbookコマンドと同じだろう。