8000 ssh: Ensure debug messages are properly converted to text by sivel · Pull Request #57850 · ansible/ansible · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

ssh: Ensure debug messages are properly converted to text #57850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/ssh-fix-text-conv.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- ssh connection plugin - Ensure that debug messages are properly encoded as text
63 changes: 32 additions & 31 deletions lib/ansible/plugins/connection/ssh.py
8000 8000
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def _handle_error(remaining_retries, command, return_tuple, no_log, host, displa
if no_log:
msg = '{0} <error censored due to no log>'.format(msg)
else:
msg = '{0} {1}'.format(msg, to_native(return_tuple[2].rstrip()))
msg = '{0} {1}'.format(msg, to_native(return_tuple[2]).rstrip())
raise AnsibleAuthenticationFailure(msg)

# sshpass returns codes are 1-6. We handle 5 previously, so this catches other scenarios.
Expand All @@ -337,7 +337,7 @@ def _handle_error(remaining_retries, command, return_tuple, no_log, host, displa
if no_log:
msg = '{0} <error censored due to no log>'.format(msg)
else:
msg = '{0} {1}'.format(msg, to_native(return_tuple[2].rstrip()))
msg = '{0} {1}'.format(msg, to_native(return_tuple[2]).rstrip())

if return_tuple[0] == 255:
SSH_ERROR = True
Expand All @@ -356,11 +356,11 @@ def _handle_error(remaining_retries, command, return_tuple, no_log, host, displa

# For other errors, no execption is raised so the connection is retried and we only log the messages
if 1 <= return_tuple[0] <= 254:
msg = "Failed to connect to the host via ssh:"
msg = u"Failed to connect to the host via ssh:"
if no_log:
msg = '{0} <error censored due to no log>'.format(msg)
msg = u'{0} <error censored due to no log>'.format(msg)
else:
msg = '{0} {1}'.format(msg, to_native(return_tuple[2]).rstrip())
msg = u'{0} {1}'.format(msg, to_text(return_tuple[2]).rstrip())
Copy link
Member Author
@sivel sivel Jun 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This msg (and the 2 above in this block) leads to a display. All others lead to an exception.

display.vvv(msg, host=host)


Expand All @@ -379,7 +379,7 @@ def _ssh_retry(func):
@wraps(func)
def wrapped(self, *args, **kwargs):
remaining_tries = int(C.ANSIBLE_SSH_RETRIES) + 1
cmd_summary = "%s..." % args[0]
cmd_summary = u"%s..." % to_text(args[0])
for attempt in range(remaining_tries):
cmd = args[0]
if attempt != 0 and self._play_context.password and isinstance(cmd, list):
Expand All @@ -391,7 +391,7 @@ def wrapped(self, *args, **kwargs):
try:
return_tuple = func(self, *args, **kwargs)
if self._play_context.no_log:
display.vvv('rc=%s, stdout and stderr censored due to no log' % return_tuple[0], host=self.host)
display.vvv(u'rc=%s, stdout and stderr censored due to no log' % return_tuple[0], host=self.host)
else:
display.vvv(return_tuple, host=self.host)
# 0 = success
Expand Down Expand Up @@ -427,9 +427,10 @@ def wrapped(self, *args, **kwargs):
pause = 30

if isinstance(e, AnsibleConnectionFailure):
msg = "ssh_retry: attempt: %d, ssh return code is 255. cmd (%s), pausing for %d seconds" % (attempt + 1, cmd_summary, pause)
msg = u"ssh_retry: attempt: %d, ssh return code is 255. cmd (%s), pausing for %d seconds" % (attempt + 1, cmd_summary, pause)
else:
msg = "ssh_retry: attempt: %d, caught exception(%s) from cmd (%s), pausing for %d seconds" % (attempt + 1, e, cmd_summary, pause)
msg = (u"ssh_retry: attempt: %d, caught exception(%s) from cmd (%s), "
u"pausing for %d seconds" % (attempt + 1, to_text(e), cmd_summary, pause))

display.vv(msg, host=self.host)

Expand Down Expand Up @@ -681,7 +682,7 @@ def _send_initial_data(self, fh, in_data, ssh_process):
just hang forever waiting for more commands.)
'''

display.debug('Sending initial data')
display.debug(u'Sending initial data')

try:
fh.write(to_bytes(in_data))
Expand All @@ -697,7 +698,7 @@ def _send_initial_data(self, fh, in_data, ssh_process):
'over ssh: %s' % (self.host, to_native(e)), orig_exc=e
)

display.debug('Sent initial data (%d bytes)' % len(in_data))
display.debug(u'Sent initial data (%d bytes)' % len(in_data))

# Used by _run() to kill processes on failures
@staticmethod
Expand Down Expand Up @@ -727,18 +728,18 @@ def _examine_output(self, source, state, b_chunk, sudoable):

# display.debug("Examining line (source=%s, state=%s): '%s'" % (source, state, display_line))
if self.become.expect_prompt() and self.become.check_password_prompt(b_line):
display.debug("become_prompt: (source=%s, state=%s): '%s'" % (source, state, display_line))
display.debug(u"become_prompt: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_prompt'] = True
suppress_output = True
elif self.become.success and self.become.check_success(b_line):
display.debug("become_success: (source=%s, state=%s): '%s'" % (source, state, display_line))
display.debug(u"become_success: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_success'] = True
suppress_output = True
elif sudoable and self.become.check_incorrect_password(b_line):
display.debug("become_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
display.debug(u"become_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_error'] = True
elif sudoable and self.become.check_missing_password(b_line):
display.debug("become_nopasswd_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
display.debug(u"become_nopasswd_error: (source=%s, state=%s): '%s'" % (source, state, display_line))
self._flags['become_nopasswd_error'] = True

if not suppress_output:
Expand All @@ -762,8 +763,8 @@ def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True):
'''

# We don't use _shell.quote as this is run on the controller and independent from the shell plugin chosen
display_cmd = list(map(shlex_quote, map(to_text, cmd)))
display.vvv(u'SSH: EXEC {0}'.format(u' '.join(display_cmd)), host=self.host)
display_cmd = u' '.join(shlex_quote(to_text(c)) for c in cmd)
display.vvv(u'SSH: EXEC {0}'.format(display_cmd), host=self.host)

# Start the given command. If we don't need to pipeline data, we can try
# to use a pseudo-tty (ssh will have been invoked with -tt). If we are
Expand Down Expand Up @@ -835,12 +836,12 @@ def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True):
# We're requesting escalation with a password, so we have to
# wait for a password prompt.
state = states.index('awaiting_prompt')
display.debug(u'Initial state: %s: %s' % (states[state], prompt))
elif self._play_context.become and self._play_context.success_key:
display.debug(u'Initial state: %s: %s' % (states[state], to_text(prompt)))
elif self._play_context.become and self.become.success:
# 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(u'Initial state: %s: %s' % (states[state], self._play_context.success_key))
display.debug(u'Initial state: %s: %s' % (states[state], to_text(self.become.success)))

# We store accumulated stdout and stderr output from the process here,
# but strip any privilege escalation prompt/confirmation lines first.
Expand Down Expand Up @@ -911,7 +912,7 @@ def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True):
# not going to arrive until the persisted connection closes.
timeout = 1
b_tmp_stdout += b_chunk
display.debug("stdout chunk (state=%s):\n>>>%s<<<\n" % (state, to_text(b_chunk)))
display.debug(u"stdout chunk (state=%s):\n>>>%s<<<\n" % (state, to_text(b_chunk)))
elif key.fileobj == p.stderr:
b_chunk = p.stderr.read()
if b_chunk == b'':
Expand Down Expand Up @@ -945,7 +946,7 @@ def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True):

