From 089f43a1ebe32bbd7e71b58f1af7076b3324e725 Mon Sep 17 00:00:00 2001 From: Santhosh Edukulla Date: Wed, 6 Nov 2013 22:41:38 +0530 Subject: [PATCH] Added fix for bug 5056. The fix contains fixes related to ssh library majorly. Complete description should be available under bug description. Signed-off-by: Santhosh Edukulla Signed-off-by: SrikanteswaraRao Talluri --- tools/marvin/marvin/codes.py | 2 + tools/marvin/marvin/integration/lib/utils.py | 17 ++- tools/marvin/marvin/remoteSSHClient.py | 150 ++++++++++++++----- 3 files changed, 127 insertions(+), 42 deletions(-) diff --git a/tools/marvin/marvin/codes.py b/tools/marvin/marvin/codes.py index b6580d0d199..f409c7c8d13 100644 --- a/tools/marvin/marvin/codes.py +++ b/tools/marvin/marvin/codes.py @@ -40,3 +40,5 @@ EMPTY_LIST = "EMPTY_LIST" FAIL = 0 PASS = 1 MATCH_NOT_FOUND = "ELEMENT NOT FOUND IN THE INPUT" +SUCCESS = "SUCCESS" +EXCEPTION_OCCURRED = "Exception Occurred" diff --git a/tools/marvin/marvin/integration/lib/utils.py b/tools/marvin/marvin/integration/lib/utils.py index 4d048f0f4e9..f5d7c5bdf83 100644 --- a/tools/marvin/marvin/integration/lib/utils.py +++ b/tools/marvin/marvin/integration/lib/utils.py @@ -113,8 +113,16 @@ def cleanup_resources(api_client, resources): obj.delete(api_client) -def is_server_ssh_ready(ipaddress, port, username, password, retries=10, timeout=30, keyPairFileLocation=None): - """Return ssh handle else wait till sshd is running""" +def is_server_ssh_ready(ipaddress, port, username, password, retries=10, retryinterv=30, timeout=3.0, keyPairFileLocation=None): + ''' + @Name: is_server_ssh_ready + @Input: timeout: tcp connection timeout flag, + others information need to be added + @Output:object for remoteSSHClient + Name of the function is little misnomer and is not + verifying anything as such mentioned + ''' + try: ssh = remoteSSHClient( host=ipaddress, @@ -123,9 +131,10 @@ def is_server_ssh_ready(ipaddress, port, username, password, retries=10, timeout passwd=password, keyPairFileLocation=keyPairFileLocation, retries=retries, - delay=timeout) + delay=retryinterv, + timeout=timeout) except Exception, e: - raise Exception("Failed to bring up ssh service in time. Waited %ss. Error is %s" % (retries * timeout, e)) + raise Exception("SSH connection has Failed. Waited %ss. Error is %s" % (retries * retryinterv, e)) else: return ssh diff --git a/tools/marvin/marvin/remoteSSHClient.py b/tools/marvin/marvin/remoteSSHClient.py index fea9b125d19..19df16e507f 100644 --- a/tools/marvin/marvin/remoteSSHClient.py +++ b/tools/marvin/marvin/remoteSSHClient.py @@ -20,57 +20,48 @@ import time import cloudstackException import contextlib import logging +from marvin.codes import ( + SUCCESS, FAIL, INVALID_INPUT, EXCEPTION_OCCURRED + ) from contextlib import closing class remoteSSHClient(object): + ''' + Added timeout flag for ssh connect calls.Default to 3.0 seconds + ''' def __init__(self, host, port, user, passwd, retries=10, delay=30, - log_lvl=logging.INFO, keyPairFileLocation=None): - self.host = host - self.port = port + log_lvl=logging.INFO, keyPairFiles=None, timeout=3.0): + self.host = None + self.port = 22 self.user = user self.passwd = passwd - self.keyPairFile = keyPairFileLocation + self.keyPairFiles = keyPairFiles self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.logger = logging.getLogger('sshClient') + self.retryCnt = 0 + self.delay = 0 + self.timeout = 3.0 ch = logging.StreamHandler() ch.setLevel(log_lvl) self.logger.addHandler(ch) - retry_count = retries - while retry_count >= 0: - try: - if keyPairFileLocation is None: - self.ssh.connect(str(host), int(port), user, passwd) - self.logger.debug("SSH connect: %s@%s with passwd %s" % - (user, str(host), passwd)) - else: - self.ssh.connect(hostname=str(host), - port=int(port), - username=str(user), - key_filename=str(keyPairFileLocation), - look_for_keys=False - ) - self.logger.debug( - "connecting to server %s with user %s key %s" % - (str(host), user, keyPairFileLocation)) - self.logger.debug("SSH connect: %s@%s with passwd %s" % - (user, str(host), passwd)) - #except paramiko.AuthenticationException, authEx: - # raise cloudstackException. \ - # InvalidParameterException("Invalid credentials to " - # + "login to %s on port %s" % - # (str(host), port)) - except Exception as se: - if retry_count == 0: - raise cloudstackException. \ - InvalidParameterException(repr(se)) - else: - return - - retry_count = retry_count - 1 - time.sleep(delay) + #Check invalid host value and raise exception + #Atleast host is required for connection + if host is not None and host != '': + self.host = host + if retries is not None and retries > 0: + self.retryCnt = retries + if delay is not None and delay > 0: + self.delay = delay + if timeout is not None and timeout > 0: + self.timeout = timeout + if port is not None or port >= 0: + self.port = port + if self.createConnection() == FAIL: + raise cloudstackException.\ + internalError("Connection Failed") def execute(self, command): stdin, stdout, stderr = self.ssh.exec_command(command) @@ -89,6 +80,88 @@ class remoteSSHClient(object): (command, str(self.host), results)) return results + def createConnection(self): + ''' + @Name: createConnection + @Desc: Creates an ssh connection for + retries mentioned,along with sleep mentioned + @Output: SUCCESS on successful connection + FAIL If connection through ssh failed + ''' + ret = FAIL + while self.retryCnt >= 0: + try: + self.logger.debug("SSH Connection: Host:%s User:%s\ + Port:%s KeyPairFile: %s" % + (self.host, self.user, str(self.port), + str(self.keyPairFiles))) + if self.keyPairFiles is None: + self.ssh.connect(hostname=self.host, + port=self.port, + username=self.user, + password=self.passwd, + timeout=self.timeout) + else: + self.ssh.connect(hostname=self.host, + port=self.port, + username=self.user, + password=self.passwd, + key_filename=self.keyPairFiles, + timeout=self.timeout, + look_for_keys=False + ) + ret = SUCCESS + break + except Exception as se: + self.retryCnt = self.retryCnt - 1 + if self.retryCnt == 0: + break + time.sleep(self.delay) + return ret + + def runCommand(self, command): + ''' + @Name: runCommand + @Desc: Runs a command over ssh and + returns the result along with status code + @Input: command to execute + @Output: 1: status of command executed. + Default to None + SUCCESS : If command execution is successful + FAIL : If command execution has failed + EXCEPTION_OCCURRED: Exception occurred while executing + command + INVALID_INPUT : If invalid value for command is passed + 2: stdin,stdout,stderr values of command output + ''' + excep_msg = '' + ret = {"status": None, "stdin": None, "stdout": None, "stderr": None} + if command is None or command == '': + ret["status"] = INVALID_INPUT + return ret + try: + status_check = 1 + stdin, stdout, stderr = self.ssh.exec_command(command) + output = stdout.readlines() + errors = stderr.readlines() + inp = stdin.readlines() + ret["stdin"] = inp + ret["stdout"] = output + ret["stderr"] = errors + if stdout is not None: + status_check = stdout.channel.recv_exit_status() + if status_check == 0: + ret["status"] = SUCCESS + else: + ret["status"] = FAIL + except Exception as e: + excep_msg = str(e) + ret["status"] = EXCEPTION_OCCURRED + finally: + self.logger.debug(" Host: %s Cmd: %s Output:%s Exception: %s" % + (self.host, command, str(ret), excep_msg)) + return ret + def scp(self, srcFile, destPath): transport = paramiko.Transport((self.host, int(self.port))) transport.connect(username=self.user, password=self.passwd) @@ -99,7 +172,8 @@ class remoteSSHClient(object): raise e def close(self): - self.ssh.close() + if self.ssh is not None: + self.ssh.close() if __name__ == "__main__":