Ansibleのcommandモジュールで冪等性を担保する

Ansibleにてモジュールの用意されていない操作を実行する場合、OS上でコマンドを実行してくれるモジュールが用意されています。 commandshell がそれにあたります。

Ansible - command

Ansible - shell

しかしながら、公式では可能な限り command モジュールの利用を推奨しているようです。

If you want to run a command through the shell (say you are using <, >, |, etc), you actually want the shell module instead. Parsing shell metacharacters can lead to unexpected commands being executed if quoting is not done correctly so it is more secure to use the command module when possible.

どちらのモジュールにも、以下のような特徴をもっています。

  • createsremoves パラメータを利用しない場合、または指定した条件に合致しない場合、必ず処理が実行される
  • コマンド実行結果ステータスが0であった時に成功、それ以外は失敗となる
  • コマンド成功時のステータスは changed となる

createsremoves パラメータとは、指定したファイルの有無により、コマンド実行を制御してくれるパラメータです。逆に言うと、このパラメータで管理できない処理に関しては、必ず処理が実行される事になります。

例えば、AnsibleにてOSのロケールを変更したいとなった場合、 localectl set-locale にあたるようなモジュールは用意されていないため、以下のようなplyabookを書くことになります。

- name: ロケールを日本語に変更
  command: localectl set-locale LANG=ja_JP.utf8

まあ、これで変更してくれるのですが、上で記載した特徴の通り、このタスクはplaybook実行時に毎回実行され、changedを返してくることになります。毎回実行されても問題ないものですが、playbookの管理上、これは望ましい事ではありません。意味のないchangedを毎回返してくることも、煩わしいです。

これを回避するアイデアを記載していきます。


環境情報です。


システムロケールLANG=ja_JP.utf8 の時にはロケール変更処理を、そうでない場合にはスキップして欲しい訳ですから、そういったplaybookを書いてあげることになります。

サンプルは下記です。

- name: システムロケールの確認
  command: localectl status
  register: localectl_result
  check_mode: no
  changed_when: false

- name: ロケールの変更
  command: localectl set-locale LANG=ja_JP.utf8
  when: "'LANG=ja_JP.utf8' not in localectl_result.stdout"

1つ目のタスクは、現在のロケール情報を取得して、変数 localectl_result に代入している処理です。 register とは、タスクの実行結果を指定した変数に代入してくれるディレクティブになります。また、 command モジュールは、終了コードが0の場合にchangedステータスを返しますが、何も変更していないのにchangedステータスとなるのは変なので、 changed_when: false として、実行結果のステータスを強制的に変更しています。この設定をすることで、okステータスを返すことになります。

Ansible - register

Ansbile - changed_when

2つ目のタスクで、ロケールの設定を変更しています。whenディレクティブではタスク実行の条件を指定することができ、今回は変数 localectl_resultstdout に、 LANG=ja_JP.utf8 の文字列がない場合のみ実行する条件となっています。

Ansible - when

変数内の値は debug モジュールを利用することで表示することができ、今回の場合、以下のようになっています。

playbook

- name: デバッグする
  debug:
    var: localectl_result

result

"localectl_result": {
    "changed": false,
    "cmd": [
        "localectl",
        "status"
    ],
    "delta": "0:00:00.006054",
    "end": "2019-05-01 19:30:29.018230",
    "failed": false,
    "rc": 0,
    "start": "2019-05-01 19:30:29.012176",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "   System Locale: LANG=ja_JP.utf8\n       VC Keymap: us\n      X11 Layout: n/a",
    "stdout_lines": [
        "   System Locale: LANG=ja_JP.utf8",
        "       VC Keymap: us",
        "      X11 Layout: n/a"
    ]
}

Ansible - debug

こうすることで、現行のロケール設定に応じて、処理を実行/スキップできるようになり、plyabook実行結果の表示も分かりやすいものになってくれます。