You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
6.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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("如果启动失败请重新执行")