Usual Software Engineer

よくあるソフトウェアエンジニアのブログ

Ansible の vars 参照で使える小ネタ

もう 11 月も半ばですが 10 月分のブログとして書きます。怠惰w

特にネタがないので以前も書きましたが Ansible の小ネタ投稿にします。

innossh.hatenablog.com

今回は変数参照について。 Ansible というか Jinja2 の内容も混じってます、思いつくけどどう表現していいかわからないような検索しづらい系の小ネタ集めました。

動作検証したコードの方を見たい場合はこちらのリポジトリをどうぞ。

github.com

hash 内の値の参照

意外と簡単です。

yaml:

vars:
  foo: foo
  example_hash:
    foo: foo
    bar: bar
...
tasks:
  - block:
    - debug:
        msg: "example_hash['foo'] is {{ example_hash['foo'] }}"
    - debug:
        msg: "example_hash[foo] is {{ example_hash[foo] }}"

出力:

"msg": "example_hash['foo'] is foo"
"msg": "example_hash[foo] is foo"

上記のように key の部分が変数名でも問題ないですね。

環境変数の参照

例えば vars_prompt で入力値を参照する場合で、かつ、デフォルト値は環境変数の値にしたい場合は以下のような感じです。

yaml:

vars_prompt:
  - name: env_baz
    prompt: Please enter baz value
    default: "{{ lookup('env', 'BAZ') }}"
    private: no
...
tasks:
  - block:
    - debug:
        msg: "env_baz is {{ env_baz }}"

出力1(環境変数あり実行 BAZ=baz ):

Please enter env_baz value [baz]:
...
"msg": "env_baz is baz"

出力2(環境変数なし実行):

Please enter env_baz value []: mybaz
...
"msg": "env_baz is mybaz"

コマンドラインオプションで vars と vars_prompt の上書き

vars も vars_prompt も -e で上書きできるので

  • 基本的に入力してもらいたいものは vars_prompt に
  • 毎回入力してもらうほどじゃないけど一応上書き可能にしておきたいものは vars に

書くようにしましょう。

yaml:

vars:
  baz: baz
...
vars_prompt:
  - name: baz_prompt
    prompt: Please enter baz_prompt value
    default: "default_baz"
    private: no
...
tasks:
  - block:
    - debug:
        msg: "baz is {{ baz }}, baz_prompt is {{ baz_prompt }}"

出力1(実行オプションなし):

Please enter baz_prompt value [default_baz]: mybaz
...
"msg": "baz is baz, baz_prompt is mybaz"

出力2(実行オプションあり -e "baz=mybaz" -e "baz_prompt=mybaz" )

(promptも聞かれない)
...
"msg": "baz is mybaz, baz_prompt is mybaz"

リストの特定の値の除外

filter で簡単にできます。

yaml:

vars:
  example_list:
    - rejected
    - one
    - two
...
tasks:
  - block:
    - debug:
        msg: "filtered example_list is including {{ item }}"
      with_items: "{{ example_list | reject('search', 'rejected') | list }}"

出力:

"msg": "filtered example_list is including one"
"msg": "filtered example_list is including two"

ポイントは | listgenerator object から元のリスト形式に戻してあげることでしょうか。

リスト内に特定の値を持つ hash があるかどうかの判定

yaml:

vars:
  example_hash_list:
    - key: key_a
      value: aaa
    - key: key_b
      value: bbb
    - key: key_c
      value: ccc
...
tasks:
  - block:
    - debug:
        msg: "example_hash_list is including key_c hash"
      when: example_hash_list | selectattr("key", "equalto", "key_c") | list | length > 0
    - debug:
        msg: "example_hash_list is NOT including key_d hash"
      when: example_hash_list | selectattr("key", "equalto", "key_d") | list | length == 0

出力:

"msg": "example_hash_list is including key_c hash"
"msg": "example_hash_list is NOT including key_d hash"

複数ホストの fact を一つの fact にまとめる

例として、複数のホストの中で最新のログを取得したい場合の playbook を紹介します。 alpine の docker image だと find コマンドで -printf 使えなかったので特に考えず(怠惰w) stretch 使いました。ちなみに hosts.ini などで ansible_python_interpreter 指定しないと動かないので python のパスに気をつけましょう。

yaml:

ansible-sandbox/playbook.example-container.yml at v0.0.1 · innossh/ansible-sandbox · GitHub

出力( /var/log/foo にログがないのでサンプル実行として -e "log_dir=/tmp" ):

PLAY [containers] *********************************************************************************************************************************

TASK [find logs] **********************************************************************************************************************************
ok: [ansible-sandbox_echo1_1]
ok: [ansible-sandbox_echo2_1]

...

TASK [output the latest log] **********************************************************************************************************************
ok: [ansible-sandbox_echo1_1 -> localhost] => {
    "msg": {
        "host": "ansible-sandbox_echo1_1",
        "path": "/tmp/ansible_command_payload_4jqmi0in",
        "time": "Sun Nov 11 09:29:36 2018",
        "unix_time": 1541928576.260902
    }
}

PLAY RECAP ****************************************************************************************************************************************
ansible-sandbox_echo1_1    : ok=6    changed=0    unreachable=0    failed=0
ansible-sandbox_echo2_1    : ok=2    changed=0    unreachable=0    failed=0

ポイントは以下で

      - block:
        - name: gather latest_log_jsons facts
          set_fact:
            latest_log_jsons: "{{ latest_log_jsons | default([]) + hostvars[item].latest_log_jsons_per_host }}"
          with_items: "{{ play_hosts }}"
...
        delegate_to: localhost
        run_once: True
  • delegate_to: localhost run_once: Trueset_fact タスクをローカルで一度だけ実行とする
  • with_items: "{{ play_hosts }}" で対象のホスト分のループを回す
  • hostvars[item].latest_log_jsons_per_host の各ホストの値を latest_log_jsons に集約する
  • あとは latest_log_jsons リスト内の最新のログを取り出すだけ

のようにして複数ホストをまたいだ最新のログを取得しています。

以上、小ネタでした。また溜まったらネタがないときに書こう。