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