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} wsrep_provider_options="pc.ignore_quorum=true" """ 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("如果启动失败请重新执行")