diff --git a/_cmd.py b/_cmd.py index 5949ec3..4e488ac 100644 --- a/_cmd.py +++ b/_cmd.py @@ -38,9 +38,9 @@ import _environ as ENV from ssh import LocalClient from const import ( - CONST_OBD_HOME, CONST_OBD_INSTALL_PATH, CONST_OBD_INSTALL_PRE, + CONST_OBD_HOME, VERSION, REVISION, BUILD_BRANCH, BUILD_TIME, FORBIDDEN_VARS, - COMP_OCEANBASE_DIAGNOSTIC_TOOL + COMP_OCEANBASE_DIAGNOSTIC_TOOL, PKG_RPM_FILE,PKG_REPO_FILE ) @@ -186,8 +186,8 @@ def _mk_usage(self): class ObdCommand(BaseCommand): OBD_PATH = OBD_HOME_PATH - OBD_INSTALL_PRE = os.environ.get(CONST_OBD_INSTALL_PRE, '/') - OBD_INSTALL_PATH = os.environ.get(CONST_OBD_INSTALL_PATH, os.path.join(OBD_INSTALL_PRE, 'usr/obd/')) + OBD_INSTALL_PRE = COMMAND_ENV.get(ENV.ENV_OBD_INSTALL_PRE, '/') + OBD_INSTALL_PATH = COMMAND_ENV.get(ENV.ENV_OBD_INSTALL_PATH, os.path.join(OBD_INSTALL_PRE, 'usr/obd/')) def init_home(self): version_path = os.path.join(self.OBD_PATH, 'version') @@ -612,7 +612,23 @@ def __init__(self): def _do_command(self, obd): url = self.cmds[0] return obd.mirror_manager.add_repo(url) - + + +class MirrorCleanPkgCommand(ObdCommand): + + def __init__(self): + super(MirrorCleanPkgCommand, self).__init__('clean', 'After the list of files to be deleted is displayed, double confirm and then clean up them.') + self.parser.add_option('-y', '--confirm', action='store_true', help="confirm to clean up.") + self.parser.add_option('-c', '--components', type='string', help="Clean up specified components. Separate multiple components with `,`.") + self.parser.add_option('-t', '--type', type='string', help="Specify the file types to be deleted as '%s or %s'." % (PKG_RPM_FILE, PKG_REPO_FILE)) + self.parser.add_option('--hash', type='string', help="Repository's md5") + + def _do_command(self, obd): + if self.opts.type and self.opts.type not in [PKG_RPM_FILE, PKG_REPO_FILE]: + ROOT_IO.error("Invalid type specified. Please specify '%s' or '%s'." % (PKG_RPM_FILE, PKG_REPO_FILE)) + return False + return obd.clean_pkg(self.opts) + class MirrorMajorCommand(MajorCommand): @@ -625,6 +641,7 @@ def __init__(self): self.register_command(MirrorEnableCommand()) self.register_command(MirrorDisableCommand()) self.register_command(MirrorAddRepoCommand()) + self.register_command(MirrorCleanPkgCommand()) class RepositoryListCommand(ObdCommand): @@ -750,7 +767,7 @@ class DemoCommand(ClusterMirrorCommand): def __init__(self): super(DemoCommand, self).__init__('demo', 'Quickly start') - self.parser.add_option('-c', '--components', type='string', help="List the components. Multiple components are separated with commas. [oceanbase-ce,obproxy-ce,obagent,prometheus,grafana]\nExample: \nstart oceanbase-ce: obd demo -c oceanbase-ce\n" + self.parser.add_option('-c', '--components', type='string', help="List the components. Multiple components are separated with commas. [oceanbase-ce,obproxy-ce,obagent,prometheus,grafana,ob-configserver]\nExample: \nstart oceanbase-ce: obd demo -c oceanbase-ce\n" + "start -c oceanbase-ce V3.2.3: obd demo -c oceanbase-ce --oceanbase-ce.version=3.2.3\n" + "start oceanbase-ce and obproxy-ce: obd demo -c oceanbase-ce,obproxy-ce", default='oceanbase-ce,obproxy-ce,obagent,prometheus,grafana') self.parser.allow_undefine = True @@ -1113,6 +1130,7 @@ def __init__(self): self.parser.add_option('--primary-zone', type='string', help="Tenant primary zone. [RANDOM].", default='RANDOM') self.parser.add_option('--locality', type='string', help="Tenant locality.") self.parser.add_option('-s', '--variables', type='string', help="Set the variables for the system tenant. [ob_tcp_invited_nodes='%'].", default="ob_tcp_invited_nodes='%'") + self.parser.add_option('-o', '--optimize', type='string', help="Specify scenario optimization when creating a tenant, the default is consistent with the cluster dimension.\n{express_oltp, complex_oltp, olap, htap, kv}\nSupported since version 4.3.") def _do_command(self, obd): if len(self.cmds) == 1: @@ -1237,6 +1255,24 @@ def _do_command(self, obd): return self._show_help() +class ClusterTenantOptimizeCommand(ClusterMirrorCommand): + + def __init__(self): + super(ClusterTenantOptimizeCommand, self).__init__('optimize','Optimizing existing tenant scenarios') + self.parser.add_option('-o', '--optimize', type='string', help='Optimize scenarios,the default is consistent with the cluster dimension.\n{express_oltp, complex_oltp, olap, htap, kv}\nSupported since version 4.3.') + + def init(self, cmd, args): + super(ClusterTenantOptimizeCommand, self).init(cmd, args) + self.parser.set_usage('%s [options]' % self.prev_cmd) + return self + + def _do_command(self, obd): + if len(self.cmds) == 2: + return obd.tenant_optimize(self.cmds[0], self.cmds[1]) + else: + return self._show_help() + + class ClusterTenantCommand(MajorCommand): def __init__(self): @@ -1248,6 +1284,7 @@ def __init__(self): self.register_command(ClusterTenantSwitchoverCommand()) self.register_command(ClusterTenantFailoverCommand()) self.register_command(ClusterTenantDecoupleCommand()) + self.register_command(ClusterTenantOptimizeCommand()) class ClusterMajorCommand(MajorCommand): @@ -1698,6 +1735,7 @@ def __init__(self): self.register_command(ObdiagGatherPlanMonitorCommand()) self.register_command(ObdiagGatherObproxyLogCommand()) self.register_command(ObdiagGatherSceneCommand()) + self.register_command(ObdiagGatherAshReportCommand()) class ObdiagGatherSceneCommand(MajorCommand): @@ -1862,7 +1900,7 @@ def __init__(self): super(ObdiagGatherPlanMonitorCommand, self).__init__('plan_monitor', 'Gather ParalleSQL information') self.parser.add_option('--trace_id', type='string', help='sql trace id') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') - self.parser.add_option('--env', type='string', help='env, eg: "{env1=xxx, env2=xxx}"') + self.parser.add_option('--env', type='string', help='''env, eg: "{db_connect='-h127.0.0.1 -P2881 -utest@test -p****** -Dtest'}"''') self.parser.add_option('--obdiag_dir', type='string', help="obdiag install dir",default=OBDIAG_HOME_PATH) @@ -1881,7 +1919,7 @@ def __init__(self): self.parser.add_option('--from', type='string', help="specify the start of the time range. format: yyyy-mm-dd hh:mm:ss") self.parser.add_option('--to', type='string', help="specify the end of the time range. format: yyyy-mm-dd hh:mm:ss") self.parser.add_option('--since', type='string', help="Specify time range that from 'n' [d]ays, 'n' [h]ours or 'n' [m]inutes. before to now. format: . example: 1h.",default='30m') - self.parser.add_option('--scope', type='string', help="log type constrains, choices=[observer, election, rootservice, all]",default='all') + self.parser.add_option('--scope', type='string', help="log type constrains, choices=[obproxy, obproxy_limit, obproxy_stat, obproxy_digest, obproxy_slow, obproxy_diagnosis, obproxy_error, all]", default='all') self.parser.add_option('--grep', type='string', help="specify keywords constrain") self.parser.add_option('--encrypt', type='string', help="Whether the returned results need to be encrypted, choices=[true, false]", default="false") self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') @@ -1915,7 +1953,6 @@ def __init__(self): self.parser.add_option('--to', type='string', help="specify the end of the time range. format: yyyy-mm-dd hh:mm:ss") self.parser.add_option('--since', type='string', help="Specify time range that from 'n' [d]ays, 'n' [h]ours or 'n' [m]inutes. before to now. format: . example: 1h.",default='30m') self.parser.add_option('--env', type='string', help='env, eg: "{env1=xxx, env2=xxx}"') - self.parser.add_option('--dis_update', type='string', help='The type is bool, assigned any value representing true', default='true') self.parser.add_option('--store_dir', type='string', help='the dir to store gather result, current dir by default.', default='./') self.parser.add_option('--obdiag_dir', type='string', help="obdiag install dir",default=OBDIAG_HOME_PATH) @@ -1935,6 +1972,41 @@ def _do_command(self, obd): return self._show_help() +class ObdiagGatherAshReportCommand(ObdCommand): + + def __init__(self): + super(ObdiagGatherAshReportCommand, self).__init__('ash', 'Gather ash report') + self.parser.add_option('--trace_id', type='string', + help="The TRACE.ID of the SQL to be sampled, if left blank or filled with NULL, indicates that TRACE.ID is not restricted.") + self.parser.add_option('--sql_id', type='string', + help="The SQL.ID, if left blank or filled with NULL, indicates that SQL.ID is not restricted.") + self.parser.add_option('--wait_class', type='string', + help='Event types to be sampled.') + self.parser.add_option('--report_type', type='string', + help='Report type, currently only supports text type.', default='TEXT') + self.parser.add_option('--from', type='string', + help="specify the start of the time range. format: 'yyyy-mm-dd hh:mm:ss'") + self.parser.add_option('--to', type='string', + help="specify the end of the time range. format: 'yyyy-mm-dd hh:mm:ss'") + self.parser.add_option('--store_dir', type='string', + help='the dir to store gather result, current dir by default.', default='./') + self.parser.add_option('--obdiag_dir', type='string', help="obdiag install dir", default=OBDIAG_HOME_PATH) + + def init(self, cmd, args): + super(ObdiagGatherAshReportCommand, self).init(cmd, args) + self.parser.set_usage('%s [options]' % self.prev_cmd) + return self + + @property + def lock_mode(self): + return LockMode.NO_LOCK + + def _do_command(self, obd): + if len(self.cmds) > 0: + return obd.obdiag_online_func(self.cmds[0], "gather_ash", self.opts) + else: + return self._show_help() + class ObdiagAnalyzeMirrorCommand(ObdCommand): def init(self, cmd, args): @@ -2013,7 +2085,6 @@ def __init__(self): super(ObdiagCheckCommand, self).__init__('check', 'check oceanbase cluster') self.parser.add_option('--cases', type='string', help="The name of the check task set that needs to be executed") self.parser.add_option('--store_dir', type='string', help='ouput report path', default='./check_report/') - self.parser.add_option('--dis_update', type='string', help='The type is bool, assigned any value representing true', default='true') self.parser.add_option('--obdiag_dir', type='string', help="obdiag install dir", default=OBDIAG_HOME_PATH) def init(self, cmd, args): @@ -2048,7 +2119,7 @@ def __init__(self): super(ObdiagRcaRunCommand, self).__init__('run', 'to run root cause analysis of oceanbase problem') self.parser.add_option('--scene', type='string', help="The name of the rca scene set that needs to be executed") self.parser.add_option('--store_dir', type='string', help='ouput result path', default='./rca/') - self.parser.add_option('--parameters', type='string', help='parameters') + self.parser.add_option('--input_parameters', type='string', help='parameters') self.parser.add_option('--obdiag_dir', type='string', help="obdiag install dir", default=OBDIAG_HOME_PATH) def init(self, cmd, args): diff --git a/_deploy.py b/_deploy.py index ef51580..824f573 100644 --- a/_deploy.py +++ b/_deploy.py @@ -392,16 +392,17 @@ def _to_cluster_config(cls, component_name, conf, added_servers=None): @classmethod def merge_config(cls, component_name, config, new_config): unmergeable_keys = cls.META_KEYS + ['global', RsyncConfig.RSYNC, ENV] + for key in unmergeable_keys: + assert key not in new_config, Exception('{} is not allowed to be set in additional conf'.format(key)) servers = cls._get_servers(config.get('servers', [])) merge_servers = cls._get_servers(new_config.get('servers', [])) for server in merge_servers: assert server not in servers, Exception('{} is already in cluster'.format(server)) - new_config['servers'] = config.get('servers', []) + new_config.get('servers', []) + config['servers'] = config.get('servers', []) + new_config.get('servers', []) merge_server_names = [server.name for server in merge_servers] - for key in new_config: - assert key not in unmergeable_keys, Exception('{} is not allowed to be set in additional conf'.format(key)) - assert key in merge_server_names or key == 'servers', Exception('{} is not allowed to be set'.format(key)) - config[key] = new_config[key] + for key in merge_server_names: + if key in new_config: + config[key] = new_config[key] return cls.to_cluster_config(component_name, config, merge_servers) @classmethod @@ -656,6 +657,16 @@ def get_depend_config(self, name, server=None, with_default=True): config = cluster_config.get_server_conf(server) if server else cluster_config.get_global_conf() return deepcopy(config) + def get_be_depend_config(self, name, server=None, with_default=True): + if name not in self._be_depends: + return None + cluster_config = self._be_depends[name] + if with_default: + config = cluster_config.get_server_conf_with_default(server) if server else cluster_config.get_global_conf_with_default() + else: + config = cluster_config.get_server_conf(server) if server else cluster_config.get_global_conf() + return deepcopy(config) + def update_server_conf(self, server, key, value, save=True): if self._deploy_config is None: return False @@ -1105,7 +1116,11 @@ def get_inner_config(component_name): def added_components(self): if self._added_components: return self._added_components + elif self._changed_components or self._removed_components: + # It's means that the components have been changed, so must not be added + return [] else: + # Maybe the deploy is new, so all components are added return self.components.keys() @property @@ -1206,13 +1221,16 @@ def scale_out(self, config_path): self.stdio.error(err.EC_COMPONENT_CHANGE_CONFIG.format(key)) ret = False elif issubclass(type(source_data[key]), dict): - if source_data[key].get('depends', []): + new_depends = source_data[key].get('depends', []) + if new_depends and new_depends != self.components[key].depends: self.stdio.error(err.EC_COMPONENT_CHANGE_CONFIG.format(message='depends:{}'.format(key))) + ret = False # temp _depends depends[key] = self.components[key].depends + if not self._merge_component(key, source_data[key]): - ret = False - continue + ret = False + for comp in depends: conf = self.components[comp] for name in depends[comp]: @@ -1472,20 +1490,17 @@ def _merge_component(self, component_name, conf): global_attr = deepcopy(src_conf['inner_config'][(ComponentInnerConfig.COMPONENT_GLOBAL_ATTRS)]) try: merged_cluster_config = parser.merge_config(component_name, src_conf['config'], conf) + self._src_data[component_name] = src_conf['config'] + self._set_component(merged_cluster_config) except Exception as e: self.stdio.exception(err.EC_COMPONENT_FAILED_TO_MERGE_CONFIG.format(message=str(e))) return False - self.update_component(merged_cluster_config) + cluster_config = self.components[component_name] for k in global_attr: v = global_attr.get(k) cluster_config.update_component_attr(k, v, save=False) self._changed_components.append(component_name) - - # 更新depends config - for comp in self.components: - if component_name in self.components[comp].depends: - self.components[comp].add_depend(component_name, merged_cluster_config) return True def change_component_config_style(self, component_name, style): diff --git a/_environ.py b/_environ.py index 2485f16..8a55fc2 100644 --- a/_environ.py +++ b/_environ.py @@ -56,4 +56,10 @@ ENV_DISABLE_RSA_ALGORITHMS = 'OBD_DISABLE_RSA_ALGORITHMS' # set local connection when using host ip. {0/1} 0 - no local connection. 1 - local connection. -ENV_HOST_IP_MODE = "HOST_IP_MODE" \ No newline at end of file +ENV_HOST_IP_MODE = "HOST_IP_MODE" + +# obdeploy install pre path. default / +ENV_OBD_INSTALL_PRE = "OBD_INSTALL_PRE" + +# obdeploy install path. default /usr/obd/ +ENV_OBD_INSTALL_PATH = "OBD_INSTALL_PATH" \ No newline at end of file diff --git a/_errno.py b/_errno.py index 856f529..39a00ee 100644 --- a/_errno.py +++ b/_errno.py @@ -166,6 +166,7 @@ class InitDirFailedErrorMessage(object): EC_OBSERVER_SYS_MEM_TOO_LARGE = OBDErrorCodeTemplate(2010, '({server}): system_memory too large. system_memory must be less than memory_limit/memory_limit_percentage.') EC_OBSERVER_GET_MEMINFO_FAIL = OBDErrorCodeTemplate(2011, "{server}: fail to get memory info.\nPlease configure 'memory_limit' manually in configuration file") EC_OBSERVER_FAIL_TO_START_OCS = OBDErrorCodeTemplate(2012, 'Failed to start {server} obshell') +EC_OBSERVER_UNKONE_SCENARIO = OBDErrorCodeTemplate(2013, 'Unknown scenario: {scenario}') WC_OBSERVER_SYS_MEM_TOO_LARGE = OBDErrorCodeTemplate(2010, '({server}): system_memory too large. system_memory should be less than {factor} * memory_limit/memory_limit_percentage.') @@ -195,9 +196,8 @@ class InitDirFailedErrorMessage(object): EC_OCP_EXPRESS_NOT_ENOUGH_MEMORY_CACHED = OBDErrorCodeTemplate(4302, '({ip}) not enough memory. (Free: {free}, Buff/Cache: {cached}, Need: {need})') EC_OCP_EXPRESS_NOT_ENOUGH_DISK = OBDErrorCodeTemplate(4303, '({ip}) {disk} not enough disk space. (Avail: {avail}, Need: {need})') EC_OCP_EXPRESS_DEPENDS_COMP_VERSION = OBDErrorCodeTemplate(4304, 'OCP express {ocp_express_version} needs to use {comp} with version {comp_version} or above') -EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_LOG_DISK_AVAILABLE = OBDErrorCodeTemplate(4305, 'There is not enough log disk for ocp meta tenant. (Avail: {avail}, Need: {need})') -EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_LOG_DISK = OBDErrorCodeTemplate(4305, 'There is not enough log disk for ocp meta tenant.') -EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_MEM = OBDErrorCodeTemplate(4305, 'There is not enough memory for ocp meta tenant') +EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_LOG_DISK_AVAILABLE = OBDErrorCodeTemplate(4305, 'There is not enough log disk. (Avail: {avail}, Need: {need})') +EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_MEM = OBDErrorCodeTemplate(4305, 'There is not enough memory. (Avail: {avail}, Need: {need})') # ocp-server @@ -220,6 +220,7 @@ class InitDirFailedErrorMessage(object): EC_OCP_SERVER_NOT_ENOUGH_MEMORY_CACHED = OBDErrorCodeTemplate(4364, '({ip}) not enough memory. (Free: {free}, Buff/Cache: {cached}, Need: {need})') EC_OCP_SERVER_NOT_ENOUGH_MEMORY = OBDErrorCodeTemplate(4364, '({ip}) not enough memory. (Free: {free}, Need: {need})') EC_OCP_SERVER_NOT_ENOUGH_DISK = OBDErrorCodeTemplate(4365, '({ip}) {disk} not enough disk space. (Avail: {avail}, Need: {need})') +EC_OCP_SERVER_RESOURCE_NOT_ENOUGH = OBDErrorCodeTemplate(4366, 'There is not enough {resource}. (Avail: {avail}, need: {need})') @@ -234,6 +235,7 @@ class InitDirFailedErrorMessage(object): EC_OBC_DATABASE_TYPE_ERROR = OBDErrorCodeTemplate(4402, '{server} ob-configserver config error: database_type can only be set to `mysql` or `sqlite3`, and must be in lowercase. ') EC_OBC_SQLITE_PERMISSION_DENIED = OBDErrorCodeTemplate(4403, 'ob-configserver connect to sqlite failed: {ip}: {path}: permission denied.') EC_OBC_DATABASE_CONNECT_ERROR = OBDErrorCodeTemplate(4404, 'ob-configserver connect to mysql failed: {server}: failed url to connect to database: {url}') +EC_OBC_MULTIPLE_SERVER_VIP_EMPTY_ERROR = OBDErrorCodeTemplate(4405, 'When you configure multiple ob-configserver servers, please set vip_address and vip_port.') # oblogproxy EC_OBLOGPROXY_DEPENDS_COMP_VERSION = OBDErrorCodeTemplate(4501, 'OBLogProxy {oblogproxy_version} needs to use {comp} with version {comp_version} or above') @@ -301,4 +303,5 @@ class InitDirFailedErrorMessage(object): SUG_SUDO_NOPASSWD = OBDErrorSuggestionTemplate('Please execute `bash -c \'echo "{user} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers`\' as root in {ip}.') SUG_OB_SYS_USERNAME = OBDErrorSuggestionTemplate('Please delete the "ob_sys_username" parameter.') SUG_OB_SYS_PASSWORD = OBDErrorSuggestionTemplate('''Please set the "ob_sys_password" for oblogproxy by configuring the "cdcro_password" parameter in the "oceanbase" or "oceanbase-ce" component.''') -SUG_OBAGENT_EDIT_HTTP_BASIC_AUTH_PASSWORD = OBDErrorSuggestionTemplate('Please edit the `http_basic_auth_password`, cannot contain characters other than uppercase letters, lowercase characters, digits, special characters:~^*{{}}[]_-+', fix_eval=[FixEval(FixEval.DEL, 'http_basic_auth_password')], auto_fix=True) \ No newline at end of file +SUG_OBAGENT_EDIT_HTTP_BASIC_AUTH_PASSWORD = OBDErrorSuggestionTemplate('Please edit the `http_basic_auth_password`, cannot contain characters other than uppercase letters, lowercase characters, digits, special characters:~^*{{}}[]_-+', fix_eval=[FixEval(FixEval.DEL, 'http_basic_auth_password')], auto_fix=True) +SUB_OBSERVER_UNKONE_SCENARIO = OBDErrorSuggestionTemplate('Please select a valid scenario from the options: {scenarios}') diff --git a/_mirror.py b/_mirror.py index 53260d8..6c35b0f 100644 --- a/_mirror.py +++ b/_mirror.py @@ -34,6 +34,8 @@ from enum import Enum from copy import deepcopy from xml.etree import cElementTree + +from _stdio import SafeStdio from ssh import LocalClient try: from ConfigParser import ConfigParser @@ -105,9 +107,10 @@ class MirrorRepositoryType(Enum): REMOTE = 'remote' -class MirrorRepository(object): +class MirrorRepository(SafeStdio): MIRROR_TYPE = None + __VERSION_KEY__ = '__version__' def __init__(self, mirror_path, stdio=None): self.stdio = stdio @@ -157,6 +160,16 @@ def get_exact_pkg_info(self, **pattern): def get_pkgs_info_with_score(self, **pattern): return [] + def get_all_rpm_pkgs(self): + pkgs = set() + for file_path in glob(os.path.join(self.mirror_path, '*.rpm')): + try: + pkgs.add(Package(file_path)) + except: + self.stdio.exception() + self.stdio.verbose("Failed to open rpm file: %s" % file_path) + return pkgs + class RemotePackageInfo(PackageInfo): @@ -166,6 +179,7 @@ def __init__(self, elem): self.checksum = (None,None) # type,value self.openchecksum = (None,None) # type,value self.time = (None, None) + self.package_size = None super(RemotePackageInfo, self).__init__(None, None, None, None, None, None) self._parser(elem) @@ -217,6 +231,10 @@ def _parser(self, elem): elif child_name == 'name': self.name = child.text + elif child_name == 'size': + self.size = int(child.attrib.get('installed')) + self.package_size = int(child.attrib.get('package')) + class RemoteMirrorRepository(MirrorRepository): class RepoData(object): @@ -272,6 +290,7 @@ def _parser(self, elem): REPO_AGE_FILE = '.rege_age' DB_CACHE_FILE = '.db' PRIMARY_REPOMD_TYPE = 'primary' + __VERSION__ = Version("1.0") def __init__(self, mirror_path, meta_data, stdio=None): self.baseurl = None @@ -349,16 +368,22 @@ def _load_db_cache(self, path): self.stdio and getattr(self.stdio, 'verbose', print)('load %s' % db_cacahe_path) with open(db_cacahe_path, 'rb') as f: self._db = pickle.load(f) + if self.__VERSION__ > Version(self.db.get(self.__VERSION_KEY__, '0')): + self._db = None + else: + del self._db[self.__VERSION_KEY__] except: pass def _dump_db_cache(self): if self._db: + data = deepcopy(self.db) + data[self.__VERSION_KEY__] = self.__VERSION__ try: db_cacahe_path = self.get_db_cache_file(self.mirror_path) self.stdio and getattr(self.stdio, 'verbose', print)('dump %s' % db_cacahe_path) with open(db_cacahe_path, 'wb') as f: - pickle.dump(self._db, f) + pickle.dump(data, f) return True except: self.stdio.exception('') @@ -473,7 +498,7 @@ def get_rpm_pkg_by_info(self, pkg_info): file_name = pkg_info.location[1] file_path = os.path.join(self.mirror_path, file_name) self.stdio and getattr(self.stdio, 'verbose', print)('get RPM package by %s' % pkg_info) - if not os.path.exists(file_path) or os.stat(file_path)[8] < pkg_info.time[1]: + if not os.path.exists(file_path) or os.stat(file_path)[8] < pkg_info.time[1] or os.path.getsize(file_path) != pkg_info.package_size: base_url = pkg_info.location[0] if pkg_info.location[0] else self.baseurl url = '%s/%s' % (base_url, pkg_info.location[1]) if not self.download_file(url, file_path, self.stdio): @@ -594,6 +619,7 @@ def match_score(self, info, name, arch, version=None, min_version=None, max_vers c = [len(name) / len(info.name), lse_score, info] return c + @staticmethod def validate_repoid(repoid): """Return the first invalid char found in the repoid, or None.""" @@ -645,7 +671,6 @@ class LocalMirrorRepository(MirrorRepository): MIRROR_TYPE = MirrorRepositoryType.LOCAL _DB_FILE = '.db' - __VERSION_KEY__ = '__version__' __VERSION__ = Version("1.0") def __init__(self, mirror_path, stdio=None): @@ -1173,3 +1198,22 @@ def add_repo(self, url): except Exception as e: self.stdio.exception("Failed to save repository file") return False + + def get_all_rpm_pkgs(self): + pkgs = list() + mirrors = self.get_mirrors() + for mirror in mirrors: + pkgs = pkgs + list(mirror.get_all_rpm_pkgs()) + return pkgs + + def delete_pkgs(self, pkgs): + if not pkgs: + return True + for pkg in pkgs: + if not pkg.path.startswith(self.path): + self.stdio.error("The path of the %s file does not start with %s." % (pkg.path, self.path)) + return False + if not FileUtil.rm(pkg.path, self.stdio): + return False + return True + diff --git a/_plugin.py b/_plugin.py index f6fe261..492524e 100644 --- a/_plugin.py +++ b/_plugin.py @@ -299,7 +299,7 @@ def _new_func( target_servers = kwargs.get('target_servers') if target_servers is not None: cluster_config.servers = target_servers - stdio.verbose('plugin %s target_servers: %s' % (self, target_servers)) + stdio and getattr(stdio, 'verbose', print)('plugin %s target_servers: %s' % (self, target_servers)) del kwargs['target_servers'] try: ret = method(self.context, *arg, **kwargs) @@ -313,12 +313,13 @@ def _new_func( finally: if target_servers: cluster_config.servers = servers - stdio.verbose('plugin %s restore servers: %s' % (self, servers)) + stdio and getattr(stdio, 'verbose', print)('plugin %s restore servers: %s' % (self, servers)) end_time = time.time() run_result[method_name]['time'] = end_time - start_time self.context.set_variable('run_result', run_result) ret = self.context.get_return() if self.context else PluginReturn() self.after_do(stdio, *arg, **kwargs) + stdio and getattr(stdio, 'verbose', print)('plugin %s result: %s' % (self, ret.value)) return ret return _new_func diff --git a/_repository.py b/_repository.py index 54f7791..9c47948 100644 --- a/_repository.py +++ b/_repository.py @@ -28,9 +28,12 @@ from multiprocessing import cpu_count from multiprocessing.pool import Pool +from _deploy import DeployStatus from _rpm import Package, PackageInfo, Version from _arch import getBaseArch from _environ import ENV_DISABLE_PARALLER_EXTRACT +from const import PKG_REPO_FILE +from ssh import LocalClient from tool import DirectoryUtil, FileUtil, YamlLoader, COMMAND_ENV from _manager import Manager from _plugin import InstallPlugin @@ -444,7 +447,6 @@ def clear(self): return DirectoryUtil.rm(self.repository_dir, self.stdio) and DirectoryUtil.mkdir(self.repository_dir, stdio=self.stdio) return True - class RepositoryVO(object): def __init__(self, name, version, release, arch, md5, path, tags=[], size=0): @@ -674,4 +676,15 @@ def get_instance_repository_from_shadow(self, repository): return self.repositories[path] except: pass - return None \ No newline at end of file + return None + + def delete_repositories(self, repositories): + if not repositories: + return True + for repository in repositories: + if not repository.path.startswith(self.path): + self.stdio.error("The path of the %s file does not start with %s." % (repository.path, self.path)) + return False + if not DirectoryUtil.rm(repository.path, self.stdio): + return False + return True diff --git a/_rpm.py b/_rpm.py index d908074..f63764d 100644 --- a/_rpm.py +++ b/_rpm.py @@ -20,6 +20,7 @@ from __future__ import absolute_import, division, print_function +import hashlib import os import re import sys @@ -162,6 +163,7 @@ def open(self): return rpmfile.open(self.path) + def get_version_from_array(array): version = '' for _i, _s in array: diff --git a/_stdio.py b/_stdio.py index 517d7d8..3ad6983 100644 --- a/_stdio.py +++ b/_stdio.py @@ -154,6 +154,7 @@ def _readline(cls): try: for line in sys.stdin: return line + return '' except IOError: return '' finally: @@ -666,8 +667,11 @@ def print_list(self, ary, field_names=None, exp=lambda x: x if isinstance(x, (li def read(self, msg='', blocked=False): if msg: - self._print(MsgLevel.INFO, msg) - return self.get_input_stream().read(blocked) + if self.syncing: + self.verbose(msg, end='') + else: + self._print(MsgLevel.INFO, msg, end='') + return self.get_input_stream().readline(not self.syncing and blocked) def confirm(self, msg): if self.default_confirm: @@ -905,7 +909,7 @@ class SafeStdioMeta(type): def _init_wrapper_func(func): def wrapper(*args, **kwargs): setattr(args[0], "_wrapper_func", {}) - func(*args, **kwargs) + safe_stdio_decorator(FAKE_IO)(func)(*args, **kwargs) if "stdio" in args[0].__dict__: args[0].__dict__["stdio"] = get_stdio(args[0].__dict__["stdio"]) diff --git a/const.py b/const.py index a4c3944..1f16dde 100644 --- a/const.py +++ b/const.py @@ -39,12 +39,8 @@ # obdeploy home path CONST_OBD_HOME = "OBD_HOME" -# obdeploy install pre path -CONST_OBD_INSTALL_PRE = "OBD_INSTALL_PRE" -# obdeploy install path -CONST_OBD_INSTALL_PATH = "OBD_INSTALL_PATH" # obdeploy forbidden variable -FORBIDDEN_VARS = (CONST_OBD_HOME, CONST_OBD_INSTALL_PRE, CONST_OBD_INSTALL_PATH) +FORBIDDEN_VARS = (CONST_OBD_HOME) # tool variable COMP_OBCLIENT = "obclient" @@ -66,4 +62,8 @@ # service docs url DISABLE_SWAGGER = '' -RSA_KEY_SIZE = 512 \ No newline at end of file +# component files type +PKG_RPM_FILE = 'rpm' +PKG_REPO_FILE = 'repository' + +RSA_KEY_SIZE = 512 diff --git a/core.py b/core.py index a75549c..9966254 100644 --- a/core.py +++ b/core.py @@ -34,17 +34,17 @@ from tool import FileUtil, DirectoryUtil, YamlLoader, timeout, COMMAND_ENV, OrderedDict from _stdio import MsgLevel, FormtatText from _rpm import Version -from _mirror import MirrorRepositoryManager, PackageInfo +from _mirror import MirrorRepositoryManager, PackageInfo, RemotePackageInfo from _plugin import PluginManager, PluginType, InstallPlugin, PluginContextNamespace from _deploy import DeployManager, DeployStatus, DeployConfig, DeployConfigStatus, Deploy, ClusterStatus from _tool import Tool, ToolManager -from _repository import RepositoryManager, LocalPackage, Repository +from _repository import RepositoryManager, LocalPackage, Repository, RepositoryVO import _errno as err from _lock import LockManager, LockMode from _optimize import OptimizeManager from _environ import ENV_REPO_INSTALL_MODE, ENV_BASE_DIR from _types import Capacity -from const import COMP_OCEANBASE_DIAGNOSTIC_TOOL, COMP_OBCLIENT +from const import COMP_OCEANBASE_DIAGNOSTIC_TOOL, COMP_OBCLIENT, PKG_RPM_FILE, PKG_REPO_FILE from ssh import LocalClient @@ -187,7 +187,7 @@ def call_plugin(self, plugin, repository, spacename=None, target_servers=None, * if self.deploy: args['deploy_name'] = self.deploy.name args['deploy_status'] = self.deploy.deploy_info.status - args['components'] = self.deploy.deploy_info.components + args['components'] = self.deploy.deploy_config.components.keys() args['cluster_config'] = self.deploy.deploy_config.components[repository.name] if "clients" not in kwargs: args['clients'] = self.get_clients(self.deploy.deploy_config, self.repositories) @@ -1535,9 +1535,10 @@ def _deploy_cluster(self, deploy, repositories, scale_out=False, dump=True): return False self._call_stdio('stop_loading', 'succeed') - self._call_stdio('start_loading', 'Parameter check') + self._call_stdio('start_loading', 'Load param plugin') # Check whether the components have the parameter plugins and apply the plugins self.search_param_plugin_and_apply(repositories, deploy_config) + self._call_stdio('stop_loading', 'succeed') # Generate password when password is None gen_config_plugins = self.search_py_script_plugin(repositories, 'generate_config') @@ -1546,7 +1547,7 @@ def _deploy_cluster(self, deploy, repositories, scale_out=False, dump=True): self.call_plugin(gen_config_plugins[repository], repository, only_generate_password=True) # Parameter check - self._call_stdio('verbose', 'Cluster param configuration check') + self._call_stdio('start_loading', 'Parameter check') errors = self.deploy_param_check(repositories, deploy_config) if errors: self._call_stdio('stop_loading', 'fail') @@ -1795,6 +1796,8 @@ def scale_out(self, name): return False errors = [] + need_start = [] + need_reload = [] for repository in all_repositories: namespace = self.get_namespace(repository.name) if repository not in scale_out_check_plugins: @@ -1802,12 +1805,13 @@ def scale_out(self, name): plugin_return = namespace.get_return(scale_out_check_plugins[repository].name) setattr(self.options, 'components', repository.name) if plugin_return.get_return('need_restart'): - setattr(self.options, 'display', None) - if not self._restart_cluster(deploy, repository): - errors.append('failed to restart {}'.format(repository.name)) - elif plugin_return.get_return('need_reload'): - if not self._reload_cluster(deploy, repository): - errors.append('failed to reload {}'.format(repository.name)) + need_start.append(repository) + if plugin_return.get_return('need_reload'): + need_reload.append(repository) + + # todo: need_reload use need_start tips,supoort later + if need_start or need_reload: + self._call_stdio('print', 'Use `obd cluster restart %s --wp` to make changes take effect.' % name) if errors: self._call_stdio('warn', err.WC_FAIL_TO_RESTART_OR_RELOAD_AFTER_SCALE_OUT.format(detail='\n -'.join(errors))) @@ -1885,8 +1889,8 @@ def add_components(self, name): if repository in scale_out_check_plugins: plugin_return = self.get_namespace(repository.name).get_return(scale_out_check_plugins[repository].name) plugins_list = plugin_return.get_return('plugins', []) - for name in plugins_list: - plugin = self.search_py_script_plugin([repository], name) + for plugin_name in plugins_list: + plugin = self.search_py_script_plugin([repository], plugin_name) if repository in plugin: succeed = succeed and self.call_plugin(plugin[repository], repository) if not succeed: @@ -1909,6 +1913,8 @@ def add_components(self, name): deploy.dump_deploy_info() errors = [] + need_start = [] + need_reload = [] for repository in all_repositories: namespace = self.get_namespace(repository.name) if repository not in scale_out_check_plugins: @@ -1916,12 +1922,13 @@ def add_components(self, name): plugin_return = namespace.get_return(scale_out_check_plugins[repository].name) setattr(self.options, 'components', repository.name) if plugin_return.get_return('need_restart'): - setattr(self.options, 'display', None) - if not self._restart_cluster(deploy, repository): - errors.append('failed to restart {}'.format(repository.name)) - elif plugin_return.get_return('need_reload'): - if not self._reload_cluster(deploy, repository): - errors.append('failed to reload {}'.format(repository.name)) + need_start.append(repository) + if plugin_return.get_return('need_reload'): + need_reload.append(repository) + + # todo: need_reload use need_start tips,supoort later + if need_start or need_reload: + self._call_stdio('print', 'Use `obd cluster restart %s --wp` to make changes take effect.' % name) if errors: self._call_stdio('warn', err.WC_FAIL_TO_RESTART_OR_RELOAD.format(action='added', detail='\n -'.join(errors))) @@ -2017,10 +2024,10 @@ def delete_components(self, name, components): setattr(self.options, 'components', repository.name) if plugin_return.get_return('need_restart'): setattr(self.options, 'display', None) - if not self._restart_cluster(deploy, repository): + if not self._restart_cluster(deploy, [repository]): errors.append('failed to restart {}'.format(repository.name)) elif plugin_return.get_return('need_reload'): - if not self._reload_cluster(deploy, repository): + if not self._reload_cluster(deploy, [repository]): errors.append('failed to reload {}'.format(repository.name)) if errors: @@ -2207,7 +2214,7 @@ def create_tenant(self, name): if not deploy: self._call_stdio('error', 'No such deploy: %s.' % name) return False - + deploy_info = deploy.deploy_info self._call_stdio('verbose', 'Deploy status judge') if deploy_info.status != DeployStatus.STATUS_RUNNING: @@ -2226,6 +2233,7 @@ def create_tenant(self, name): connect_plugins = self.search_py_script_plugin(repositories, 'connect') create_tenant_plugins = self.search_py_script_plugin(repositories, 'create_tenant', no_found_act='ignore') + scenario_check_plugins = self.search_py_script_plugin(repositories, 'scenario_check', no_found_act='ignore') tenant_optimize_plugins = self.search_py_script_plugin(repositories, 'tenant_optimize', no_found_act='ignore') self._call_stdio('stop_loading', 'succeed') @@ -2233,6 +2241,10 @@ def create_tenant(self, name): ssh_clients = self.get_clients(deploy_config, repositories) for repository in create_tenant_plugins: + if repository in scenario_check_plugins: + if not self.call_plugin(scenario_check_plugins[repository], repository): + return False + if not self.call_plugin(connect_plugins[repository], repository): return False @@ -2502,6 +2514,49 @@ def list_tenant(self, name): return False return True + def tenant_optimize(self, deploy_name, tenant_name): + self._call_stdio('verbose', 'Get Deploy by name') + deploy = self.deploy_manager.get_deploy_config(deploy_name) + self.set_deploy(deploy) + if not deploy: + self._call_stdio('error', 'No such deploy: %s.' % deploy_name) + return False + + deploy_info = deploy.deploy_info + self._call_stdio('verbose', 'Deploy status judge') + if deploy_info.status != DeployStatus.STATUS_RUNNING: + self._call_stdio('print', 'Deploy "%s" is %s' % (deploy_name, deploy_info.status.value)) + return False + self._call_stdio('verbose', 'Get deploy config') + deploy_config = deploy.deploy_config + + self._call_stdio('start_loading', 'Get local repositories and plugins') + # Get the repository + repositories = self.load_local_repositories(deploy_info) + self.set_repositories(repositories) + # Check whether the components have the parameter plugins and apply the plugins + self.search_param_plugin_and_apply(repositories, deploy_config) + + connect_plugins = self.search_py_script_plugin(repositories, 'connect') + scenario_check_plugins = self.search_py_script_plugin(repositories, 'scenario_check', no_found_act='ignore') + tenant_optimize_plugins = self.search_py_script_plugin(repositories, 'tenant_optimize', no_found_act='ignore') + if not tenant_optimize_plugins: + self._call_stdio('error', 'The %s %s does not support tenant optimize' % (repositories[0].name, repositories[0].version)) + return False + self._call_stdio('stop_loading', 'succeed') + + for repository in tenant_optimize_plugins: + if repository in scenario_check_plugins: + if not self.call_plugin(scenario_check_plugins[repository], repository): + return False + + if not self.call_plugin(connect_plugins[repository], repository): + return False + + if not self.call_plugin(tenant_optimize_plugins[repository], repository, tenant_name=tenant_name): + return False + return True + def reload_cluster(self, name): self._call_stdio('verbose', 'Get Deploy by name') deploy = self.deploy_manager.get_deploy_config(name) @@ -4906,6 +4961,44 @@ def install_utils_to_servers(self, repositories, repositories_utils_map, unuse_u return False return True + def clean_pkg(self, opts): + filter_pkgs, filter_repositories = {}, {} + if opts.type != PKG_REPO_FILE: + downloaded_pkgs = self.mirror_manager.get_all_rpm_pkgs() + if not downloaded_pkgs: + self._call_stdio('print', 'There are no RPM files in the remote and local.') + else: + filter_pkgs = self.filter_pkgs(downloaded_pkgs, 'DELETE', hash=opts.hash, components=opts.components, max_version=True) + if opts.type != PKG_RPM_FILE: + repositories = self.repository_manager.get_repositories_view() + if not repositories: + self._call_stdio('print', 'There are no repositories.') + else: + filter_repositories = self.filter_pkgs(repositories, 'DELETE', hash=opts.hash, components=opts.components) + delete_pkgs, cant_delete_pkgs = filter_pkgs.get('delete', []), filter_pkgs.get('cant_delete', []) + delete_repositories, cant_delete_repositories = filter_repositories.get('delete', []), filter_repositories.get('cant_delete', []) + if delete_pkgs + delete_repositories: + self._call_stdio('print_list', delete_pkgs + delete_repositories, ['name', 'version', 'release', 'arch', 'md5', 'type'], + lambda x: [x.name, x.version, x.release, x.arch, x.md5, PKG_REPO_FILE if isinstance(x, RepositoryVO) else PKG_RPM_FILE], + title='Delete PKG Files List' + ) + if cant_delete_pkgs + cant_delete_repositories: + self._call_stdio('print_list', cant_delete_pkgs + cant_delete_repositories, ['name', 'version', 'release', 'arch', 'md5', 'type', 'reason'], + lambda x: [x.name, x.version, x.release, x.arch, x.md5, PKG_REPO_FILE if isinstance(x, RepositoryVO) else PKG_RPM_FILE, x.reason], + title='Can`t Delete PKG Files List' + ) + if not delete_pkgs + delete_repositories: + self._call_stdio('print', 'No Package need deleted') + return False + if not opts.confirm and not self._call_stdio('confirm', FormtatText.warning('Are you sure to delete the files listed above ?')): + return False + if not self.mirror_manager.delete_pkgs(delete_pkgs) or not self.repository_manager.delete_repositories(delete_repositories): + return False + + self.stdio.print("Delete the files listed above successful!") + return True + + def print_tools(self, tools, title): if tools: self._call_stdio('print_list', tools, @@ -5301,4 +5394,109 @@ def takeover(self, name): self.deploy_manager.remove_deploy_config(name) self._call_stdio('stop_loading', 'failed') self._call_stdio('error', 'Failed to takeover OceanBase cluster' ) - return False \ No newline at end of file + return False + + def filter_pkgs(self, pkgs, basic_condition, **pattern): + ret_pkgs = {} + if not pkgs: + self.stdio.verbose("filter pkgs failed, pkgs is empty.") + return ret_pkgs + + used_pkgs = [] + max_version_pkgs = [] + hash_hit_pkgs = [] + component_hit_pkgs = [] + # no pattern,default hit + hit_pkgs = [] + + # filter pkgs by DeployStatus + for deploy in self.deploy_manager.get_deploy_configs(): + if deploy.deploy_info.status != DeployStatus.STATUS_DESTROYED: + for component in deploy.deploy_config.components.values(): + for pkg in pkgs: + if pkg.md5 == component.package_hash: + used_pkgs.append(pkg) + break + + # filter pkgs by pattern.hash + if pattern.get('hash'): + md5s = list(set(pattern['hash'].split(','))) + for md5 in md5s: + check_hash = False + for pkg in pkgs: + if pkg.md5 == md5: + check_hash = True + hash_hit_pkgs.append(pkg) + if not check_hash: + self.stdio.print("There is no RPM file with the hash value of %s." % md5) + + # filter pkgs by pattern.components + if pattern.get("components"): + components = list(set(pattern['components'].split(','))) + for component in components: + check_component_name = False + for pkg in pkgs: + if pkg.name == component: + check_component_name = True + component_hit_pkgs.append(pkg) + if not check_component_name: + self.stdio.print("There are no RPM files for the %s component." % check_component_name) + + # filter pkgs by pattern.max_version + if pattern.get("max_version"): + version_sorted_components_map = {} + max_version_components_map = {} + for pkg in pkgs: + if pkg.name in version_sorted_components_map: + version_sorted_components_map[pkg.name].append(str(pkg.version)) + else: + version_sorted_components_map[pkg.name] = [str(pkg.version)] + for component, versions in version_sorted_components_map.items(): + max_version_components_map[component] = sorted(versions).pop() + for pkg in pkgs: + if max_version_components_map.get(pkg.name) and pkg.version == max_version_components_map.get(pkg.name): + del max_version_components_map[pkg.name] + max_version_pkgs.append(pkg) + + # get all hit pkgs + if not pattern.get('hash') and not pattern.get('components'): + for pkg in pkgs: + hit_pkgs.append(pkg) + + # filter libds pkgs and utils pkgs + for pkg in pkgs: + if pkg.name in ['oceanbase', 'oceanbase-ce'] and (pkg in hit_pkgs or pkg in hash_hit_pkgs or pkg in component_hit_pkgs): + for sub_pkg in pkgs: + if (sub_pkg.name == '%s-libs' % pkg.name or sub_pkg.name == '%s-utils' % pkg.name) and sub_pkg.release == pkg.release: + hit_pkgs.append(sub_pkg) + hash_hit_pkgs.append(sub_pkg) + component_hit_pkgs.append(sub_pkg) + used_pkgs.append(sub_pkg) + max_version_pkgs.append(sub_pkg) + + # filter the pkg that meets the deletion criteria. + if basic_condition == 'DELETE': + delete_pkgs = [] + cant_delete_pkgs = [] + for pkg in pkgs: + if pkg in hit_pkgs or pkg in hash_hit_pkgs or pkg in component_hit_pkgs: + if pkg not in used_pkgs: + if pkg in hash_hit_pkgs or ((pkg in hit_pkgs or pkg in component_hit_pkgs) and pkg not in max_version_pkgs): + delete_pkgs.append(pkg) + elif pkg in max_version_pkgs and (pkg in hit_pkgs or pkg in component_hit_pkgs): + cant_delete_pkgs.append(pkg) + else: + if pkg in hash_hit_pkgs or pkg in hit_pkgs or pkg in component_hit_pkgs: + cant_delete_pkgs.append(pkg) + + for pkg in cant_delete_pkgs: + if pkg in used_pkgs: + setattr(pkg, "reason", "in use") + elif pkg in max_version_pkgs: + setattr(pkg, "reason", "the latest version") + else: + setattr(pkg, "reason", "other reason") + + ret_pkgs = {'delete': delete_pkgs, 'cant_delete': cant_delete_pkgs} + + return ret_pkgs diff --git a/example/all-components-min.yaml b/example/all-components-min.yaml index cc92fb1..698e8ff 100644 --- a/example/all-components-min.yaml +++ b/example/all-components-min.yaml @@ -146,7 +146,7 @@ prometheus: # - job_name: node # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/node/host # scheme: http # file_sd_configs: # Set the targets to be collected by reading local files. The example is to collect targets corresponding to all yaml files in the 'targets' directory under $home_path. @@ -155,7 +155,7 @@ prometheus: # - job_name: ob_basic # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/ob/basic # scheme: http # file_sd_configs: @@ -164,7 +164,7 @@ prometheus: # - job_name: ob_extra # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/ob/extra # scheme: http # file_sd_configs: @@ -173,7 +173,7 @@ prometheus: # - job_name: agent # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/stat # scheme: http # file_sd_configs: @@ -188,7 +188,7 @@ grafana: - prometheus global: home_path: /root/grafana - login_password: xxxxxxxxx # Grafana login password. + login_password: '******' # Grafana login password. # data_dir: # Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used).$data_dir can be empty. The default value is $home_path/data. # logs_dir: # Directory where grafana can store logs, can be empty. The default value is $data_dir/log. # plugins_dir: # Directory where grafana will automatically scan and look for plugins, can be empty. The default value is $data_dir/plugins. diff --git a/example/all-components.yaml b/example/all-components.yaml index c899097..b3bb627 100644 --- a/example/all-components.yaml +++ b/example/all-components.yaml @@ -147,7 +147,7 @@ prometheus: # - job_name: node # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/node/host # scheme: http # file_sd_configs: # Set the targets to be collected by reading local files. The example is to collect targets corresponding to all yaml files in the 'targets' directory under $home_path. @@ -156,7 +156,7 @@ prometheus: # - job_name: ob_basic # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/ob/basic # scheme: http # file_sd_configs: @@ -165,7 +165,7 @@ prometheus: # - job_name: ob_extra # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/ob/extra # scheme: http # file_sd_configs: @@ -174,7 +174,7 @@ prometheus: # - job_name: agent # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/stat # scheme: http # file_sd_configs: @@ -189,7 +189,7 @@ grafana: - prometheus global: home_path: /root/grafana - login_password: xxxxxxxxx # Grafana login password. + login_password: '******' # Grafana login password. # data_dir: # Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used).$data_dir can be empty. The default value is $home_path/data. # logs_dir: # Directory where grafana can store logs, can be empty. The default value is $data_dir/log. # plugins_dir: # Directory where grafana will automatically scan and look for plugins, can be empty. The default value is $data_dir/plugins. diff --git a/example/grafana/prometheus-and-grafana.yaml b/example/grafana/prometheus-and-grafana.yaml index 71e49e2..36c7dea 100644 --- a/example/grafana/prometheus-and-grafana.yaml +++ b/example/grafana/prometheus-and-grafana.yaml @@ -35,7 +35,7 @@ prometheus: # - job_name: node # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/node/host # scheme: http # file_sd_configs: # Set the targets to be collected by reading local files. The example is to collect targets corresponding to all yaml files in the 'targets' directory under $home_path. @@ -44,7 +44,7 @@ prometheus: # - job_name: ob_basic # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/ob/basic # scheme: http # file_sd_configs: @@ -62,7 +62,7 @@ prometheus: # - job_name: agent # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/stat # scheme: http # file_sd_configs: @@ -77,7 +77,7 @@ grafana: - prometheus global: home_path: /root/grafana - login_password: xxxxxxxxx # Grafana login password. + login_password: '******' # Grafana login password. # data_dir: # Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used).$data_dir can be empty. The default value is $home_path/data. # logs_dir: # Directory where grafana can store logs, can be empty. The default value is $data_dir/log. # plugins_dir: # Directory where grafana will automatically scan and look for plugins, can be empty. The default value is $data_dir/plugins. diff --git a/example/obagent/distributed-with-obproxy-and-obagent-example.yaml b/example/obagent/distributed-with-obproxy-and-obagent-example.yaml index 51cd3f5..ee817f3 100644 --- a/example/obagent/distributed-with-obproxy-and-obagent-example.yaml +++ b/example/obagent/distributed-with-obproxy-and-obagent-example.yaml @@ -125,7 +125,7 @@ obagent: # Username for HTTP authentication. The default value is admin. http_basic_auth_user: admin # Password for HTTP authentication. The default is a random password. - http_basic_auth_password: ****** + http_basic_auth_password: '******' # Monitor password for OceanBase Database. The default value is empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. The value is the same as the ocp_agent_monitor_password in oceanbase-ce. # monitor_password: # The SQL port for observer. The default value is 2881. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. The value is the same as the mysql_port in oceanbase-ce. diff --git a/example/obagent/obagent-only-1.2.0-example.yaml b/example/obagent/obagent-only-1.2.0-example.yaml index 5d8bcfb..19efd97 100644 --- a/example/obagent/obagent-only-1.2.0-example.yaml +++ b/example/obagent/obagent-only-1.2.0-example.yaml @@ -37,11 +37,11 @@ obagent: # Username for HTTP authentication. The default value is admin. http_basic_auth_user: admin # Password for HTTP authentication. The default is a random password. - http_basic_auth_password: ****** + http_basic_auth_password: '******' # Username for debug service. The default value is admin. pprof_basic_auth_user: admin # Password for debug service. The default value is root. - pprof_basic_auth_password: root + pprof_basic_auth_password: '******' # Monitor username for OceanBase Database. The user must have read access to OceanBase Database as a system tenant. The default value is root. monitor_user: root # Monitor password for OceanBase Database. The default value is empty. When a depends exists, OBD gets this value from the oceanbase-ce of the depends. The value is the same as the root_password in oceanbase-ce. diff --git a/example/prometheus/prometheus-only-example.yaml b/example/prometheus/prometheus-only-example.yaml index eccde76..ad6aeea 100644 --- a/example/prometheus/prometheus-only-example.yaml +++ b/example/prometheus/prometheus-only-example.yaml @@ -35,7 +35,7 @@ prometheus: # - job_name: node # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/node/host # scheme: http # file_sd_configs: # Set the targets to be collected by reading local files. The example is to collect targets corresponding to all yaml files in the 'targets' directory under $home_path. @@ -44,7 +44,7 @@ prometheus: # - job_name: ob_basic # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/ob/basic # scheme: http # file_sd_configs: @@ -53,7 +53,7 @@ prometheus: # - job_name: ob_extra # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/ob/extra # scheme: http # file_sd_configs: @@ -62,7 +62,7 @@ prometheus: # - job_name: agent # basic_auth: # username: admin - # password: root + # password: '******' # metrics_path: /metrics/stat # scheme: http # file_sd_configs: diff --git a/example/scale-out/ob-configserver-component-add.yaml b/example/scale-out/ob-configserver-component-add.yaml new file mode 100644 index 0000000..2db7652 --- /dev/null +++ b/example/scale-out/ob-configserver-component-add.yaml @@ -0,0 +1,6 @@ +ob-configserver: + servers: + - 192.168.1.1 + global: + listen_port: 8080 # The port of ob-configserver web + home_path: /home/admin/ob-configserver # The working directory for prometheus. ob-configserver is started under this directory. This is a required field. \ No newline at end of file diff --git a/example/scale-out/ob-configserver-scale-out.yaml b/example/scale-out/ob-configserver-scale-out.yaml new file mode 100644 index 0000000..44fb1f4 --- /dev/null +++ b/example/scale-out/ob-configserver-scale-out.yaml @@ -0,0 +1,7 @@ +ob-configserver: + servers: + - name: server1 + ip: 192.168.1.1 + server1: + listen_port: 8080 # The port of ob-configserver web + home_path: /home/admin/ob-configserver # The working directory for prometheus. ob-configserver is started under this directory. This is a required field. \ No newline at end of file diff --git a/example/scale-out/oblogproxy-component-add.yaml b/example/scale-out/oblogproxy-component-add.yaml new file mode 100644 index 0000000..4c86744 --- /dev/null +++ b/example/scale-out/oblogproxy-component-add.yaml @@ -0,0 +1,9 @@ +oblogproxy: + depends: + - oceanbase-ce + servers: + - 192.168.1.1 + version: 2.0.0 + global: + home_path: /root/oblogproxy + service_port: 2983 # External port. The default value is 2983. \ No newline at end of file diff --git a/example/scale-out/ocp-component-add.yaml b/example/scale-out/ocp-component-add.yaml new file mode 100644 index 0000000..7bdf26d --- /dev/null +++ b/example/scale-out/ocp-component-add.yaml @@ -0,0 +1,34 @@ +ocp-server-ce: + depends: + - oceanbase-ce + servers: + - 192.168.0.3 + global: + home_path: /root/ocp +# soft_dir: /home/root/software # Directory used to store packages +# log_dir: /home/root/logs # Directory used to temporary store downloaded logs +# ocp_site_url: http://192.168.0.3:8080 +# port: 8080 +# admin_password: '********' +# memory_size: 4G +# ocp_meta_tenant: +# tenant_name: ocp_meta +# max_cpu: 2.0 +# memory_size: 4G +# ocp_meta_username: root +# ocp_meta_password: '********' +# ocp_meta_db: meta_database +# ocp_meta_username: root # User to use under ocp meta tenant +# ocp_meta_password: ****** # Password used to connect to ocp meta tenant +# ocp_meta_db: meta_database # Database used to store ocp meta data +# OCP monitor tenant definition, including tenant name, cpu and memory +# ocp_monitor_tenant: +# tenant_name: ocp_monitor +# max_cpu: 2.0 +# memory_size: 4G +# ocp_monitor_username: root +# ocp_monitor_password: '********' +# ocp_monitor_db: monitor_database +# ocp_monitor_username: root # User to use under ocp monitor tenant +# ocp_monitor_password: ****** # Password used to connect to ocp meta tenant +# ocp_monitor_db: monitor_database # Database used to store ocp meta data \ No newline at end of file diff --git a/example/scale-out/ocp-scale-out.yaml b/example/scale-out/ocp-scale-out.yaml new file mode 100644 index 0000000..d447895 --- /dev/null +++ b/example/scale-out/ocp-scale-out.yaml @@ -0,0 +1,10 @@ +ocp-server-ce: + servers: + - name: server2 + # Please don't use hostname, only IP can be supported + ip: 192.168.1.3 + server2: + home_path: /root/ocp +# soft_dir: /home/root/software # Directory used to store packages +# log_dir: /home/root/logs # Directory used to temporary store downloaded logs +# port: 8080 diff --git a/executer27.py b/executer27.py index ad03e0e..7aa373b 100644 --- a/executer27.py +++ b/executer27.py @@ -33,7 +33,6 @@ import socket import platform - if __name__ == '__main__': defaultencoding = 'utf-8' if sys.getdefaultencoding() != defaultencoding: diff --git a/optimize/oceanbase-ce/4.0.0.0/tpch.yaml b/optimize/oceanbase-ce/4.0.0.0/tpch.yaml index a9a1215..0839301 100644 --- a/optimize/oceanbase-ce/4.0.0.0/tpch.yaml +++ b/optimize/oceanbase-ce/4.0.0.0/tpch.yaml @@ -2,6 +2,9 @@ test: system_config: - name: enable_sql_audit value: false + - name: sleep + value: 5 + optimizer: sleep - name: enable_sql_extension value: true optimizer: tenant diff --git a/optimize/oceanbase-ce/4.1.0/tpch.yaml b/optimize/oceanbase-ce/4.1.0/tpch.yaml index 3d6ad45..c383982 100644 --- a/optimize/oceanbase-ce/4.1.0/tpch.yaml +++ b/optimize/oceanbase-ce/4.1.0/tpch.yaml @@ -2,6 +2,9 @@ test: system_config: - name: enable_sql_audit value: false + - name: sleep + value: 5 + optimizer: sleep - name: syslog_level value: PERF - name: enable_perf_event diff --git a/optimize/oceanbase-ce/4.2.0/tpch.yaml b/optimize/oceanbase-ce/4.2.0/tpch.yaml index 2578e10..21e6312 100644 --- a/optimize/oceanbase-ce/4.2.0/tpch.yaml +++ b/optimize/oceanbase-ce/4.2.0/tpch.yaml @@ -2,6 +2,9 @@ test: system_config: - name: enable_sql_audit value: false + - name: sleep + value: 5 + optimizer: sleep - name: syslog_level value: PERF - name: enable_perf_event diff --git a/optimize/oceanbase-ce/4.3.0/tpch.yaml b/optimize/oceanbase-ce/4.3.0/tpch.yaml index 2578e10..21e6312 100644 --- a/optimize/oceanbase-ce/4.3.0/tpch.yaml +++ b/optimize/oceanbase-ce/4.3.0/tpch.yaml @@ -2,6 +2,9 @@ test: system_config: - name: enable_sql_audit value: false + - name: sleep + value: 5 + optimizer: sleep - name: syslog_level value: PERF - name: enable_perf_event diff --git a/plugins/ob-configserver/1.0.0/start_check.py b/plugins/ob-configserver/1.0.0/start_check.py index da22c9e..a3ae3b2 100644 --- a/plugins/ob-configserver/1.0.0/start_check.py +++ b/plugins/ob-configserver/1.0.0/start_check.py @@ -165,6 +165,7 @@ def critical(item, error, suggests=[]): 'port': err.CheckStatus(), 'parameter': err.CheckStatus(), 'database': err.CheckStatus(), + 'vip': err.CheckStatus(), } if work_dir_check: check_status[server]['dir'] = err.CheckStatus() @@ -173,6 +174,11 @@ def critical(item, error, suggests=[]): return plugin_context.return_true(start_check_status=check_status) stdio.start_loading('Check before start ob-configserver') + + global_config = cluster_config.get_global_conf() + if len(cluster_config.servers) > 1 and (not global_config.get('vip_address') or not global_config.get('vip_port')): + critical('vip', err.EC_OBC_MULTIPLE_SERVER_VIP_EMPTY_ERROR.format()) + for server in cluster_config.servers: server_config = cluster_config.get_server_conf_with_default(server) home_path = server_config['home_path'] diff --git a/plugins/oblogproxy/2.0.0/scale_in_check.py b/plugins/oblogproxy/2.0.0/scale_in_check.py index b2d9e06..e2890cb 100644 --- a/plugins/oblogproxy/2.0.0/scale_in_check.py +++ b/plugins/oblogproxy/2.0.0/scale_in_check.py @@ -1,4 +1,3 @@ -# coding: utf-8 # OceanBase Deploy. # Copyright (C) 2021 OceanBase # @@ -16,13 +15,8 @@ # # You should have received a copy of the GNU General Public License # along with OceanBase Deploy. If not, see . - - from __future__ import absolute_import, division, print_function def scale_in_check(plugin_context, *args, **kwargs): - cluster_config = plugin_context.cluster_config - stdio = plugin_context.stdio - stdio.error("Not support component `%s`".format(cluster_config.name)) - return plugin_context.return_false() + return plugin_context.return_true() \ No newline at end of file diff --git a/plugins/oblogproxy/2.0.0/scale_out_check.py b/plugins/oblogproxy/2.0.0/scale_out_check.py index 598aa83..822a693 100644 --- a/plugins/oblogproxy/2.0.0/scale_out_check.py +++ b/plugins/oblogproxy/2.0.0/scale_out_check.py @@ -1,4 +1,3 @@ -# coding: utf-8 # OceanBase Deploy. # Copyright (C) 2021 OceanBase # @@ -16,13 +15,13 @@ # # You should have received a copy of the GNU General Public License # along with OceanBase Deploy. If not, see . - - from __future__ import absolute_import, division, print_function def scale_out_check(plugin_context, *args, **kwargs): cluster_config = plugin_context.cluster_config - stdio = plugin_context.stdio - stdio.error("Not support component `%s`".format(cluster_config.name)) - return plugin_context.return_false() + changed_components = cluster_config.get_deploy_changed_components() + if 'oblogproxy' in changed_components: + plugin_context.stdio.error("Not support component `oblogproxy`") + return plugin_context.return_false() + return plugin_context.return_true() \ No newline at end of file diff --git a/plugins/obproxy-ce/4.2.1/file_map.yaml b/plugins/obproxy-ce/4.2.1/file_map.yaml new file mode 100644 index 0000000..e9911dd --- /dev/null +++ b/plugins/obproxy-ce/4.2.1/file_map.yaml @@ -0,0 +1,7 @@ +- src_path: ./home/admin/obproxy-$version/bin/obproxy + target_path: bin/obproxy + type: bin + mode: 755 +- src_path: ./home/admin/obproxy-$version/lib + target_path: lib + type: dir \ No newline at end of file diff --git a/plugins/obproxy/3.1.0/file_map.yaml b/plugins/obproxy/3.1.0/file_map.yaml index b7a1393..34ec912 100644 --- a/plugins/obproxy/3.1.0/file_map.yaml +++ b/plugins/obproxy/3.1.0/file_map.yaml @@ -1,4 +1,4 @@ -- src_path: ./home/admin/obproxy-$version/bin/obproxy +- src_path: ./opt/taobao/install/obproxy-$version/bin/obproxy target_path: bin/obproxy type: bin mode: 755 \ No newline at end of file diff --git a/plugins/obproxy/3.1.0/generate_config.py b/plugins/obproxy/3.1.0/generate_config.py index 79ec04b..d03c758 100644 --- a/plugins/obproxy/3.1.0/generate_config.py +++ b/plugins/obproxy/3.1.0/generate_config.py @@ -72,10 +72,10 @@ def generate_config(plugin_context, generate_config_mini=False, auto_depend=Fals resource[cluster_config.name]['memory'][server.ip] = cluster_config.get_global_conf_with_default()['proxy_mem_limited'] if auto_depend: - for depend in ['oceanbase', 'oceanbase-ce']: - if cluster_config.add_depend_component(depend): - stdio.stop_loading('succeed') - return plugin_context.return_true() + for comp in ['oceanbase', 'oceanbase-ce', 'ob-configserver']: + if comp in plugin_context.components: + cluster_config.add_depend_component(comp) + stdio.stop_loading('succeed') return plugin_context.return_true() diff --git a/plugins/obproxy/3.1.0/scale_out_check.py b/plugins/obproxy/3.1.0/scale_out_check.py new file mode 100644 index 0000000..1533725 --- /dev/null +++ b/plugins/obproxy/3.1.0/scale_out_check.py @@ -0,0 +1,36 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function + + +def scale_out_check(plugin_context, *args, **kwargs): + cluster_config = plugin_context.cluster_config + added_components = cluster_config.get_deploy_added_components() + plugins = [] + plugin_context.set_variable('need_bootstrap', False) + need_restart = False + if 'ob-configserver' in added_components: + cluster_config.add_depend_component('ob-configserver') + need_restart = True + + plugin_context.stdio.verbose('scale_out_check plugins: %s' % plugins) + plugin_context.stdio.verbose('added_components: %s' % added_components) + return plugin_context.return_true(plugins=plugins, need_restart=need_restart) diff --git a/plugins/obproxy/4.2.1/file_map.yaml b/plugins/obproxy/4.2.1/file_map.yaml new file mode 100644 index 0000000..114adcd --- /dev/null +++ b/plugins/obproxy/4.2.1/file_map.yaml @@ -0,0 +1,7 @@ +- src_path: ./opt/taobao/install/obproxy-$version/bin/obproxy + target_path: bin/obproxy + type: bin + mode: 755 +- src_path: ./opt/taobao/install/obproxy-$version/lib + target_path: lib + type: dir \ No newline at end of file diff --git a/plugins/obproxy/4.2.3/generate_config.py b/plugins/obproxy/4.2.3/generate_config.py index f931cfd..2a73e90 100644 --- a/plugins/obproxy/4.2.3/generate_config.py +++ b/plugins/obproxy/4.2.3/generate_config.py @@ -77,10 +77,9 @@ def generate_config(plugin_context, generate_config_mini=False, auto_depend=Fals cluster_config.update_global_conf('proxy_mem_limited', '500M', False) if auto_depend: - for depend in ['oceanbase', 'oceanbase-ce']: - if cluster_config.add_depend_component(depend): - stdio.stop_loading('succeed') - return plugin_context.return_true() + for comp in ['oceanbase', 'oceanbase-ce', 'ob-configserver']: + if comp in plugin_context.components: + cluster_config.add_depend_component(comp) stdio.stop_loading('succeed') return plugin_context.return_true() diff --git a/plugins/obproxy/4.2.3/scale_out_check.py b/plugins/obproxy/4.2.3/scale_out_check.py new file mode 100644 index 0000000..1533725 --- /dev/null +++ b/plugins/obproxy/4.2.3/scale_out_check.py @@ -0,0 +1,36 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function + + +def scale_out_check(plugin_context, *args, **kwargs): + cluster_config = plugin_context.cluster_config + added_components = cluster_config.get_deploy_added_components() + plugins = [] + plugin_context.set_variable('need_bootstrap', False) + need_restart = False + if 'ob-configserver' in added_components: + cluster_config.add_depend_component('ob-configserver') + need_restart = True + + plugin_context.stdio.verbose('scale_out_check plugins: %s' % plugins) + plugin_context.stdio.verbose('added_components: %s' % added_components) + return plugin_context.return_true(plugins=plugins, need_restart=need_restart) diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_all.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_all.py index 5221134..0bf8d5e 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_all.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_all.py @@ -35,11 +35,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather all".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag gather all".format(install_dir=obdiag_install_dir) cmd = r"{base} --from {from_option} --to {to_option} --scope {scope_option} --encrypt {encrypt_option}".format( base=base_commond, from_option=from_option, diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_clog.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_clog.py index 970032a..ec4a7ad 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_clog.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_clog.py @@ -34,11 +34,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond = r"cd {install_dir} && ./obdiag gather clog".format(install_dir=obdiag_install_dir) + base_commond = r"{install_dir}/obdiag gather clog".format(install_dir=obdiag_install_dir) cmd = r"{base} --from {from_option} --to {to_option} --encrypt {encrypt_option}".format( base = base_commond, from_option = from_option, diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_log.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_log.py index 67c8d76..2e1d22d 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_log.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_log.py @@ -34,11 +34,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather log".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag gather log".format(install_dir=obdiag_install_dir) cmd = r"{base} --from {from_option} --to {to_option} --scope {scope_option} --encrypt {encrypt_option}".format( base = base_commond, from_option = from_option, diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_obproxy_log.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_obproxy_log.py index a716b36..203dff0 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_obproxy_log.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_obproxy_log.py @@ -34,11 +34,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather obproxy_log".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag gather obproxy_log".format(install_dir=obdiag_install_dir) cmd = r"{base} --from {from_option} --to {to_option} --scope {scope_option} --encrypt {encrypt_option}".format( base = base_commond, from_option = from_option, diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_perf.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_perf.py index f3027e3..e56f7be 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_perf.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_perf.py @@ -33,11 +33,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather perf".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag gather perf".format(install_dir=obdiag_install_dir) cmd = r"{base} --scope {scope_option} ".format( base = base_commond, scope_option = scope_option diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_plan_monitor.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_plan_monitor.py index 4a426d4..5373a59 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_plan_monitor.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_plan_monitor.py @@ -33,11 +33,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather plan_monitor".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag gather plan_monitor".format(install_dir=obdiag_install_dir) cmd = r"{base} --trace_id {trace_id}".format( base=base_commond, trace_id=trace_id, diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_slog.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_slog.py index 73e747a..60070ed 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_slog.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_slog.py @@ -35,11 +35,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond = r"cd {install_dir} && ./obdiag gather slog".format(install_dir=obdiag_install_dir) + base_commond = r"{install_dir}/obdiag gather slog".format(install_dir=obdiag_install_dir) cmd = r"{base} --from {from_option} --to {to_option} --encrypt {encrypt_option}".format( base = base_commond, from_option = from_option, diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_stack.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_stack.py index e1c7739..d6adb9a 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_stack.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_stack.py @@ -33,11 +33,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond = r"cd {install_dir} && ./obdiag gather stack".format(install_dir=obdiag_install_dir) + base_commond = r"{install_dir}/obdiag gather stack".format(install_dir=obdiag_install_dir) cmd = r"{base} ".format( base = base_commond ) diff --git a/plugins/oceanbase-diagnostic-tool/1.0/gather_sysstat.py b/plugins/oceanbase-diagnostic-tool/1.0/gather_sysstat.py index fcd7c78..96d34f5 100644 --- a/plugins/oceanbase-diagnostic-tool/1.0/gather_sysstat.py +++ b/plugins/oceanbase-diagnostic-tool/1.0/gather_sysstat.py @@ -33,11 +33,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather sysstat".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag gather sysstat".format(install_dir=obdiag_install_dir) cmd = r"{base}".format( base=base_commond, ) diff --git a/plugins/oceanbase-diagnostic-tool/1.3/analyze_log.py b/plugins/oceanbase-diagnostic-tool/1.3/analyze_log.py index 4804486..ba26eff 100644 --- a/plugins/oceanbase-diagnostic-tool/1.3/analyze_log.py +++ b/plugins/oceanbase-diagnostic-tool/1.3/analyze_log.py @@ -34,11 +34,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag analyze log".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag analyze log".format(install_dir=obdiag_install_dir) if files_option_path: cmd = r"{base} --files {files_option_path}".format( base = base_commond, diff --git a/plugins/oceanbase-diagnostic-tool/1.4/checker.py b/plugins/oceanbase-diagnostic-tool/1.4/checker.py index 26f849e..b073b6c 100644 --- a/plugins/oceanbase-diagnostic-tool/1.4/checker.py +++ b/plugins/oceanbase-diagnostic-tool/1.4/checker.py @@ -42,7 +42,7 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): diff --git a/plugins/oceanbase-diagnostic-tool/1.5/analyze_flt_trace.py b/plugins/oceanbase-diagnostic-tool/1.5/analyze_flt_trace.py index 2156608..df383d4 100644 --- a/plugins/oceanbase-diagnostic-tool/1.5/analyze_flt_trace.py +++ b/plugins/oceanbase-diagnostic-tool/1.5/analyze_flt_trace.py @@ -33,11 +33,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag analyze flt_trace --flt_trace_id {trace_id}".format(install_dir=obdiag_install_dir, trace_id=flt_trace_id) + base_commond=r"{install_dir}/obdiag analyze flt_trace --flt_trace_id {trace_id}".format(install_dir=obdiag_install_dir, trace_id=flt_trace_id) cmd = base_commond if files_option_path: cmd = r"{base} --files {files_option_path}".format( diff --git a/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_list.py b/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_list.py index 986175d..7bd319d 100644 --- a/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_list.py +++ b/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_list.py @@ -32,11 +32,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather scene list".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag gather scene list".format(install_dir=obdiag_install_dir) cmd = r"{base}".format( base=base_commond, ) diff --git a/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_run.py b/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_run.py index a91cd1c..0330f06 100644 --- a/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_run.py +++ b/plugins/oceanbase-diagnostic-tool/1.6/gather_scene_run.py @@ -23,6 +23,8 @@ import os from tool import TimeUtils import _errno as err +from datetime import datetime +from _stdio import FormtatText def gather_scene_run(plugin_context, *args, **kwargs): @@ -34,11 +36,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag gather scene run --scene={scene}".format(install_dir=obdiag_install_dir,scene=scene_option) + base_commond=r"{install_dir}/obdiag gather scene run --scene={scene}".format(install_dir=obdiag_install_dir,scene=scene_option) cmd = r"{base} --from {from_option} --to {to_option} ".format( base = base_commond, from_option = from_option, @@ -47,15 +49,15 @@ def get_obdiag_cmd(): if store_dir_option: cmd = cmd + r" --store_dir {store_dir_option}".format(store_dir_option=store_dir_option) if env_option: - cmd = cmd + r" --env '{env_option}'".format(env_option=env_option) - if dis_update_option: - cmd = cmd + r" --dis_update '{dis_update_option}'".format(dis_update_option=dis_update_option) + cmd = cmd + " --env \"{env_option}\"".format(env_option=env_option) return cmd def run(): obdiag_cmd = get_obdiag_cmd() stdio.verbose('execute cmd: {}'.format(obdiag_cmd)) - return LocalClient.run_command(obdiag_cmd, env=None, stdio=stdio) + run_result = LocalClient.run_command(obdiag_cmd, env=None, stdio=stdio) + stdio.warn(FormtatText.warning("\nGather all result stored in this directory: {0}\n".format(store_dir_option))) + return run_result options = plugin_context.options obdiag_bin = "obdiag" @@ -68,8 +70,7 @@ def run(): if not scene_option: stdio.error("failed get --scene option, example: obd obdiag gather scene run {0} --scene ".format(plugin_context.deploy_name)) return plugin_context.return_false() - dis_update_option = get_option('dis_update') - store_dir_option = os.path.join(os.path.abspath(get_option('store_dir')), 'gather_scene') + store_dir_option = os.path.join(os.path.abspath(get_option('store_dir')), 'gather_scene_{0}'.format(datetime.now().strftime('%Y%m%d%H%M%S'))) obdiag_install_dir = get_option('obdiag_dir') from_option, to_option, ok = TimeUtils.parse_time_from_to(from_time=from_option, to_time=to_option, stdio=stdio) if not ok: diff --git a/plugins/oceanbase-diagnostic-tool/1.6/rca_list.py b/plugins/oceanbase-diagnostic-tool/1.6/rca_list.py index 8b0e7ab..01880d3 100644 --- a/plugins/oceanbase-diagnostic-tool/1.6/rca_list.py +++ b/plugins/oceanbase-diagnostic-tool/1.6/rca_list.py @@ -32,11 +32,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag rca list".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag rca list".format(install_dir=obdiag_install_dir) cmd = r"{base}".format( base=base_commond, ) diff --git a/plugins/oceanbase-diagnostic-tool/1.6/rca_run.py b/plugins/oceanbase-diagnostic-tool/1.6/rca_run.py index 6c86571..4def31d 100644 --- a/plugins/oceanbase-diagnostic-tool/1.6/rca_run.py +++ b/plugins/oceanbase-diagnostic-tool/1.6/rca_run.py @@ -33,11 +33,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag rca run --scene={scene}".format(install_dir=obdiag_install_dir, scene=scene_option) + base_commond=r"{install_dir}/obdiag rca run --scene={scene}".format(install_dir=obdiag_install_dir, scene=scene_option) cmd = r"{base}".format( base=base_commond, ) @@ -60,7 +60,7 @@ def run(): if not scene_option: stdio.error("failed get --scene option, example: obd obdiag rca run {0} --scene ".format(plugin_context.deploy_name)) return plugin_context.return_false() - parameters_option = get_option('parameters') + parameters_option = get_option('input_parameters') store_dir_option = os.path.abspath(get_option('store_dir')) ret = local_execute_command('%s --help' % obdiag_bin) diff --git a/plugins/oceanbase-diagnostic-tool/1.6/update_scene.py b/plugins/oceanbase-diagnostic-tool/1.6/update_scene.py index e165d91..a55c58c 100644 --- a/plugins/oceanbase-diagnostic-tool/1.6/update_scene.py +++ b/plugins/oceanbase-diagnostic-tool/1.6/update_scene.py @@ -33,11 +33,11 @@ def get_option(key, default=''): return value def local_execute_command(command, env=None, timeout=None): - command = r"cd {install_dir} && ./".format(install_dir=obdiag_install_dir) + command + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) return LocalClient.execute_command(command, env, timeout, stdio) def get_obdiag_cmd(): - base_commond=r"cd {install_dir} && ./obdiag update ".format(install_dir=obdiag_install_dir) + base_commond=r"{install_dir}/obdiag update ".format(install_dir=obdiag_install_dir) cmd = r"{base}".format( base=base_commond, ) diff --git a/plugins/oceanbase-diagnostic-tool/2.1.0/gather_ash.py b/plugins/oceanbase-diagnostic-tool/2.1.0/gather_ash.py new file mode 100644 index 0000000..60434db --- /dev/null +++ b/plugins/oceanbase-diagnostic-tool/2.1.0/gather_ash.py @@ -0,0 +1,68 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function +from ssh import LocalClient +import os +from tool import TimeUtils +import _errno as err + + +def gather_ash(plugin_context, *args, **kwargs): + def get_option(key, default=''): + value = getattr(options, key) + if value is None: + value = default + stdio.verbose('get option: %s value %s' % (key, value)) + return value + + def local_execute_command(command, env=None, timeout=None): + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) + return LocalClient.execute_command(command, env, timeout, stdio) + + def get_obdiag_cmd(): + cmd=r"{install_dir}/obdiag gather ash ".format(install_dir=obdiag_install_dir ) + options_dict = vars(options) + # check options + for option, value in options_dict.items(): + if value is not None: + if option == "obdiag_dir": + continue + cmd += ' --{} {}'.format(option, value) + return cmd + + def run(): + obdiag_cmd = get_obdiag_cmd() + stdio.verbose('execute cmd: {}'.format(obdiag_cmd)) + return LocalClient.run_command(obdiag_cmd, env=None, stdio=stdio) + options = plugin_context.options + obdiag_bin = "obdiag" + stdio = plugin_context.stdio + obdiag_install_dir = get_option('obdiag_dir') + ret = local_execute_command('%s --help' % obdiag_bin) + if not ret: + stdio.error(err.EC_OBDIAG_NOT_FOUND.format()) + return plugin_context.return_false() + try: + if run(): + plugin_context.return_true() + except KeyboardInterrupt: + stdio.exception("obdiag gather ash failed") + return plugin_context.return_false() \ No newline at end of file diff --git a/plugins/oceanbase-diagnostic-tool/2.1.0/rca_run.py b/plugins/oceanbase-diagnostic-tool/2.1.0/rca_run.py new file mode 100644 index 0000000..cfd02ef --- /dev/null +++ b/plugins/oceanbase-diagnostic-tool/2.1.0/rca_run.py @@ -0,0 +1,75 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function +from ssh import LocalClient +import _errno as err +import os + + +def rca_run(plugin_context, *args, **kwargs): + def get_option(key, default=''): + value = getattr(options, key) + if value is None: + value = default + stdio.verbose('get option: %s value %s' % (key, value)) + return value + + def local_execute_command(command, env=None, timeout=None): + command = r"{install_dir}/obdiag".format(install_dir=obdiag_install_dir) + return LocalClient.execute_command(command, env, timeout, stdio) + + def get_obdiag_cmd(): + base_commond=r"{install_dir}/obdiag rca run --scene={scene}".format(install_dir=obdiag_install_dir, scene=scene_option) + cmd = r"{base}".format( + base=base_commond, + ) + if store_dir_option: + cmd = cmd + r" --store_dir {store_dir}".format(store_dir=store_dir_option) + if parameters_option: + cmd = cmd + r" --input_parameters '{input_parameters}'".format(input_parameters=parameters_option) + return cmd + + def run(): + obdiag_cmd = get_obdiag_cmd() + stdio.verbose('execute cmd: {}'.format(obdiag_cmd)) + return LocalClient.run_command(obdiag_cmd, env=None, stdio=stdio) + + options = plugin_context.options + obdiag_bin = "obdiag" + stdio = plugin_context.stdio + obdiag_install_dir = get_option('obdiag_dir') + scene_option = get_option('scene') + if not scene_option: + stdio.error("failed get --scene option, example: obd obdiag rca run {0} --scene ".format(plugin_context.deploy_name)) + return plugin_context.return_false() + parameters_option = get_option('input_parameters') + store_dir_option = os.path.abspath(get_option('store_dir')) + + ret = local_execute_command('%s --help' % obdiag_bin) + if not ret: + stdio.error(err.EC_OBDIAG_NOT_FOUND.format()) + return plugin_context.return_false() + try: + if run(): + plugin_context.return_true() + except KeyboardInterrupt: + stdio.exception("obdiag rca run failed") + return plugin_context.return_false() \ No newline at end of file diff --git a/plugins/oceanbase/3.1.0/create_tenant.py b/plugins/oceanbase/3.1.0/create_tenant.py index 8fa0587..1717202 100644 --- a/plugins/oceanbase/3.1.0/create_tenant.py +++ b/plugins/oceanbase/3.1.0/create_tenant.py @@ -29,7 +29,7 @@ tenant_cursor = None -def exec_sql_in_tenant(sql, cursor, tenant, mode, retries=10, args=[]): +def exec_sql_in_tenant(sql, cursor, tenant, mode, retries=10, args=[], stdio=None): global tenant_cursor if not tenant_cursor: user = 'SYS' if mode == 'oracle' else 'root' @@ -37,8 +37,8 @@ def exec_sql_in_tenant(sql, cursor, tenant, mode, retries=10, args=[]): if not tenant_cursor and retries: retries -= 1 time.sleep(2) - return exec_sql_in_tenant(sql, cursor, tenant, mode, retries=retries, args=args) - return tenant_cursor.execute(sql, args=args) + return exec_sql_in_tenant(sql, cursor, tenant, mode, retries=retries, args=args, stdio=stdio) + return tenant_cursor.execute(sql, args=args, stdio=stdio) def create_tenant(plugin_context, create_tenant_options=[], cursor=None, *args, **kwargs): @@ -242,14 +242,14 @@ def error(*arg, **kwargs): # create resource unit sql = 'create resource unit %s max_cpu %.1f, max_memory %d, max_iops %d, max_disk_size %d, max_session_num %d, min_cpu %.1f, min_memory %d, min_iops %d' sql = sql % (unit_name, max_cpu, max_memory, max_iops, max_disk_size, max_session_num, min_cpu, min_memory, min_iops) - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: stdio.stop_loading('fail') return # create resource pool sql = "create resource pool %s unit='%s', unit_num=%d, zone_list=%s" % (pool_name, unit_name, unit_num, zone_list) - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: stdio.stop_loading('fail') return @@ -273,7 +273,7 @@ def error(*arg, **kwargs): sql += "set %s, %s" % (variables, set_mode) else: sql += "set %s" % set_mode - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: stdio.stop_loading('fail') return @@ -283,7 +283,7 @@ def error(*arg, **kwargs): database = get_option('database') if database: sql = 'create database {}'.format(database) - if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode) and not create_if_not_exists: + if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, stdio=stdio) and not create_if_not_exists: stdio.error('failed to create database {}'.format(database)) return @@ -296,7 +296,7 @@ def error(*arg, **kwargs): username=db_username) else: error("Create user in oracle tenant is not supported") - if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, args=[db_password]): + if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, args=[db_password], stdio=stdio): stdio.error('failed to create user {}'.format(db_username)) return diff --git a/plugins/oceanbase/3.1.0/display.py b/plugins/oceanbase/3.1.0/display.py index b3a5f96..47bae19 100644 --- a/plugins/oceanbase/3.1.0/display.py +++ b/plugins/oceanbase/3.1.0/display.py @@ -20,7 +20,33 @@ from __future__ import absolute_import, division, print_function +import re import time +import uuid + + +class Codec(object): + + NAMESPACE = uuid.uuid5(uuid.NAMESPACE_DNS, "oceanbase.com") + + @staticmethod + def encoding_version(version): + version = re.match("(\d+).(\d+).(\d+).(\d+)", version) + if version is None: + raise ValueError("Invalid version") + + ver = 0 + for i, v in enumerate(version.groups()): + ver |= int(v) << (i * 8) + return "%08x" % ver + + @staticmethod + def encoding(cid, version): + ver = Codec.encoding_version(version) + code = "%08x-%s" % (cid, ver) + uid = uuid.uuid5(Codec.NAMESPACE, code) + count = sum(uid.bytes) + return "%s-%08x-%s" % (uid, cid + count, ver) def passwd_format(passwd): @@ -51,7 +77,13 @@ def display(plugin_context, cursor, *args, **kwargs): "password": password, "cmd": cmd } - return plugin_context.return_true(info=info_dict) + + var = cursor.fetchone('select unix_timestamp(gmt_create) as gmt_create from oceanbase.__all_virtual_sys_variable limit 1', raise_exception=True, exc_level='verbose') + if var: + cid = int(var['gmt_create'] * 1000) + unique_id = Codec.encoding(cid, servers[0]['build_version']) + stdio.print('cluster unique id: %s\n' % unique_id) + return plugin_context.return_true(info=info_dict, unique_id=unique_id) except Exception as e: code = e.args[0] if code != 1146 and code != 4012: @@ -59,4 +91,5 @@ def display(plugin_context, cursor, *args, **kwargs): time.sleep(3) except: stdio.stop_loading('fail', 'observer need bootstarp') + stdio.exception('') plugin_context.return_false() diff --git a/plugins/oceanbase/3.1.0/generate_config.py b/plugins/oceanbase/3.1.0/generate_config.py index bf98533..6846649 100644 --- a/plugins/oceanbase/3.1.0/generate_config.py +++ b/plugins/oceanbase/3.1.0/generate_config.py @@ -40,7 +40,7 @@ def get_system_memory(memory_limit): return str(Capacity(system_memory, 0)) -def generate_config(plugin_context, generate_config_mini=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): +def generate_config(plugin_context, generate_config_mini=False, auto_depend=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): if return_generate_keys: generate_keys = [] if not only_generate_password: @@ -55,6 +55,8 @@ def generate_config(plugin_context, generate_config_mini=False, generate_check=T cluster_config = plugin_context.cluster_config original_global_conf = cluster_config.get_original_global_conf() + if not original_global_conf.get('appname'): + cluster_config.update_global_conf('appname', plugin_context.deploy_name) if original_global_conf.get('cluster_id') is None: cluster_config.update_global_conf('cluster_id', round(time.time()) % 4294901759, False) if generate_password: @@ -192,7 +194,7 @@ def summit_config(): success = False continue else: - memory_limit = server_config.get('memory_limit') + memory_limit = Capacity(server_config.get('memory_limit')).btyes auto_set_system_memory = False if not user_server_config.get('system_memory'): @@ -382,6 +384,9 @@ def summit_config(): # summit_config summit_config() + if auto_depend and 'ob-configserver' in plugin_context.components: + cluster_config.add_depend_component('ob-configserver') + if success: stdio.stop_loading('succeed') return plugin_context.return_true() diff --git a/plugins/oceanbase/3.1.0/upgrade.py b/plugins/oceanbase/3.1.0/upgrade.py index c4c894f..101de52 100644 --- a/plugins/oceanbase/3.1.0/upgrade.py +++ b/plugins/oceanbase/3.1.0/upgrade.py @@ -62,7 +62,7 @@ def exector(self): @property def cmd(self): if self._cmd is None: - self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, "-p '%s'" % self.pwd if self.pwd else '') + self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, '-p %s' % tool.ConfigUtil.passwd_format(self.pwd) if self.pwd else '') return self._cmd @host.setter @@ -142,7 +142,6 @@ def __init__(self, plugin_context, search_py_script_plugin, apply_param_plugin, self.unuse_lib_repository = unuse_lib_repository self.local_home_path = local_home_path self.exector_path = exector_path - self.components = plugin_context.components self.exector = None self.db = None self.cursor = None diff --git a/plugins/oceanbase/4.0.0.0/bootstrap.py b/plugins/oceanbase/4.0.0.0/bootstrap.py index 8155e18..48d5da9 100644 --- a/plugins/oceanbase/4.0.0.0/bootstrap.py +++ b/plugins/oceanbase/4.0.0.0/bootstrap.py @@ -31,8 +31,10 @@ def bootstrap(plugin_context, *args, **kwargs): stdio = plugin_context.stdio cursor = plugin_context.get_return('connect').get_return('cursor') added_components = cluster_config.get_deploy_added_components() + changed_components = cluster_config.get_deploy_changed_components() be_depend = cluster_config.be_depends global_conf = cluster_config.get_global_conf() + ocp_config = cluster_config.get_be_depend_config('ocp-server-ce', with_default=False) bootstrap = [] floor_servers = {} zones_config = {} @@ -98,11 +100,15 @@ def is_bootstrap(): stdio.stop_loading('succeed') has_obproxy = False + has_obproxy_scale_out = False for component_name in ['obproxy', 'obproxy-ce']: if component_name in added_components and component_name in be_depend: has_obproxy = True break - if has_obproxy or 'proxyro_password' in global_conf: + if component_name in changed_components: + has_obproxy_scale_out = True + break + if has_obproxy or ('proxyro_password' in global_conf and not has_obproxy_scale_out): value = global_conf['proxyro_password'] if global_conf.get('proxyro_password') is not None else '' sql = 'create user if not exists "proxyro" IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) @@ -110,7 +116,7 @@ def is_bootstrap(): raise_cursor.execute(sql, [value]) has_obagent = "obagent" in added_components and "obagent" in be_depend - if has_obagent or 'ocp_agent_monitor_password' in global_conf: + if has_obagent or ('ocp_agent_monitor_password' in global_conf and "obagent" not in changed_components): value = global_conf['ocp_agent_monitor_password'] if global_conf.get('ocp_agent_monitor_password') is not None else '' sql = 'create user if not exists "ocp_monitor" IDENTIFIED BY %s' stdio.verbose(sql) @@ -142,6 +148,8 @@ def is_bootstrap(): # set create tenant variable for key in global_conf_with_default: if key.startswith(prefix) and original_global_conf.get(key, None): + if ocp_config and ocp_config.get(key, None): + global_conf_with_default[key] = ocp_config[key] global_conf_with_default[prefix + 'tenant'][key.replace(prefix, '', 1)] = global_conf_with_default[key] tenant_info = global_conf_with_default[prefix + "tenant"] tenant_info["variables"] = "ob_tcp_invited_nodes='%'" diff --git a/plugins/oceanbase/4.0.0.0/create_tenant.py b/plugins/oceanbase/4.0.0.0/create_tenant.py index a443f6b..86cd967 100644 --- a/plugins/oceanbase/4.0.0.0/create_tenant.py +++ b/plugins/oceanbase/4.0.0.0/create_tenant.py @@ -30,7 +30,7 @@ tenant_cursor_cache = defaultdict(dict) -def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_exception=True, retries=20, args=[]): +def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_exception=True, retries=20, args=[], stdio=None): if not user: user = 'SYS' if mode == 'oracle' else 'root' # find tenant ip, port @@ -51,8 +51,8 @@ def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_ex break if not tenant_cursor and retries: time.sleep(1) - return exec_sql_in_tenant(sql, cursor, tenant, mode, user, password, print_exception=print_exception, retries=retries-1, args=args) - return tenant_cursor.execute(sql, args=args, raise_exception=False, exc_level='verbose') if tenant_cursor else False + return exec_sql_in_tenant(sql, cursor, tenant, mode, user, password, print_exception=print_exception, retries=retries-1, args=args, stdio=stdio) + return tenant_cursor.execute(sql, args=args, raise_exception=False, exc_level='verbose', stdio=stdio) if tenant_cursor else False def create_tenant(plugin_context, create_tenant_options=[], cursor=None, *args, **kwargs): @@ -254,14 +254,14 @@ def error(msg='', *arg, **kwargs): if log_disk_size is not None: sql += ', log_disk_size %d' % log_disk_size - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: error() return # create resource pool sql = "create resource pool %s unit='%s', unit_num=%d, zone_list=%s" % (pool_name, unit_name, unit_num, zone_list) - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: error() return @@ -285,7 +285,7 @@ def error(msg='', *arg, **kwargs): sql += "set %s, %s" % (variables, set_mode) else: sql += "set %s" % set_mode - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: error() return diff --git a/plugins/oceanbase/4.0.0.0/generate_config.py b/plugins/oceanbase/4.0.0.0/generate_config.py index 431859c..2a02784 100644 --- a/plugins/oceanbase/4.0.0.0/generate_config.py +++ b/plugins/oceanbase/4.0.0.0/generate_config.py @@ -50,7 +50,7 @@ def get_system_memory(memory_limit, min_pool_memory, generate_config_mini): return max(system_memory, min_pool_memory) -def generate_config(plugin_context, generate_config_mini=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): +def generate_config(plugin_context, generate_config_mini=False, auto_depend=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): if return_generate_keys: generate_keys = [] if not only_generate_password: @@ -64,6 +64,8 @@ def generate_config(plugin_context, generate_config_mini=False, generate_check=T cluster_config = plugin_context.cluster_config original_global_conf = cluster_config.get_original_global_conf() + if not original_global_conf.get('appname'): + cluster_config.update_global_conf('appname', plugin_context.deploy_name) if original_global_conf.get('cluster_id') is None: cluster_config.update_global_conf('cluster_id', round(time.time()) % 4294901759, False) if generate_password: @@ -537,6 +539,9 @@ def summit_config(): # summit_config summit_config() + if auto_depend and 'ob-configserver' in plugin_context.components: + cluster_config.add_depend_component('ob-configserver') + if success: stdio.stop_loading('succeed') return plugin_context.return_true() diff --git a/plugins/oceanbase/4.0.0.0/parameter.yaml b/plugins/oceanbase/4.0.0.0/parameter.yaml index f775d63..f7a10c2 100644 --- a/plugins/oceanbase/4.0.0.0/parameter.yaml +++ b/plugins/oceanbase/4.0.0.0/parameter.yaml @@ -1830,6 +1830,8 @@ description_en: 'the number of RDMA I/O threads for Libreasy. Range: [0, 8] in integer, 0 stands for RDMA being disabled.' description_local: Libreasy 的 RDMA I/O 线程数。 - name: production_mode + name_local: 生产模式开关 + essential: true require: true type: BOOL default: true @@ -1844,7 +1846,7 @@ require: false type: DICT default: - tenant_name: ocp + tenant_name: ocp_meta max_cpu: 1 memory_size: 2147483648 need_redeploy: true @@ -1880,7 +1882,7 @@ - name: ocp_meta_db require: false type: SAFE_STRING - default: ocp_express + default: ocp_meta need_redeploy: true description_en: The database name for ocp meta db description_local: ocp express的元数据库使用的数据库名 diff --git a/plugins/oceanbase/4.0.0.0/start_check.py b/plugins/oceanbase/4.0.0.0/start_check.py index 5d21e95..b6d816b 100644 --- a/plugins/oceanbase/4.0.0.0/start_check.py +++ b/plugins/oceanbase/4.0.0.0/start_check.py @@ -208,12 +208,13 @@ def system_memory_check(): 'aio': err.CheckStatus(), 'net': err.CheckStatus(), 'ntp': err.CheckStatus(), - 'ocp meta db': err.CheckStatus() + 'ocp tenant memory': err.CheckStatus(), + 'ocp tenant disk': err.CheckStatus() } check_status[server].update(kernel_check_status) if work_dir_check: check_status[server]['dir'] = err.CheckStatus() - + if init_check_status: return plugin_context.return_true(start_check_status=check_status) @@ -663,19 +664,31 @@ def system_memory_check(): critical('disk', err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=p, avail=str(Capacity(avail)), need=str(Capacity(need))), tmp_suggests + suggests) global_conf = cluster_config.get_global_conf() - has_ocp = 'ocp-express' in plugin_context.components + has_ocp = 'ocp-express' in plugin_context.components or 'ocp-server-ce' in plugin_context.components if not has_ocp and any([key.startswith('ocp_meta') for key in global_conf]): has_ocp = True if has_ocp and need_bootstrap and parameter_check: global_conf_with_default = copy.deepcopy(cluster_config.get_global_conf_with_default()) original_global_conf = cluster_config.get_original_global_conf() - ocp_meta_tenant_prefix = 'ocp_meta_tenant_' - for key in global_conf_with_default: - if key.startswith(ocp_meta_tenant_prefix) and original_global_conf.get(key, None): - global_conf_with_default['ocp_meta_tenant'][key.replace(ocp_meta_tenant_prefix, '', 1)] = global_conf_with_default[key] - meta_db_memory_size = Capacity(global_conf_with_default['ocp_meta_tenant'].get('memory_size')).btyes + tenants_componets_map = { + "meta": ["ocp-express", "ocp-server", "ocp-server-ce"], + "monitor": ["ocp-server", "ocp-server-ce"], + } + tenant_memory = tenant_log_disk = memory_limit = system_memory = log_disk_size = sys_log_disk_size = 0 + for tenant, component_list in tenants_componets_map.items(): + prefix = "ocp_%s_tenant_" % tenant + tenant_key = "ocp_%s_tenant" % tenant + for key in global_conf_with_default: + if key.startswith(prefix) and not original_global_conf.get(key, None): + global_conf_with_default['ocp_%s_tenant' % tenant][key.replace(prefix, '', 1)] = global_conf_with_default[key] + if set(list(plugin_context.components)) & set(component_list): + tenant_memory_default = global_conf_with_default[tenant_key].get('memory_size', '0') + tenant_memory += Capacity(original_global_conf.get(tenant_key, {}).get('memory_size', tenant_memory_default)).btyes + tenant_log_disk_default = global_conf_with_default[tenant_key].get('log_disk_size', '0') + tenant_log_disk += Capacity(original_global_conf.get(tenant_key, {}).get('log_disk_size', tenant_log_disk_default)).btyes + servers_sys_memory = {} - if meta_db_memory_size: + if tenant_memory: sys_memory_size = None if 'sys_tenant' in global_conf and 'memory_size' in global_conf['sys_tenant']: sys_memory_size = global_conf['sys_tenant']['memory_size'] @@ -690,32 +703,19 @@ def system_memory_check(): system_memory = get_system_memory(memory_limit, min_pool_memory) if not sys_memory_size: sys_memory_size = servers_sys_memory[server] = max(min_pool_memory, min((memory_limit - system_memory) * 0.25, Capacity('16G').btyes)) - if meta_db_memory_size + system_memory + sys_memory_size <= memory_limit: + if tenant_memory + system_memory + sys_memory_size <= memory_limit: break else: - suggest = err.SUG_OCP_EXPRESS_REDUCE_META_DB_MEM.format() - suggest.auto_fix = True - if 'ocp_meta_tenant_memory_size' in global_generate_config: - suggest.auto_fix = False - error('ocp meta db', err.EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_MEM.format(), [suggest]) - - meta_db_log_disk_size = global_conf_with_default['ocp_meta_tenant'].get('log_disk_size') - meta_db_log_disk_size = Capacity(meta_db_log_disk_size).btyes if meta_db_log_disk_size else meta_db_log_disk_size - if not meta_db_log_disk_size and meta_db_memory_size: - meta_db_log_disk_size = meta_db_memory_size * 3 - if meta_db_log_disk_size: + critical('ocp tenant memory', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='mem', avail=Capacity(memory_limit - system_memory - sys_memory_size), need=Capacity(tenant_memory))) + + if tenant_log_disk: for server in cluster_config.servers: log_disk_size = servers_log_disk_size[server] sys_log_disk_size = servers_sys_memory.get(server, 0) - if meta_db_log_disk_size + sys_log_disk_size <= log_disk_size: + if tenant_log_disk + sys_log_disk_size <= log_disk_size: break else: - suggest = err.SUG_OCP_EXPRESS_REDUCE_META_DB_LOG_DISK.format() - suggest.auto_fix = True - if 'ocp_meta_tenant_log_disk_size' in global_generate_config: - suggest.auto_fix = False - error('ocp meta db', err.EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_LOG_DISK.format(), [suggest]) - + critical('ocp tenant disk', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='log_disk_size', avail=Capacity(log_disk_size - sys_log_disk_size), need=Capacity(tenant_log_disk))) if success: for ip in servers_net_interface: diff --git a/plugins/oceanbase/4.0.0.0/upgrade.py b/plugins/oceanbase/4.0.0.0/upgrade.py index 3771654..0502d3b 100644 --- a/plugins/oceanbase/4.0.0.0/upgrade.py +++ b/plugins/oceanbase/4.0.0.0/upgrade.py @@ -63,7 +63,7 @@ def exector(self): @property def cmd(self): if self._cmd is None: - self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, "-p '%s'" % self.pwd if self.pwd else '') + self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, '-p %s' % tool.ConfigUtil.passwd_format(self.pwd) if self.pwd else '') return self._cmd @host.setter @@ -143,7 +143,6 @@ def __init__(self, plugin_context, search_py_script_plugin, apply_param_plugin, self.unuse_lib_repository = unuse_lib_repository self.local_home_path = local_home_path self.exector_path = exector_path - self.components = plugin_context.components self.exector = None self.db = None self.cursor = None diff --git a/plugins/oceanbase/4.0.0.0/upgrade_route.py b/plugins/oceanbase/4.0.0.0/upgrade_route.py index ebf3c02..f2f126b 100644 --- a/plugins/oceanbase/4.0.0.0/upgrade_route.py +++ b/plugins/oceanbase/4.0.0.0/upgrade_route.py @@ -165,11 +165,15 @@ def findShortestUpgradePath(self, current_repository, dest_repository, stdio): def format_route(routes, repository): route_res = [] + from_version = repository.version + from_release = repository.release for i, node in enumerate(routes): require_from_binary = getattr(node, 'require_from_binary', False) if getattr(node, 'when_come_from', False): - require_from_binary = require_from_binary and (repository.version in node.when_come_from or '%s-%s' % (repository.version, repository.release.split('.')[0]) in node.when_come_from) - + require_from_binary = require_from_binary and (from_version in node.when_come_from or '%s-%s' % (from_version, from_release.split('.')[0]) in node.when_come_from) + if require_from_binary: + from_version = node.version + from_release = node.release route_res.append({ 'version': node.version, 'release': None if node.release == VersionNode.RELEASE_NULL else node.release, diff --git a/plugins/oceanbase/4.1.0.0/upgrade.py b/plugins/oceanbase/4.1.0.0/upgrade.py index ebf7101..c879ed7 100644 --- a/plugins/oceanbase/4.1.0.0/upgrade.py +++ b/plugins/oceanbase/4.1.0.0/upgrade.py @@ -65,7 +65,7 @@ def exector(self): @property def cmd(self): if self._cmd is None: - self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, "-p '%s'" % self.pwd if self.pwd else '') + self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, '-p %s' % tool.ConfigUtil.passwd_format(self.pwd) if self.pwd else '') return self._cmd @host.setter @@ -146,7 +146,6 @@ def __init__(self, plugin_context, search_py_script_plugin, apply_param_plugin, self.unuse_lib_repository = unuse_lib_repository self.local_home_path = local_home_path self.exector_path = exector_path - self.components = plugin_context.components self.exector = None self.db = None self.cursor = None diff --git a/plugins/oceanbase/4.2.0.0/create_tenant.py b/plugins/oceanbase/4.2.0.0/create_tenant.py index f1b288c..c2c9ca8 100644 --- a/plugins/oceanbase/4.2.0.0/create_tenant.py +++ b/plugins/oceanbase/4.2.0.0/create_tenant.py @@ -31,7 +31,7 @@ tenant_cursor_cache = defaultdict(dict) -def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_exception=True, retries=20, args=[]): +def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_exception=True, retries=20, args=[], stdio=None): if not user: user = 'SYS' if mode == 'oracle' else 'root' # find tenant ip, port @@ -52,8 +52,8 @@ def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_ex break if not tenant_cursor and retries: time.sleep(1) - return exec_sql_in_tenant(sql, cursor, tenant, mode, user, password, print_exception=print_exception, retries=retries-1, args=args) - return tenant_cursor.execute(sql, args=args, raise_exception=False, exc_level='verbose') if tenant_cursor else False + return exec_sql_in_tenant(sql, cursor, tenant, mode, user, password, print_exception=print_exception, retries=retries-1, args=args, stdio=stdio) + return tenant_cursor.execute(sql, args=args, raise_exception=False, exc_level='verbose', stdio=stdio) if tenant_cursor else False def dump_standby_relation(relation_tenants, cluster_configs, dump_relation_tenants, stdio): @@ -344,7 +344,7 @@ def error(msg='', *arg, **kwargs): if log_disk_size is not None: sql += ', log_disk_size %d' % log_disk_size - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: error() return @@ -352,7 +352,7 @@ def error(msg='', *arg, **kwargs): # create resource pool sql = "create resource pool %s unit='%s', unit_num=%d, zone_list=%s" % (pool_name, unit_name, unit_num, zone_list) try: - cursor.execute(sql, raise_exception=True) + cursor.execute(sql, raise_exception=True, stdio=stdio) except Exception as e: stdio.exception('create resource pool failed, you can try again by using SQL "drop resource pool {}" to delete the resource pool, if you are certain that the resource pool is not being used. error info: {}'.format(pool_name, e)) return @@ -379,7 +379,7 @@ def error(msg='', *arg, **kwargs): else: sql += "set %s" % set_mode try: - cursor.execute(sql, raise_exception=True) + cursor.execute(sql, raise_exception=True, stdio=stdio) except Exception as e: stdio.exception('Create error, error info:{}'.format(e)) return @@ -424,7 +424,7 @@ def error(msg='', *arg, **kwargs): sql += ", locality = '%s'" % locality try: - cursor.execute(sql, raise_exception=True, exc_level='verbose') + cursor.execute(sql, raise_exception=True, exc_level='verbose', stdio=stdio) except Exception as e: stdio.verbose('create standby tenant fail, clean and retry. fail message:{}'.format(e)) # clean and retry create standby tenant @@ -435,7 +435,7 @@ def error(msg='', *arg, **kwargs): if res: # drop tenant tenant_id = res['TENANT_ID'] - res = cursor.execute("drop tenant %s FORCE" % name, raise_exception=False) + res = cursor.execute("drop tenant %s FORCE" % name, raise_exception=False, stdio=stdio) if res is False: error('Create standby tenant fail. message:{}'.format(e)) return @@ -458,7 +458,7 @@ def error(msg='', *arg, **kwargs): # create again try: - cursor.execute(sql, raise_exception=True) + cursor.execute(sql, raise_exception=True, stdio=stdio) except Exception as e: retry_message = 'After resolving this issue, you can clean up the environment by manually executing "obd cluster tenant drop {} -t {}", and then wait for a while before re-creating the standby tenant.'.format(standby_deploy_name, name) error("create standby tenant failed, error: {}".format(e)) diff --git a/plugins/oceanbase/4.2.0.0/generate_config.py b/plugins/oceanbase/4.2.0.0/generate_config.py index 2b4be81..900ee01 100644 --- a/plugins/oceanbase/4.2.0.0/generate_config.py +++ b/plugins/oceanbase/4.2.0.0/generate_config.py @@ -50,7 +50,7 @@ def get_system_memory(memory_limit, min_pool_memory, generate_config_mini): return max(system_memory, min_pool_memory) -def generate_config(plugin_context, generate_config_mini=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): +def generate_config(plugin_context, generate_config_mini=False, auto_depend=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): if return_generate_keys: generate_keys = [] if not only_generate_password: @@ -65,6 +65,8 @@ def generate_config(plugin_context, generate_config_mini=False, generate_check=T cluster_config = plugin_context.cluster_config original_global_conf = cluster_config.get_original_global_conf() + if not original_global_conf.get('appname'): + cluster_config.update_global_conf('appname', plugin_context.deploy_name) if original_global_conf.get('cluster_id') is None: cluster_config.update_global_conf('cluster_id', round(time.time()) % 4294901759, False) if generate_password: @@ -553,6 +555,9 @@ def summit_config(): # summit_config summit_config() + if auto_depend and 'ob-configserver' in plugin_context.components: + cluster_config.add_depend_component('ob-configserver') + if success: stdio.stop_loading('succeed') return plugin_context.return_true() diff --git a/plugins/oceanbase/4.2.0.0/parameter.yaml b/plugins/oceanbase/4.2.0.0/parameter.yaml index 37653f0..ce64655 100644 --- a/plugins/oceanbase/4.2.0.0/parameter.yaml +++ b/plugins/oceanbase/4.2.0.0/parameter.yaml @@ -1794,6 +1794,8 @@ description_en: 'the number of RDMA I/O threads for Libreasy. Range: [0, 8] in integer, 0 stands for RDMA being disabled.' description_local: Libreasy 的 RDMA I/O 线程数。 - name: production_mode + name_local: 生产模式开关 + essential: true require: true type: BOOL default: true @@ -1808,7 +1810,7 @@ require: false type: DICT default: - tenant_name: ocp + tenant_name: ocp_meta max_cpu: 1 memory_size: 2147483648 need_redeploy: true @@ -1844,7 +1846,7 @@ - name: ocp_meta_db require: false type: SAFE_STRING - default: ocp_express + default: ocp_meta need_redeploy: true description_en: The database name for ocp meta db description_local: ocp express的元数据库使用的数据库名 diff --git a/plugins/oceanbase/4.2.0.0/start_check.py b/plugins/oceanbase/4.2.0.0/start_check.py index 936960f..5d41dde 100644 --- a/plugins/oceanbase/4.2.0.0/start_check.py +++ b/plugins/oceanbase/4.2.0.0/start_check.py @@ -209,7 +209,8 @@ def system_memory_check(): 'aio': err.CheckStatus(), 'net': err.CheckStatus(), 'ntp': err.CheckStatus(), - 'ocp meta db': err.CheckStatus() + 'ocp tenant memory': err.CheckStatus(), + 'ocp tenant disk': err.CheckStatus() } check_status[server].update(kernel_check_status) if work_dir_check: @@ -663,19 +664,31 @@ def system_memory_check(): critical('disk', err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=p, avail=Capacity(avail), need=Capacity(need)), tmp_suggests + suggests) global_conf = cluster_config.get_global_conf() - has_ocp = 'ocp-express' in plugin_context.components + has_ocp = 'ocp-express' in plugin_context.components or 'ocp-server-ce' in plugin_context.components if not has_ocp and any([key.startswith('ocp_meta') for key in global_conf]): has_ocp = True if has_ocp and need_bootstrap and parameter_check: global_conf_with_default = copy.deepcopy(cluster_config.get_global_conf_with_default()) original_global_conf = cluster_config.get_original_global_conf() - ocp_meta_tenant_prefix = 'ocp_meta_tenant_' - for key in global_conf_with_default: - if key.startswith(ocp_meta_tenant_prefix) and original_global_conf.get(key, None): - global_conf_with_default['ocp_meta_tenant'][key.replace(ocp_meta_tenant_prefix, '', 1)] = global_conf_with_default[key] - meta_db_memory_size = Capacity(global_conf_with_default['ocp_meta_tenant'].get('memory_size')).btyes + tenants_componets_map = { + "meta": ["ocp-express", "ocp-server", "ocp-server-ce"], + "monitor": ["ocp-server", "ocp-server-ce"], + } + tenant_memory = tenant_log_disk = memory_limit = system_memory = log_disk_size = sys_log_disk_size = 0 + for tenant, component_list in tenants_componets_map.items(): + prefix = "ocp_%s_tenant_" % tenant + tenant_key = "ocp_%s_tenant" % tenant + for key in global_conf_with_default: + if key.startswith(prefix) and not original_global_conf.get(key, None): + global_conf_with_default['ocp_%s_tenant' % tenant][key.replace(prefix, '', 1)] = global_conf_with_default[key] + if set(list(plugin_context.components)) & set(component_list): + tenant_memory_default = global_conf_with_default[tenant_key].get('memory_size', '0') + tenant_memory += Capacity(original_global_conf.get(tenant_key, {}).get('memory_size', tenant_memory_default)).btyes + tenant_log_disk_default = global_conf_with_default[tenant_key].get('log_disk_size', '0') + tenant_log_disk += Capacity(original_global_conf.get(tenant_key, {}).get('log_disk_size', tenant_log_disk_default)).btyes + servers_sys_memory = {} - if meta_db_memory_size: + if tenant_memory: sys_memory_size = None if 'sys_tenant' in global_conf and 'memory_size' in global_conf['sys_tenant']: sys_memory_size = global_conf['sys_tenant']['memory_size'] @@ -690,31 +703,19 @@ def system_memory_check(): system_memory = get_system_memory(memory_limit, min_pool_memory) if not sys_memory_size: sys_memory_size = servers_sys_memory[server] = max(min_pool_memory, min((memory_limit - system_memory) * 0.25, Capacity('16G').btyes)) - if meta_db_memory_size + system_memory + sys_memory_size <= memory_limit: + if tenant_memory + system_memory + sys_memory_size <= memory_limit: break else: - suggest = err.SUG_OCP_EXPRESS_REDUCE_META_DB_MEM.format() - suggest.auto_fix = True - if 'ocp_meta_tenant_memory_size' in global_generate_config: - suggest.auto_fix = False - error('ocp meta db', err.EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_MEM.format(), [suggest]) - - meta_db_log_disk_size = global_conf_with_default['ocp_meta_tenant'].get('log_disk_size') - meta_db_log_disk_size = Capacity(meta_db_log_disk_size).btyes if meta_db_log_disk_size else meta_db_log_disk_size - if not meta_db_log_disk_size and meta_db_memory_size: - meta_db_log_disk_size = meta_db_memory_size * 3 - if meta_db_log_disk_size: + critical('ocp tenant memory', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='memory', avail=Capacity(memory_limit - system_memory - sys_memory_size), need=Capacity(tenant_memory))) + + if tenant_log_disk: for server in cluster_config.servers: log_disk_size = servers_log_disk_size[server] sys_log_disk_size = servers_sys_memory.get(server, 0) - if meta_db_log_disk_size + sys_log_disk_size <= log_disk_size: + if tenant_log_disk + sys_log_disk_size <= log_disk_size: break else: - suggest = err.SUG_OCP_EXPRESS_REDUCE_META_DB_LOG_DISK.format() - suggest.auto_fix = True - if 'ocp_meta_tenant_log_disk_size' in global_generate_config: - suggest.auto_fix = False - error('ocp meta db', err.EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_LOG_DISK.format(), [suggest]) + critical('ocp tenant disk', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='log_disk_size', avail=Capacity(log_disk_size - sys_log_disk_size), need=Capacity(tenant_log_disk))) if success: for ip in servers_net_interface: diff --git a/plugins/oceanbase/4.2.1.0/bootstrap.py b/plugins/oceanbase/4.2.1.0/bootstrap.py index 4545547..dd065db 100644 --- a/plugins/oceanbase/4.2.1.0/bootstrap.py +++ b/plugins/oceanbase/4.2.1.0/bootstrap.py @@ -32,8 +32,10 @@ def bootstrap(plugin_context, *args, **kwargs): stdio = plugin_context.stdio cursor = plugin_context.get_return('connect').get_return('cursor') added_components = cluster_config.get_deploy_added_components() + changed_components = cluster_config.get_deploy_changed_components() be_depend = cluster_config.be_depends global_conf = cluster_config.get_global_conf() + ocp_config = cluster_config.get_be_depend_config('ocp-server-ce', with_default=False) bootstrap = [] floor_servers = {} zones_config = {} @@ -99,11 +101,15 @@ def is_bootstrap(): stdio.stop_loading('succeed') has_obproxy = False + has_obproxy_scale_out = False for component_name in ['obproxy', 'obproxy-ce']: if component_name in added_components and component_name in be_depend: has_obproxy = True break - if has_obproxy or 'proxyro_password' in global_conf: + if component_name in changed_components: + has_obproxy_scale_out = True + break + if has_obproxy or ('proxyro_password' in global_conf and not has_obproxy_scale_out): value = global_conf['proxyro_password'] if global_conf.get('proxyro_password') is not None else '' sql = 'create user if not exists "proxyro" IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) @@ -111,15 +117,15 @@ def is_bootstrap(): raise_cursor.execute(sql, [value]) has_oblogproxy = "oblogproxy" in added_components and "oblogproxy" in be_depend - if has_oblogproxy or 'cdcro_password' in global_conf: + if has_oblogproxy or ('cdcro_password' in global_conf and 'oblogproxy' not in changed_components): value = global_conf['cdcro_password'] if global_conf.get('cdcro_password') is not None else '' - sql = 'create user "cdcro" IDENTIFIED BY %s' + sql = 'create user if not exists "cdcro" IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) sql = 'grant select on oceanbase.* to cdcro IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) has_obagent = "obagent" in added_components and "obagent" in be_depend - if has_obagent or 'ocp_agent_monitor_password' in global_conf: + if has_obagent or ('ocp_agent_monitor_password' in global_conf and 'obagent' not in changed_components): value = global_conf['ocp_agent_monitor_password'] if global_conf.get('ocp_agent_monitor_password') is not None else '' sql = 'create user if not exists "ocp_monitor" IDENTIFIED BY %s' stdio.verbose(sql) @@ -151,6 +157,8 @@ def is_bootstrap(): # set create tenant variable for key in global_conf_with_default: if key.startswith(prefix) and original_global_conf.get(key, None): + if ocp_config and ocp_config.get(key, None): + global_conf_with_default[key] = ocp_config[key] global_conf_with_default[prefix + 'tenant'][key.replace(prefix, '', 1)] = global_conf_with_default[key] tenant_info = global_conf_with_default[prefix + "tenant"] tenant_info["variables"] = "ob_tcp_invited_nodes='%'" diff --git a/plugins/oceanbase/4.2.1.0/generate_config.py b/plugins/oceanbase/4.2.1.0/generate_config.py index 273afa3..604d871 100644 --- a/plugins/oceanbase/4.2.1.0/generate_config.py +++ b/plugins/oceanbase/4.2.1.0/generate_config.py @@ -50,7 +50,7 @@ def get_system_memory(memory_limit, min_pool_memory, generate_config_mini): return max(system_memory, min_pool_memory) -def generate_config(plugin_context, generate_config_mini=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): +def generate_config(plugin_context, generate_config_mini=False, auto_depend=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): if return_generate_keys: generate_keys = [] if not only_generate_password: @@ -65,6 +65,8 @@ def generate_config(plugin_context, generate_config_mini=False, generate_check=T cluster_config = plugin_context.cluster_config original_global_conf = cluster_config.get_original_global_conf() + if not original_global_conf.get('appname'): + cluster_config.update_global_conf('appname', plugin_context.deploy_name) if original_global_conf.get('cluster_id') is None: cluster_config.update_global_conf('cluster_id', round(time.time()) % 4294901759, False) if generate_password: @@ -519,6 +521,10 @@ def summit_config(): for server in servers: del generate_configs[server][key] + + if auto_depend and 'ob-configserver' in plugin_context.components: + cluster_config.add_depend_component('ob-configserver') + # merge_generate_config merge_config = {} generate_global_config = generate_configs['global'] @@ -563,6 +569,8 @@ def summit_config(): # summit_config summit_config() + + if success: stdio.stop_loading('succeed') return plugin_context.return_true() diff --git a/plugins/oceanbase/4.2.1.0/parameter.yaml b/plugins/oceanbase/4.2.1.0/parameter.yaml index 3efe577..f06ca39 100644 --- a/plugins/oceanbase/4.2.1.0/parameter.yaml +++ b/plugins/oceanbase/4.2.1.0/parameter.yaml @@ -1803,6 +1803,8 @@ description_en: 'the number of RDMA I/O threads for Libreasy. Range: [0, 8] in integer, 0 stands for RDMA being disabled.' description_local: Libreasy 的 RDMA I/O 线程数。 - name: production_mode + name_local: 生产模式开关 + essential: true require: true type: BOOL default: true diff --git a/plugins/oceanbase/4.2.1.4/bootstrap.py b/plugins/oceanbase/4.2.1.4/bootstrap.py index 9ff8806..c486916 100644 --- a/plugins/oceanbase/4.2.1.4/bootstrap.py +++ b/plugins/oceanbase/4.2.1.4/bootstrap.py @@ -44,8 +44,10 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): cursor = plugin_context.get_return('connect').get_return('cursor') ocs_cursor = plugin_context.get_return('connect').get_return('ocs_cursor') added_components = [] if cluster_config.added_servers else cluster_config.get_deploy_added_components() + changed_components = cluster_config.get_deploy_changed_components() be_depend = cluster_config.be_depends global_conf = cluster_config.get_global_conf() + ocp_config = cluster_config.get_be_depend_config('ocp-server-ce', with_default=False) bootstrap = [] floor_servers = {} zones_config = {} @@ -161,7 +163,7 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): success = True for server in cluster_config.servers: # get status - status = ocs_cursor[server].status_request() + status = ocs_cursor[server].status_request(ignore_ConnectionError=True) if not status: stdio.verbose('get status failed, count: %d' % count) success = False @@ -212,11 +214,15 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): stdio.stop_loading('succeed') has_obproxy = False + has_obproxy_scale_out = False for component_name in ['obproxy', 'obproxy-ce']: if component_name in added_components and component_name in be_depend: has_obproxy = True break - if has_obproxy or 'proxyro_password' in global_conf: + if component_name in changed_components: + has_obproxy_scale_out = True + break + if has_obproxy or ('proxyro_password' in global_conf and not has_obproxy_scale_out): value = global_conf['proxyro_password'] if global_conf.get('proxyro_password') is not None else '' sql = 'create user if not exists "proxyro" IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) @@ -224,15 +230,15 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): raise_cursor.execute(sql, [value]) has_oblogproxy = "oblogproxy" in added_components and "oblogproxy" in be_depend - if has_oblogproxy or 'cdcro_password' in global_conf: + if has_oblogproxy or ('cdcro_password' in global_conf and 'oblogproxy' not in changed_components): value = global_conf['cdcro_password'] if global_conf.get('cdcro_password') is not None else '' - sql = 'create user "cdcro" IDENTIFIED BY %s' + sql = 'create user if not exists "cdcro" IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) sql = 'grant select on oceanbase.* to cdcro IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) has_obagent = "obagent" in added_components and "obagent" in be_depend - if has_obagent or 'ocp_agent_monitor_password' in global_conf: + if has_obagent or ('ocp_agent_monitor_password' in global_conf and not 'obagent' not in changed_components): value = global_conf['ocp_agent_monitor_password'] if global_conf.get('ocp_agent_monitor_password') is not None else '' sql = 'create user if not exists "ocp_monitor" IDENTIFIED BY %s' stdio.verbose(sql) @@ -264,6 +270,8 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): # set create tenant variable for key in global_conf_with_default: if key.startswith(prefix) and original_global_conf.get(key, None): + if ocp_config and ocp_config.get(key, None): + global_conf_with_default[key] = ocp_config[key] global_conf_with_default[prefix + 'tenant'][key.replace(prefix, '', 1)] = global_conf_with_default[key] tenant_info = global_conf_with_default[prefix + "tenant"] tenant_info["variables"] = "ob_tcp_invited_nodes='%'" diff --git a/plugins/oceanbase/4.2.1.4/connect.py b/plugins/oceanbase/4.2.1.4/connect.py index fcee8bc..0ce5b72 100644 --- a/plugins/oceanbase/4.2.1.4/connect.py +++ b/plugins/oceanbase/4.2.1.4/connect.py @@ -306,8 +306,16 @@ def _make_headers(self, headers=None, safe=None, uri=None): def _make_url(self, url): return 'http://{ip}:{port}{url}'.format(ip=self.ip, port=self.port, url=url) + + # put new password to obshell + def update_password(self, password): + self.password = password + self._auth_header = None + # Invoke any API that requires the `safe_request` method to securely update passwords. + self.pkg_upload_request() + return - def _request(self, method, url, data=None, headers=None, params=None, safe=None, *args, **kwargs): + def _request(self, method, url, data=None, headers=None, params=None, safe=None, ignore_ConnectionError=False, *args, **kwargs): try: if data is not None: data = json.dumps(data) @@ -318,6 +326,9 @@ def _request(self, method, url, data=None, headers=None, params=None, safe=None, self.stdio.verbose('send request to obshell: method: {}, url: {}, data: {}, headers: {}, params: {}'.format(method, url, data, headers, params)) resp = requests.request(method, self._make_url(url), data=data, headers=self._make_headers(headers, safe, url), params=params, *args, **kwargs) except Exception as e: + if ignore_ConnectionError and isinstance(e, requests.exceptions.ConnectionError): + self.stdio.verbose('Attempt to connect failed:{}'.format(self._make_url(url))) + return None self.stdio.error('request error: {}'.format(e)) return None parsed_resp = self._response_parser(resp) @@ -366,8 +377,8 @@ def _get_secrets(self): resp = self._request('GET', '/api/v1/secret') return resp.public_key if resp else None - def request(self, method, url, data=None, headers=None, params=None, *args, **kwargs): - return self._request(method, url, data, headers, params, *args, **kwargs) + def request(self, method, url, data=None, headers=None, params=None, ignore_ConnectionError=False, *args, **kwargs): + return self._request(method, url, data, headers, params, ignore_ConnectionError=ignore_ConnectionError, *args, **kwargs) def safe_request(self, method, url, data=None, headers=None, params=None, *args, **kwargs): return self._request(method, url, data, headers, params, safe=True, *args, **kwargs) @@ -397,8 +408,8 @@ def info_request(self): resp = self.request('GET', '/api/v1/info') return resp.info if resp and resp.type == 'InfoDTO' else None - def status_request(self): - resp = self.request('GET', '/api/v1/status') + def status_request(self, ignore_ConnectionError=False): + resp = self.request('GET', '/api/v1/status', ignore_ConnectionError=ignore_ConnectionError) return resp.status if resp and resp.type == 'StatusDTO' else None def secret_request(self): @@ -459,7 +470,7 @@ def observer_scale_out_request(self, ip, port, zone, server_config): # upgrade routes def pkg_upload_request(self, data = None): - return self.safe_request('POST', '/api/v1/upgrade/pkg/upload', data=data) + return self.safe_request('POST', '/api/v1/upgrade/package', data=data) def params_backup_request(self, data = None): return self.safe_request('POST', '/api/v1/upgrade/params/backup', data=data) diff --git a/plugins/oceanbase/4.2.1.4/parameter.yaml b/plugins/oceanbase/4.2.1.4/parameter.yaml index fc76c9b..5f2a95f 100644 --- a/plugins/oceanbase/4.2.1.4/parameter.yaml +++ b/plugins/oceanbase/4.2.1.4/parameter.yaml @@ -1820,7 +1820,7 @@ require: false type: DICT default: - tenant_name: ocp + tenant_name: ocp_meta max_cpu: 1 memory_size: 2147483648 need_redeploy: true @@ -1856,7 +1856,7 @@ - name: ocp_meta_db require: false type: SAFE_STRING - default: ocp_express + default: ocp_meta need_redeploy: true description_en: The database name for ocp meta db description_local: ocp express的元数据库使用的数据库名 @@ -1939,3 +1939,12 @@ need_redeploy: true description_en: The password for ocp monitor db description_local: ocp 的监控数据库使用的密码 +- name: cdcro_password + require: false + type: STRING + default: '' + min_value: NULL + max_value: NULL + need_restart: true + description_en: password of observer cdcro user + description_local: cdcro用户的密码 diff --git a/plugins/oceanbase/4.2.1.4/reload.py b/plugins/oceanbase/4.2.1.4/reload.py new file mode 100644 index 0000000..8bb6459 --- /dev/null +++ b/plugins/oceanbase/4.2.1.4/reload.py @@ -0,0 +1,142 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function + +from _deploy import InnerConfigItem +from _errno import EC_OBSERVER_INVALID_MODFILY_GLOBAL_KEY + + +def reload(plugin_context, new_cluster_config, *args, **kwargs): + stdio = plugin_context.stdio + cluster_config = plugin_context.cluster_config + servers = cluster_config.servers + cursor = plugin_context.get_return('connect').get_return('cursor') + ocs_cursor = plugin_context.get_return('connect').get_return('ocs_cursor') + + inner_config = { + InnerConfigItem('$_zone_idc'): 'idc' + } + not_paramters = ['production_mode', 'local_ip', 'obshell_port'] + inner_keys = inner_config.keys() + zones_config = {} + cluster_server = {} + change_conf = {} + global_change_conf = {} + global_ret = True + for server in servers: + change_conf[server] = {} + stdio.verbose('get %s old configuration' % (server)) + config = cluster_config.get_server_conf_with_default(server) + stdio.verbose('get %s new configuration' % (server)) + new_config = new_cluster_config.get_server_conf_with_default(server) + stdio.verbose('get %s cluster address' % (server)) + cluster_server[server] = '%s:%s' % (server.ip, config['rpc_port']) + stdio.verbose('compare configuration of %s' % (server)) + for key in new_config: + if key in not_paramters: + stdio.verbose('%s is not a oceanbase parameter. skip' % key) + continue + n_value = new_config[key] + if key not in config or config[key] != n_value: + if isinstance(key, InnerConfigItem) and key in inner_keys: + zone = config['zone'] + if zone not in zones_config: + zones_config[zone] = {} + zones_config[zone][key] = n_value + else: + item = cluster_config.get_temp_conf_item(key) + if item: + if item.need_redeploy or item.need_restart: + stdio.verbose('%s can not be reload' % key) + global_ret = False + continue + try: + item.modify_limit(config.get(key), n_value) + except Exception as e: + global_ret = False + stdio.verbose('%s: %s' % (server, str(e))) + continue + change_conf[server][key] = n_value + if key not in global_change_conf: + global_change_conf[key] = {'value': n_value, 'count': 1} + elif n_value == global_change_conf[key]['value']: + global_change_conf[key]['count'] += 1 + + servers_num = len(servers) + stdio.verbose('apply new configuration') + stdio.start_loading('Reload observer') + for zone in zones_config: + zone_config = zones_config[zone] + for key in zone_config: + sql = 'alter system modify zone %s set %s = %%s' % (zone, inner_config[key]) + if cursor.execute(sql, [zone_config[key]]) is False: + return + stdio.verbose('%s ok' % sql) + + raise_cursor = cursor.raise_cursor + for key in global_change_conf: + try: + if key in ['proxyro_password', 'root_password']: + if global_change_conf[key]['count'] != servers_num: + stdio.warn(EC_OBSERVER_INVALID_MODFILY_GLOBAL_KEY.format(key=key)) + continue + value = change_conf[server][key] if change_conf[server].get(key) is not None else '' + user = key.split('_')[0] + msg = sql = 'CREATE USER IF NOT EXISTS %s IDENTIFIED BY %%s' % (user) + raise_cursor.execute(sql, [value]) + msg = sql = 'alter user "%s" IDENTIFIED BY %%s' % (user) + raise_cursor.execute(sql, [value]) + if key == 'root_password': + for server in servers: + stdio.verbose('update %s obshell password' % (server)) + ocs_cursor[server].update_password(value) + continue + if global_change_conf[key]['count'] == servers_num: + msg = sql = 'alter system set %s = %%s' % key + value = change_conf[server][key] + raise_cursor.execute(sql, [value]) + cluster_config.update_global_conf(key, value, False) + continue + for server in servers: + if key not in change_conf[server]: + continue + value = change_conf[server][key] + msg = sql = 'alter system set %s = %%s server=%%s' % key + raise_cursor.execute(sql, [value, cluster_server[server]]) + cluster_config.update_server_conf(server, key, value, False) + except: + stdio.exception("") + global_ret = False + + try: + raise_cursor.execute('alter system reload server') + raise_cursor.execute('alter system reload zone') + raise_cursor.execute('alter system reload unit') + except: + stdio.exception("") + global_ret = False + + if global_ret: + stdio.stop_loading('succeed') + return plugin_context.return_true() + else: + stdio.stop_loading('fail') + return diff --git a/plugins/oceanbase/4.2.1.4/scale_out_check.py b/plugins/oceanbase/4.2.1.4/scale_out_check.py index d1dcd12..fc6ea34 100644 --- a/plugins/oceanbase/4.2.1.4/scale_out_check.py +++ b/plugins/oceanbase/4.2.1.4/scale_out_check.py @@ -30,9 +30,11 @@ def add_plugin(component_name, plugins): def scale_out_check(plugin_context, *args, **kwargs): cluster_config = plugin_context.cluster_config added_components = cluster_config.get_deploy_added_components() + changed_components = cluster_config.get_deploy_changed_components() be_depend = cluster_config.be_depends plugins = [] plugin_context.set_variable('need_bootstrap', False) + need_restart = False if 'obagent' in added_components and 'obagent' in be_depend: add_plugin('generate_config', plugins) add_plugin('connect', plugins) @@ -46,12 +48,25 @@ def scale_out_check(plugin_context, *args, **kwargs): add_plugin('connect', plugins) add_plugin('bootstrap', plugins) add_plugin('create_tenant', plugins) + if 'ocp-server-ce' in added_components and 'ocp-server-ce' in be_depend: + add_plugin('generate_config', plugins) + add_plugin('connect', plugins) + add_plugin('bootstrap', plugins) + add_plugin('create_tenant', plugins) + if 'oblogproxy' in added_components and 'oblogproxy' in be_depend: + add_plugin('generate_config', plugins) + add_plugin('connect', plugins) + add_plugin('bootstrap', plugins) + if 'ob-configserver' in added_components: + cluster_config.add_depend_component('ob-configserver') + need_restart = True if cluster_config.added_servers: add_plugin('connect', plugins) add_plugin('bootstrap', plugins) if (COMP_OB_CE in added_components or COMP_OB in added_components) and not cluster_config.added_servers: plugin_context.set_variable('need_bootstrap', True) + plugin_context.set_variable('scale_out', True) plugin_context.stdio.verbose('scale_out_check plugins: %s' % plugins) plugin_context.stdio.verbose('added_components: %s' % added_components) - return plugin_context.return_true(plugins=plugins) + return plugin_context.return_true(plugins=plugins, need_restart=need_restart) diff --git a/plugins/oceanbase/4.2.1.4/takeover.py b/plugins/oceanbase/4.2.1.4/takeover.py index 5e36a1f..bbb03f9 100644 --- a/plugins/oceanbase/4.2.1.4/takeover.py +++ b/plugins/oceanbase/4.2.1.4/takeover.py @@ -138,12 +138,13 @@ def error(msg): return error('Server %s:%s version is not match' % (server['ip'], server['mysql_port'])) home_path = server['home_path'] - for client in clients.values(): - owner = client.execute_command("ls -ld %s/etc | awk '{print $3}'" % home_path).stdout.strip() - if owner != client.config.username: - return error('Server {}:{} owner is not match. The SSH user for takeover does not match the owner that OceanBase is running under, SSH user: {}, expected: {}.'.format(server['ip'], server['mysql_port'], ssh_user, owner)) - bin_is_symbolic = client.execute_command('''[ -L "%s/bin/observer" ]''' % home_path).code == 0 - break + for svr, client in clients.items(): + if server['ip'] == svr.ip: + owner = client.execute_command("ls -ld %s/etc | awk '{print $3}'" % home_path).stdout.strip() + if owner != client.config.username: + return error('Server {}:{} owner is not match. The SSH user for takeover does not match the owner that OceanBase is running under, SSH user: {}, expected: {}.'.format(server['ip'], server['mysql_port'], client.config.username, owner)) + bin_is_symbolic = client.execute_command('''[ -L "%s/bin/observer" ]''' % home_path).code == 0 + break ip = server['ip'] rpc_port = server['rpc_port'] diff --git a/plugins/oceanbase/4.2.1.4/upgrade.py b/plugins/oceanbase/4.2.1.4/upgrade.py index 1ecd8a9..b9d87ae 100644 --- a/plugins/oceanbase/4.2.1.4/upgrade.py +++ b/plugins/oceanbase/4.2.1.4/upgrade.py @@ -65,7 +65,7 @@ def exector(self): @property def cmd(self): if self._cmd is None: - self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, "-p '%s'" % self.pwd if self.pwd else '') + self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, '-p %s' % tool.ConfigUtil.passwd_format(self.pwd) if self.pwd else '') return self._cmd @host.setter @@ -148,7 +148,6 @@ def __init__(self, plugin_context, search_py_script_plugin, apply_param_plugin, self.unuse_lib_repository = unuse_lib_repository self.local_home_path = local_home_path self.exector_path = exector_path - self.components = plugin_context.components self.exector = None self.db = None self.cursor = None diff --git a/plugins/oceanbase/4.2.2.0/bootstrap.py b/plugins/oceanbase/4.2.2.0/bootstrap.py index 9ff8806..076050d 100644 --- a/plugins/oceanbase/4.2.2.0/bootstrap.py +++ b/plugins/oceanbase/4.2.2.0/bootstrap.py @@ -44,8 +44,10 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): cursor = plugin_context.get_return('connect').get_return('cursor') ocs_cursor = plugin_context.get_return('connect').get_return('ocs_cursor') added_components = [] if cluster_config.added_servers else cluster_config.get_deploy_added_components() + changed_components = cluster_config.get_deploy_changed_components() be_depend = cluster_config.be_depends global_conf = cluster_config.get_global_conf() + ocp_config = cluster_config.get_be_depend_config('ocp-server-ce', with_default=False) bootstrap = [] floor_servers = {} zones_config = {} @@ -152,7 +154,7 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): if not client.execute_command(cmd): stdio.stop_loading('fail') stdio.error('%s obshell failed', server) - return + return time.sleep(3) retry_times = 600 @@ -161,7 +163,7 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): success = True for server in cluster_config.servers: # get status - status = ocs_cursor[server].status_request() + status = ocs_cursor[server].status_request(ignore_ConnectionError=True) if not status: stdio.verbose('get status failed, count: %d' % count) success = False @@ -210,13 +212,17 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): # TODO: 如果任务出错,提示用户是否需要重试(TAKE OVER 不可以回滚) return plugin_context.return_false() stdio.stop_loading('succeed') - + has_obproxy = False + has_obproxy_scale_out = False for component_name in ['obproxy', 'obproxy-ce']: if component_name in added_components and component_name in be_depend: has_obproxy = True break - if has_obproxy or 'proxyro_password' in global_conf: + if component_name in changed_components: + has_obproxy_scale_out = True + break + if has_obproxy or ('proxyro_password' in global_conf and not has_obproxy_scale_out): value = global_conf['proxyro_password'] if global_conf.get('proxyro_password') is not None else '' sql = 'create user if not exists "proxyro" IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) @@ -224,15 +230,15 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): raise_cursor.execute(sql, [value]) has_oblogproxy = "oblogproxy" in added_components and "oblogproxy" in be_depend - if has_oblogproxy or 'cdcro_password' in global_conf: + if has_oblogproxy or ('cdcro_password' in global_conf and 'oblogproxy' not in changed_components): value = global_conf['cdcro_password'] if global_conf.get('cdcro_password') is not None else '' - sql = 'create user "cdcro" IDENTIFIED BY %s' + sql = 'create user if not exists "cdcro" IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) sql = 'grant select on oceanbase.* to cdcro IDENTIFIED BY %s' raise_cursor.execute(sql, [value]) has_obagent = "obagent" in added_components and "obagent" in be_depend - if has_obagent or 'ocp_agent_monitor_password' in global_conf: + if has_obagent or ('ocp_agent_monitor_password' in global_conf and 'obagent' not in changed_components): value = global_conf['ocp_agent_monitor_password'] if global_conf.get('ocp_agent_monitor_password') is not None else '' sql = 'create user if not exists "ocp_monitor" IDENTIFIED BY %s' stdio.verbose(sql) @@ -263,7 +269,9 @@ def bootstrap(plugin_context, need_bootstrap=True, *args, **kwargs): continue # set create tenant variable for key in global_conf_with_default: - if key.startswith(prefix) and original_global_conf.get(key, None): + if key.startswith(prefix) and not original_global_conf.get(key, None): + if ocp_config and ocp_config.get(key, None): + global_conf_with_default[key] = ocp_config[key] global_conf_with_default[prefix + 'tenant'][key.replace(prefix, '', 1)] = global_conf_with_default[key] tenant_info = global_conf_with_default[prefix + "tenant"] tenant_info["variables"] = "ob_tcp_invited_nodes='%'" diff --git a/plugins/oceanbase/4.2.2.0/connect.py b/plugins/oceanbase/4.2.2.0/connect.py index fcee8bc..0ce5b72 100644 --- a/plugins/oceanbase/4.2.2.0/connect.py +++ b/plugins/oceanbase/4.2.2.0/connect.py @@ -306,8 +306,16 @@ def _make_headers(self, headers=None, safe=None, uri=None): def _make_url(self, url): return 'http://{ip}:{port}{url}'.format(ip=self.ip, port=self.port, url=url) + + # put new password to obshell + def update_password(self, password): + self.password = password + self._auth_header = None + # Invoke any API that requires the `safe_request` method to securely update passwords. + self.pkg_upload_request() + return - def _request(self, method, url, data=None, headers=None, params=None, safe=None, *args, **kwargs): + def _request(self, method, url, data=None, headers=None, params=None, safe=None, ignore_ConnectionError=False, *args, **kwargs): try: if data is not None: data = json.dumps(data) @@ -318,6 +326,9 @@ def _request(self, method, url, data=None, headers=None, params=None, safe=None, self.stdio.verbose('send request to obshell: method: {}, url: {}, data: {}, headers: {}, params: {}'.format(method, url, data, headers, params)) resp = requests.request(method, self._make_url(url), data=data, headers=self._make_headers(headers, safe, url), params=params, *args, **kwargs) except Exception as e: + if ignore_ConnectionError and isinstance(e, requests.exceptions.ConnectionError): + self.stdio.verbose('Attempt to connect failed:{}'.format(self._make_url(url))) + return None self.stdio.error('request error: {}'.format(e)) return None parsed_resp = self._response_parser(resp) @@ -366,8 +377,8 @@ def _get_secrets(self): resp = self._request('GET', '/api/v1/secret') return resp.public_key if resp else None - def request(self, method, url, data=None, headers=None, params=None, *args, **kwargs): - return self._request(method, url, data, headers, params, *args, **kwargs) + def request(self, method, url, data=None, headers=None, params=None, ignore_ConnectionError=False, *args, **kwargs): + return self._request(method, url, data, headers, params, ignore_ConnectionError=ignore_ConnectionError, *args, **kwargs) def safe_request(self, method, url, data=None, headers=None, params=None, *args, **kwargs): return self._request(method, url, data, headers, params, safe=True, *args, **kwargs) @@ -397,8 +408,8 @@ def info_request(self): resp = self.request('GET', '/api/v1/info') return resp.info if resp and resp.type == 'InfoDTO' else None - def status_request(self): - resp = self.request('GET', '/api/v1/status') + def status_request(self, ignore_ConnectionError=False): + resp = self.request('GET', '/api/v1/status', ignore_ConnectionError=ignore_ConnectionError) return resp.status if resp and resp.type == 'StatusDTO' else None def secret_request(self): @@ -459,7 +470,7 @@ def observer_scale_out_request(self, ip, port, zone, server_config): # upgrade routes def pkg_upload_request(self, data = None): - return self.safe_request('POST', '/api/v1/upgrade/pkg/upload', data=data) + return self.safe_request('POST', '/api/v1/upgrade/package', data=data) def params_backup_request(self, data = None): return self.safe_request('POST', '/api/v1/upgrade/params/backup', data=data) diff --git a/plugins/oceanbase/4.2.2.0/create_takeover_base_config.py b/plugins/oceanbase/4.2.2.0/create_takeover_base_config.py new file mode 100644 index 0000000..b7d74e2 --- /dev/null +++ b/plugins/oceanbase/4.2.2.0/create_takeover_base_config.py @@ -0,0 +1,58 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function + +from tool import OrderedDict + + +def create_takeover_base_config(plugin_context, *args, **kwargs): + options = plugin_context.options + host = getattr(options, 'host') + mysql_port = getattr(options, 'mysql_port') + root_password = getattr(options, 'root_password') + + config = OrderedDict() + component_name = 'oceanbase-ce' + global_config = { + 'mysql_port': mysql_port, + 'root_password': root_password + } + config[component_name] = { + 'servers': [host], + 'global': global_config + } + + user_config = {} + ssh_key_map = { + 'username': 'ssh_user', + 'ssh_password': 'password', + 'key_file': 'ssh_key_file', + 'port': 'ssh_port' + } + for key in ssh_key_map: + opt = ssh_key_map[key] + val = getattr(options, opt) + if val is not None: + user_config[key] = val + if user_config: + config['user'] = user_config + + return plugin_context.return_true('takeover_config', config) \ No newline at end of file diff --git a/plugins/oceanbase/4.2.2.0/parameter.yaml b/plugins/oceanbase/4.2.2.0/parameter.yaml index fc76c9b..42d3b0d 100644 --- a/plugins/oceanbase/4.2.2.0/parameter.yaml +++ b/plugins/oceanbase/4.2.2.0/parameter.yaml @@ -1806,6 +1806,8 @@ description_en: 'the number of RDMA I/O threads for Libreasy. Range: [0, 8] in integer, 0 stands for RDMA being disabled.' description_local: Libreasy 的 RDMA I/O 线程数。 - name: production_mode + name_local: 生产模式开关 + essential: true require: true type: BOOL default: true @@ -1820,7 +1822,7 @@ require: false type: DICT default: - tenant_name: ocp + tenant_name: ocp_meta max_cpu: 1 memory_size: 2147483648 need_redeploy: true @@ -1856,7 +1858,7 @@ - name: ocp_meta_db require: false type: SAFE_STRING - default: ocp_express + default: ocp_meta need_redeploy: true description_en: The database name for ocp meta db description_local: ocp express的元数据库使用的数据库名 @@ -1939,3 +1941,12 @@ need_redeploy: true description_en: The password for ocp monitor db description_local: ocp 的监控数据库使用的密码 +- name: cdcro_password + require: false + type: STRING + default: '' + min_value: NULL + max_value: NULL + need_restart: true + description_en: password of observer cdcro user + description_local: cdcro用户的密码 diff --git a/plugins/oceanbase/4.2.2.0/scale_out_check.py b/plugins/oceanbase/4.2.2.0/scale_out_check.py index 6ccb25c..9af0fb6 100644 --- a/plugins/oceanbase/4.2.2.0/scale_out_check.py +++ b/plugins/oceanbase/4.2.2.0/scale_out_check.py @@ -20,6 +20,7 @@ from __future__ import absolute_import, division, print_function from const import COMP_OB, COMP_OB_CE +from copy import deepcopy def add_plugin(component_name, plugins): @@ -30,9 +31,11 @@ def add_plugin(component_name, plugins): def scale_out_check(plugin_context, *args, **kwargs): cluster_config = plugin_context.cluster_config added_components = cluster_config.get_deploy_added_components() + changed_components = cluster_config.get_deploy_changed_components() be_depend = cluster_config.be_depends plugins = [] plugin_context.set_variable('need_bootstrap', False) + need_restart = False if 'obagent' in added_components and 'obagent' in be_depend: add_plugin('generate_config', plugins) add_plugin('connect', plugins) @@ -46,6 +49,18 @@ def scale_out_check(plugin_context, *args, **kwargs): add_plugin('connect', plugins) add_plugin('bootstrap', plugins) add_plugin('create_tenant', plugins) + if 'ocp-server-ce' in added_components and 'ocp-server-ce' in be_depend: + add_plugin('generate_config', plugins) + add_plugin('connect', plugins) + add_plugin('bootstrap', plugins) + add_plugin('create_tenant', plugins) + if 'oblogproxy' in added_components and 'oblogproxy' in be_depend: + add_plugin('generate_config', plugins) + add_plugin('connect', plugins) + add_plugin('bootstrap', plugins) + if 'ob-configserver' in added_components: + cluster_config.add_depend_component('ob-configserver') + need_restart = True if cluster_config.added_servers: add_plugin('connect', plugins) add_plugin('bootstrap', plugins) @@ -54,4 +69,5 @@ def scale_out_check(plugin_context, *args, **kwargs): plugin_context.stdio.verbose('scale_out_check plugins: %s' % plugins) plugin_context.stdio.verbose('added_components: %s' % added_components) - return plugin_context.return_true(plugins=plugins) + plugin_context.stdio.verbose('changed_components: %s' % changed_components) + return plugin_context.return_true(plugins=plugins, need_restart=need_restart) diff --git a/plugins/oceanbase/4.2.2.0/start_check.py b/plugins/oceanbase/4.2.2.0/start_check.py index 0b1525e..ebf2d0c 100644 --- a/plugins/oceanbase/4.2.2.0/start_check.py +++ b/plugins/oceanbase/4.2.2.0/start_check.py @@ -215,7 +215,8 @@ def system_memory_check(): 'aio': err.CheckStatus(), 'net': err.CheckStatus(), 'ntp': err.CheckStatus(), - 'ocp meta db': err.CheckStatus() + 'ocp tenant memory': err.CheckStatus(), + 'ocp tenant disk': err.CheckStatus() } check_status[server].update(kernel_check_status) @@ -680,19 +681,32 @@ def system_memory_check(): critical('disk', err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=p, avail=Capacity(avail), need=Capacity(need)), tmp_suggests + suggests) global_conf = cluster_config.get_global_conf() - has_ocp = 'ocp-express' in plugin_context.components + has_ocp = 'ocp-express' in plugin_context.components or 'ocp-server-ce' in plugin_context.components if not has_ocp and any([key.startswith('ocp_meta') for key in global_conf]): has_ocp = True + if has_ocp and need_bootstrap and parameter_check: global_conf_with_default = copy.deepcopy(cluster_config.get_global_conf_with_default()) original_global_conf = cluster_config.get_original_global_conf() - ocp_meta_tenant_prefix = 'ocp_meta_tenant_' - for key in global_conf_with_default: - if key.startswith(ocp_meta_tenant_prefix) and original_global_conf.get(key, None): - global_conf_with_default['ocp_meta_tenant'][key.replace(ocp_meta_tenant_prefix, '', 1)] = global_conf_with_default[key] - meta_db_memory_size = Capacity(global_conf_with_default['ocp_meta_tenant'].get('memory_size')).btyes + tenants_componets_map = { + "meta": ["ocp-express", "ocp-server", "ocp-server-ce"], + "monitor": ["ocp-server", "ocp-server-ce"], + } + tenant_memory = tenant_log_disk = memory_limit = system_memory = log_disk_size = sys_log_disk_size = 0 + for tenant, component_list in tenants_componets_map.items(): + prefix = "ocp_%s_tenant_" % tenant + tenant_key = "ocp_%s_tenant" % tenant + for key in global_conf_with_default: + if key.startswith(prefix) and not original_global_conf.get(key, None): + global_conf_with_default['ocp_%s_tenant' % tenant][key.replace(prefix, '', 1)] = global_conf_with_default[key] + if set(list(plugin_context.components)) & set(component_list): + tenant_memory_default = global_conf_with_default[tenant_key].get('memory_size', '0') + tenant_memory += Capacity(original_global_conf.get(tenant_key, {}).get('memory_size', tenant_memory_default)).btyes + tenant_log_disk_default = global_conf_with_default[tenant_key].get('log_disk_size', '0') + tenant_log_disk += Capacity(original_global_conf.get(tenant_key, {}).get('log_disk_size', tenant_log_disk_default)).btyes + servers_sys_memory = {} - if meta_db_memory_size: + if tenant_memory: sys_memory_size = None if 'sys_tenant' in global_conf and 'memory_size' in global_conf['sys_tenant']: sys_memory_size = global_conf['sys_tenant']['memory_size'] @@ -707,31 +721,19 @@ def system_memory_check(): system_memory = get_system_memory(memory_limit, min_pool_memory) if not sys_memory_size: sys_memory_size = servers_sys_memory[server] = max(min_pool_memory, min((memory_limit - system_memory) * 0.25, Capacity('16G').btyes)) - if meta_db_memory_size + system_memory + sys_memory_size <= memory_limit: + if tenant_memory + system_memory + sys_memory_size <= memory_limit: break else: - suggest = err.SUG_OCP_EXPRESS_REDUCE_META_DB_MEM.format() - suggest.auto_fix = True - if 'ocp_meta_tenant_memory_size' in global_generate_config: - suggest.auto_fix = False - error('ocp meta db', err.EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_MEM.format(), [suggest]) - - meta_db_log_disk_size = global_conf_with_default['ocp_meta_tenant'].get('log_disk_size') - meta_db_log_disk_size = Capacity(meta_db_log_disk_size).btyes if meta_db_log_disk_size else meta_db_log_disk_size - if not meta_db_log_disk_size and meta_db_memory_size: - meta_db_log_disk_size = meta_db_memory_size * 3 - if meta_db_log_disk_size: + critical('ocp tenant memory', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='memory', avail=Capacity(memory_limit - system_memory - sys_memory_size), need=Capacity(tenant_memory))) + + if tenant_log_disk: for server in cluster_config.servers: log_disk_size = servers_log_disk_size[server] sys_log_disk_size = servers_sys_memory.get(server, 0) - if meta_db_log_disk_size + sys_log_disk_size <= log_disk_size: + if tenant_log_disk + sys_log_disk_size <= log_disk_size: break else: - suggest = err.SUG_OCP_EXPRESS_REDUCE_META_DB_LOG_DISK.format() - suggest.auto_fix = True - if 'ocp_meta_tenant_log_disk_size' in global_generate_config: - suggest.auto_fix = False - error('ocp meta db', err.EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_LOG_DISK.format(), [suggest]) + critical('ocp tenant disk', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='log_disk_size', avail=Capacity(log_disk_size - sys_log_disk_size), need=Capacity(tenant_log_disk))) if success: for ip in servers_net_interface: diff --git a/plugins/oceanbase/4.2.2.0/upgrade.py b/plugins/oceanbase/4.2.2.0/upgrade.py index 1ecd8a9..b9d87ae 100644 --- a/plugins/oceanbase/4.2.2.0/upgrade.py +++ b/plugins/oceanbase/4.2.2.0/upgrade.py @@ -65,7 +65,7 @@ def exector(self): @property def cmd(self): if self._cmd is None: - self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, "-p '%s'" % self.pwd if self.pwd else '') + self._cmd = '%s %%s -h %s -P %s -u %s %s' % (self._exector, self.host, self.port, self.user, '-p %s' % tool.ConfigUtil.passwd_format(self.pwd) if self.pwd else '') return self._cmd @host.setter @@ -148,7 +148,6 @@ def __init__(self, plugin_context, search_py_script_plugin, apply_param_plugin, self.unuse_lib_repository = unuse_lib_repository self.local_home_path = local_home_path self.exector_path = exector_path - self.components = plugin_context.components self.exector = None self.db = None self.cursor = None diff --git a/plugins/oceanbase/4.3.0.0/create_tenant.py b/plugins/oceanbase/4.3.0.0/create_tenant.py index b0a72ee..a0b8bfb 100644 --- a/plugins/oceanbase/4.3.0.0/create_tenant.py +++ b/plugins/oceanbase/4.3.0.0/create_tenant.py @@ -31,7 +31,7 @@ tenant_cursor_cache = defaultdict(dict) -def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_exception=True, retries=20, args=[]): +def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_exception=True, retries=20, args=[], stdio=None): if not user: user = 'SYS' if mode == 'oracle' else 'root' # find tenant ip, port @@ -52,8 +52,8 @@ def exec_sql_in_tenant(sql, cursor, tenant, mode, user='', password='', print_ex break if not tenant_cursor and retries: time.sleep(1) - return exec_sql_in_tenant(sql, cursor, tenant, mode, user, password, print_exception=print_exception, retries=retries-1, args=args) - return tenant_cursor.execute(sql, args=args, raise_exception=False, exc_level='verbose') if tenant_cursor else False + return exec_sql_in_tenant(sql, cursor, tenant, mode, user, password, print_exception=print_exception, retries=retries-1, args=args, stdio=stdio) + return tenant_cursor.execute(sql, args=args, raise_exception=False, exc_level='verbose', stdio=stdio) if tenant_cursor else False def dump_standby_relation(relation_tenants, cluster_configs, dump_relation_tenants, stdio): @@ -345,7 +345,7 @@ def error(msg='', *arg, **kwargs): if log_disk_size is not None: sql += ', log_disk_size %d' % log_disk_size - res = cursor.execute(sql) + res = cursor.execute(sql, stdio=stdio) if res is False: error() return @@ -353,7 +353,7 @@ def error(msg='', *arg, **kwargs): # create resource pool sql = "create resource pool %s unit='%s', unit_num=%d, zone_list=%s" % (pool_name, unit_name, unit_num, zone_list) try: - cursor.execute(sql, raise_exception=True) + cursor.execute(sql, raise_exception=True, stdio=stdio) except Exception as e: stdio.exception('create resource pool failed, you can try again by using SQL "drop resource pool {}" to delete the resource pool, if you are certain that the resource pool is not being used. error info: {}'.format(pool_name, e)) return @@ -380,7 +380,7 @@ def error(msg='', *arg, **kwargs): else: sql += "set %s" % set_mode try: - cursor.execute(sql, raise_exception=True) + cursor.execute(sql, raise_exception=True, stdio=stdio) except Exception as e: stdio.exception('Create error, error info:{}'.format(e)) return @@ -389,13 +389,13 @@ def error(msg='', *arg, **kwargs): if root_password: sql = "alter user root IDENTIFIED BY %s" stdio.verbose(sql) - if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, args=[root_password]) and not create_if_not_exists: + if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, args=[root_password], stdio=stdio) and not create_if_not_exists: stdio.error('failed to set root@{} password {}'.format(name)) return database = get_option('database') if database: sql = 'create database {}'.format(database) - if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, password=root_password if root_password else '') and not create_if_not_exists: + if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, password=root_password if root_password else '', stdio=stdio) and not create_if_not_exists: stdio.error('failed to create database {}'.format(database)) return @@ -408,10 +408,10 @@ def error(msg='', *arg, **kwargs): username=db_username) else: error("Create user in oracle tenant is not supported") - if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, args=[db_password]): + if not exec_sql_in_tenant(sql=sql, cursor=cursor, tenant=name, mode=mode, args=[db_password], stdio=stdio): stdio.error('failed to create user {}'.format(db_username)) return - exec_sql_in_tenant(sql='show databases;', cursor=cursor, tenant=name, mode=mode, password=root_password if root_password else '') + exec_sql_in_tenant(sql='show databases;', cursor=cursor, tenant=name, mode=mode, password=root_password if root_password else '', stdio=stdio) cursors.append(tenant_cursor) else: # create standby tenant @@ -427,7 +427,7 @@ def error(msg='', *arg, **kwargs): sql += ", locality = '%s'" % locality try: - cursor.execute(sql, raise_exception=True, exc_level='verbose') + cursor.execute(sql, raise_exception=True, exc_level='verbose', stdio=stdio) except Exception as e: stdio.verbose('create standby tenant fail, clean and retry. fail message:{}'.format(e)) # clean and retry create standby tenant @@ -438,7 +438,7 @@ def error(msg='', *arg, **kwargs): if res: # drop tenant tenant_id = res['TENANT_ID'] - res = cursor.execute("drop tenant %s FORCE" % name, raise_exception=False) + res = cursor.execute("drop tenant %s FORCE" % name, raise_exception=False, stdio=stdio) if res is False: error('Create standby tenant fail. message:{}'.format(e)) return @@ -461,7 +461,7 @@ def error(msg='', *arg, **kwargs): # create again try: - cursor.execute(sql, raise_exception=True) + cursor.execute(sql, raise_exception=True, stdio=stdio) except Exception as e: retry_message = 'After resolving this issue, you can clean up the environment by manually executing "obd cluster tenant drop {} -t {}", and then wait for a while before re-creating the standby tenant.'.format(standby_deploy_name, name) error("create standby tenant failed, error: {}".format(e)) diff --git a/plugins/oceanbase/4.3.0.0/generate_config.py b/plugins/oceanbase/4.3.0.0/generate_config.py new file mode 100644 index 0000000..699b0f3 --- /dev/null +++ b/plugins/oceanbase/4.3.0.0/generate_config.py @@ -0,0 +1,633 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function + + +import re, os +import time +from math import sqrt + +from _errno import EC_OBSERVER_NOT_ENOUGH_MEMORY_ALAILABLE, EC_OBSERVER_NOT_ENOUGH_MEMORY_CACHED, EC_OBSERVER_GET_MEMINFO_FAIL +import _errno as err +from tool import ConfigUtil +from _types import Capacity + + +def get_system_memory(memory_limit, min_pool_memory, generate_config_mini): + if generate_config_mini and memory_limit <= 6 << 30: + system_memory = 1 << 30 + elif memory_limit <= 8 << 30: + system_memory = 2 << 30 + elif memory_limit <= 16 << 30: + system_memory = 3 << 30 + elif memory_limit <= 32 << 30: + system_memory = 5 << 30 + elif memory_limit <= 48 << 30: + system_memory = 7 << 30 + elif memory_limit <= 64 << 30: + system_memory = 10 << 30 + else: + memory_limit_gb = memory_limit >> 30 + system_memory = int(3 * (sqrt(memory_limit_gb) - 3)) << 30 + return max(system_memory, min_pool_memory) + + +def generate_config(plugin_context, generate_config_mini=False, auto_depend=False, generate_check=True, return_generate_keys=False, generate_consistent_config=False, only_generate_password=False, generate_password=True, *args, **kwargs): + if return_generate_keys: + generate_keys = [] + if not only_generate_password: + generate_keys += [ + 'memory_limit', 'datafile_size', 'log_disk_size', 'system_memory', 'cpu_count', 'production_mode', + 'syslog_level', 'enable_syslog_recycle', 'enable_syslog_wf', 'max_syslog_file_count', 'cluster_id', 'ocp_meta_tenant_log_disk_size', + 'datafile_next', 'datafile_maxsize' + ] + if generate_password: + generate_keys += ['root_password', 'proxyro_password', 'ocp_meta_password', 'ocp_agent_monitor_password'] + return plugin_context.return_true(generate_keys=generate_keys) + + cluster_config = plugin_context.cluster_config + original_global_conf = cluster_config.get_original_global_conf() + if not original_global_conf.get('appname'): + cluster_config.update_global_conf('appname', plugin_context.deploy_name) + if original_global_conf.get('cluster_id') is None: + cluster_config.update_global_conf('cluster_id', round(time.time()) % 4294901759, False) + generate_scenario(plugin_context, generate_config_mini) + if generate_password: + generate_random_password(cluster_config) + if only_generate_password: + return plugin_context.return_true() + + def update_server_conf(server, key, value): + if server not in generate_configs: + generate_configs[server] = {} + generate_configs[server][key] = value + def update_global_conf(key, value): + generate_configs['global'][key] = value + def summit_config(): + generate_global_config = generate_configs['global'] + for key in generate_global_config: + stdio.verbose('Update global config %s to %s' % (key, generate_global_config[key])) + cluster_config.update_global_conf(key, generate_global_config[key], False) + for server in cluster_config.servers: + if server not in generate_configs: + continue + generate_server_config = generate_configs[server] + for key in generate_server_config: + stdio.verbose('Update server %s config %s to %s' % (server, key, generate_server_config[key])) + cluster_config.update_server_conf(server, key, generate_server_config[key], False) + + clients = plugin_context.clients + stdio = plugin_context.stdio + success = True + added_components = cluster_config.get_deploy_added_components() + be_depend = cluster_config.be_depends + generate_configs = {'global': {}} + plugin_context.set_variable('generate_configs', generate_configs) + stdio.start_loading('Generate observer configuration') + + global_config = cluster_config.get_global_conf() + max_syslog_file_count_default = 4 + if global_config.get('enable_syslog_recycle') is None: + update_global_conf('enable_syslog_recycle', True) + if global_config.get('enable_syslog_wf') is None: + update_global_conf('enable_syslog_wf', False) + if global_config.get('max_syslog_file_count') is None: + update_global_conf('max_syslog_file_count', max_syslog_file_count_default) + + MIN_MEMORY = 6 << 30 + PRO_MEMORY_MIN = 16 << 30 + SLOG_SIZE = 4 << 30 + MIN_CPU_COUNT = 16 + START_NEED_MEMORY = 3 << 30 + + MINI_MEMORY_SIZE = MIN_MEMORY + MINI_DATA_FILE_SIZE = 2 << 30 + MINI_DATA_FILE_NEXT = 2 << 30 + MINI_DATA_FILE_MAX_SIZE = 8 << 30 + MINI_LOG_DISK_SIZE = 14 << 30 + + has_ocp = 'ocp-express' in added_components and 'ocp-express' in be_depend + if any([key in global_config for key in ["ocp_meta_tenant", "ocp_meta_db", "ocp_meta_username", "ocp_meta_password"]]): + has_ocp = True + ip_server_memory_info = {} + servers_info = {} + for server in cluster_config.servers: + ip = server.ip + client = clients[server] + server_config = cluster_config.get_server_conf_with_default(server) + user_server_config = cluster_config.get_original_server_conf_with_global(server, format_conf=True) + + dirs = {"home_path": server_config['home_path']} + dirs["data_dir"] = server_config['data_dir'] if server_config.get('data_dir') else os.path.join(server_config['home_path'], 'store') + dirs["redo_dir"] = server_config['redo_dir'] if server_config.get('redo_dir') else dirs["data_dir"] + dirs["clog_dir"] = server_config['clog_dir'] if server_config.get('clog_dir') else os.path.join(dirs["redo_dir"], 'clog') + + # memory + auto_set_memory = False + auto_set_system_memory = False + auto_set_min_pool_memory = False + system_memory = 0 + if user_server_config.get('system_memory'): + system_memory = Capacity(user_server_config.get('system_memory')).btyes + if generate_config_mini and '__min_full_resource_pool_memory' not in user_server_config: + auto_set_min_pool_memory = True + min_pool_memory = server_config['__min_full_resource_pool_memory'] + min_memory = max(system_memory, MIN_MEMORY) + if ip not in ip_server_memory_info: + ret = client.execute_command('cat /proc/meminfo') + if ret: + ip_server_memory_info[ip] = server_memory_stats = {} + memory_key_map = { + 'MemTotal': 'total', + 'MemFree': 'free', + 'MemAvailable': 'available', + 'Buffers': 'buffers', + 'Cached': 'cached' + } + for key in memory_key_map: + server_memory_stats[memory_key_map[key]] = 0 + for k, v in re.findall('(\w+)\s*:\s*(\d+\s*\w+)', ret.stdout): + if k in memory_key_map: + key = memory_key_map[k] + server_memory_stats[key] = Capacity(str(v)).btyes + + if user_server_config.get('memory_limit_percentage'): + if ip in ip_server_memory_info: + total_memory = Capacity(ip_server_memory_info[ip]['total']).btyes + memory_limit = int(total_memory * user_server_config.get('memory_limit_percentage') / 100) + elif generate_check: + stdio.error(EC_OBSERVER_GET_MEMINFO_FAIL.format(server=server)) + success = False + continue + else: + memory_limit = MIN_MEMORY + elif not server_config.get('memory_limit'): + if generate_config_mini: + memory_limit = MINI_MEMORY_SIZE + update_server_conf(server, 'memory_limit', str(Capacity(memory_limit, 0))) + update_server_conf(server, 'production_mode', False) + if auto_set_min_pool_memory: + min_pool_memory = 1073741824 + update_server_conf(server, '__min_full_resource_pool_memory', min_pool_memory) + else: + if ip in ip_server_memory_info: + server_memory_stats = ip_server_memory_info[ip] + if generate_check: + if server_memory_stats['available'] < START_NEED_MEMORY: + stdio.error(EC_OBSERVER_NOT_ENOUGH_MEMORY_ALAILABLE.format(ip=ip, available=str(Capacity(server_memory_stats['available'])), need=str(Capacity(START_NEED_MEMORY)))) + success = False + continue + + if server_memory_stats['free'] + server_memory_stats['buffers'] + server_memory_stats['cached'] < MIN_MEMORY: + stdio.error(EC_OBSERVER_NOT_ENOUGH_MEMORY_CACHED.format(ip=ip, free=str(Capacity(server_memory_stats['free'])), cached=str(Capacity(server_memory_stats['buffers'] + server_memory_stats['cached'])), need=str(Capacity(MIN_MEMORY)))) + success = False + continue + memory_limit = max(MIN_MEMORY, int(int(server_memory_stats['available'] * 0.9))) + update_server_conf(server, 'memory_limit', str(Capacity(memory_limit, 0))) + auto_set_memory = True + elif generate_check: + stdio.error(EC_OBSERVER_GET_MEMINFO_FAIL.format(server=server)) + success = False + continue + else: + memory_limit = MIN_MEMORY + else: + memory_limit = Capacity(server_config.get('memory_limit')).btyes + + if system_memory == 0: + auto_set_system_memory = True + system_memory = get_system_memory(memory_limit, min_pool_memory, generate_config_mini) + update_server_conf(server, 'system_memory', str(Capacity(system_memory, 0))) + + # cpu + if not server_config.get('cpu_count'): + ret = client.execute_command("grep -e 'processor\s*:' /proc/cpuinfo | wc -l") + if ret and ret.stdout.strip().isdigit(): + cpu_num = int(ret.stdout) + server_config['cpu_count'] = max(MIN_CPU_COUNT, int(cpu_num - 2)) + else: + server_config['cpu_count'] = MIN_CPU_COUNT + update_server_conf(server, 'cpu_count', server_config['cpu_count']) + elif server_config['cpu_count'] < MIN_CPU_COUNT: + update_server_conf(server, 'cpu_count', MIN_CPU_COUNT) + stdio.warn('(%s): automatically adjust the cpu_count %s' % (server, MIN_CPU_COUNT)) + + # disk + datafile_size = server_config.get('datafile_size', 0) + log_disk_size = server_config.get('log_disk_size', 0) + if not server_config.get('datafile_size') or not server_config.get('log_disk_size'): + disk = {'/': 0} + ret = client.execute_command('df --block-size=1024') + if ret: + for total, used, avail, puse, path in re.findall('(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)', ret.stdout): + disk[path] = { + 'total': int(total) << 10, + 'avail': int(avail) << 10, + 'need': 0, + } + for include_dir in dirs.values(): + while include_dir not in disk: + ret = client.execute_command('df --block-size=1024 %s' % include_dir) + if ret: + for total, used, avail, puse, path in re.findall('(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)', ret.stdout): + disk[path] = { + 'total': int(total) << 10, + 'avail': int(avail) << 10, + 'need': 0, + } + break + else: + include_dir = os.path.dirname(include_dir) + mounts = {} + for key in dirs: + path = dirs[key] + kp = '/' + for p in disk: + if p in path: + if len(p) > len(kp): + kp = p + mounts[path] = kp + + home_path_mount = mounts[dirs['home_path']] + home_path_disk = disk[home_path_mount] + + data_dir_mount = mounts[dirs['data_dir']] + data_dir_disk = disk[data_dir_mount] + + clog_dir_mount = mounts[dirs['clog_dir']] + clog_dir_disk = disk[clog_dir_mount] + + auto_set_datafile_size = False + auto_set_log_disk_size = False + + if not datafile_size: + datafile_disk_percentage = int(user_server_config.get('datafile_disk_percentage', 0)) + if datafile_disk_percentage: + datafile_size = data_dir_disk['total'] * datafile_disk_percentage / 100 + elif generate_config_mini: + datafile_size = MINI_DATA_FILE_SIZE + update_server_conf(server, 'datafile_size', str(Capacity(datafile_size, 0))) + if 'datafile_maxsize' not in user_server_config: + update_server_conf(server, 'datafile_maxsize', str(Capacity(MINI_DATA_FILE_MAX_SIZE, 0))) + if 'datafile_next' not in user_server_config: + update_server_conf(server, 'datafile_next', str(Capacity(MINI_DATA_FILE_NEXT, 0))) + else: + auto_set_datafile_size = True + + if not log_disk_size: + log_disk_percentage = int(user_server_config.get('log_disk_percentage', 0)) + if log_disk_percentage: + log_disk_size = int(clog_dir_disk['total'] * log_disk_percentage / 100) + elif generate_config_mini: + log_disk_size = MINI_LOG_DISK_SIZE + update_server_conf(server, 'log_disk_size', str(Capacity(log_disk_size, 0))) + else: + auto_set_log_disk_size = True + + if user_server_config.get('enable_syslog_recycle') is False: + log_size = 1 << 30 # 默认先给1G普通日志空间 + else: + log_size = (256 << 20) * int(user_server_config.get('max_syslog_file_count', max_syslog_file_count_default)) * 4 + + datafile_maxsize = datafile_next = 0 + DATA_RESERVED = 0.95 + DATA_NEXT = 0.1 + if clog_dir_mount == data_dir_mount: + min_log_size = log_size if clog_dir_mount == home_path_mount else 0 + MIN_NEED = min_log_size + SLOG_SIZE + if auto_set_datafile_size: + datafile_size =min_datafile_size = MINI_DATA_FILE_SIZE + else: + min_datafile_size = datafile_size + MIN_NEED += Capacity(min_datafile_size).btyes + if auto_set_log_disk_size: + log_disk_size = min_log_disk_size = (memory_limit - system_memory) * 3 + system_memory + else: + min_log_disk_size = log_disk_size + MIN_NEED += Capacity(min_log_disk_size).btyes + min_need = min_log_size + Capacity(min_datafile_size).btyes + Capacity(min_log_disk_size).btyes + + disk_free = data_dir_disk['avail'] + if MIN_NEED > disk_free: + if generate_check: + stdio.error(err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=data_dir_mount, avail=str(Capacity(disk_free)), need=str(Capacity(MIN_NEED)))) + success = False + continue + else: + if auto_set_memory: + memory_limit = MIN_MEMORY + update_server_conf(server, 'memory_limit', str(Capacity(memory_limit, 0))) + if auto_set_system_memory: + system_memory = get_system_memory(memory_limit, min_pool_memory, generate_config_mini) + update_server_conf(server, 'system_memory', str(Capacity(system_memory, 0))) + if auto_set_datafile_size: + datafile_size = MINI_DATA_FILE_SIZE + if auto_set_log_disk_size: + log_disk_size = (memory_limit - system_memory) * 3 + system_memory + elif min_need > disk_free: + if generate_check and not auto_set_memory: + stdio.error(err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=data_dir_mount, avail=str(Capacity(disk_free)), need=str(Capacity(min_need)))) + success = False + continue + + disk_free = disk_free - log_size - SLOG_SIZE + memory_factor = 6 + if auto_set_datafile_size is False: + disk_free -= min_datafile_size + memory_factor -= 3 + if auto_set_log_disk_size is False: + disk_free -= min_log_disk_size + memory_factor -= 3 + memory_limit = str(Capacity(disk_free / max(1, memory_factor), 0)) + update_server_conf(server, 'memory_limit', memory_limit) + memory_limit = Capacity(memory_limit).bytes + if auto_set_system_memory: + system_memory = get_system_memory(memory_limit, min_pool_memory, generate_config_mini) + update_server_conf(server, 'system_memory', str(Capacity(system_memory, 0))) + if auto_set_log_disk_size: + log_disk_size = (memory_limit - system_memory) * 3 + system_memory + if auto_set_datafile_size: + datafile_size = min(disk_free - log_disk_size, memory_limit * 3) + datafile_maxsize = max(disk_free - log_disk_size, memory_limit * 3) + datafile_next = DATA_NEXT * datafile_maxsize + else: + if auto_set_log_disk_size: + log_disk_size = (memory_limit - system_memory) * 3 + system_memory + if auto_set_datafile_size: + datafile_size = min((disk_free - log_size - SLOG_SIZE - log_disk_size) * DATA_RESERVED, memory_limit * 3) + datafile_maxsize = max((disk_free - log_size - SLOG_SIZE - log_disk_size) * DATA_RESERVED, memory_limit * 3) + datafile_next = DATA_NEXT * datafile_maxsize + + if auto_set_datafile_size: + update_server_conf(server, 'datafile_size', str(Capacity(datafile_size, 0))) + if datafile_maxsize > datafile_size: + update_server_conf(server, 'datafile_maxsize', str(Capacity(datafile_maxsize, 0))) + update_server_conf(server, 'datafile_next', str(Capacity(datafile_next, 0))) + if auto_set_log_disk_size: + update_server_conf(server, 'log_disk_size', str(Capacity(log_disk_size, 0))) + else: + datafile_min_memory_limit = memory_limit + if auto_set_datafile_size: + datafile_size = 3 * datafile_min_memory_limit + min_log_size = log_size if data_dir_mount == home_path_mount else 0 + disk_free = data_dir_disk['avail'] + min_need = min_log_size + datafile_size + SLOG_SIZE + + if generate_check and min_need > disk_free: + if not auto_set_memory: + stdio.error(err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=data_dir_mount, avail=str(Capacity(disk_free)), need=str(Capacity(min_need)))) + success = False + continue + datafile_min_memory_limit = (disk_free - min_log_size - SLOG_SIZE) / 3 + if datafile_min_memory_limit < min_memory: + stdio.error(err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=data_dir_mount, avail=str(Capacity(disk_free)), need=str(Capacity(min_need)))) + success = False + continue + datafile_min_memory_limit = Capacity(str(Capacity(datafile_min_memory_limit, 0))).btyes + datafile_size = datafile_min_memory_limit * 3 + datafile_maxsize = (disk_free - min_log_size - SLOG_SIZE) * DATA_RESERVED + datafile_next = DATA_NEXT * datafile_maxsize + + log_disk_min_memory_limit = memory_limit + if auto_set_log_disk_size: + log_disk_size = 3 * memory_limit + min_log_size = log_size if clog_dir_mount == home_path_mount else 0 + disk_free = clog_dir_disk['avail'] + min_need = min_log_size + log_disk_size + if generate_check and min_need > disk_free: + if not auto_set_memory: + stdio.error(err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=data_dir_mount, avail=str(Capacity(disk_free)), need=str(Capacity(min_need)))) + success = False + continue + log_disk_min_memory_limit = (disk_free - log_size) / 3 + if log_disk_min_memory_limit < min_memory: + stdio.error(err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=data_dir_mount, avail=str(Capacity(disk_free)), need=str(Capacity(min_need)))) + success = False + continue + log_disk_min_memory_limit = Capacity(str(Capacity(log_disk_min_memory_limit, 0))).bytes + log_disk_size = log_disk_min_memory_limit * 3 + + if auto_set_memory: + update_server_conf(server, 'memory_limit', str(Capacity(memory_limit, 0))) + if auto_set_system_memory: + system_memory = get_system_memory(memory_limit, min_pool_memory, generate_config_mini) + update_server_conf(server, 'system_memory', system_memory) + + if auto_set_datafile_size: + update_server_conf(server, 'datafile_size', str(Capacity(datafile_size, 0))) + if datafile_maxsize > datafile_size: + update_server_conf(server, 'datafile_maxsize', str(Capacity(datafile_maxsize, 0))) + update_server_conf(server, 'datafile_next', str(Capacity(datafile_next, 0))) + if auto_set_log_disk_size: + update_server_conf(server, 'log_disk_size', str(Capacity(log_disk_size, 0))) + + if memory_limit < PRO_MEMORY_MIN: + update_server_conf(server, 'production_mode', False) + servers_info[server] = { + "memory_limit": memory_limit, + "system_memory": system_memory, + "min_pool_memory": min_pool_memory, + "log_disk_size": log_disk_size + } + + # ocp meta db + SYS_TENANT_LOG_DISK_SCALE = 1 + if has_ocp: + if 'ocp_meta_tenant_log_disk_size' not in global_config and 'log_disk_size' not in global_config.get('ocp_meta_tenant', {}): + if generate_config_mini: + update_global_conf('ocp_meta_tenant_log_disk_size', '6656M') + else: + meta_min_log_disk_size = 6 << 30 + expect_log_disk_size = (9 * 512 + 512 * len(cluster_config.servers) + 512 * 3) << 20 + max_available = 0 + sys_memory_size = None + sys_log_disk_size = None + if 'sys_tenant' in global_config: + if 'memory_size' in global_config['sys_tenant']: + sys_memory_size = global_config['sys_tenant']['memory_size'] + if 'log_disk_size' in global_config['sys_tenant']: + sys_log_disk_size = global_config['sys_tenant']['log_disk_size'] + for server in cluster_config.servers: + # server_config = cluster_config.get_server_conf_with_default(server) + server_info = servers_info.get(server) + if not server_info: + continue + memory_limit = Capacity(server_info['memory_limit']).btyes + system_memory = Capacity(server_info['system_memory']).btyes + log_disk_size = Capacity(server_info['log_disk_size']).btyes + min_pool_memory = Capacity(server_info['min_pool_memory']).btyes + if not sys_log_disk_size: + if not sys_memory_size: + sys_memory_size = max(min_pool_memory, min(int((memory_limit - system_memory) * 0.25), 16 << 30)) + sys_log_disk_size = sys_memory_size * SYS_TENANT_LOG_DISK_SCALE + max_available = max(max_available, log_disk_size - sys_log_disk_size) + if expect_log_disk_size > max_available: + expect_log_disk_size = meta_min_log_disk_size + if expect_log_disk_size > max_available and generate_check: + stdio.error(err.EC_OCP_EXPRESS_META_DB_NOT_ENOUGH_LOG_DISK_AVAILABLE.format(avail=max_available, need=expect_log_disk_size)) + success = False + cluster_config.update_global_conf('ocp_meta_tenant_log_disk_size', str(Capacity(expect_log_disk_size, 0)), False) + if generate_config_mini and 'ocp_meta_tenant_memory_size' not in global_config and 'memory_size' not in global_config.get('ocp_meta_tenant', {}): + update_global_conf('ocp_meta_tenant_memory_size', '1536M') + + if generate_password: + generate_random_password(cluster_config) + + if generate_consistent_config: + generate_global_config = generate_configs['global'] + server_num = len(cluster_config.servers) + keys = ['memory_limit', 'datafile_size', 'system_memory', 'log_disk_size', 'cpu_count', 'production_mode'] + for key in keys: + servers = [] + values = [] + is_capacity_key = (key != 'cpu_count' and key != 'production_mode') + for server in cluster_config.servers: + if key in generate_configs.get(server, {}): + value = generate_configs[server][key] + servers.append(server) + values.append(Capacity(value).btyes if is_capacity_key else value) + if values: + if len(values) != server_num and key in generate_global_config: + continue + value = min(values) + generate_global_config[key] = str(Capacity(value, 0)) if is_capacity_key else value + for server in servers: + del generate_configs[server][key] + + # merge_generate_config + merge_config = {} + generate_global_config = generate_configs['global'] + count_base = len(cluster_config.servers) - 1 + if count_base < 1: + for server in cluster_config.servers: + if server not in generate_configs: + continue + generate_global_config.update(generate_configs[server]) + generate_configs[server] = {} + else: + for server in cluster_config.servers: + if server not in generate_configs: + continue + generate_server_config = generate_configs[server] + merged_server_config = {} + for key in generate_server_config: + if key in generate_global_config: + if generate_global_config[key] != generate_server_config[key]: + merged_server_config[key] = generate_server_config[key] + elif key in merge_config: + if merge_config[key]['value'] != generate_server_config[key]: + merged_server_config[key] = generate_server_config[key] + elif count_base == merge_config[key]['count']: + generate_global_config[key] = generate_server_config[key] + del merge_config[key] + else: + merge_config[key]['severs'].append(server) + merge_config[key]['count'] += 1 + else: + merge_config[key] = {'value': generate_server_config[key], 'severs': [server], 'count': 1} + generate_configs[server] = merged_server_config + + for key in merge_config: + config_st = merge_config[key] + for server in config_st['severs']: + if server not in generate_configs: + continue + generate_server_config = generate_configs[server] + generate_server_config[key] = config_st['value'] + + # summit_config + summit_config() + + if auto_depend and 'ob-configserver' in plugin_context.components: + cluster_config.add_depend_component('ob-configserver') + + if success: + stdio.stop_loading('succeed') + return plugin_context.return_true() + + stdio.stop_loading('fail') + + +def generate_random_password(cluster_config): + add_components = cluster_config.get_deploy_added_components() + be_depend = cluster_config.be_depends + global_config = cluster_config.get_original_global_conf() + if cluster_config.name in add_components and 'root_password' not in global_config: + cluster_config.update_global_conf('root_password', ConfigUtil.get_random_pwd_by_total_length(20), False) + if 'obagent' in add_components and 'obagent' in be_depend and 'ocp_agent_monitor_password' not in global_config: + cluster_config.update_global_conf('ocp_agent_monitor_password', ConfigUtil.get_random_pwd_by_total_length(), False) + + if 'proxyro_password' not in global_config: + for component_name in ['obproxy', 'obproxy-ce']: + if component_name in add_components and component_name in be_depend: + cluster_config.update_global_conf('proxyro_password', ConfigUtil.get_random_pwd_by_total_length(), False) + + if ('ocp-express' in add_components and 'ocp-express' in be_depend and 'ocp_meta_password' not in global_config) or \ + (any([key in global_config for key in ["ocp_meta_tenant", "ocp_meta_db", "ocp_meta_username", "ocp_meta_password"]])): + if 'ocp_root_password' not in global_config : + cluster_config.update_global_conf('ocp_root_password', ConfigUtil.get_random_pwd_by_total_length(), False) # 不支持在配置文件中中配置 + if 'ocp_meta_password' not in global_config : + cluster_config.update_global_conf('ocp_meta_password', ConfigUtil.get_random_pwd_by_total_length(), False) + +def generate_scenario(plugin_context, generate_config_mini): + cluster_config = plugin_context.cluster_config + stdio = plugin_context.stdio + scenarios = ['express_oltp', 'complex_oltp', 'olap', 'htap', 'kv'] + scenario_check = lambda scenario: scenario in scenarios + global_config = cluster_config.get_original_global_conf() + scenario = global_config.get('scenario', None) + default_scenario = cluster_config.get_global_conf_with_default().get('scenario') + if not scenario: + if generate_config_mini: + scenario = 'express_oltp' + else: + optzs = { + '1': 'express_oltp', + '2': 'complex_oltp', + '3': 'olap', + '4': 'htap', + '5': 'kv', + } + if stdio.isatty(): + stdio.print("Scenario not specified, please specify the scenario you want.") + default_key = '1' + for k, v in optzs.items(): + if v == default_scenario: + default_key = k + stdio.print("%s. %s (default)" % (k, v)) + else: + stdio.print("%s. %s" % (k, v)) + while not scenario: + optz = stdio.read('Please input the scenario [default: %s]: ' % default_key, blocked=True).strip().lower() + if not optz: + scenario = default_scenario + elif optz in optzs: + scenario = optzs[optz] + elif scenario_check(optz): + scenario = optz + else: + stdio.error('Invalid scenario, please input again.') + else: + scenario = default_scenario + stdio.verbose("scenario not specified, use the default scenario: %s." % scenario) + + stdio.verbose('set scenario %s.' % scenario) + cluster_config.update_global_conf('scenario', scenario, False) diff --git a/plugins/oceanbase/4.3.0.0/parameter.yaml b/plugins/oceanbase/4.3.0.0/parameter.yaml index 33ad413..681dcff 100644 --- a/plugins/oceanbase/4.3.0.0/parameter.yaml +++ b/plugins/oceanbase/4.3.0.0/parameter.yaml @@ -1806,6 +1806,8 @@ description_en: 'the number of RDMA I/O threads for Libreasy. Range: [0, 8] in integer, 0 stands for RDMA being disabled.' description_local: Libreasy 的 RDMA I/O 线程数。 - name: production_mode + name_local: 生产模式开关 + essential: true require: true type: BOOL default: true @@ -1820,7 +1822,7 @@ require: false type: DICT default: - tenant_name: ocp + tenant_name: ocp_meta max_cpu: 1 memory_size: 2147483648 need_redeploy: true @@ -1856,7 +1858,7 @@ - name: ocp_meta_db require: false type: SAFE_STRING - default: ocp_express + default: ocp_meta need_redeploy: true description_en: The database name for ocp meta db description_local: ocp express的元数据库使用的数据库名 @@ -1884,10 +1886,11 @@ - name: scenario require: true type: STRING - default: 'express_oltp' + default: 'htap' + essential: true need_restart: true - description_en: The current definition of five typical production environment scenarios, such as express_oltp, complex_oltp, olap, htap, kv. - description_local: 当前定义5种典型生产环境的场景:如express_oltp, complex_oltp, olap, htap, kv. + description_en: 'scenarios: htap, express_oltp, complex_oltp, olap, kv.' + description_local: 业务场景:如 htap, express_oltp, complex_oltp, olap, kv. - name: ocp_monitor_tenant require: false type: DICT @@ -1945,4 +1948,13 @@ default: oceanbase need_redeploy: true description_en: The password for ocp monitor db - description_local: ocp 的监控数据库使用的密码 \ No newline at end of file + description_local: ocp 的监控数据库使用的密码 +- name: cdcro_password + require: false + type: STRING + default: '' + min_value: NULL + max_value: NULL + need_restart: true + description_en: password of observer cdcro user + description_local: cdcro用户的密码 \ No newline at end of file diff --git a/plugins/oceanbase/4.3.0.0/scenario_check.py b/plugins/oceanbase/4.3.0.0/scenario_check.py new file mode 100644 index 0000000..a1d8126 --- /dev/null +++ b/plugins/oceanbase/4.3.0.0/scenario_check.py @@ -0,0 +1,66 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +def scenario_check(plugin_context, scenario='', *args, **kwargs): + cluster_config = plugin_context.cluster_config + stdio = plugin_context.stdio + scenarios = ['express_oltp', 'complex_oltp', 'olap', 'htap', 'kv'] + scenario_check = lambda scenario: scenario in scenarios + scenario = getattr(plugin_context.options, 'optimize', scenario) + global_config = cluster_config.get_global_conf_with_default() + default_scenario = global_config.get('scenario') + if not scenario: + optzs = { + '1': 'express_oltp', + '2': 'complex_oltp', + '3': 'olap', + '4': 'htap', + '5': 'kv', + } + if not getattr(plugin_context.options, 'yes', False) and stdio.isatty(): + stdio.print("Tenant optimization scenario not specified, please specify the scenario you want to optimize.") + default_key = '1' + for k, v in optzs.items(): + if v == default_scenario: + default_key = k + stdio.print("%s. %s (default, follow cluster)" % (k, v)) + else: + stdio.print("%s. %s" % (k, v)) + while not scenario: + optz = stdio.read('Please input the scenario you want to optimize [default: %s]: ' % default_key, blocked=True).strip().lower() + if not optz: + scenario = default_scenario + elif optz in optzs: + scenario = optzs[optz] + elif scenario_check(optz): + scenario = optz + else: + stdio.error('Invalid input, please input again.') + else: + stdio.verbose("Tenant optimization scenario not specified, use the cluster scenario: %s." % default_scenario) + scenario = default_scenario + else: + if not scenario_check(scenario): + stdio.error('This scenario is not supported: %s. scenarios: %s' % (scenario, ', '.join(scenarios))) + return plugin_context.return_false() + + stdio.verbose('set scenario %s.' % scenario) + plugin_context.set_variable('scenario', scenario) + return plugin_context.return_true() diff --git a/plugins/oceanbase/4.3.0.0/start.py b/plugins/oceanbase/4.3.0.0/start.py index c26a9eb..7745fbf 100644 --- a/plugins/oceanbase/4.3.0.0/start.py +++ b/plugins/oceanbase/4.3.0.0/start.py @@ -133,6 +133,10 @@ def start(plugin_context, start_obshell=True, *args, **kwargs): else: scale_out = False need_bootstrap = True + + + scenario = cluster_config.get_global_conf_with_default().get('scenario') + need_bootstrap and stdio.print('cluster scenario: %s' % scenario) stdio.start_loading('Start observer') for server in cluster_config.original_servers: config = cluster_config.get_server_conf(server) @@ -167,7 +171,6 @@ def _optimize(): client = clients[server] server_config = cluster_config.get_server_conf(server) home_path = server_config['home_path'] - scenario = server_config['scenario'] if not server_config.get('data_dir'): server_config['data_dir'] = '%s/store' % home_path diff --git a/plugins/oceanbase/4.3.0.0/start_check.py b/plugins/oceanbase/4.3.0.0/start_check.py new file mode 100644 index 0000000..eb34084 --- /dev/null +++ b/plugins/oceanbase/4.3.0.0/start_check.py @@ -0,0 +1,795 @@ +# coding: utf-8 +# OceanBase Deploy. +# Copyright (C) 2021 OceanBase +# +# This file is part of OceanBase Deploy. +# +# OceanBase Deploy is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OceanBase Deploy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OceanBase Deploy. If not, see . + + +from __future__ import absolute_import, division, print_function + +import os +import re +import time +import socket +import sys +import copy +from math import sqrt + +import _errno as err +from _types import Capacity + + +stdio = None +success = True +production_mode = False + +def get_port_socket_inode(client, port): + port = hex(port)[2:].zfill(4).upper() + cmd = "bash -c 'cat /proc/net/{tcp*,udp*}' | awk -F' ' '{if($4==\"0A\") print $2,$4,$10}' | grep ':%s' | awk -F' ' '{print $3}' | uniq" % port + res = client.execute_command(cmd) + if not res or not res.stdout.strip(): + return False + stdio.verbose(res.stdout) + return res.stdout.strip().split('\n') + + +def time_delta(client): + time_st = time.time() * 1000 + time_srv = int(client.execute_command('date +%s%N').stdout) / 1000000 + time_ed = time.time() * 1000 + + time_it = time_ed - time_st + time_srv -= time_it + return time_srv - time_st + + +def get_mount_path(disk, _path): + _mount_path = '/' + for p in disk: + if p in _path and _path.startswith(p): + if len(p) > len(_mount_path): + _mount_path = p + return _mount_path + + +def get_system_memory(memory_limit, min_pool_memory): + if memory_limit <= 8 << 30: + system_memory = 2 << 30 + elif memory_limit <= 16 << 30: + system_memory = 3 << 30 + elif memory_limit <= 32 << 30: + system_memory = 5 << 30 + elif memory_limit <= 48 << 30: + system_memory = 7 << 30 + elif memory_limit <= 64 << 30: + system_memory = 10 << 30 + else: + memory_limit_gb = memory_limit >> 30 + system_memory = int(3 * (sqrt(memory_limit_gb) - 3)) << 30 + return max(system_memory, min_pool_memory) + + +def get_disk_info_by_path(path, client, stdio): + disk_info = {} + ret = client.execute_command('df --block-size=1024 {}'.format(path)) + if ret: + for total, used, avail, puse, path in re.findall(r'(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)', ret.stdout): + disk_info[path] = {'total': int(total) << 10, 'avail': int(avail) << 10, 'need': 0} + stdio.verbose('get disk info for path {}, total: {} avail: {}'.format(path, disk_info[path]['total'], disk_info[path]['avail'])) + return disk_info + + +def get_disk_info(all_paths, client, stdio): + overview_ret = True + disk_info = get_disk_info_by_path('', client, stdio) + if not disk_info: + overview_ret = False + disk_info = get_disk_info_by_path('/', client, stdio) + if not disk_info: + disk_info['/'] = {'total': 0, 'avail': 0, 'need': 0} + all_path_success = {} + for path in all_paths: + all_path_success[path] = False + cur_path = path + while cur_path not in disk_info: + disk_info_for_current_path = get_disk_info_by_path(cur_path, client, stdio) + if disk_info_for_current_path: + disk_info.update(disk_info_for_current_path) + all_path_success[path] = True + break + else: + cur_path = os.path.dirname(cur_path) + if overview_ret or all(all_path_success.values()): + return disk_info + +def has_obshell(repository): + repository_dir = repository.repository_dir + obshell_path = os.path.join(repository_dir, 'bin', 'obshell') + return os.path.exists(obshell_path) + +def start_check(plugin_context, init_check_status=False, strict_check=False, work_dir_check=False, work_dir_empty_check=True, generate_configs={}, precheck=False, source_option='start', *args, **kwargs): + def check_pass(item): + status = check_status[server] + if status[item].status == err.CheckStatus.WAIT: + status[item].status = err.CheckStatus.PASS + def check_fail(item, error, suggests=[]): + status = check_status[server][item] + if status.status == err.CheckStatus.WAIT: + status.error = error + status.suggests = suggests + status.status = err.CheckStatus.FAIL + def wait_2_pass(): + status = check_status[server] + for item in status: + check_pass(item) + def alert(item, error, suggests=[]): + global success + if strict_check: + success = False + check_fail(item, error, suggests) + print_with_suggests(error, suggests) + else: + stdio.warn(error) + + def alert_strict(item, error, suggests=[]): + global success + if strict_check or production_mode: + success = False + check_fail(item, error, suggests) + print_with_suggests(error, suggests) + else: + stdio.warn(error) + + def error(item, _error, suggests=[]): + global success + if plugin_context.dev_mode: + stdio.warn(_error) + else: + success = False + check_fail(item, _error, suggests) + print_with_suggests(_error, suggests) + + def critical(item, error, suggests=[]): + global success + success = False + check_fail(item, error, suggests) + print_with_suggests(error, suggests) + + def print_with_suggests(error, suggests=[]): + stdio.error('{}, {}'.format(error, suggests[0].msg if suggests else '')) + + def system_memory_check(): + server_memory_config = server_memory_stat['servers'] + for server in server_memory_config: + if server_memory_config[server]['system_memory']: + memory_limit = server_memory_config[server]['num'] + if not memory_limit: + server_memory_config[server]['num'] = memory_limit = server_memory_config[server]['percentage'] * server_memory_stats['total'] + factor = 0.75 + suggest = err.SUG_OBSERVER_SYS_MEM_TOO_LARGE.format(factor=factor) + suggest.auto_fix = 'system_memory' not in global_generate_config and 'system_memory' not in generate_configs.get(server, {}) + if memory_limit < server_memory_config[server]['system_memory']: + critical('mem', err.EC_OBSERVER_SYS_MEM_TOO_LARGE.format(server=server), [suggest]) + elif memory_limit * factor < server_memory_config[server]['system_memory']: + alert('mem', err.WC_OBSERVER_SYS_MEM_TOO_LARGE.format(server=server, factor=factor), [suggest]) + + global stdio, success + success = True + check_status = {} + cluster_config = plugin_context.cluster_config + INF = float('inf') + plugin_context.set_variable('start_check_status', check_status) + + kernel_check_items = [ + {'check_item': 'vm.max_map_count', 'need': [327600, 1310720], 'recommend': 655360}, + {'check_item': 'vm.min_free_kbytes', 'need': [32768, 2097152], 'recommend': 2097152}, + {'check_item': 'vm.overcommit_memory', 'need': 0, 'recommend': 0}, + {'check_item': 'fs.file-max', 'need': [6573688, INF], 'recommend': 6573688}, + ] + + kernel_check_status = {} + for kernel_param in kernel_check_items: + check_item = kernel_param['check_item'] + kernel_check_status[check_item] = err.CheckStatus() + + for server in cluster_config.servers: + check_status[server] = { + 'port': err.CheckStatus(), + 'mem': err.CheckStatus(), + 'disk': err.CheckStatus(), + 'dir': err.CheckStatus(), + 'ulimit': err.CheckStatus(), + 'aio': err.CheckStatus(), + 'net': err.CheckStatus(), + 'ntp': err.CheckStatus(), + 'scenario': err.CheckStatus(), + 'ocp tenant memory': err.CheckStatus(), + 'ocp tenant disk': err.CheckStatus() + } + check_status[server].update(kernel_check_status) + + if init_check_status: + return plugin_context.return_true(start_check_status=check_status) + + clients = plugin_context.clients + stdio = plugin_context.stdio + servers_clients = {} + servers_port = {} + servers_memory = {} + servers_disk = {} + servers_clog_mount = {} + servers_net_interface = {} + servers_dirs = {} + servers_check_dirs = {} + servers_log_disk_size = {} + servers_min_pool_memory = {} + PRO_MEMORY_MIN = 16 << 30 + PRO_POOL_MEM_MIN = 2147483648 + START_NEED_MEMORY = 3 << 30 + global_generate_config = plugin_context.get_variable('global_generate_config', default=generate_configs.get('global', {})) + plugin_context.set_variable('global_generate_config', global_generate_config) + stdio.start_loading('Check before {} observer'.format(source_option)) + + need_bootstrap = True + parameter_check = True + port_check = True + kernel_check = True + is_running_opt = source_option in ['restart', 'upgrade'] + upgrade_opt = source_option == 'upgrade' + for server in cluster_config.servers: + ip = server.ip + client = clients[server] + server_generate_config = generate_configs.get(server, {}) + servers_clients[ip] = client + server_config = cluster_config.get_server_conf_with_default(server) + original_server_conf = cluster_config.get_original_server_conf_with_global(server) + home_path = server_config['home_path'] + production_mode = server_config.get('production_mode', False) + if not precheck: + if need_bootstrap: + data_dir = server_config['data_dir'] if server_config.get('data_dir') else '%s/store' % home_path + if client.execute_command('ls %s/clog/tenant_1/' % data_dir).stdout.strip(): + need_bootstrap = False + remote_pid_path = '%s/run/observer.pid' % home_path + remote_pid = client.execute_command('cat %s' % remote_pid_path).stdout.strip() + if remote_pid: + if client.execute_command('ls /proc/%s' % remote_pid): + stdio.verbose('%s is runnning, skip' % server) + work_dir_check = False + for repository in plugin_context.repositories: + if repository.name == cluster_config.name: + break + port_check = upgrade_opt and not has_obshell(repository) + parameter_check = False + kernel_check = is_running_opt + + if work_dir_check: + stdio.verbose('%s dir check' % server) + if ip not in servers_dirs: + servers_dirs[ip] = {} + servers_check_dirs[ip] = {} + dirs = servers_dirs[ip] + check_dirs = servers_check_dirs[ip] + + if not server_config.get('data_dir'): + server_config['data_dir'] = '%s/store' % home_path + if not server_config.get('redo_dir'): + server_config['redo_dir'] = server_config['data_dir'] + if not server_config.get('clog_dir'): + server_config['clog_dir'] = '%s/clog' % server_config['redo_dir'] + if not server_config.get('ilog_dir'): + server_config['ilog_dir'] = '%s/ilog' % server_config['redo_dir'] + if not server_config.get('slog_dir'): + server_config['slog_dir'] = '%s/slog' % server_config['data_dir'] + if server_config['redo_dir'] == server_config['data_dir']: + keys = ['home_path', 'data_dir', 'clog_dir', 'ilog_dir', 'slog_dir'] + else: + keys = ['home_path', 'data_dir', 'redo_dir', 'clog_dir', 'ilog_dir', 'slog_dir'] + + for key in keys: + path = server_config.get(key) + suggests = [err.SUG_CONFIG_CONFLICT_DIR.format(key=key, server=server)] + if path in dirs and dirs[path]: + critical('dir', err.EC_CONFIG_CONFLICT_DIR.format(server1=server, path=path, server2=dirs[path]['server'], key=dirs[path]['key']), suggests) + dirs[path] = { + 'server': server, + 'key': key, + } + if key not in original_server_conf: + continue + empty_check = work_dir_empty_check + while True: + if path in check_dirs: + if check_dirs[path] != True: + critical('dir', check_dirs[path], suggests) + break + + if client.execute_command('bash -c "[ -a %s ]"' % path): + is_dir = client.execute_command('[ -d {} ]'.format(path)) + has_write_permission = client.execute_command('[ -w {} ]'.format(path)) + if is_dir and has_write_permission: + if empty_check: + ret = client.execute_command('ls %s' % path) + if not ret or ret.stdout.strip(): + check_dirs[path] = err.EC_FAIL_TO_INIT_PATH.format(server=server, key=key, msg=err.InitDirFailedErrorMessage.NOT_EMPTY.format(path=path)) + else: + check_dirs[path] = True + else: + check_dirs[path] = True + else: + if not is_dir: + check_dirs[path] = err.EC_FAIL_TO_INIT_PATH.format(server=server, key=key, msg=err.InitDirFailedErrorMessage.NOT_DIR.format(path=path)) + else: + check_dirs[path] = err.EC_FAIL_TO_INIT_PATH.format(server=server, key=key, msg=err.InitDirFailedErrorMessage.PERMISSION_DENIED.format(path=path)) + else: + path = os.path.dirname(path) + empty_check = False + + if ip not in servers_port: + servers_disk[ip] = {} + servers_port[ip] = {} + servers_clog_mount[ip] = {} + servers_net_interface[ip] = {} + servers_memory[ip] = {'num': 0, 'percentage': 0, 'servers': {}} + memory = servers_memory[ip] + ports = servers_port[ip] + disk = servers_disk[ip] + clog_mount = servers_clog_mount[ip] + interfaces = servers_net_interface[ip] + + if port_check: + stdio.verbose('%s port check' % server) + if upgrade_opt: + keys = ['obshell_port'] + else: + keys = ['mysql_port', 'rpc_port', 'obshell_port'] + + for key in keys: + port = int(server_config.get(key)) + if port in ports: + critical( + 'port', + err.EC_CONFIG_CONFLICT_PORT.format(server1=server, port=port, server2=ports[port]['server'], key=ports[port]['key']), + [err.SUG_PORT_CONFLICTS.format()] + ) + continue + ports[port] = { + 'server': server, + 'key': key + } + if get_port_socket_inode(client, port): + critical('port', err.EC_CONFLICT_PORT.format(server=ip, port=port), [err.SUG_USE_OTHER_PORT.format()]) + + if parameter_check: + servers_min_pool_memory[server] = __min_full_resource_pool_memory = server_config.get('__min_full_resource_pool_memory') + if production_mode and __min_full_resource_pool_memory < PRO_POOL_MEM_MIN: + error('mem', err.EC_OBSERVER_PRODUCTION_MODE_LIMIT.format(server=server, key="__min_full_resource_pool_memory", limit=PRO_POOL_MEM_MIN), [err.SUB_SET_NO_PRODUCTION_MODE.format()]) + + memory_limit = 0 + percentage = 0 + if server_config.get('memory_limit'): + memory_limit = Capacity(server_config['memory_limit']).btyes + if production_mode and memory_limit < PRO_MEMORY_MIN: + error('mem', err.EC_OBSERVER_PRODUCTION_MODE_LIMIT.format(server=server, key='memory_limit', limit=Capacity(PRO_MEMORY_MIN)), [err.SUB_SET_NO_PRODUCTION_MODE.format()]) + memory['num'] += memory_limit + elif 'memory_limit_percentage' in server_config: + percentage = server_config['memory_limit_percentage'] + memory['percentage'] += percentage + else: + percentage = 80 + memory['percentage'] += percentage + memory['servers'][server] = { + 'num': memory_limit, + 'percentage': percentage, + 'system_memory': Capacity(server_config.get('system_memory', 0)).btyes + } + + data_path = server_config['data_dir'] if server_config.get('data_dir') else os.path.join(server_config['home_path'], 'store') + redo_dir = server_config['redo_dir'] if server_config.get('redo_dir') else data_path + clog_dir = server_config['clog_dir'] if server_config.get('clog_dir') else os.path.join(redo_dir, 'clog') + if not client.execute_command('ls %s/sstable/block_file' % data_path): + disk[data_path] = {'server': server} + clog_mount[clog_dir] = {'server': server} + if 'datafile_size' in server_config and server_config['datafile_size'] and server_config['datafile_size']: + # if need is string, it means use datafile_size + disk[data_path]['need'] = server_config['datafile_size'] + elif 'datafile_disk_percentage' in server_config and server_config['datafile_disk_percentage']: + # if need is integer, it means use datafile_disk_percentage + disk[data_path]['need'] = int(server_config['datafile_disk_percentage']) + + if 'log_disk_size' in server_config and server_config['log_disk_size'] and server_config['log_disk_size']: + # if need is string, it means use log_disk_size + clog_mount[clog_dir]['need'] = server_config['log_disk_size'] + elif 'log_disk_percentage' in server_config and server_config['log_disk_percentage']: + # if need is integer, it means use log_disk_percentage + clog_mount[clog_dir]['need'] = int(server_config['log_disk_percentage']) + + devname = server_config.get('devname') + if devname: + if not client.execute_command("grep -e '^ *%s:' /proc/net/dev" % devname): + suggest = err.SUG_NO_SUCH_NET_DEVIC.format(ip=ip) + suggest.auto_fix = 'devname' not in global_generate_config and 'devname' not in server_generate_config + critical('net', err.EC_NO_SUCH_NET_DEVICE.format(server=server, devname=devname), suggests=[suggest]) + if devname not in interfaces: + interfaces[devname] = [] + interfaces[devname].append(ip) + + + if need_bootstrap: + scenarios = ['express_oltp', 'complex_oltp', 'olap', 'htap', 'kv'] + scenario_check = lambda scenario: scenario in scenarios + global_config = cluster_config.get_original_global_conf() + scenario = global_config.get('scenario', None) + if not scenario_check(scenario): + critical('scenario', err.EC_OBSERVER_UNKONE_SCENARIO.format(scenario=scenario), [err.SUB_OBSERVER_UNKONE_SCENARIO.format(scenarios=scenarios)]) + return plugin_context.return_false() + + ip_server_memory_info = {} + for ip in servers_disk: + if not client.execute_command("[ -w /tmp/ ] || [ -w /tmp/obshell ]"): + critical("dir", err.EC_FAIL_TO_INIT_PATH.format(server=server, key='sock path', msg=err.InitDirFailedErrorMessage.PERMISSION_DENIED.format(path='/tmp/obshell'))) + + ip_servers = servers_memory[ip]['servers'].keys() + server_num = len(ip_servers) + client = servers_clients[ip] + ret = client.execute_command('cat /proc/sys/fs/aio-max-nr /proc/sys/fs/aio-nr') + if not ret: + for server in ip_servers: + alert('aio', err.EC_FAILED_TO_GET_AIO_NR.format(ip=ip), [err.SUG_CONNECT_EXCEPT.format()]) + else: + try: + max_nr, nr = ret.stdout.strip().split('\n') + max_nr, nr = int(max_nr), int(nr) + need = server_num * 20000 + RECD_AIO = 1048576 + if need > max_nr - nr: + for server in ip_servers: + critical('aio', err.EC_AIO_NOT_ENOUGH.format(ip=ip, avail=max_nr - nr, need=need), [err.SUG_SYSCTL.format(var='fs.aio-max-nr', value=max(RECD_AIO, need), ip=ip)]) + elif int(max_nr) < RECD_AIO: + for server in ip_servers: + alert('aio', err.WC_AIO_NOT_ENOUGH.format(ip=ip, current=max_nr), [err.SUG_SYSCTL.format(var='fs.aio-max-nr', value=RECD_AIO, ip=ip)]) + except: + for server in ip_servers: + alert('aio', err.EC_FAILED_TO_GET_AIO_NR.format(ip=ip), [err.SUG_UNSUPPORT_OS.format()]) + stdio.exception('') + + ret = client.execute_command('ulimit -a') + ulimits_min = { + 'open files': { + 'need': lambda x: 20000 * x, + 'recd': lambda x: 655350, + 'name': 'nofile' + }, + 'max user processes': { + 'need': lambda x: 120000, + 'recd': lambda x: 655350, + 'name': 'nproc' + }, + 'core file size': { + 'need': lambda x: 0, + 'recd': lambda x: INF, + 'below_need_error': False, + 'below_recd_error_strict': False, + 'name': 'core' + }, + 'stack size': { + 'need': lambda x: 1024, + 'recd': lambda x: INF, + 'below_recd_error_strict': False, + 'name': 'stack' + }, + } + ulimits = {} + src_data = re.findall('\s?([a-zA-Z\s]+[a-zA-Z])\s+\([a-zA-Z\-,\s]+\)\s+([\d[a-zA-Z]+)', ret.stdout) if ret else [] + for key, value in src_data: + ulimits[key] = value + for key in ulimits_min: + value = ulimits.get(key) + if value == 'unlimited': + continue + if not value or not (value.strip().isdigit()): + for server in ip_servers: + alert('ulimit', '(%s) failed to get %s' % (ip, key), suggests=[err.SUG_UNSUPPORT_OS.format()]) + else: + value = int(value) + need = ulimits_min[key]['need'](server_num) + if need > value: + if (strict_check or production_mode) and ulimits_min[key].get('below_recd_error_strict', True) and value < ulimits_min[key]['recd'](server_num): + need = ulimits_min[key]['recd'](server_num) + need = need if need != INF else 'unlimited' + for server in ip_servers: + if ulimits_min[key].get('below_need_error', True): + critical('ulimit', err.EC_ULIMIT_CHECK.format(server=ip, key=key, need=need, now=value), [err.SUG_ULIMIT.format(name=ulimits_min[key]['name'], value=need, ip=ip)]) + else: + alert('ulimit', err.EC_ULIMIT_CHECK.format(server=ip, key=key, need=need, now=value), suggests=[err.SUG_ULIMIT.format(name=ulimits_min[key]['name'], value=need, ip=ip)]) + else: + need = ulimits_min[key]['recd'](server_num) + if need > value: + need = need if need != INF else 'unlimited' + for server in ip_servers: + if ulimits_min[key].get('below_recd_error_strict', True): + alert('ulimit', err.WC_ULIMIT_CHECK.format(server=ip, key=key, need=need, now=value), suggests=[err.SUG_ULIMIT.format(name=ulimits_min[key]['name'], value=need, ip=ip)]) + else: + stdio.warn(err.WC_ULIMIT_CHECK.format(server=ip, key=key, need=need, now=value)) + + if kernel_check: + # check kernel params + try: + cmd = 'sysctl -a' + ret = client.execute_command(cmd) + if not ret: + alert_strict('kernel', err.EC_FAILED_TO_GET_PARAM.format(key='kernel parameter ', cmd=cmd), [err.SUG_CONNECT_EXCEPT.format(ip=ip)]) + continue + kernel_params = {} + kernel_param_src = ret.stdout.split('\n') + for kernel in kernel_param_src: + if not kernel: + continue + kernel = kernel.split('=') + kernel_params[kernel[0].strip()] = re.findall(r"[-+]?\d+", kernel[1]) + + for kernel_param in kernel_check_items: + check_item = kernel_param['check_item'] + if check_item not in kernel_params: + continue + values = kernel_params[check_item] + needs = kernel_param['need'] + recommends = kernel_param['recommend'] + for i in range(len(values)): + # This case is not handling the value of 'default'. Additional handling is required for 'default' in the future. + item_value = int(values[i]) + need = needs[i] if isinstance(needs, tuple) else needs + recommend = recommends[i] if isinstance(recommends, tuple) else recommends + if isinstance(need, list): + if item_value < need[0] or item_value > need[1]: + suggest = [err.SUG_SYSCTL.format(var=check_item, value=' '.join(str(i) for i in recommend) if isinstance(recommend, list) else recommend, ip=ip)] + need = 'within {}'.format(needs) if needs[-1] != INF else 'greater than {}'.format(needs[0]) + now = '[{}]'.format(', '.join(values)) if len(values) > 1 else item_value + alert_strict(check_item, err.EC_PARAM_NOT_IN_NEED.format(ip=ip, check_item=check_item, need=need, now=now, recommend=recommends), suggest) + break + elif item_value != need: + alert_strict(check_item, err.EC_PARAM_NOT_IN_NEED.format(ip=ip, check_item=check_item, need=needs, recommend=recommend, now=item_value), [err.SUG_SYSCTL.format(var=check_item, value=recommend, ip=ip)]) + except: + stdio.exception('') + + # memory + ret = client.execute_command('cat /proc/meminfo') + if ret: + server_memory_stats = {} + memory_key_map = { + 'MemTotal': 'total', + 'MemFree': 'free', + 'MemAvailable': 'available', + 'Buffers': 'buffers', + 'Cached': 'cached' + } + for key in memory_key_map: + server_memory_stats[memory_key_map[key]] = 0 + for k, v in re.findall('(\w+)\s*:\s*(\d+\s*\w+)', ret.stdout): + if k in memory_key_map: + key = memory_key_map[k] + server_memory_stats[key] = Capacity(str(v)).btyes + + ip_server_memory_info[ip] = server_memory_stats + server_memory_stat = servers_memory[ip] + min_start_need = server_num * START_NEED_MEMORY + total_use = int(server_memory_stat['percentage'] * server_memory_stats['total'] / 100 + server_memory_stat['num']) + if min_start_need > server_memory_stats['available']: + for server in ip_servers: + error('mem', err.EC_OBSERVER_NOT_ENOUGH_MEMORY_ALAILABLE.format(ip=ip, available=Capacity(server_memory_stats['available']), need=Capacity(min_start_need)), [err.SUG_OBSERVER_NOT_ENOUGH_MEMORY_ALAILABLE.format(ip=ip)]) + elif total_use > server_memory_stats['free'] + server_memory_stats['buffers'] + server_memory_stats['cached']: + for server in ip_servers: + server_generate_config = generate_configs.get(server, {}) + suggest = err.SUG_OBSERVER_REDUCE_MEM.format() + suggest.auto_fix = True + for key in ['memory_limit', 'memory_limit_percentage']: + if key in global_generate_config or key in server_generate_config: + suggest.auto_fix = False + break + error('mem', err.EC_OBSERVER_NOT_ENOUGH_MEMORY_CACHED.format(ip=ip, free=Capacity(server_memory_stats['free']), cached=Capacity(server_memory_stats['buffers'] + server_memory_stats['cached']), need=Capacity(total_use)), [suggest]) + elif total_use > server_memory_stats['free']: + system_memory_check() + for server in ip_servers: + alert('mem', err.EC_OBSERVER_NOT_ENOUGH_MEMORY.format(ip=ip, free=Capacity(server_memory_stats['free']), need=Capacity(total_use)), [err.SUG_OBSERVER_REDUCE_MEM.format()]) + else: + system_memory_check() + + # disk + all_path = set(list(servers_disk[ip].keys()) + list(servers_clog_mount[ip].keys())) + disk = get_disk_info(all_paths=all_path, client=client, stdio=stdio) + stdio.verbose('disk: {}'.format(disk)) + for path in servers_disk[ip]: + mount_path = get_mount_path(disk, path) + need = servers_disk[ip][path].get('need') + if not need: + for clog_path in servers_clog_mount[ip]: + clog_mount_path = get_mount_path(disk, clog_path) + if clog_mount_path == mount_path: + need = 60 + stdio.verbose('clog and data use the same disk, datadisk percentage: {}'.format(need)) + break + else: + need = 90 + stdio.verbose('datadisk percentage: {}'.format(need)) + slog_size = float(4 << 30) + if isinstance(need, int): + # slog need 4G + disk[mount_path]['need'] += max(disk[mount_path]['total'] - slog_size, 0) * need / 100 + else: + disk[mount_path]['need'] += Capacity(need).btyes + + disk[mount_path]['need'] += slog_size + disk[mount_path]['is_data_disk'] = True + for path in servers_clog_mount[ip]: + mount_path = get_mount_path(disk, path) + if 'need' in servers_clog_mount[ip][path]: + need = servers_clog_mount[ip][path]['need'] + elif disk[mount_path].get('is_data_disk'): + # hard code + need = 30 + stdio.verbose('clog and data use the same disk, clog percentage: {}'.format(need)) + else: + need = 90 + stdio.verbose('clog percentage: {}'.format(need)) + if isinstance(need, int): + # log_disk_percentage + log_disk_size = disk[mount_path]['total'] * need / 100 + else: + # log_disk_size + log_disk_size = Capacity(need).btyes + servers_log_disk_size[servers_clog_mount[ip][path]['server']] = log_disk_size + disk[mount_path]['need'] += log_disk_size + disk[mount_path]['is_clog_disk'] = True + for p in disk: + avail = disk[p]['avail'] + need = disk[p]['need'] + suggests = [] + if disk[p].get('is_data_disk') and disk[p].get('is_clog_disk'): + suggests.append(err.SUG_OBSERVER_SAME_DISK.format()) + for server in ip_servers: + alert('disk', err.WC_OBSERVER_SAME_DISK.format(ip=ip, disk=p), suggests) + if need > avail: + suggest_temps = { + 'data': { + 'tmplate': err.SUG_OBSERVER_NOT_ENOUGH_DISK, + 'keys': ['datafile_size', 'datafile_disk_percentage'] + } + } + if suggests: + suggest_temps['mem'] = { + 'tmplate': err.SUG_OBSERVER_REDUCE_MEM, + 'keys': ['memory_limit', 'memory_limit_percentage'] + } + suggest_temps['redo'] = { + 'tmplate': err.SUG_OBSERVER_REDUCE_REDO, + 'keys': ['log_disk_size', 'log_disk_percentage'] + } + for server in ip_servers: + tmp_suggests = [] + server_generate_config = generate_configs.get(server, {}) + for item in suggest_temps: + suggest = suggest_temps[item]['tmplate'].format() + suggest.auto_fix = True + for key in suggest_temps[item]['keys']: + if key in global_generate_config or key in server_generate_config: + suggest.auto_fix = False + break + tmp_suggests.append(suggest) + tmp_suggests = sorted(tmp_suggests, key=lambda suggest: suggest.auto_fix, reverse=True) + critical('disk', err.EC_OBSERVER_NOT_ENOUGH_DISK.format(ip=ip, disk=p, avail=Capacity(avail), need=Capacity(need)), tmp_suggests + suggests) + + global_conf = cluster_config.get_global_conf() + has_ocp = 'ocp-express' in plugin_context.components or 'ocp-server-ce' in plugin_context.components + if not has_ocp and any([key.startswith('ocp_meta') for key in global_conf]): + has_ocp = True + + if has_ocp and need_bootstrap and parameter_check: + global_conf_with_default = copy.deepcopy(cluster_config.get_global_conf_with_default()) + original_global_conf = cluster_config.get_original_global_conf() + tenants_componets_map = { + "meta": ["ocp-express", "ocp-server", "ocp-server-ce"], + "monitor": ["ocp-server", "ocp-server-ce"], + } + tenant_memory = tenant_log_disk = memory_limit = system_memory = log_disk_size = sys_log_disk_size = 0 + for tenant, component_list in tenants_componets_map.items(): + prefix = "ocp_%s_tenant_" % tenant + tenant_key = "ocp_%s_tenant" % tenant + for key in global_conf_with_default: + if key.startswith(prefix) and not original_global_conf.get(key, None): + global_conf_with_default['ocp_%s_tenant' % tenant][key.replace(prefix, '', 1)] = global_conf_with_default[key] + if set(list(plugin_context.components)) & set(component_list): + tenant_memory_default = global_conf_with_default[tenant_key].get('memory_size', '0') + tenant_memory += Capacity(original_global_conf.get(tenant_key, {}).get('memory_size', tenant_memory_default)).btyes + tenant_log_disk_default = global_conf_with_default[tenant_key].get('log_disk_size', '0') + tenant_log_disk += Capacity(original_global_conf.get(tenant_key, {}).get('log_disk_size', tenant_log_disk_default)).btyes + + servers_sys_memory = {} + if tenant_memory: + sys_memory_size = None + if 'sys_tenant' in global_conf and 'memory_size' in global_conf['sys_tenant']: + sys_memory_size = global_conf['sys_tenant']['memory_size'] + for server in cluster_config.servers: + if server.ip not in servers_memory or server not in servers_memory[server.ip]['servers'] or server not in servers_min_pool_memory: + stdio.verbose('skip server {} for missing some memory info.'.format(server)) + continue + memory_limit = servers_memory[server.ip]['servers'][server]['num'] + system_memory = servers_memory[server.ip]['servers'][server]['system_memory'] + min_pool_memory = servers_min_pool_memory[server] + if system_memory == 0: + system_memory = get_system_memory(memory_limit, min_pool_memory) + if not sys_memory_size: + sys_memory_size = servers_sys_memory[server] = max(min_pool_memory, min((memory_limit - system_memory) * 0.25, Capacity('16G').btyes)) + if tenant_memory + system_memory + sys_memory_size <= memory_limit: + break + else: + critical('ocp tenant memory', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='memory', avail=Capacity(memory_limit - system_memory - sys_memory_size), need=Capacity(tenant_memory))) + + if tenant_log_disk: + for server in cluster_config.servers: + log_disk_size = servers_log_disk_size[server] + sys_log_disk_size = servers_sys_memory.get(server, 0) + if tenant_log_disk + sys_log_disk_size <= log_disk_size: + break + else: + critical('ocp tenant disk', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='log_disk_size', avail=Capacity(log_disk_size - sys_log_disk_size), need=Capacity(tenant_log_disk))) + + if success: + for ip in servers_net_interface: + client = servers_clients[ip] + for devname in servers_net_interface[ip]: + if client.is_localhost() and (devname != 'lo' and devname is not None) or (not client.is_localhost() and devname == 'lo'): + suggest = err.SUG_NO_SUCH_NET_DEVIC.format(ip=ip) + suggest.auto_fix = client.is_localhost() and 'devname' not in global_generate_config and 'devname' not in server_generate_config + for server in ip_servers: + critical('net', err.EC_OBSERVER_PING_FAILED.format(ip1=ip, devname=devname, ip2=ip), [suggest]) + continue + for _ip in servers_clients: + if ip == _ip: + continue + ping_cmd = 'ping -W 1 -c 1 -I %s %s' % (devname, _ip) if devname is not None else 'ping -W 1 -c 1 %s' % _ip + if not client.execute_command(ping_cmd): + suggest = err.SUG_NO_SUCH_NET_DEVIC.format(ip=ip) + suggest.auto_fix = 'devname' not in global_generate_config and 'devname' not in server_generate_config + for server in ip_servers: + if devname is not None: + critical('net', err.EC_OBSERVER_PING_FAILED.format(ip1=ip, devname=devname, ip2=_ip), [suggest]) + else: + critical('net', err.EC_OBSERVER_PING_FAILED_WITH_NO_DEVNAME.format(ip1=ip, ip2=_ip), [suggest]) + break + + + if success: + times = [] + for ip in servers_clients: + client = servers_clients[ip] + delta = time_delta(client) + stdio.verbose('%s time delta %s' % (ip, delta)) + times.append(delta) + if times and max(times) - min(times) > 500: + critical('ntp', err.EC_OBSERVER_TIME_OUT_OF_SYNC.format(), [err.SUG_OBSERVER_TIME_OUT_OF_SYNC.format()]) + for server in cluster_config.servers: + status = check_status[server] + for key in status: + if status[key].status == err.CheckStatus.WAIT: + status[key].status = err.CheckStatus.PASS + + if success: + stdio.stop_loading('succeed') + plugin_context.return_true() + else: + stdio.stop_loading('fail') + + diff --git a/plugins/oceanbase/4.3.0.0/tenant_optimize.py b/plugins/oceanbase/4.3.0.0/tenant_optimize.py index 23c110e..6d31c54 100644 --- a/plugins/oceanbase/4.3.0.0/tenant_optimize.py +++ b/plugins/oceanbase/4.3.0.0/tenant_optimize.py @@ -19,16 +19,73 @@ import json import os +import time from tool import FileUtil -def tenant_optimize(plugin_context, tenant_cursor=None, *args, **kwargs): +def exec_sql_in_tenant(cursor, tenant, mode='mysql', user='', password='', print_exception=True, retries=20, args=[]): + if not user: + user = 'SYS' if mode == 'oracle' else 'root' + + query_sql = "select a.SVR_IP,c.SQL_PORT from oceanbase.DBA_OB_UNITS as a, oceanbase.DBA_OB_TENANTS as b, oceanbase.DBA_OB_SERVERS as c where a.TENANT_ID=b.TENANT_ID and a.SVR_IP=c.SVR_IP and a.svr_port=c.SVR_PORT and TENANT_NAME=%s" + tenant_server_ports = cursor.fetchall(query_sql, (tenant,), raise_exception=False, exc_level='verbose') + tenant_cursor = [] + + for tenant_server_port in tenant_server_ports: + tenant_ip = tenant_server_port['SVR_IP'] + tenant_port = tenant_server_port['SQL_PORT'] + cursor_tenant = cursor.new_cursor(tenant=tenant, user=user, password=password, ip=tenant_ip, + port=tenant_port, print_exception=print_exception) + if cursor_tenant: + tenant_cursor.append(cursor_tenant) + return tenant_cursor + if not tenant_cursor and retries: + time.sleep(1) + return exec_sql_in_tenant(cursor, tenant, mode, user, password, print_exception=print_exception, + retries=retries - 1, args=args) + + +def scenario_check(scenario): + if scenario not in ['express_oltp', 'complex_oltp', 'olap', 'htap', 'kv']: + return False + return True + + +def tenant_check(tenant): + if tenant == 'sys': + return False + return True + + +def tenant_optimize(plugin_context, tenant_name='test', tenant_cursor=None, scenario=None, *args, **kwargs): cluster_config = plugin_context.cluster_config clients = plugin_context.clients stdio = plugin_context.stdio repositories = plugin_context.repositories - tenant_cursor = plugin_context.get_return('create_tenant').get_return('tenant_cursor') if not tenant_cursor else tenant_cursor + + if tenant_name: + check_result = tenant_check(tenant_name) + if not check_result: + stdio.error('Sys tenant is not supported, please use ordinary tenants') + return plugin_context.return_false() + + create_tenant = plugin_context.get_return('create_tenant') + if create_tenant: + tenant_cursor = create_tenant.get_return('tenant_cursor') if not tenant_cursor else tenant_cursor + + if not tenant_cursor: + cursor = plugin_context.get_return('connect').get_return('cursor') + sql = "select * from oceanbase.DBA_OB_TENANTS where tenant_name = '%s'" % tenant_name + try: + tenant = cursor.fetchone(sql, raise_exception=True) + except Exception as e: + stdio.exception('Select tenant error, error info:{}'.format(e)) + return + if not tenant: + stdio.error('No such Tenant %s' % tenant_name) + return plugin_context.return_false() + tenant_cursor = exec_sql_in_tenant(cursor, tenant_name) def _optimize(json_files): for file in json_files: @@ -59,14 +116,20 @@ def _optimize(json_files): path = repository.repository_dir break - for server in cluster_config.servers: - client = clients[server] - server_config = cluster_config.get_server_conf(server) - scenario = server_config['scenario'] - system_variable_json = f'{path}/etc/default_system_variable.json' - default_parameters_json = f'{path}/etc/default_parameter.json' - - stdio.start_loading(f'optimize tenant with scenario: {scenario}') - if _optimize([system_variable_json, default_parameters_json]): - stdio.stop_loading('succeed') - return plugin_context.return_true() + scenario = getattr(plugin_context.options, 'optimize', '') + global_config = cluster_config.get_global_conf_with_default() + if scenario: + check_result = scenario_check(scenario) + if not check_result: + stdio.error('This scenario is not supported: %s.' % scenario) + return plugin_context.return_false() + else: + stdio.verbose("Tenant optimization scenario not specified, use the cluster scenario: %s." % global_config['scenario']) + scenario = global_config['scenario'] + + system_variable_json = f'{path}/etc/default_system_variable.json' + default_parameters_json = f'{path}/etc/default_parameter.json' + stdio.start_loading(f'optimize tenant with scenario: {scenario}') + if _optimize([system_variable_json, default_parameters_json]): + stdio.stop_loading('succeed') + return plugin_context.return_true() diff --git a/plugins/ocp-server/4.2.1/destroy.py b/plugins/ocp-server/4.2.1/destroy.py index 93ccc8c..5c2e389 100644 --- a/plugins/ocp-server/4.2.1/destroy.py +++ b/plugins/ocp-server/4.2.1/destroy.py @@ -21,12 +21,79 @@ from __future__ import absolute_import, division, print_function import re +from copy import deepcopy + import _errno as err from tool import Cursor global_ret = True +def get_missing_required_parameters(parameters): + results = [] + for key in ["jdbc_url"]: + if parameters.get(key) is None: + results.append(key) + return results + + +def get_ocp_depend_config(cluster_config, stdio): + # depends config + env = {} + depend_observer = False + depend_info = {} + ob_servers_conf = {} + for comp in ["oceanbase", "oceanbase-ce"]: + ob_zones = {} + if comp in cluster_config.depends: + depend_observer = True + ob_servers = cluster_config.get_depend_servers(comp) + for ob_server in ob_servers: + ob_servers_conf[ob_server] = ob_server_conf = cluster_config.get_depend_config(comp, ob_server) + if 'server_ip' not in depend_info: + depend_info['server_ip'] = ob_server.ip + depend_info['mysql_port'] = ob_server_conf['mysql_port'] + depend_info['meta_tenant'] = ob_server_conf['ocp_meta_tenant']['tenant_name'] + depend_info['meta_user'] = ob_server_conf['ocp_meta_username'] + depend_info['meta_password'] = ob_server_conf['ocp_meta_password'] + depend_info['meta_db'] = ob_server_conf['ocp_meta_db'] + depend_info['monitor_tenant'] = ob_server_conf['ocp_monitor_tenant']['tenant_name'] + depend_info['monitor_user'] = ob_server_conf['ocp_monitor_username'] + depend_info['monitor_password'] = ob_server_conf['ocp_monitor_password'] + depend_info['monitor_db'] = ob_server_conf['ocp_monitor_db'] + zone = ob_server_conf['zone'] + if zone not in ob_zones: + ob_zones[zone] = ob_server + break + for comp in ['obproxy', 'obproxy-ce']: + if comp in cluster_config.depends: + obproxy_servers = cluster_config.get_depend_servers(comp) + obproxy_server = obproxy_servers[0] + obproxy_server_config = cluster_config.get_depend_config(comp, obproxy_server) + depend_info['server_ip'] = obproxy_server.ip + depend_info['mysql_port'] = obproxy_server_config['listen_port'] + break + + for server in cluster_config.servers: + default_server_config = deepcopy(cluster_config.get_server_conf_with_default(server)) + server_config = deepcopy(cluster_config.get_server_conf(server)) + original_server_config = cluster_config.get_original_server_conf_with_global(server) + missed_keys = get_missing_required_parameters(original_server_config) + if missed_keys: + if 'jdbc_url' in missed_keys and depend_observer: + default_server_config['jdbc_url'] = 'jdbc:oceanbase://{}:{}/{}'.format(depend_info['server_ip'], depend_info['mysql_port'], depend_info['meta_db'] if not original_server_config.get('ocp_meta_db', None) else original_server_config['ocp_meta_db']) if not original_server_config.get('jdbc_url', None) else original_server_config['jdbc_url'] + default_server_config['ocp_meta_username'] = depend_info['meta_user'] if not original_server_config.get('ocp_meta_username', None) else original_server_config['ocp_meta_username'] + default_server_config['ocp_meta_tenant']['tenant_name'] = depend_info['meta_tenant'] if not original_server_config.get('ocp_meta_tenant', None) else original_server_config['ocp_meta_tenant']['tenant_name'] + default_server_config['ocp_meta_password'] = depend_info['meta_password'] if not original_server_config.get('ocp_meta_password', None) else original_server_config['ocp_meta_password'] + default_server_config['ocp_meta_db'] = depend_info['meta_db'] if not original_server_config.get('ocp_meta_db', None) else original_server_config['ocp_meta_db'] + default_server_config['ocp_monitor_username'] = depend_info['monitor_user'] if not original_server_config.get('ocp_monitor_username', None) else original_server_config['ocp_monitor_username'] + default_server_config['ocp_monitor_tenant']['tenant_name'] = depend_info['monitor_tenant'] if not original_server_config.get('ocp_monitor_tenant', None) else original_server_config['ocp_monitor_tenant']['tenant_name'] + default_server_config['ocp_monitor_password'] = depend_info['monitor_password'] if not original_server_config.get('ocp_monitor_password', None) else original_server_config['ocp_monitor_password'] + default_server_config['ocp_monitor_db'] = depend_info['monitor_db'] if not original_server_config.get('ocp_monitor_db', None) else original_server_config['ocp_monitor_db'] + env[server] = default_server_config + return env + + def destroy(plugin_context, *args, **kwargs): def clean_database(cursor, database): @@ -66,21 +133,26 @@ def clean(path): clean(path) if clean_data: + env = get_ocp_depend_config(cluster_config, stdio) + if not env: + return plugin_context.return_true() + + server_config = env[cluster_config.servers[0]] + jdbc_host, jdbc_port = "", 0 - matched = re.match(r"^jdbc:\S+://(\S+?)(|:\d+)/(\S+)", cluster_config.get_global_conf_with_default()['jdbc_url']) + matched = re.match(r"^jdbc:\S+://(\S+?)(|:\d+)/(\S+)", server_config['jdbc_url']) if matched: jdbc_host = matched.group(1) jdbc_port = matched.group(2)[1:] else: stdio.error("failed to parse jdbc_url") - global_conf = cluster_config.get_global_conf_with_default() stdio.verbose("clean metadb") try: - meta_cursor = Cursor(jdbc_host, jdbc_port, user=global_conf['ocp_meta_username'], tenant=global_conf['ocp_meta_tenant']['tenant_name'], password=global_conf['ocp_meta_password'], stdio=stdio) - clean_database(meta_cursor, global_conf['ocp_meta_db']) + meta_cursor = Cursor(jdbc_host, jdbc_port, user=server_config['ocp_meta_username'], tenant=server_config['ocp_meta_tenant']['tenant_name'], password=server_config['ocp_meta_password'], stdio=stdio) + clean_database(meta_cursor, server_config['ocp_meta_db']) stdio.verbose("clean monitordb") - monitor_cursor = Cursor(jdbc_host, jdbc_port, user=global_conf['ocp_monitor_username'], tenant=global_conf['ocp_monitor_tenant']['tenant_name'], password=global_conf['ocp_monitor_password'], stdio=stdio) - clean_database(monitor_cursor, global_conf['ocp_monitor_db']) + monitor_cursor = Cursor(jdbc_host, jdbc_port, user=server_config['ocp_monitor_username'], tenant=server_config['ocp_monitor_tenant']['tenant_name'], password=server_config['ocp_monitor_password'], stdio=stdio) + clean_database(monitor_cursor, server_config['ocp_monitor_db']) except Exception: stdio.error("failed to clean meta and monitor data") global_ret = False diff --git a/plugins/ocp-server/4.2.1/display.py b/plugins/ocp-server/4.2.1/display.py index dc723eb..9069ae3 100644 --- a/plugins/ocp-server/4.2.1/display.py +++ b/plugins/ocp-server/4.2.1/display.py @@ -33,35 +33,33 @@ def get_missing_required_parameters(parameters): return results -def prepare_parameters(cluster_config, stdio): +def get_ocp_depend_config(cluster_config, stdio): # depends config env = {} depend_observer = False depend_info = {} ob_servers_conf = {} - root_servers = [] for comp in ["oceanbase", "oceanbase-ce"]: ob_zones = {} if comp in cluster_config.depends: depend_observer = True - observer_globals = cluster_config.get_depend_config(comp) - ocp_meta_keys = [ - "ocp_meta_tenant", "ocp_meta_db", "ocp_meta_username", "ocp_meta_password", "appname", "cluster_id", "root_password" - ] - for key in ocp_meta_keys: - value = observer_globals.get(key) - if value is not None: - depend_info[key] = value ob_servers = cluster_config.get_depend_servers(comp) for ob_server in ob_servers: ob_servers_conf[ob_server] = ob_server_conf = cluster_config.get_depend_config(comp, ob_server) if 'server_ip' not in depend_info: depend_info['server_ip'] = ob_server.ip depend_info['mysql_port'] = ob_server_conf['mysql_port'] + depend_info['meta_tenant'] = ob_server_conf['ocp_meta_tenant']['tenant_name'] + depend_info['meta_user'] = ob_server_conf['ocp_meta_username'] + depend_info['meta_password'] = ob_server_conf['ocp_meta_password'] + depend_info['meta_db'] = ob_server_conf['ocp_meta_db'] + depend_info['monitor_tenant'] = ob_server_conf['ocp_monitor_tenant']['tenant_name'] + depend_info['monitor_user'] = ob_server_conf['ocp_monitor_username'] + depend_info['monitor_password'] = ob_server_conf['ocp_monitor_password'] + depend_info['monitor_db'] = ob_server_conf['ocp_monitor_db'] zone = ob_server_conf['zone'] if zone not in ob_zones: ob_zones[zone] = ob_server - root_servers = ob_zones.values() break for comp in ['obproxy', 'obproxy-ce']: if comp in cluster_config.depends: @@ -73,29 +71,22 @@ def prepare_parameters(cluster_config, stdio): break for server in cluster_config.servers: - server_config = deepcopy(cluster_config.get_server_conf_with_default(server)) - original_server_config = cluster_config.get_original_server_conf(server) + default_server_config = deepcopy(cluster_config.get_server_conf_with_default(server)) + server_config = deepcopy(cluster_config.get_server_conf(server)) + original_server_config = cluster_config.get_original_server_conf_with_global(server) missed_keys = get_missing_required_parameters(original_server_config) if missed_keys: if 'jdbc_url' in missed_keys and depend_observer: - server_config['jdbc_url'] = 'jdbc:oceanbase://{}:{}/{}'.format(depend_info['server_ip'], depend_info['mysql_port'], depend_info['ocp_meta_db']) - if 'jdbc_username' in missed_keys and depend_observer: - server_config['jdbc_username'] = "{}@{}".format(depend_info['ocp_meta_username'], - depend_info.get('ocp_meta_tenant', {}).get("tenant_name")) - depends_key_maps = { - "jdbc_password": "ocp_meta_password", - "cluster_name": "appname", - "ob_cluster_id": "cluster_id", - "root_sys_password": "root_password", - "agent_username": "obagent_username", - "agent_password": "obagent_password", - "server_addresses": "server_addresses" - } - for key in depends_key_maps: - if key in missed_keys: - if depend_info.get(depends_key_maps[key]) is not None: - server_config[key] = depend_info[depends_key_maps[key]] - env[server] = server_config + default_server_config['jdbc_url'] = 'jdbc:oceanbase://{}:{}/{}'.format(depend_info['server_ip'], depend_info['mysql_port'], depend_info['meta_db'] if not original_server_config.get('ocp_meta_db', None) else original_server_config['ocp_meta_db']) if not original_server_config.get('jdbc_url', None) else original_server_config['jdbc_url'] + default_server_config['ocp_meta_username'] = depend_info['meta_user'] if not original_server_config.get('ocp_meta_username', None) else original_server_config['ocp_meta_username'] + default_server_config['ocp_meta_tenant']['tenant_name'] = depend_info['meta_tenant'] if not original_server_config.get('ocp_meta_tenant', None) else original_server_config['ocp_meta_tenant']['tenant_name'] + default_server_config['ocp_meta_password'] = depend_info['meta_password'] if not original_server_config.get('ocp_meta_password', None) else original_server_config['ocp_meta_password'] + default_server_config['ocp_meta_db'] = depend_info['meta_db'] if not original_server_config.get('ocp_meta_db', None) else original_server_config['ocp_meta_db'] + default_server_config['ocp_monitor_username'] = depend_info['monitor_user'] if not original_server_config.get('ocp_monitor_username', None) else original_server_config['ocp_monitor_username'] + default_server_config['ocp_monitor_tenant']['tenant_name'] = depend_info['monitor_tenant'] if not original_server_config.get('ocp_monitor_tenant', None) else original_server_config['ocp_monitor_tenant']['tenant_name'] + default_server_config['ocp_monitor_password'] = depend_info['monitor_password'] if not original_server_config.get('ocp_monitor_password', None) else original_server_config['ocp_monitor_password'] + default_server_config['ocp_monitor_db'] = depend_info['monitor_db'] if not original_server_config.get('ocp_monitor_db', None) else original_server_config['ocp_monitor_db'] + env[server] = default_server_config return env @@ -104,10 +95,11 @@ def display(plugin_context, cursor, *args, **kwargs): stdio = plugin_context.stdio servers = cluster_config.servers results = [] - start_env = prepare_parameters(cluster_config, stdio) + start_env = get_ocp_depend_config(cluster_config, stdio) for server in servers: api_cursor = cursor.get(server) server_config = start_env[server] + original_global_conf = cluster_config.get_original_global_conf() ip = server.ip if ip == '127.0.0.1': ip = NetUtil.get_host_ip() @@ -116,11 +108,11 @@ def display(plugin_context, cursor, *args, **kwargs): 'ip': ip, 'port': api_cursor.port, 'user': "admin", - 'password': server_config['admin_password'], + 'password': server_config['admin_password'] if not original_global_conf.get('admin_password', '') else original_global_conf['admin_password'], 'url': url, 'status': 'active' if api_cursor and api_cursor.status(stdio) else 'inactive' }) - stdio.print_list(results, ['url', 'username', 'password', 'status'], lambda x: [x['url'], 'admin', server_config['admin_password'], x['status']], title='%s' % cluster_config.name) + stdio.print_list(results, ['url', 'username', 'password', 'status'], lambda x: [x['url'], 'admin', server_config['admin_password'] if not original_global_conf.get('admin_password', '') else original_global_conf['admin_password'], x['status']], title='%s' % cluster_config.name) active_result = [r for r in results if r['status'] == 'active'] info_dict = active_result[0] if len(active_result) > 0 else None if info_dict is not None: diff --git a/plugins/ocp-server/4.2.1/generate_config.py b/plugins/ocp-server/4.2.1/generate_config.py index d2ec372..213f9d6 100644 --- a/plugins/ocp-server/4.2.1/generate_config.py +++ b/plugins/ocp-server/4.2.1/generate_config.py @@ -48,7 +48,7 @@ def generate_config(plugin_context, auto_depend=False, generate_config_mini=Fals return plugin_context.return_false() if 'memory_size' not in global_config: - cluster_config.update_global_conf('memory_size', min_memory_size) + cluster_config.update_global_conf('memory_size', min_memory_size, False) # write required memory into resource namespace resource = plugin_context.namespace.get_variable("required_resource") diff --git a/plugins/ocp-server/4.2.1/init.py b/plugins/ocp-server/4.2.1/init.py index df81d30..c90a912 100644 --- a/plugins/ocp-server/4.2.1/init.py +++ b/plugins/ocp-server/4.2.1/init.py @@ -28,9 +28,6 @@ from ssh import LocalClient -OBD_INSTALL_PRE = os.environ.get('OBD_INSTALL_PRE', '/') - - def _clean(server, client, path, stdio=None): ret = client.execute_command('rm -fr %s' % path, timeout=-1) if not ret: diff --git a/plugins/ocp-server/4.2.1/parameter.yaml b/plugins/ocp-server/4.2.1/parameter.yaml index 1f353ab..e22047a 100644 --- a/plugins/ocp-server/4.2.1/parameter.yaml +++ b/plugins/ocp-server/4.2.1/parameter.yaml @@ -278,7 +278,7 @@ require: true type: DICT default: - tenant_name: meta_tenant + tenant_name: ocp_meta max_cpu: 1 memory_size: 2147483648 need_redeploy: true @@ -288,7 +288,7 @@ require: false type: DICT default: - tenant_name: monitor_tenant + tenant_name: ocp_monitor max_cpu: 1 memory_size: 2147483648 need_redeploy: true @@ -329,14 +329,14 @@ - name: ocp_meta_db require: false type: SAFE_STRING - default: meta_database + default: ocp_meta need_redeploy: true description_en: The database name for ocp meta db description_local: ocp server的元数据库使用的数据库名 - name: ocp_monitor_username require: false type: OB_USER - default: monitor_user + default: monitor need_restart: true description_en: The username for obagent monitor user description_local: obagent 监控用户的用户名 @@ -350,7 +350,7 @@ - name: ocp_monitor_db require: false type: SAFE_STRING - default: monitor_database + default: ocp_monitor need_redeploy: true description_en: The database name for ocp meta db description_local: ocp server的监控数据库使用的数据库名 diff --git a/plugins/ocp-server/4.2.1/scale_in_check.py b/plugins/ocp-server/4.2.1/scale_in_check.py index a0d222e..1c87404 100644 --- a/plugins/ocp-server/4.2.1/scale_in_check.py +++ b/plugins/ocp-server/4.2.1/scale_in_check.py @@ -16,13 +16,8 @@ # # You should have received a copy of the GNU General Public License # along with OceanBase Deploy. If not, see . - - from __future__ import absolute_import, division, print_function def scale_in_check(plugin_context, *args, **kwargs): - cluster_config = plugin_context.cluster_config - stdio = plugin_context.stdio - stdio.error("Not support component {}".format(cluster_config.name)) - return plugin_context.return_false() + return plugin_context.return_true() \ No newline at end of file diff --git a/plugins/ocp-server/4.2.1/scale_out_check.py b/plugins/ocp-server/4.2.1/scale_out_check.py index 598aa83..0034021 100644 --- a/plugins/ocp-server/4.2.1/scale_out_check.py +++ b/plugins/ocp-server/4.2.1/scale_out_check.py @@ -1,4 +1,3 @@ -# coding: utf-8 # OceanBase Deploy. # Copyright (C) 2021 OceanBase # @@ -16,13 +15,17 @@ # # You should have received a copy of the GNU General Public License # along with OceanBase Deploy. If not, see . - - from __future__ import absolute_import, division, print_function def scale_out_check(plugin_context, *args, **kwargs): cluster_config = plugin_context.cluster_config stdio = plugin_context.stdio - stdio.error("Not support component `%s`".format(cluster_config.name)) - return plugin_context.return_false() + added_servers = cluster_config.added_servers + + for server in added_servers: + server_config = cluster_config.get_original_server_conf(server) + if server_config.get('admin_password'): + stdio.error('Do not set ‘admin_password’ during OCP server scale-out.') + return plugin_context.return_false() + return plugin_context.return_true() \ No newline at end of file diff --git a/plugins/ocp-server/4.2.1/start.py b/plugins/ocp-server/4.2.1/start.py index 790c20f..543d2d3 100644 --- a/plugins/ocp-server/4.2.1/start.py +++ b/plugins/ocp-server/4.2.1/start.py @@ -24,17 +24,12 @@ import os import re import time -from glob import glob from copy import deepcopy -from const import CONST_OBD_HOME -from optparse import Values from tool import Cursor from _types import Capacity, CapacityWithB -OBD_INSTALL_PRE = os.environ.get('OBD_INSTALL_PRE', '/') - PRI_KEY_FILE = '.ocp-server' PUB_KEY_FILE = '.ocp-server.pub' @@ -313,6 +308,8 @@ def start_cluster(times=0): cmd += ' --with-property=ocp.site.url:{}'.format(site_url) # set connection mode to direct to avoid obclient issue cmd += ' --with-property=obsdk.ob.connection.mode:direct' + cmd += ' --with-property=ocp.iam.login.client.max-attempts:60' + cmd += ' --with-property=ocp.iam.login.client.lockout-minutes:1' if server_config['admin_password'] != '********': admin_password = server_config['admin_password'].replace("'", """'"'"'""") environ_variable += "export OCP_INITIAL_ADMIN_PASSWORD=\'%s\';" % admin_password @@ -350,7 +347,7 @@ def start_cluster(times=0): stdio.start_loading("%s program health check" % cluster_config.name) failed = [] servers = server_pid.keys() - count = 40 + count = 120 while servers and count: count -= 1 tmp_servers = [] @@ -463,7 +460,6 @@ def stop_cluster(): if not start_cluster(1): stdio.error('start %s failed' % cluster_config.name) return plugin_context.return_false() - time.sleep(20) plugin_context.set_variable('start_env', start_env) return plugin_context.return_true(need_bootstrap=True) diff --git a/plugins/ocp-server/4.2.1/start_check.py b/plugins/ocp-server/4.2.1/start_check.py index 2dd8439..6e93d3f 100644 --- a/plugins/ocp-server/4.2.1/start_check.py +++ b/plugins/ocp-server/4.2.1/start_check.py @@ -23,6 +23,7 @@ import re import os import time +import copy import datetime from copy import deepcopy @@ -67,9 +68,9 @@ def get_mount_path(disk, _path): return _mount_path -def get_disk_info_by_path(path, client, stdio): +def get_disk_info_by_path(ocp_user, path, client, stdio): disk_info = {} - ret = client.execute_command('df --block-size=1024 {}'.format(path)) + ret = client.execute_command(execute_cmd(ocp_user, 'df --block-size=1024 {}'.format(path))) if ret: for total, used, avail, puse, path in re.findall(r'(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)', ret.stdout): disk_info[path] = {'total': int(total) << 10, 'avail': int(avail) << 10, 'need': 0} @@ -77,12 +78,12 @@ def get_disk_info_by_path(path, client, stdio): return disk_info -def get_disk_info(all_paths, client, stdio): +def get_disk_info(all_paths, client, ocp_user, stdio): overview_ret = True - disk_info = get_disk_info_by_path('', client, stdio) + disk_info = get_disk_info_by_path(ocp_user, '', client, stdio) if not disk_info: overview_ret = False - disk_info = get_disk_info_by_path('/', client, stdio) + disk_info = get_disk_info_by_path(ocp_user, '/', client, stdio) if not disk_info: disk_info['/'] = {'total': 0, 'avail': 0, 'need': 0} all_path_success = {} @@ -90,7 +91,7 @@ def get_disk_info(all_paths, client, stdio): all_path_success[path] = False cur_path = path while cur_path not in disk_info: - disk_info_for_current_path = get_disk_info_by_path(cur_path, client, stdio) + disk_info_for_current_path = get_disk_info_by_path(ocp_user, cur_path, client, stdio) if disk_info_for_current_path: disk_info.update(disk_info_for_current_path) all_path_success[path] = True @@ -157,8 +158,8 @@ def get_ocp_depend_config(cluster_config, stdio): env[server] = default_server_config return env -def execute_cmd(server_config, cmd): - return cmd if not server_config.get('launch_user', None) else 'sudo ' + cmd +def execute_cmd(ocp_user, cmd): + return cmd if not ocp_user else 'sudo ' + cmd def start_check(plugin_context, init_check_status=False, work_dir_check=False, work_dir_empty_check=True, strict_check=False, precheck=False, @@ -226,9 +227,11 @@ def get_option(key, default=''): 'time check': err.CheckStatus(), 'launch user': err.CheckStatus(), 'sudo nopasswd': err.CheckStatus(), - 'tenant': err.CheckStatus(), 'clockdiff': err.CheckStatus(), - 'admin_password': err.CheckStatus() + 'admin_password': err.CheckStatus(), + 'tenant cpu': err.CheckStatus(), + 'tenant mem': err.CheckStatus(), + 'tenant clog': err.CheckStatus() } if work_dir_check: check_status[server]['dir'] = err.CheckStatus() @@ -273,6 +276,7 @@ def get_option(key, default=''): critical('sudo nopasswd', err.EC_OCP_SERVER_SUDO_NOPASSWD.format(ip=str(server), user=client.config.username), [err.SUG_OCP_SERVER_SUDO_NOPASSWD.format(ip=str(server), user=client.config.username)]) server_config = env[server] + ocp_user = server_config.get('launch_user', '') missed_keys = get_missing_required_parameters(server_config) if missed_keys: stdio.error(err.EC_NEED_CONFIG.format(server=server, component=cluster_config.name, miss_keys=missed_keys)) @@ -280,14 +284,14 @@ def get_option(key, default=''): home_path = server_config['home_path'] if not precheck: remote_pid_path = '%s/run/ocp-server.pid' % home_path - remote_pid = client.execute_command(execute_cmd(server_config, 'cat %s' % remote_pid_path)).stdout.strip() + remote_pid = client.execute_command(execute_cmd(ocp_user, 'cat %s' % remote_pid_path)).stdout.strip() if remote_pid: - if client.execute_command(execute_cmd(server_config, 'ls /proc/%s' % remote_pid)): + if client.execute_command(execute_cmd(ocp_user, 'ls /proc/%s' % remote_pid)): stdio.verbose('%s is running, skip' % server) wait_2_pass() continue - if not cluster_config.depends and not precheck: + if not cluster_config.depends: # check meta db connect before start jdbc_url = server_config['jdbc_url'] matched = re.match(r"^jdbc:\S+://(\S+?)(|:\d+)/(\S+)", jdbc_url) @@ -300,11 +304,16 @@ def get_option(key, default=''): jdbc_database = matched.group(3) connected = False retries = 10 + if jdbc_url: + username = server_config['jdbc_username'] + password = server_config['jdbc_password'] + else: + username = "{0}@{1}".format(server_config['ocp_meta_username'], server_config['ocp_meta_tenant']['tenant_name']) + password = server_config['ocp_meta_password'] while not connected and retries: retries -= 1 try: - cursor = Cursor(ip=jdbc_host, port=jdbc_port, user="{0}@{1}".format(server_config['ocp_meta_username'], server_config['ocp_meta_tenant']['tenant_name']), password=server_config['ocp_meta_password'], - stdio=stdio) + cursor = Cursor(ip=jdbc_host, port=jdbc_port, user=username, password=password, stdio=stdio) connected = True stdio.verbose('check cursor passed') except: @@ -328,12 +337,78 @@ def get_option(key, default=''): if not abs((now - ob_time).total_seconds()) < 180: critical('time check', err.EC_OCP_SERVER_TIME_SHIFT.format(server=server)) + if cursor: + stdio.verbose('tenant check ') + zone_obs_num = {} + sql = "select zone, count(*) num from oceanbase.DBA_OB_SERVERS where status = 'active' group by zone" + res = cursor.fetchall(sql) + if res is False: + return + + for row in res: + zone_obs_num[str(row['zone'])] = row['num'] + zone_list = zone_obs_num.keys() + if isinstance(zone_list, str): + zones = zone_list.replace(';', ',').split(',') + else: + zones = zone_list + zone_list = "('%s')" % "','".join(zones) + + min_unit_num = min(zone_obs_num.items(), key=lambda x: x[1])[1] + unit_num = get_option('unit_num', min_unit_num) + if unit_num > min_unit_num: + return error('resource pool unit num is bigger than zone server count') + + sql = "select count(*) num from oceanbase.DBA_OB_SERVERS where status = 'active' and start_service_time > 0" + count = 30 + while count: + num = cursor.fetchone(sql) + if num is False: + return + num = num['num'] + if num >= unit_num: + break + count -= 1 + time.sleep(1) + + sql = "SELECT * FROM oceanbase.GV$OB_SERVERS where zone in %s" % zone_list + servers_stats = cursor.fetchall(sql) + if servers_stats is False: + return + cpu_available = servers_stats[0]['CPU_CAPACITY_MAX'] - servers_stats[0]['CPU_ASSIGNED_MAX'] + mem_available = servers_stats[0]['MEM_CAPACITY'] - servers_stats[0]['MEM_ASSIGNED'] + disk_available = servers_stats[0]['DATA_DISK_CAPACITY'] - servers_stats[0]['DATA_DISK_IN_USE'] + log_disk_available = servers_stats[0]['LOG_DISK_CAPACITY'] - servers_stats[0]['LOG_DISK_ASSIGNED'] + for servers_stat in servers_stats[1:]: + cpu_available = min(servers_stat['CPU_CAPACITY_MAX'] - servers_stat['CPU_ASSIGNED_MAX'], cpu_available) + mem_available = min(servers_stat['MEM_CAPACITY'] - servers_stat['MEM_ASSIGNED'], mem_available) + disk_available = min(servers_stat['DATA_DISK_CAPACITY'] - servers_stat['DATA_DISK_IN_USE'], disk_available) + log_disk_available = min(servers_stat['LOG_DISK_CAPACITY'] - servers_stat['LOG_DISK_ASSIGNED'], log_disk_available) + + global_conf_with_default = copy.deepcopy(cluster_config.get_global_conf_with_default()) + meta_db_memory_size = Capacity(global_conf_with_default['ocp_meta_tenant'].get('memory_size')).btyes + monitor_db_memory_size = Capacity(global_conf_with_default['ocp_monitor_tenant'].get('memory_size', 0)).btyes + meta_db_max_cpu = global_conf_with_default['ocp_meta_tenant'].get('max_cpu') + monitor_db_max_cpu = global_conf_with_default['ocp_monitor_tenant'].get('max_cpu', 0) + meta_db_log_disk_size = global_conf_with_default['ocp_meta_tenant'].get('log_disk_size', 0) + meta_db_log_disk_size = Capacity(meta_db_log_disk_size).btyes + monitor_db_log_disk_size = global_conf_with_default['ocp_monitor_tenant'].get('log_disk_size', 0) + monitor_db_log_disk_size = Capacity(monitor_db_log_disk_size).btyes + if meta_db_max_cpu and monitor_db_max_cpu: + if int(meta_db_max_cpu) + int(monitor_db_max_cpu) > cpu_available: + critical('tenant cpu', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='cpu', avail=cpu_available, need=int(meta_db_max_cpu) + int(monitor_db_max_cpu))) + if meta_db_memory_size and monitor_db_memory_size: + if meta_db_memory_size + monitor_db_memory_size > mem_available: + critical('tenant mem', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='memory', avail=Capacity(mem_available), need=Capacity(meta_db_memory_size + monitor_db_memory_size))) + if meta_db_log_disk_size and monitor_db_log_disk_size: + if meta_db_log_disk_size + monitor_db_log_disk_size > log_disk_available: + critical('tenant clog', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='log_disk_size', avail=Capacity(log_disk_available), need=Capacity(meta_db_log_disk_size + monitor_db_log_disk_size))) + # user check stdio.verbose('user check ') - ocp_user = server_config.get('launch_user', '') if ocp_user: client = clients[server] - if not client.execute_command(execute_cmd(server_config, "id -u %s" % ocp_user)): + if not client.execute_command(execute_cmd(ocp_user, "id -u %s" % ocp_user)): critical('launch user', err.EC_OCP_SERVER_LAUNCH_USER_NOT_EXIST.format(server=server, user=ocp_user)) if work_dir_check: @@ -365,9 +440,9 @@ def get_option(key, default=''): critical('dir', check_dirs[path], suggests) break - if client.execute_command(execute_cmd(server_config, 'bash -c "[ -a %s ]"' % path)): - is_dir = client.execute_command(execute_cmd(server_config, '[ -d {} ]'.format(path))) - has_write_permission = client.execute_command(execute_cmd(server_config, '[ -w {} ]'.format(path))) + if client.execute_command(execute_cmd(ocp_user, 'bash -c "[ -a %s ]"' % path)): + is_dir = client.execute_command(execute_cmd(ocp_user, '[ -d {} ]'.format(path))) + has_write_permission = client.execute_command(execute_cmd(ocp_user, '[ -w {} ]'.format(path))) if is_dir and has_write_permission: if empty_check: check_privilege_cmd = "ls %s" % path @@ -422,7 +497,7 @@ def get_option(key, default=''): stdio.verbose('java check ') java_bin = server_config.get('java_bin', '/usr/bin/java') client.add_env('PATH', '%s/jre/bin:' % server_config['home_path']) - ret = client.execute_command(execute_cmd(server_config, '{} -version'.format(java_bin))) + ret = client.execute_command(execute_cmd(ocp_user, '{} -version'.format(java_bin))) stdio.verbose('java version %s' % ret) if not ret: critical('java', err.EC_OCP_SERVER_JAVA_NOT_FOUND.format(server=server), [err.SUG_OCP_SERVER_INSTALL_JAVA_WITH_VERSION.format(version='1.8.0')]) @@ -458,7 +533,7 @@ def get_option(key, default=''): clockdiff_bin = 'type -P clockdiff' res = client.execute_command(clockdiff_bin).stdout client.execute_command('sudo chmod u+s %s' % res) - client.execute_command("sudo setcap 'cap_net_raw+ep' %s" % res) + client.execute_command("sudo setcap 'cap_sys_nice+ep cap_net_raw+ep' %s" % res) except Exception as e: stdio.error(e) critical('clockdiff', err.EC_OCP_SERVER_CLOCKDIFF_NOT_EXISTS.format(server=server)) @@ -532,7 +607,7 @@ def get_option(key, default=''): stdio.verbose('disk check ') for ip in servers_disk: client = servers_client[ip] - disk_info = get_disk_info(all_paths=servers_disk[ip], client=client, stdio=stdio) + disk_info = get_disk_info(all_paths=servers_disk[ip], client=client, ocp_user=ocp_user, stdio=stdio) if disk_info: for path in servers_disk[ip]: disk_needed = servers_disk[ip][path] diff --git a/plugins/ocp-server/4.2.1/upgrade.py b/plugins/ocp-server/4.2.1/upgrade.py index b62fd4e..312f008 100644 --- a/plugins/ocp-server/4.2.1/upgrade.py +++ b/plugins/ocp-server/4.2.1/upgrade.py @@ -72,7 +72,7 @@ def upgrade(plugin_context, search_py_script_plugin, apply_param_plugin, *args, ret = connect_plugin(namespace, namespaces, deploy_name, deploy_status, repositories, components, clients, cluster_config, cmds, options, stdio, *args, **kwargs) if ret: - cursor = ret.get_return('cursor') - if display_plugin(namespace, namespaces, deploy_name, deploy_status, repositories, components, clients, cluster_config, cmds, options, stdio, cursor=cursor, *args, **kwargs): + meta_cursor = ret.get_return('cursor') + if display_plugin(namespace, namespaces, deploy_name, deploy_status, repositories, components, clients, cluster_config, cmds, options, stdio, cursor=meta_cursor, *args, **kwargs): return plugin_context.return_true() return plugin_context.return_false() diff --git a/plugins/ocp-server/4.2.2/start_check.py b/plugins/ocp-server/4.2.2/start_check.py index 28ab95e..506f2fb 100644 --- a/plugins/ocp-server/4.2.2/start_check.py +++ b/plugins/ocp-server/4.2.2/start_check.py @@ -22,6 +22,7 @@ import re import os +import copy import time import datetime @@ -86,9 +87,9 @@ def get_mount_path(disk, _path): return _mount_path -def get_disk_info_by_path(path, client, stdio): +def get_disk_info_by_path(ocp_user, path, client, stdio): disk_info = {} - ret = client.execute_command('df --block-size=1024 {}'.format(path)) + ret = client.execute_command(execute_cmd(ocp_user, 'df --block-size=1024 {}'.format(path))) if ret: for total, used, avail, puse, path in re.findall(r'(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)\s+(.+)', ret.stdout): disk_info[path] = {'total': int(total) << 10, 'avail': int(avail) << 10, 'need': 0} @@ -96,12 +97,12 @@ def get_disk_info_by_path(path, client, stdio): return disk_info -def get_disk_info(all_paths, client, stdio): +def get_disk_info(all_paths, client, ocp_user, stdio): overview_ret = True - disk_info = get_disk_info_by_path('', client, stdio) + disk_info = get_disk_info_by_path(ocp_user, '', client, stdio) if not disk_info: overview_ret = False - disk_info = get_disk_info_by_path('/', client, stdio) + disk_info = get_disk_info_by_path(ocp_user, '/', client, stdio) if not disk_info: disk_info['/'] = {'total': 0, 'avail': 0, 'need': 0} all_path_success = {} @@ -109,7 +110,7 @@ def get_disk_info(all_paths, client, stdio): all_path_success[path] = False cur_path = path while cur_path not in disk_info: - disk_info_for_current_path = get_disk_info_by_path(cur_path, client, stdio) + disk_info_for_current_path = get_disk_info_by_path(ocp_user, cur_path, client, stdio) if disk_info_for_current_path: disk_info.update(disk_info_for_current_path) all_path_success[path] = True @@ -176,8 +177,8 @@ def get_ocp_depend_config(cluster_config, stdio): env[server] = default_server_config return env -def execute_cmd(server_config, cmd): - return cmd if not server_config.get('launch_user', None) else 'sudo ' + cmd +def execute_cmd(ocp_user, cmd): + return cmd if not ocp_user else 'sudo ' + cmd def start_check(plugin_context, init_check_status=False, work_dir_check=False, work_dir_empty_check=True, strict_check=False, precheck=False, @@ -245,9 +246,11 @@ def get_option(key, default=''): 'time check': err.CheckStatus(), 'launch user': err.CheckStatus(), 'sudo nopasswd': err.CheckStatus(), - 'tenant': err.CheckStatus(), 'clockdiff': err.CheckStatus(), - 'admin_password': err.CheckStatus() + 'admin_password': err.CheckStatus(), + 'tenant cpu': err.CheckStatus(), + 'tenant mem': err.CheckStatus(), + 'tenant log disk': err.CheckStatus() } if work_dir_check: check_status[server]['dir'] = err.CheckStatus() @@ -292,6 +295,7 @@ def get_option(key, default=''): critical('sudo nopasswd', err.EC_OCP_SERVER_SUDO_NOPASSWD.format(ip=str(server), user=client.config.username), [err.SUG_OCP_SERVER_SUDO_NOPASSWD.format(ip=str(server), user=client.config.username)]) server_config = env[server] + ocp_user = server_config.get('launch_user', '') missed_keys = get_missing_required_parameters(server_config) if missed_keys: stdio.error(err.EC_NEED_CONFIG.format(server=server, component=cluster_config.name, miss_keys=missed_keys)) @@ -299,14 +303,14 @@ def get_option(key, default=''): home_path = server_config['home_path'] if not precheck: remote_pid_path = '%s/run/ocp-server.pid' % home_path - remote_pid = client.execute_command(execute_cmd(server_config, 'cat %s' % remote_pid_path)).stdout.strip() + remote_pid = client.execute_command(execute_cmd(ocp_user, 'cat %s' % remote_pid_path)).stdout.strip() if remote_pid: - if client.execute_command(execute_cmd(server_config, 'ls /proc/%s' % remote_pid)): + if client.execute_command(execute_cmd(ocp_user, 'ls /proc/%s' % remote_pid)): stdio.verbose('%s is running, skip' % server) wait_2_pass() continue - if not cluster_config.depends and not precheck: + if not cluster_config.depends: # check meta db connect before start jdbc_url = server_config['jdbc_url'] matched = re.match(r"^jdbc:\S+://(\S+?)(|:\d+)/(\S+)", jdbc_url) @@ -319,11 +323,16 @@ def get_option(key, default=''): jdbc_database = matched.group(3) connected = False retries = 10 + if jdbc_url: + username = server_config['jdbc_username'] + password = server_config['jdbc_password'] + else: + username = "{0}@{1}".format(server_config['ocp_meta_username'], server_config['ocp_meta_tenant']['tenant_name']) + password = server_config['ocp_meta_password'] while not connected and retries: retries -= 1 try: - cursor = Cursor(ip=jdbc_host, port=jdbc_port, user="{0}@{1}".format(server_config['ocp_meta_username'], server_config['ocp_meta_tenant']['tenant_name']), password=server_config['ocp_meta_password'], - stdio=stdio) + cursor = Cursor(ip=jdbc_host, port=jdbc_port, user=username, password=password, stdio=stdio) connected = True stdio.verbose('check cursor passed') except: @@ -347,12 +356,78 @@ def get_option(key, default=''): if not abs((now - ob_time).total_seconds()) < 180: critical('time check', err.EC_OCP_SERVER_TIME_SHIFT.format(server=server)) + if cursor: + stdio.verbose('tenant check ') + zone_obs_num = {} + sql = "select zone, count(*) num from oceanbase.DBA_OB_SERVERS where status = 'active' group by zone" + res = cursor.fetchall(sql) + if res is False: + return + + for row in res: + zone_obs_num[str(row['zone'])] = row['num'] + zone_list = zone_obs_num.keys() + if isinstance(zone_list, str): + zones = zone_list.replace(';', ',').split(',') + else: + zones = zone_list + zone_list = "('%s')" % "','".join(zones) + + min_unit_num = min(zone_obs_num.items(), key=lambda x: x[1])[1] + unit_num = get_option('unit_num', min_unit_num) + if unit_num > min_unit_num: + return error('resource pool unit num is bigger than zone server count') + + sql = "select count(*) num from oceanbase.DBA_OB_SERVERS where status = 'active' and start_service_time > 0" + count = 30 + while count: + num = cursor.fetchone(sql) + if num is False: + return + num = num['num'] + if num >= unit_num: + break + count -= 1 + time.sleep(1) + + sql = "SELECT * FROM oceanbase.GV$OB_SERVERS where zone in %s" % zone_list + servers_stats = cursor.fetchall(sql) + if servers_stats is False: + return + cpu_available = servers_stats[0]['CPU_CAPACITY_MAX'] - servers_stats[0]['CPU_ASSIGNED_MAX'] + mem_available = servers_stats[0]['MEM_CAPACITY'] - servers_stats[0]['MEM_ASSIGNED'] + disk_available = servers_stats[0]['DATA_DISK_CAPACITY'] - servers_stats[0]['DATA_DISK_IN_USE'] + log_disk_available = servers_stats[0]['LOG_DISK_CAPACITY'] - servers_stats[0]['LOG_DISK_ASSIGNED'] + for servers_stat in servers_stats[1:]: + cpu_available = min(servers_stat['CPU_CAPACITY_MAX'] - servers_stat['CPU_ASSIGNED_MAX'], cpu_available) + mem_available = min(servers_stat['MEM_CAPACITY'] - servers_stat['MEM_ASSIGNED'], mem_available) + disk_available = min(servers_stat['DATA_DISK_CAPACITY'] - servers_stat['DATA_DISK_IN_USE'], disk_available) + log_disk_available = min(servers_stat['LOG_DISK_CAPACITY'] - servers_stat['LOG_DISK_ASSIGNED'], log_disk_available) + + global_conf_with_default = copy.deepcopy(cluster_config.get_global_conf_with_default()) + meta_db_memory_size = Capacity(global_conf_with_default['ocp_meta_tenant'].get('memory_size')).btyes + monitor_db_memory_size = Capacity(global_conf_with_default['ocp_monitor_tenant'].get('memory_size', 0)).btyes + meta_db_max_cpu = global_conf_with_default['ocp_meta_tenant'].get('max_cpu') + monitor_db_max_cpu = global_conf_with_default['ocp_monitor_tenant'].get('max_cpu', 0) + meta_db_log_disk_size = global_conf_with_default['ocp_meta_tenant'].get('log_disk_size', 0) + meta_db_log_disk_size = Capacity(meta_db_log_disk_size).btyes + monitor_db_log_disk_size = global_conf_with_default['ocp_monitor_tenant'].get('log_disk_size', 0) + monitor_db_log_disk_size = Capacity(monitor_db_log_disk_size).btyes + if meta_db_max_cpu and monitor_db_max_cpu: + if int(meta_db_max_cpu) + int(monitor_db_max_cpu) > cpu_available: + critical('tenant cpu', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='cpu', avail=cpu_available, need=int(meta_db_max_cpu) + int(monitor_db_max_cpu))) + if meta_db_memory_size and monitor_db_memory_size: + if meta_db_memory_size + monitor_db_memory_size > mem_available: + critical('tenant mem', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='memory', avail=Capacity(mem_available), need=Capacity(meta_db_memory_size + monitor_db_memory_size))) + if meta_db_log_disk_size and monitor_db_log_disk_size: + if meta_db_log_disk_size + monitor_db_log_disk_size > log_disk_available: + critical('tenant log disk', err.EC_OCP_SERVER_RESOURCE_NOT_ENOUGH.format(resource='log_disk_size', avail=Capacity(log_disk_available), need=Capacity(meta_db_log_disk_size + monitor_db_log_disk_size))) + # user check stdio.verbose('user check ') - ocp_user = server_config.get('launch_user', '') if ocp_user: client = clients[server] - if not client.execute_command(execute_cmd(server_config, "id -u %s" % ocp_user)): + if not client.execute_command(execute_cmd(ocp_user, "id -u %s" % ocp_user)): critical('launch user', err.EC_OCP_SERVER_LAUNCH_USER_NOT_EXIST.format(server=server, user=ocp_user)) if work_dir_check: @@ -384,9 +459,9 @@ def get_option(key, default=''): critical('dir', check_dirs[path], suggests) break - if client.execute_command(execute_cmd(server_config, 'bash -c "[ -a %s ]"' % path)): - is_dir = client.execute_command(execute_cmd(server_config, '[ -d {} ]'.format(path))) - has_write_permission = client.execute_command(execute_cmd(server_config, '[ -w {} ]'.format(path))) + if client.execute_command(execute_cmd(ocp_user, 'bash -c "[ -a %s ]"' % path)): + is_dir = client.execute_command(execute_cmd(ocp_user, '[ -d {} ]'.format(path))) + has_write_permission = client.execute_command(execute_cmd(ocp_user, '[ -w {} ]'.format(path))) if is_dir and has_write_permission: if empty_check: check_privilege_cmd = "ls %s" % path @@ -441,7 +516,7 @@ def get_option(key, default=''): stdio.verbose('java check ') java_bin = server_config.get('java_bin', '/usr/bin/java') client.add_env('PATH', '%s/jre/bin:' % server_config['home_path']) - ret = client.execute_command(execute_cmd(server_config, '{} -version'.format(java_bin))) + ret = client.execute_command(execute_cmd(ocp_user, '{} -version'.format(java_bin))) stdio.verbose('java version %s' % ret) if not ret: critical('java', err.EC_OCP_SERVER_JAVA_NOT_FOUND.format(server=server), [err.SUG_OCP_SERVER_INSTALL_JAVA_WITH_VERSION.format(version='1.8.0')]) @@ -477,7 +552,7 @@ def get_option(key, default=''): clockdiff_bin = 'type -P clockdiff' res = client.execute_command(clockdiff_bin).stdout client.execute_command('sudo chmod u+s %s' % res) - client.execute_command("sudo setcap 'cap_net_raw+ep' %s" % res) + client.execute_command("sudo setcap 'cap_sys_nice+ep cap_net_raw+ep' %s" % res) except Exception as e: stdio.error(e) critical('clockdiff', err.EC_OCP_SERVER_CLOCKDIFF_NOT_EXISTS.format(server=server)) @@ -551,7 +626,7 @@ def get_option(key, default=''): stdio.verbose('disk check ') for ip in servers_disk: client = servers_client[ip] - disk_info = get_disk_info(all_paths=servers_disk[ip], client=client, stdio=stdio) + disk_info = get_disk_info(all_paths=servers_disk[ip], client=client, ocp_user=ocp_user, stdio=stdio) if disk_info: for path in servers_disk[ip]: disk_needed = servers_disk[ip][path] diff --git a/profile/obd.sh b/profile/obd.sh index ccba7f7..69df1b0 100644 --- a/profile/obd.sh +++ b/profile/obd.sh @@ -72,7 +72,7 @@ function _obd_complete_func all_cmds["obd"]="mirror cluster test update repo demo web obdiag display-trace" all_cmds["obd cluster"]="autodeploy tenant component start deploy redeploy restart reload destroy stop edit-config takeover export-to-ocp list display upgrade chst check4ocp reinstall scale_out" all_cmds["obd cluster *"]="_obd_reply_deploy_names" - all_cmds["obd cluster tenant"]="create drop show create-standby switchover failover decouple" + all_cmds["obd cluster tenant"]="create drop show create-standby switchover failover decouple optimize" all_cmds["obd cluster tenant *"]="_obd_reply_deploy_names" all_cmds["obd cluster tenant create-standby *"]="_obd_reply_deploy_names" all_cmds["obd cluster tenant switchover *"]="_obd_reply_primary_standby_tenant_names $prev" @@ -84,7 +84,7 @@ function _obd_complete_func all_cmds["obd cluster tenant decouple"]="_obd_reply_deploy_names" all_cmds["obd cluster component"]="add del" all_cmds["obd cluster component *"]="_obd_reply_deploy_names" - all_cmds["obd mirror"]="clone create list update enable disable" + all_cmds["obd mirror"]="clone create clean list update enable disable" all_cmds["obd mirror clone"]="_obd_reply_current_files" all_cmds["obd repo"]="list" all_cmds["obd test"]="mysqltest sysbench tpch tpcc" @@ -93,7 +93,7 @@ function _obd_complete_func all_cmds["obd web install *"]="_obd_reply_deploy_names" all_cmds["obd web upgrade *"]="_obd_reply_deploy_names" all_cmds["obd obdiag"]="check gather deploy analyze rca update" - all_cmds["obd obdiag gather"]="all log clog slog obproxy_log perf plan_monitor stack sysstat scene" + all_cmds["obd obdiag gather"]="all log clog slog obproxy_log perf plan_monitor stack sysstat scene ash" all_cmds["obd obdiag gather scene"]="run list" all_cmds["obd obdiag gather scene run"]="_obd_reply_deploy_names" all_cmds["obd obdiag gather *"]="_obd_reply_deploy_names" diff --git a/rpm/ob-deploy.spec b/rpm/ob-deploy.spec index fe61eb8..2adcaf0 100644 --- a/rpm/ob-deploy.spec +++ b/rpm/ob-deploy.spec @@ -133,9 +133,19 @@ echo -e 'Installation of obd finished successfully\nPlease source /etc/profile.d #/sbin/chkconfig obd on %changelog +* Fri Apr 19 2024 obd 2.9.0 + - new features: support for graphical deployment of ConfigServer + - new features: supports custom production_mode, scenario configuration in graphical deployment of OceanBase + - new features: supports OCP, ConfigServer and Logproxy component changes + - new features: supports scenario-based optimization when creating a tenant + - new features: supports package cleanup + - new features: adapt to obdiag V2.1.0 + - enhancements: optimize the pre-checking tenant resources when deploying OCP and OCP-Express + - bug fixes: fixed an issue where the custom run user was not effective when deploying OCP + - bug fixes: fixed an issue where incomplete installation package downloads were not re-downloaded during a second deployment + - bug fixes: fixed an issue where validation failed during takeover in specific scenarios * Fri Apr 19 2024 obd 2.8.0 - new features: supports takeover of OceanBase_CE clusters - - new features: supports custom production_mode configuration in web - bug fixes: fixed an issue where clicking the previous step after a blank screen deployment of OCP fails does not work as expected - bug fixes: fixed an issue ith failing to upgrade certain versions of OceanBase * Thu Mar 28 2024 obd 2.7.0 diff --git a/service/common/const.py b/service/common/const.py index ea7639f..4fbbbf8 100644 --- a/service/common/const.py +++ b/service/common/const.py @@ -41,8 +41,10 @@ OCP_EXPRESS = 'ocpexpress' OCP_SERVER_CE = 'ocp-server-ce' OCP_SERVER = 'ocp-server' - OBAGENT = 'obagent' +OB_CONFIGSERVER = 'obconfigserver' + +no_generate_comps = ['ob-configserver'] DESTROY_PLUGIN = "destroy" INIT_PLUGINS = ("init",) diff --git a/service/handler/component_handler.py b/service/handler/component_handler.py index 7fc4110..dd6189d 100644 --- a/service/handler/component_handler.py +++ b/service/handler/component_handler.py @@ -183,11 +183,13 @@ def list_component_parameters(self, parameter_request, accept_language): self.obd.set_repositories([repository]) ret = self.obd.call_plugin(gen_config_plugin, repository, return_generate_keys=True, generate_consistent_config=True, spacename=spacename, clients={}) del(self.obd.namespaces[spacename]) - if not ret: - self.obd.deploy_manager.remove_deploy_config(name) - raise Exception("genconfig failed for component: {0}".format(parameter_filter.component)) - else: - auto_keys = ret.get_return("generate_keys") + auto_keys = [] + if parameter_filter.component not in const.no_generate_comps: + if not ret: + self.obd.deploy_manager.remove_deploy_config(name) + raise Exception("genconfig failed for component: {0}".format(parameter_filter.component)) + else: + auto_keys = ret.get_return("generate_keys") log.get_logger().info("auto keys for component %s are %s", parameter_filter.component, auto_keys) parameter_plugin = self.obd.plugin_manager.get_best_plugin(PluginType.PARAM, parameter_filter.component, parameter_filter.version) diff --git a/service/handler/deployment_handler.py b/service/handler/deployment_handler.py index 6128ca6..cd28a6f 100644 --- a/service/handler/deployment_handler.py +++ b/service/handler/deployment_handler.py @@ -78,6 +78,8 @@ def generate_deployment_config(self, name: str, config: DeploymentConfig): cluster_config[config.components.obagent.component] = self.generate_component_config(config, const.OBAGENT, ['monagent_http_port', 'mgragent_http_port']) if config.components.ocpexpress is not None: cluster_config[config.components.ocpexpress.component] = self.generate_component_config(config, const.OCP_EXPRESS, ['port']) + if config.components.obconfigserver is not None: + cluster_config[config.components.obconfigserver.component] = self.generate_component_config(config, const.OB_CONFIGSERVER, ['listen_port']) cluster_config_yaml_path = '' log.get_logger().info('dump config from path: %s' % cluster_config_yaml_path) with tempfile.NamedTemporaryFile(delete=False, prefix="obd", suffix="yaml", mode="w", encoding="utf-8") as f: @@ -552,9 +554,10 @@ def _do_precheck(self, repositories, start_check_plugins): if not check_pass: return + components = [comp_name for comp_name in self.obd.deploy.deploy_config.components.keys()] for repository in repositories: ret = self.obd.call_plugin(gen_config_plugins[repository], repository, generate_check=False, - generate_consistent_config=True, auto_depend=True) + generate_consistent_config=True, auto_depend=True, components=components) if ret is None: raise Exception("generate config error") elif not ret and ret.get_return("exception"): diff --git a/service/handler/metadb_handler.py b/service/handler/metadb_handler.py index 99c0d2f..a3e8f5d 100644 --- a/service/handler/metadb_handler.py +++ b/service/handler/metadb_handler.py @@ -399,9 +399,10 @@ def _do_precheck(self, repositories, start_check_plugins): if not check_pass: return + components = [comp_name for comp_name in self.obd.deploy.deploy_config.components.keys()] for repository in repositories: ret = self.obd.call_plugin(gen_config_plugins[repository], repository, generate_check=False, - generate_consistent_config=True, auto_depend=True) + generate_consistent_config=True, auto_depend=True, components=components) if ret is None: raise Exception("generate config error") elif not ret and ret.get_return("exception"): diff --git a/service/handler/ocp_handler.py b/service/handler/ocp_handler.py index 447360f..7e2a962 100644 --- a/service/handler/ocp_handler.py +++ b/service/handler/ocp_handler.py @@ -191,6 +191,9 @@ def generate_ocp_config(self, cluster_config, config, home_path, launch_user, ob if config_dict[key] and key in ('port', 'admin_password', 'memory_size', 'manage_info', 'home_path', 'soft_dir', 'log_dir', 'ocp_site_url', 'launch_user'): ocp_config['global'][key] = config_dict[key] + if launch_user: + ocp_config['global']['launch_user'] = launch_user + if config.metadb: ocp_config['global']['jdbc_url'] = 'jdbc:oceanbase://' + config_dict['metadb']['host'] + ':' + str(config_dict['metadb']['port']) + config_dict['metadb']['database'] ocp_config['global']['jdbc_username'] = config_dict['metadb']['user'] @@ -471,9 +474,10 @@ def _do_precheck(self, repositories, start_check_plugins): if not check_pass: return + components = [comp_name for comp_name in self.obd.deploy.deploy_config.components.keys()] for repository in repositories: ret = self.obd.call_plugin(gen_config_plugins[repository], repository, generate_check=False, - generate_consistent_config=True, auto_depend=True) + generate_consistent_config=True, auto_depend=True, components=components) if ret is None: raise Exception("generate config error") elif not ret and ret.get_return("exception"): @@ -497,7 +501,7 @@ def _do_precheck(self, repositories, start_check_plugins): java_check = False res = self.obd.call_plugin(start_check_plugins[repository], repository, init_check_status=False, work_dir_check=True, precheck=True, java_check=java_check, clients=ssh_clients, - sys_cursor=self.context['metadb_cursor']) + sys_cursor=self.context['metadb_cursor'], components=list(self.obd.deploy.deploy_config.components.keys())) if not res and res.get_return("exception"): raise res.get_return("exception") log.get_logger().info('end start_check: %s' % repository.name) @@ -956,9 +960,10 @@ def _do_reinstall(self, id): self.obd.set_repositories(repositories) gen_config_plugins = self.obd.search_py_script_plugin(repositories, 'generate_config') + components = [comp_name for comp_name in self.obd.deploy.deploy_config.components.keys()] for repository in repositories: ret = self.obd.call_plugin(gen_config_plugins[repository], repository, generate_check=False, - generate_consistent_config=True, auto_depend=True) + generate_consistent_config=True, auto_depend=True, components=components) if ret is None: raise Exception("generate config error") elif not ret and ret.get_return("exception"): @@ -1257,8 +1262,9 @@ def _do_upgrade_precheck(self, repositories, start_check_plugins): if len(repositories) != len(gen_config_plugins): raise Exception("param_check: config error, check stop!") + components = [comp_name for comp_name in self.obd.deploy.deploy_config.components.keys()] for repository in repositories: - ret = self.obd.call_plugin(gen_config_plugins[repository], repository, generate_check=False, generate_consistent_config=True, auto_depend=True) + ret = self.obd.call_plugin(gen_config_plugins[repository], repository, generate_check=False, generate_consistent_config=True, auto_depend=True, components=components) if ret is None: raise Exception("generate config error") elif not ret and ret.get_return("exception"): diff --git a/service/model/deployments.py b/service/model/deployments.py index ab4c310..f30589f 100644 --- a/service/model/deployments.py +++ b/service/model/deployments.py @@ -138,6 +138,14 @@ class ObClient(BaseModel): home_path: str = Body('', description='install obclient home path') servers: List[str] = Body(..., description="server ip, ex:[ '1.1.1.1','2.2.2.2']") +class ObConfigserver(BaseModel): + component: str = Body('ob-configserver', description='ob-configserver component name,ex:ob-configserver') + version: str = Body(..., description='version') + release: str = Body(..., description='ob-configserver release no') + parameters: List[Parameter] = Body(None, description='config parameter') + home_path: str = Body('', description='install ob-configserver home path') + servers: List[str] = Body(..., description="server ip, ex:[ '1.1.1.1','2.2.2.2']") + listen_port: int = Body(..., description='server port') class ComponentConfig(BaseModel): oceanbase: OceanBase @@ -145,6 +153,7 @@ class ComponentConfig(BaseModel): ocpexpress: Optional[OcpExpress] obagent: Optional[ObAgent] obclient: Optional[ObClient] + obconfigserver: Optional[ObConfigserver] class DeploymentConfig(BaseModel): diff --git a/tool.py b/tool.py index a632a74..bd96942 100644 --- a/tool.py +++ b/tool.py @@ -836,4 +836,3 @@ def close(self): if self.db: self.db.close() self.db = None - diff --git a/web/src/component/CustomPasswordInput/index.tsx b/web/src/component/CustomPasswordInput/index.tsx index 7afe95d..59e8b6b 100644 --- a/web/src/component/CustomPasswordInput/index.tsx +++ b/web/src/component/CustomPasswordInput/index.tsx @@ -155,6 +155,16 @@ export default function CustomPasswordInput({ defaultMessage: '请输入密码', }), }, + { + validator:(_,value)=>{ + let validateRes = validateInput(value) + if( validateRes.validateStatus === 'success'){ + return Promise.resolve() + }else{ + return Promise.reject(new Error(validateRes.errorMsg!)) + } + } + } ]} name={name} {...props} diff --git a/web/src/component/DeployConfig/index.tsx b/web/src/component/DeployConfig/index.tsx index 4c611d4..99c9c42 100644 --- a/web/src/component/DeployConfig/index.tsx +++ b/web/src/component/DeployConfig/index.tsx @@ -86,6 +86,7 @@ export default function DeployConfig({ setDeployMemory, tableData, setTableData, + setNeedDestroy } = useModel('ocpInstallData'); const { @@ -586,6 +587,14 @@ export default function DeployConfig({ ...ocpConfigData, components: newComponents, }; + if ( + isUpdate && + !clusterOption.some((option) => option.value === values?.appname) + ) { + setNeedDestroy(true); + }else{ + setNeedDestroy(false); + } if (isUpdate && changeClusterName(values?.appname)) { try { const { success, data } = await getConnectInfoReq({ diff --git a/web/src/component/InsstallResult/index.tsx b/web/src/component/InsstallResult/index.tsx index 07810aa..e308749 100644 --- a/web/src/component/InsstallResult/index.tsx +++ b/web/src/component/InsstallResult/index.tsx @@ -478,7 +478,7 @@ const InsstallResult: React.FC = ({ type="primary" onClick={() => { copyText( - ocpInfo ? JSON.stringify(ocpInfo) : '', + ocpInfo ? JSON.stringify(ocpInfo, null, 4) : '', ); }} > diff --git a/web/src/component/InstallResult/index.tsx b/web/src/component/InstallResult/index.tsx index 0ea466c..1cbf02d 100644 --- a/web/src/component/InstallResult/index.tsx +++ b/web/src/component/InstallResult/index.tsx @@ -489,7 +489,7 @@ const InstallResult: React.FC = ({