How to use ansible-playbook from python code - Ansible playbookをPython コードから実行する

# Overview   

- 基本はこれ。
 -  https://hawksnowlog.blogspot.com/2018/06/call-ansible-playbook-from-python.html

ただし、こちらの記事の記載の通り既存のplaybookが完成している場合、
互換性がないので使えない。

python nativeで最初から作るならば問題ないが、
既にAnsibleで作ってしまった環境を徐々に移行していくためには
当面、ansible-playbookを実行する必要がある。
従ってpython native コードに求められる機能は主に二つ。

- ansible-playbook を読み込める（実行できる）
 - 既存のレシピを実行する為
- ansible-playbook を生成できる
 - 完全に移行するまでは ansile-playbook... の実行も続くため

# どうやってansible-playbookを実行するか

perl の systemみたいに ansible-playbook xxxx としてシェルを呼ぶのでは意味が無い。
ちゃんとpythonから呼ぶ必要がある
どうやら ansible playbook-executor を使えば良いようだ。

具体的には以下の部分。
play_sourceを定義し、TQMを用いた場合と異なり
PlaybookExecutor に渡すことで、ansible-playbookコマンドを実行した通りの動作になる。
resultや出力の制御を行っていないがその辺はおいおい。

```python

    pbex = PlaybookExecutor(
        playbooks=[playbook_path],
        inventory=inventory,
        variable_manager=variable_manager,
        loader=loader,
        options=options,
        passwords=passwords
    )
    results = pbex.run()
```

taskを直接記述する場合

```python
        play_source = dict(
            name="Ansible Play",
            hosts=['localhost'],
            gather_facts='no',
            tasks=[dict(action=dict(
                module='command',
                args=dict(cmd='/usr/bin/uptime')
            ))]
        )
        play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

        tqm = None
        results_callback = ResultCallback()
        try:
            tqm = TaskQueueManager(
                inventory=inventory,
                variable_manager=variable_manager,
                loader=loader,
                options=options,
                passwords=passwords,
                stdout_callback=results_callback,
            )
```

## 2018.10.29 追記

以下のように`PlaybookExecutor`の`._tqm._stdout_callback`をフックしてやれば拾えた。
既存サンプルを更新しても良いが比較できるように
*ansible-runner_callback.py* として commit しました。


```python
class ResultCallback(CallbackBase):

    def __init__(self, *args, **kwargs):
        super(ResultCallback, self).__init__(*args, **kwargs)
        self.host_ok = {}
        self.host_unreachable = {}
        self.host_failed = {}

    def v2_runner_on_unreachable(self, result):
        super(ResultCallback, self).v2_runner_on_unreachable(result)
        print(json.dumps({result._host.name: result._result}, indent=4))

    def v2_runner_on_ok(self, result, **kwargs):
        super(ResultCallback, self).v2_runner_on_ok(result)
        print(json.dumps({result._host.name: result._result}, indent=4))

    def v2_runner_on_failed(self, result, *args, **kwargs):
        super(ResultCallback, self).v2_runner_on_failed(result)
        print(json.dumps({result._host.name: result._result}, indent=4))
```

```python

    executor = PlaybookExecutor(
        playbooks=[playbook_path],
        inventory=inventory,
        variable_manager=variable_manager,
        loader=loader,
        options=options,
        passwords=passwords
    )
    results_callback = ResultCallback()
    executor._tqm._stdout_callback = results_callback
    results = executor.run()
```


# サンプルスクリプト

- 以下に置いた。説明などは今後追加する
 - https://github.com/ainamori/python-ansible