てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] Python から Playbook を実行できる ansible-sdk をためしてみた

はじめに

最近、ansible/ansible-sdk というリポジトリが公開されました。

Python から Playbook を実行できるようです。とりあえず気になるので試してみました。

リポジトリ内にサンプルの Python スクリプトと、Playbookが入ってるので試すだけならかなり簡単でした。

現状はまだPyPi にも公開されておらず、ドキュメント上も TBD な箇所も多いため、発展途上のようです。書いてあることがすぐ通用しなくなる可能性が高いのでご了承ください。雰囲気だけということで・・。

[2023/05/14 追記ここから]

2023/03/08 に バージョン 1.0.0 としてリリースされていたようです。PyPi にも登録され、pip install ansible-sdk でインストールできるようになりました。ただし、記事全体的に 2022年10月に試した時点となっていますのでご注意ください。

[追記ここまで]

インストール

ドキュメントにインストール方法が掲載されています。

https://github.com/ansible/ansible-sdk/blob/main/docs/source/install.rst

まずは ansible-sdk リポジトリを clone して、できたディレクトリに移動します。

git clone https://github.com/ansible/ansible-sdk.git
cd ansible-sdk

(ドキュメントにあった git clone git://github.com/ansible/ansible-sdkOperation timed out というエラーになってしまったので、https にしました)

続いて関連パッケージのインストールです。今回試す範囲では不要そうなものもありますが、今回用の venv 内にとりあえず全部インストールしてみます。

pip install ansible-core
pip install ansible-runner
pip install receptorctl

最後に、ansible-sdk をインストールします。

pip install -e .

先述の通りまだ PyPi に登録されていないので、git clone して このようにする必要があります。

ansible 関連の Python パッケージは以下のようになりました。

 % pip list | grep -i ansible
ansible-core    2.13.5
ansible-runner  2.2.1
ansible-sdk     0.0.1   /Users/sakana/Documents/git/ansible-sdk

ここでの ansible-sdk のバージョン表記は 0.0.1 となりました。ただ、リポジトリ上は現状はまだタグやリリースがない状態です。

おためし

サンプル類の調査とそれの実行を試します。

サンプルはすべて、このコミット時点のものです。

サンプル類の調査

clone したディレクトリの examples 内にサンプルがいくつかあります。

% cd examples
% tree
├── datadir
│   ├── inventory
│   │   └── hosts
│   └── project
│       └── pb.yml
├── example_common.py
├── example_docker_job.py
├── example_mesh_job.py
├── example_podman_job.py
├── example_subprocess_job.py
└── receptor_config
    ├── bar.yml
    ├── baz.yml
    └── foo.yml

example_*.py の種類を見るといくつか形式があるようです。今思えば mesh に対応するために receptorctl をインストールするという手順だったのかなと思います。

今回は、一番シンプルそうな example_subprocess_job.py を試します。

example_subprocess_job.py

import asyncio


from ansible_sdk.executors import AnsibleSubprocessJobExecutor, AnsibleSubprocessJobOptions
from example_common import run_one_stdout, run_one_events, run_many


async def main():
    executor = AnsibleSubprocessJobExecutor()
    executor_options = AnsibleSubprocessJobOptions()

    await run_one_stdout(executor, executor_options)
    await run_one_events(executor, executor_options)
    await run_many(executor, executor_options)


if __name__ == '__main__':
    asyncio.run(main())

import されている example_common.py の方も見てみます。run_one_stdout だけ抜粋します。

async def run_one_stdout(executor, executor_options):
    """
    Run a single playbook job with several hosts and echo the display output as it arrives
    """
    try:
        job_def = AnsibleJobDef(data_dir='datadir', playbook='pb.yml')
        job_status = await executor.submit_job(job_def, executor_options)

        async for line in job_status.stdout_lines:
            print(line)

        # directly await the job object
        print('*** directly awaiting the job status...')
        await job_status
    finally:
        print('all done, exiting

Running Ansible jobs のドキュメントによると、以下のような使い方のようです。

  • AnsibleJobDed でジョブを定義する
    • JobExecutor でジョブを実行する
  • 今回の場合は、example_subprocess_job.pyimport している AnsibleSubprocessJobExecutorJobExecutor に当たるということだと思います。

AnsibleJobDeddata_dir で指定されているディレクトdatadir には Playbook と インベントリがありました。

# expamples ディレクトリ抜粋
├── datadir
│   ├── inventory
│   │   └── hosts
│   └── project
│       └── pb.yml

invnetory ディレクトリにインベントリファイルを入れ、project ディレクトリに Playbook を入れておくのは、ansible-runner のディレクトリ構造と同じですね。

この pb.ymlPython 経由で実行するということのようです。pingwait_forshell モジュールを利用した簡単な内容です。

インベントリは inventory/hosts が利用されます。

実行

お目当ての example_subprocess_job.py を実行します。

・・・と一回ためしたのですが、ログが沢山表示されてよくわからなくなってしまったので、実行する箇所をしぼりました。

    await run_one_stdout(executor, executor_options)   # これだけ
    # await run_one_events(executor, executor_options)
    # await run_many(executor, executor_options)

気を取り直して、再度実行。

% python example_subprocess_job.py

PLAY [taskhosts] ***************************************************************

TASK [Gathering Facts] *********************************************************
[WARNING]: Platform darwin on host h4 is using the discovered Python
interpreter at /usr/local/bin/python3.10, but future installation of another
Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-
core/2.13/reference_appendices/interpreter_discovery.html for more information.
ok: [h4]
[WARNING]: Platform darwin on host h2 is using the discovered Python
interpreter at /usr/local/bin/python3.10, but future installation of another
Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-
core/2.13/reference_appendices/interpreter_discovery.html for more information.
ok: [h2]
[WARNING]: Platform darwin on host h3 is using the discovered Python
interpreter at /usr/local/bin/python3.10, but future installation of another
Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-
core/2.13/reference_appendices/interpreter_discovery.html for more information.
ok: [h3]
[WARNING]: Platform darwin on host h1 is using the discovered Python
interpreter at /usr/local/bin/python3.10, but future installation of another
Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-
core/2.13/reference_appendices/interpreter_discovery.html for more information.
ok: [h1]
[WARNING]: Platform darwin on host h5 is using the discovered Python
interpreter at /usr/local/bin/python3.10, but future installation of another
Python interpreter could change the meaning of that path. See
https://docs.ansible.com/ansible-
core/2.13/reference_appendices/interpreter_discovery.html for more information.
ok: [h5]

TASK [ping] ********************************************************************
ok: [h5]
ok: [h2]
ok: [h3]
ok: [h4]
ok: [h1]

TASK [wait_for] ****************************************************************
ok: [h4]
ok: [h2]
ok: [h1]
ok: [h5]
ok: [h3]

TASK [shell] *******************************************************************
changed: [h4]
changed: [h3]
changed: [h5]
changed: [h2]
changed: [h1]

PLAY RECAP *********************************************************************
h1                         : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
h2                         : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
h3                         : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
h4                         : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
h5                         : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
*** directly awaiting the job status...
all done, exiting

っぽいのが表示されました。

なお、Gathering Facts で表示されている、Python インタープリターの警告は、明示的に指定していないと表示されるものです。

気になる場合は、inventory/hostsansible_python_interpreter 変数で指定するなりします。

[taskhosts:vars]
ansible_connection=local
ansible_pipelining=true
# 以下追記例
ansible_python_interpreter=/Users/sakana/envs/as/bin/python

おわりに

用意されたとても簡単なサンプルを動かしただけですが、ansible-sdk を試してみました。

使ったことないですが、Ansible の Python APIよりも簡単に使える雰囲気でした。ansible-runner を経由するおかげでしょうか。

先日書いたansible-rulebook もそうですが、何かに Ansible を組み込む手段が最近増えてきたように思います。

参考資料

もっと作り込まれたデモ github.com

www.youtube.com

www.youtube.com