LoginSignup
1
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-07-31

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)

ログは読みにくく保守性に欠ける。
実行順序が保障されていない。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1