Пример #1
0
def input(label, is_password=False, answers=None, default=None):
    """
    Input loop. (like raw_input, but nice colored.)
    'answers' can be either None or a list of the accepted answers.
    """
    def print_question():
        answers_str = (' [%s]' % (','.join(answers)) if answers else '')
        default_str = (' (default=%s)' % default if default else '')
        sys.stdout.write(
            colored('  %s%s%s: ' % (label, answers_str, default_str), 'cyan'))
        sys.stdout.flush()

    def read_answer():
        value = ''
        print_question()

        while True:
            c = sys.stdin.read(1)

            # Enter pressed
            if c in ('\r', '\n') and (value or default):
                sys.stdout.write('\r\n')
                break

            # Backspace pressed
            elif c == '\x7f' and value:
                sys.stdout.write('\b \b')
                value = value[:-1]

            # Valid character
            elif ord(c) in range(32, 127):
                sys.stdout.write(
                    colored('*' if is_password else c, attrs=['bold']))
                value += c

            elif c == '\x03':  # Ctrl-C
                raise NoInput

            sys.stdout.flush()

        # Return result
        if not value and default:
            return default
        else:
            return value

    with std.raw_mode(sys.stdin):
        while True:
            value = read_answer()

            # Return if valid anwer
            if not answers or value in answers:
                return value

            # Otherwise, ask again.
            else:
                sys.stdout.write('Invalid answer.\r\n')
                sys.stdout.flush()
Пример #2
0
def input(label, is_password=False, answers=None, default=None):
    """
    Input loop. (like raw_input, but nice colored.)
    'answers' can be either None or a list of the accepted answers.
    """

    def print_question():
        answers_str = " [%s]" % (",".join(answers)) if answers else ""
        default_str = " (default=%s)" % default if default else ""
        sys.stdout.write(colored("  %s%s%s: " % (label, answers_str, default_str), "cyan"))
        sys.stdout.flush()

    def read_answer():
        value = ""
        print_question()

        while True:
            c = sys.stdin.read(1)

            # Enter pressed
            if c in ("\r", "\n") and (value or default):
                sys.stdout.write("\r\n")
                break

            # Backspace pressed
            elif c == "\x7f" and value:
                sys.stdout.write("\b \b")
                value = value[:-1]

            # Valid character
            elif ord(c) in range(32, 127):
                sys.stdout.write(colored("*" if is_password else c, attrs=["bold"]))
                value += c

            elif c == "\x03":  # Ctrl-C
                raise NoInput

            sys.stdout.flush()

        # Return result
        if not value and default:
            return default
        else:
            return value

    with std.raw_mode(sys.stdin):
        while True:
            value = read_answer()

            # Return if valid anwer
            if not answers or value in answers:
                return value

            # Otherwise, ask again.
            else:
                sys.stdout.write("Invalid answer.\r\n")
                sys.stdout.flush()
Пример #3
0
    def cmdloop(self):
        try:
            while True:
                with raw_mode(self.stdin):
                    result = self.read().strip()

                if result:
                    self.lines_history.append(result)
                    self.history_position = 0

                    self.handle([ p for p in result.split(' ') if p ])

        except ExitCLILoop, e:
            print # Print newline
            return e.result
Пример #4
0
    def lesspipe(self, line_iterator):
        """
        Paginator for output. This will print one page at a time. When the user
        presses a key, the next page is printed. ``Ctrl-c`` or ``q`` will quit
        the paginator.

        :param line_iterator: A generator function that yields lines (without
                              trailing newline)
        """
        stdin = self._pty.stdin
        stdout = self._pty.stdout
        height = self._pty.get_size()[0] - 1

        with std.raw_mode(stdin):
            lines = 0
            for l in line_iterator:
                # Print next line
                stdout.write(l)
                stdout.write('\r\n')
                lines += 1

                # When we are at the bottom of the terminal
                if lines == height:
                    # Wait for the user to press enter.
                    stdout.write(colored('  Press enter to continue...', 'cyan'))
                    stdout.flush()

                    try:
                        c = stdin.read(1)

                        # Control-C or 'q' will quit pager.
                        if c in ('\x03', 'q'):
                            stdout.write('\r\n')
                            stdout.flush()
                            return
                    except IOError:
                        # Interupted system call.
                        pass

                    # Move backwards and erase until the end of the line.
                    stdout.write('\x1b[40D\x1b[K')
                    lines = 0
            stdout.flush()
