stdoutとstdout_linesの改行

はじめに

registerを使用したときに保存されるデータの中に、stdoutstdout_linesがあります。 この2つの改行の扱いについて検証しました。

やったこと

Cisco DevNet SandboxのIOS XE on CSR1000vにshow ntp associationsして、下記を比較しました。

stdout stdout[0] stdout_lines stdout_lines[0]
debug出力結果 ? ? ? ?
copy保存結果 ? ? ? ?

結論

  • debugモジュールの結果を見やすくしたいなら、stdout_linesを使用する
  • copyモジュールで保存するならstdout[0]を使用する

環境

Ansibleのバージョン

# ansible --version
ansible 2.9.7
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.6.8 (default, Apr  2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

Playbook

処理概要は下記です。

  1. show ntp associationsの実行結果をregisterする
  2. stdoutロール内で、下記を実行
    1. stdoutdebugモジュールで出力
    2. stdout[0]debugモジュールで出力
    3. stdoutcopyモジュールで保存
    4. stdout[0]copyモジュールで保存
  3. stdout_linesロール内で、下記を実行
    1. stdout_linesdebugモジュールで出力
    2. stdout_lines[0]debugモジュールで出力
    3. stdout_linescopyモジュールで保存
    4. stdout_lines[0]copyモジュールで保存

Playbook構成

# tree
.
├── inventory.yml
├── ntp.yml
└── roles
     ├── stdout
     │   └── tasks
     │       └── main.yml
     └── stdout_lines
         └── tasks
             └── main.yml

ntp.yml

---
- hosts: csr1000v
  gather_facts: no
  tasks:

  - name: show ntp associations
    ios_command:
      commands:
        - show ntp associations
    register: res_ntp
    tags: common

  - name: import stdout
    import_role:
      name: stdout
    tags: stdout

  - name: import stdout_lines
    import_role:
      name: stdout_lines
    tags: stdout_lines

stdoutロール

- block:
  - name: debug res_ntp as stdout without index
    debug:
      var: res_ntp.stdout

  - name: debug res_ntp as stdout with index
    debug:
      var: res_ntp.stdout[0]

  tags: show

- block:
  - name: save res_ntp as stdout without index
    copy:
      content: "{{ res_ntp.stdout }}"
      dest: ntp_stdout.txt

  - name: save res_ntp as stdout with index
    copy:
      content: "{{ res_ntp.stdout[0] }}"
      dest: ntp_stdout0.txt

  tags: save

stdout_linesロール

- block:
  - name: debug res_ntp as stdout_lines without index
    debug:
      var: res_ntp.stdout_lines

  - name: debug res_ntp as stdout_lines with index
    debug:
      var: res_ntp.stdout_lines[0]

  tags: show

- block:
  - name: save res_ntp as stdout_lines without index
    copy:
      content: "{{ res_ntp.stdout_lines }}"
      dest: ntp_stdout_lines.txt

  - name: save res_ntp as stdout_lines with index
    copy:
      content: "{{ res_ntp.stdout_lines[0] }}"
      dest: ntp_stdout_lines0.txt

  tags: save

実行結果

stdout

# ansible-playbook -i inventory.yml ntp.yml -t common,stdout

PLAY [csr1000v] *****************************************************************************

TASK [show ntp associations] ****************************************************************
ok: [csr1000v]

TASK [stdout : debug res_ntp as stdout without index] ***************************************
ok: [csr1000v] => {
    "res_ntp.stdout": [
        "address         ref clock       st   when   poll reach  delay  offset   disp\n ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.\n ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.\n ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.\n * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured"
    ]
}

TASK [stdout : debug res_ntp as stdout with index] ******************************************
ok: [csr1000v] => {
    "res_ntp.stdout[0]": "address         ref clock       st   when   poll reach  delay  offset   disp\n ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.\n ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.\n ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.\n * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured"
}

TASK [stdout : save res_ntp as stdout without index] ****************************************
ok: [csr1000v]

TASK [stdout : save res_ntp as stdout with index] *******************************************
ok: [csr1000v]

PLAY RECAP **********************************************************************************
csr1000v                   : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
# cat -n ntp_stdout.txt 
     1  ["address         ref clock       st   when   poll reach  delay  offset   disp\n ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.\n ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.\n ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.\n * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured"]
     
# cat -n ntp_stdout0.txt 
     1  address         ref clock       st   when   poll reach  delay  offset   disp
     2   ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.
     3   ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.
     4   ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.
     5   * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured

stdout_lines

# ansible-playbook -i inventory.yml ntp.yml -t common,stdout_lines 
PLAY [csr1000v] *****************************************************************************

TASK [show ntp associations] ****************************************************************
ok: [csr1000v]

TASK [stdout_lines : debug res_ntp as stdout_lines without index] ***************************
ok: [csr1000v] => {
    "res_ntp.stdout_lines": [
        [
            "address         ref clock       st   when   poll reach  delay  offset   disp",
            " ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.",
            " ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.",
            " ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.",
            " * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured"
        ]
    ]
}

TASK [stdout_lines : debug res_ntp as stdout_lines with index] ******************************
ok: [csr1000v] => {
    "res_ntp.stdout_lines[0]": [
        "address         ref clock       st   when   poll reach  delay  offset   disp",
        " ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.",
        " ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.",
        " ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.",
        " * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured"
    ]
}

TASK [stdout_lines : save res_ntp as stdout_lines without index] ****************************
ok: [csr1000v]

TASK [stdout_lines : save res_ntp as stdout_lines with index] *******************************
ok: [csr1000v]

PLAY RECAP **********************************************************************************
csr1000v                   : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
# cat -n ntp_stdout_lines.txt 
     1  [["address         ref clock       st   when   poll reach  delay  offset   disp", " ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.", " ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.", " ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.", " * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured"]]
# cat -n ntp_stdout_lines0.txt 
     1  ["address         ref clock       st   when   poll reach  delay  offset   disp", " ~1.1.1.1         .INIT.          16      -   1024     0  0.000   0.000 15937.", " ~2.2.2.2         .INIT.          16      -   1024     0  0.000   0.000 15937.", " ~3.3.3.3         .INIT.          16      -   1024     0  0.000   0.000 15937.", " * sys.peer, # selected, + candidate, - outlyer, x falseticker, ~ configured"]

まとめ

表現が正しいか自信がないですが、下記の結果となりました。

stdout stdout[0] stdout_lines stdout_lines[0]
debug出力結果 リスト
\nが解釈されない
文字列
\nrが解釈されない
リストのリスト
\nrが解釈される
リスト
\nrが解釈される
copy保存結果 リスト
\nrが解釈されない
文字列
\nrが解釈される
リストのリスト
\nrなし
リスト
\nrなし

改行コードを含むコマンドの出力結果をregisterで保存した場合、stdoutは生で格納し、stdout_linesは、改行コードを基準にリストとして分割する動作になります。今回の検証パターンでは、copyモジュールは改行コードを解釈しますが、リストでなく純粋な文字列であることが前提になります。 したがって、はじめの結論に書いたように、下記の利用方法が適切のようです。

  • debugモジュールの結果を見やすくしたいなら、stdout_linesを使用する
  • copyモジュールで保存するならstdout[0]を使用する

データ構造大事。