def wait_finished(self, host_output, timeout=None): """Wait for EOF from channel and close channel. Used to wait for remote command completion and be able to gather exit code. :param host_output: Host output of command to wait for. :type host_output: :py:class:`pssh.output.HostOutput` :param timeout: Timeout value in seconds - defaults to no timeout. :type timeout: float :raises: :py:class:`pssh.exceptions.Timeout` after <timeout> seconds if timeout set. """ if not isinstance(host_output, HostOutput): raise ValueError("%s is not a HostOutput object" % (host_output, )) channel = host_output.channel if channel is None: return logger.debug("Sending EOF on channel %s", channel) self._eagain(channel.send_eof, timeout=self.timeout) logger.debug("Waiting for readers, timeout %s", timeout) with GTimeout(seconds=timeout, exception=Timeout): joinall((host_output.buffers.stdout.reader, host_output.buffers.stderr.reader)) logger.debug("Readers finished, closing channel") self.close_channel(channel)
def _read_output(self, read_func, timeout=None): remainder = b"" remainder_len = 0 size, data = read_func() t = GTimeout(seconds=timeout, exception=Timeout) t.start() try: while size == LIBSSH2_ERROR_EAGAIN or size > 0: while size == LIBSSH2_ERROR_EAGAIN: self.poll(timeout=timeout) size, data = read_func() while size > 0: pos = 0 while pos < size: linesep, new_line_pos = find_eol(data, pos) if linesep == -1: remainder += data[pos:] remainder_len = len(remainder) break end_of_line = pos + linesep if remainder_len > 0: line = remainder + data[pos:end_of_line] remainder = b"" remainder_len = 0 else: line = data[pos:end_of_line] yield line pos += linesep + new_line_pos size, data = read_func() if remainder_len > 0: # Finished reading without finding ending linesep yield remainder finally: t.close()
def _read_output_buffer(self, _buffer, timeout=None): timer = GTimeout(seconds=timeout, exception=Timeout) remainder = b"" remainder_len = 0 timer.start() try: for data in _buffer: pos = 0 size = len(data) while pos < size: linesep, new_line_pos = find_eol(data, pos) if linesep == -1: remainder += data[pos:] remainder_len = len(remainder) break end_of_line = pos+linesep if remainder_len > 0: line = remainder + data[pos:end_of_line] remainder = b"" remainder_len = 0 else: line = data[pos:end_of_line] yield line pos += linesep + new_line_pos if remainder_len > 0: # Finished reading without finding ending linesep yield remainder finally: timer.close()
def _eagain_errcode(self, func, eagain, *args, **kwargs): timeout = kwargs.pop('timeout', self.timeout) with GTimeout(seconds=timeout, exception=Timeout): ret = func(*args, **kwargs) while ret == eagain: self.poll() ret = func(*args, **kwargs) return ret
def _eagain(self, func, *args, **kwargs): timeout = kwargs.pop('timeout', self.timeout) with GTimeout(seconds=timeout, exception=Timeout): ret = func(*args, **kwargs) while ret == LIBSSH2_ERROR_EAGAIN: self.poll() ret = func(*args, **kwargs) return ret
def _eagain(self, func, *args, **kwargs): """Run function given and handle EAGAIN for an ssh-python session""" timeout = kwargs.pop('timeout', self.timeout) with GTimeout(seconds=timeout, exception=Timeout): ret = func(*args, **kwargs) while ret == SSH_AGAIN: self.poll(timeout=timeout) ret = func(*args, **kwargs) return ret
def run_command(self, command, sudo=False, user=None, use_pty=False, shell=None, encoding='utf-8', timeout=None, read_timeout=None): """Run remote command. :param command: Command to run. :type command: str :param sudo: Run command via sudo as super-user. :type sudo: bool :param user: Run command as user via sudo :type user: str :param use_pty: Whether or not to obtain a PTY on the channel. :type use_pty: bool :param shell: (Optional) Override shell to use to run command with. Defaults to login user's defined shell. Use the shell's command syntax, eg `shell='bash -c'` or `shell='zsh -c'`. :type shell: str :param encoding: Encoding to use for output. Must be valid `Python codec <https://docs.python.org/library/codecs.html>`_ :type encoding: str :param read_timeout: (Optional) Timeout in seconds for reading output. :type read_timeout: float :param timeout: Deprecated - use read_timeout. :rtype: :py:class:`pssh.output.HostOutput` """ # Fast path for no command substitution needed if not sudo and not user and not shell: _command = command else: _command = '' if sudo and not user: _command = 'sudo -S ' elif user: _command = 'sudo -u %s -S ' % (user, ) _shell = shell if shell else '$SHELL -c' _command += "%s '%s'" % ( _shell, command, ) _command = _command.encode(encoding) with GTimeout(seconds=self.timeout): channel = self.execute(_command, use_pty=use_pty) _timeout = read_timeout if read_timeout else timeout host_out = self._make_host_output(channel, encoding, _timeout) return host_out
def _make_ssh_client(self, host_i, host): auth_thread_pool = True if self.proxy_host is not None and self._tunnel is None: self._start_tunnel_thread() logger.debug( "Make client request for host %s, (host_i, host) in clients: %s", host, (host_i, host) in self._host_clients) if (host_i, host) not in self._host_clients \ or self._host_clients[(host_i, host)] is None: _user, _port, _password, _pkey = self._get_host_config_values( host_i, host) proxy_host = None if self.proxy_host is None else '127.0.0.1' if proxy_host is not None: auth_thread_pool = False max_wait = self.timeout if self.timeout is not None else 60 with self._tunnel_lock: self._tunnel_in_q.append((host, _port)) with GTimeout(seconds=max_wait, exception=Timeout): while True: try: _port = self._tunnel_out_q.pop() except IndexError: logger.debug( "Waiting on tunnel to open listening port") sleep(.1) else: break _client = SSHClient( host, user=_user, password=_password, port=_port, pkey=_pkey, num_retries=self.num_retries, timeout=self.timeout, allow_agent=self.allow_agent, retry_delay=self.retry_delay, proxy_host=proxy_host, _auth_thread_pool=auth_thread_pool, forward_ssh_agent=self.forward_ssh_agent, keepalive_seconds=self.keepalive_seconds, identity_auth=self.identity_auth, ) self._host_clients[(host_i, host)] = _client return _client return self._host_clients[(host_i, host)]
def test_server_start(self): _port = 1234 class Server(object): def __init__(self): self.started = False self.listen_port = _port server = Server() forwarder = LocalForwarder() let = spawn(forwarder._get_server_listen_port, None, server) let.start() sleep(.01) server.started = True sleep(.01) with GTimeout(seconds=1): port = forwarder.out_q.get() self.assertEqual(port, _port)
def wait_finished(self, channel, timeout=None): """Wait for EOF from channel and close channel. Used to wait for remote command completion and be able to gather exit code. :param channel: The channel to use. :type channel: :py:class:`ssh.channel.Channel` :param timeout: Timeout value in seconds - defaults to no timeout. :type timeout: float :raises: :py:class:`pssh.exceptions.Timeout` after <timeout> seconds if timeout given. """ if channel is None: return logger.debug("Sending EOF on channel %s", channel) self._eagain(channel.send_eof, timeout=self.timeout) logger.debug("Waiting for readers, timeout %s", timeout) with GTimeout(seconds=timeout, exception=Timeout): joinall((self._stdout_reader, self._stderr_reader)) logger.debug("Readers finished, closing channel") # Close channel self.close_channel(channel)