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
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)