if states[state] == 'awaiting_prompt':
if self._flags['become_prompt']:
display.debug('Sending become_password in response to prompt')
display.debug(u'Sending become_password in response to prompt')
stdin.write(to_bytes(self._play_context.become_pass) + b'\n')
# On python3 stdin is a BufferedWriter, and we don't have a guarantee
# that the write will happen without a flush
Expand All @@ -960,23 +961,23 @@ def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True):

if states[state] == 'awaiting_escalation':
if self._flags['become_success']:
display.vvv('Escalation succeeded')
display.vvv(u'Escalation succeeded')
self._flags['become_success'] = False
state += 1
elif self._flags['become_error']:
display.vvv('Escalation failed')
display.vvv(u'Escalation failed')
self._terminate_process(p)
self._flags['become_error'] = False
raise AnsibleError('Incorrect %s password' % self._play_context.become_method)
elif self._flags['become_nopasswd_error']:
display.vvv('Escalation requires password')
display.vvv(u'Escalation requires password')
self._terminate_process(p)
self._flags['become_nopasswd_error'] = False
raise AnsibleError('Missing %s password' % self._play_context.become_method)
elif self._flags['become_prompt']:
# This shouldn't happen, because we should see the "Sorry,
# try again" message first.
display.vvv('Escalation prompt repeated')
display.vvv(u'Escalation prompt repeated')
self._terminate_process(p)
self._flags['become_prompt'] = False
raise AnsibleError('Incorrect %s password' % self._play_context.become_method)
Expand Down Expand Up @@ -1126,9 +1127,9 @@ def _file_transport_command(self, in_path, out_path, sftp_action):
else:
# If not in smart mode, the data will be printed by the raise below
if len(methods) > 1:
display.warning('%s transfer mechanism failed on %s. Use ANSIBLE_DEBUG=1 to see detailed information' % (method, host))
display.debug('%s' % to_text(stdout))
displ 6D47 ay.debug('%s' % to_text(stderr))
display.warning(u'%s transfer mechanism failed on %s. Use ANSIBLE_DEBUG=1 to see detailed information' % (method, host))
display.debug(u'%s' % to_text(stdout))
display.debug(u'%s' % to_text(stderr))

if returncode == 255:
raise AnsibleConnectionFailure("Failed to connect to the host via %s: %s" % (method, to_native(stderr)))
Expand Down Expand Up @@ -1237,12 +1238,12 @@ def reset(self):
run_reset = True

if run_reset:
display.vvv(u'sending stop: %s' % cmd)
display.vvv(u'sending stop: %s' % to_text(cmd))
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
status_code = p.wait()
if status_code != 0:
display.warning("Failed to reset connection:%s" % stderr)
display.warning(u"Failed to reset connection:%s" % to_text(stderr))

self.close()

Expand Down
0