初めてのAnsible(10章:カスタムモジュール)

  • 2
    いいね
  • 0
    コメント

「初めてのAnsible」を読み進めながらのメモ

前掲
初めてのAnsible(1章:イントロダクション)
初めてのAnsible(2章:Playbook:始めてみよう)
初めてのAnsible(3章:インベントリ:サーバーの記述)
初めてのAnsible(4章:変数とファクト)
初めてのAnsible(7章:複雑なPlaybook)
初めてのAnsible(8章:ロール:プレイブックのスケールアップ)
初めてのAnsible(9章:Ansibleの高速化)

必要なら自分でモジュールかいてプルリクエストなげてみたらって章

カスタムモジュール

Ansibleのモジュールを記述する手段として2通りを挙げている

  • Pythonで記述する場合
  • Python以外(bashやRubyなど)で記述する場合

AnsibleがPythonでモジュールを記述するためのヘルパークラスを提供していることから、Pythonで記述することを推奨

ここでも詳細はPythonモジュールに関してのみまとめる

カスタムモジュールの置き場所

playbookが playbooks/playbook に置かれていれば
カスタムモジュールは playbooks/library/custom_module に置く

モジュール呼び出し時の挙動

Ansibleがモジュールを呼び出す際には以下のような挙動をとる

  1. (Pythonモジュールの場合} スタンドアロンのPythonスクリプトを引数付きで生成

    • ヘルパークラスのコードや引数などを埋め込み1つにまとめたPythonスクリプトを生成
  2. モジュールをホストにコピー

    • (Pythonモジュールの場合) 生成したスクリプトをコピー
    • (Pythonモジュールでない場合) playbooks/library/custom_module をコピー
  3. (Pythonモジュールでない場合) 引数ファイルをホスト上で生成

    • モジュールに渡された引数をそのまま書き込んだ引数ファイルを生成
    • カスタムモジュール内に # WANT_JSON を記述することで引数ファイルをJSONとすることも可能
  4. 引数ファイルを引数として渡しホスト上でモジュールを呼び出し

Pythonモジュール

Ansibleが提供する AnsibleModule を使うことで以下が容易になる

  • 入力のパース
  • JSONフォーマットでの出力
  • 外部プログラムの呼び出し

以下のサンプルコードを見ながらまとめる(ansiblebook/ch10/playbooks/library/can_reachより)

python
#!/usr/bin/python

def can_reach(module, host, port, timeout):
    nc_path = module.get_bin_path('nc', required=True)
    args = [nc_path, "-z", "-w", str(timeout), host, str(port)]
    (rc, stdout, stderr) = module.run_command(args)
    return rc == 0

def main():
    module = AnsibleModule(
        argument_spec=dict(
            host=dict(required=True),
            port=dict(required=True, type='int'),
            timeout=dict(required=False, type='int', default=3)
        ),
        supports_check_mode=True
    )

    # In check mode, we take no action
    # Since this module never changes system state, we just
    # return changed=False
    if module.check_mode:
        module.exit_json(changed=False)

    host = module.params['host']
    port = module.params['port']
    timeout = module.params['timeout']

    if can_reach(module, host, port, timeout):
        module.exit_json(changed=False)
    else:
        msg = "Could not reach %s:%s" % (host, port)
        module.fail_json(msg=msg)

from ansible.module_utils.basic import *
main()

can_reach

def can_reach(module, host, port, timeout):
    nc_path = module.get_bin_path('nc', required=True)
    args = [nc_path, "-z", "-w", str(timeout), host, str(port)]
    (rc, stdout, stderr) = module.run_command(args)
    return rc == 0

get_bin_path

  • 外部プログラムのパスを取得
    • ここでは nc(netcat) コマンドのパスを取得
  • required=True が指定されている場合、実行ファイルが見つからないと fail_jsonメソッド(後述)をなげる

run_command

  • 外部プログラムを呼び出し
  • いくつかの引数をとるけどもここでは args についてのみまとめる
    • 細かいことはリンク先のコード追えばいいかな

run_command の引数 : args

  • 型 : 文字列 or リスト
    • 上記の例では、引数をリストで渡している
  • 実行するコマンドとそのオプション等
  • 返り値は rc, stdout, stderr

AnsibleModule

module = AnsibleModule(
    argument_spec=dict(
        host=dict(required=True),
        port=dict(required=True, type='int'),
        timeout=dict(required=False, type='int', default=3)
    ),
    supports_check_mode=True
)

argument_spec

  • AnsibleModuleのとる引数のうち、唯一必須
  • モジュールで使用できる引数に関する辞書 上記の例では host, port, timeout を引数としている

引数のオプション

上記の例でも引数(host, port, timeout)にオプションが指定されている

  • required

    • Trueであれば引数は必須
  • type

    • 引数の型を指定(デフォルトでは文字列)
    • str, list, dict, bool, int, float が指定可能
  • default

    • デフォルト値
    • required=False とする場合には指定しておいた方がよい

チェックモード

  • AnsibleModuleの引数の一つ
    • Trueの場合にはチェックモードをサポートする
  • ansible-playbook-C or --check フラグを渡すことで有効化
  • チェックモードでは各タスクがホストの状態を変化させるか、変化させずに成功するか、エラーを返すかといったことのみを知らせる

exit_json / fail_json

if module.check_mode:
    module.exit_json(changed=False)

host = module.params['host']
port = module.params['port']
timeout = module.params['timeout']

if can_reach(module, host, port, timeout):
    module.exit_json(changed=False)
else:
    msg = "Could not reach %s:%s" % (host, port)
    module.fail_json(msg=msg)

exit_json

  • 成功したことを示す
  • 引数 changed は必須 : ホストの状態を変化させたかどうか
  • 引数 msg を利用し、メッセージを返すことも可能

fail_json

  • 失敗したことを示す
  • 引数 msg で失敗の理由を返すべき

モジュールの引数へのアクセス

  • argument_spec で定義した各引数へ params によりアクセス可能

AnsibleModuleヘルパークラスのインポート

from ansible.module_utils.basic import *
  • カスタムモジュールでは末尾にimport文を書いた方がよい
    • 前述した通りPythonモジュールの場合、 ヘルパークラスのコードや引数などを埋め込み1つにまとめたPythonスクリプトを生成 するため、モジュール内の行番号は元のファイルと生成されたものとで異なる
    • 先頭にimport文をもってきてしまうと、xx行でエラーが起きてるよーみたいな時にわかりづらくなる