ansibleでは、コマンド実行結果をRegistered variableに突っ込んで便利に使えます
Registered Variablesとは?
ターゲットホストで実行したコマンドの結果を任意の変数に入れることができます。
使い方を見ていきます。ansibleのバージョンは 1.9.4
です。
サンプルとして、簡単なplaybookを作成しました。
---
- hosts: 127.0.0.1
connection: local
tasks:
- name: exec whoami
shell: whoami
register: result
- name: debug result var
debug: var=result
whoamiコマンドの実行結果をregisterキーワードで設定した result
変数に受け、次のdebug
タスクでハンドリングします。
変数の中身はどうなっている?
では、resultの中身はどうなっているのでしょうか? playbookの実行結果から見ていきます。
[ec2-user@ip-10-0-0-83 ~]$ ansible-playbook playbook.yml
PLAY [127.0.0.1] **************************************************************
GATHERING FACTS ***************************************************************
ok: [127.0.0.1]
TASK: [exec whoami] ***********************************************************
changed: [127.0.0.1]
TASK: [debug result var] ******************************************************
ok: [127.0.0.1] => {
"var": {
"result": {
"changed": true,
"cmd": "whoami",
"delta": "0:00:00.002182",
"end": "2016-01-08 04:26:38.950255",
"invocation": {
"module_args": "whoami",
"module_complex_args": {},
"module_name": "shell"
},
"rc": 0,
"start": "2016-01-08 04:26:38.948073",
"stderr": "",
"stdout": "ec2-user",
"stdout_lines": [
"ec2-user"
],
"warnings": []
}
}
}
PLAY RECAP ********************************************************************
127.0.0.1 : ok=3 changed=1 unreachable=0 failed=0
おそらく、この中でよく使うのは、rc、 stdout、stderr、stdout_linesの4つだと思います。
stdoutが複数行の場合、\n
を改行コードとしてstdoutに値がセットされます。
そして、その改行を1行づつバラしたのがstdout_linesです。
ディレクトリ一覧などを取って、ぐるぐる回す時など便利ですね。
複数回実行されるタスクの場合は?
サンプルとして、以下のようなplaybookを用意しました。
lsコマンドの結果を、statコマンドに渡しています。
---
- hosts: 127.0.0.1
connection: local
tasks:
- name: ls dir
shell: ls -U /tmp/dir
register: file_list
- name: stat file in dir
shell: stat /tmp/dir/{{ item }}
register: stat_list
with_items: file_list.stdout_lines
- debug: var=stat_list
結果は、こんな感じです。
変数stat_listの中に、resultsというキーで配列として実行結果がセットされています。
[ec2-user@ip-10-0-0-83 ~]$ ansible-playbook playbook.yml
PLAY [127.0.0.1] **************************************************************
GATHERING FACTS ***************************************************************
ok: [127.0.0.1]
TASK: [ls dir] ****************************************************************
changed: [127.0.0.1]
TASK: [shell stat /tmp/dir/{{ item }}] ****************************************
changed: [127.0.0.1] => (item=foo)
changed: [127.0.0.1] => (item=bar)
TASK: [debug var=stat_list] ***************************************************
ok: [127.0.0.1] => {
"var": {
"stat_list": {
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": "stat /tmp/dir/foo",
"delta": "0:00:00.002497",
"end": "2016-01-08 05:15:36.943282",
"invocation": {
"module_args": "stat /tmp/dir/foo",
"module_complex_args": {},
"module_name": "shell"
},
"item": "foo",
"rc": 0,
"start": "2016-01-08 05:15:36.940785",
"stderr": "",
"stdout": " File: ‘/tmp/dir/foo’\n Size: 0 \tBlocks: 0 IO Block: 4096 regular empty file\nDevice: ca01h/51713d\tInode: 1794 Links: 1\nAccess: (0664/-rw-rw-r--) Uid: ( 500/ec2-user) Gid: ( 500/ec2-user)\nAccess: 2016-01-08 04:58:45.291452795 +0000\nModify: 2016-01-08 04:58:45.291452795 +0000\nChange: 2016-01-08 04:58:45.291452795 +0000\n Birth: -",
"stdout_lines": [
" File: ‘/tmp/dir/foo’",
" Size: 0 \tBlocks: 0 IO Block: 4096 regular empty file",
"Device: ca01h/51713d\tInode: 1794 Links: 1",
"Access: (0664/-rw-rw-r--) Uid: ( 500/ec2-user) Gid: ( 500/ec2-user)",
"Access: 2016-01-08 04:58:45.291452795 +0000",
"Modify: 2016-01-08 04:58:45.291452795 +0000",
"Change: 2016-01-08 04:58:45.291452795 +0000",
" Birth: -"
],
"warnings": []
},
{
"changed": true,
"cmd": "stat /tmp/dir/bar",
"delta": "0:00:00.002559",
"end": "2016-01-08 05:15:36.999742",
"invocation": {
"module_args": "stat /tmp/dir/bar",
"module_complex_args": {},
"module_name": "shell"
},
"item": "bar",
"rc": 0,
"start": "2016-01-08 05:15:36.997183",
"stderr": "",
"stdout": " File: ‘/tmp/dir/bar’\n Size: 0 \tBlocks: 0 IO Block: 4096 regular empty file\nDevice: ca01h/51713d\tInode: 1795 Links: 1\nAccess: (0664/-rw-rw-r--) Uid: ( 500/ec2-user) Gid: ( 500/ec2-user)\nAccess: 2016-01-08 04:58:45.291452795 +0000\nModify: 2016-01-08 04:58:45.291452795 +0000\nChange: 2016-01-08 04:58:45.291452795 +0000\n Birth: -",
"stdout_lines": [
" File: ‘/tmp/dir/bar’",
" Size: 0 \tBlocks: 0 IO Block: 4096 regular empty file",
"Device: ca01h/51713d\tInode: 1795 Links: 1",
"Access: (0664/-rw-rw-r--) Uid: ( 500/ec2-user) Gid: ( 500/ec2-user)",
"Access: 2016-01-08 04:58:45.291452795 +0000",
"Modify: 2016-01-08 04:58:45.291452795 +0000",
"Change: 2016-01-08 04:58:45.291452795 +0000",
" Birth: -"
],
"warnings": []
}
]
}
}
}
タスクが失敗したときは?
タスクが失敗した時にRegistered variable はどうなるでしょうか?
こんな playbook を用意しました。fooというコマンドは存在しないので、エラーになります。
---
- hosts: 127.0.0.1
connection: local
tasks:
- shell: /usr/bin/foo
register: foo_result
ignore_errors: True
- debug: var=foo_result
実行結果です。rcに0以外が入り、stderrにエラーメッセージが入っていることが確認できます。
[ec2-user@ip-10-0-0-83 ~]$ ansible-playbook foo.yml
PLAY [127.0.0.1] **************************************************************
GATHERING FACTS ***************************************************************
ok: [127.0.0.1]
TASK: [shell /usr/bin/foo] ****************************************************
failed: [127.0.0.1] => {"changed": true, "cmd": "/usr/bin/foo", "delta": "0:00:00.001851", "end": "2016-01-08 05:42:46.712188", "rc": 127, "start": "2016-01-08 05:42:46.710337", "warnings": []}
stderr: /bin/sh: /usr/bin/foo: No such file or directory
...ignoring
TASK: [debug var=foo_result] **************************************************
ok: [127.0.0.1] => {
"var": {
"foo_result": {
"changed": true,
"cmd": "/usr/bin/foo",
"delta": "0:00:00.001851",
"end": "2016-01-08 05:42:46.712188",
"invocation": {
"module_args": "/usr/bin/foo",
"module_complex_args": {},
"module_name": "shell"
},
"rc": 127,
"start": "2016-01-08 05:42:46.710337",
"stderr": "/bin/sh: /usr/bin/foo: No such file or directory",
"stdout": "",
"stdout_lines": [],
"warnings": []
}
}
}
PLAY RECAP ********************************************************************
127.0.0.1 : ok=3 changed=1 unreachable=0 failed=0
ちなみに、公式サイトにNoteとして以下のような記述があります。
If a task fails or is skipped, the variable still is registered with a failure or skipped status, the only way to avoid registering a variable is using tags.
タスクが失敗したり、スキップしたりした場合も変数は設定されます。失敗(or スキップ)したからと言って変数が設定されないわけではないので、playbookの中で、whenキーワードで変数の宣言自体をハンドリングする(is defined)ようなtaskには注意が必要かもしれません。
まとめ
Registered variableを使うと、ターゲットホストの状態をハンドリングしながらplaybookが書けるので、とても便利です
rcをハンドリングして細かい条件分岐をしたり、複数回実行される時のパターンのように直接with_itemsに突っ込んだり、用途はいろいろ考えられます。
実際によくやる手としては、ファイルやディレクトリをチェックしてその後の処理を分岐させたりすることなどが多いです。
そのうち、よくやるTips集などをまとめてみたいと思います。
参考
http://docs.ansible.com/ansible/playbooks_variables.html#registered-variables
http://docs.ansible.com/ansible/playbooks_conditionals.html#register-variables