From ae24bbff4abf7dce43026f18bf113c2f76ba8301 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Tue, 5 Mar 2019 11:31:00 +1000 Subject: [PATCH] winrm - try and recover from a send input failure (#53187) --- .../fragments/winrm-send-input-check.yaml | 2 ++ lib/ansible/plugins/connection/winrm.py | 23 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 changelogs/fragments/winrm-send-input-check.yaml diff --git a/changelogs/fragments/winrm-send-input-check.yaml b/changelogs/fragments/winrm-send-input-check.yaml new file mode 100644 index 00000000000..50d2a2ee0d6 --- /dev/null +++ b/changelogs/fragments/winrm-send-input-check.yaml @@ -0,0 +1,2 @@ +bugfixes: +- winrm - attempt to recover from a WinRM send input failure if possible diff --git a/lib/ansible/plugins/connection/winrm.py b/lib/ansible/plugins/connection/winrm.py index c669e72b52c..eda7f6ac16b 100644 --- a/lib/ansible/plugins/connection/winrm.py +++ b/lib/ansible/plugins/connection/winrm.py @@ -116,6 +116,7 @@ except ImportError: from ansible import constants as C from ansible.errors import AnsibleError, AnsibleConnectionFailure from ansible.errors import AnsibleFileNotFound +from ansible.module_utils.json_utils import _filter_non_json_lines from ansible.module_utils.parsing.convert_bool import boolean from ansible.module_utils.six.moves.urllib.parse import urlunsplit from ansible.module_utils._text import to_bytes, to_native, to_text @@ -451,7 +452,9 @@ class Connection(ConnectionBase): self._winrm_send_input(self.protocol, self.shell_id, command_id, data, eof=is_last) except Exception as ex: - display.warning("FATAL ERROR DURING FILE TRANSFER: %s" % to_text(ex)) + display.warning("ERROR DURING WINRM SEND INPUT - attempting to recover: %s %s" + % (type(ex).__name__, to_text(ex))) + display.debug(traceback.format_exc()) stdin_push_failed = True # NB: this can hang if the receiver is still running (eg, network failed a Send request but the server's still happy). @@ -471,11 +474,19 @@ class Connection(ConnectionBase): display.vvvvvv('WINRM STDERR %s' % to_text(response.std_err), host=self._winrm_host) if stdin_push_failed: - stderr = to_bytes(response.std_err, encoding='utf-8') - if self.is_clixml(stderr): - stderr = self.parse_clixml_stream(stderr) - - raise AnsibleError('winrm send_input failed; \nstdout: %s\nstderr %s' % (to_native(response.std_out), to_native(stderr))) + # There are cases where the stdin input failed but the WinRM service still processed it. We attempt to + # see if stdout contains a valid json return value so we can ignore this error + try: + filtered_output, dummy = _filter_non_json_lines(response.std_out) + json.loads(filtered_output) + except ValueError: + # stdout does not contain a return response, stdin input was a fatal error + stderr = to_bytes(response.std_err, encoding='utf-8') + if self.is_clixml(stderr): + stderr = self.parse_clixml_stream(stderr) + + raise AnsibleError('winrm send_input failed; \nstdout: %s\nstderr %s' + % (to_native(response.std_out), to_native(stderr))) return response except requests.exceptions.ConnectionError as exc: