Ansible: Playbookの繰り返し制御の色々 (5)

投稿者: | 2017-06-26

今回もAnsiblePlaybookにおける繰り返し制御について、学んでいきます。

引き続き題材は本家のドキュメントから。

シリーズはこちら。

Ansible: Loops

Looping Over A List With An Index

こちらも「滅多に使わないんじゃない?」という注意書きのあるものです。
ループを利用する際に、配列の数値の添え字を取り出せるよ、ということだそうです。

実際に見てみましょう。

- hosts: 127.0.0.1
  connection: local
  vars:
    some_list: [ 'item1', 'item2' ]

  tasks:
  - name: indexed loop demo
    debug:
      msg: "at array position {{ item.0 }} there is a value {{ item.1 }}"
    with_indexed_items:
      - "{{ some_list }}"

with_indexed_items を使って書くと、 item.0 で添え字を取り出せます。

PLAY [127.0.0.1] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [indexed loop demo] *******************************************************
ok: [localhost] => (item=(0, u'item1')) => {
    "item": [
        0, 
        "item1"
    ], 
    "msg": "at array position 0 there is a value item1"
}
ok: [localhost] => (item=(1, u'item2')) => {
    "item": [
        1, 
        "item2"
    ], 
    "msg": "at array position 1 there is a value item2"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0   

Using ini file with a loop

続いては、 ini 形式のファイルをループとして使う方法です。
ini 内のキーを正規表現で取り出して使えるようです。

こちらは、具体的なところが本家のドキュメントに記載されています。

まずは、iniファイル。

[section1]
value1=section1/value1
value2=section1/value2

[section2]
value1=section2/value1
value2=section2/value2

そして、Ansible Playbook。

- debug:
    msg: "{{ item }}"
  with_ini:
    - value[1-2]
    - section: section1
    - file: "lookup.ini"
    - re: true

これらを揃えて、実行した結果が下記となるようです。

{
      "changed": false,
      "msg": "All items completed",
      "results": [
          {
              "invocation": {
                  "module_args": "msg=\"section1/value1\"",
                  "module_name": "debug"
              },
              "item": "section1/value1",
              "msg": "section1/value1",
              "verbose_always": true
          },
          {
              "invocation": {
                  "module_args": "msg=\"section1/value2\"",
                  "module_name": "debug"
              },
              "item": "section1/value2",
              "msg": "section1/value2",
              "verbose_always": true
          }
      ]
  }

iniファイル内の section1 の中の、さらにキー名を value[1-2] という正規表現で取り出していますね。

Flattening A List

タイトルを訳すと「リストの平坦化」といった意味になります。
あまり伝わってこないので、実際に確かめてみます。

ドキュメント内にある例を、実際にAnsible Playbookにしてみます。

- hosts: 127.0.0.1
  connection: local
  vars:
    packages_base:
      - [ 'foo-package', 'bar-package' ]
    packages_apps:
      - [ ['one-package', 'two-package' ]]
      - [ ['red-package'], ['blue-package']]

  tasks:
  - name: indexed loop demo
    debug:
      msg: "{{ item }}"
    with_flattened:
      - "{{ packages_base }}"
      - "{{ packages_apps }}"

これを実行すると、下記となります。

PLAY [127.0.0.1] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [indexed loop demo] *******************************************************
ok: [localhost] => (item=foo-package) => {
    "item": "foo-package", 
    "msg": "foo-package"
}
ok: [localhost] => (item=bar-package) => {
    "item": "bar-package", 
    "msg": "bar-package"
}
ok: [localhost] => (item=one-package) => {
    "item": "one-package", 
    "msg": "one-package"
}
ok: [localhost] => (item=two-package) => {
    "item": "two-package", 
    "msg": "two-package"
}
ok: [localhost] => (item=red-package) => {
    "item": "red-package", 
    "msg": "red-package"
}
ok: [localhost] => (item=blue-package) => {
    "item": "blue-package", 
    "msg": "blue-package"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

このように、配列の構造に関わらずすべてを単純な一次元の配列として扱うようになります。
色々と集めた処理結果の配列を、構造を気にせずに一気に処理したい、、というような使い方だと使えるかも、、、といったところでしょうか。

Using register with a loop

次は、 register を使って、ループの実行結果を変数内に results 属性として保存できるよ、というところです。

というわけで、ドキュメント内の例をベースに動きを確かめます。

- hosts: 127.0.0.1
  connection: local

  tasks:
  - name: indexed loop demo
    shell: "echo {{ item }}"
    with_items:
      - "one"
      - "two"
    register: echo
  - name: result
    debug:
      msg: "{{ echo }}"

with_items の直後に register を記載しています。これで echo という変数の中に結果が格納され、次の debug で中身を確認できます。

動作結果も見てみましょう。

PLAY [127.0.0.1] **************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************
ok: [localhost]

TASK [indexed loop demo] ******************************************************************************************
changed: [localhost] => (item=one)
changed: [localhost] => (item=two)

TASK [result] *****************************************************************************************************
ok: [localhost] => {
    "msg": {
        "changed": true, 
        "msg": "All items completed", 
        "results": [
            {
                "_ansible_item_result": true, 
                "_ansible_no_log": false, 
                "_ansible_parsed": true, 
                "changed": true, 
                "cmd": "echo one", 
                "delta": "0:00:00.005954", 
                "end": "2017-06-26 23:03:36.802751", 
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo one", 
                        "_uses_shell": true, 
                        "chdir": null, 
                        "creates": null, 
                        "executable": null, 
                        "removes": null, 
                        "warn": true
                    }
                }, 
                "item": "one", 
                "rc": 0, 
                "start": "2017-06-26 23:03:36.796797", 
                "stderr": "", 
                "stderr_lines": [], 
                "stdout": "one", 
                "stdout_lines": [
                    "one"
                ]
            }, 
            {
                "_ansible_item_result": true, 
                "_ansible_no_log": false, 
                "_ansible_parsed": true, 
                "changed": true, 
                "cmd": "echo two", 
                "delta": "0:00:00.006686", 
                "end": "2017-06-26 23:03:37.089991", 
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo two", 
                        "_uses_shell": true, 
                        "chdir": null, 
                        "creates": null, 
                        "executable": null, 
                        "removes": null, 
                        "warn": true
                    }
                }, 
                "item": "two", 
                "rc": 0, 
                "start": "2017-06-26 23:03:37.083305", 
                "stderr": "", 
                "stderr_lines": [], 
                "stdout": "two", 
                "stdout_lines": [
                    "two"
                ]
            }
        ]
    }
}

PLAY RECAP ********************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0

with_items で与えた二つの要素の実行結果が、配列で results の中に格納されていますね。

また、 results の結果も配列なので、その後にループの要素として利用できます。
ドキュメント内の例ですと、実行結果が失敗の場合の処理を判定するものになっています。

- name: Fail if return code is not 0
  fail:
    msg: "The command ({{ item.cmd }}) did not have a 0 return code"
  when: item.rc != 0
  with_items: "{{ echo.results }}"

また、最初の例ではループが全て終わってから結果を取り出していましたが、各要素ごとの結果を利用する例も記載されています。

- shell: echo "{{ item }}"
  with_items:
    - one
    - two
  register: echo
  changed_when: echo.stdout != "one"

つまり、 changed_when の呼ばれるタイミングでは、 変数 echo の属性 stdout には、要素一つずつの実行結果が格納されている、ということになりますね。

まとめ

今回も、複雑な動きや独特な取り扱いのものが多かったと思います。
こういうものでも、自分で動かしながら確かめることで、意味を理解しやすくなるのでは無いでしょうか。

次回が、繰り返し制御の最終回になるはずです!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください