def double_timeout_channel(connected_channel):
    connected_channel._check_console_mode = mock.Mock(
        side_effect=exceptions.CmdlineTimeout(
            timeout=libvirtchannel.DEFAULT_EXPECT_TIMEOUT))
    connected_channel.expect = mock.Mock(side_effect=exceptions.CmdlineTimeout(
        timeout=libvirtchannel.DEFAULT_EXPECT_TIMEOUT))
    return connected_channel
    def _handle_init_login(self, match_res, timeout):
        """
        Handle initial login.

        :param match_res: Pattern(s) of prompts to look for after login.
            May be a single regex string, or a list of them.
        :param timeout: maximum time, in seconds, to wait for a regular
            expression match. 0 to wait forever.
        :return: Python :class:`re.MatchObject` containing data on what
            was matched after login.
        :raises CmdlineTimeout: if any step of the login exceeds the timeout.
        """

        # Add login prompt and password prompt so that we can detect
        # what require for login
        reg_with_login_prompts = match_res
        reg_with_login_prompts.insert(0, self.PASSWORD_PROMPT)
        reg_with_login_prompts.insert(0, self.LOGIN_PROMPT)

        (index, match, data) = self.channel.expect(reg_with_login_prompts,
                                                   timeout)

        if index == 0:
            # username is required for login
            logging.debug("Sending login user ...")
            # Encode text to ascii; telnetlib does not work well with unicode
            # literals.
            text_to_send = (self._user + self.ENTER_LINE).encode('ascii')
            self.channel.write(text_to_send)
            (index, match, data) = self.channel.expect(reg_with_login_prompts,
                                                       timeout)
        if index == 1:
            # password is required for login
            logging.debug("Sending password ...")
            # Encode text to ascii; telnetlib does not work well with unicode
            # literals.
            text_to_send = (self._password + self.ENTER_LINE).encode('ascii')
            self.channel.write(text_to_send)
            (index, match, data) = self.channel.expect(reg_with_login_prompts,
                                                       timeout)
        # At this point, we should already logged in; raises exceptions if not
        if index < 0:
            raise exceptions.CmdlineTimeout(timeout=timeout,
                                            failed_match=match_res)
        elif index in (0, 1):
            logging.info("Login failed, still waiting for %s prompt",
                         ('username' if index == 0 else 'password'))
            raise exceptions.CmdlineTimeout(
                timeout=timeout, failed_match=reg_with_login_prompts[index])

        # Login successfully if reach this point
        logging.info('Telnet channel to "%s" started' % self._host)
        return match
def bad_password_channel(connected_channel):
    connected_channel._check_console_mode = mock.Mock(return_value=MATCH_LOGIN)
    connected_channel.expect = mock.Mock(side_effect=(
        ('', MATCH_PASSWORD),
        exceptions.CmdlineTimeout(
            timeout=libvirtchannel.DEFAULT_EXPECT_TIMEOUT),
    ))
    return connected_channel
def initial_timeout_channel(connected_channel):
    connected_channel._check_console_mode = mock.Mock(
        side_effect=exceptions.CmdlineTimeout(
            timeout=libvirtchannel.DEFAULT_EXPECT_TIMEOUT))
    # We expect to restart the login prompt matching process after a timeout.
    connected_channel.expect = mock.Mock(side_effect=(
        ('', MATCH_LOGIN),
        ('', MATCH_PASSWORD),
        ('', MATCH_ROOT),
    ))
    return connected_channel
    def expect(self, match_res, timeout=60):
        """
        Waits for some text to be received that matches one or more regex
        patterns.

        Note that data may have been received before this call and is waiting
        in the buffer; you may want to call receive_all() to flush the receive
        buffer before calling send() and call this function to match the
        output from your send() only.

        :param match_res: Pattern(s) to look for to be considered successful.
                          May be a single regex string, or a list of them.
        :param timeout: maximum time, in seconds, to wait for a regular
                        expression match. 0 to wait forever.

        :return: ``(output, match_object)`` where output is the output of
            the command (without the matched text), and match_object is a
            Python :class:`re.MatchObject` containing data on what was matched.

            You may use ``MatchObject.string[m.start():m.end()]`` to recover
            the actual matched text, which will be unicode.

            ``re.MatchObject.pattern`` will contain the pattern that matched,
            which will be one of the elements of match_res passed in.

        :raises CmdlineTimeout: if no match found before timeout.
        """

        match_res, safe_match_text = self._expect_init(match_res)
        (index, matched, data) = self.channel.expect(match_res, timeout)
        if index == -1:
            raise exceptions.CmdlineTimeout(timeout=timeout,
                                            failed_match=match_res)
        # Remove matched string at the end
        length = matched.start() - matched.end()
        if length < 0:
            data = data[:length]

        # Normalize carriage returns
        data = self.fixup_carriage_returns(data)

        return (data, matched)
