Ansible 自作モジュール作成 ～その4:名前付きパイプがなければ作りたい人生(完結)～

前回の記事1：[Ansible 自作モジュール作成 ～その1:引数受け取りたい人生～](https://qiita.com/Ki2neudon/items/f6e1ee4fc8f29e12893f)
前回の記事2：[Ansible 自作モジュール作成 ～その2:ただコマンドを実行したい人生～](https://qiita.com/Ki2neudon/items/75b8c3dd406e11a386c1)
前回の記事3：[Ansible 自作モジュール作成 ～その3:Change要否を判定したい人生～](https://qiita.com/Ki2neudon/items/d6d4e5e98c210bd74f3a)

# 振り返り

- モジュールの雛形作った(その1)
- 複数の引数受け取れた(その1)
- 引数をコマンドと結合して実行した(その2)
- 変更要否を判定した(その3)

# コマンド実行要否の判定

実行するコマンドと実行する条件は以下のとおり

- ``mkfifo``コマンド
    - ``changed``判定時の「パイプがまだ存在しない」場合
- ``chown``コマンド、``chmod``コマンド
    - ``changed``が``True``の場合

# ``changed``判定を微修正

前回は「名前付きパイプが存在しない」もしくは「所有者、パーミッションが違う」場合に一律``changed = True``にしていたが、前者の場合だけ``mkfifo``コマンドが必要になるので戻り値の受け取り方を変える必要があった。

```python:mkfifo.py
: #(省略)
    # testコマンドでpathに名前付きパイプが存在するか確認
    rc_mkfifo, stdout, stderr = module.run_command("test -p " + module.params['path'])
    # 確認結果
    if ( rc_mkfifo != 0 ):
        # 存在しないので変更対象
        changed = True
    else:
        # 存在する場合はowner:group:modeを取得
        rc_chom, stdout, stderr = module.run_command("stat -c %U:%G:0%a " + module.params['path'])
        # 比較。コマンド結果は最後に改行コード \n が付くことに注意
        if ( stdout != module.params['owner'] + ':' + module.params['group'] + ':' + module.params['mode'] + '\n'):
            # owner:group:modeが異なるので変更対象
            changed = True

    # 終了
    module.exit_json(changed=changed,stdout=stdout,stderr=stderr)
: #(省略)
```

上記のとおり``test -p ``の結果は``rc_mkfifo``に入れるように変更した。

続いてコマンド実行要否を判定。

```python:mkfifo.py
: #(省略)
            # owner:group:modeが異なるので変更対象
            changed = True

    # 名前付きパイプが存在したか
    if ( rc_mkfifo != 0 ):
        # 存在しないのでコマンド実行
        rc, stdout, stderr = module.run_command('mkfifo ' + module.params['path'])

        # return codeが0でなければココで終了する
        if ( rc != 0 ):
            # コマンド異常
            module.fail_json(msg = stderr, rc = rc)

    # 変更が発生していたか
    if ( changed == True ):
        # 変更が発生しているので所有者を変更
        rc, stdout, stderr = module.run_command('chown ' + module.params['owner'] + ':' + module.params['group'] + ' ' + module.params['path'])
        # return codeが0でなければココで終了する
        if ( rc != 0 ):
            # コマンド異常
            module.fail_json(msg = stderr, rc = rc)

        # 変更が発生しているので権限を変更
        rc, stdout, stderr = module.run_command('chmod ' + module.params['mode'] + ' ' + module.params['path'])
        # return codeが0でなければココで終了する
        if ( rc != 0 ):
            # コマンド異常
            module.fail_json(msg = stderr, rc = rc)

    # 処理終了
    module.exit_json(changed = changed)
: #(省略)
```

実行～

```bash
# 事前にファイルが無いことの確認
$ ssh root@192.168.56.104 "ls -l /tmp"
合計 0
-rw-r--r--. 1 root root 0  5月 29 10:45 2020 udon.txt
-rw-------. 1 root root 0  5月 27 06:34 2020 yum.log

# 実行
$ ansible -i test_grp 192.168.56.104 -m mkfifo -M library -u root -a "path=/tmp/testfifo owner=root group=root mode=0777"
192.168.56.104 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": true
}

# 生成確認
$ ssh root@192.168.56.104 "ls -l /tmp"
合計 0
prwxrwxrwx. 1 root root 0  6月  1 02:52 2020 testfifo
-rw-r--r--. 1 root root 0  5月 29 10:45 2020 udon.txt
-rw-------. 1 root root 0  5月 27 06:34 2020 yum.log

# 再実行で空振りすることを確認
$ ansible -i test_grp 192.168.56.104 -m mkfifo -M library -u root -a "path=/tmp/testfifo owner=root group=root mode=0777"
192.168.56.104 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}
```

めでたし。

この他追加すべき内容は

- OS,バージョン毎にコマンドを分岐(``Factoryクラス``)
- テストrun時にコマンドを実行しない

なんてあるだろうけど、取り敢えずはひとつ作り上げたということでこのシリーズは終了。あー楽しかった。

# 全文

最後に全文を載せておきます。

```python:mkfifo.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(required=True,type='str'),
            # 引数: owner(str型,デフォルト=root)
            owner=dict(type='str',default='root'),
            # 引数: group(str型,デフォルト=root)
            group=dict(type='str',default='root'),
            # 引数: mode(str型,デフォルト=0644)
            mode=dict(type='str',default='0644'),
        ),
        # 引数チェックを有効
        supports_check_mode=True
    )
    # 変更有無を初期化(変更なし)
    changed = False

    # testコマンドでpathに名前付きパイプが存在するか確認
    rc_mkfifo, stdout, stderr = module.run_command('test -p ' + module.params['path'])
    # 確認結果
    if ( rc_mkfifo != 0 ):
        # 存在しないので変更対象
        changed = True
    else:
        # 存在する場合はowner:group:modeを取得
        rc_chom, stdout, stderr = module.run_command('stat -c %U:%G:0%a ' + module.params['path'])
        # 比較。コマンド結果は最後に改行コード \n が付くことに注意
        if ( stdout != module.params['owner'] + ':' + module.params['group'] + ':' + module.params['mode'] + '\n'):
            # owner:group:modeが異なるので変更対象
            changed = True

    # 名前付きパイプが存在したか
    if ( rc_mkfifo != 0 ):
        # 存在しないのでコマンド実行
        rc, stdout, stderr = module.run_command('mkfifo ' + module.params['path'])

        # return codeが0でなければココで終了する
        if ( rc != 0 ):
            # コマンド異常
            module.fail_json(msg = stderr, rc = rc)

    # 変更が発生していたか
    if ( changed == True ):
        # 変更が発生しているので所有者を変更
        rc, stdout, stderr = module.run_command('chown ' + module.params['owner'] + ':' + module.params['group'] + ' ' + module.params['path'])
        # return codeが0でなければココで終了する
        if ( rc != 0 ):
            # コマンド異常
            module.fail_json(msg = stderr, rc = rc)

        # 変更が発生しているので権限を変更
        rc, stdout, stderr = module.run_command('chmod ' + module.params['mode'] + ' ' + module.params['path'])
        # return codeが0でなければココで終了する
        if ( rc != 0 ):
            # コマンド異常
            module.fail_json(msg = stderr, rc = rc)

    # 処理終了
    module.exit_json(changed = changed)

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