るつぼっと

NWエンジニアな人たちに向けて

[Ansible] PlaybookのYAML構文を理解する

はじめに

Ansibleを調べ始めたけど、YAML形式がよく分からんという方向けの内容です。
具体的にはハイフンやインデントが何を意味するか見ていきます。
私自身Pythonにあまり詳しくないこともあり、正しくない表現があるかもしれません。
あくまでもイメージを掴むためのものとしてご覧ください。

使われるデータ型(変数の形式)

まずはAnsibleで使用されるデータ型とその表現方法をざっくり整理します。
AnsibleはPythonで実装されているため詳細はPythonのデータ型を調べてみてください。

各データ型のYAMLでの表現

いきなりですが、各データ型をYAMLで表現します。 コメントと併せてご覧ください。
var6についてはイメージ図と併せて見れば分かりやすいかと思います。

var1: 1     # int型:単一要素(整数)を格納
var2: "2"   # string型:単一要素(文字列)を格納
var3: true  # boolean型:単一要素(真偽値)を格納
var4:       # list型:複数要素を格納。各要素にはindexが割り当たる
- "apple"
- "fruit"
- 100
var5:       # dictionary型:複数要素を格納。key: valueのセットとなる
  name: "apple"
  type: "fruit"
  cost: 100
var6:       # list型の中にdictionary型を階層的に定義したパターン
- name: "apple"
  type: "fruit"
  cost: 100
- name: "melon"
  type: "fruit"
  cost: 500
イメージ図

f:id:yoloz:20220313172210p:plain

ポイント

下記のあたりを抑えておく。
個人的には最後の点に混乱させられました。

  • list型は各要素を1行ずつハイフンで定義する(全要素を1行で表現する形式も有る)
  • dictionary型は1行ずつkey: valueで定義する(全要素を1行で表現する形式も有る)
  • list型は定義順に各要素へ0始まりの番号(index)が割り当たる
  • dictionary型には各要素の順番の概念がない
  • ある変数の配下であることはインデントで表現する
  • ただし例のとおりlistの各要素はインデントを下げなくても問題ない

Playbookの読み解き

以上の内容を踏まえ、ここからはPlaybookのサンプルを読み解いてみます。

実施したい作業

f:id:yoloz:20220313134847p:plain

用意したplaybook

上図の作業をPlaybookで表現するとこうなります。
モジュールの内容等は主題から外れるのであまり気にせず、全体の形を見てください。

---
- hosts: rt1
  gather_facts: false
  tasks:
  - name: execute
    vyos_command:
      commands: show version
      retries: 1
    register: response
  - name: print
    debug:
      msg:  "{{ response }}"

- hosts: rt2
  gather_facts: false
  tasks:
  - name: execute
    vyos_command:
      commands: "{{ cmd_list }}"
    vars:
      cmd_list:
        - show version
        - show interface
Playbookをイメージ図にしてみる

どうしてこんな構成になったのか、今一つピンと来ないかもしれません。
そこでイメージ図を起こしてみました。長くなってしまいましたが、大きな単位からPlaybookと1つ1つ紐づけて眺めてみてください。
全体として最初に図示した作業内容と似た形になっていることが分かります。

f:id:yoloz:20220313141814p:plain

ここで気になること

ひとまず以上のように表現できましたが、さて、このうち Playbookとしては
どこをどういじると影響が有る、または無いのでしょうか。
データ型の一覧で抑えたうちの下記内容が重要になります。

  • list型は定義順に各要素へ0始まりの番号(インデックス)が割り当たる
  • dictionary型には各要素の順番の概念がない
  • ある変数の配下であることはインデントで表現する

これらを踏まえ幾つか確認してみましょう。

やると影響が有ること

Playbookをいじってみます。見やすくするためrt2は省略です。

list型の要素の順番を入れ替える
- hosts: rt1
  gather_facts: false
  tasks:
  - name: print
    debug:
      msg:  "{{ response }}"
  - name: execute
    vyos_command:
      commands: show version
      retries: 1
    register: response

f:id:yoloz:20220313172534p:plain

tasksのリストの順番を入れ替えました。
影響として、結果出力とコマンド実行の動作順序が逆転してしまいます。
これはその時点で存在しない変数 response を参照するためエラーになります。

