# -*- 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. # ---------------------------------------------------------------------------- # Description : gs_om is a utility to manage a Gauss200 cluster. ############################################################################# import subprocess import os import sys import pwd from datetime import datetime import getpass sys.path.append(sys.path[0] + "/../../../") from gspylib.common.DbClusterInfo import dbClusterInfo, queryCmd from gspylib.threads.SshTool import SshTool from gspylib.common.DbClusterStatus import DbClusterStatus from gspylib.common.ErrorCode import ErrorCode from gspylib.common.Common import DefaultValue, ClusterCommand from gspylib.common.OMCommand import OMCommand from gspylib.os.gsfile import g_file from base_utils.os.cmd_util import CmdUtil from base_utils.os.compress_util import CompressUtil from base_utils.os.env_util import EnvUtil from base_utils.os.file_util import FileUtil from base_utils.os.net_util import NetUtil from base_utils.os.user_util import UserUtil from base_utils.common.constantsbase import ConstantsBase from domain_utils.cluster_file.cluster_dir import ClusterDir from base_utils.executor.cmd_executor import CmdExecutor # Cert EMPTY_CERT = "emptyCert" EMPTY_FLAG = "emptyflag" ########################################### class OmImpl: """ init the command options save command line parameter values """ def __init__(self, OperationManager): """ function: constructor """ # global self.context = OperationManager self.logger = OperationManager.logger self.user = OperationManager.user self.newClusterInfo = None self.oldClusterInfo = None self.utilsPath = None self.mpprcFile = "" self.nodeId = OperationManager.g_opts.nodeId self.time_out = OperationManager.g_opts.time_out self.mode = OperationManager.g_opts.mode self.clusterInfo = OperationManager.clusterInfo self.dataDir = OperationManager.g_opts.dataDir self.sshTool = None def doStopCluster(self): """ function: do stop cluster input: NA output: NA """ pass def doStart(self): """ function:Start cluster or node input:NA output:NA """ self.doStartCluster() def doStop(self): """ function:Stop cluster or node input:NA output:NA """ self.logger.debug("Operating: Stopping.") self.doStopCluster() def getNodeStatus(self, nodename): """ function: get node status input: nodename output: NA """ try: # Create a temporary file to save cluster status tmpDir = EnvUtil.getTmpDirFromEnv() tmpFile = os.path.join(tmpDir, "gauss_cluster_status.dat_" + \ str(datetime.now().strftime( '%Y%m%d%H%M%S')) + "_" + str( os.getpid())) # Perform the start operation # Writes the execution result to a temporary file cmd = ClusterCommand.getQueryStatusCmd("", tmpFile, True) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): self.logger.debug("The cmd is %s " % cmd) raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % \ cmd + "Error: \n%s" % output) # Initialize cluster status information for the temporary file clusterStatus = DbClusterStatus() clusterStatus.initFromFile(tmpFile) # Get node status nodeStatusInfo = None for dbNode in clusterStatus.dbNodes: if (dbNode.name == nodename): nodeStatusInfo = dbNode if (nodeStatusInfo and nodeStatusInfo.isNodeHealthy()): nodeStatus = clusterStatus.OM_NODE_STATUS_NORMAL else: nodeStatus = clusterStatus.OM_NODE_STATUS_ABNORMAL FileUtil.cleanTmpFile(tmpFile) return nodeStatus except Exception as e: FileUtil.cleanTmpFile(tmpFile) self.logger.debug( "Failed to get node status. Error: \n%s." % str(e)) return "Abnormal" def getQueryStatusByCm(self, nodeId): """ function: query status by cm :param nodeId: :return: """ # Call cm_ctl to query the cluster status get_cluster_status_cmd = ClusterCommand.getQueryStatusCmdForDisplay( nodeId, self.context.g_opts.outFile, self.context.clusterInfo.clusterType, self.context.g_opts.show_detail, self.context.g_opts.showAll) if self.context.g_opts.outFile != "": get_cluster_status_cmd += "&& chmod %s '%s'" % ( DefaultValue.KEY_FILE_MODE, self.context.g_opts.outFile) # Call cm_ctl to query the cluster status (status, output) = subprocess.getstatusoutput(get_cluster_status_cmd) if status != 0: if not(status == 255 and "Down" in output): raise Exception(ErrorCode.GAUSS_516[ "GAUSS_51600"] + "\nCommand:%s\nError: \n%s" % ( get_cluster_status_cmd, output)) node_status_line = "" if self.context.g_opts.nodeName != "": node_status = self.getNodeStatus(self.context.g_opts.nodeName) node_status_line = "node_state : %s\n\n" % node_status + \ "-----------------------------------------------------------------------\n" # Outputs the check result if no output file is specified if self.context.g_opts.outFile == "": if status == 0 and self.context.g_opts.show_detail: temp_lines = output.splitlines() node_head = temp_lines[-3].split("|")[0] node_split = "-" * len(node_head) node_info = [info.strip() for info in temp_lines[-1].split("|")] self.logger.log("\n".join(temp_lines[:-3] + [node_head] + [node_split] + node_info)) else: self.logger.log(output) if node_status_line: self.logger.log(node_status_line) else: if node_status_line: FileUtil.createFileInSafeMode(self.context.g_opts.outFile) with open(self.context.g_opts.outFile, "a") as fp: fp.write(node_status_line) fp.write(os.linesep) fp.flush() fp.close() self.logger.log("Status check is completed.") def getQueryStatusWithoutCm(self, nodeId=0, sshtool=None, hostName=""): """ function:query status without cm :param nodeId: :param sshtool: :param hostName: :return: """ cmd = queryCmd() if self.context.g_opts.outFile != "": cmd.outputFile = self.context.g_opts.outFile else: cmd.outputFile = self.logger.logFile if self.context.g_opts.show_detail: if (self.context.clusterInfo.clusterType == DefaultValue.CLUSTER_TYPE_SINGLE_PRIMARY_MULTI_STANDBY): cmd.dataPathQuery = True cmd.azNameQuery = True else: cmd.dataPathQuery = True else: if nodeId > 0: self.context.clusterInfo.queryNodeInfo(sshtool, hostName, nodeId, cmd.outputFile) return az_name = self.context.g_opts.azName if az_name: self.context.clusterInfo.queryNodeInfo(sshtool, hostName, nodeId, cmd.outputFile, az_name) return if self.context.g_opts.showAll: self.context.clusterInfo.queryNodeInfo(sshtool, hostName, nodeId, cmd.outputFile) return cmd.clusterStateQuery = True db_nums = len(self.context.clusterInfo.dbNodes) ssh_tools = [] for _ in range(db_nums - 1): ssh_tools.append(SshTool([], timeout=self.time_out)) self.context.clusterInfo.queryClsInfo(hostName, ssh_tools, self.context.mpprcFile, cmd) def doStatus(self): """ function:Get the status of cluster or node input:NA output:NA """ host_name = NetUtil.GetHostIpOrName() sshtool = SshTool(self.context.clusterInfo.getClusterNodeNames(), timeout=self.time_out) node_id = 0 if self.context.g_opts.nodeName != "": for db_node in self.context.clusterInfo.dbNodes: if db_node.name == self.context.g_opts.nodeName: node_id = db_node.id if node_id == 0: raise Exception( ErrorCode.GAUSS_516["GAUSS_51619"] % self.context.g_opts.nodeName) if ((not self.context.clusterInfo.hasNoCm()) and DefaultValue.isgreyUpgradeNodeSpecify(self.context.user, DefaultValue.GREY_UPGRADE_STEP_UPGRADE_PROCESS, None, self.context.logger)): self.getQueryStatusByCm(node_id) else: self.getQueryStatusWithoutCm(node_id, sshtool, host_name) self.logger.debug("Successfully obtained the cluster status.") def change_cluster_info(self): temp_change_dic = {} for i in range(0, len(self.context.g_opts.old_values)): temp_change_dic['90123456789%d' % i] = (self.context.g_opts.old_values[i], self.context.g_opts.new_values[i]) # change old value to middle value for key, value in temp_change_dic.items(): for node in self.context.clusterInfo.dbNodes: self.change_node_info(node, key, value[0]) for inst in node.datanodes: self.change_inst_info(inst, key, value[0]) # change middle value to new value for key, value in temp_change_dic.items(): for node in self.context.clusterInfo.dbNodes: self.change_node_info(node, value[1], key) for inst in node.datanodes: self.change_inst_info(inst, value[1], key) def change_node_info(self, node, new_value, old_value): if node.name == old_value: node.name = new_value for i in range(len(node.backIps)): if node.backIps[i] == old_value: node.backIps[i] = new_value for i in range(len(node.virtualIp)): if node.virtualIp[i] == old_value: node.virtualIp[i] = new_value for i in range(len(node.sshIps)): if node.sshIps[i] == old_value: node.sshIps[i] = new_value self.logger.debug("Change the info of node %s successfully." % node.name) def change_inst_info(self, inst, new_value, old_value): if inst.hostname == old_value: inst.hostname = new_value if str(inst.port) == old_value: inst.port = int(new_value) if str(inst.haPort) == old_value: inst.haPort = int(new_value) for i in range(len(inst.listenIps)): if inst.listenIps[i] == old_value: inst.listenIps[i] = new_value for i in range(len(inst.haIps)): if inst.haIps[i] == old_value: inst.haIps[i] = new_value self.logger.debug("Change the DN inst on %s successfully." % inst.hostname) def doRebuildConf(self): """ generating static configuration files for all nodes input:NA output:NA """ try: self.logger.log( "Generating static configuration files for all nodes.") tmpDirName = "" # Initialize the cluster information according to the XML file self.context.clusterInfo = dbClusterInfo() if self.context.g_opts.old_values: self.context.clusterInfo.initFromStaticConfig(self.context.user) self.change_cluster_info() else: self.context.clusterInfo.initFromXml(self.context.g_opts.confFile) # 1.create a tmp dir self.logger.log( "Creating temp directory to store static configuration files.") dirName = os.path.dirname(os.path.realpath(__file__)) tmpDirName = os.path.realpath( "%s/../../static_config_files" % dirName) cmd = "mkdir -p -m %s '%s'" % ( DefaultValue.KEY_DIRECTORY_MODE, tmpDirName) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): raise Exception( ErrorCode.GAUSS_502["GAUSS_50208"] % "temporary directory" + "\nCommand:%s\nError: %s" % (cmd, output)) self.logger.log("Successfully created the temp directory.") # create static files self.logger.log("Generating static configuration files.") for dbNode in self.context.clusterInfo.dbNodes: staticConfigPath = "%s/cluster_static_config_%s" % ( tmpDirName, dbNode.name) self.context.clusterInfo.saveToStaticConfig(staticConfigPath, dbNode.id) self.logger.log( "Successfully generated static configuration files.") self.logger.log( "Static configuration files for all nodes are saved in %s." % tmpDirName) # check if need send static config files if not self.context.g_opts.distribute: self.logger.debug( "No need to distribute static configuration files " "to installation directory.") return # distribute static config file self.logger.log( "Distributing static configuration files to all nodes.") for dbNode in self.context.clusterInfo.dbNodes: if (dbNode.name != NetUtil.GetHostIpOrName()): cmd = \ "pscp -H %s '%s'/cluster_static_config_%s '%s'" \ "/bin/cluster_static_config" % ( dbNode.name, tmpDirName, dbNode.name, self.context.clusterInfo.appPath) else: cmd = \ "cp '%s'/cluster_static_config_%s '%s'" \ "/bin/cluster_static_config" % ( tmpDirName, dbNode.name, self.context.clusterInfo.appPath) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): raise Exception( ErrorCode.GAUSS_502["GAUSS_50216"] % "static configuration file" + "Node: %s.\nCommand: \n%s\nError: \n%s" % (dbNode.name, cmd, output)) self.logger.log( "Successfully distributed static configuration files.") except Exception as e: FileUtil.removeDirectory(tmpDirName) raise Exception(str(e)) ########################################################################## # doReplaceSSLCert start ########################################################################## def doReplaceSSLCert(self): """ function: replace ssl cert files input: NA output: NA """ try: # Initialize the cluster information according to the xml file self.context.clusterInfo = dbClusterInfo() self.context.clusterInfo.initFromStaticConfig( UserUtil.getPathOwner(self.context.g_opts.certFile)[0]) self.sshTool = SshTool( self.context.clusterInfo.getClusterNodeNames(), self.logger.logFile) except Exception as e: raise Exception(str(e)) try: self.logger.log("Starting ssl cert files replace.", "addStep") tempDir = os.path.join(EnvUtil.getTmpDirFromEnv(), "tempCertDir") # unzip files to temp directory if (os.path.exists(tempDir)): FileUtil.removeDirectory(tempDir) FileUtil.createDirectory(tempDir, True, DefaultValue.KEY_DIRECTORY_MODE) CompressUtil.decompressZipFiles(self.context.g_opts.certFile, tempDir) realCertList = DefaultValue.CERT_FILES_LIST clientCertList = DefaultValue.CLIENT_CERT_LIST # check file exists for clientCert in clientCertList: sslFile = os.path.join(tempDir, clientCert) if (not os.path.isfile(sslFile)): raise Exception( (ErrorCode.GAUSS_502["GAUSS_50201"] % sslFile) + \ "Missing SSL client cert file in ZIP file.") certList = [] dnDict = self.getDnNodeDict() for cert in realCertList: sslFile = os.path.join(tempDir, cert) if (not os.path.isfile( sslFile) and cert != DefaultValue.SSL_CRL_FILE): raise Exception( (ErrorCode.GAUSS_502["GAUSS_50201"] % sslFile) + \ "Missing SSL server cert file in ZIP file.") if (os.path.isfile(sslFile)): certList.append(cert) # distribute cert files to datanodes self.doDNBackup() self.distributeDNCert(certList, dnDict) # clear temp directory FileUtil.removeDirectory(tempDir) if (not self.context.g_opts.localMode): self.logger.log( "Successfully distributed cert files on all nodes.") except Exception as e: FileUtil.removeDirectory(tempDir) raise Exception(str(e)) def isDnEmpty(self, nodeName=""): """ function: Is there exists empty file in dbnodes directory. input: node name output: True/False """ allDnNodeDict = self.getDnNodeDict() nodeDnDir = allDnNodeDict[nodeName] emptyCert = os.path.join(nodeDnDir, EMPTY_CERT) status = self.sshTool.checkRemoteFileExist( nodeName, emptyCert, self.context.g_opts.mpprcFile) return status def backup_cert_files(self, ssl_dir): """ function: Back up the certificate files in the ssl_dir directory. input: certificate directory output: NA """ if self.context.g_opts.localMode: # empty flag if os.path.isfile( os.path.join(ssl_dir, DefaultValue.CERT_BACKUP_FILE)): FileUtil.removeFile( os.path.join(ssl_dir, DefaultValue.CERT_BACKUP_FILE)) if not os.path.exists(os.path.join(ssl_dir, EMPTY_FLAG)): os.mknod(os.path.join(ssl_dir, EMPTY_FLAG)) cmd = " %s && " % CmdUtil.getCdCmd(ssl_dir) cmd += CompressUtil.getCompressFilesCmd(DefaultValue.CERT_BACKUP_FILE, "*") (status, output) = CmdUtil.retryGetstatusoutput(cmd) if status != 0: FileUtil.removeFile(os.path.join(ssl_dir, EMPTY_FLAG)) raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % \ cmd + "Failed to backup cert files on local node." + "Error: \n%s" % output) if os.path.exists(os.path.join(ssl_dir, EMPTY_FLAG)): os.remove(os.path.join(ssl_dir, EMPTY_FLAG)) else: sshcmd = "if [ -d '%s' ]; then " % ssl_dir sshcmd += " %s && " % CmdUtil.getCdCmd(ssl_dir) sshcmd += g_file.SHELL_CMD_DICT["deleteFile"] % ( DefaultValue.CERT_BACKUP_FILE, DefaultValue.CERT_BACKUP_FILE) + ";" sshcmd += CmdUtil.getTouchCmd(EMPTY_FLAG) sshcmd += " && " + CompressUtil.getCompressFilesCmd(DefaultValue.CERT_BACKUP_FILE, "*;") sshcmd += g_file.SHELL_CMD_DICT["deleteFile"] % (EMPTY_FLAG, EMPTY_FLAG) sshcmd += " && " + g_file.SHELL_CMD_DICT["changeMode"] % ( DefaultValue.KEY_FILE_MODE, DefaultValue.CERT_BACKUP_FILE) sshcmd += "; fi" self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node.name for node in self.context.clusterInfo.dbNodes], self.context.g_opts.mpprcFile) def doDNBackup(self): """ function: backup SSL cert files on single_inst cluster. input: backupFlag is a flag of exist DB in node output: NA """ self.logger.log("Backing up old ssl cert files.") backupList = DefaultValue.CERT_FILES_LIST[:] allDnNodeDict = self.getDnNodeDict() normalNodeList = [] dss_ssl_dir = os.path.join(ClusterDir.getInstallDir(self.context.g_opts.user), "share/sslcert/dss/") cm_ssl_dir = os.path.join(ClusterDir.getInstallDir(self.context.g_opts.user), "share/sslcert/cm/") om_ssl_dir = os.path.join(ClusterDir.getInstallDir(self.context.g_opts.user), "share/sslcert/om/") tarBackupList = [] if (self.context.g_opts.localMode): self.logger.debug("Backing up database node SSL cert files.") nodeDnDir = allDnNodeDict[NetUtil.GetHostIpOrName()] backupFlagFile = os.path.join(nodeDnDir, "certFlag") if (os.path.isfile(backupFlagFile)): self.logger.log("There is no need to backup ssl cert files.") return os.mknod(backupFlagFile, ConstantsBase.KEY_FILE_PERMISSION) for certFile in backupList: realCertFile = os.path.join(nodeDnDir, certFile) if (os.path.isfile(realCertFile)): tarBackupList.append(certFile) if (len(tarBackupList) == 0): os.mknod(os.path.join(nodeDnDir, EMPTY_CERT)) cmd = " %s && " % CmdUtil.getCdCmd(nodeDnDir) cmd += CompressUtil.getCompressFilesCmd( DefaultValue.CERT_BACKUP_FILE, EMPTY_CERT) else: cmd = " %s && " % CmdUtil.getCdCmd(nodeDnDir) cmd += "tar -zcvf %s" % (DefaultValue.CERT_BACKUP_FILE) for certFile in tarBackupList: cmd += " %s" % certFile (status, output) = CmdUtil.retryGetstatusoutput(cmd) if (status != 0): raise Exception( ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + "Failed backup gds cert files on local node." + "Error: \n%s" % output) # Clear empty file if (os.path.isfile(os.path.join(nodeDnDir, EMPTY_CERT))): os.remove(os.path.join(nodeDnDir, EMPTY_CERT)) if os.path.isdir(dss_ssl_dir): self.backup_cert_files(dss_ssl_dir) if os.path.isdir(cm_ssl_dir): self.backup_cert_files(cm_ssl_dir) if os.path.isdir(om_ssl_dir): self.backup_cert_files(om_ssl_dir) self.logger.log("Successfully executed local backup.") return # 1 check backup flag file on all dbnodes. for node in allDnNodeDict.keys(): nodeDnDir = allDnNodeDict[node] backupFlagFile = os.path.join(nodeDnDir, "certFlag") status = self.sshTool.checkRemoteFileExist( node, backupFlagFile, self.context.g_opts.mpprcFile) if not status: normalNodeList.append(node) # 2 if exists flag file on anyone node, there will be return. if (len(normalNodeList) != len(allDnNodeDict.keys())): self.logger.log("There is no need to backup on all dbnodes.") return # 3 backup cert files on all dbnodes. for node in allDnNodeDict.keys(): nodeDnDir = allDnNodeDict[node] backupFlagFile = os.path.join(nodeDnDir, "certFlag") backupTar = os.path.join(nodeDnDir, DefaultValue.CERT_BACKUP_FILE) sshcmd = g_file.SHELL_CMD_DICT["overWriteFile"] % ( "backupflagfile", backupFlagFile) sshcmd += " && " + g_file.SHELL_CMD_DICT["changeMode"] % ( DefaultValue.KEY_FILE_MODE, backupFlagFile) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) for certFile in backupList: realCertFile = os.path.join(nodeDnDir, certFile) status = self.sshTool.checkRemoteFileExist( node, realCertFile, self.context.g_opts.mpprcFile) if status: tarBackupList.append(certFile) # if no cert files, # there will be create a file for '.tar' file. if (len(tarBackupList) == 0): sshcmd = CmdUtil.getCreateFileCmd( os.path.join(nodeDnDir, EMPTY_CERT)) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) sshcmd = " %s && " % CmdUtil.getCdCmd(nodeDnDir) sshcmd += CompressUtil.getCompressFilesCmd( DefaultValue.CERT_BACKUP_FILE, EMPTY_CERT) else: sshcmd = " %s && " % CmdUtil.getCdCmd(nodeDnDir) sshcmd += "tar -zcvf %s" % (DefaultValue.CERT_BACKUP_FILE) for certDir in tarBackupList: sshcmd += " %s" % certDir self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) # Clear empty file if (self.isDnEmpty(node)): sshcmd = g_file.SHELL_CMD_DICT["deleteFile"] % ( os.path.join(nodeDnDir, EMPTY_CERT), os.path.join(nodeDnDir, EMPTY_CERT)) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) self.logger.log( "Successfully backup SSL cert files on [%s]." % node) sshcmd = g_file.SHELL_CMD_DICT["changeMode"] % ( DefaultValue.KEY_FILE_MODE, backupTar) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) self.backup_cert_files(dss_ssl_dir) self.backup_cert_files(cm_ssl_dir) self.backup_cert_files(om_ssl_dir) def local_rollback_cert(self, ssl_dir, cert_list): """ function: rollback SSL cert files input: Certificate directory, which is used to replace the certificate file. output: NA """ # create dss temp dir temp_dir = os.path.join(ssl_dir, "dssTempDir") if not os.path.exists(temp_dir): os.mkdir(temp_dir) else: FileUtil.removeDirectory(temp_dir) os.mkdir(temp_dir) # backup cert cert to temp dir for num in iter(cert_list): certfile = os.path.join(ssl_dir, num) if os.path.exists(certfile): FileUtil.moveFile(certfile, temp_dir) # decompress backup package to cert dir cmd = "cd '%s' && if [ -f '%s' ];then tar -zxf %s;fi" % \ (ssl_dir, DefaultValue.CERT_BACKUP_FILE, DefaultValue.CERT_BACKUP_FILE) status, _ = subprocess.getstatusoutput(cmd) if status != 0: # copy cert files from temp dir to cert dir cmd = "cp '%s'/* '%s' && rm -rf '%s' && rm -rf '%s'" % \ (temp_dir, temp_dir, temp_dir, os.path.join(temp_dir, EMPTY_FLAG)) CmdUtil.getstatusoutput_by_fast_popen(cmd) FileUtil.removeDirectory(temp_dir) FileUtil.removeDirectory(os.path.join(temp_dir, EMPTY_FLAG)) self.logger.debug("Successfully rollback ssl cert files.") def cert_rollback(self): """ function: Certificate Rollback input: NA output: NA """ dss_ssl_dir = os.path.join(ClusterDir.getInstallDir( self.context.g_opts.user), "share/sslcert/dss/") cm_ssl_dir = os.path.join(ClusterDir.getInstallDir( self.context.g_opts.user), "share/sslcert/cm/") om_ssl_dir = os.path.join(ClusterDir.getInstallDir( self.context.g_opts.user), "share/sslcert/om/") if self.context.g_opts.localMode: if os.path.exists(dss_ssl_dir) and EnvUtil.get_dss_ssl_status( getpass.getuser()) == 'on': self.local_rollback_cert(dss_ssl_dir, DefaultValue.GDS_CERT_LIST) self.regen_dss_cert() self.logger.log("Successfully rollback dss ssl cert files.") if os.path.exists(cm_ssl_dir): self.local_rollback_cert(cm_ssl_dir, DefaultValue.GDS_CERT_LIST) self.logger.log("Successfully rollback cm ssl cert files.") if os.path.exists(om_ssl_dir): self.local_rollback_cert(om_ssl_dir, DefaultValue.SERVER_CERT_LIST) self.logger.log("Successfully rollback om ssl cert files.") else: # rollback dss cert files if os.path.exists(dss_ssl_dir) and EnvUtil.get_dss_ssl_status( getpass.getuser()) == 'on': self.rollback_cert(dss_ssl_dir, DefaultValue.GDS_CERT_LIST) self.regen_dss_cert() self.logger.log("Successfully replace dss SSL cert files.") # rollback cm cert files if os.path.exists(cm_ssl_dir): self.rollback_cert(cm_ssl_dir, DefaultValue.GDS_CERT_LIST) self.logger.log("Successfully replace cm SSL cert files.") # rollback om cert files if os.path.exists(om_ssl_dir): self.rollback_cert(om_ssl_dir, DefaultValue.SERVER_CERT_LIST) self.logger.log("Successfully replace om SSL cert files.") def doDNSSLCertRollback(self): """ function: rollback SSL cert file in DN instance directory input: NA output: NA """ self.context.clusterInfo = dbClusterInfo() self.context.clusterInfo.initFromStaticConfig( pwd.getpwuid(os.getuid()).pw_name) self.sshTool = SshTool(self.context.clusterInfo.getClusterNodeNames(), self.logger.logFile) backupList = DefaultValue.CERT_FILES_LIST[:] allDnNodeDict = self.getDnNodeDict() noBackupList = [] temp = "tempDir" if self.context.g_opts.localMode: if ((NetUtil.GetHostIpOrName() in allDnNodeDict.keys()) and os.path.isfile(os.path.join( allDnNodeDict[NetUtil.GetHostIpOrName()], DefaultValue.CERT_BACKUP_FILE))): localDnDir = allDnNodeDict[NetUtil.GetHostIpOrName()] tempDir = os.path.join(localDnDir, temp) if (os.path.exists(tempDir)): FileUtil.removeDirectory(tempDir) os.mkdir(tempDir, ConstantsBase.KEY_DIRECTORY_PERMISSION) for certFile in backupList: realCertFile = os.path.join(localDnDir, certFile) if (os.path.exists(realCertFile)): FileUtil.moveFile(realCertFile, tempDir) cmd = "cd '%s' && if [ -f '%s' ];then tar -zxvf %s;fi" % \ (localDnDir, DefaultValue.CERT_BACKUP_FILE, DefaultValue.CERT_BACKUP_FILE) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): cmd = "cp '%s'/* '%s' && rm -rf '%s'" % ( tempDir, localDnDir, tempDir) (status, output) = subprocess.getstatusoutput(cmd) raise Exception( (ErrorCode.GAUSS_514["GAUSS_51400"] % cmd) + "Failed uncompression SSL backup file." + "Error: \n%s" % output) # remove temp directory if (os.path.exists(tempDir)): FileUtil.removeDirectory(tempDir) # set guc option if (os.path.isfile( os.path.join(localDnDir, DefaultValue.SSL_CRL_FILE))): cmd = \ "gs_guc set -D %s " \ "-c \"ssl_crl_file=\'%s\'\"" \ % (localDnDir, DefaultValue.SSL_CRL_FILE) else: cmd = \ "gs_guc set -D %s " \ "-c \"ssl_crl_file=\'\'\"" % localDnDir (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): raise Exception( ErrorCode.GAUSS_514["GAUSS_51400"] % cmd + "Error: \n%s" % output) if (os.path.isfile(os.path.join(localDnDir, EMPTY_CERT))): os.remove(os.path.join(localDnDir, EMPTY_CERT)) self.cert_rollback() self.logger.log( "Successfully rollback SSL cert files with local mode.") return else: self.cert_rollback() self.logger.log("There is not node data exists backup files.") return # 1.check backup file "gsql_cert_backup.tar.gz" on all dbnodes. for node in allDnNodeDict.keys(): backupGzFile = os.path.join(allDnNodeDict[node], DefaultValue.CERT_BACKUP_FILE) status = self.sshTool.checkRemoteFileExist( node, backupGzFile, self.context.g_opts.mpprcFile) if not status: noBackupList.append(node) if (len(noBackupList) > 0): raise Exception( (ErrorCode.GAUSS_502["GAUSS_50201"] % DefaultValue.CERT_BACKUP_FILE) + "Can't rollback SSL cert files on %s." % noBackupList) # 2.perform rollback on all dbnodes. for node in allDnNodeDict.keys(): backupGzFile = os.path.join( allDnNodeDict[node], DefaultValue.CERT_BACKUP_FILE) # 2-1.move SSL cert files in dn directory to temp directory. sshcmd = "cd '%s' && if [ -d '%s' ];then rm -rf '%s'" \ " && mkdir '%s';else mkdir '%s';fi" % \ (allDnNodeDict[node], temp, temp, temp, temp) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) for certFile in backupList: realCertFile = os.path.join(allDnNodeDict[node], certFile) sshcmd = " %s && " % CmdUtil.getCdCmd( os.path.join(allDnNodeDict[node], temp)) sshcmd += g_file.SHELL_CMD_DICT["renameFile"] % ( realCertFile, realCertFile, "./") self.sshTool.executeCommand( sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) # 2-2.uncompression "gsql_cert_backup.tar.gz" file sshcmd = "cd '%s' && if [ -f '%s' ];then tar -zxvf %s;fi" % \ (allDnNodeDict[node], DefaultValue.CERT_BACKUP_FILE, DefaultValue.CERT_BACKUP_FILE) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) # 2-3.clear temp directory sshcmd = " %s && " % CmdUtil.getCdCmd(allDnNodeDict[node]) sshcmd += g_file.SHELL_CMD_DICT["deleteDir"] % (temp, temp) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) # 2-4.is have "sslcrl-file.crl",config 'ssl_crl_file' option status = self.sshTool.checkRemoteFileExist( node, os.path.join( allDnNodeDict[node], DefaultValue.SSL_CRL_FILE), self.context.g_opts.mpprcFile) # exists 'sslcrl-file.crl' file ,config option of 'postgresql.conf' if (status): if node == NetUtil.GetHostIpOrName(): sshcmd = \ "gs_guc set -D %s " \ "-c \"ssl_crl_file='%s'\"" \ % (allDnNodeDict[node], DefaultValue.SSL_CRL_FILE) else: sshcmd = "gs_guc set -D %s " \ "-c \"ssl_crl_file=\\\\\\'%s\\\\\\'\"" \ % (allDnNodeDict[node], DefaultValue.SSL_CRL_FILE) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) else: if (node == NetUtil.GetHostIpOrName()): sshcmd = "gs_guc set " \ "-D %s -c \"ssl_crl_file=''\"" % ( allDnNodeDict[node]) else: sshcmd = "gs_guc set " \ "-D %s -c \"ssl_crl_file=\\\\\\'\\\\\\'\"" \ % (allDnNodeDict[node]) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) # Clear empty file. if (self.isDnEmpty(node)): sshcmd = g_file.SHELL_CMD_DICT["deleteFile"] % ( os.path.join(allDnNodeDict[node], EMPTY_CERT), os.path.join(allDnNodeDict[node], EMPTY_CERT)) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) self.logger.log( "Successfully rollback SSL cert files on [%s]." % node) self.cert_rollback() def rollback_cert(self, ssl_dir, cert_list): """ function: Delete the certificate and decompress the backup package. input: Certificate directory, certificate list output: NA """ cmd = "cd '%s';rm -f" % ssl_dir for num in cert_list: cmd += " {}".format(num) sshcmd = "%s && tar -zxf %s;rm -f '%s'" % (cmd, DefaultValue.CERT_BACKUP_FILE, EMPTY_FLAG) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node.name for node in self.context.clusterInfo.dbNodes], self.context.g_opts.mpprcFile) def getDnNodeDict(self): """ function: get dbnodes information input: NA output: dictionary """ clusterDnNodes = {} if (not self.context.clusterInfo.isSingleInstCluster()): return clusterDnNodes for node in self.context.clusterInfo.dbNodes: if (len(node.datanodes) > 0): clusterDnNodes[node.datanodes[0].hostname] = node.datanodes[ 0].datadir self.logger.debug("Successfully get database node dict.") return clusterDnNodes def distribute_ssl_cert(self, params): """ function: Distributing SSL Certificate Files input: Certificate parameters output: NA """ cert_path_list, ssl_dir, cert_lit, mode = params if self.context.g_opts.localMode: cert_path_list_num = len(cert_path_list) for num in range(cert_path_list_num): # distribute cert if os.path.isfile(os.path.join(ssl_dir, cert_lit[num])): os.remove(os.path.join(ssl_dir, cert_lit[num])) if os.path.isfile(cert_path_list[num]): FileUtil.cpFile(cert_path_list[num], os.path.join(ssl_dir, cert_lit[num])) FileUtil.changeMode(mode, os.path.join(ssl_dir, cert_lit[num])) else: cert_lit_num = len(cert_lit) for num in range(cert_lit_num): sshcmd = g_file.SHELL_CMD_DICT["deleteFile"] % ( os.path.join(ssl_dir, cert_lit[num]), os.path.join(ssl_dir, cert_lit[num])) self.sshTool.executeCommand( sshcmd, DefaultValue.SUCCESS, [node.name for node in self.context.clusterInfo.dbNodes], self.context.g_opts.mpprcFile) cert_path_list_num = len(cert_path_list) for num in range(cert_path_list_num): if os.path.exists(cert_path_list[num]): cmd = g_file.SHELL_CMD_DICT["changeMode"] % ( mode, cert_path_list[num]) (status, output) = subprocess.getstatusoutput(cmd) if status != 0: raise Exception("Failed to chmod dss cert files" + "Error: \n%s" % output) self.sshTool.scpFiles(cert_path_list[num], ssl_dir, [node.name for node in self.context.clusterInfo.dbNodes]) def regen_dss_cert(self): """ function: Re-generate the ciphertext of the DSS. input: NA output: NA """ try: self.logger.debug("Re-generate the ciphertext of the DSS") cmd = "source %s; %s -t dss_cert_replacer -U %s -l %s" % ( self.context.g_opts.mpprcFile, OMCommand.getLocalScript("Local_Collect"), self.context.user, self.context.localLog) CmdExecutor.execCommandWithMode( cmd, self.sshTool, self.context.g_opts.localMode, self.context.g_opts.mpprcFile, [node.name for node in self.context.clusterInfo.dbNodes]) self.logger.debug("The cmd is %s " % cmd) except Exception as e: self.logger.log("Failed to re-generate the ciphertext of the DSS.") raise Exception(str(e)) def distribute_cert(self): """ function: Distributing Certificates input: NA output: NA """ tempDir = "tempCertDir" gphost = EnvUtil.getTmpDirFromEnv() cm_dss_sslcert_cert_path_list = [] server_sslcert_cert_path_list = [] dss_ssl_dir = os.path.join(ClusterDir.getInstallDir( self.context.g_opts.user), "share/sslcert/dss/") cm_ssl_dir = os.path.join(ClusterDir.getInstallDir( self.context.g_opts.user), "share/sslcert/cm/") om_ssl_dir = os.path.join(ClusterDir.getInstallDir( self.context.g_opts.user), "share/sslcert/om/") for num in range(len(DefaultValue.GDS_CERT_LIST)): sslPath = os.path.join(os.path.join(gphost, tempDir), DefaultValue.GDS_CERT_LIST[num]) cm_dss_sslcert_cert_path_list.append(sslPath) for num in range(len(DefaultValue.SERVER_CERT_LIST)): sslPath = os.path.join(os.path.join(gphost, tempDir), DefaultValue.SERVER_CERT_LIST[num]) server_sslcert_cert_path_list.append(sslPath) # distribute dss SSL cert if os.path.isdir(dss_ssl_dir) and EnvUtil.get_dss_ssl_status( getpass.getuser()) == 'on': self.distribute_ssl_cert((cm_dss_sslcert_cert_path_list, dss_ssl_dir, DefaultValue.GDS_CERT_LIST, DefaultValue.MIN_FILE_MODE)) self.regen_dss_cert() self.logger.log("Successfully replace dss ssl cert files.") # distribute cm SSL cert if os.path.isdir(cm_ssl_dir): self.distribute_ssl_cert((cm_dss_sslcert_cert_path_list, cm_ssl_dir, DefaultValue.GDS_CERT_LIST, DefaultValue.MIN_FILE_MODE)) self.logger.log("Successfully replace cm ssl cert files.") # distribute om SSL cert if os.path.isdir(om_ssl_dir): self.distribute_ssl_cert((server_sslcert_cert_path_list, om_ssl_dir, DefaultValue.SERVER_CERT_LIST, DefaultValue.KEY_FILE_MODE)) self.logger.log("Successfully replace om ssl cert files.") def distributeDNCert(self, certList, dnDict=None): """ function: distribute ssl cert files on single_inst cluster input: certList: cert files list dnDict: dictionary output: NA """ tempDir = "tempCertDir" gphost = EnvUtil.getTmpDirFromEnv() if dnDict is None: dnDict = {} dnName = dnDict.keys() certPathList = [] self.logger.debug(certList) for num in iter(certList): sslPath = os.path.join(os.path.join(gphost, tempDir), num) certPathList.append(sslPath) # local mode if self.context.g_opts.localMode: localDnDir = dnDict[NetUtil.GetHostIpOrName()] for num in range(len(certList)): # distribute gsql SSL cert if os.path.isfile(os.path.join(localDnDir, certList[num])): os.remove(os.path.join(localDnDir, certList[num])) if os.path.isfile(certPathList[num]): FileUtil.cpFile(certPathList[num], os.path.join(localDnDir, certList[num])) FileUtil.changeMode(DefaultValue.KEY_FILE_MODE, os.path.join(localDnDir, certList[num])) self.distribute_cert() # remove 'sslcrl-file.crl' file if (DefaultValue.SSL_CRL_FILE not in certList and os.path.isfile( os.path.join(localDnDir, DefaultValue.SSL_CRL_FILE))): os.remove(os.path.join(localDnDir, DefaultValue.SSL_CRL_FILE)) # config 'sslcrl-file.crl' option in 'postgresql.conf' if (os.path.isfile( os.path.join(localDnDir, DefaultValue.SSL_CRL_FILE))): cmd = "gs_guc set " \ "-D %s -c \"ssl_crl_file=\'%s\'\"" % \ (localDnDir, DefaultValue.SSL_CRL_FILE) (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): raise Exception( (ErrorCode.GAUSS_514["GAUSS_51400"] % cmd) + "Failed set 'ssl_crl_file' option." + "Error: \n%s" % output) else: cmd = "gs_guc set -D %s -c \"ssl_crl_file=\'\'\"" \ % localDnDir (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): raise Exception( (ErrorCode.GAUSS_514["GAUSS_51400"] % cmd) + "Failed set 'ssl_crl_file' option." + "Error: \n%s" % output) # remove backup flag file 'certFlag' if (os.path.isfile(os.path.join(localDnDir, 'certFlag'))): os.remove(os.path.join(localDnDir, 'certFlag')) self.logger.log( "Replace SSL cert files with local mode successfully.") return # not local mode for node in dnName: for num in range(len(certList)): sshcmd = g_file.SHELL_CMD_DICT["deleteFile"] % ( os.path.join(dnDict[node], certList[num]), os.path.join(dnDict[node], certList[num])) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) if (os.path.exists(certPathList[num])): self.sshTool.scpFiles(certPathList[num], dnDict[node], [node]) # change permission of cert file 600, # there no need to is exists file, # because the files must be exist. sshcmd = g_file.SHELL_CMD_DICT["changeMode"] % ( DefaultValue.KEY_FILE_MODE, os.path.join(dnDict[node], certList[num])) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) if (DefaultValue.SSL_CRL_FILE in certList): if (node == NetUtil.GetHostIpOrName()): sshcmd = "gs_guc set " \ "-D %s -c \"ssl_crl_file='%s'\"" \ % (dnDict[node], DefaultValue.SSL_CRL_FILE) else: sshcmd = "gs_guc set " \ " -D %s -c \"ssl_crl_file=\\\\\\'%s\\\\\\'\"" \ % (dnDict[node], DefaultValue.SSL_CRL_FILE) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) else: # no ssl cert file there will delete old cert file, # and config option ssl_crl_file = '' sshcmd = g_file.SHELL_CMD_DICT["deleteFile"] % ( os.path.join(dnDict[node], DefaultValue.SSL_CRL_FILE), os.path.join(dnDict[node], DefaultValue.SSL_CRL_FILE)) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) if (node == NetUtil.GetHostIpOrName()): sshcmd = "gs_guc set " \ "-D %s -c \"ssl_crl_file=\'\'\"" % (dnDict[node]) else: sshcmd = \ "gs_guc set " \ "-D %s " \ "-c \"ssl_crl_file=\\\\\\'\\\\\\'\"" % (dnDict[node]) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) # remove file 'sslcrl-file.crl' sshcmd = g_file.SHELL_CMD_DICT["deleteFile"] % ( os.path.join(dnDict[node], DefaultValue.SSL_CRL_FILE), os.path.join(dnDict[node], DefaultValue.SSL_CRL_FILE)) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) # remove backup flag file 'certFlag' sshcmd = g_file.SHELL_CMD_DICT["deleteFile"] % ( os.path.join(dnDict[node], "certFlag"), os.path.join(dnDict[node], "certFlag")) self.sshTool.executeCommand(sshcmd, DefaultValue.SUCCESS, [node], self.context.g_opts.mpprcFile) self.logger.log("%s replace SSL cert files successfully." % node) self.distribute_cert() ########################################################################### # Kerberos Flow ########################################################################### def doKerberos(self): """ function: operation kerberos input: NA output: NA """ try: if self.context.g_opts.kerberosMode == "install": self.logger.log("Starting install Kerberos.", "addStep") cmd = "%s -m %s -U %s --%s" % \ (OMCommand.getLocalScript("Local_Kerberos"), "install", self.context.g_opts.clusterUser, self.context.g_opts.kerberosType) # local mode (status, output) = subprocess.getstatusoutput(cmd) if (status != 0): raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % "Command: %s. Error:\n%s" % (cmd, output)) self.logger.log("Successfully install Kerberos.") elif self.context.g_opts.kerberosMode == "uninstall": self.logger.log("Starting uninstall Kerberos.", "addStep") cmd = "%s -m %s -U %s" % \ (OMCommand.getLocalScript("Local_Kerberos"), "uninstall", self.context.g_opts.clusterUser) # local mode (status, output) = subprocess.getstatusoutput(cmd) if status != 0: raise Exception(ErrorCode.GAUSS_514["GAUSS_51400"] % "Command: %s. Error:\n%s" % (cmd, output)) self.logger.log("Successfully uninstall Kerberos.") except Exception as e: raise Exception(str(e)) def checkRemoteFileExist(self, filepath): """ funciton:check file exist on remote node input:filepath output:dictionary """ existNodes = [] for nodeName in self.context.clusterInfo.getClusterNodeNames(): if (nodeName == NetUtil.GetHostIpOrName()): continue if (self.sshTool.checkRemoteFileExist(nodeName, filepath, "")): existNodes.append(nodeName) return existNodes def recursivePath(self, filepath): """ function: recursive path input: filepath output: NA """ fileList = os.listdir(filepath) for fileName in fileList: fileName = os.path.join(filepath, fileName) # change the owner of files FileUtil.changeOwner(self.context.g_opts.user, fileName) if (os.path.isfile(fileName)): # change fileName permission FileUtil.changeMode(DefaultValue.KEY_FILE_MODE, fileName) else: # change directory permission FileUtil.changeMode(DefaultValue.KEY_DIRECTORY_MODE, fileName, True) self.recursivePath(fileName) def stopCluster(self): """ function:Stop cluster input:NA output:NA """ pass def startCluster(self): """ function:Start cluster input:NA output:NA """ pass