Ejemplo n.º 1
0
def translate_exception(e, msg=None):
    """
    In many cases, we should be able to roughly infer the exception cause
    from the error message -- this is centrally done in this method.  If
    possible, it will return a new exception with a more concise error
    message and appropriate exception type.
    """

    if not issubclass(e.__class__, se.SagaException):
        # we do not touch non-saga exceptions
        return e

    if not issubclass(e.__class__, se.NoSuccess):
        # this seems to have a specific cause already, leave it alone
        return e

    cmsg = e._plain_message

    if msg:
        cmsg = "%s (%s)" % (cmsg, msg)

    lmsg = cmsg.lower()

    if 'could not resolve hostname' in lmsg:
        e = se.BadParameter(cmsg)

    elif 'connection timed out' in lmsg:
        e = se.BadParameter(cmsg)

    elif 'connection refused' in lmsg:
        e = se.BadParameter(cmsg)

    elif 'auth' in lmsg:
        e = se.AuthorizationFailed(cmsg)

    elif 'pass' in lmsg:
        e = se.AuthenticationFailed(cmsg)

    elif 'ssh_exchange_identification' in lmsg:
        e = se.AuthenticationFailed(
            "too frequent login attempts, or sshd misconfiguration: %s" % cmsg)

    elif 'denied' in lmsg:
        e = se.PermissionDenied(cmsg)

    elif 'shared connection' in lmsg:
        e = se.NoSuccess("Insufficient system resources: %s" % cmsg)

    elif 'pty allocation' in lmsg:
        e = se.NoSuccess("Insufficient system resources: %s" % cmsg)

    elif 'Connection to master closed' in lmsg:
        e = se.NoSuccess(
            "Connection failed (insufficient system resources?): %s" % cmsg)

    return e