TASK [print] *********************************************************************************************************************
fatal: [rt1]: FAILED! => 
  msg: |-
    The task includes an option with an undefined variable. The error was: 'response' is undefined
  
    The error appears to be in '/home/ansible/test/test.yml': line 5, column 7, but may
    be elsewhere in the file depending on the exact syntax problem.
  
    The offending line appears to be:
  
      tasks:
        - name: print
          ^ here
階層をずらす
- hosts: rt1
  gather_facts: false
  tasks:
  - name: execute
    vyos_command:
      commands: show version
      retries: 1
    register: response
  - name: print
  debug:
    msg:  "{{ response }}"

f:id:yoloz:20220313175804p:plain

debug: のインデントを上げました。
影響として、debugがlistから飛び出ています。
これは不正な構文のためエラーとなります。

ERROR! 'debug' is not a valid attribute for a Play

The error appears to be in '/home/ansible/test/test.yml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

---
- hosts: rt1
  ^ here

やっても影響が無いこと

今度は動作に影響が無いパターンです。こちらもrt2は省略しています。

list型以外の要素の並び順を入れ替える
- gather_facts: false
  tasks:
  - name: execute
    register: response
    vyos_command:
      retries: 1
      commands: show version
  - debug:
      msg:  "{{ response }}"
    name: print
  hosts: rt1

f:id:yoloz:20220313172637p:plain

大分ぐちゃぐちゃにしましたが、Playbookの動作には意外と影響ありません。
 ※ 実行結果は参考として本記事の最後へ掲載
それぞれの階層にある要素や、listの各Index配下の順序に変動が無いためです。

とはいえ非常に読み難いためこのような書き方は全く推奨されません。

所感

自分が最初に戸惑った内容を纏めてみました。
上手く伝わって、Ansible理解の足掛かりになれば幸いです。

参考:正常動作時の出力

Playbook

---
- gather_facts: false
  tasks:
  - name: execute
    register: response
    vyos_command:
      retries: 1
      commands: show version
  - debug:
      msg:  "{{ response }}"
    name: print
  hosts: rt1

- hosts: rt2
  gather_facts: false
  tasks:
  - name: execute
    vyos_command:
      commands: "{{ cmd_list }}"
    vars:
      cmd_list:
        - show version
        - show interface

実行結果

# ansible-playbook -i inventory.ini test.yml 

PLAY [rt1] ***********************************************************************************************************************

TASK [execute] *******************************************************************************************************************
ok: [rt1]

TASK [print] *********************************************************************************************************************
ok: [rt1] => 
  msg:
    changed: false
    failed: false
    stdout:
    - |-
      Version:          VyOS 1.4-rolling-202112280317
      Release train:    sagitta
  
      Built by:         autobuild@vyos.net
      Built on:         Tue 28 Dec 2021 03:17 UTC
      Build UUID:       00fbb68a-9364-48c0-9039-d2a0ec19e39f
      Build commit ID:  4ccdaf58fc7b9b
  
      Architecture:     x86_64
      Boot via:         installed image
      System type:      KVM guest
  
      Hardware vendor:  innotek GmbH
      Hardware model:   VirtualBox
      Hardware S/N:     0
      Hardware UUID:    8bd5d9c0-edf3-654f-8d43-b41f82681b81
  
      Copyright:        VyOS maintainers and contributors
    stdout_lines:
    - - 'Version:          VyOS 1.4-rolling-202112280317'
      - 'Release train:    sagitta'
      - ''
      - 'Built by:         autobuild@vyos.net'
      - 'Built on:         Tue 28 Dec 2021 03:17 UTC'
      - 'Build UUID:       00fbb68a-9364-48c0-9039-d2a0ec19e39f'
      - 'Build commit ID:  4ccdaf58fc7b9b'
      - ''
      - 'Architecture:     x86_64'
      - 'Boot via:         installed image'
      - 'System type:      KVM guest'
      - ''
      - 'Hardware vendor:  innotek GmbH'
      - 'Hardware model:   VirtualBox'
      - 'Hardware S/N:     0'
      - 'Hardware UUID:    8bd5d9c0-edf3-654f-8d43-b41f82681b81'
      - ''
      - 'Copyright:        VyOS maintainers and contributors'

PLAY [rt2] ***********************************************************************************************************************

TASK [execute] *******************************************************************************************************************
ok: [rt2]

PLAY RECAP ***********************************************************************************************************************
rt1                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
rt2                        : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

#