317 lines
11 KiB
Python
317 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding:utf-8 -*-
|
|
#############################################################################
|
|
# Copyright (c) 2023 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_perfconfg is a utility to optimize system and database configure about openGauss
|
|
#############################################################################
|
|
|
|
import os
|
|
import sys
|
|
import getopt
|
|
import logging
|
|
from base_utils.common.dialog import DialogUtil
|
|
from impl.perf_config.basic.project import Project, PorjectError, ProjectLogLevel
|
|
from impl.perf_config.basic.anti import AntiLog
|
|
from impl.perf_config.preset.preset import Preset
|
|
from impl.perf_config.perf_probe import PerfProbe
|
|
from impl.perf_config.perf_tuner import PerfTuneTarget, PerfTuner
|
|
|
|
|
|
def usage():
|
|
Project.msg("""
|
|
Usage of gs_perfconfig:
|
|
gs_perfconfig [ACTION [ ACTION-PARAM ] ]
|
|
|
|
Action, action-params and functions are as follows:
|
|
help (No params) :
|
|
Display help document of gs_perfconfig. '--help', '-h', '-?' are also effective.
|
|
|
|
tune [ -t [ all,os,setup,guc,suggest ] ] [ --apply ] [--env envfile] [ -y ]:
|
|
'-t' include four tuning target modules:
|
|
os : (only root) Including os parameters, software, hardware configurations.
|
|
setup : Including database setup configuration.
|
|
guc : Including database GUC configuration.
|
|
suggest : Give some additional suggestions.
|
|
'all' indicates all modules, but 'os' depend on the executor.
|
|
'--apply' indicates that the actual application is adjusted, and the database may need
|
|
to be restarted multiple times during this period. Otherwise, only one report is generated.
|
|
'--env' specifies the environment variables file.
|
|
'-y' Accept the requirements for configuring operating system parameters, GUC parameters,
|
|
and restarting the database during adjustment.
|
|
|
|
When the root user is used to execute the process, the process is completely executed from
|
|
the beginning and certain information is generated. When the user executes the command, the
|
|
system first attempts to load the information left by the root user, and then skips certain
|
|
processes.
|
|
|
|
preset [ --help | (No params) | preset-name | ]
|
|
Print the preset addition guide, all preset list or specify the contents of the preset.
|
|
|
|
recover [ -y ] :
|
|
Recover the content of the last tuning.
|
|
'-y' Accept the requirements for configuring operating system parameters, GUC parameters,
|
|
and restarting the database during adjustment.
|
|
|
|
|
|
The environment variable GS_PERFCONFIG_OPTIONS sets some behavior within the tool.
|
|
export GS_PERFCONFIG_OPTIONS='param1=value1:param2=value2:param3=value3'
|
|
|
|
params and values:
|
|
|
|
lowest_print_log_level = log/notice/warning/error/fatal. The lowest level of print logs, default is notice.
|
|
You can also set 'msg', but it will not take effect.
|
|
""")
|
|
|
|
|
|
class TuneTask(object):
|
|
"""
|
|
This is the tune task.
|
|
Responsible for controlling the flow of the entire adjustment task.
|
|
The operations include environment initialization, performance data detection,
|
|
adjustment process, and rollback process.
|
|
"""
|
|
def __init__(self, argv):
|
|
self.accept_risk = False
|
|
target, apply, env = self._parse_arg(argv)
|
|
self.tune_target = PerfTuneTarget(target, apply)
|
|
if self.tune_target.noTarget():
|
|
return
|
|
|
|
# environment initialization
|
|
Project.initEnviron(env, True)
|
|
Project.initRole()
|
|
Project.initProjectLog(Project.environ.run_log)
|
|
Project.prepareReport(Project.environ.report)
|
|
Project.log(Project.environ.__str__())
|
|
if self.tune_target.apply():
|
|
AntiLog.initAntiLog(Project.environ.anti_log)
|
|
Project.setTask(self)
|
|
|
|
def _parse_arg(self, argv):
|
|
short_options = 't:y'
|
|
long_options = ['apply', 'env=']
|
|
opts, args = getopt.getopt(argv, short_options, long_options)
|
|
if len(args) > 1:
|
|
Project.fatal('unknown param ' + args[0])
|
|
target = 'all'
|
|
apply = False
|
|
env = None
|
|
for opt, arg in opts:
|
|
if opt == '-t':
|
|
target = arg
|
|
elif opt == '--apply':
|
|
apply = True
|
|
elif opt == '--env':
|
|
env = arg
|
|
elif opt == '-y':
|
|
self.accept_risk = True
|
|
else:
|
|
Project.fatal('unknown param ' + opt)
|
|
return target, apply, env
|
|
|
|
def run(self):
|
|
"""
|
|
Task details. Control the entire tune process.
|
|
"""
|
|
if self.tune_target.noTarget():
|
|
Project.notice('nothing to tune.')
|
|
return
|
|
do_apply = self.tune_target.apply()
|
|
# 1, Operation content and risk tips.
|
|
self.risk_disclosure()
|
|
|
|
# 2, start probe
|
|
Project.notice('Start probe detect.')
|
|
infos = PerfProbe()
|
|
Project.setGlobalPerfProbe(infos)
|
|
infos.detect()
|
|
|
|
# 3, shutdown openGauss if necessary
|
|
og_alive_at_first = Project.isOpenGaussAlive()
|
|
if og_alive_at_first and do_apply:
|
|
Project.stopOpenGauss()
|
|
|
|
# 4, tune and apply, and rollback apply when errors.
|
|
error_occurred = False
|
|
Project.notice('Prepare tune plan.')
|
|
tuner = PerfTuner()
|
|
Project.setGlobalPerfTuner(tuner)
|
|
tuner.calculate()
|
|
|
|
try:
|
|
Project.notice('execute tune plan({0})...'.format(
|
|
'report and apply' if do_apply else 'just report'))
|
|
tuner.explain(do_apply)
|
|
Project.report.dump()
|
|
Project.notice('Tune finish.')
|
|
|
|
except PorjectError:
|
|
error_occurred = True
|
|
except Exception as e:
|
|
Project.notice('Some errors have occurred.')
|
|
Project.notice(e.__str__())
|
|
logging.exception(e)
|
|
error_occurred = True
|
|
|
|
if error_occurred and do_apply:
|
|
Project.notice('start rollback.')
|
|
PerfTuner.rollback(None)
|
|
|
|
# 5, start og if necessary.
|
|
if og_alive_at_first and do_apply:
|
|
Project.startOpenGauss()
|
|
|
|
def risk_disclosure(self):
|
|
question = ('Certainly, we will perform some stress tests, make adjustments to the \n'
|
|
'configuration of operating system parameters, database parameters, and \n'
|
|
'also perform a database restart during the optimization process. \n'
|
|
'Are you accepting of the aforementioned circumstances?')
|
|
Project.log('risk disclosure: ' + question)
|
|
if self.accept_risk:
|
|
Project.notice(f'user choose yes by "-y" on the question:\n ({question}).')
|
|
else:
|
|
ok = DialogUtil.yesOrNot(question)
|
|
Project.log('user choose: ' + ('y' if ok else 'n'))
|
|
if not ok:
|
|
Project.fatal('The user has chosen to cancel the operation.')
|
|
|
|
|
|
class PresetTask(object):
|
|
"""
|
|
This is the preset task.
|
|
Responsible for showing how many presets there are, the details of the presets,
|
|
and showing how to edit the presets.
|
|
"""
|
|
def __init__(self, argv):
|
|
Project.set_lowest_print_level(ProjectLogLevel.WARNING)
|
|
Project.initEnviron(None, False)
|
|
Project.reset_lowest_print_level()
|
|
|
|
self.target_preset = None if (argv is None or len(argv) == 0) else argv[0]
|
|
if len(argv) > 1:
|
|
Project.fatal('unknown param {}'.format(argv[1]))
|
|
|
|
def run(self):
|
|
if self.target_preset is None:
|
|
self._show_all_presets()
|
|
elif self.target_preset.lower() in ['help', '--help', '-h', '-?']:
|
|
self._show_preset_usage()
|
|
else:
|
|
self._show_one_preset(self.target_preset)
|
|
|
|
def _show_all_presets(self):
|
|
builtins, usersets = Preset.get_all_presets()
|
|
|
|
msg = 'Builtin Presets:\n'
|
|
for preset in builtins:
|
|
msg += f' {preset}\n'
|
|
Project.msg(msg)
|
|
|
|
msg = 'User Presets:\n'
|
|
for preset in usersets:
|
|
msg += f' {preset}\n'
|
|
Project.msg(msg)
|
|
|
|
def _show_one_preset(self, name):
|
|
preset = Preset(name)
|
|
Project.msg(preset.__str__())
|
|
|
|
def _show_preset_usage(self):
|
|
preset_usage = Preset.usage()
|
|
Project.msg(preset_usage)
|
|
|
|
|
|
class RecoverTask(object):
|
|
"""
|
|
This is a recovery mission
|
|
Read the anti log, undo the last adjustment, and restore the environment
|
|
to the way it was before the tune. Only the most recent adjustment can be undone.
|
|
"""
|
|
def __init__(self, argv):
|
|
self.accept_risk = False
|
|
if len(argv) > 0:
|
|
if len(argv) == 1 and argv[0] == '-y':
|
|
self.accept_risk = True
|
|
else:
|
|
Project.fatal('invalid param {}'.format(' '.join(argv)))
|
|
|
|
Project.initEnviron()
|
|
Project.initRole()
|
|
Project.initProjectLog(Project.environ.run_log)
|
|
Project.prepareReport(Project.environ.report)
|
|
Project.log(Project.environ.__str__())
|
|
|
|
AntiLog.initAntiLog(Project.environ.anti_log, reload=True)
|
|
|
|
def run(self):
|
|
Project.notice('start rollback.')
|
|
self.risk_disclosure()
|
|
|
|
og_is_alive_first = Project.isOpenGaussAlive()
|
|
if og_is_alive_first:
|
|
Project.stopOpenGauss()
|
|
|
|
PerfTuner.rollback(None)
|
|
|
|
Project.notice('Destroy anti log.')
|
|
AntiLog.destroyAntiLog()
|
|
|
|
if og_is_alive_first:
|
|
Project.startOpenGauss()
|
|
|
|
Project.notice('rollback finish.')
|
|
|
|
def risk_disclosure(self):
|
|
question = ('Certainly, we will make adjustments to the configuration of operating \n'
|
|
'system parameters, database parameters, and also perform a database \n'
|
|
'restart during the recover process.\n'
|
|
'Are you accepting of the aforementioned circumstances?')
|
|
Project.log('risk disclosure: ' + question)
|
|
if self.accept_risk:
|
|
ok = True
|
|
Project.notice(f'user choose yes by "-y" on the question:\n ({question}).')
|
|
else:
|
|
ok = DialogUtil.yesOrNot(question)
|
|
Project.log('user choose: ' + ('y' if ok else 'n'))
|
|
|
|
if not ok:
|
|
Project.fatal('The user has chosen to cancel the recover.')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
task = None
|
|
|
|
if len(sys.argv) == 1 or sys.argv[1].lower() in ['help', '--help', '-h', '-?']:
|
|
usage()
|
|
exit(0)
|
|
|
|
elif sys.argv[1] == 'tune':
|
|
task = TuneTask(sys.argv[2:])
|
|
|
|
elif sys.argv[1] == 'preset':
|
|
task = PresetTask(sys.argv[2:])
|
|
|
|
elif sys.argv[1] == 'recover':
|
|
task = RecoverTask(sys.argv[2:])
|
|
|
|
else:
|
|
Project.fatal('Unknown task: ' + sys.argv[1])
|
|
|
|
task.run()
|
|
|
|
|