def test_winrm_run_cmd_timeout(self, mock_get_command_output): mock_protocol = mock.MagicMock() mock_protocol.open_shell.return_value = 123 mock_protocol.run_command.return_value = 456 mock_session = mock.MagicMock(protocol=mock_protocol) mock_get_command_output.side_effect = WinRmRunnerTimoutError( Response(("", "", 5))) self._init_runner() result = self._runner._winrm_run_cmd( mock_session, "fake-command", args=["arg1", "arg2"], env={"PATH": "C:\\st2\\bin"}, cwd="C:\\st2", ) expected_response = Response(("", "", 5)) expected_response.timeout = True self.assertEqual(result.__dict__, expected_response.__dict__) mock_protocol.open_shell.assert_called_with( env_vars={"PATH": "C:\\st2\\bin"}, working_directory="C:\\st2") mock_protocol.run_command.assert_called_with(123, "fake-command", ["arg1", "arg2"]) mock_protocol.cleanup_command.assert_called_with(123, 456) mock_protocol.close_shell.assert_called_with(123)
def test_winrm_run_cmd(self): mock_protocol = mock.MagicMock() mock_protocol.open_shell.return_value = 123 mock_protocol.run_command.return_value = 456 mock_protocol._raw_get_command_output.return_value = (b'output', b'error', 9, True) mock_session = mock.MagicMock(protocol=mock_protocol) self._init_runner() result = self._runner._winrm_run_cmd(mock_session, "fake-command", args=['arg1', 'arg2'], env={'PATH': 'C:\\st2\\bin'}, cwd='C:\\st2') expected_response = Response((b'output', b'error', 9)) expected_response.timeout = False self.assertEquals(result.__dict__, expected_response.__dict__) mock_protocol.open_shell.assert_called_with( env_vars={'PATH': 'C:\\st2\\bin'}, working_directory='C:\\st2') mock_protocol.run_command.assert_called_with(123, 'fake-command', ['arg1', 'arg2']) mock_protocol._raw_get_command_output.assert_called_with(123, 456) mock_protocol.cleanup_command.assert_called_with(123, 456) mock_protocol.close_shell.assert_called_with(123)
def test_winrm_run_cmd(self): mock_protocol = mock.MagicMock() mock_protocol.open_shell.return_value = 123 mock_protocol.run_command.return_value = 456 mock_protocol._raw_get_command_output.return_value = ( b"output", b"error", 9, True, ) mock_session = mock.MagicMock(protocol=mock_protocol) self._init_runner() result = self._runner._winrm_run_cmd( mock_session, "fake-command", args=["arg1", "arg2"], env={"PATH": "C:\\st2\\bin"}, cwd="C:\\st2", ) expected_response = Response((b"output", b"error", 9)) expected_response.timeout = False self.assertEqual(result.__dict__, expected_response.__dict__) mock_protocol.open_shell.assert_called_with( env_vars={"PATH": "C:\\st2\\bin"}, working_directory="C:\\st2") mock_protocol.run_command.assert_called_with(123, "fake-command", ["arg1", "arg2"]) mock_protocol._raw_get_command_output.assert_called_with(123, 456) mock_protocol.cleanup_command.assert_called_with(123, 456) mock_protocol.close_shell.assert_called_with(123)
def test_translate_response_timeout(self): response = Response(('output1', 'error1', 123)) response.timeout = True result = self._runner._translate_response(response) self.assertEquals(result, ('timeout', { 'failed': True, 'succeeded': False, 'return_code': -1, 'stdout': 'output1', 'stderr': 'error1' }, None))
def test_translate_response_timeout(self): response = Response(('output1', 'error1', 123)) response.timeout = True result = self._runner._translate_response(response) self.assertEquals(result, ('timeout', {'failed': True, 'succeeded': False, 'return_code': -1, 'stdout': 'output1', 'stderr': 'error1'}, None))
def _winrm_exec(self, command, args=(), from_exec=False): if from_exec: vvvv("WINRM EXEC %r %r" % (command, args), host=self.host) else: vvvvv("WINRM EXEC %r %r" % (command, args), host=self.host) if not self.protocol: self.protocol = self._winrm_connect() if not self.shell_id: self.shell_id = self.protocol.open_shell() command_id = None try: command_id = self.protocol.run_command(self.shell_id, command, args) response = Response( self.protocol.get_command_output(self.shell_id, command_id)) if from_exec: vvvv('WINRM RESULT %r' % response, host=self.host) else: vvvvv('WINRM RESULT %r' % response, host=self.host) vvvvv('WINRM STDOUT %s' % response.std_out, host=self.host) vvvvv('WINRM STDERR %s' % response.std_err, host=self.host) return response finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)
def _winrm_get_command_output(self, protocol, shell_id, command_id): # NOTE: this is copied from pywinrm because it doesn't support # timeouts stdout_buffer, stderr_buffer = [], [] return_code = 0 command_done = False start_time = time.time() while not command_done: # check if we need to timeout (StackStorm custom) current_time = time.time() elapsed_time = current_time - start_time if self._timeout and (elapsed_time > self._timeout): raise WinRmRunnerTimoutError( Response(( b"".join(stdout_buffer), b"".join(stderr_buffer), WINRM_TIMEOUT_EXIT_CODE, ))) # end stackstorm custom try: ( stdout, stderr, return_code, command_done, ) = protocol._raw_get_command_output(shell_id, command_id) stdout_buffer.append(stdout) stderr_buffer.append(stderr) except WinRMOperationTimeoutError: # this is an expected error when waiting for a long-running process, # just silently retry pass return b"".join(stdout_buffer), b"".join(stderr_buffer), return_code
def test_winrm_run_ps(self, mock_run_cmd): mock_run_cmd.return_value = Response(("output", "", 3)) script = "Get-ADUser stanley" result = self._runner._winrm_run_ps("session", script, env={"PATH": "C:\\st2\\bin"}, cwd="C:\\st2") self.assertEqual(result.__dict__, Response(("output", "", 3)).__dict__) expected_ps = "powershell -encodedcommand " + b64encode( "Get-ADUser stanley".encode("utf_16_le")).decode("ascii") mock_run_cmd.assert_called_with("session", expected_ps, env={"PATH": "C:\\st2\\bin"}, cwd="C:\\st2")
def test_winrm_run_ps(self, mock_run_cmd): mock_run_cmd.return_value = Response(('output', '', 3)) script = "Get-ADUser stanley" result = self._runner._winrm_run_ps("session", script, env={'PATH': 'C:\\st2\\bin'}, cwd='C:\\st2') self.assertEqual(result.__dict__, Response(('output', '', 3)).__dict__) expected_ps = ('powershell -encodedcommand ' + b64encode( "Get-ADUser stanley".encode('utf_16_le')).decode('ascii')) mock_run_cmd.assert_called_with("session", expected_ps, env={'PATH': 'C:\\st2\\bin'}, cwd='C:\\st2')
def _winrm_exec(self, command, args=(), from_exec=False): if from_exec: self._display.vvvvv("WINRM EXEC %r %r" % (command, args), host=self._play_context.remote_addr) else: self._display.vvvvvv("WINRM EXEC %r %r" % (command, args), host=self._play_context.remote_addr) if not self.protocol: self.protocol = self._winrm_connect() if not self.shell_id: self.shell_id = self.protocol.open_shell(codepage=65001) # UTF-8 command_id = None try: command_id = self.protocol.run_command(self.shell_id, to_bytes(command), map(to_bytes, args)) response = Response( self.protocol.get_command_output(self.shell_id, command_id)) if from_exec: self._display.vvvvv('WINRM RESULT %r' % to_unicode(response), host=self._play_context.remote_addr) else: self._display.vvvvv('WINRM RESULT %r' % to_unicode(response), host=self._play_context.remote_addr) self._display.vvvvvv('WINRM STDOUT %s' % to_unicode(response.std_out), host=self._play_context.remote_addr) self._display.vvvvvv('WINRM STDERR %s' % to_unicode(response.std_err), host=self._play_context.remote_addr) return response finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)
def run_cmd(self, command, args=()): # TODO optimize perf. Do not call open/close shell every time shell_id = self.protocol.open_shell(codepage=65001) command_id = self.protocol.run_command(shell_id, command, args) rs = Response(self.protocol.get_command_output(shell_id, command_id)) self.protocol.cleanup_command(shell_id, command_id) self.protocol.close_shell(shell_id) return rs
def run_cmd(self, command, args=()): self.protocol.DEFAULT_READ_TIMEOUT_SEC = 300 self.protocol.DEFAULT_OPERATION_TIMEOUT_SEC = 200 shell_id = self.protocol.open_shell(codepage=65001) command_id = self.protocol.run_command(shell_id, command, args) rs = Response(self.protocol.get_command_output(shell_id, command_id)) self.protocol.cleanup_command(shell_id, command_id) self.protocol.close_shell(shell_id) return rs
def test_winrm_run_ps_clean_stderr(self, mock_run_cmd): mock_run_cmd.return_value = Response(('output', 'error', 3)) mock_session = mock.MagicMock() mock_session._clean_error_msg.return_value = 'e' script = "Get-ADUser stanley" result = self._runner._winrm_run_ps(mock_session, script, env={'PATH': 'C:\\st2\\bin'}, cwd='C:\\st2') self.assertEqual(result.__dict__, Response(('output', 'e', 3)).__dict__) expected_ps = ('powershell -encodedcommand ' + b64encode("Get-ADUser stanley".encode('utf_16_le')).decode('ascii')) mock_run_cmd.assert_called_with(mock_session, expected_ps, env={'PATH': 'C:\\st2\\bin'}, cwd='C:\\st2') mock_session._clean_error_msg.assert_called_with('error')
def test_translate_response_timeout(self): response = Response(("output1", "error1", 123)) response.timeout = True result = self._runner._translate_response(response) self.assertEqual( result, ( "timeout", { "failed": True, "succeeded": False, "return_code": -1, "stdout": "output1", "stderr": "error1", }, None, ), )
def test_winrm_run_ps_clean_stderr(self, mock_run_cmd): mock_run_cmd.return_value = Response(("output", "error", 3)) mock_session = mock.MagicMock() mock_session._clean_error_msg.return_value = "e" script = "Get-ADUser stanley" result = self._runner._winrm_run_ps(mock_session, script, env={"PATH": "C:\\st2\\bin"}, cwd="C:\\st2") self.assertEqual(result.__dict__, Response(("output", "e", 3)).__dict__) expected_ps = "powershell -encodedcommand " + b64encode( "Get-ADUser stanley".encode("utf_16_le")).decode("ascii") mock_run_cmd.assert_called_with(mock_session, expected_ps, env={"PATH": "C:\\st2\\bin"}, cwd="C:\\st2") mock_session._clean_error_msg.assert_called_with("error")
def _winrm_run_cmd(self, session, command, args=(), env=None, cwd=None): # NOTE: this is copied from pywinrm because it doesn't support # passing env and working_directory from the Session.run_cmd. # It also doesn't support timeouts. All of these things have been # added shell_id = session.protocol.open_shell(env_vars=env, working_directory=cwd) command_id = session.protocol.run_command(shell_id, command, args) # try/catch is for custom timeout handing (StackStorm custom) try: rs = Response( self._winrm_get_command_output(session.protocol, shell_id, command_id)) rs.timeout = False except WinRmRunnerTimoutError as e: rs = e.response rs.timeout = True # end stackstorm custom session.protocol.cleanup_command(shell_id, command_id) session.protocol.close_shell(shell_id) return rs
def _winrm_run_cmd(self, session, command, args=(), env=None, cwd=None): # NOTE: this is copied from pywinrm because it doesn't support # passing env and working_directory from the Session.run_cmd. # It also doesn't support timeouts. All of these things have been # added shell_id = session.protocol.open_shell(env_vars=env, working_directory=cwd) command_id = session.protocol.run_command(shell_id, command, args) # try/catch is for custom timeout handing (StackStorm custom) try: rs = Response(self._winrm_get_command_output(session.protocol, shell_id, command_id)) rs.timeout = False except WinRmRunnerTimoutError as e: rs = e.response rs.timeout = True # end stackstorm custom session.protocol.cleanup_command(shell_id, command_id) session.protocol.close_shell(shell_id) return rs
def test_winrm_run_cmd_timeout(self, mock_get_command_output): mock_protocol = mock.MagicMock() mock_protocol.open_shell.return_value = 123 mock_protocol.run_command.return_value = 456 mock_session = mock.MagicMock(protocol=mock_protocol) mock_get_command_output.side_effect = WinRmRunnerTimoutError(Response(('', '', 5))) self._init_runner() result = self._runner._winrm_run_cmd(mock_session, "fake-command", args=['arg1', 'arg2'], env={'PATH': 'C:\\st2\\bin'}, cwd='C:\\st2') expected_response = Response(('', '', 5)) expected_response.timeout = True self.assertEquals(result.__dict__, expected_response.__dict__) mock_protocol.open_shell.assert_called_with(env_vars={'PATH': 'C:\\st2\\bin'}, working_directory='C:\\st2') mock_protocol.run_command.assert_called_with(123, 'fake-command', ['arg1', 'arg2']) mock_protocol.cleanup_command.assert_called_with(123, 456) mock_protocol.close_shell.assert_called_with(123)
def test_winrm_run_cmd(self): mock_protocol = mock.MagicMock() mock_protocol.open_shell.return_value = 123 mock_protocol.run_command.return_value = 456 mock_protocol._raw_get_command_output.return_value = (b'output', b'error', 9, True) mock_session = mock.MagicMock(protocol=mock_protocol) self._init_runner() result = self._runner._winrm_run_cmd(mock_session, "fake-command", args=['arg1', 'arg2'], env={'PATH': 'C:\\st2\\bin'}, cwd='C:\\st2') expected_response = Response((b'output', b'error', 9)) expected_response.timeout = False self.assertEquals(result.__dict__, expected_response.__dict__) mock_protocol.open_shell.assert_called_with(env_vars={'PATH': 'C:\\st2\\bin'}, working_directory='C:\\st2') mock_protocol.run_command.assert_called_with(123, 'fake-command', ['arg1', 'arg2']) mock_protocol._raw_get_command_output.assert_called_with(123, 456) mock_protocol.cleanup_command.assert_called_with(123, 456) mock_protocol.close_shell.assert_called_with(123)
def _winrm_exec(self, command, args=(), from_exec=False, stdin_iterator=None): if not self.protocol: self.protocol = self._winrm_connect() self._connected = True if from_exec: display.vvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) else: display.vvvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) command_id = None try: stdin_push_failed = False command_id = self.protocol.run_command(self.shell_id, to_bytes(command), map(to_bytes, args), console_mode_stdin=(stdin_iterator is None)) # TODO: try/except around this, so we can get/return the command result on a broken pipe or other failure (probably more useful than the 500 that # comes from this) try: if stdin_iterator: for (data, is_last) in stdin_iterator: self._winrm_send_input(self.protocol, self.shell_id, command_id, data, eof=is_last) except Exception as ex: from traceback import format_exc display.warning("FATAL ERROR DURING FILE TRANSFER: %s" % format_exc(ex)) stdin_push_failed = True if stdin_push_failed: raise AnsibleError('winrm send_input failed') # NB: this can hang if the receiver is still running (eg, network failed a Send request but the server's still happy). # FUTURE: Consider adding pywinrm status check/abort operations to see if the target is still running after a failure. resptuple = self.protocol.get_command_output(self.shell_id, command_id) # ensure stdout/stderr are text for py3 # FUTURE: this should probably be done internally by pywinrm response = Response(tuple(to_text(v) if isinstance(v, binary_type) else v for v in resptuple)) # TODO: check result from response and set stdin_push_failed if we have nonzero if from_exec: display.vvvvv('WINRM RESULT %r' % to_text(response), host=self._winrm_host) else: display.vvvvvv('WINRM RESULT %r' % to_text(response), host=self._winrm_host) display.vvvvvv('WINRM STDOUT %s' % to_text(response.std_out), host=self._winrm_host) display.vvvvvv('WINRM STDERR %s' % to_text(response.std_err), host=self._winrm_host) if stdin_push_failed: raise AnsibleError('winrm send_input failed; \nstdout: %s\nstderr %s' % (response.std_out, response.std_err)) return response finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)
def _winrm_exec(self, command, args=(), from_exec=False, stdin_iterator=None): if not self.protocol: self.protocol = self._winrm_connect() self._connected = True if from_exec: display.vvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) else: display.vvvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) command_id = None try: stdin_push_failed = False command_id = self.protocol.run_command(self.shell_id, to_bytes(command), map(to_bytes, args), console_mode_stdin=(stdin_iterator is None)) try: if stdin_iterator: for (data, is_last) in stdin_iterator: 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)) 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). # FUTURE: Consider adding pywinrm status check/abort operations to see if the target is still running after a failure. resptuple = self.protocol.get_command_output(self.shell_id, command_id) # ensure stdout/stderr are text for py3 # FUTURE: this should probably be done internally by pywinrm response = Response(tuple(to_text(v) if isinstance(v, binary_type) else v for v in resptuple)) # TODO: check result from response and set stdin_push_failed if we have nonzero if from_exec: display.vvvvv('WINRM RESULT %r' % to_text(response), host=self._winrm_host) else: display.vvvvvv('WINRM RESULT %r' % to_text(response), host=self._winrm_host) display.vvvvvv('WINRM STDOUT %s' % to_text(response.std_out), host=self._winrm_host) 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))) return response except requests.exceptions.ConnectionError as exc: raise AnsibleConnectionFailure('winrm connection error: %s' % to_native(exc)) finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)
def run_cmd(cmd, proto): """ Run batch script on target machine Args: cmd (str): batch script to run proto (Protocol): Protocol containing target machine Returns: Response: Object containing stderr, stdout and exit_status """ shell_id = proto.open_shell() command_id = proto.run_command(shell_id, cmd) rs = Response(proto.get_command_output(shell_id, command_id)) proto.cleanup_command(shell_id, command_id) proto.close_shell(shell_id) return rs
def _winrm_exec(self, command, args=(), from_exec=False, stdin_iterator=None): if not self.protocol: self.protocol = self._winrm_connect() self._connected = True if not self.shell_id: self.shell_id = self.protocol.open_shell(codepage=65001) # UTF-8 display.vvvvv('WINRM OPEN SHELL: %s' % self.shell_id, host=self._winrm_host) if from_exec: display.vvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) else: display.vvvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) command_id = None try: stdin_push_failed = False command_id = self.protocol.run_command(self.shell_id, to_bytes(command), map(to_bytes, args), console_mode_stdin=(stdin_iterator == None)) # TODO: try/except around this, so we can get/return the command result on a broken pipe or other failure (probably more useful than the 500 that comes from this) try: if stdin_iterator: for (data, is_last) in stdin_iterator: self._winrm_send_input(self.protocol, self.shell_id, command_id, data, eof=is_last) except: stdin_push_failed = True # NB: this could hang if the receiver is still running (eg, network failed a Send request but the server's still happy). # FUTURE: Consider adding pywinrm status check/abort operations to see if the target is still running after a failure. response = Response(self.protocol.get_command_output(self.shell_id, command_id)) if from_exec: display.vvvvv('WINRM RESULT %r' % to_unicode(response), host=self._winrm_host) else: display.vvvvvv('WINRM RESULT %r' % to_unicode(response), host=self._winrm_host) display.vvvvvv('WINRM STDOUT %s' % to_unicode(response.std_out), host=self._winrm_host) display.vvvvvv('WINRM STDERR %s' % to_unicode(response.std_err), host=self._winrm_host) if stdin_push_failed: raise AnsibleError('winrm send_input failed; \nstdout: %s\nstderr %s' % (response.std_out, response.std_err)) return response finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)
def run_cmd_winrm(cmd: str) -> Response: """ Run batch script using winrm client. Args: cmd: batch script to run. Returns: Response object containing stderr, stdout and exit_status. """ client = Protocol(endpoint='http://{}:5985/wsman'.format(config['host']), transport='ntlm', username='******'.format(config['domain'], config['user']), password=config['pass'], server_cert_validation='ignore') shell_id = client.open_shell() command_id = client.run_command(shell_id, cmd) rs = Response(client.get_command_output(shell_id, command_id)) client.cleanup_command(shell_id, command_id) client.close_shell(shell_id) return rs
def _winrm_exec(self, command, args=(), from_exec=False, stdin_iterator=None): if not self.protocol: self.protocol = self._winrm_connect() self._connected = True if from_exec: display.vvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) else: display.vvvvvv("WINRM EXEC %r %r" % (command, args), host=self._winrm_host) command_id = None try: stdin_push_failed = False command_id = self.protocol.run_command(self.shell_id, to_bytes(command), map(to_bytes, args), console_mode_stdin=(stdin_iterator is None)) try: if stdin_iterator: for (data, is_last) in stdin_iterator: self._winrm_send_input(self.protocol, self.shell_id, command_id, data, eof=is_last) except Exception as 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). # FUTURE: Consider adding pywinrm status check/abort operations to see if the target is still running after a failure. resptuple = self.protocol.get_command_output(self.shell_id, command_id) # ensure stdout/stderr are text for py3 # FUTURE: this should probably be done internally by pywinrm response = Response(tuple(to_text(v) if isinstance(v, binary_type) else v for v in resptuple)) # TODO: check result from response and set stdin_push_failed if we have nonzero if from_exec: display.vvvvv('WINRM RESULT %r' % to_text(response), host=self._winrm_host) else: display.vvvvvv('WINRM RESULT %r' % to_text(response), host=self._winrm_host) display.vvvvvv('WINRM STDOUT %s' % to_text(response.std_out), host=self._winrm_host) display.vvvvvv('WINRM STDERR %s' % to_text(response.std_err), host=self._winrm_host) if stdin_push_failed: # 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 stderr.startswith(b"#< CLIXML"): stderr = _parse_clixml(stderr) raise AnsibleError('winrm send_input failed; \nstdout: %s\nstderr %s' % (to_native(response.std_out), to_native(stderr))) return response except requests.exceptions.Timeout as exc: raise AnsibleConnectionFailure('winrm connection error: %s' % to_native(exc)) finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)