Пример #5
0
    def cmdloop(self):
        try:
            while True:
                # Set handler for resize terminal events.
                self.pty.set_ssh_channel_size = lambda: self.print_command()

                # Read command
                with raw_mode(self.stdin):
                    result = self.read().strip()

                self.pty.set_ssh_channel_size = None

                # Handle result
                if result:
                    self.lines_history.append(result)
                    self.history_position = 0

                    self.handle([p for p in result.split(' ') if p])

        except ExitCLILoop, e:
            print  # Print newline
            return e.result
Пример #6
0
    def cmdloop(self):
        try:
            while True:
                # Set handler for resize terminal events.
                self.pty.set_ssh_channel_size = lambda: self.print_command()

                # Read command
                with raw_mode(self.stdin):
                    result = self.read().strip()

                self.pty.set_ssh_channel_size = None

                # Handle result
                if result:
                    self.lines_history.append(result)
                    self.history_position = 0

                    self.handle([ p for p in result.split(' ') if p ])

        except ExitCLILoop, e:
            print # Print newline
            return e.result
Пример #7
0
def lesspipe(line_iterator, pty):
    """
    Paginator for output.
    """
    height = pty.get_size()[0] - 1

    with std.raw_mode(sys.stdin):
        lines = 0
        for l in line_iterator:
            # Print next line
            sys.stdout.write(l)
            sys.stdout.write('\r\n')
            lines += 1

            # When we are at the bottom of the terminal
            if lines == height:
                # Wait for the user to press enter.
                sys.stdout.write(
                    colored('  Press enter to continue...', 'cyan'))
                sys.stdout.flush()

                try:
                    c = sys.stdin.read(1)

                    # Control-C or 'q' will quit pager.
                    if c in ('\x03', 'q'):
                        sys.stdout.write('\r\n')
                        sys.stdout.flush()
                        return
                except IOError, e:
                    # Interupted system call.
                    pass

                # Move backwards and erase until the end of the line.
                sys.stdout.write('\x1b[40D\x1b[K')
                lines = 0
        sys.stdout.flush()
Пример #8
0
def lesspipe(line_iterator, pty):
    """
    Paginator for output.
    """
    height = pty.get_size()[0] - 1

    with std.raw_mode(sys.stdin):
        lines = 0
        for l in line_iterator:
            # Print next line
            sys.stdout.write(l)
            sys.stdout.write("\r\n")
            lines += 1

            # When we are at the bottom of the terminal
            if lines == height:
                # Wait for the user to press enter.
                sys.stdout.write(colored("  Press enter to continue...", "cyan"))
                sys.stdout.flush()

                try:
                    c = sys.stdin.read(1)

                    # Control-C or 'q' will quit pager.
                    if c in ("\x03", "q"):
                        sys.stdout.write("\r\n")
                        sys.stdout.flush()
                        return
                except IOError, e:
                    # Interupted system call.
                    pass

                # Move backwards and erase until the end of the line.
                sys.stdout.write("\x1b[40D\x1b[K")
                lines = 0
        sys.stdout.flush()
