Ansible 自作モジュール作成 ～その1:引数受け取りたい人生～

Ansible使おうとしたら名前付きパイプ(``makefifo``)相当がなかったので自作モジュールを頑張ることにした。

# 参考サイト

- [Ansibleのモジュール開発（Python実装編)](https://dev.classmethod.jp/articles/ansible-develop-module-python/)
    - すっごい分かりやすくて好き！
- [ansible コマンドでモジュール引数 ( パラメータ ) を複数渡す方法](https://qiita.com/YumaInaura/items/6e05d87858bd647d11ab)
    - ``ansible``コマンド実行時に複数の引数を与える方法

# 環境

```bash
$ cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)

$ ansible --version
ansible 2.9.7
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/ansi/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
```

# 雛形を作る

参考サイトを見つつ

```python:makefifo.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from ansible.module_utils.basic import AnsibleModule
module = AnsibleModule(
    dict=(
        message=dict()
    )
)
print '{"message_key":"%s"}' % (module.params['message'])
```

んーとこんな感じでいいのかなぁ～。早速実行してみよう。

```bash
$ ansible -i test_grp 192.168.56.104 -m makefifo -M library -u root -a message=test
192.168.56.104 | FAILED! => {
    "msg": "Unable to import makefifo due to invalid syntax"
}
```

OOPS!! あれれ。。いきなり躓いたぞい。

Ansibleのバージョンによって差分があるのかな？ということで既存のモジュールを解体して作ってみようと方向シフト。

```bash
$ find /usr/lib/python2.7/site-packages -type f -name ping.py
/usr/lib/python2.7/site-packages/ansible/modules/system/ping.py
```

コイツをコピってきて``pong.py``にリネームしてまずは正常動作確認。

```bash
$ ansible -i test_grp 192.168.56.104 -m pong -M library -u root -a data=hoge
192.168.56.104 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "hoge"
}
```

うん、動いてる。

ココから中身の要らなさそうな部分を削ってみた結果、以下まで削ることができた。

```python:pong.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from ansible.module_utils.basic import AnsibleModule

# メイン処理
#-----------------------------------------------------------
def main():
    # AnsibleModuleクラス: moduleを作成
    module = AnsibleModule(

        # 引数受け取り
        argument_spec=dict(

            # 引数: data(str型,def:pong)
            data=dict(type='str', default='pong'),
        ),
        # 引数チェックを有効
        supports_check_mode=True
    )

    # 結果dict: resultを作成
    result = dict(
        # key: ping に引数で与えられたkey: data のvalueを格納
        ping=module.params['data'],
    )

    # resultの中身を key=value,の形で出力
    module.exit_json(**result)

if __name__ == '__main__':
    main()
```

```bash
$ ansible -i test_grp 192.168.56.104 -m pong -M library -u root -a data=hoge
192.168.56.104 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "hoge"
}
```

コイツを雛形にしよう。

# 引数を受け取る

``makefifo``に必要なパラメータは以下かなと考える。

|パラメータ|変数名(key名)|型|必須|
|---|---|---|---|
|ファイルパス|path|str|○|
|所有者(ユーザ)|owner|str|○|
|所有者(グループ)|group|str|○|
|パーミッション|mode|str|○|

まずはコイツらを受け取っって結果に返すだけのモジュールを作ってみる。

```python:makefifo.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

from ansible.module_utils.basic import AnsibleModule

# メイン処理
#-----------------------------------------------------------
def main():
    # AnsibleModuleクラス: moduleを作成
    module = AnsibleModule(

        # 引数受け取り
        argument_spec=dict(

            # 引数: path(str型, 必須)
            path=dict(type='str', required=True),
            # 引数: owner(str型, 必須)
            owner=dict(type='str', required=True),
            # 引数: group(str型, 必須)
            group=dict(type='str', required=True),
            # 引数: mode(str型, 必須)
            mode=dict(type='str', required=True),
        ),
        # 引数チェックを有効
        supports_check_mode=True
    )

    # 結果dict: resultを作成
    result = dict(
        path=module.params['path'],
        owner=module.params['owner'],
        group=module.params['group'],
        mode=module.params['mode'],
    )

    # resultの中身を key=value,の形で出力
    module.exit_json(**result)

if __name__ == '__main__':
    main()
```

``required``とかそのあたりは[Ansibleのモジュール開発（Python実装編)](https://dev.classmethod.jp/articles/ansible-develop-module-python/)さんを参考に。

さて動作確認...複数の引数を与えるのはどうするんだ？で調べたら以下が見つかった。

- [ansible コマンドでモジュール引数 ( パラメータ ) を複数渡す方法](https://qiita.com/YumaInaura/items/6e05d87858bd647d11ab)

ありがてぇありがてぇ。

```bash
$ ansible -i test_grp 192.168.56.104 -m makefifo -M library -u root -a "path=/tmp/hoge owner=root group=root mode=0644"
192.168.56.104 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "group": "root",
    "mode": "0644",
    "owner": "root",
    "path": "/tmp/hoge"
}
```

できた！よしよし、あとは``Python``で実装するだけだな。
取り敢えず今日はココまで。