diff options
author | Toshio Kuratomi <toshio@fedoraproject.org> | 2016-04-05 16:33:56 -0700 |
---|---|---|
committer | Toshio Kuratomi <toshio@fedoraproject.org> | 2016-04-12 08:01:07 -0700 |
commit | b5717ef696198ce11104885faaf5f86fe66a02cc (patch) | |
tree | 600983dd7743388c1ebc4aa5f10c8f0dc1135f6a /lib/ansible/executor/module_common.py | |
parent | b4b058794ea3d0623e59f8e3967f17e189f621d0 (diff) | |
download | ansible-b5717ef696198ce11104885faaf5f86fe66a02cc.tar.gz |
First whack at a recursive and ast based ziploader. I'm afraid this one may be too slow.
Diffstat (limited to 'lib/ansible/executor/module_common.py')
-rw-r--r-- | lib/ansible/executor/module_common.py | 72 |
1 files changed, 56 insertions, 16 deletions
diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py index f66787a18b..9685411dc7 100644 --- a/lib/ansible/executor/module_common.py +++ b/lib/ansible/executor/module_common.py @@ -20,6 +20,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import ast import base64 import json import os @@ -194,6 +195,38 @@ finally: pass ''' +class ModuleDepFinder(ast.NodeVisitor): + # Caveats: + # This code currently does not handle: + # * relative imports from py2.6+ from . import urls + # * python packages (directories with __init__.py in them) + IMPORT_PREFIX_SIZE = len('ansible.module_utils.') + + def __init__(self, *args, **kwargs): + super(ModuleDepFinder, self).__init__(*args, **kwargs) + self.module_files = set() + + def visit_Import(self, node): + # import ansible.module_utils.MODLIB[.other] + for alias in (a for a in node.names if a.name.startswith('ansible.module_utils.')): + py_mod = alias.name[self.IMPORT_PREFIX_SIZE:].split('.', 1)[0] + self.module_files.add(py_mod) + self.generic_visit(node) + + def visit_ImportFrom(self, node): + if node.module.startswith('ansible.module_utils'): + where_from = node.module[self.IMPORT_PREFIX_SIZE:] + # from ansible.module_utils.MODLIB[.other] import foo + if where_from: + py_mod = where_from.split('.', 1)[0] + self.module_files.add(py_mod) + else: + # from ansible.module_utils import MODLIB + for alias in node.names: + self.module_files.add(alias.name) + self.generic_visit(node) + + def _strip_comments(source): # Strip comments and blank lines from the wrapper buf = [] @@ -242,6 +275,24 @@ def _get_facility(task_vars): facility = task_vars['ansible_syslog_facility'] return facility +def meta_finder(data, snippet_names, snippet_data, zf): + tree = ast.parse(data) + finder = ModuleDepFinder() + finder.visit(tree) + + new_snippets = set() + for snippet_name in finder.module_files.difference(snippet_names): + fname = '%s.py' % snippet_name + new_snippets.add(snippet_name) + if snippet_name not in snippet_data: + snippet_data[snippet_name] = _slurp(os.path.join(_SNIPPET_PATH, fname)) + zf.writestr(os.path.join("ansible/module_utils", fname), snippet_data[snippet_name]) + snippet_names.update(new_snippets) + + for snippet_name in tuple(new_snippets): + meta_finder(snippet_data[snippet_name], snippet_names, snippet_data, zf) + del snippet_data[snippet_name] + def _find_snippet_imports(module_name, module_data, module_path, module_args, task_vars, module_compression): """ Given the source of the module, convert it to a Jinja2 template to insert @@ -283,8 +334,6 @@ def _find_snippet_imports(module_name, module_data, module_path, module_args, ta module_args_json = to_bytes(json.dumps(module_args)) output = BytesIO() - lines = module_data.split(b'\n') - snippet_names = set() if module_substyle == 'python': @@ -308,20 +357,10 @@ def _find_snippet_imports(module_name, module_data, module_path, module_args, ta zf.writestr('ansible/module_exec/__init__.py', b'') zf.writestr('ansible/module_exec/%s/__init__.py' % module_name, b"") - final_data = [] - - for line in lines: - if line.startswith(b'from ansible.module_utils.'): - tokens=line.split(b".") - snippet_name = tokens[2].split()[0] - snippet_names.add(snippet_name) - fname = to_unicode(snippet_name + b".py") - zf.writestr(os.path.join("ansible/module_utils", fname), _slurp(os.path.join(_SNIPPET_PATH, fname))) - final_data.append(line) - else: - final_data.append(line) + zf.writestr('ansible/module_exec/%s/__main__.py' % module_name, module_data) - zf.writestr('ansible/module_exec/%s/__main__.py' % module_name, b"\n".join(final_data)) + snippet_data = dict() + meta_finder(module_data, snippet_names, snippet_data, zf) zf.close() shebang, interpreter = _get_shebang(u'/usr/bin/python', task_vars) if shebang is None: @@ -341,11 +380,12 @@ def _find_snippet_imports(module_name, module_data, module_path, module_args, ta # modules that use ziploader may implement their own helpers and not # need basic.py. All the constants that we substituted into basic.py # for module_replacer are now available in other, better ways. - if b'basic' not in snippet_names: + if 'basic' not in snippet_names: raise AnsibleError("missing required import in %s: Did not import ansible.module_utils.basic for boilerplate helper code" % module_path) elif module_substyle == 'powershell': # Module replacer for jsonargs and windows + lines = module_data.split(b'\n') for line in lines: if REPLACER_WINDOWS in line: ps_data = _slurp(os.path.join(_SNIPPET_PATH, "powershell.ps1")) |