Пример #9
0
    def __call__(self, context):
        # Real stdin/out of the current logging session
        stdin = context.cli.stdin
        stdout = context.cli.stdout

        # Use a pseudo terminal to pass the logging IO from other deployment
        # sessions to this one.
        master, slave = os.openpty()
        slave_stdout = os.fdopen(slave, 'w', 0)
        master_stdin = os.fdopen(master, 'r', 0)
        fdesc.setNonBlocking(master_stdin)

        def now():
            return datetime.datetime.now().strftime('%H:%I:%S')

        # Create monitor handler
        class M(Monitor):
            def __init__(self):
                self.line = 0 # Keep line numbers

                slave_stdout.write("Press 'q' or Ctrl-c to exit the monitor\n")
                slave_stdout.write(colored('%-12s' % 'Username', 'cyan'))
                slave_stdout.write(colored('%-9s' % 'Time', 'cyan'))
                slave_stdout.write(colored('%-17s' % 'Host', 'cyan'))
                slave_stdout.write(colored('%s' % 'Command', 'cyan'))
                slave_stdout.write('\n')
                slave_stdout.flush()

            def _print_username(self, username):
                slave_stdout.write(colored('%-12s' % username, 'magenta'))
                slave_stdout.write(colored('%-9s' % now()))

            def _print_user_and_event(self, username, event):
                self._print_username(username)
                slave_stdout.write(' '*17 + event)
                slave_stdout.flush()

            def _print_status(self, line,  succeeded):
                slave_stdout.write('\0337') # ESC 7: Save cursor position
                slave_stdout.write('\033[%iA' % (self.line-line)) # Move cursor up
                slave_stdout.write('\033[1000C') # Move cursor to end of line
                slave_stdout.write('\033[8D') # Move cursor 8 chars back

                if succeeded:
                    slave_stdout.write(colored('succeeded', 'green'))
                else:
                    slave_stdout.write(colored('failed', 'red'))

                slave_stdout.write('\0338') # ESC 8: Restore cursor position

            def log_run(self, session, run):
                if not run.sandboxing:
                    line = self.line

                    self._print_username(session.username)
                    slave_stdout.write(colored('%-17s' % run.host.slug, 'cyan'))
                    slave_stdout.write(colored('%s' % (' (sudo) ' if run.use_sudo else ''), 'red'))
                    slave_stdout.write(colored(run.command, 'green'))

                    slave_stdout.write('\n')
                    slave_stdout.flush()
                    self.line += 1

                    # TODO: I guess these writes should be atomic (group in
                    # one statement), because they can come from different
                    # threads.

                    def callback():
                        # TODO: Make more generic. We don't want to write to
                        # this logger when it has been closed. It may be
                        # possible that the logger has been disconnected and
                        # removed from the globals loggers list, but the
                        # callback still exists.
                        if not slave_stdout.closed:
                            self._print_status(line, run.succeeded)
                    return callback
                else:
                    return lambda:None

            def log_file_opened(self, session, file_entry):
                if not file_entry.sandboxing:
                    line = self.line

                    self._print_username(session.username)
                    slave_stdout.write(colored('%-17s' % file_entry.host.slug, 'cyan'))
                    slave_stdout.write(colored('%s' % (' (sudo) ' if file_entry.use_sudo else ''), 'red'))

                    if file_entry.entry_type == Actions.Get:
                        slave_stdout.write('Downloading: ')
                        slave_stdout.write(colored(file_entry.remote_path, 'green'))

                    elif file_entry.entry_type == Actions.Put:
                        slave_stdout.write('Uploading: ')
                        slave_stdout.write(colored(file_entry.remote_path, 'green'))

                    elif file_entry.entry_type == Actions.Open:
                        slave_stdout.write('Opening file: ')
                        slave_stdout.write(colored(file_entry.remote_path, 'green'))
                        slave_stdout.write(' mode=')
                        slave_stdout.write(colored(file_entry.mode, 'green'))

                    slave_stdout.write('\n')
                    slave_stdout.flush()
                    self.line += 1

                    def callback():
                        if not slave_stdout.closed:
                            self._print_status(line, file_entry.succeeded)
                    return callback
                else:
                    return lambda:None

            def action_started(self, session, action_entry):
                line = self.line
                if action_entry.sandboxing:
                    self._print_user_and_event(session.username, 'Calling action (in sandbox): %s\n' %
                                    colored(action_entry.command, 'green'))
                else:
                    self._print_user_and_event(session.username, 'Calling action: %s\n' %
                                    colored(action_entry.command, 'green'))

                class Callbacks(object):
                    def succeeded(s, result):
                        if not slave_stdout.closed:
                            self._print_status(line, True)

                    def failed(s, exception):
                        if not slave_stdout.closed:
                            self._print_status(line, False)

                self.line += 1
                return Callbacks()

            def session_created(self, session):
                self.line += 1
                self._print_user_and_event(session.username,
                            'Started session host=%s id=%s\n' % (session.host, session.id))

            def session_closed(self, session):
                self.line += 1
                self._print_user_and_event(session.username,
                            'Closed session host=%s id=%s\n' % (session.host, session.id))

        monitor = M()
        monitors.append(monitor)

        with std.raw_mode(stdin):
            while True:
                r, w, e = select.select([master_stdin, stdin], [], [])

                # Receive stream from monitor
                if master_stdin in r:
                    stdout.write(master_stdin.read(4096))

                if stdin in r:
                    char = stdin.read(1)

                    # Leave monitor when 'q' or Ctrl-C has been pressed.
                    if char in ('q', '\x03'):
                        # Cleanup
                        monitors.remove(monitor)
                        slave_stdout.close()
                        master_stdin.close()
                        return