def bad_username_channel(connected_channel):
    connected_channel._check_console_mode = mock.Mock(return_value=MATCH_LOGIN)
    connected_channel.expect = mock.Mock(
        side_effect=exceptions.CmdlineTimeout(
            timeout=libvirtchannel.DEFAULT_EXPECT_TIMEOUT))
    return connected_channel
 def expectsig(signum, frame):
     raise exceptions.CmdlineTimeout(timeout)
    def expect(self, match_res, timeout=DEFAULT_EXPECT_TIMEOUT):
        """
        Waits for text to be received that matches one or more regex patterns.

        Note that data may have been received before this call and is waiting
        in the buffer; you may want to call receive_all() to flush the receive
        buffer before calling send() and call this function to match the
        output from your send() only.

        :param match_res: Pattern(s) to look for to be considered successful.
                          May be a single regex string, or a list of them.
                          Currently cannot match multiple lines.
        :param timeout: maximum time, in seconds, to wait for a regular
                        expression match. 0 to wait forever.

        :return: ``(output, match_object)`` where output is the output of
            the command (without the matched text), and match_object is a
            Python :class:`re.MatchObject` containing data on what was matched.

            You may use ``MatchObject.string[m.start():m.end()]`` to recover
            the actual matched text, which will be unicode.

            ``re.MatchObject.pattern`` will contain the pattern that matched,
            which will be one of the elements of match_res passed in.

        :raises CmdlineTimeout: if no match found before timeout.
        :raises ConnectionError: if the channel is closed.
        """

        match_res, safe_match_text = self._expect_init(match_res)
        received_data = ''

        # Index into received_data marking the start of the first unprocessed
        # line.
        next_line_start = 0

        starttime = time.time()

        while True:
            # Use select to check whether channel is ready for read.
            # Reading on the channel directly would block until data is
            # ready, where select blocks at most 10 seconds which allows
            # us to check whether the specified timeout has been reached.
            # If channel is not ready for reading within 10 seconds,
            # select returns an empty list to 'readers'.
            (readers, w, x) = select.select([self.channel], [], [], 10)

            # Timeout if this is taking too long.
            if timeout and ((time.time() - starttime) > timeout):
                partial_output = repr(self.safe_line_feeds(received_data))
                raise exceptions.CmdlineTimeout(command=None,
                                                output=partial_output,
                                                timeout=timeout,
                                                failed_match=match_res)

            new_data = None

            # We did not find clear documentation in Paramiko on how to check
            # whether a channel is closed unexpectedly. Our current logic is
            # that a channel is closed if:
            #   (1) read the channel and get 0 bytes, or
            #   (2) channel is not ready for reading but exit_status_ready()
            # Our experiments has shown that this correctly handles detecting
            # if a channel has been unexpected closed.
            if len(readers) > 0:
                new_data = self.channel.recv(4096)

                if len(new_data) == 0:
                    # Channel closed
                    raise exceptions.ConnectionError(
                        failed_match=match_res,
                        context='Channel unexpectedly closed')

                # If we're still here, we have new data to process.
                received_data, new_lines = self._process_data(
                    new_data, received_data, next_line_start)

                output, match = self._match_lines(received_data,
                                                  next_line_start, new_lines,
                                                  match_res)

                if (output, match) != (None, None):
                    return output, match

                # Update next_line_start to be the index of the last \n
                next_line_start = received_data.rfind('\n') + 1

            elif self.channel.exit_status_ready():
                raise exceptions.ConnectionError(
                    failed_match=match_res,
                    context='Channel unexpectedly closed')
Esempio n. 9
0
    def _exec_paramiko_command(self, command, timeout, retry_count,
                               retry_delay):
        try:
            channel = self.sshprocess.transport.open_session()
        except socket.error:
            if retry_count == 0:
                logging.error("Socket Error %s" % traceback.format_exc())
                raise exceptions.ConnectionError

            # Reconnect and try again
            logging.info("connection seems broken, reconnect...")
            self._reconnect(retry_count=retry_count, retry_delay=retry_delay)
            channel = self.sshprocess.transport.open_session()

        # Put stderr into the same output as stdout.
        channel.set_combine_stderr(True)

        starttime = time.time()

        # Paramiko 1.7.5 has a bug in its internal event system
        # that can cause it to sometimes throw a 'not connected' exception when
        # running exec_command.  If we get that exception here, but we're still
        # connected, then just eat the exception and go on, since that's the
        # normal case.  This will hopefully be fixed in 1.7.6 and this
        # try/except removed.
        try:
            channel.exec_command(command)
        except paramiko.SSHException:
            if not self.sshprocess.is_connected():
                logging.info("Not connected to %s", self._host)
                logging.error("SSHException %s" % traceback.format_exc())
                raise exceptions.ConnectionError
            else:
                logging.debug('Ignore Paramiko SSHException due to 1.7.5 bug')

        chan_closed = False
        output = ""

        # Read until we time out or the channel closes
        while not chan_closed:

            # Use select to check whether channel is ready for read.
            # Reading on the channel directly would block until data is
            # ready, where select blocks at most 10 seconds which allows
            # us to check whether the specified timeout has been reached.
            # If channel is not ready for reading within 10 seconds,
            # select returns an empty list to 'readers'.
            (readers, w, x) = select.select([channel], [], [], 10)

            # Timeout if this is taking too long.
            if timeout and ((time.time() - starttime) > timeout):
                raise exceptions.CmdlineTimeout(command=command,
                                                timeout=timeout)

            # If the reader-ready list isn't empty, then read.  We know it must
            # be channel here, since thats all we're waiting on.
            if len(readers) > 0:
                data = channel.recv(4096)

                # If we get no data back, the channel has closed.
                if len(data) > 0:
                    output += data
                else:
                    chan_closed = True

            elif channel.exit_status_ready():
                # If no readers were available, see if the
                # exit status is ready - if so, the channel must be closed.
                # exit_status_ready can return true before we've read all the
                # data.  Problem is, I know I've seen it return true when there
                # was no data, and then data came in afterwards, so this might
                # occasionally trip early.  If only paramiko.channel had a way
                # to see if it was closed..
                chan_closed = True

        # Done reading.  Now we need to wait for the exit status/channel close.
        # Rather than block here, we'll poll to ensure we don't get stuck.
        while not channel.exit_status_ready():
            if timeout and ((time.time() - starttime) > timeout):
                raise exceptions.CmdlineTimeout(command=command,
                                                timeout=timeout)
            else:
                time.sleep(0.5)

        exit_status = channel.recv_exit_status()
        channel.close()

        return output, exit_status