diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index cd88ca04a91..0e95b5bb8fa 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -222,10 +222,10 @@ _literal_eval = literal_eval # Backwards compat. There were present in basic.py before from ansible.module_utils.pycompat24 import get_exception -# Internal global holding passed in params and constants. This is consulted -# in case multiple AnsibleModules are created. Otherwise each AnsibleModule -# would attempt to read from stdin. Other code should not use this directly -# as it is an internal implementation detail +# Internal global holding passed in params. This is consulted in case +# multiple AnsibleModules are created. Otherwise each AnsibleModule would +# attempt to read from stdin. Other code should not use this directly as it +# is an internal implementation detail _ANSIBLE_ARGS = None FILE_COMMON_ARGUMENTS=dict( @@ -508,9 +508,59 @@ def is_executable(path): or stat.S_IXGRP & os.stat(path)[stat.ST_MODE] or stat.S_IXOTH & os.stat(path)[stat.ST_MODE]) +def _load_params(): + ''' read the modules parameters and store them globally. -class AnsibleFallbackNotFound(Exception): - pass + This function may be needed for certain very dynamic custom modules which + want to process the parameters that are being handed the module. Since + this is so closely tied to the implementation of modules we cannot + guarantee API stability for it (it may change between versions) however we + will try not to break it gratuitously. It is certainly more future-proof + to call this function and consume its outputs than to implement the logic + inside it as a copy in your own code. + ''' + global _ANSIBLE_ARGS + if _ANSIBLE_ARGS is not None: + buffer = _ANSIBLE_ARGS + else: + # debug overrides to read args from file or cmdline + + # Avoid tracebacks when locale is non-utf8 + # We control the args and we pass them as utf8 + if len(sys.argv) > 1: + if os.path.isfile(sys.argv[1]): + fd = open(sys.argv[1], 'rb') + buffer = fd.read() + fd.close() + else: + buffer = sys.argv[1] + if sys.version_info >= (3,): + buffer = buffer.encode('utf-8', errors='surrogateescape') + # default case, read from stdin + else: + if sys.version_info < (3,): + buffer = sys.stdin.read() + else: + buffer = sys.stdin.buffer.read() + _ANSIBLE_ARGS = buffer + + try: + params = json.loads(buffer.decode('utf-8')) + except ValueError: + # This helper used too early for fail_json to work. + print('\n{"msg": "Error: Module unable to decode valid JSON on stdin. Unable to figure out what parameters were passed", "failed": true}') + sys.exit(1) + + if sys.version_info < (3,): + params = json_dict_unicode_to_bytes(params) + + try: + return params['ANSIBLE_MODULE_ARGS'] + except KeyError: + # This helper does not have access to fail_json so we have to print + # json output on our own. + print('\n{"msg": "Error: Module unable to locate ANSIBLE_MODULE_ARGS in json data from stdin. Unable to figure out what parameters were passed", "failed": true}') + sys.exit(1) def env_fallback(*args, **kwargs): ''' Load value from environment ''' @@ -521,6 +571,10 @@ def env_fallback(*args, **kwargs): raise AnsibleFallbackNotFound +class AnsibleFallbackNotFound(Exception): + pass + + class AnsibleModule(object): def __init__(self, argument_spec, bypass_checks=False, no_log=False, check_invalid_arguments=True, mutually_exclusive=None, required_together=None, @@ -1465,48 +1519,13 @@ class AnsibleModule(object): continue def _load_params(self): - ''' read the input and set the params attribute. Sets the constants as well.''' - # debug overrides to read args from file or cmdline + ''' read the input and set the params attribute. - global _ANSIBLE_ARGS - if _ANSIBLE_ARGS is not None: - buffer = _ANSIBLE_ARGS - else: - # Avoid tracebacks when locale is non-utf8 - # We control the args and we pass them as utf8 - if len(sys.argv) > 1: - if os.path.isfile(sys.argv[1]): - fd = open(sys.argv[1], 'rb') - buffer = fd.read() - fd.close() - else: - buffer = sys.argv[1] - if sys.version_info >= (3,): - buffer = buffer.encode('utf-8', errors='surrogateescape') - # default case, read from stdin - else: - if sys.version_info < (3,): - buffer = sys.stdin.read() - else: - buffer = sys.stdin.buffer.read() - _ANSIBLE_ARGS = buffer - - try: - params = json.loads(buffer.decode('utf-8')) - except ValueError: - # This helper used too early for fail_json to work. - print('\n{"msg": "Error: Module unable to decode valid JSON on stdin. Unable to figure out what parameters were passed", "failed": true}') - sys.exit(1) - - if sys.version_info < (3,): - params = json_dict_unicode_to_bytes(params) - - try: - self.params = params['ANSIBLE_MODULE_ARGS'] - except KeyError: - # This helper used too early for fail_json to work. - print('\n{"msg": "Error: Module unable to locate ANSIBLE_MODULE_ARGS and ANSIBLE_MODULE_CONSTANTS in json data from stdin. Unable to figure out what parameters were passed", "failed": true}') - sys.exit(1) + This method is for backwards compatibility. The guts of the function + were moved out in 2.1 so that custom modules could read the parameters. + ''' + # debug overrides to read args from file or cmdline + self.params = _load_params() def _log_to_syslog(self, msg): if HAS_SYSLOG: