# -*- coding:utf-8 -*- # Copyright (c) 2020 Huawei Technologies Co.,Ltd. # # openGauss is licensed under Mulan PSL v2. # You can use this software according to the terms # and conditions of the Mulan PSL v2. # You may obtain a copy of Mulan PSL v2 at: # # http://license.coscl.org.cn/MulanPSL2 # # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, # WITHOUT WARRANTIES OF ANY KIND, # EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, # MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. # See the Mulan PSL v2 for more details. # ---------------------------------------------------------------------------- import os import sys import subprocess import grp import pwd import getpass sys.path.append(sys.path[0] + "/../") from gspylib.threads.parallelTool import parallelTool from gspylib.common.DbClusterInfo import initParserXMLFile, \ readOneClusterConfigItem from gspylib.common.Common import DefaultValue, ClusterCommand from gspylib.common.OMCommand import OMCommand from gspylib.common.ErrorCode import ErrorCode from gspylib.os.gsfile import g_file from gspylib.os.gsfile import g_Platform from gspylib.common.VersionInfo import VersionInfo import impl.upgrade.UpgradeConst as Const sys.path.append(sys.path[0] + "/../../../lib/") DefaultValue.doConfigForParamiko() import paramiko ############################################################################# # Global variables ############################################################################# gphome = None # system config file PROFILE_FILE = '/etc/profile' # pssh directory name PSSHDIR = 'pssh-2.3.1' # action name ACTION_CLEAN_TOOL_ENV = "clean_tool_env" ACTION_CHECK_UNPREINSTALL = "check_unpreinstall" ACTION_CLEAN_GAUSS_ENV = "clean_gauss_env" ACTION_DELETE_GROUP = "delete_group" ACTION_CLEAN_SYSLOG_CONFIG = 'clean_syslog_config' ACTION_CLEAN_DEPENDENCY = "clean_dependency" class PostUninstallImpl: """ init the command options input : NA output: NA """ def __init__(self, GaussPost): """ function: constructor """ pass def checkLogFilePath(self): """ function: Check log file path input : NA output: NA """ clusterPath = [] try: self.logger.log("Check log file path.", "addStep") # get tool path clusterPath.append(DefaultValue.getClusterToolPath()) # get tmp path tmpDir = DefaultValue.getTmpDir(self.user, self.xmlFile) clusterPath.append(tmpDir) # get cluster path hostName = DefaultValue.GetHostIpOrName() dirs = self.clusterInfo.getClusterDirectorys(hostName, False) for checkdir in dirs.values(): clusterPath.extend(checkdir) self.logger.debug("Cluster paths %s." % clusterPath) # check directory g_file.checkIsInDirectory(self.logFile, clusterPath) self.logger.log("Successfully checked log file path.", "constant") except Exception as e: self.logger.logExit(str(e)) ########################################################################## # Uninstall functions ########################################################################## def doCleanEnvironment(self): """ function: Clean Environment input : NA output: NA """ self.logger.debug("Do clean Environment.", "addStep") try: # check uninstall self.checkUnPreInstall() # clean app/log/data/temp dirs self.cleanDirectory() # clean other user self.cleanRemoteOsUser() # clean other nodes environment software and variable self.cleanOtherNodesEnvSoftware() # clean other nodes log self.cleanOtherNodesLog() # clean local node environment software and variable self.cleanLocalNodeEnvSoftware() # clean local user self.cleanLocalOsUser() except Exception as e: self.logger.logExit(str(e)) self.logger.debug("Do clean Environment succeeded.", "constant") def checkUnPreInstall(self): """ function: check whether do uninstall before unpreinstall input : NA output: NA """ self.logger.log("Checking unpreinstallation.") if not self.localMode: DefaultValue.checkAllNodesMpprcFile( self.clusterInfo.getClusterNodeNames(), self.clusterInfo.appPath, self.mpprcFile) cmd = "%s -t %s -u %s -l '%s' -X '%s'" % ( OMCommand.getLocalScript("Local_UnPreInstall"), ACTION_CHECK_UNPREINSTALL, self.user, self.localLog, self.xmlFile) self.logger.debug("Command for checking unpreinstall: %s" % cmd) # check if do postuninstall in all nodes DefaultValue.execCommandWithMode(cmd, "check unpreinstall", self.sshTool, self.localMode, self.mpprcFile) self.logger.log("Successfully checked unpreinstallation.") def cleanDirectory(self): """ function: clean install/instance/temp dirs input : NA output: NA """ # clean instance path hostName = DefaultValue.GetHostIpOrName() dbNodeInfo = self.clusterInfo.getDbNodeByName(hostName) instanceDirs = [] # get DB instance for dbInst in dbNodeInfo.datanodes: instanceDirs.append(dbInst.datadir) if (len(dbInst.ssdDir) != 0): instanceDirs.append(dbInst.ssdDir) # clean all instances if (len(instanceDirs) > 0): if (os.path.exists(instanceDirs[0]) and len( os.listdir(instanceDirs[0])) == 0): self.CleanInstanceDir() else: self.logger.debug( "Instance directory [%s] is not empty. " "Skip to delete instance's directory." % instanceDirs[0]) else: self.logger.debug( "Instance's directory is not been found. " "Skip to delete instance's directory.") # clean install path if (os.path.exists(self.clusterInfo.appPath)): self.logger.log("Deleting the installation directory.") cmd = "rm -rf '%s'" % self.clusterInfo.appPath self.logger.debug( "Command for deleting the installation path: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete install path", self.sshTool, self.localMode, self.mpprcFile) self.logger.log("Successfully deleted the installation directory.") # clean tmp dir self.logger.log("Deleting the temporary directory.") tmpDir = DefaultValue.getTmpDir(self.user, self.xmlFile) cmd = "rm -rf '%s'; rm -rf /tmp/gs_checkos; rm -rf /tmp/gs_virtualip" \ % tmpDir self.logger.debug( "Command for deleting the temporary directory: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete the temporary directory", self.sshTool, self.localMode, self.mpprcFile) self.logger.log("Successfully deleted the temporary directory.") def CleanInstanceDir(self): """ function: Clean instance directory input : NA output: NA """ self.logger.log("Deleting the instance's directory.") cmd = "%s -U %s -l '%s' -X '%s'" % ( OMCommand.getLocalScript("Local_Clean_Instance"), self.user, self.localLog, self.xmlFile) self.logger.debug("Command for deleting the instance: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete the instances data", self.sshTool, self.localMode, self.mpprcFile) # clean upgrade temp backup path cmd = "rm -rf '%s'" % DefaultValue.getBackupDir("upgrade") self.logger.debug( "Command for deleting the upgrade temp backup path: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete backup directory for upgrade", self.sshTool, self.localMode, self.mpprcFile) self.logger.log("Successfully deleted the instance's directory.") def cleanRemoteOsUser(self): """ function: Clean remote os user input : NA output: NA """ # check if local mode if (self.localMode): return if (not self.deleteUser): # clean static config file cmd = "rm -rf '%s'" % self.clusterInfo.appPath DefaultValue.execCommandWithMode(cmd, "delete install directory", self.sshTool, self.localMode, self.mpprcFile) return group = grp.getgrgid(pwd.getpwnam(self.user).pw_gid).gr_name # get other nodes hostName = DefaultValue.GetHostIpOrName() otherNodes = self.clusterInfo.getClusterNodeNames() for otherNode in otherNodes: if (otherNode == hostName): otherNodes.remove(otherNode) # clean remote user self.logger.log("Deleting remote OS user.") cmd = "%s -U %s -l %s" % ( OMCommand.getLocalScript("Local_Clean_OsUser"), self.user, self.localLog) self.logger.debug("Command for deleting remote OS user: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete OS user", self.sshTool, self.localMode, self.mpprcFile, otherNodes) self.logger.log("Successfully deleted remote OS user.") if (self.deleteGroup): # clean remote group self.logger.debug("Deleting remote OS group.") cmd = "%s -t %s -u %s -l '%s' -X '%s'" % ( OMCommand.getLocalScript("Local_UnPreInstall"), ACTION_DELETE_GROUP, group, self.localLog, self.xmlFile) self.logger.debug("Command for deleting remote OS group: %s" % cmd) status = self.sshTool.getSshStatusOutput(cmd, otherNodes, self.mpprcFile)[0] outputMap = self.sshTool.parseSshOutput(otherNodes) for node in status.keys(): if (status[node] != DefaultValue.SUCCESS): self.logger.log((outputMap[node]).strip("\n")) self.logger.debug("Deleting remote group is completed.") def cleanOtherNodesEnvSoftware(self): """ function: clean other nodes environment software and variable input : NA output: NA """ # check if local mode if self.localMode: return self.logger.log( "Deleting software packages " "and environmental variables of other nodes.") try: # get other nodes hostName = DefaultValue.GetHostIpOrName() otherNodes = self.clusterInfo.getClusterNodeNames() for otherNode in otherNodes: if (otherNode == hostName): otherNodes.remove(otherNode) self.logger.debug( "Deleting environmental variables of nodes: %s." % otherNodes) # clean $GAUSS_ENV if (not self.deleteUser): cmd = "%s -t %s -u %s -l '%s' -X '%s'" % ( OMCommand.getLocalScript("Local_UnPreInstall"), ACTION_CLEAN_GAUSS_ENV, self.user, self.localLog, self.xmlFile) self.logger.debug("Command for deleting $GAUSS_ENV: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete $GAUSS_ENV", self.sshTool, self.localMode, self.mpprcFile, otherNodes) cmd = "%s -t %s -u %s -l '%s' -X '%s'" % ( OMCommand.getLocalScript("Local_UnPreInstall"), ACTION_CLEAN_TOOL_ENV, self.user, self.localLog, self.xmlFile) self.logger.debug( "Command for deleting environmental variables: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete environment variables", self.sshTool, self.localMode, self.mpprcFile, otherNodes) except Exception as e: self.logger.logExit(str(e)) self.logger.log( "Successfully deleted software packages " "and environmental variables of other nodes.") def cleanOtherNodesLog(self): """ function: clean other nodes log input : NA output: NA """ # check if local mode if self.localMode: return self.logger.log("Deleting logs of other nodes.") try: # get other nodes hostName = DefaultValue.GetHostIpOrName() otherNodes = self.clusterInfo.getClusterNodeNames() for otherNode in otherNodes: if (otherNode == hostName): otherNodes.remove(otherNode) # clean log cmd = "rm -rf '%s/%s'; rm -rf /tmp/gauss_*;" % ( self.clusterInfo.logPath, self.user) cmd += "rm -rf '%s/Python-2.7.9'" \ % DefaultValue.getClusterToolPath() self.logger.debug( "Command for deleting logs of other nodes: %s" % cmd) DefaultValue.execCommandWithMode(cmd, "delete user log directory", self.sshTool, self.localMode, self.mpprcFile, otherNodes) self.logger.debug( "Successfully deleted logs of the nodes: %s." % otherNodes) except Exception as e: self.logger.logExit( ErrorCode.GAUSS_502["GAUSS_50207"] % "other nodes log" + " Error: \n%s." % str(e)) self.logger.log("Successfully deleted logs of other nodes.") def cleanLocalNodeEnvSoftware(self): """ function: clean local node environment software and variable input : NA output: NA in this function, Gauss-MPPDB* & sctp_patch is came from R5 upgrade R7 """ self.logger.log( "Deleting software packages " "and environmental variables of the local node.") try: self.clusterToolPath = DefaultValue.getClusterToolPath() # clean local node environment software path = "%s/%s" % (self.clusterToolPath, PSSHDIR) g_file.removeDirectory(path) path = "%s/upgrade.sh" % self.clusterToolPath g_file.removeFile(path) path = "%s/version.cfg" % self.clusterToolPath g_file.removeFile(path) path = "%s/GaussDB.py" % self.clusterToolPath g_file.removeFile(path) path = "%s/libcgroup" % self.clusterToolPath g_file.removeDirectory(path) path = "%s/unixodbc" % self.clusterToolPath g_file.removeDirectory(path) path = "%s/server.key.cipher" % self.clusterToolPath g_file.removeFile(path) path = "%s/server.key.rand" % self.clusterToolPath g_file.removeFile(path) path = "%s/%s*" % (self.clusterToolPath, VersionInfo.PRODUCT_NAME) g_file.removeDirectory(path) path = "%s/server.key.rand" % self.clusterToolPath g_file.removeFile(path) path = "%s/Gauss*" % (self.clusterToolPath) g_file.removeDirectory(path) path = "%s/sctp_patch" % (self.clusterToolPath) g_file.removeDirectory(path) path = "%s/%s" % (Const.UPGRADE_SQL_FILE, self.clusterToolPath) g_file.removeFile(path) path = "%s/%s" % (Const.UPGRADE_SQL_SHA, self.clusterToolPath) g_file.removeFile(path) self.logger.debug( "Deleting environmental software of local nodes.") # clean local node environment variable cmd = "(if [ -s '%s' ]; then " % PROFILE_FILE cmd += "sed -i -e '/^export GPHOME=%s$/d' %s " % ( self.clusterToolPath.replace('/', '\/'), PROFILE_FILE) cmd += \ "-e '/^export PATH=\$GPHOME\/pssh-2.3.1\/bin:" \ "\$GPHOME\/script:\$PATH$/d' %s " % PROFILE_FILE cmd += \ "-e '/^export PATH=\$GPHOME\/script\/gspylib\/pssh\/bin:" \ "\$GPHOME\/script:\$PATH$/d' %s " % PROFILE_FILE cmd += \ "-e '/^export LD_LIBRARY_PATH=\$GPHOME\/script" \ "\/gspylib\/clib:\$LD_LIBRARY_PATH$/d' %s " % PROFILE_FILE cmd += \ "-e '/^export LD_LIBRARY_PATH=\$GPHOME\/lib:" \ "\$LD_LIBRARY_PATH$/d' %s " % PROFILE_FILE cmd += \ "-e '/^export PATH=\/root\/gauss_om\/%s\/script:" \ "\$PATH$/d' %s " % (self.user, PROFILE_FILE) cmd += \ "-e '/^export PYTHONPATH=\$GPHOME\/lib$/d' %s; fi) " \ % PROFILE_FILE self.logger.debug( "Command for deleting environment variable: %s" % cmd) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): self.logger.logExit( ErrorCode.GAUSS_502["GAUSS_50207"] % "environment variables of the local node" + " Error: \n%s" % output) # check if user profile exist userProfile = "" if (self.mpprcFile is not None and self.mpprcFile != ""): userProfile = self.mpprcFile else: userProfile = "/home/%s/.bashrc" % self.user if (not os.path.exists(userProfile)): self.logger.debug( "The %s does not exist. " "Please skip to clean $GAUSS_ENV." % userProfile) return # clean user's environmental variable DefaultValue.cleanUserEnvVariable(userProfile, cleanGAUSS_WARNING_TYPE=True) # clean $GAUSS_ENV if (not self.deleteUser): envContent = "^\\s*export\\s*GAUSS_ENV=.*$" g_file.deleteLine(userProfile, envContent) self.logger.debug("Command for deleting $GAUSS_ENV: %s" % cmd, "constant") except Exception as e: self.logger.logExit(str(e)) self.logger.log( "Successfully deleted software packages " "and environmental variables of the local nodes.") def cleanLocalOsUser(self): """ function: Clean local os user input : NA output: NA """ if (not self.deleteUser): if (self.localMode): cmd = "rm -rf '%s'" % self.clusterInfo.appPath DefaultValue.execCommandWithMode(cmd, "delete install directory", self.sshTool, self.localMode, self.mpprcFile) return group = grp.getgrgid(pwd.getpwnam(self.user).pw_gid).gr_name # clean local user self.logger.log("Deleting local OS user.") cmd = "%s -U %s -l %s" % ( OMCommand.getLocalScript("Local_Clean_OsUser"), self.user, self.localLog) self.logger.debug("Command for deleting local OS user: %s" % cmd) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): self.logger.logExit(output) self.logger.log("Successfully deleted local OS user.") if (self.deleteGroup): # clean local user group self.logger.debug("Deleting local OS group.") cmd = "%s -t %s -u %s -l '%s' -X '%s'" % ( OMCommand.getLocalScript("Local_UnPreInstall"), ACTION_DELETE_GROUP, group, self.localLog, self.xmlFile) self.logger.debug("Command for deleting local OS group: %s" % cmd) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): self.logger.log(output.strip()) self.logger.debug("Deleting local group is completed.") def cleanLocalLog(self): """ function: Clean default log input : NA output: NA """ self.logger.log("Deleting local node's logs.", "addStep") try: # clean log path = "%s/%s" % (self.clusterInfo.logPath, self.user) g_file.removeDirectory(path) except Exception as e: self.logger.logExit( ErrorCode.GAUSS_502["GAUSS_50207"] % "logs" + " Error: \n%s." % str(e)) self.logger.log("Successfully deleted local node's logs.", "constant") def cleanMpprcFile(self): """ function: clean mpprc file if we are using environment seperate version. input : NA output: NA """ self.logger.debug("Clean mpprc file.", "addStep") # check if mpprcfile is null if (self.mpprcFile != ""): baseCmd = "rm -rf '%s'" % self.mpprcFile # check if local mode if (self.localMode): (status, output) = subprocess.getstatusoutput(baseCmd) if (status != 0): self.logger.logExit( ErrorCode.GAUSS_502["GAUSS_50207"] % "MPPRC file" + " Command: %s. Error: \n%s" % (baseCmd, output)) else: dbNodeNames = self.clusterInfo.getClusterNodeNames() for dbNodeName in dbNodeNames: cmd = "pssh -s -H %s '%s'" % (dbNodeName, baseCmd) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): self.logger.logExit( ErrorCode.GAUSS_502["GAUSS_50207"] % "MPPRC file" + " Command: %s. Error: \n%s" % (cmd, output)) self.logger.debug("Successfully cleaned mpprc file.", "constant") def cleanScript(self): """ clean script directory """ self.logger.debug("Clean script path") cmd = "%s -t %s -u %s -Q %s" % ( OMCommand.getLocalScript("Local_UnPreInstall"), ACTION_CLEAN_DEPENDENCY, self.user, self.clusterToolPath) if self.deleteUser: cmd += " -P %s" % self.userHome DefaultValue.execCommandWithMode(cmd, "clean script", self.sshTool, self.localMode, self.mpprcFile) self.logger.debug("Clean script path successfully.") def cleanSyslogConfig(self): """ function: clean syslog config input : NA output: NA """ try: # only suse11/suse12 can support it distname = g_Platform.dist()[0] if (distname.upper() != "SUSE"): return # clean syslog-ng/rsyslog config cmd = "%s -t %s -u %s -l '%s' -X '%s'" % ( OMCommand.getLocalScript("Local_UnPreInstall"), ACTION_CLEAN_SYSLOG_CONFIG, self.user, self.localLog, self.xmlFile) self.logger.debug( "Command for clean syslog-ng/rsyslog config: %s" % cmd) DefaultValue.execCommandWithMode( cmd, "clean syslog-ng/rsyslog config", self.sshTool, self.localMode, self.mpprcFile, self.clusterInfo.getClusterNodeNames()) except Exception as e: self.logger.logExit(str(e)) def sshExecWithPwd(self, host): """ function: execute command with root password input : host output: NA """ cmd = "rm -rf %s/* && echo 'OKOKOK'" % gphome ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(host, 22, "root", self.sshpwd) stdin, stdout, stderr = ssh.exec_command(cmd) output = stdout.read() self.logger.debug("%s: %s" % (str(host), str(output))) if output.find('OKOKOK') < 0: errMsg = stderr.read() raise Exception( ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + "host: %s. Error:\n%s" % (host, output)) def verifyCleanGphome(self, localMode=True): """ function: verify clean gphome and get root password input : localMode output: str """ sshpwd = "" flag = input( "Are you sure you want to clean gphome[%s] (yes/no)? " % gphome) while (True): if ( flag.upper() != "YES" and flag.upper() != "NO" and flag.upper() != "Y" and flag.upper() != "N"): flag = input("Please type 'yes' or 'no': ") continue break if (flag.upper() == "NO" or flag.upper() == "N"): sys.exit(0) if "HOST_IP" in os.environ.keys() and not localMode: sshpwd = getpass.getpass("Please enter password for root:") sshpwd_check = getpass.getpass("Please repeat password for root:") if sshpwd_check != sshpwd: sshpwd_check = "" sshpwd = "" raise Exception(ErrorCode.GAUSS_503["GAUSS_50306"] % "root") sshpwd_check = "" return sshpwd def checkAuthentication(self, hostname): """ function: Ensure the proper password-less access to the remote host. input : hostname output: True/False, hostname """ cmd = 'ssh -n %s %s true' % (DefaultValue.SSH_OPTION, hostname) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): self.logger.debug("The cmd is %s " % cmd) self.logger.debug( "Failed to check authentication. Hostname:%s. Error: \n%s" % ( hostname, output)) return (False, hostname) return (True, hostname) def getItemValueFromXml(self, itemName): """ function: Get item from xml tag CLUSTER. input : hostname output: True/False, hostname """ (retStatus, retValue) = readOneClusterConfigItem( initParserXMLFile(self.xmlFile), itemName, "cluster") if (retStatus != 0): raise Exception( ErrorCode.GAUSS_502["GAUSS_50204"] % itemName + " Error: \n%s" % retValue) return retValue def cleanGphomeScript(self): """ function: clean gphome script input : NA output: NA """ try: if not self.clean_gphome: return global gphome gphome = os.path.normpath( self.getItemValueFromXml("gaussdbToolPath")) cmd = "rm -rf %s/*" % gphome if "HOST_IP" in os.environ.keys(): # Agent Mode if self.localMode: # clean gphome in local mode self.verifyCleanGphome() (status, output) = subprocess.getstatusoutput(cmd) if status != 0: raise Exception( ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error:\n%s" % output) self.logger.logExit("Successfully clean gphome locally.") else: # clean gphome with specified node self.sshpwd = self.verifyCleanGphome(self.localMode) parallelTool.parallelExecute(self.sshExecWithPwd, self.clean_host) self.logger.logExit( "Successfully clean gphome on node %s." % self.clean_host) else: # SSH Mode SSH_TRUST = True self.nodeList = self.getItemValueFromXml("nodeNames").split( ",") if len(self.nodeList) == 0: raise Exception( ErrorCode.GAUSS_502["GAUSS_50203"] % "nodeList") results = parallelTool.parallelExecute( self.checkAuthentication, self.nodeList) for (key, value) in results: if (not key): self.logger.log("SSH trust has not been created. \ \nFor node : %s. Only clean local node." % value, "constant") SSH_TRUST = False break if SSH_TRUST and not self.localMode: # SSH trust has been created self.verifyCleanGphome() parallelTool.parallelExecute(self.sshExecWithPwd, self.nodeList) if not SSH_TRUST or self.localMode: # SSH trust has not been created # which means clean gphome locally self.verifyCleanGphome() (status, output) = subprocess.getstatusoutput(cmd) if status != 0: raise Exception( ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + " Error:\n%s" % output) self.logger.logExit("Successfully clean gphome.") except Exception as e: self.logger.logExit(str(e)) def run(self): try: self.logger.debug( "gs_postuninstall execution takes %s steps in total" % ClusterCommand.countTotalSteps("gs_postuninstall")) self.cleanGphomeScript() self.checkLogFilePath() self.cleanSyslogConfig() self.doCleanEnvironment() self.logger.closeLog() self.cleanLocalLog() self.cleanMpprcFile() self.cleanScript() self.logger.log("Successfully cleaned environment.") except Exception as e: self.logger.logExit(str(e)) sys.exit(0)