Пример #10
0
    def _posix_shell(self, pty, chan, raw=True, log_entry=None, initial_input=None):
        """
        Create a loop which redirects sys.stdin/stdout into this channel.
        The loop ends when channel.recv() returns 0.

        Code inspired by the Paramiko interactive demo.
        """
        result = []
        password_sent = False

        # Make stdin non-blocking. (The select call will already
        # block for us, we want sys.stdin.read() to read as many
        # bytes as possible without blocking.)
        fdesc.setNonBlocking(pty.stdin)

        # Set terminal in raw mode
        if raw:
            context = raw_mode(pty.stdin)
        else:
            context = contextlib.nested()

        with context:
            try:
                chan.settimeout(0.0)

                # When initial input has been given, send this first
                if initial_input:
                    time.sleep(0.2) # Wait a very short while for the channel to be initialized, before sending.
                    chan.send(initial_input)

                reading_from_stdin = True

                # Read/write loop
                while True:
                    # Don't wait for any input when an exit status code has been
                    # set already.
                    if chan.status_event.isSet():
                        break;

                    if reading_from_stdin:
                        r, w, e = select([pty.stdin, chan], [], [])
                    else:
                        r, w, e = select([chan], [], [])

                    # Receive stream
                    if chan in r:
                        try:
                            x = chan.recv(1024)

                            # Received length 0 -> end of stream
                            if len(x) == 0:
                                break

                            # Log received characters
                            log_entry.log_io(x)

                            # Write received characters to stdout and flush
                            while True:
                                try:
                                    pty.stdout.write(x)
                                    break
                                except IOError, e:
                                    # Sometimes, when we have a lot of output, we get here:
                                    # IOError: [Errno 11] Resource temporarily unavailable
                                    # Just waiting a little, and retrying seems to work.
                                    # See also: deployer.run.socket_client for a similar issue.
                                    time.sleep(0.2)

                            pty.stdout.flush()

                            # Also remember received output.
                            # We want to return what's written to stdout.
                            result.append(x)

                            # Do we need to send the sudo password? (It's when the
                            # magic prompt has been detected in the stream) Note
                            # that we only monitor the last part of 'result', it's
                            # a bit fuzzy, but works.
                            if not password_sent and self.magic_sudo_prompt in ''.join(result[-32:]):
                                chan.send(self.password)
                                chan.send('\n')
                                password_sent = True
                        except socket.timeout:
                            pass

                    # Send stream (one by one character)
                    if pty.stdin in r:
                        try:
                            x = pty.stdin.read(1024)

                            # We receive \n from stdin, but \r is required to
                            # send. (Until now, the only place where the
                            # difference became clear is in redis-cli, which
                            # only accepts \r as confirmation.)
                            x = x.replace('\n', '\r')
                        except IOError, e:
                            # What to do with IOError exceptions?
                            # (we see what happens in the next select-call.)
                            continue

                        # Received length 0
                        # There's no more at stdin to read.
                        if len(x) == 0:
                            # However, we should go on processing the input
                            # from the remote end, until the process finishes
                            # there (because it was done or processed Ctrl-C or
                            # Ctrl-D/EOF.)
                            #
                            # The end of the input stream happens when we are
                            # using StringIO at the client side, and we're not
                            # attached to a real pseudo terminal. (For
                            # unit-testing, or background commands.)
                            reading_from_stdin = False
                            continue

                        # Write to channel
                        chan.send(x)

                        # Not sure about this. Sometimes, when pasting large data
                        # in the command line, the many concecutive read or write
                        # commands will make Paramiko hang somehow...  (This was
                        # the case, when still using a blocking pty.stdin.read(1)
                        # instead of a non-blocking readmany.
                        time.sleep(0.01)
