ansible: raise AnsibleConnectionFailure on connection failure; closes #183

Before:

    $ ANSIBLE_STRATEGY=mitogen ansible -i derp, derp -m setup
    An exception occurred during task execution. To see the full traceback, use -vvv. The error was:     (''.join(bits)[-300:],)
    derp | FAILED! => {
        "msg": "Unexpected failure during module execution.",
        "stdout": ""
    }

After:

    $ ANSIBLE_STRATEGY=mitogen ansible -i derp, derp -m setup
    derp | UNREACHABLE! => {
        "changed": false,
        "msg": "EOF on stream; last 300 bytes received: 'ssh: Could not resolve hostname derp: nodename nor servname provided, or not known\\r\\n'",
        "unreachable": true
    }
pull/193/head
David Wilson 7 years ago
parent b9d4ec57b3
commit 7fd88868a6

@ -37,7 +37,7 @@ import ansible.errors
import ansible.plugins.connection import ansible.plugins.connection
import mitogen.unix import mitogen.unix
from mitogen.utils import cast import mitogen.utils
import ansible_mitogen.helpers import ansible_mitogen.helpers
import ansible_mitogen.process import ansible_mitogen.process
@ -137,59 +137,63 @@ class Connection(ansible.plugins.connection.ConnectionBase):
def connected(self): def connected(self):
return self.broker is not None return self.broker is not None
def _wrap_connect(self, args):
dct = mitogen.service.call(
context=self.parent,
handle=ContextService.handle,
obj=mitogen.utils.cast(args),
)
if dct['msg']:
raise ansible.errors.AnsibleConnectionFailure(dct['msg'])
return dct['context'], dct['home_dir']
def _connect_local(self): def _connect_local(self):
""" """
Fetch a reference to the local() Context from ContextService in the Fetch a reference to the local() Context from ContextService in the
master process. master process.
""" """
return mitogen.service.call(self.parent, ContextService.handle, cast({ return self._wrap_connect({
'method': 'local', 'method': 'local',
'python_path': self.python_path, 'python_path': self.python_path,
})) })
def _connect_ssh(self): def _connect_ssh(self):
""" """
Fetch a reference to an SSH Context matching the play context from Fetch a reference to an SSH Context matching the play context from
ContextService in the master process. ContextService in the master process.
""" """
return mitogen.service.call( return self._wrap_connect({
self.parent, 'method': 'ssh',
ContextService.handle, 'check_host_keys': False, # TODO
cast({ 'hostname': self._play_context.remote_addr,
'method': 'ssh', 'discriminator': self.mitogen_ssh_discriminator,
'check_host_keys': False, # TODO 'username': self._play_context.remote_user,
'hostname': self._play_context.remote_addr, 'password': self._play_context.password,
'discriminator': self.mitogen_ssh_discriminator, 'port': self._play_context.port,
'username': self._play_context.remote_user, 'python_path': self.python_path,
'password': self._play_context.password, 'identity_file': self._play_context.private_key_file,
'port': self._play_context.port, 'ssh_path': self._play_context.ssh_executable,
'python_path': self.python_path, 'connect_timeout': self.ansible_ssh_timeout,
'identity_file': self._play_context.private_key_file, 'ssh_args': [
'ssh_path': self._play_context.ssh_executable, term
'connect_timeout': self.ansible_ssh_timeout, for s in (
'ssh_args': [ getattr(self._play_context, 'ssh_args', ''),
term getattr(self._play_context, 'ssh_common_args', ''),
for s in ( getattr(self._play_context, 'ssh_extra_args', '')
getattr(self._play_context, 'ssh_args', ''), )
getattr(self._play_context, 'ssh_common_args', ''), for term in shlex.split(s or '')
getattr(self._play_context, 'ssh_extra_args', '') ]
) })
for term in shlex.split(s or '')
]
})
)
def _connect_docker(self): def _connect_docker(self):
return mitogen.service.call( return self._wrap_connect({
self.parent, 'method': 'docker',
ContextService.handle, 'container': self._play_context.remote_addr,
cast({ 'python_path': self.python_path,
'method': 'docker', 'connect_timeout': self._play_context.timeout,
'container': self._play_context.remote_addr, })
'python_path': self.python_path,
'connect_timeout': self._play_context.timeout,
})
)
def _connect_sudo(self, via=None, python_path=None): def _connect_sudo(self, via=None, python_path=None):
""" """
@ -200,23 +204,19 @@ class Connection(ansible.plugins.connection.ConnectionBase):
Parent Context of the sudo Context. For Ansible, this should always Parent Context of the sudo Context. For Ansible, this should always
be a Context returned by _connect_ssh(). be a Context returned by _connect_ssh().
""" """
return mitogen.service.call( return self._wrap_connect({
self.parent, 'method': 'sudo',
ContextService.handle, 'username': self._play_context.become_user,
cast({ 'password': self._play_context.become_pass,
'method': 'sudo', 'python_path': python_path or self.python_path,
'username': self._play_context.become_user, 'sudo_path': self.sudo_path,
'password': self._play_context.become_pass, 'connect_timeout': self._play_context.timeout,
'python_path': python_path or self.python_path, 'via': via,
'sudo_path': self.sudo_path, 'sudo_args': shlex.split(
'connect_timeout': self._play_context.timeout, self._play_context.sudo_flags or
'via': via, self._play_context.become_flags or ''
'sudo_args': shlex.split( ),
self._play_context.sudo_flags or })
self._play_context.become_flags or ''
),
})
)
def _connect(self): def _connect(self):
""" """
@ -318,8 +318,8 @@ class Connection(ansible.plugins.connection.ConnectionBase):
emulate_tty = (not in_data and sudoable) emulate_tty = (not in_data and sudoable)
rc, stdout, stderr = self.call( rc, stdout, stderr = self.call(
ansible_mitogen.helpers.exec_command, ansible_mitogen.helpers.exec_command,
cmd=cast(cmd), cmd=mitogen.utils.cast(cmd),
in_data=cast(in_data), in_data=mitogen.utils.cast(in_data),
chdir=mitogen_chdir, chdir=mitogen_chdir,
emulate_tty=emulate_tty, emulate_tty=emulate_tty,
) )
@ -341,7 +341,7 @@ class Connection(ansible.plugins.connection.ConnectionBase):
Local filesystem path to write. Local filesystem path to write.
""" """
output = self.call(ansible_mitogen.helpers.read_path, output = self.call(ansible_mitogen.helpers.read_path,
cast(in_path)) mitogen.utils.cast(in_path))
ansible_mitogen.helpers.write_path(out_path, output) ansible_mitogen.helpers.write_path(out_path, output)
def put_data(self, out_path, data): def put_data(self, out_path, data):
@ -355,7 +355,8 @@ class Connection(ansible.plugins.connection.ConnectionBase):
Remote filesystem path to write. Remote filesystem path to write.
""" """
self.call(ansible_mitogen.helpers.write_path, self.call(ansible_mitogen.helpers.write_path,
cast(out_path), cast(data)) mitogen.utils.cast(out_path),
mitogen.utils.cast(data))
def put_file(self, in_path, out_path): def put_file(self, in_path, out_path):
""" """

@ -81,9 +81,21 @@ class ContextService(mitogen.service.DeduplicatingService):
def get_response(self, args): def get_response(self, args):
args.pop('discriminator', None) args.pop('discriminator', None)
method = getattr(self.router, args.pop('method')) method = getattr(self.router, args.pop('method'))
context = method(**args) try:
context = method(**args)
except mitogen.core.StreamError as e:
return {
'context': None,
'home_dir': None,
'msg': str(e),
}
home_dir = context.call(os.path.expanduser, '~') home_dir = context.call(os.path.expanduser, '~')
return context, home_dir return {
'context': context,
'home_dir': home_dir,
'msg': None,
}
class FileService(mitogen.service.Service): class FileService(mitogen.service.Service):

Loading…
Cancel
Save