Edited at

ChefとAnsibleでのhash|mappingデータに対する多重loopの比較

More than 1 year has passed since last update.

hash|mappingの構造に沿って多重ループを行う例をChefとAnsibleで示す。


Chefの例


レシピ


recipes/default.rb

node.default['var_test']['a']['aa']['aaa'] = 'AAA'

node.default['var_test']['a']['aa']['aab'] = 'AAB'
node.default['var_test']['a']['ab']['aba'] = 'ABA'
node.default['var_test']['b']['ba']['baa'] = 'BAA'
node.default['var_test']['c']['ca']['caa'] = 'CAA'
node.default['var_test']['c']['cb']['cba'] = 'CBA'

node['var_test'].each do |key1, val1|
val1.each do |key2, val2|
val2.each do |key3, val3|
log "key1:#{key1} key2:#{key2} key3:#{key3} val:#{val3}"
end
end
end


hashに対する.eachを用いて多重ループをシンプルに記述可能。

なお、attributeは以下の様な書き方やJSON渡しも可能。

node.default['var_test'] = {

'a' => {
'aa' => {
'aaa' => 'AAA',
'aab' => 'AAB',
},
...


実行結果

$ kitchen conv

-----> Starting Kitchen (v1.21.2)
-----> Converging <chef-loop-centos-7>...
Preparing files for transfer
Preparing dna.json
Resolving cookbook dependencies with Berkshelf 7.0.2...
Removing non-cookbook files before transfer
Preparing validation.pem
Preparing client.rb
-----> Chef Omnibus installation detected (install only if missing)
Transferring files to <chef-loop-centos-7>
Starting Chef Client, version 14.3.37
resolving cookbooks for run list: ["chef_loop::default"]
Synchronizing Cookbooks:
- chef_loop (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 6 resources
Recipe: chef_loop::default
* log[key1:a key2:aa key3:aaa val:AAA] action write

* log[key1:a key2:aa key3:aab val:AAB] action write

* log[key1:a key2:ab key3:aba val:AAA] action write

* log[key1:b key2:ba key3:baa val:BAA] action write

* log[key1:c key2:ca key3:caa val:CAA] action write

* log[key1:c key2:cb key3:cba val:CBA] action write

Running handlers:
Running handlers complete
Chef Client finished, 6/6 resources updated in 01 seconds
Downloading files from <chef-loop-centos-7>
Finished converging <chef-loop-centos-7> (0m7.12s).
-----> Kitchen is finished. (0m10.56s)

簡潔に実行されている

実行順序もデータ順が保たれている。

Chefでは、構造化されたデータをループで階層毎に処理していくことで、可読性、メンテナンス性、可搬性、抽象化といったメリットが得られていると思える。


Ansibleの例

Ansibleのplaybookはプログラミング言語では無く、データ構造を用いて無理やりロジックを実装しているので、ロジックが必要なケースではとても読みにくい記述となってしまう。


playbook


recipes/default.yml

---

- hosts: all
vars:
var_test:
a:
aa:
aaa: AAA
aab: AAB
ab:
aba: ABA
b:
ba:
baa: BAA
c:
ca:
caa: CAA
cb:
cba: CBA

tasks:
- name: "loop0"
include: loop1.yml
with_dict : "{{ vars.var_test }}"


データはChefの例と同じ構造体

ハッシュ|マッピングに対するループはwith_dictを使用するが、内容にブロックを指定できず、includeでループ内容を別ファイル(ここではloop1.yml)に分ける必要がある。

その為、可読性、保守性が低い。

参照:Ansible block単位でLoopしたい!でも、出来ない ので ワークアラウンド


recipes/loop1.yml

---

- name: loop1
include: loop2.yml
with_dict: "{{ item.value }}"
loop_control:
loop_var: item1

ここから更にループさせるには、更にファイルを分ける必要がある。(ここではloop2.yml)

なお、単純に2重ループ目を作るとitemという内容を指定するキーワードが重複する為、item1という別名を使用している。


recipes/loop2.yml


---
- name: loop2
debug: msg="key1:{{ item.key }} key2:{{ item1.key }} key3:{{ item2.key }} val:{{ item2.value }}"
with_dict: "{{ item1.value }}"
loop_control:
loop_var: item2

ループの最後で処理の本体。

キーワードの重複を避ける為、item2という別名を使用している。


実行結果

$ kitchen conv

-----> Starting Kitchen (v1.21.2)
-----> Converging <ansible-loop-centos-7>...
Preparing files for transfer
*************** AnsiblePush install_command ***************
Ansible push config validated
Transferring files to <ansible-loop-centos-7>
*************** AnsiblePush run ***************

PLAY [all] ******************************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [ansible-loop-centos-7]

TASK [loop0] ****************************************************************************************
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop1.yml for ansible-loop-centos-7
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop1.yml for ansible-loop-centos-7
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop1.yml for ansible-loop-centos-7

TASK [loop1] ****************************************************************************************
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop2.yml for ansible-loop-centos-7
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop2.yml for ansible-loop-centos-7

TASK [loop2] ****************************************************************************************
ok: [ansible-loop-centos-7] => (item={'value': u'AAA', 'key': u'aaa'}) => {
"msg": "key1:a key2:aa key3:aaa val:AAA"
}
ok: [ansible-loop-centos-7] => (item={'value': u'AAB', 'key': u'aab'}) => {
"msg": "key1:a key2:aa key3:aab val:AAB"
}

TASK [loop2] ****************************************************************************************
ok: [ansible-loop-centos-7] => (item={'value': u'ABA', 'key': u'aba'}) => {
"msg": "key1:a key2:ab key3:aba val:ABA"
}

TASK [loop1] ****************************************************************************************
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop2.yml for ansible-loop-centos-7
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop2.yml for ansible-loop-centos-7

TASK [loop2] ****************************************************************************************
ok: [ansible-loop-centos-7] => (item={'value': u'CBA', 'key': u'cba'}) => {
"msg": "key1:c key2:cb key3:cba val:CBA"
}

TASK [loop2] ****************************************************************************************
ok: [ansible-loop-centos-7] => (item={'value': u'CAA', 'key': u'caa'}) => {
"msg": "key1:c key2:ca key3:caa val:CAA"
}

TASK [loop1] ****************************************************************************************
included: /Users/aa220269/repo/repo-test/cookbooks/ansible_loop/recipes/loop2.yml for ansible-loop-centos-7

TASK [loop2] ****************************************************************************************
ok: [ansible-loop-centos-7] => (item={'value': u'BAA', 'key': u'baa'}) => {
"msg": "key1:b key2:ba key3:baa val:BAA"
}

PLAY RECAP ******************************************************************************************
ansible-loop-centos-7 : ok=14 changed=0 unreachable=0 failed=0

*************** AnsiblePush end run *******************
Downloading files from <ansible-loop-centos-7>
Finished converging <ansible-loop-centos-7> (0m5.49s).
-----> Kitchen is finished. (0m8.37s)

ログは読みにくく保守性に欠ける。

実行順序が保障されていない。