Пример #11
0
    def input(self, label, is_password=False, answers=None, default=None):
        """
        Ask for plain text input. (Similar to raw_input.)

        :param is_password: Show stars instead of the actual user input.
        :type is_password: bool
        :param answers: A list of the accepted answers or None.
        :param default: Default answer.
        """
        def print_question():
            answers_str = (' [%s]' % (','.join(answers)) if answers else '')
            default_str = (' (default=%s)' % default if default is not None else '')
            sys.stdout.write(colored('  %s%s%s: ' % (label, answers_str, default_str), 'cyan'))
            sys.stdout.flush()

        def read_answer():
            value = ''
            print_question()

            while True:
                c = sys.stdin.read(1)

                # Enter pressed
                if c in ('\r', '\n') and (value or default):
                    sys.stdout.write('\r\n')
                    break

                # Backspace pressed
                elif c == '\x7f' and value:
                    sys.stdout.write('\b \b')
                    value = value[:-1]

                # Valid character
                elif ord(c) in range(32, 127):
                    sys.stdout.write(colored('*' if is_password else c, attrs=['bold']))
                    value += c

                elif c == '\x03': # Ctrl-C
                    raise NoInput

                sys.stdout.flush()

            # Return result
            if not value and default is not None:
                return default
            else:
                return value

        with std.raw_mode(sys.stdin):
            while True:
                if self._pty.interactive:
                    value = read_answer()
                elif default is not None:
                    print_question()
                    sys.stdout.write('[non interactive] %r\r\n' % default)
                    sys.stdout.flush()
                    value = default
                else:
                    # XXX: Asking for input in non-interactive session
                    value = read_answer()

                # Return if valid anwer
                if not answers or value in answers:
                    return value

                # Otherwise, ask again.
                else:
                    sys.stdout.write('Invalid answer.\r\n')
                    sys.stdout.flush()
