summaryrefslogtreecommitdiff
path: root/lib/ansible/plugins/connection/ssh.py
diff options
context:
space:
mode:
authorToshio Kuratomi <a.badger@gmail.com>2016-08-25 10:57:55 -0700
committerGitHub <noreply@github.com>2016-08-25 10:57:55 -0700
commitbd68c324cebce599ff07d6fd90c36a224581e065 (patch)
treeaffc84d26655b439ce6efb7204cefc885ad7715c /lib/ansible/plugins/connection/ssh.py
parentdbab23e68f1c623f6ce4afdaf71d2d10c231d5e9 (diff)
downloadansible-bd68c324cebce599ff07d6fd90c36a224581e065.tar.gz
Get the ssh plugin working with python3 (#17234)
Diffstat (limited to 'lib/ansible/plugins/connection/ssh.py')
-rw-r--r--lib/ansible/plugins/connection/ssh.py111
1 files changed, 56 insertions, 55 deletions
diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py
index 3add4c1a83..def3f85403 100644
--- a/lib/ansible/plugins/connection/ssh.py
+++ b/lib/ansible/plugins/connection/ssh.py
@@ -107,7 +107,7 @@ class Connection(ConnectionBase):
explanation of why they were added.
"""
self._command += args
- display.vvvvv('SSH: ' + explanation + ': (%s)' % ')('.join(args), host=self._play_context.remote_addr)
+ display.vvvvv('SSH: ' + explanation + ': (%s)' % ')('.join(map(to_unicode, args)), host=self._play_context.remote_addr)
def _build_command(self, binary, *other_args):
'''
@@ -217,15 +217,14 @@ class Connection(ConnectionBase):
if not controlpath:
cpdir = unfrackpath('$HOME/.ansible/cp')
+ b_cpdir = to_bytes(cpdir)
# The directory must exist and be writable.
- makedirs_safe(cpdir, 0o700)
- if not os.access(cpdir, os.W_OK):
- raise AnsibleError("Cannot write to ControlPath %s" % cpdir)
+ makedirs_safe(b_cpdir, 0o700)
+ if not os.access(b_cpdir, os.W_OK):
+ raise AnsibleError("Cannot write to ControlPath %s" % to_str(cpdir))
- args = ("-o", "ControlPath={0}".format(
- to_bytes(C.ANSIBLE_SSH_CONTROL_PATH % dict(directory=cpdir)))
- )
+ args = ("-o", "ControlPath=" + C.ANSIBLE_SSH_CONTROL_PATH % dict(directory=cpdir))
self._add_args("found only ControlPersist; added ControlPath", args)
## Finally, we add any caller-supplied extras.
@@ -233,7 +232,8 @@ class Connection(ConnectionBase):
if other_args:
self._command += other_args
- return self._command
+ cmd = [to_bytes(a) for a in self._command]
+ return cmd
def _send_initial_data(self, fh, in_data):
'''
@@ -245,7 +245,7 @@ class Connection(ConnectionBase):
display.debug('Sending initial data')
try:
- fh.write(in_data)
+ fh.write(to_bytes(in_data))
fh.close()
except (OSError, IOError):
raise AnsibleConnectionFailure('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh')
@@ -263,7 +263,7 @@ class Connection(ConnectionBase):
# This is separate from _run() because we need to do the same thing for stdout
# and stderr.
- def _examine_output(self, source, state, chunk, sudoable):
+ def _examine_output(self, source, state, b_chunk, sudoable):
'''
Takes a string, extracts complete lines from it, tests to see if they
are a prompt, error message, etc., and sets appropriate flags in self.
@@ -274,46 +274,47 @@ class Connection(ConnectionBase):
'''
output = []
- for l in chunk.splitlines(True):
+ for b_line in b_chunk.splitlines(True):
+ display_line = to_unicode(b_line, errors='replace').rstrip('\r\n')
suppress_output = False
- #display.debug("Examining line (source=%s, state=%s): '%s'" % (source, state, l.rstrip('\r\n')))
- if self._play_context.prompt and self.check_password_prompt(l):
- display.debug("become_prompt: (source=%s, state=%s): '%s'" % (source, state, l.rstrip('\r\n')))
+ #display.debug("Examining line (source=%s, state=%s): '%s'" % (source, state, display_line))
+ if self._play_context.prompt and self.check_password_prompt(b_line):
+ display.debug("become_prompt: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_prompt'] = True
suppress_output = True
- elif self._play_context.success_key and self.check_become_success(l):
- display.debug("become_success: (source=%s, state=%s): '%s'" % (source, state, l.rstrip('\r\n')))
+ elif self._play_context.success_key and self.check_become_success(b_line):
+ display.debug("become_success: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_success'] = True
suppress_output = True
- elif sudoable and self.check_incorrect_password(l):
- display.debug("become_error: (source=%s, state=%s): '%s'" % (source, state, l.rstrip('\r\n')))
+ elif sudoable and self.check_incorrect_password(b_line):
+ display.debug("become_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_error'] = True
- elif sudoable and self.check_missing_password(l):
- display.debug("become_nopasswd_error: (source=%s, state=%s): '%s'" % (source, state, l.rstrip('\r\n')))
+ elif sudoable and self.check_missing_password(b_line):
+ display.debug("become_nopasswd_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_nopasswd_error'] = True
if not suppress_output:
- output.append(l)
+ output.append(b_line)
# The chunk we read was most likely a series of complete lines, but just
# in case the last line was incomplete (and not a prompt, which we would
# have removed from the output), we retain it to be processed with the
# next chunk.
- remainder = ''
- if output and not output[-1].endswith('\n'):
+ remainder = b''
+ if output and not output[-1].endswith(b'\n'):
remainder = output[-1]
output = output[:-1]
- return ''.join(output), remainder
+ return b''.join(output), remainder
def _run(self, cmd, in_data, sudoable=True):
'''
Starts the command and communicates with it until it ends.
'''
- display_cmd = map(to_unicode, map(pipes.quote, cmd))
+ display_cmd = list(map(pipes.quote, map(to_unicode, cmd)))
display.vvv(u'SSH: EXEC {0}'.format(u' '.join(display_cmd)), host=self.host)
# Start the given command. If we don't need to pipeline data, we can try
@@ -333,7 +334,7 @@ class Connection(ConnectionBase):
# Make sure stdin is a proper pty to avoid tcgetattr errors
master, slave = pty.openpty()
p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdin = os.fdopen(master, 'w', 0)
+ stdin = os.fdopen(master, 'wb', 0)
os.close(slave)
except (OSError, IOError):
p = None
@@ -348,7 +349,7 @@ class Connection(ConnectionBase):
if self._play_context.password:
os.close(self.sshpass_pipe[0])
try:
- os.write(self.sshpass_pipe[1], "{0}\n".format(to_bytes(self._play_context.password)))
+ os.write(self.sshpass_pipe[1], to_bytes(self._play_context.password) + b'\n')
except OSError as e:
# Ignore broken pipe errors if the sshpass process has exited.
if e.errno != errno.EPIPE or p.poll() is None:
@@ -375,12 +376,12 @@ class Connection(ConnectionBase):
# We're requesting escalation with a password, so we have to
# wait for a password prompt.
state = states.index('awaiting_prompt')
- display.debug('Initial state: %s: %s' % (states[state], self._play_context.prompt))
+ display.debug(u'Initial state: %s: %s' % (states[state], self._play_context.prompt))
elif self._play_context.become and self._play_context.success_key:
# We're requesting escalation without a password, so we have to
# detect success/failure before sending any initial data.
state = states.index('awaiting_escalation')
- display.debug('Initial state: %s: %s' % (states[state], self._play_context.success_key))
+ display.debug(u'Initial state: %s: %s' % (states[state], self._play_context.success_key))
# We store accumulated stdout and stderr output from the process here,
# but strip any privilege escalation prompt/confirmation lines first.
@@ -388,8 +389,8 @@ class Connection(ConnectionBase):
# an array, then checked and removed or copied to stdout or stderr. We
# set any flags based on examining the output in self._flags.
- stdout = stderr = ''
- tmp_stdout = tmp_stderr = ''
+ b_stdout = b_stderr = b''
+ b_tmp_stdout = b_tmp_stderr = b''
self._flags = dict(
become_prompt=False, become_success=False,
@@ -423,43 +424,43 @@ class Connection(ConnectionBase):
if p.poll() is not None:
break
self._terminate_process(p)
- raise AnsibleError('Timeout (%ds) waiting for privilege escalation prompt: %s' % (timeout, stdout))
+ raise AnsibleError('Timeout (%ds) waiting for privilege escalation prompt: %s' % (timeout, to_str(b_stdout)))
# Read whatever output is available on stdout and stderr, and stop
# listening to the pipe if it's been closed.
if p.stdout in rfd:
- chunk = p.stdout.read()
- if chunk == '':
+ b_chunk = p.stdout.read()
+ if b_chunk == b'':
rpipes.remove(p.stdout)
- tmp_stdout += chunk
- display.debug("stdout chunk (state=%s):\n>>>%s<<<\n" % (state, chunk))
+ b_tmp_stdout += b_chunk
+ display.debug("stdout chunk (state=%s):\n>>>%s<<<\n" % (state, to_unicode(b_chunk, errors='replace')))
if p.stderr in rfd:
- chunk = p.stderr.read()
- if chunk == '':
+ b_chunk = p.stderr.read()
+ if b_chunk == b'':
rpipes.remove(p.stderr)
- tmp_stderr += chunk
- display.debug("stderr chunk (state=%s):\n>>>%s<<<\n" % (state, chunk))
+ b_tmp_stderr += b_chunk
+ display.debug("stderr chunk (state=%s):\n>>>%s<<<\n" % (state, to_unicode(b_chunk, errors='replace')))
# We examine the output line-by-line until we have negotiated any
# privilege escalation prompt and subsequent success/error message.
# Afterwards, we can accumulate output without looking at it.
if state < states.index('ready_to_send'):
- if tmp_stdout:
- output, unprocessed = self._examine_output('stdout', states[state], tmp_stdout, sudoable)
- stdout += output
- tmp_stdout = unprocessed
-
- if tmp_stderr:
- output, unprocessed = self._examine_output('stderr', states[state], tmp_stderr, sudoable)
- stderr += output
- tmp_stderr = unprocessed
+ if b_tmp_stdout:
+ b_output, b_unprocessed = self._examine_output('stdout', states[state], b_tmp_stdout, sudoable)
+ b_stdout += b_output
+ b_tmp_stdout = b_unprocessed
+
+ if b_tmp_stderr:
+ b_output, b_unprocessed = self._examine_output('stderr', states[state], b_tmp_stderr, sudoable)
+ b_stderr += b_output
+ b_tmp_stderr = b_unprocessed
else:
- stdout += tmp_stdout
- stderr += tmp_stderr
- tmp_stdout = tmp_stderr = ''
+ b_stdout += b_tmp_stdout
+ b_stderr += b_tmp_stderr
+ b_tmp_stdout = b_tmp_stderr = b''
# If we see a privilege escalation prompt, we send the password.
# (If we're expecting a prompt but the escalation succeeds, we
@@ -468,7 +469,7 @@ class Connection(ConnectionBase):
if states[state] == 'awaiting_prompt':
if self._flags['become_prompt']:
display.debug('Sending become_pass in response to prompt')
- stdin.write('{0}\n'.format(to_bytes(self._play_context.become_pass )))
+ stdin.write(to_bytes(self._play_context.become_pass) + b'\n')
self._flags['become_prompt'] = False
state += 1
elif self._flags['become_success']:
@@ -546,14 +547,14 @@ class Connection(ConnectionBase):
if cmd[0] == b"sshpass" and p.returncode == 6:
raise AnsibleError('Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host\'s fingerprint to your known_hosts file to manage this host.')
- controlpersisterror = 'Bad configuration option: ControlPersist' in stderr or 'unknown configuration option: ControlPersist' in stderr
+ controlpersisterror = b'Bad configuration option: ControlPersist' in b_stderr or b'unknown configuration option: ControlPersist' in b_stderr
if p.returncode != 0 and controlpersisterror:
raise AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ssh_args in [ssh_connection] section of the config file) before running again')
if p.returncode == 255 and in_data:
raise AnsibleConnectionFailure('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh')
- return (p.returncode, stdout, stderr)
+ return (p.returncode, b_stdout, b_stderr)
def _exec_command(self, cmd, in_data=None, sudoable=True):
''' run a command on the remote host '''