Ejemplo n.º 2
0
    def _initialize_pty(self, pty_shell, info, posix=None):

        # posix: only for posix shells we use prompt triggers.  sftp for example
        # does not deal well with triggers (no printf).

        with self.rlock:

            # import pprint
            # pprint.pprint (info)

            shell_pass = info['pass']
            key_pass = info['key_pass']
            prompt = info['prompt']
            logger = info['logger']
            latency = info['latency']
            timeout = info['ssh_timeout']

            pty_shell.latency = latency

            if posix == None:
                posix = info['posix']

            # if we did not see a decent prompt within 'delay' time, something
            # went wrong.  Try to prompt a prompt (duh!)  Delay should be
            # minimum 0.1 second (to avoid flooding of local shells), and at
            # maximum 1 second (to keep startup time reasonable)
            # most one second.  We try to get within that range with 10*latency.
            delay = min(1.0, max(0.1, 10 * latency))

            try:
                prompt_patterns = [
                    "[Pp]assword:\s*$",  # password   prompt
                    "Enter passphrase for .*:\s*$",  # passphrase prompt
                    "Token_Response.*:\s*$",  # passtoken  prompt
                    "Enter PASSCODE:$",  # RSA SecureID
                    "want to continue connecting",  # hostkey confirmation
                    ".*HELLO_\\d+_SAGA$",  # prompt detection helper
                    prompt
                ]  # greedy native shell prompt

                # use a very aggressive, but portable prompt setting scheme.
                # Error messages may appear for tcsh and others.  Excuse
                # non-posix shells
                if posix:
                    pty_shell.write(
                        " export PROMPT_COMMAND='' PS1='$' ; set prompt='$'\n")

                # find a prompt
                n, match = pty_shell.find(prompt_patterns, delay)

                # this loop will run until we finally find the shell prompt, or
                # if we think we have tried enough and give up.  On success
                # we'll try to set a different prompt, and when we found that,
                # too, we exit the loop and are be ready to running shell

                # commands.
                retries = 0
                retry_trigger = True
                used_trigger = False
                found_trigger = ""
                time_start = time.time()

                while True:

                    # --------------------------------------------------------------
                    if n == None:

                        # we found none of the prompts, yet, and need to try
                        # again.  But to avoid hanging on invalid prompts, we
                        # print 'HELLO_x_SAGA', and search for that one, too.
                        # We actually do 'printf HELLO_%d_SAGA x' so that the
                        # pattern only appears in the result, not in the
                        # command...

                        if time.time() - time_start > timeout:
                            raise se.NoSuccess(
                                "Could not detect shell prompt (timeout)")

                        # make sure we retry a finite time...
                        retries += 1

                        if not retry_trigger:
                            # just waiting for the *right* trigger or prompt,
                            # don't need new ones...
                            continue

                        if posix:
                            # use a very aggressive, but portable prompt setting scheme
                            pty_shell.write(
                                " export PROMPT_COMMAND='' PS1='$' > /dev/null 2>&1 || set prompt='$'\n"
                            )
                            pty_shell.write(
                                " printf 'HELLO_%%d_SAGA\\n' %d\n" % retries)
                            used_trigger = True

                        # FIXME:  consider better timeout
                        n, match = pty_shell.find(prompt_patterns, delay)

                    # --------------------------------------------------------------
                    elif n == 0:
                        logger.info("got password prompt")
                        if not shell_pass:
                            raise se.AuthenticationFailed ("prompted for unknown password (%s)" \
                                                          % match)

                        pty_shell.write("%s\n" % shell_pass, nolog=True)
                        n, match = pty_shell.find(prompt_patterns, delay)

                    # --------------------------------------------------------------
                    elif n == 1:
                        logger.info("got passphrase prompt : %s" % match)

                        start = string.find(match, "'", 0)
                        end = string.find(match, "'", start + 1)

                        if start == -1 or end == -1:
                            raise se.AuthenticationFailed(
                                "could not extract key name (%s)" % match)

                        key = match[start + 1:end]

                        if not key in key_pass:
                            raise se.AuthenticationFailed ("prompted for unknown key password (%s)" \
                                                          % key)

                        pty_shell.write("%s\n" % key_pass[key], nolog=True)
                        n, match = pty_shell.find(prompt_patterns, delay)

                    # --------------------------------------------------------------
                    elif n == 2 or n == 3:
                        logger.info("got token prompt")
                        import getpass
                        token = getpass.getpass("enter token: ")
                        pty_shell.write("%s\n" % token.strip(), nolog=True)
                        n, match = pty_shell.find(prompt_patterns, delay)

                    # --------------------------------------------------------------
                    elif n == 4:
                        logger.info("got hostkey prompt")
                        pty_shell.write("yes\n")
                        n, match = pty_shell.find(prompt_patterns, delay)

                    # --------------------------------------------------------------
                    elif n == 5:

                        # one of the trigger commands got through -- we can now
                        # hope to find the prompt (or the next trigger...)
                        logger.debug("got shell prompt trigger (%s) (%s)" %
                                     (n, match))

                        found_trigger = match
                        retry_trigger = False
                        n, match = pty_shell.find(prompt_patterns, delay)
                        continue

                    # --------------------------------------------------------------
                    elif n == 6:

                        logger.debug("got initial shell prompt (%s) (%s)" %
                                     (n, match))

                        if retries:
                            if used_trigger:
                                # we already sent triggers -- so this match is only
                                # useful if saw the *correct* shell prompt trigger
                                # first
                                trigger = "HELLO_%d_SAGA" % retries

                                if not trigger in found_trigger:

                                    logger.debug ("waiting for prompt trigger %s: (%s) (%s)" \
                                               % (trigger, n, match))
                                    # but more retries won't help...
                                    retry_trigger = False
                                    attempts = 0
                                    n = None

                                    while not n:

                                        attempts += 1
                                        n, match = pty_shell.find(
                                            prompt_patterns, delay)

                                        if not n:
                                            if attempts == 1:
                                                if posix:
                                                    pty_shell.write(
                                                        " printf 'HELLO_%%d_SAGA\\n' %d\n"
                                                        % retries)

                                            if attempts > 100:
                                                raise se.NoSuccess(
                                                    "Could not detect shell prompt (timeout)"
                                                )

                                    continue

                        logger.debug("Got initial shell prompt (%s) (%s)" %
                                     (n, match))
                        # we are done waiting for a prompt
                        break

            except Exception as e:
                logger.exception(e)
                raise ptye.translate_exception(e)