Пример #12
0
    def __call__(self, context):
        # Real stdin/out of the current logging session
        stdin = context.cli.stdin
        stdout = context.cli.stdout

        # Use a pseudo terminal to pass the logging IO from other deployment
        # sessions to this one.
        master, slave = os.openpty()
        slave_stdout = os.fdopen(slave, 'w', 0)
        master_stdin = os.fdopen(master, 'r', 0)
        fdesc.setNonBlocking(master_stdin)

        def now():
            return datetime.datetime.now().strftime('%H:%I:%S')

        # Create monitor handler
        class M(Monitor):
            def __init__(self):
                self.line = 0  # Keep line numbers

                slave_stdout.write("Press 'q' or Ctrl-c to exit the monitor\n")
                slave_stdout.write(colored('%-12s' % 'Username', 'cyan'))
                slave_stdout.write(colored('%-9s' % 'Time', 'cyan'))
                slave_stdout.write(colored('%-17s' % 'Host', 'cyan'))
                slave_stdout.write(colored('%s' % 'Command', 'cyan'))
                slave_stdout.write('\n')
                slave_stdout.flush()

            def _print_username(self, username):
                slave_stdout.write(colored('%-12s' % username, 'magenta'))
                slave_stdout.write(colored('%-9s' % now()))

            def _print_user_and_event(self, username, event):
                self._print_username(username)
                slave_stdout.write(' ' * 17 + event)
                slave_stdout.flush()

            def _print_status(self, line, succeeded):
                slave_stdout.write('\0337')  # ESC 7: Save cursor position
                slave_stdout.write('\033[%iA' %
                                   (self.line - line))  # Move cursor up
                slave_stdout.write('\033[1000C')  # Move cursor to end of line
                slave_stdout.write('\033[8D')  # Move cursor 8 chars back

                if succeeded:
                    slave_stdout.write(colored('succeeded', 'green'))
                else:
                    slave_stdout.write(colored('failed', 'red'))

                slave_stdout.write('\0338')  # ESC 8: Restore cursor position

            def log_run(self, session, run):
                if not run.sandboxing:
                    line = self.line

                    self._print_username(session.username)
                    slave_stdout.write(colored('%-17s' % run.host.slug,
                                               'cyan'))
                    slave_stdout.write(
                        colored('%s' % (' (sudo) ' if run.use_sudo else ''),
                                'red'))
                    slave_stdout.write(colored(run.command, 'green'))

                    slave_stdout.write('\n')
                    slave_stdout.flush()
                    self.line += 1

                    # TODO: I guess these writes should be atomic (group in
                    # one statement), because they can come from different
                    # threads.

                    def callback():
                        # TODO: Make more generic. We don't want to write to
                        # this logger when it has been closed. It may be
                        # possible that the logger has been disconnected and
                        # removed from the globals loggers list, but the
                        # callback still exists.
                        if not slave_stdout.closed:
                            self._print_status(line, run.succeeded)

                    return callback
                else:
                    return lambda: None

            def log_file_opened(self, session, file_entry):
                if not file_entry.sandboxing:
                    line = self.line

                    self._print_username(session.username)
                    slave_stdout.write(
                        colored('%-17s' % file_entry.host.slug, 'cyan'))
                    slave_stdout.write(
                        colored(
                            '%s' % (' (sudo) ' if file_entry.use_sudo else ''),
                            'red'))

                    if file_entry.entry_type == Actions.Get:
                        slave_stdout.write('Downloading: ')
                        slave_stdout.write(
                            colored(file_entry.remote_path, 'green'))

                    elif file_entry.entry_type == Actions.Put:
                        slave_stdout.write('Uploading: ')
                        slave_stdout.write(
                            colored(file_entry.remote_path, 'green'))

                    elif file_entry.entry_type == Actions.Open:
                        slave_stdout.write('Opening file: ')
                        slave_stdout.write(
                            colored(file_entry.remote_path, 'green'))
                        slave_stdout.write(' mode=')
                        slave_stdout.write(colored(file_entry.mode, 'green'))

                    slave_stdout.write('\n')
                    slave_stdout.flush()
                    self.line += 1

                    def callback():
                        if not slave_stdout.closed:
                            self._print_status(line, file_entry.succeeded)

                    return callback
                else:
                    return lambda: None

            def action_started(self, session, action_entry):
                line = self.line
                if action_entry.sandboxing:
                    self._print_user_and_event(
                        session.username, 'Calling action (in sandbox): %s\n' %
                        colored(action_entry.command, 'green'))
                else:
                    self._print_user_and_event(
                        session.username, 'Calling action: %s\n' %
                        colored(action_entry.command, 'green'))

                class Callbacks(object):
                    def succeeded(s, result):
                        if not slave_stdout.closed:
                            self._print_status(line, True)

                    def failed(s, exception):
                        if not slave_stdout.closed:
                            self._print_status(line, False)

                self.line += 1
                return Callbacks()

            def session_created(self, session):
                self.line += 1
                self._print_user_and_event(
                    session.username, 'Started session host=%s id=%s\n' %
                    (session.host, session.id))

            def session_closed(self, session):
                self.line += 1
                self._print_user_and_event(
                    session.username, 'Closed session host=%s id=%s\n' %
                    (session.host, session.id))

        monitor = M()
        monitors.append(monitor)

        with std.raw_mode(stdin):
            while True:
                r, w, e = select.select([master_stdin, stdin], [], [])

                # Receive stream from monitor
                if master_stdin in r:
                    stdout.write(master_stdin.read(4096))

                if stdin in r:
                    char = stdin.read(1)

                    # Leave monitor when 'q' or Ctrl-C has been pressed.
                    if char in ('q', '\x03'):
                        # Cleanup
                        monitors.remove(monitor)
                        slave_stdout.close()
                        master_stdin.close()
                        return
