#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#############################################################################
# Copyright (c) 2025 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_preupgradechk is a utility to check system resoures before upgrade.
#############################################################################
import os
import sys
import subprocess
from gspylib.threads.SshTool import SshTool
from gspylib.common.GaussLog import GaussLog
from gspylib.common.Common import DefaultValue
from gspylib.common.OMCommand import OMCommand
from gspylib.common.DbClusterInfo import dbClusterInfo
from gspylib.common.ErrorCode import ErrorCode
from gspylib.common.ParameterParsecheck import Parameter
from base_utils.os.net_util import NetUtil
from base_utils.os.user_util import UserUtil
from base_utils.os.env_util import EnvUtil
from base_utils.security.security_checker import SecurityChecker
from domain_utils.domain_common.cluster_constants import ClusterConstants
from domain_utils.cluster_file.cluster_log import ClusterLog
# check item
CHECK_ITEMNUMLIST = ["A1", "A2", "A3", "A4", "A5", "A6", "A7"]
ACTION_CHECK_ALL = "Check_All"
ACTION_CHECK_CPU = "Check_Cpu"
ACTION_CHECK_MEM = "Check_Mem"
ACTION_CHECK_DISK = "Check_Disk"
ACTION_CHECK_PROCESS = "Check_Process"
ACTION_CHECK_NETWORK = "Check_Network"
ACTION_CHECK_DATABASE = "Check_Database"
ACTION_CHECK_REPLAYGAY = "Check_Replaygay"
DEFAULT_INTERVAL = 60
DEFAULT_TIMEOUT = 50
g_pre_upgrade_check_opts = {
"A1": ["Checking items", "[ cpu used status ]", "Normal", "OK"],
"A2": ["Checking items", "[ mem used status ]", "Normal", "OK"],
"A3": ["Checking items", "[ disk used status ]", "Normal", "OK"],
"A4": ["Checking items", "[ process status ]", "Normal", "OK"],
"A5": ["Checking items", "[ network status ]", "Normal", "OK"],
"A6": ["Checking items", "[ database status ]", "Normal", "OK"],
"A7": ["Checking items", "[ replaygay status ]", "Normal", "OK"],
}
class CmdOptions:
"""
command-line options
"""
def __init__(self):
# initialize variable
self.user = ""
self.nodes = []
self.local_mode = False
self.is_single = False
self.log_file = ""
self.local_log = ""
self.conf_file = ""
self.item_str = ""
self.hostname_list = []
self.item_detail = []
self.is_dss = False
def usage():
"""
gs_preupgradechk is a utility to check system resoures before upgrade.
Usage:
gs_preupgradechk -? | --help
gs_preupgradechk -V | --version
gs_preupgradechk -i ITEM [-l LOGFILE]
General options:
-i Item number. To check all items, enter "-i A"
-l Path of log file.
-?, --help Show help information for this utility, and exit the command line mode.
-V --version Show version information.
Item number description:
'A1':[ cpu idle status ]
'A2':[ mem used status ]
'A3':[ disk used status ]
'A4':[ process status ]
'A5':[ network status ]
'A6':[ database status ]
'A7':[ replaygay status ]
"""
print(usage.__doc__)
def parse_command_lines():
"""
function: Parse command line and save to global variable
input : NA
output: NA
"""
global g_opts
g_opts = CmdOptions()
ParaObj = Parameter()
ParaDict = ParaObj.ParameterCommandLine("preupgradecheck")
if ParaDict.__contains__("helpFlag"):
usage()
sys.exit(0)
if ParaDict.__contains__("logFile"):
g_opts.log_file = ParaDict.get("logFile")
if ParaDict.__contains__("itemstr"):
g_opts.item_str = ParaDict.get("itemstr")
def check_parameter():
"""
Check parameter from command line
"""
get_user_info()
check_items()
parse_item_opts(g_opts.item_str)
is_dss_mode()
def is_dss_mode():
"""
function: is dss mode
input: NA
output: NA
"""
if EnvUtil.getEnv("DSS_HOME"):
g_opts.is_dss = True
def get_user_info():
"""
function: get user
input: NA
output: NA
"""
if os.getuid() != 0:
user_info = UserUtil.getUserInfo()
g_opts.user = user_info.get("name")
DefaultValue.checkPathVaild(g_opts.user)
def check_items():
"""
check items
input: NA
output: NA
"""
if g_opts.item_str == "":
GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50001"] % "i" + ".")
def parse_item_opts(item_list):
"""
parse item
input: item list
output: NA
"""
# Flatten list by splitting comma-separated values
items = []
for item in item_list:
items.extend(item.split(","))
# Process each item
for item in items:
item = item.strip().upper()
# Validate item is either in CHECK_ITEMNUMLIST or is 'A'
if item not in CHECK_ITEMNUMLIST and item != "A":
GaussLog.exitWithError(ErrorCode.GAUSS_500["GAUSS_50004"] % "i")
# Add unique valid items to detail list
if item not in g_opts.item_detail:
g_opts.item_detail.append(item)
def init_globals():
"""
init the global parameter g_logger and g_sshTool
"""
global g_logger
global g_sshTool
global g_clusterInfo
if g_opts.log_file == "":
g_opts.log_file = ClusterLog.getOMLogPath(
ClusterConstants.GS_PREUPGRADECHK_LOG_FILE, g_opts.user
)
g_logger = GaussLog(g_opts.log_file, ClusterConstants.GS_PREUPGRADECHK_LOG_FILE)
dir_name = os.path.dirname(g_opts.log_file)
g_opts.local_log = os.path.join(dir_name, ClusterConstants.LOCAL_LOG_FILE)
g_clusterInfo = dbClusterInfo()
g_clusterInfo.initFromStaticConfig(g_opts.user)
g_opts.hostname_list = g_clusterInfo.getClusterNodeNames()
if (
len(g_opts.hostname_list) == 1
and g_opts.hostname_list[0] == NetUtil.GetHostIpOrName()
):
g_opts.local_mode = True
if not g_opts.local_mode:
g_sshTool = SshTool(
g_opts.hostname_list, g_logger.logFile, timeout=DEFAULT_TIMEOUT
)
def check_system_resource(resource_type):
"""
Check system resource usage.
Args:
resource_type (str): Type of resource to check ('cpu', 'mem', 'disk', 'process')
Returns:
bool: True if check passed, False otherwise
"""
if resource_type == "A":
check_all()
elif resource_type == "A1":
check_cpu_usage()
elif resource_type == "A2":
check_mem_usage()
elif resource_type == "A3":
check_disk_usage()
elif resource_type == "A4":
check_process_usage()
elif resource_type == "A5":
check_network_usage()
elif resource_type == "A6":
check_database()
elif resource_type == "A7":
check_replaygay()
def format_check_result(raw_result):
lines = raw_result.strip().splitlines()
output = []
node = None
for line in lines:
if line.startswith("[") and line.endswith("]"):
node = line
output.append("\n" + node)
else:
if "[" in line and "]" in line:
msg, status = line.rsplit("[", 1)
status = status.rstrip("]")
output.append(f" {msg.strip():<55} [{status}]")
else:
output.append(f" {line}")
return "\n".join(output).lstrip()
def check_all():
g_logger.log("Checking all item")
try:
failed_flag = False
result_msg = ""
cmd = "%s -t %s -U %s -l %s" % (
Local_Check_Pre_Upgrade,
ACTION_CHECK_ALL,
g_opts.user,
g_opts.local_log,
)
(status, output, output_map) = get_cmd_output(cmd)
g_logger.debug(
"The cmd is %s\n output is %s\n output_map is %s"
% (cmd, output, output_map)
)
for node in list(status.keys()):
if status[node] != DefaultValue.SUCCESS:
g_logger.logExit(
"[%s]: \n" % node
+ ErrorCode.GAUSS_516["GAUSS_51632"] % cmd
+ " Error: \n%s" % output_map[node].strip()
)
else:
lines = output_map[node].strip().split("\n")
lines.sort()
result_msg += "[%s]\n" % node
for line in lines:
if "," in line:
res_status, res_output = line.split(",", 1)
res_status = res_status.strip()
res_output = res_output.strip()
else:
res_status = "Normal"
res_output = line.strip()
result_msg += " %s [%s]\n" % (res_output, res_status)
if res_status == "Error":
failed_flag = True
result_msg = format_check_result(result_msg)
if failed_flag:
g_logger.log(
"All items have been completed, but there have been failures.Error: \n%s"
% result_msg
)
else:
g_logger.log("Successfully checked all item, result is \n%s" % result_msg)
except Exception as e:
g_logger.debug(str(e))
def check_resource_usage(action, item_key, log_msg):
g_logger.log(log_msg)
try:
detail_msg = ""
cmd = "%s -t %s -U %s -l %s" % (
Local_Check_Pre_Upgrade,
action,
g_opts.user,
g_opts.local_log,
)
(status, output, output_map) = get_cmd_output(cmd)
g_logger.debug(
"The cmd is %s\n output is %s\n output_map is %s"
% (cmd, output, output_map)
)
for node in status:
if status[node] != DefaultValue.SUCCESS:
g_logger.logExit(
"[%s]: \n%s Error: \n%s"
% (
node,
ErrorCode.GAUSS_516["GAUSS_51632"] % cmd,
output_map[node].strip(),
)
)
else:
lines = [line for line in output_map[node].split("\n") if line.strip()]
detail_msg += "[%s]\n" % node
for line in lines:
res_status, res_output = line.split(",", 1)
detail_msg += "%s [%s]\n" % (res_output.strip(), res_status)
if "Error" in detail_msg:
g_pre_upgrade_check_opts[item_key][2] = "Error"
elif "Warning" in detail_msg:
g_pre_upgrade_check_opts[item_key][2] = "Warning"
else:
g_pre_upgrade_check_opts[item_key][2] = "Normal"
g_pre_upgrade_check_opts[item_key][3] = "\n%s" % detail_msg
except Exception as e:
g_pre_upgrade_check_opts[item_key][2] = "Error"
g_pre_upgrade_check_opts[item_key][3] = "\n%s" % str(e)
g_logger.debug(str(e))
g_logger.log("Successfully checked %s." % log_msg.lower())
def check_cpu_usage():
check_resource_usage(ACTION_CHECK_CPU, "A1", "CPU usage status")
def check_mem_usage():
check_resource_usage(ACTION_CHECK_MEM, "A2", "mem usage status")
def check_disk_usage():
check_resource_usage(ACTION_CHECK_DISK, "A3", "disk usage status")
def check_process_usage():
check_resource_usage(ACTION_CHECK_PROCESS, "A4", "process usage status")
def check_network_usage():
check_resource_usage(ACTION_CHECK_NETWORK, "A5", "network usage status")
def check_database():
check_resource_usage(ACTION_CHECK_DATABASE, "A6", "database usage status")
def check_replaygay():
check_resource_usage(ACTION_CHECK_REPLAYGAY, "A7", "replaygay usage status")
def get_cmd_output(cmd):
"""
function: execute the cmd and get the output
input: cmd
output:status, output, outputMap
"""
local_host = NetUtil.GetHostIpOrName()
status = {}
output_map = {}
output = ""
try:
if g_opts.local_mode == True:
result = subprocess.run(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
timeout=DEFAULT_TIMEOUT,
)
if result.returncode != 0:
status[local_host] = "Failure"
output = result.stderr
else:
status[local_host] = "Success"
output = result.stdout
output_map[local_host] = output
else:
gp_path = os.path.dirname(os.path.realpath(__file__))
(status, output) = g_sshTool.getSshStatusOutput(
cmd, [], "", "%s/../" % gp_path
)
output_map = g_sshTool.parseSshOutput(g_opts.hostname_list)
return (status, output, output_map)
except Exception as e:
g_logger.debug(str(e))
output = str(e)
status = {}
output_map = {}
if "Timed out" not in output:
raise Exception(output)
for line in output.split("\n"):
if "Timed out" not in line or not line.startswith("["):
continue
parts = line.split()
if len(parts) < 4:
continue
ip_or_name = parts[3]
if SecurityChecker.check_is_ip(ip_or_name):
ip_or_name = get_name_by_ip(ip_or_name)
status[ip_or_name] = "Success"
output_map[ip_or_name] = (
"Error, Timed out, please check the network or ssh connection!"
)
return (status, output, output_map)
def get_name_by_ip(ip):
for dbinfo in g_clusterInfo.dbNodes:
if dbinfo.backIps[0] == ip:
return dbinfo.name
return ""
def display_result_information(item):
"""
Display formatted check result information.
Args:
Item: The check item identifier (e.g. 'A1', 'A2')
output: Output file object or sys.stdout
"""
def format_item_line(item):
"""Helper function to format a single item line"""
# Format the item identifier and description
item_header = (
"%s%s" % (("%s." % item).ljust(4), g_pre_upgrade_check_opts[item][1])
).ljust(DEFAULT_INTERVAL)
# Get the appropriate detail column based on mode
detail_col = g_pre_upgrade_check_opts[item][3]
title = "%s:\n " % g_pre_upgrade_check_opts[item][0]
return "%s%s: %s %s" % (
title,
item_header,
g_pre_upgrade_check_opts[item][2].ljust(10),
detail_col.ljust(DEFAULT_INTERVAL),
)
print(format_item_line(item))
def main():
"""
main function
"""
if os.getuid() == 0:
GaussLog.exitWithError(ErrorCode.GAUSS_501["GAUSS_50105"])
try:
parse_command_lines()
check_parameter()
init_globals()
global Local_Check_Pre_Upgrade
Local_Check_Pre_Upgrade = OMCommand.getLocalScript("Local_Check_Pre_Upgrade")
except Exception as e:
GaussLog.exitWithError(str(e))
if "A" in g_opts.item_detail:
item_list = "A"
else:
sort_list = sorted(g_opts.item_detail)
item_list = sort_list
try:
if "A" == item_list:
check_system_resource(item_list)
else:
for item in item_list:
check_system_resource(item)
display_result_information(item)
except Exception as ex:
g_logger.logExit("Error: %s" % str(ex))
if __name__ == "__main__":
main()