diff --git a/changelogs/fragments/missing_interpreter.yml b/changelogs/fragments/missing_interpreter.yml new file mode 100644 index 0000000000..38e711b5e1 --- /dev/null +++ b/changelogs/fragments/missing_interpreter.yml @@ -0,0 +1,2 @@ +bugfixes: + - nicer message when we are missing interpreter diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index 72dd769686..41ac119d95 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -60,6 +60,8 @@ class ActionBase(with_metaclass(ABCMeta, object)): # Backwards compat: self._display isn't really needed, just import the global display and use that. self._display = display + self._used_interpreter = None + @abstractmethod def run(self, tmp=None, task_vars=None): """ Action Plugins should implement this method to perform their @@ -753,6 +755,7 @@ class ActionBase(with_metaclass(ABCMeta, object)): if not shebang and module_style != 'binary': raise AnsibleError("module (%s) is missing interpreter line" % module_name) + self._used_interpreter = shebang remote_module_path = None if not self._is_pipelining_enabled(module_style, wrap_async): @@ -896,12 +899,20 @@ class ActionBase(with_metaclass(ABCMeta, object)): except ValueError: # not valid json, lets try to capture error data = dict(failed=True, _ansible_parsed=False) - data['msg'] = "MODULE FAILURE" data['module_stdout'] = res.get('stdout', u'') if 'stderr' in res: data['module_stderr'] = res['stderr'] if res['stderr'].startswith(u'Traceback'): data['exception'] = res['stderr'] + + # try to figure out if we are missing interpreter + if self._used_interpreter is not None and '%s: No such file or directory' % self._used_interpreter.lstrip('!#') in data['module_stderr']: + data['msg'] = "The module failed to execute correctly, you probably need to set the interpreter." + else: + data['msg'] = "MODULE FAILURE" + + data['msg'] += '\nSee stdout/stderr for the exact error' + if 'rc' in res: data['rc'] = res['rc'] return data