From 25e9eb2e123997981ee4d0b9ee7b0ef0d74625d2 Mon Sep 17 00:00:00 2001 From: ghb <3779166520@qq.com> Date: Wed, 10 Jul 2024 08:36:07 +0800 Subject: [PATCH] add config.py --- config.py | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++ example.conf | 52 ++++++++++++++ monitor.py | 85 ++++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 config.py create mode 100644 example.conf create mode 100644 monitor.py diff --git a/config.py b/config.py new file mode 100644 index 0000000..80a5c42 --- /dev/null +++ b/config.py @@ -0,0 +1,197 @@ +import ipaddress +import os +import pathlib +import time + +import argparse + +config_format = """ +server_id={server_id} +binlog_format=row +default_storage_engine=InnoDB +innodb_file_per_table=1 +innodb_autoinc_lock_mode=2 + +wsrep_on=ON +wsrep_provider=/usr/lib/galera/libgalera_smm.so +wsrep_cluster_name="{cluster_name}" +wsrep_cluster_address="gcomm://{cluster_address}" +wsrep_node_name="{node_name}" +wsrep_node_address="{node_address}" +wsrep_sst_method=rsync +wsrep_sst_auth={auth} +""" + +parser = argparse.ArgumentParser(description="Galera Cluster Install Tool(集群配置工具)") +parser.add_argument("-t", "--type", choices=("master", "node"), + help="Node Type(当前设备是主节点(master)、还是普通节点(node))", + required=True) +parser.add_argument("-id", "--server_id", help="Cluster Server ID (集群内设备id,不可重复)", type=int, required=True) +parser.add_argument("-ip", "--node_address", help="Current Node Address (当前设备IP,能被其它节点访问到)", required=True) +parser.add_argument("-n", "--node_name", help="Current Node Name (集群内设备名称,不可重复)") +parser.add_argument("-c", "--cluster_name", default="YaneiGaleraCluster", + help="Galera Cluster Name(集群名称,集群内节点需配置一致)") +parser.add_argument("-f", "--mysql_file", default="/etc/mysql/mysql.conf.d/mysqld.cnf", + help="mysqld.cnf (MySQL配置文件路径,相关集群配置写入到该文件)") +parser.add_argument("-u", "--username", help="MySQL Username", default="root") +parser.add_argument("-p", "--password", help="MySQL Password", default="yanei!23") +parser.add_argument("-g", "--cluster_address", required=True, + help="Galera Cluster Nodes IP Address (集群其它设备的ip如:192.168.1.100,192.168.1.101)") + + +def clear_cache(): + cache_file = pathlib.Path("/var/lib/mysql/galera.cache") + bat_file = pathlib.Path("/var/lib/mysql/grastate.dat") + if cache_file.exists(): + cache_file.unlink() + print(f"Cache file {cache_file} has been deleted") + + if bat_file.exists(): + bat_file.unlink() + print(f"Bat file {bat_file} has been deleted") + + +def parse_validate_args(args): + node_type = args.type + server_id = args.server_id + + # cluster_address 校验 + cluster_address_str_value = args.cluster_address + if cluster_address_str_value: + if "," in cluster_address_str_value: + cluster_address_str_list = cluster_address_str_value.split(",") + elif "," in cluster_address_str_value: + cluster_address_str_list = cluster_address_str_value.split(",") + else: + cluster_address_str_list = [cluster_address_str_value] + + cluster_address_list = [] + for address_str in cluster_address_str_list: + try: + ipaddress.ip_address(address_str) + except ValueError: + raise ValueError(f"cluster_address {address_str} 参数错误, 必须ip格式") + cluster_address_list.append(address_str) + else: + cluster_address_list = [] + + # node_address 校验 + node_address = args.node_address + try: + ipaddress.ip_address(node_address) + except ValueError: + raise ValueError(f"node_address {node_address} 参数错误, 必须ip格式") + + if cluster_address_list: + if node_address in cluster_address_list: + raise ValueError("当前设备IP不能配置在集群ip中") + + # node_name + node_name = args.node_name + if not node_name: + node_name = f"node_{node_address}".replace(".", "_") + + # cluster_name + cluster_name = args.cluster_name + + # mysql_file + mysql_file = args.mysql_file + file_path = pathlib.Path(mysql_file) + if not file_path.exists(): + raise ValueError(f"{mysql_file} 配置文件未找到") + + # auth + mysql_auth = f"{args.username}:{args.password}" + + return dict( + server_id=server_id, + cluster_name=cluster_name, + cluster_address=",".join(cluster_address_list) if cluster_address_list else '', + node_name=node_name, + node_address=node_address, + auth=mysql_auth, + mysql_file=mysql_file, + ) + + +def write_file(file_path, new_content): + config_kv = set() + for line in config_format.strip().split("\n"): + if line: + config_kv.add(line.split("=")[0]) + + def in_config_kv(line): + if not line: + return False + if line.startswith("#"): + return False + if line.startswith("\n"): + return False + + for k in config_kv: + if line.startswith(k): + return True + return False + + with open(file_path, "r") as f: + content_line = f.readlines() + + c = [] + for line in content_line: + if in_config_kv(line): + continue + c.append(line) + + raw = "".join(c) + new_content + with open(file_path, "w") as f: + f.write(raw) + + +def node_config(config_data): + mysql_file = config_data.pop("mysql_file") + config_str = config_format.format(**config_data) + print(config_str) + write_file(mysql_file, config_str) + clear_cache() + print("重启数据库...") + os.system("systemctl restart mysql") + + +def master_config(config_data): + mysql_file = config_data.pop("mysql_file") + cluster_address = config_data.pop("cluster_address") + config_data["cluster_address"] = "" + config_str = config_format.format(**config_data) + + print(config_str) + write_file(mysql_file, config_str) + clear_cache() + print("关闭旧服务") + os.system("systemctl stop mysql") + print("启动主节点服务...") + os.system("/usr/bin/mysqld_bootstrap") + + input("请启动其它节点,启动后再继续。。。。") + input("确认继续?") + config_data["cluster_address"] = cluster_address + config_str = config_format.format(**config_data) + print(config_str) + write_file(mysql_file, config_str) + print("重启数据库...") + os.system("systemctl restart mysql") + + +if __name__ == '__main__': + args = parser.parse_args() + config_data = parse_validate_args(args) + if args.type == "master": + master_config(config_data) + elif args.type == "node": + node_config(config_data) + else: + raise ValueError("type error") + + time.sleep(3) + os.system("ps -aux | grep mysql") + print("如果启动失败请重新执行") + diff --git a/example.conf b/example.conf new file mode 100644 index 0000000..8aae679 --- /dev/null +++ b/example.conf @@ -0,0 +1,52 @@ +# Copyright (c) 2014, 2024, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, +# as published by the Free Software Foundation. +# +# This program is designed to work with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an additional +# permission to link the program and your derivative works with the +# separately licensed software that they have either included with +# the program or referenced in the documentation. +# +# This program 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, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# +# The MySQL Server configuration file. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +[mysqld] +pid-file = /var/run/mysqld/mysqld.pid +socket = /var/run/mysqld/mysqld.sock +datadir = /var/lib/mysql +log-error = /var/log/mysql/error.log + +bind-address = 0.0.0.0 +port = 3344 + +server_id=130 +binlog_format=row +default_storage_engine=InnoDB +innodb_file_per_table=1 +innodb_autoinc_lock_mode=2 + +wsrep_on=ON +wsrep_provider=/usr/lib/galera/libgalera_smm.so +wsrep_cluster_name="YaneiGaleraCluster" +wsrep_cluster_address="gcomm://192.168.100.129,129.168.100.128" +wsrep_node_name="node_130" +wsrep_node_address="192.168.100.130" +wsrep_sst_method=rsync +wsrep_sst_auth=root:yanei!23 diff --git a/monitor.py b/monitor.py new file mode 100644 index 0000000..0ce2e01 --- /dev/null +++ b/monitor.py @@ -0,0 +1,85 @@ +import os +import sys +import time + +import psutil + + +# 定义状态 +NORMAL = 'NORMAL' +ABNORMAL = 'ABNORMAL' + +# 定义状态持续时间阈值(秒) +NORMAL_THRESHOLD = 10 * 60 +ABNORMAL_THRESHOLD = 30 + + +def get_process(name="mysqld"): + + for proc in psutil.process_iter(): + if proc.name() == name: + return proc + return None + + +# 定义状态机类 +class ProcessMonitorStateMachine: + def __init__(self, process_name): + self.process_name = process_name + self.state = NORMAL + self.last_state_change_time = time.time() + + def check_process(self): + """检查进程是否存在,并返回其状态""" + process = get_process(name=self.process_name) + if process: + return NORMAL + else: + return ABNORMAL + + def process_event(self): + """处理事件,根据事件类型切换状态""" + current_time = time.time() + + process_state = self.check_process() + print(f"当前状态:{process_state} {current_time}") + if process_state != self.state: + self.state = process_state + self.last_state_change_time = current_time + print(f"状态变更为: {self.state}") + if self.state == ABNORMAL: + self.handle_abnormal_timeout() + + else: + if self.state == NORMAL and (current_time - self.last_state_change_time) > NORMAL_THRESHOLD: + self.handle_normal_timeout() + self.last_state_change_time = current_time + + elif self.state == ABNORMAL and (current_time - self.last_state_change_time) > ABNORMAL_THRESHOLD: + self.handle_abnormal_timeout() + self.last_state_change_time = current_time + + def handle_normal_timeout(self): + """处理正常状态超时的指令""" + print("程序已正常,退出监控") + sys.exit(0) + + def handle_abnormal_timeout(self): + """处理异常状态超时的指令""" + print("异常状态持续时间过长,执行特定指令。") + os.system("/usr/bin/mysqld_bootstrap") + + +# 使用状态机监控进程 +def main(): + process_name = "mysqld" # 指定要监控的进程名称 + monitor = ProcessMonitorStateMachine(process_name) + print("start...") + # 模拟定时检查进程状态 + while True: + monitor.process_event() + time.sleep(10) # 每10秒检查一次 + + +if __name__ == '__main__': + main() \ No newline at end of file