Usual Software Engineer

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

Ansible の with_items の出力を抑制する方法

ネタがない上に 3 月になってしまったので恒例の Ansible 小ネタですw まずはこの playbook を見てみましょう。

---
- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: execute docker commands
      command: "docker {{ item }}"
      with_items:
        - "ps --help"
        - "logs --help"
      changed_when: false
      register: command_result

    - name: output docker command's help
      debug:
        msg: "{{ item.stdout_lines }}"
      with_items: "{{ command_result.results }}"
      when: command_result.results is defined
        and item.stdout_lines is defined

やっていることは command のリストを用意して loop 実行して、その結果を表示する、ですね。 ただ実際に実行してみると

$ ansible-playbook loop.yml

PLAY [localhost] ****************************************************************************************************************************

TASK [execute docker commands] **************************************************************************************************************
ok: [localhost] => (item=ps --help)
ok: [localhost] => (item=logs --help)

TASK [output docker command's help] *********************************************************************************************************
ok: [localhost] => (item={'changed': False, 'end': '2019-03-09 13:21:42.414016', 'stdout': "\nUsage:\tdocker ps [OPTIONS]\n\nList containers\
n\nOptions:\n  -a, --all             Show all containers (default shows just running)\n  -f, --filter filter   Filter output based on conditi
ons provided\n      --format string   Pretty-print containers using a Go template\n  -n, --last int        Show n last created containers (in
cludes all\n                        states) (default -1)\n  -l, --latest          Show the latest created container (includes all\n          
              states)\n      --no-trunc        Don't truncate output\n  -q, --quiet           Only display numeric IDs\n  -s, --size         
   Display total file sizes", 'cmd': ['docker', 'ps', '--help'], 'rc': 0, 'start': '2019-03-09 13:21:42.361156', 'stderr': '', 'delta': '0:00
:00.052860', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': False, '_raw_params': 'docker ps --help', 're
moves': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 'stdout_lines': ['', 'Usage:\tdocker ps [O
PTIONS]', '', 'List containers', '', 'Options:', '  -a, --all             Show all containers (default shows just running)', '  -f, --filter 
filter   Filter output based on conditions provided', '      --format string   Pretty-print containers using a Go template', '  -n, --last in
t        Show n last created containers (includes all', '                        states) (default -1)', '  -l, --latest          Show the lat
est created container (includes all', '                        states)', "      --no-trunc        Don't truncate output", '  -q, --quiet     
      Only display numeric IDs', '  -s, --size            Display total file sizes'], 'stderr_lines': [], '_ansible_no_log': False, 'failed':
 False, 'item': 'ps --help', '_ansible_item_result': True, '_ansible_ignore_errors': None, '_ansible_item_label': 'ps --help'}) => {
    "msg": [
        "",
        "Usage:\tdocker ps [OPTIONS]",
        "",
        "List containers",
        "",
        "Options:",
        "  -a, --all             Show all containers (default shows just running)",
        "  -f, --filter filter   Filter output based on conditions provided",
        "      --format string   Pretty-print containers using a Go template",
        "  -n, --last int        Show n last created containers (includes all",
        "                        states) (default -1)",
        "  -l, --latest          Show the latest created container (includes all",
        "                        states)",
        "      --no-trunc        Don't truncate output",
        "  -q, --quiet           Only display numeric IDs",
        "  -s, --size            Display total file sizes"
    ]
}
ok: [localhost] => (item={'changed': False, 'end': '2019-03-09 13:21:42.671432', 'stdout': '\nUsage:\tdocker logs [OPTIONS] CONTAINER\n\nFet
ch the logs of a container\n\nOptions:\n      --details        Show extra details provided to logs\n  -f, --follow         Follow log output
\n      --since string   Show logs since timestamp (e.g.\n                       2013-01-02T13:23:37) or relative (e.g. 42m for 42\n        
               minutes)\n      --tail string    Number of lines to show from the end of the logs\n                       (default "all")\n  
-t, --timestamps     Show timestamps\n      --until string   Show logs before a timestamp (e.g.\n                       2013-01-02T13:23:37)
 or relative (e.g. 42m for 42\n                       minutes)', 'cmd': ['docker', 'logs', '--help'], 'rc': 0, 'start': '2019-03-09 13:21:42
.619107', 'stderr': '', 'delta': '0:00:00.052325', 'invocation': {'module_args': {'creates': None, 'executable': None, '_uses_shell': False,
 '_raw_params': 'docker logs --help', 'removes': None, 'argv': None, 'warn': True, 'chdir': None, 'stdin': None}}, '_ansible_parsed': True, 
'stdout_lines': ['', 'Usage:\tdocker logs [OPTIONS] CONTAINER', '', 'Fetch the logs of a container', '', 'Options:', '      --details       
 Show extra details provided to logs', '  -f, --follow         Follow log output', '      --since string   Show logs since timestamp (e.g.',
 '                       2013-01-02T13:23:37) or relative (e.g. 42m for 42', '                       minutes)', '      --tail string    Numb
 er of lines to show from the end of the logs', '                       (default "all")', '  -t, --timestamps     Show timestamps', '      -
-until string   Show logs before a timestamp (e.g.', '                       2013-01-02T13:23:37) or relative (e.g. 42m for 42', '          
             minutes)'], 'stderr_lines': [], '_ansible_no_log': False, 'failed': False, 'item': 'logs --help', '_ansible_item_result': True,
 '_ansible_ignore_errors': None, '_ansible_item_label': 'logs --help'}) => {
    "msg": [
        "",
        "Usage:\tdocker logs [OPTIONS] CONTAINER",
        "",
        "Fetch the logs of a container",
        "",
        "Options:",
        "      --details        Show extra details provided to logs",
        "  -f, --follow         Follow log output",
        "      --since string   Show logs since timestamp (e.g.",
        "                       2013-01-02T13:23:37) or relative (e.g. 42m for 42",
        "                       minutes)",
        "      --tail string    Number of lines to show from the end of the logs",
        "                       (default \"all\")",
        "  -t, --timestamps     Show timestamps",
        "      --until string   Show logs before a timestamp (e.g.",
        "                       2013-01-02T13:23:37) or relative (e.g. 42m for 42",
        "                       minutes)"
    ]
}

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

(ターミナルの行折返しの表示を真似てあえて改行を加えて書きましたが) item の全内容が loop 毎に表示されてとても見づらいですね。 それならということで no_log を付けてみるわけですが、これはもちろん全出力が抑えられてしまいます。

$ ansible-playbook loop.yml

PLAY [localhost] ****************************************************************************************************************************

TASK [execute docker commands] **************************************************************************************************************
ok: [localhost] => (item=ps --help)
ok: [localhost] => (item=logs --help)

TASK [output docker command's help] *********************************************************************************************************
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost]

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

まだ手はあるぞということで item 自体の内容を減らしてみますが、これだと当然 item 内に参照したい値が複数ある場合にやっぱり困ります。

    - name: output docker command's help
      debug:
        msg: "{{ item }}"
      with_items: "{{ command_result.results | map(attribute='stdout_lines') | list }}"
      when: command_result.results is defined

そんな時に使えるのが loop_control の label です。

https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#loop-control

使い方はまさにこのユースケースどおりで、 label 部分のみ制御することができます。

---
- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: execute docker commands
      command: "docker {{ item }}"
      with_items:
        - "ps --help"
        - "logs --help"
      changed_when: false
      register: command_result

    - name: output docker command's help
      debug:
        msg: "{{ item.stdout_lines }}"
      with_items: "{{ command_result.results }}"
      loop_control:
        label: "{{ item.cmd | default(item) }}"
      when: command_result.results is defined
        and item.stdout_lines is defined

結果は

$ ansible-playbook loop.yml

PLAY [localhost] ****************************************************************************************************************************

TASK [execute docker commands] **************************************************************************************************************
ok: [localhost] => (item=ps --help)
ok: [localhost] => (item=logs --help)

TASK [output docker command's help] *********************************************************************************************************
ok: [localhost] => (item=['docker', 'ps', '--help']) => {
    "msg": [
        "",
        "Usage:\tdocker ps [OPTIONS]",
        "",
        "List containers",
        "",
        "Options:",
        "  -a, --all             Show all containers (default shows just running)",
        "  -f, --filter filter   Filter output based on conditions provided",
        "      --format string   Pretty-print containers using a Go template",
        "  -n, --last int        Show n last created containers (includes all",
        "                        states) (default -1)",
        "  -l, --latest          Show the latest created container (includes all",
        "                        states)",
        "      --no-trunc        Don't truncate output",
        "  -q, --quiet           Only display numeric IDs",
        "  -s, --size            Display total file sizes"
    ]
}
ok: [localhost] => (item=['docker', 'logs', '--help']) => {
    "msg": [
        "",
        "Usage:\tdocker logs [OPTIONS] CONTAINER",
        "",
        "Fetch the logs of a container",
        "",
        "Options:",
        "      --details        Show extra details provided to logs",
        "  -f, --follow         Follow log output",
        "      --since string   Show logs since timestamp (e.g.",
        "                       2013-01-02T13:23:37) or relative (e.g. 42m for 42",
        "                       minutes)",
        "      --tail string    Number of lines to show from the end of the logs",
        "                       (default \"all\")",
        "  -t, --timestamps     Show timestamps",
        "      --until string   Show logs before a timestamp (e.g.",
        "                       2013-01-02T13:23:37) or relative (e.g. 42m for 42",
        "                       minutes)"
    ]
}

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

label 部分がコマンドの内容になり、出力は stdout_lines のみになってスッキリしましたね。

困った時は Loop Control 、使ってみてください。

Ansible実践ガイド 第2版 (impress top gear)

Ansible実践ガイド 第2版 (impress top gear)