Ansible/development
Lookupプラグインの作り方についてメモ。
独自のLookupモジュール †
以下のように独自のlookupプラグインを作成する。
- debug: msg={{ lookup('mylookup', 'args', key=value) }}
プラグインのひな型 †
上記のようなlookupプラグインの実体は以下のようなクラスとなる。
from __future__ import (absolute_import, division, print_function) __metaclass__ = type # lookupプラグインのベースクラス from ansible.plugins.lookup import LookupBase class LookupModule(LookupBase): # terms プラグインの引数 # variables ansibleで使える変数が渡される # kwargs プラグインのキーワード引数 def run(self, terms, variables, **kwargs): """my loopup plugin. description """ return []
プラグインのロード †
ansible.cfgで独自の場所を定義する場合は以下のようになる。
[defaults] lookup_plugins = /path/to/plugin
参考
lookupが呼ばれる流れ †
独自のプラグインが呼ばれる場合のバックトレースを見てみる。
lookupば呼ばれる流れ言っているが、実際にはjinja2でテンプレートが評価される流れである。
jinja2テンプレートはパースされコンパイルされて実行される。
(Pdb) bt /work/.pyenv/versions/3.8.1/bin/ansible-playbook(123)<module>() -> exit_code = cli.run() /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/cli/playbook.py(129)run() -> results = pbex.run() /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/executor/playbook_executor.py(172)run() -> result = self._tqm.run(play=play) /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/executor/task_queue_manager.py(242)run() -> play_return = strategy.run(iterator, play_context) /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/plugins/strategy/linear.py(310)run() -> self._queue_task(host, task, task_vars, play_context) /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/plugins/strategy/__init__.py(360)_queue_task() -> worker_prc.start() /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/executor/process/worker.py(96)start() -> return super(WorkerProcess, self).start() /work/.pyenv/versions/3.8.1/lib/python3.8/multiprocessing/process.py(121)start() -> self._popen = self._Popen(self) /work/.pyenv/versions/3.8.1/lib/python3.8/multiprocessing/context.py(276)_Popen() -> return Popen(process_obj) /work/.pyenv/versions/3.8.1/lib/python3.8/multiprocessing/popen_fork.py(19)__init__() -> self._launch(process_obj) /work/.pyenv/versions/3.8.1/lib/python3.8/multiprocessing/popen_fork.py(75)_launch() -> code = process_obj._bootstrap(parent_sentinel=child_r) /work/.pyenv/versions/3.8.1/lib/python3.8/multiprocessing/process.py(315)_bootstrap() -> self.run() /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/executor/process/worker.py(130)run() -> return self._run() /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/executor/process/worker.py(151)_run() -> executor_result = TaskExecutor( /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/executor/task_executor.py(146)run() -> res = self._execute() /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/executor/task_executor.py(588)_execute() -> self._task.post_validate(templar=templar) /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/playbook/task.py(296)post_validate() -> super(Task, self).post_validate(templar) /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/playbook/base.py(431)post_validate() -> value = templar.template(getattr(self, name)) /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/template/__init__.py(618)template() -> d[k] = self.template( /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/template/__init__.py(572)template() -> result = self.do_template( /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/template/__init__.py(837)do_template() -> res = j2_concat(rf) <template>(12)root() /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/jinja2/runtime.py(290)call() -> return __obj(*args, **kwargs) /work/.pyenv/versions/3.8.1/lib/python3.8/site-packages/ansible/template/__init__.py(731)_lookup() -> ran = instance.run(loop_terms, variables=self._available_variables, **kwargs) > /work/ansible/plugins/lookup/mylookup.py(10)run()
参考
- ansible/template/__init__.py#L772 - on https://github.com/ansible/ansible/
- ansible/template/__init__.py#L719 - on https://github.com/ansible/ansible/
Ansibleは、モジュールパラメータに渡されたデータをjinja2のテンプレートエンジンで評価している。
最終的にjinja2エンジンにcompileされて以下のように実行される。
from __future__ import division, generator_stop from jinja2.runtime import LoopContext, TemplateReference, Macro, Markup, TemplateRuntimeError, missing, concat, escape, markup_join, unicode_join, to_string, identity, TemplateNotFound, Namespace, Undefined name = None def root(context, missing=missing, environment=environment): resolve = context.resolve_or_missing undefined = environment.undefined cond_expr_undefined = Undefined if 0: yield None l_0_lookup = resolve('lookup') pass yield to_string(environment.finalize(context.call((undefined(name='lookup') if l_0_lookup is missing else l_0_lookup), 'mylookup', hoge='foo'))) blocks = {} debug_info = '1=12'
compile関数でバイトコードに変換され、実行コンテキストでexecで評価される。
参考