CM/tool/cm_tool/cm_uninstall

298 lines
12 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c) 2022 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 : cm_install is a utility to uninstall CM tool to openGauss database cluster.
#############################################################################
import os
import sys
import getopt
import subprocess
from CMLog import CMLog
from ErrorCode import ErrorCode
from InstallImpl import InstallImpl
from Common import *
class UnIntall:
def __init__(self) -> None:
self.envFile = ""
self.xmlFile = ""
self.bDeleteData = False
self.bDeleteBinary = False
self.hostnames = []
self.cmDataPaths = []
self.localhostName = getLocalhostName()
def usage(self):
"""
cm_uninstall is a utility to uninstall CM tool to openGauss database cluster.
Usage:
cm_uninstall -? | --help
cm_uninstall -X XMLFILE [-e envFile] [--deleteData] [--deleteBinary]
General options:
-X Path of the XML configuration file.
-e Path of env file.
Default value "~/.bashrc".
--deleteData Specify to true if you want to remove cmdatapath and GAUSSLOG/cm.
--deleteBinary Specify to true if you want to remove CM binary file.
-?, --help Show help information for this
utility, and exit the command line mode.
"""
print(self.usage.__doc__)
def parseCommandLine(self):
if len(sys.argv) == 1:
self.usage()
sys.exit(1)
try:
opts, args = getopt.getopt(sys.argv[1:], "?X:e:",
["help", "deleteData", "deleteBinary"])
except getopt.GetoptError as e:
CMLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50000"] % str(e))
for opt, value in opts:
if opt in ("-?", "--help"):
self.usage()
sys.exit(0)
elif opt in ("-X"):
self.xmlFile = value
elif opt in ("-e"):
self.envFile = value
elif opt in ("--deleteData"):
self.bDeleteData = True
elif opt in ("--deleteBinary"):
self.bDeleteBinary = True
def checkParam(self):
if self.xmlFile == "":
CMLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50001"] % 'X' + ".")
checkXMLFile(self.xmlFile)
if self.envFile == "":
self.envFile = os.path.join(os.environ['HOME'], ".bashrc")
if not os.path.exists(self.envFile):
CMLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50201"] % ("envFile " + self.envFile))
if not os.path.isfile(self.envFile):
CMLog.exitWithError(ErrorCode.GAUSS_502["GAUSS_50210"] % ("envFile " + self.envFile))
mppdbEnv = getEnvParam(self.envFile, "MPPDB_ENV_SEPARATE_PATH")
if mppdbEnv != "":
self.envFile = mppdbEnv
if self.envFile == "" or not os.path.exists(self.envFile) or not os.path.isfile(self.envFile):
CMLog.exitWithError(ErrorCode.GAUSS_518["GAUSS_51802"] % 'MPPDB_ENV_SEPARATE_PATH' + ".")
def checkXMLContainCMInfo(self):
cmd = "grep -E 'cmDir|cmsNum|cmServerPortBase|cmServerPortStandby|cmServerlevel|" \
"cmServerListenIp1|cmServerRelation' " + self.xmlFile
status, output = subprocess.getstatusoutput(cmd)
if status == 0:
CMLog.exitWithError("%s should not contain CM infos.\nDetail:\n%s" % (self.xmlFile, output))
def getHostnames(self):
"""
Get hostnames of all nodes from static file.
"""
self.logger.debug("Getting hostnames")
cmd = "source %s; cm_ctl view | grep 'nodeName'" % self.envFile
status, output = subprocess.getstatusoutput(cmd)
if status != 0:
self.logger.logExit((ErrorCode.GAUSS_512["GAUSS_51203"] % "hostnames") + output)
hostnames = output.split('\n')
for i in range(0, len(hostnames)):
hostname = hostnames[i].split(':')[1].strip()
self.hostnames.append(hostname)
self.logger.debug("hostnames=" + ','.join(self.hostnames))
def getCMDataPaths(self):
"""
Get cmDataPaths of all nodes from static file.
"""
if not self.bDeleteData:
return
self.logger.debug("Getting CM datapaths.")
cmd = "source %s; cm_ctl view | grep -E 'cmDataPath'" % self.envFile
status, output = subprocess.getstatusoutput(cmd)
if status != 0:
self.logger.logExit((ErrorCode.GAUSS_512["GAUSS_51203"] % "cmDataPaths") + output)
cmDataPaths = output.split('\n')
for p in cmDataPaths:
cmDataPath = p.split(':')[1].strip()
self.cmDataPaths.append(cmDataPath)
if self.cmDataPaths == []:
self.logger.logExit("")
self.logger.debug("cmDataPaths=" + ','.join(self.cmDataPaths))
def cancleMonitorCrontab(self):
"""
Cancle monitor crontab of all nodes.
"""
self.logger.log("Cancling monitor crontab.")
cronContentTmpFile = os.path.join("/tmp", "cronContentTmpFile_" + str(os.getpid()))
cmd = """
crontab -l > {cronContentTmpFile};
sed '/.*om_monitor.*/d' {cronContentTmpFile} -i;
crontab {cronContentTmpFile} &&
rm -f {cronContentTmpFile}
""".format(cronContentTmpFile=cronContentTmpFile)
for host in self.hostnames:
isLocal = False
if host == self.localhostName:
isLocal = True
status, output = executeCmdOnHost(host, cmd, isLocal)
if status != 0:
errorDetail = "\nCommand: %s\nStatus: %s\nOutput: %s" % (cmd, status, output)
self.logger.logExit(("Failed to cancle monitor crontab on host %s." % host) + errorDetail)
def stopCMProcess(self):
"""
Stop CM process of all nodes, includeing om_monitor, cm_agent, cm_server, gaussdb fence
"""
self.logger.log("Stopping CM process.")
# When you execute the awk command through Python, sometimes the awk command is invalid,
# just like your command does not use the awk command. Therefore, we have to avoid using awk.
getPidsCmd = "ps -xo pid,command | grep -E 'om_monitor|cm_agent|cm_server|fenced UDF' | grep -v grep"
for host in self.hostnames:
isLocal = False
if host == self.localhostName:
isLocal = True
# get CM process pids
status, output = executeCmdOnHost(host, getPidsCmd, isLocal)
self.logger.debug("Command: " + getPidsCmd)
self.logger.debug("Status: " + str(status))
self.logger.debug("Output: " + output)
if output == "":
continue
processList = output.strip().split('\n')
pidList = []
for process in processList:
pid = process.split()[0]
if pid.isdigit():
pidList.append(pid)
if pidList == []:
continue
# kill CM process
pidsStr = ' '.join(pidList)
killCMProcessCmd = "kill -9 " + pidsStr
status, output = executeCmdOnHost(host, killCMProcessCmd, isLocal)
if status != 0:
errorDetail = "\nCommand: %s\nStatus: %s\nOutput: %s" % (killCMProcessCmd, status, output)
self.logger.logExit((ErrorCode.GAUSS_516["GAUSS_51606"] % "CM") + errorDetail)
def refreshStaticAndDynamicFile(self):
self.logger.log("Refreshing static and dynamic file using xml file without cm.")
status, output = InstallImpl.refreshStaticFile(self.envFile, self.xmlFile)
if status != 0:
self.logger.logExit("Failed to refresh static file." + output)
# Remove dynamic file, if the cluster is stopped currently.
removeDynamicCmd = "source %s; rm -f $GAUSSHOME/bin/cluster_dynamic_config" % self.envFile
for host in self.hostnames:
isLocal = False
if host == self.localhostName:
isLocal = True
executeCmdOnHost(host, removeDynamicCmd, isLocal)
clusterStopped = False
checkClusterStoppedCmd = "source %s; ls $GAUSSHOME/bin/cluster_manual_start" % self.envFile
status, output = subprocess.getstatusoutput(checkClusterStoppedCmd)
if status == 0:
clusterStopped = True
self.logger.debug("Command: " + checkClusterStoppedCmd)
self.logger.debug("Status: %s\nOtput: %s" % (status, output))
if clusterStopped:
return
status, output = InstallImpl.refreshDynamicFile(self.envFile)
if status != 0:
if "The number of master dn must equal to 1." in output:
self.logger.debug("Cann't refresh dynamic file when there is no normal primary in the current cluster.")
else:
self.logger.logExit("Failed to refresh dynamic file." + output)
def deleteData(self):
"""
remove cmdatapath
"""
if not self.bDeleteData:
return
self.logger.log("Deleting CM data path.")
self.logger.closeLog()
for host, path in zip(self.hostnames, self.cmDataPaths):
isLocal = False
if host == self.localhostName:
isLocal = True
cmd = "source %s; rm -rf %s $GAUSSLOG/cm $GAUSSHOME/share/sslcert/cm" % (self.envFile, path)
status, output = executeCmdOnHost(host, cmd, isLocal)
if status != 0:
errorDetail = "\nCommand: %s\nStatus: %s\nOutput: %s" % (cmd, status, output)
CMLog.exitWithError(("Failed to delete CM data path on host %s." % host) + errorDetail)
def deleteBinary(self):
"""
delete CM binaries, include om_monitor, cm_agent, cm_server, cm_ctl
"""
if not self.bDeleteBinary:
return
self.logger.log("Deleting CM binaries.")
for host in self.hostnames:
isLocal = False
if host == self.localhostName:
isLocal = True
cmd = "source %s; cd $GAUSSHOME/bin; rm -f om_monitor* cm_agent* cm_server* " \
"cm_ctl* cm_persist* *manual*start* promote_mode_cms; cd -" % self.envFile
status, output = executeCmdOnHost(host, cmd, isLocal)
if status != 0:
errorDetail = "\nCommand: %s\nStatus: %s\nOutput: %s" % (cmd, status, output)
self.logger.logExit(("Failed to delete CM binaries on host %s." % host) + errorDetail)
def initLogger(self):
gaussLog = getEnvParam(self.envFile, "GAUSSLOG")
logPath = os.path.join(gaussLog, "cm", "cm_tool")
if not os.path.exists(logPath):
os.makedirs(logPath)
self.logger = CMLog(logPath, module="cm_uninstall", prefix="cm_uninstall")
def checkExeUser(self):
if os.getuid() == 0:
CMLog.exitWithError(ErrorCode.GAUSS_501["GAUSS_50105"])
def checkHostTrust(self):
checkHostsTrust(self.hostnames)
def run(self):
self.checkExeUser()
self.parseCommandLine()
self.checkParam()
self.checkXMLContainCMInfo()
self.initLogger()
self.getHostnames()
self.checkHostTrust()
self.logger.log("Start to uninstall CM.")
self.getCMDataPaths()
self.cancleMonitorCrontab()
self.stopCMProcess()
self.refreshStaticAndDynamicFile()
self.deleteBinary()
self.deleteData()
if self.bDeleteData:
self.logger.printMessage("Uninstall CM tool success.")
else:
self.logger.logExit("Uninstall CM tool success.")
if __name__ == "__main__":
unInstall = UnIntall()
unInstall.run()