Пример #13
0
    def input(self, label, is_password=False, answers=None, default=None):
        """
        Ask for plain text input. (Similar to raw_input.)

        :param is_password: Show stars instead of the actual user input.
        :type is_password: bool
        :param answers: A list of the accepted answers or None.
        :param default: Default answer.
        """
        stdin = self._pty.stdin
        stdout = self._pty.stdout

        def print_question():
            answers_str = (' [%s]' % (','.join(answers)) if answers else '')
            default_str = (' (default=%s)' % default if default is not None else '')
            stdout.write(colored('  %s%s%s: ' % (label, answers_str, default_str), 'cyan'))
            stdout.flush()

        def read_answer():
            value = ''
            print_question()

            while True:
                c = stdin.read(1)

                # Enter pressed
                if c in ('\r', '\n') and (value or default):
                    stdout.write('\r\n')
                    break

                # Backspace pressed
                elif c == '\x7f' and value:
                    stdout.write('\b \b')
                    value = value[:-1]

                # Valid character
                elif ord(c) in range(32, 127):
                    stdout.write(colored('*' if is_password else c, attrs=['bold']))
                    value += c

                elif c == '\x03': # Ctrl-C
                    raise NoInput

                stdout.flush()

            # Return result
            if not value and default is not None:
                return default
            else:
                return value

        with std.raw_mode(stdin):
            while True:
                if self._pty.interactive:
                    value = read_answer()
                elif default is not None:
                    print_question()
                    stdout.write('[non interactive] %r\r\n' % default)
                    stdout.flush()
                    value = default
                else:
                    # XXX: Asking for input in non-interactive session
                    value = read_answer()

                # Return if valid anwer
                if not answers or value in answers:
                    return value

                # Otherwise, ask again.
                else:
                    stdout.write('Invalid answer.\r\n')
                    stdout.flush()
