summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToshio Kuratomi <a.badger@gmail.com>2016-04-05 11:06:17 -0700
committerToshio Kuratomi <a.badger@gmail.com>2016-04-05 11:06:17 -0700
commit4b0aa1214ca989c71bb87f7a805b1e3673faefa9 (patch)
tree62f7f893cd26e41f002e1c84941da8be96e0510b
parent6a3670b1f07e16ec71b00f20ab2a9e60eecb5e34 (diff)
downloadansible-4b0aa1214ca989c71bb87f7a805b1e3673faefa9.tar.gz
Ziploader
* Ziploader proof of concept (jimi-c) * Cleanups to proof of concept ziploader branch: * python3 compatible base64 encoding * zipfile compression (still need to enable toggling this off for systems without zlib support in python) * Allow non-wildcard imports (still need to make this recusrsive so that we can have module_utils code that imports other module_utils code.) * Better tracebacks: module filename is kept and module_utils directory is kept so that tracebacks show the real filenames that the errors appear in. * Make sure we import modules that are used into the module_utils files that they are used in. * Set ansible version in a more pythonic way for ziploader than we were doing in module replacer * Make it possible to set the module compression as an inventory var This may be necessary on systems where python has been compiled without zlib compression. * Refactoring of module_common code: * module replacer only replaces values that make sense for that type of file (example: don't attempt to replace python imports if we're in a powershell module). * Implement configurable shebang support for ziploader wrapper * Implement client-side constants (for SELINUX_SPECIAL_FS and SYSLOG) via environment variable. * Remove strip_comments param as we're never going to use it (ruins line numbering) * Don't repeat ourselves about detecting REPLACER * Add an easy way to debug * Port test-module to the ziploader-aware modify_module() * strip comments and blank lines from the wrapper so we send less over the wire. * Comments cleanup * Remember to output write the module line itself in powershell modules * for line in lines strips the newlines so we have to add them back in
-rw-r--r--examples/ansible.cfg10
-rwxr-xr-xhacking/test-module3
-rw-r--r--lib/ansible/constants.py1
-rw-r--r--lib/ansible/executor/module_common.py336
-rw-r--r--lib/ansible/module_utils/a10.py4
-rw-r--r--lib/ansible/module_utils/basic.py70
-rw-r--r--lib/ansible/module_utils/ec2.py7
-rw-r--r--lib/ansible/module_utils/eos.py7
-rw-r--r--lib/ansible/module_utils/gce.py2
-rw-r--r--lib/ansible/module_utils/ios.py6
-rw-r--r--lib/ansible/module_utils/iosxr.py6
-rw-r--r--lib/ansible/module_utils/junos.py4
-rw-r--r--lib/ansible/module_utils/mysql.py6
-rw-r--r--lib/ansible/module_utils/netcfg.py3
-rw-r--r--lib/ansible/module_utils/nxos.py10
-rw-r--r--lib/ansible/module_utils/openswitch.py6
-rw-r--r--lib/ansible/module_utils/powershell.ps12
-rw-r--r--lib/ansible/module_utils/rax.py4
-rw-r--r--lib/ansible/module_utils/shell.py2
-rw-r--r--lib/ansible/module_utils/urls.py25
-rw-r--r--lib/ansible/module_utils/vca.py2
-rw-r--r--lib/ansible/module_utils/vmware.py2
-rw-r--r--lib/ansible/playbook/play_context.py2
-rw-r--r--lib/ansible/plugins/action/__init__.py2
-rw-r--r--test/integration/roles/test_hash_behavior/tasks/main.yml6
-rw-r--r--test/units/module_utils/basic/test__log_invocation.py1
-rw-r--r--test/units/module_utils/basic/test_exit_json.py3
-rw-r--r--test/units/module_utils/basic/test_log.py12
-rw-r--r--test/units/module_utils/basic/test_run_command.py1
-rw-r--r--test/units/module_utils/basic/test_safe_eval.py1
-rw-r--r--test/units/module_utils/test_basic.py22
-rw-r--r--test/units/plugins/action/test_action.py3
32 files changed, 442 insertions, 129 deletions
diff --git a/examples/ansible.cfg b/examples/ansible.cfg
index b1a6774f35..f0236d6ad6 100644
--- a/examples/ansible.cfg
+++ b/examples/ansible.cfg
@@ -236,6 +236,16 @@
# is used. This value must be an integer from 0 to 9.
#var_compression_level = 9
+# controls what compression method is used for new-style ansible modules when
+# they are sent to the remote system. The compression types depend on having
+# support compiled into both the controller's python and the client's python.
+# The names should match with the python Zipfile compression types:
+# * ZIP_STORED (no compression. available everywhere)
+# * ZIP_DEFLATED (uses zlib, the default)
+# These values may be set per host via the ansible_module_compression inventory
+# variable
+#module_compression = 'ZIP_DEFLATED'
+
# This controls the cutoff point (in bytes) on --diff for files
# set to 0 for unlimited (RAM may suffer!).
#max_diff_size = 1048576
diff --git a/hacking/test-module b/hacking/test-module
index d9f19b70b1..c824dcf577 100755
--- a/hacking/test-module
+++ b/hacking/test-module
@@ -131,7 +131,10 @@ def boilerplate_module(modfile, args, interpreter, check, destfile):
if check:
complex_args['_ansible_check_mode'] = True
+ modname = os.path.basename(modfile)
+ modname = os.path.splitext(modname)[0]
(module_data, module_style, shebang) = module_common.modify_module(
+ modname,
modfile,
complex_args,
task_vars=task_vars
diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py
index 52e86e6116..393598c3fe 100644
--- a/lib/ansible/constants.py
+++ b/lib/ansible/constants.py
@@ -140,6 +140,7 @@ DEFAULT_MODULE_NAME = get_config(p, DEFAULTS, 'module_name', None,
DEFAULT_FORKS = get_config(p, DEFAULTS, 'forks', 'ANSIBLE_FORKS', 5, integer=True)
DEFAULT_MODULE_ARGS = get_config(p, DEFAULTS, 'module_args', 'ANSIBLE_MODULE_ARGS', '')
DEFAULT_MODULE_LANG = get_config(p, DEFAULTS, 'module_lang', 'ANSIBLE_MODULE_LANG', os.getenv('LANG', 'en_US.UTF-8'))
+DEFAULT_MODULE_COMPRESSION= get_config(p, DEFAULTS, 'module_compression', None, 'ZIP_DEFLATED')
DEFAULT_TIMEOUT = get_config(p, DEFAULTS, 'timeout', 'ANSIBLE_TIMEOUT', 10, integer=True)
DEFAULT_POLL_INTERVAL = get_config(p, DEFAULTS, 'poll_interval', 'ANSIBLE_POLL_INTERVAL', 15, integer=True)
DEFAULT_REMOTE_USER = get_config(p, DEFAULTS, 'remote_user', 'ANSIBLE_REMOTE_USER', None)
diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py
index 9d50be0ca3..63f8c4da5b 100644
--- a/lib/ansible/executor/module_common.py
+++ b/lib/ansible/executor/module_common.py
@@ -20,11 +20,12 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-# from python and deps
-from io import BytesIO
+import base64
import json
import os
import shlex
+import zipfile
+from io import BytesIO
# from Ansible
from ansible import __version__
@@ -32,13 +33,17 @@ from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.utils.unicode import to_bytes, to_unicode
+try:
+ from __main__ import display
+except ImportError:
+ from ansible.utils.display import Display
+ display = Display()
+
REPLACER = b"#<<INCLUDE_ANSIBLE_MODULE_COMMON>>"
-REPLACER_ARGS = b"\"<<INCLUDE_ANSIBLE_MODULE_ARGS>>\""
+REPLACER_VERSION = b"\"<<ANSIBLE_VERSION>>\""
REPLACER_COMPLEX = b"\"<<INCLUDE_ANSIBLE_MODULE_COMPLEX_ARGS>>\""
REPLACER_WINDOWS = b"# POWERSHELL_COMMON"
-REPLACER_WINARGS = b"<<INCLUDE_ANSIBLE_MODULE_WINDOWS_ARGS>>"
REPLACER_JSONARGS = b"<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"
-REPLACER_VERSION = b"\"<<ANSIBLE_VERSION>>\""
REPLACER_SELINUX = b"<<SELINUX_SPECIAL_FILESYSTEMS>>"
# We could end up writing out parameters with unicode characters so we need to
@@ -50,6 +55,82 @@ _SNIPPET_PATH = os.path.join(os.path.dirname(__file__), '..', 'module_utils')
# ******************************************************************************
+ZIPLOADER_TEMPLATE = u'''%(shebang)s
+# -*- coding: utf-8 -*-'
+import os
+import sys
+import base64
+import tempfile
+
+ZIPDATA = """%(zipdata)s"""
+
+def debug(command, zipped_mod):
+ # The code here normally doesn't run. It's only used for debugging on the
+ # remote machine. Run with ANSIBLE_KEEP_REMOTE_FILES=1 envvar and -vvv
+ # to save the module file remotely. Login to the remote machine and use
+ # /path/to/module explode to extract the ZIPDATA payload into source
+ # files. Edit the source files to instrument the code or experiment with
+ # different values. Then use /path/to/module execute to run the extracted
+ # files you've edited instead of the actual zipped module.
+ #
+ # Okay to use __file__ here because we're running from a kept file
+ basedir = os.path.dirname(__file__)
+ if command == 'explode':
+ import zipfile
+ z = zipfile.ZipFile(zipped_mod)
+ for filename in z.namelist():
+ if filename.startswith('/'):
+ raise Exception('Something wrong with this module zip file: should not contain absolute paths')
+ dest_filename = os.path.join(basedir, filename)
+ if dest_filename.endswith(os.path.sep) and not os.path.exists(dest_filename):
+ os.makedirs(dest_filename)
+ else:
+ directory = os.path.dirname(dest_filename)
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+ f = open(dest_filename, 'w')
+ f.write(z.read(filename))
+ f.close()
+ print('Module expanded into: %%s' %% os.path.join(basedir, 'ansible'))
+ elif command == 'execute':
+ sys.path.insert(0, basedir)
+ from ansible.module_exec.%(ansible_module)s.__main__ import main
+ main()
+
+os.environ['ANSIBLE_MODULE_ARGS'] = %(args)s
+os.environ['ANSIBLE_MODULE_CONSTANTS'] = %(constants)s
+
+try:
+ temp_fd, temp_path = tempfile.mkstemp(prefix='ansible_')
+ os.write(temp_fd, base64.b64decode(ZIPDATA))
+ if len(sys.argv) == 2:
+ debug(sys.argv[1], temp_path)
+ else:
+ sys.path.insert(0, temp_path)
+ from ansible.module_exec.%(ansible_module)s.__main__ import main
+ main()
+finally:
+ try:
+ os.close(temp_fd)
+ os.remove(temp_path)
+ except NameError:
+ # mkstemp failed
+ pass
+'''
+
+def _strip_comments(source):
+ # Strip comments and blank lines from the wrapper
+ buf = []
+ for line in source.splitlines():
+ l = line.strip()
+ if not l or l.startswith(u'#'):
+ continue
+ buf.append(line)
+ return u'\n'.join(buf)
+
+# ZIPLOADER_TEMPLATE stripped of comments for smaller over the wire size
+STRIPPED_ZIPLOADER_TEMPLATE = _strip_comments(ZIPLOADER_TEMPLATE)
+
def _slurp(path):
if not os.path.exists(path):
raise AnsibleError("imported module support code does not exist at %s" % path)
@@ -58,69 +139,171 @@ def _slurp(path):
fd.close()
return data
-def _find_snippet_imports(module_data, module_path, strip_comments):
+def _get_shebang(interpreter, task_vars, args=tuple()):
+ """
+ Note not stellar API:
+ Returns None instead of always returning a shebang line. Doing it this
+ way allows the caller to decide to use the shebang it read from the
+ file rather than trust that we reformatted what they already have
+ correctly.
+ """
+ interpreter_config = u'ansible_%s_interpreter' % os.path.basename(interpreter)
+
+ if interpreter_config not in task_vars:
+ return None
+
+ interpreter = task_vars[interpreter_config]
+ shebang = u'#!' + interpreter
+
+ if args:
+ shebang = shebang + u' ' + u' '.join(args)
+
+ return shebang
+
+def _get_facility(task_vars):
+ facility = C.DEFAULT_SYSLOG_FACILITY
+ if 'ansible_syslog_facility' in task_vars:
+ facility = task_vars['ansible_syslog_facility']
+ return facility
+
+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
module code and return whether it's a new or old style module.
"""
- module_style = 'old'
+ module_substyle = module_style = 'old'
+
+ # module_style is something important to calling code (ActionBase). It
+ # determines how arguments are formatted (json vs k=v) and whether
+ # a separate arguments file needs to be sent over the wire.
+ # module_substyle is extra information that's useful internally. It tells
+ # us what we have to look to substitute in the module files and whether
+ # we're using module replacer or ziploader to format the module itself.
if REPLACER in module_data:
+ # Do REPLACER before from ansible.module_utils because we need make sure
+ # we substitute "from ansible.module_utils basic" for REPLACER
+ module_style = 'new'
+ module_substyle = 'python'
+ module_data = module_data.replace(REPLACER, b'from ansible.module_utils.basic import *')
+ elif b'from ansible.module_utils.' in module_data:
module_style = 'new'
+ module_substyle = 'python'
elif REPLACER_WINDOWS in module_data:
module_style = 'new'
+ module_substyle = 'powershell'
elif REPLACER_JSONARGS in module_data:
module_style = 'new'
- elif b'from ansible.module_utils.' in module_data:
- module_style = 'new'
+ module_substyle = 'jsonargs'
elif b'WANT_JSON' in module_data:
- module_style = 'non_native_want_json'
+ module_substyle = module_style = 'non_native_want_json'
+
+ shebang = None
+ # Neither old-style nor non_native_want_json modules should be modified
+ # except for the shebang line (Done by modify_module)
+ if module_style in ('old', 'non_native_want_json'):
+ return module_data, module_style, shebang
+
+ module_args_json = to_bytes(json.dumps(module_args))
output = BytesIO()
lines = module_data.split(b'\n')
- snippet_names = []
-
- for line in lines:
-
- if REPLACER in line:
- output.write(_slurp(os.path.join(_SNIPPET_PATH, "basic.py")))
- snippet_names.append(b'basic')
- if REPLACER_WINDOWS in line:
- ps_data = _slurp(os.path.join(_SNIPPET_PATH, "powershell.ps1"))
- output.write(ps_data)
- snippet_names.append(b'powershell')
- elif line.startswith(b'from ansible.module_utils.'):
- tokens=line.split(b".")
- import_error = False
- if len(tokens) != 3:
- import_error = True
- if b" import *" not in line:
- import_error = True
- if import_error:
- raise AnsibleError("error importing module in %s, expecting format like 'from ansible.module_utils.<lib name> import *'" % module_path)
- snippet_name = tokens[2].split()[0]
- snippet_names.append(snippet_name)
- output.write(_slurp(os.path.join(_SNIPPET_PATH, to_unicode(snippet_name) + ".py")))
- else:
- if strip_comments and line.startswith(b"#") or line == b'':
- pass
- output.write(line)
- output.write(b"\n")
-
- if not module_path.endswith(".ps1"):
- # Unixy modules
- if len(snippet_names) > 0 and not b'basic' in snippet_names:
- raise AnsibleError("missing required import in %s: from ansible.module_utils.basic import *" % module_path)
- else:
- # Windows modules
- if len(snippet_names) > 0 and not b'powershell' in snippet_names:
+
+ snippet_names = set()
+
+ if module_substyle == 'python':
+ # ziploader for new-style python classes
+ python_repred_args = to_bytes(repr(module_args_json))
+ constants = dict(
+ SELINUX_SPECIAL_FS=C.DEFAULT_SELINUX_SPECIAL_FS,
+ SYSLOG_FACILITY=_get_facility(task_vars),
+ )
+ python_repred_constants = to_bytes(repr(json.dumps(constants)), errors='strict')
+
+ try:
+ compression_method = getattr(zipfile, module_compression)
+ except AttributeError:
+ display.warning(u'Bad module compression string specified: %s. Using ZIP_STORED (no compression)' % module_compression)
+ compression_method = zipfile.ZIP_STORED
+ zipoutput = BytesIO()
+ zf = zipfile.ZipFile(zipoutput, mode='w', compression=compression_method)
+ zf.writestr('ansible/__init__.py', b''.join((b"__version__ = '", to_bytes(__version__), b"'\n")))
+ zf.writestr('ansible/module_utils/__init__.py', b'')
+ 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, b"\n".join(final_data))
+ zf.close()
+ shebang = _get_shebang(u'/usr/bin/python', task_vars) or u'#!/usr/bin/python'
+ output.write(to_bytes(STRIPPED_ZIPLOADER_TEMPLATE % dict(
+ zipdata=base64.b64encode(zipoutput.getvalue()),
+ ansible_module=module_name,
+ args=python_repred_args,
+ constants=python_repred_constants,
+ shebang=shebang,
+ )))
+ module_data = output.getvalue()
+
+ # Sanity check from 1.x days. Maybe too strict. Some custom python
+ # 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:
+ 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
+ for line in lines:
+ if REPLACER_WINDOWS in line:
+ ps_data = _slurp(os.path.join(_SNIPPET_PATH, "powershell.ps1"))
+ output.write(ps_data)
+ snippet_names.add(b'powershell')
+ continue
+ output.write(line + b'\n')
+ module_data = output.getvalue()
+ module_data = module_data.replace(REPLACER_JSONARGS, module_args_json)
+
+ # Sanity check from 1.x days. This is currently useless as we only
+ # get here if we are going to substitute powershell.ps1 into the
+ # module anyway. Leaving it for when/if we add other powershell
+ # module_utils files.
+ if b'powershell' not in snippet_names:
raise AnsibleError("missing required import in %s: # POWERSHELL_COMMON" % module_path)
- return (output.getvalue(), module_style)
+ elif module_substyle == 'jsonargs':
+ # these strings could be included in a third-party module but
+ # officially they were included in the 'basic' snippet for new-style
+ # python modules (which has been replaced with something else in
+ # ziploader) If we remove them from jsonargs-style module replacer
+ # then we can remove them everywhere.
+ module_data = module_data.replace(REPLACER_VERSION, to_bytes(repr(__version__)))
+ module_data = module_data.replace(REPLACER_COMPLEX, python_repred_args)
+ module_data = module_data.replace(REPLACER_SELINUX, to_bytes(','.join(C.DEFAULT_SELINUX_SPECIAL_FS)))
+
+ # The main event -- substitute the JSON args string into the module
+ module_data = module_data.replace(REPLACER_JSONARGS, module_args_json)
+
+ facility = b'syslog.' + to_bytes(_get_facility(task_vars), errors='strict')
+ module_data = module_data.replace(b'syslog.LOG_USER', facility)
+
+ return (module_data, module_style, shebang)
# ******************************************************************************
-def modify_module(module_path, module_args, task_vars=dict(), strip_comments=False):
+def modify_module(module_name, module_path, module_args, task_vars=dict(), module_compression='ZIP_STORED'):
"""
Used to insert chunks of code into modules before transfer rather than
doing regular python imports. This allows for more efficient transfer in
@@ -163,43 +346,28 @@ def modify_module(module_path, module_args, task_vars=dict(), strip_comments=Fal
# read in the module source
module_data = f.read()
- (module_data, module_style) = _find_snippet_imports(module_data, module_path, strip_comments)
+ (module_data, module_style, shebang) = _find_snippet_imports(module_name, module_data, module_path, module_args, task_vars, module_compression)
- module_args_json = to_bytes(json.dumps(module_args))
- python_repred_args = to_bytes(repr(module_args_json))
-
- # these strings should be part of the 'basic' snippet which is required to be included
- module_data = module_data.replace(REPLACER_VERSION, to_bytes(repr(__version__)))
- module_data = module_data.replace(REPLACER_COMPLEX, python_repred_args)
- module_data = module_data.replace(REPLACER_WINARGS, module_args_json)
- module_data = module_data.replace(REPLACER_JSONARGS, module_args_json)
- module_data = module_data.replace(REPLACER_SELINUX, to_bytes(','.join(C.DEFAULT_SELINUX_SPECIAL_FS)))
-
- if module_style == 'new':
- facility = C.DEFAULT_SYSLOG_FACILITY
- if 'ansible_syslog_facility' in task_vars:
- facility = task_vars['ansible_syslog_facility']
- module_data = module_data.replace(b'syslog.LOG_USER', to_bytes("syslog.%s" % facility))
-
- lines = module_data.split(b"\n", 1)
- shebang = None
- if lines[0].startswith(b"#!"):
- shebang = lines[0].strip()
- args = shlex.split(str(shebang[2:]))
- interpreter = args[0]
- interpreter_config = 'ansible_%s_interpreter' % os.path.basename(interpreter)
- interpreter = to_bytes(interpreter)
-
- if interpreter_config in task_vars:
- interpreter = to_bytes(task_vars[interpreter_config], errors='strict')
- lines[0] = shebang = b"#!{0} {1}".format(interpreter, b" ".join(args[1:]))
-
- if os.path.basename(interpreter).startswith(b'python'):
- lines.insert(1, ENCODING_STRING)
- else:
- # No shebang, assume a binary module?
- pass
+ if shebang is None:
+ lines = module_data.split(b"\n", 1)
+ if lines[0].startswith(b"#!"):
+ shebang = lines[0].strip()
+ args = shlex.split(str(shebang[2:]))
+ interpreter = args[0]
+ interpreter = to_bytes(interpreter)
+
+ new_shebang = to_bytes(_get_shebang(interpreter, task_vars, args[1:]), errors='strict', nonstring='passthru')
+ if new_shebang:
+ lines[0] = shebang = new_shebang
- module_data = b"\n".join(lines)
+ if os.path.basename(interpreter).startswith(b'python'):
+ lines.insert(1, ENCODING_STRING)
+ else:
+ # No shebang, assume a binary module?
+ pass
+
+ module_data = b"\n".join(lines)
+ else:
+ shebang = to_bytes(shebang, errors='strict')
return (module_data, module_style, shebang)
diff --git a/lib/ansible/module_utils/a10.py b/lib/ansible/module_utils/a10.py
index 92a9a69c69..45fda0eb39 100644
--- a/lib/ansible/module_utils/a10.py
+++ b/lib/ansible/module_utils/a10.py
@@ -27,8 +27,8 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import json
-# Note: modules using this must have from ansible.module_utils.urls import *
-# before this is imported
+
+from ansible.module_utils.urls import fetch_url
AXAPI_PORT_PROTOCOLS = {
'tcp': 2,
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index f45973012b..1af0bdb01a 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -27,25 +27,13 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
-# == BEGIN DYNAMICALLY INSERTED CODE ==
-
-ANSIBLE_VERSION = "<<ANSIBLE_VERSION>>"
-
-MODULE_ARGS = "<<INCLUDE_ANSIBLE_MODULE_ARGS>>"
-MODULE_COMPLEX_ARGS = "<<INCLUDE_ANSIBLE_MODULE_COMPLEX_ARGS>>"
-
BOOLEANS_TRUE = ['yes', 'on', '1', 'true', 1, True]
BOOLEANS_FALSE = ['no', 'off', '0', 'false', 0, False]
BOOLEANS = BOOLEANS_TRUE + BOOLEANS_FALSE
-SELINUX_SPECIAL_FS="<<SELINUX_SPECIAL_FILESYSTEMS>>"
-
# ansible modules can be written in any language. To simplify
-# development of Python modules, the functions available here
-# can be inserted in any module source automatically by including
-# #<<INCLUDE_ANSIBLE_MODULE_COMMON>> on a blank line by itself inside
-# of an ansible module. The source of this common code lives
-# in ansible/executor/module_common.py
+# development of Python modules, the functions available here can
+# be used to do many common tasks
import locale
import os
@@ -231,6 +219,27 @@ except ImportError:
_literal_eval = literal_eval
+from ansible import __version__
+# Backwards compat. New code should just import and use __version__
+ANSIBLE_VERSION = __version__
+
+try:
+ # MODULE_COMPLEX_ARGS is an old name kept for backwards compat
+ MODULE_COMPLEX_ARGS = os.environ.pop('ANSIBLE_MODULE_ARGS')
+except KeyError:
+ # This file might be used for its utility functions. So don't fail if
+ # running outside of a module environment (will fail in _load_params()
+ # instead)
+ MODULE_COMPLEX_ARGS = None
+
+try:
+ # ARGS are for parameters given in the playbook. Constants are for things
+ # that ansible needs to configure controller side but are passed to all
+ # modules.
+ MODULE_CONSTANTS = os.environ.pop('ANSIBLE_MODULE_CONSTANTS')
+except KeyError:
+ MODULE_CONSTANTS = None
+
FILE_COMMON_ARGUMENTS=dict(
src = dict(),
mode = dict(type='raw'),
@@ -539,7 +548,8 @@ class AnsibleModule(object):
if k not in self.argument_spec:
self.argument_spec[k] = v
- self.params = self._load_params()
+ self._load_constants()
+ self._load_params()
# append to legal_inputs and then possibly check against them
try:
@@ -754,7 +764,7 @@ class AnsibleModule(object):
(device, mount_point, fstype, options, rest) = line.split(' ', 4)
if path_mount_point == mount_point:
- for fs in SELINUX_SPECIAL_FS.split(','):
+ for fs in self.constants['SELINUX_SPECIAL_FS']:
if fs in fstype:
special_context = self.selinux_context(path_mount_point)
return (True, special_context)
@@ -1412,16 +1422,38 @@ class AnsibleModule(object):
self.params[k] = default
def _load_params(self):
- ''' read the input and return a dictionary and the arguments string '''
+ ''' read the input and set the params attribute'''
+ if MODULE_COMPLEX_ARGS is None:
+ # This helper used too early for fail_json to work.
+ print('{"msg": "Error: ANSIBLE_MODULE_ARGS not found in environment. Unable to figure out what parameters were passed", "failed": true}')
+ sys.exit(1)
+
params = json_dict_unicode_to_bytes(json.loads(MODULE_COMPLEX_ARGS))
if params is None:
params = dict()
- return params
+ self.params = params
+
+ def _load_constants(self):
+ ''' read the input and set the constants attribute'''
+ if MODULE_CONSTANTS is None:
+ # This helper used too early for fail_json to work.
+ print('{"msg": "Error: ANSIBLE_MODULE_CONSTANTS not found in environment. Unable to figure out what constants were passed", "failed": true}')
+ sys.exit(1)
+
+ # Make constants into "native string"
+ if sys.version_info >= (3,):
+ constants = json_dict_bytes_to_unicode(json.loads(MODULE_CONSTANTS))
+ else:
+ constants = json_dict_unicode_to_bytes(json.loads(MODULE_CONSTANTS))
+ if constants is None:
+ constants = dict()
+ self.constants = constants
def _log_to_syslog(self, msg):
if HAS_SYSLOG:
module = 'ansible-%s' % os.path.basename(__file__)
- syslog.openlog(str(module), 0, syslog.LOG_USER)
+ facility = getattr(syslog, self.constants.get('SYSLOG_FACILITY', 'LOG_USER'), syslog.LOG_USER)
+ syslog.openlog(str(module), 0, facility)
syslog.syslog(syslog.LOG_INFO, msg)
def debug(self, msg):
diff --git a/lib/ansible/module_utils/ec2.py b/lib/ansible/module_utils/ec2.py
index b0c4a323ad..d966429a8c 100644
--- a/lib/ansible/module_utils/ec2.py
+++ b/lib/ansible/module_utils/ec2.py
@@ -25,10 +25,17 @@
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
import os
from time import sleep
try:
+ import boto
+ HAS_BOTO = True
+except ImportError:
+ HAS_BOTO = False
+
+try:
import boto3
import botocore
HAS_BOTO3 = True
diff --git a/lib/ansible/module_utils/eos.py b/lib/ansible/module_utils/eos.py
index 7ff043f247..d2cada6007 100644
--- a/lib/ansible/module_utils/eos.py
+++ b/lib/ansible/module_utils/eos.py
@@ -18,6 +18,13 @@
#
import os
+import re
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.shell import Shell, Command, HAS_PARAMIKO
+from ansible.module_utils.netcfg import parse
+from ansible.module_utils.urls import fetch_url
+
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
NET_COMMON_ARGS = dict(
diff --git a/lib/ansible/module_utils/gce.py b/lib/ansible/module_utils/gce.py
index ad72a9b82b..5f222739aa 100644
--- a/lib/ansible/module_utils/gce.py
+++ b/lib/ansible/module_utils/gce.py
@@ -29,6 +29,8 @@
import os
import traceback
+
+from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver
USER_AGENT_PRODUCT="Ansible-gce"
diff --git a/lib/ansible/module_utils/ios.py b/lib/ansible/module_utils/ios.py
index 29c5e92ddf..a8a6345d58 100644
--- a/lib/ansible/module_utils/ios.py
+++ b/lib/ansible/module_utils/ios.py
@@ -17,6 +17,12 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
+import re
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.shell import Shell, Command, HAS_PARAMIKO
+from ansible.module_utils.netcfg import parse
+
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
NET_COMMON_ARGS = dict(
diff --git a/lib/ansible/module_utils/iosxr.py b/lib/ansible/module_utils/iosxr.py
index d56a076859..76c644f783 100644
--- a/lib/ansible/module_utils/iosxr.py
+++ b/lib/ansible/module_utils/iosxr.py
@@ -17,6 +17,12 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
+import re
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.shell import Shell, HAS_PARAMIKO
+from ansible.module_utils.netcfg import parse
+
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
NET_COMMON_ARGS = dict(
diff --git a/lib/ansible/module_utils/junos.py b/lib/ansible/module_utils/junos.py
index 85aad60aca..be1a9b8c23 100644
--- a/lib/ansible/module_utils/junos.py
+++ b/lib/ansible/module_utils/junos.py
@@ -17,6 +17,10 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.shell import Shell, HAS_PARAMIKO
+from ansible.module_utils.netcfg import parse
+
NET_COMMON_ARGS = dict(
host=dict(required=True),
port=dict(default=22, type='int'),
diff --git a/lib/ansible/module_utils/mysql.py b/lib/ansible/module_utils/mysql.py
index 693650dac6..e5ff711987 100644
--- a/lib/ansible/module_utils/mysql.py
+++ b/lib/ansible/module_utils/mysql.py
@@ -27,7 +27,13 @@
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import os
+try:
+ import MySQLdb
+ mysqldb_found = True
+except ImportError:
+ mysqldb_found = False
def mysql_connect(module, login_user=None, login_password=None, config_file='', ssl_cert=None, ssl_key=None, ssl_ca=None, db=None, cursor_class=None, connect_timeout=30):
config = {
diff --git a/lib/ansible/module_utils/netcfg.py b/lib/ansible/module_utils/netcfg.py
index 2deaba8c0f..0c2f57880b 100644
--- a/lib/ansible/module_utils/netcfg.py
+++ b/lib/ansible/module_utils/netcfg.py
@@ -20,6 +20,9 @@
import re
import collections
import itertools
+import shlex
+
+from ansible.module_utils.basic import BOOLEANS_TRUE, BOOLEANS_FALSE
DEFAULT_COMMENT_TOKENS = ['#', '!']
diff --git a/lib/ansible/module_utils/nxos.py b/lib/ansible/module_utils/nxos.py
index 83a340bb16..7b90ce8dd1 100644
--- a/lib/ansible/module_utils/nxos.py
+++ b/lib/ansible/module_utils/nxos.py
@@ -16,6 +16,14 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
+
+import re
+
+from ansible.module_utils.urls import fetch_url
+from ansible.module_utils.shell import Shell, HAS_PARAMIKO
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.netcfg import parse
+
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
NET_COMMON_ARGS = dict(
@@ -117,8 +125,6 @@ class Nxapi(object):
(command_type, ','.join(NXAPI_COMMAND_TYPES))
self.module_fail_json(msg=msg)
- debug = dict()
-
data = self._get_body(clist, command_type, encoding)
data = self.module.jsonify(data)
diff --git a/lib/ansible/module_utils/openswitch.py b/lib/ansible/module_utils/openswitch.py
index a2fa7ee827..e4596712df 100644
--- a/lib/ansible/module_utils/openswitch.py
+++ b/lib/ansible/module_utils/openswitch.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
+import re
import time
import json
@@ -28,6 +29,11 @@ try:
except ImportError:
HAS_OPS = False
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.urls import fetch_url
+from ansible.module_utils.shell import Shell, HAS_PARAMIKO
+from ansible.module_utils.netcfg import parse
+
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
NET_COMMON_ARGS = dict(
diff --git a/lib/ansible/module_utils/powershell.ps1 b/lib/ansible/module_utils/powershell.ps1
index fc1d49f4ed..757279aba6 100644
--- a/lib/ansible/module_utils/powershell.ps1
+++ b/lib/ansible/module_utils/powershell.ps1
@@ -32,7 +32,7 @@ Set-StrictMode -Version 2.0
# JSON; assign them to an environment variable and redefine $args so existing
# modules will continue to work.
$complex_args = @'
-<<INCLUDE_ANSIBLE_MODULE_WINDOWS_ARGS>>
+<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>
'@
Set-Content env:MODULE_COMPLEX_ARGS -Value $complex_args
$args = @('env:MODULE_COMPLEX_ARGS')
diff --git a/lib/ansible/module_utils/rax.py b/lib/ansible/module_utils/rax.py
index 6dd0b9da65..d9eb74f071 100644
--- a/lib/ansible/module_utils/rax.py
+++ b/lib/ansible/module_utils/rax.py
@@ -32,6 +32,8 @@ import os
import re
from uuid import UUID
+from ansible import __version__
+from ansible.module_utils.basic import BOOLEANS
FINAL_STATUSES = ('ACTIVE', 'ERROR')
VOLUME_STATUS = ('available', 'attaching', 'creating', 'deleting', 'in-use',
@@ -262,7 +264,7 @@ def rax_required_together():
def setup_rax_module(module, rax_module, region_required=True):
"""Set up pyrax in a standard way for all modules"""
- rax_module.USER_AGENT = 'ansible/%s %s' % (ANSIBLE_VERSION,
+ rax_module.USER_AGENT = 'ansible/%s %s' % (__version__,
rax_module.USER_AGENT)
api_key = module.params.get('api_key')
diff --git a/lib/ansible/module_utils/shell.py b/lib/ansible/module_utils/shell.py
index 4d73c8db52..90100fece4 100644
--- a/lib/ansible/module_utils/shell.py
+++ b/lib/ansible/module_utils/shell.py
@@ -146,7 +146,7 @@ class Shell(object):
cmd = '%s\r' % str(command)
self.shell.sendall(cmd)
responses.append(self.receive(command))
- except socket.timeout, exc:
+ except socket.timeout:
raise ShellError("timeout trying to send command", cmd)
return responses
diff --git a/lib/ansible/module_utils/urls.py b/lib/ansible/module_utils/urls.py
index 23f2869eec..17c3662f5f 100644
--- a/lib/ansible/module_utils/urls.py
+++ b/lib/ansible/module_utils/urls.py
@@ -81,6 +81,18 @@
# agrees to be bound by the terms and conditions of this License
# Agreement.
+import httplib
+import netrc
+import os
+import re
+import sys
+import socket
+import platform
+import tempfile
+import base64
+
+from ansible.module_utils.basic import get_distribution
+
try:
import urllib2
HAS_URLLIB2 = True
@@ -151,8 +163,6 @@ if not HAS_MATCH_HOSTNAME:
"""The match_hostname() function from Python 3.4, essential when using SSL."""
- import re
-
class CertificateError(ValueError):
pass
@@ -257,17 +267,6 @@ if not HAS_MATCH_HOSTNAME:
HAS_MATCH_HOSTNAME = True
-import httplib
-import netrc
-import os
-import re
-import sys
-import socket
-import platform
-import tempfile
-import base64
-
-
# This is a dummy cacert provided for Mac OS since you need at least 1
# ca cert, regardless of validity, for Python on Mac OS to use the
# keychain functionality in OpenSSL for validating SSL certificates.
diff --git a/lib/ansible/module_utils/vca.py b/lib/ansible/module_utils/vca.py
index 9737cca8b4..92e51183d3 100644
--- a/lib/ansible/module_utils/vca.py
+++ b/lib/ansible/module_utils/vca.py
@@ -21,6 +21,8 @@ try:
except ImportError:
HAS_PYVCLOUD = False
+from ansible.module_utils.basic import AnsibleModule
+
SERVICE_MAP = {'vca': 'ondemand', 'vchs': 'subscription', 'vcd': 'vcd'}
LOGIN_HOST = {'vca': 'vca.vmware.com', 'vchs': 'vchs.vmware.com'}
diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py
index 9950cdd937..5316cdf1db 100644
--- a/lib/ansible/module_utils/vmware.py
+++ b/lib/ansible/module_utils/vmware.py
@@ -25,7 +25,7 @@ try:
# requests is required for exception handling of the ConnectionError
import requests
from pyVim import connect
- from pyVmomi import vim, vmodl
+ from pyVmomi import vim
HAS_PYVMOMI = True
except ImportError:
HAS_PYVMOMI = False
diff --git a/lib/ansible/playbook/play_context.py b/lib/ansible/playbook/play_context.py
index 6f276067d7..b3e7fcf140 100644
--- a/lib/ansible/playbook/play_context.py
+++ b/lib/ansible/playbook/play_context.py
@@ -80,6 +80,7 @@ MAGIC_VARIABLE_MAPPING = dict(
su_exe = ('ansible_su_exe',),
su_flags = ('ansible_su_flags',),
executable = ('ansible_shell_executable',),
+ module_compression = ('ansible_module_compression',),
)
SU_PROMPT_LOCALIZATIONS = [
@@ -169,6 +170,7 @@ class PlayContext(Base):
_accelerate_ipv6 = FieldAttribute(isa='bool', default=False, always_post_validate=True)
_accelerate_port = FieldAttribute(isa='int', default=C.ACCELERATE_PORT, always_post_validate=True)
_executable = FieldAttribute(isa='string', default=C.DEFAULT_EXECUTABLE)
+ _module_compression = FieldAttribute(isa='string', default=C.DEFAULT_MODULE_COMPRESSION)
# privilege escalation fields
_become = FieldAttribute(isa='bool')
diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py
index 3313245a17..e40f430baa 100644
--- a/lib/ansible/plugins/action/__init__.py
+++ b/lib/ansible/plugins/action/__init__.py
@@ -144,7 +144,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
"run 'git submodule update --init --recursive' to correct this problem." % (module_name))
# insert shared code and arguments into the module
- (module_data, module_style, module_shebang) = modify_module(module_path, module_args, task_vars=task_vars)
+ (module_data, module_style, module_shebang) = modify_module(module_name, module_path, module_args, task_vars=task_vars, module_compression=self._play_context.module_compression)
return (module_style, module_shebang, module_data)
diff --git a/test/integration/roles/test_hash_behavior/tasks/main.yml b/test/integration/roles/test_hash_behavior/tasks/main.yml
index 99d9db2293..463141edd3 100644
--- a/test/integration/roles/test_hash_behavior/tasks/main.yml
+++ b/test/integration/roles/test_hash_behavior/tasks/main.yml
@@ -17,8 +17,12 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
- name: get the hash behavior env setting
- shell: env | grep ANSIBLE_HASH_BEHAVIOUR | cut -f2- -d'='
+ shell: env | grep ^ANSIBLE_HASH_BEHAVIOUR'=' | cut -f2- -d'='
register: hash_behavior
+ # This only works with the local connection. The way this test is run means the
+ connection: local
+ delegate_to: localhost
+
- name: debug hash behavior result
debug: var=hash_behavior.stdout
diff --git a/test/units/module_utils/basic/test__log_invocation.py b/test/units/module_utils/basic/test__log_invocation.py
index f07b00e99b..5e7524e360 100644
--- a/test/units/module_utils/basic/test__log_invocation.py
+++ b/test/units/module_utils/basic/test__log_invocation.py
@@ -33,6 +33,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
# test basic log invocation
basic.MODULE_COMPLEX_ARGS = json.dumps(dict(foo=False, bar=[1,2,3], bam="bam", baz=u'baz'))
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec=dict(
foo = dict(default=True, type='bool'),
diff --git a/test/units/module_utils/basic/test_exit_json.py b/test/units/module_utils/basic/test_exit_json.py
index 7d32c8082f..ffb98e0b58 100644
--- a/test/units/module_utils/basic/test_exit_json.py
+++ b/test/units/module_utils/basic/test_exit_json.py
@@ -39,6 +39,7 @@ class TestAnsibleModuleExitJson(unittest.TestCase):
def setUp(self):
self.COMPLEX_ARGS = basic.MODULE_COMPLEX_ARGS
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
self.old_stdout = sys.stdout
self.fake_stream = BytesIO()
@@ -129,6 +130,7 @@ class TestAnsibleModuleExitValuesRemoved(unittest.TestCase):
for args, return_val, expected in self.dataset:
sys.stdout = BytesIO()
basic.MODULE_COMPLEX_ARGS = json.dumps(args)
+ basic.MODULE_CONSTANTS = '{}'
module = basic.AnsibleModule(
argument_spec = dict(
username=dict(),
@@ -148,6 +150,7 @@ class TestAnsibleModuleExitValuesRemoved(unittest.TestCase):
expected['failed'] = True
sys.stdout = BytesIO()
basic.MODULE_COMPLEX_ARGS = json.dumps(args)
+ basic.MODULE_CONSTANTS = '{}'
module = basic.AnsibleModule(
argument_spec = dict(
username=dict(),
diff --git a/test/units/module_utils/basic/test_log.py b/test/units/module_utils/basic/test_log.py
index 27e56db1cb..0a78ffb96d 100644
--- a/test/units/module_utils/basic/test_log.py
+++ b/test/units/module_utils/basic/test_log.py
@@ -42,7 +42,9 @@ class TestAnsibleModuleSysLogSmokeTest(unittest.TestCase):
def setUp(self):
self.complex_args_token = basic.MODULE_COMPLEX_ARGS
+ self.constants_sentinel = basic.MODULE_CONSTANTS
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
self.am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -54,6 +56,7 @@ class TestAnsibleModuleSysLogSmokeTest(unittest.TestCase):
def tearDown(self):
basic.MODULE_COMPLEX_ARGS = self.complex_args_token
+ basic.MODULE_CONSTANTS = self.constants_sentinel
basic.has_journal = self.has_journal
def test_smoketest_syslog(self):
@@ -73,13 +76,16 @@ class TestAnsibleModuleJournaldSmokeTest(unittest.TestCase):
def setUp(self):
self.complex_args_token = basic.MODULE_COMPLEX_ARGS
+ self.constants_sentinel = basic.MODULE_CONSTANTS
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
self.am = basic.AnsibleModule(
argument_spec = dict(),
)
def tearDown(self):
basic.MODULE_COMPLEX_ARGS = self.complex_args_token
+ basic.MODULE_CONSTANTS = self.constants_sentinel
@unittest.skipUnless(basic.has_journal, 'python systemd bindings not installed')
def test_smoketest_journal(self):
@@ -116,7 +122,9 @@ class TestAnsibleModuleLogSyslog(unittest.TestCase):
def setUp(self):
self.complex_args_token = basic.MODULE_COMPLEX_ARGS
+ self.constants_sentinel = basic.MODULE_CONSTANTS
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
self.am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -127,6 +135,7 @@ class TestAnsibleModuleLogSyslog(unittest.TestCase):
def tearDown(self):
basic.MODULE_COMPLEX_ARGS = self.complex_args_token
+ basic.MODULE_CONSTANTS = self.constants_sentinel
basic.has_journal = self.has_journal
@patch('syslog.syslog', autospec=True)
@@ -168,7 +177,9 @@ class TestAnsibleModuleLogJournal(unittest.TestCase):
def setUp(self):
self.complex_args_token = basic.MODULE_COMPLEX_ARGS
+ self.constants_sentinel = basic.MODULE_CONSTANTS
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
self.am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -188,6 +199,7 @@ class TestAnsibleModuleLogJournal(unittest.TestCase):
def tearDown(self):
basic.MODULE_COMPLEX_ARGS = self.complex_args_token
+ basic.MODULE_CONSTANTS = self.constants_sentinel
basic.has_journal = self.has_journal
if self.module_patcher:
self.module_patcher.stop()
diff --git a/test/units/module_utils/basic/test_run_command.py b/test/units/module_utils/basic/test_run_command.py
index 191560e961..8a17f7e55a 100644
--- a/test/units/module_utils/basic/test_run_command.py
+++ b/test/units/module_utils/basic/test_run_command.py
@@ -62,6 +62,7 @@ class TestAnsibleModuleRunCommand(unittest.TestCase):
raise OSError(errno.EPERM, "Permission denied: '/inaccessible'")
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
self.module = AnsibleModule(argument_spec=dict())
self.module.fail_json = MagicMock(side_effect=SystemExit)
diff --git a/test/units/module_utils/basic/test_safe_eval.py b/test/units/module_utils/basic/test_safe_eval.py
index 32a2c4c27a..cb28e9063f 100644
--- a/test/units/module_utils/basic/test_safe_eval.py
+++ b/test/units/module_utils/basic/test_safe_eval.py
@@ -29,6 +29,7 @@ class TestAnsibleModuleExitJson(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec=dict(),
)
diff --git a/test/units/module_utils/test_basic.py b/test/units/module_utils/test_basic.py
index 0a4ed0763d..39d9efe065 100644
--- a/test/units/module_utils/test_basic.py
+++ b/test/units/module_utils/test_basic.py
@@ -267,6 +267,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec=dict(),
)
@@ -282,6 +283,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
# should test ok
basic.MODULE_COMPLEX_ARGS = '{"foo":"hello"}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = arg_spec,
mutually_exclusive = mut_ex,
@@ -296,6 +298,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
# fail, because a required param was not specified
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
self.assertRaises(
SystemExit,
basic.AnsibleModule,
@@ -310,6 +313,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
# fail because of mutually exclusive parameters
basic.MODULE_COMPLEX_ARGS = '{"foo":"hello", "bar": "bad", "bam": "bad"}'
+ basic.MODULE_CONSTANTS = '{}'
self.assertRaises(
SystemExit,
basic.AnsibleModule,
@@ -324,6 +328,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
# fail because a param required due to another param was not specified
basic.MODULE_COMPLEX_ARGS = '{"bam":"bad"}'
+ basic.MODULE_CONSTANTS = '{}'
self.assertRaises(
SystemExit,
basic.AnsibleModule,
@@ -340,6 +345,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -389,6 +395,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -409,6 +416,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -423,6 +431,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -455,6 +464,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -491,6 +501,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -533,10 +544,11 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
- basic.SELINUX_SPECIAL_FS = 'nfs,nfsd,foos'
+ basic.MODULE_CONSTANTS = '{"SELINUX_SPECIAL_FS": "nfs,nfsd,foos"}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
+ print(am.constants)
def _mock_find_mount_point(path):
if path.startswith('/some/path'):
@@ -574,6 +586,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -585,6 +598,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -600,6 +614,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -624,6 +639,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -669,6 +685,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -747,6 +764,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -835,6 +853,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
@@ -1013,6 +1032,7 @@ class TestModuleUtilsBasic(unittest.TestCase):
from ansible.module_utils import basic
basic.MODULE_COMPLEX_ARGS = '{}'
+ basic.MODULE_CONSTANTS = '{}'
am = basic.AnsibleModule(
argument_spec = dict(),
)
diff --git a/test/units/plugins/action/test_action.py b/test/units/plugins/action/test_action.py
index 8c97bf0415..dc6324d924 100644
--- a/test/units/plugins/action/test_action.py
+++ b/test/units/plugins/action/test_action.py
@@ -52,7 +52,6 @@ python_module_replacers = b"""
#!/usr/bin/python
#ANSIBLE_VERSION = "<<ANSIBLE_VERSION>>"
-#MODULE_ARGS = "<<INCLUDE_ANSIBLE_MODULE_ARGS>>"
#MODULE_COMPLEX_ARGS = "<<INCLUDE_ANSIBLE_MODULE_COMPLEX_ARGS>>"
#SELINUX_SPECIAL_FS="<<SELINUX_SPECIAL_FILESYSTEMS>>"
@@ -61,7 +60,7 @@ from ansible.module_utils.basic import *
"""
powershell_module_replacers = b"""
-WINDOWS_ARGS = "<<INCLUDE_ANSIBLE_MODULE_WINDOWS_ARGS>>"
+WINDOWS_ARGS = "<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"
# POWERSHELL_COMMON
"""