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

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