Пример #14
0
    def _posix_shell(self, chan, raw=True, initial_input=None):
        """
        Create a loop which redirects sys.stdin/stdout into this channel.
        The loop ends when channel.recv() returns 0.

        Code inspired by the Paramiko interactive demo.
        """
        result = []
        password_sent = False

        # Set terminal in raw mode
        if raw:
            context = raw_mode(self.pty.stdin)
        else:
            context = contextlib.nested()

        assert self.pty.set_ssh_channel_size
        with context:
            # Make channel non blocking.
            chan.settimeout(0.0)

            # When initial input has been given, send this first
            if initial_input:
                time.sleep(
                    0.2
                )  # Wait a very short while for the channel to be initialized, before sending.
                chan.send(initial_input)

            reading_from_stdin = True

            # Read/write loop
            while True:
                # Don't wait for any input when an exit status code has been
                # set already. (But still wait for the output to finish.)
                if chan.status_event.isSet():
                    reading_from_stdin = False

                    # When the channel is closed, and there's nothing to read
                    # anymore. We can return what we got from Paramiko. (Not
                    # sure why this happens. Most of the time, select() still
                    # returns and chan.recv() returns an empty string, but when
                    # read_ready is False, select() doesn't return anymore.)
                    if chan.closed and not chan.in_buffer.read_ready():
                        break

                channels = [self.pty.stdin, chan
                            ] if reading_from_stdin else [chan]
                r, w, e = select(channels, [], [], 1)
                # Note the select-timeout. That is required in order to
                # check for the status_event every second.

                # Receive stream
                if chan in r:
                    try:
                        x = chan.recv(1024)

                        # Received length 0 -> end of stream
                        if len(x) == 0:
                            break

                        # Write received characters to stdout and flush
                        while True:
                            try:
                                self.pty.stdout.write(x)
                                break
                            except IOError as e:
                                # Sometimes, when we have a lot of output, we get here:
                                # IOError: [Errno 11] Resource temporarily unavailable
                                # Just waiting a little, and retrying seems to work.
                                # See also: deployer.run.socket_client for a similar issue.
                                time.sleep(0.2)

                        self.pty.stdout.flush()

                        # Also remember received output.
                        # We want to return what's written to stdout.
                        result.append(x)

                        # Do we need to send the sudo password? (It's when the
                        # magic prompt has been detected in the stream) Note
                        # that we only monitor the last part of 'result', it's
                        # a bit fuzzy, but works.
                        if not password_sent and self.magic_sudo_prompt in ''.join(
                                result[-32:]):
                            chan.send(self.password)
                            chan.send('\n')
                            password_sent = True
                    except socket.timeout:
                        pass

                # Send stream (one by one character)
                # (use 'elif', read stdin only when there is no more output to be received.)
                elif self.pty.stdin in r:
                    try:
                        # Make stdin non-blocking. (The select call already
                        # blocked for us, we want sys.stdin.read() to read
                        # as many bytes as possible without blocking.)
                        try:
                            fdesc.setNonBlocking(self.pty.stdin)
                            x = self.pty.stdin.read(1024)
                        finally:
                            # Set stdin blocking again
                            # (Writing works better in blocking mode.
                            # Especially OS X seems to be very sensitive if we
                            # write lange amounts [>1000 bytes] nonblocking to
                            # stdout. That causes a lot of IOErrors.)
                            fdesc.setBlocking(self.pty.stdin)

                        # We receive \n from stdin, but \r is required to
                        # send. (Until now, the only place where the
                        # difference became clear is in redis-cli, which
                        # only accepts \r as confirmation.)
                        x = x.replace('\n', '\r')
                    except IOError as e:
                        # What to do with IOError exceptions?
                        # (we see what happens in the next select-call.)
                        continue

                    # Received length 0
                    # There's no more at stdin to read.
                    if len(x) == 0:
                        # However, we should go on processing the input
                        # from the remote end, until the process finishes
                        # there (because it was done or processed Ctrl-C or
                        # Ctrl-D/EOF.)
                        #
                        # The end of the input stream happens when we are
                        # using StringIO at the client side, and we're not
                        # attached to a real pseudo terminal. (For
                        # unit-testing, or background commands.)
                        reading_from_stdin = False
                        continue

                    # Write to channel
                    chan.send(x)

                    # Not sure about this. Sometimes, when pasting large data
                    # in the command line, the many concecutive read or write
                    # commands will make Paramiko hang somehow...  (This was
                    # the case, when still using a blocking pty.stdin.read(1)
                    # instead of a non-blocking readmany.
                    time.sleep(0.01)

            return ''.join(result)