157 lines
5.2 KiB
Python
157 lines
5.2 KiB
Python
#### @martysama0134 backup scripts ####
|
|
### Inside /etc/crontab paste:
|
|
## for automatic backups every hour:
|
|
# 0 * * * * root cd /home/metin2/baks/ && /usr/local/bin/python3 backup_dbrev.py dump
|
|
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
import platform
|
|
|
|
def IsWindows(): return platform.system()=="Windows"
|
|
def cecho(text): print(text) if IsWindows() else print("\033[36m" + text + "\033[0m")
|
|
|
|
OUTPATH = "dbrev"
|
|
MY_LOGF = os.path.join(OUTPATH, "log.txt")
|
|
|
|
MYSQLDUMP = "/usr/local/bin/mysqldump"
|
|
if IsWindows():
|
|
MYSQLDUMP = "mysqldump"
|
|
|
|
MY_HOST = "localhost"
|
|
MY_USER = "root"
|
|
MY_PASS = "password"
|
|
PREFIX = "srv1_"
|
|
DATABASES = [
|
|
"mysql",
|
|
PREFIX+"account",
|
|
PREFIX+"common",
|
|
PREFIX+"player",
|
|
PREFIX+"log",
|
|
]
|
|
TABLES = {
|
|
PREFIX+"account" : "block_exception GameTime GameTimeIP iptocountry string",
|
|
PREFIX+"common" : "",
|
|
PREFIX+"player" : "banword item_attr item_attr_rare item_proto land mob_proto object_proto refine_proto shop shop_item skill_proto string",
|
|
}
|
|
|
|
# Determine MySQL version
|
|
def process_flags(FLAGS):
|
|
global mysql_version
|
|
mysql_version = subprocess.check_output(["mysql", "--version"]).decode("utf-8").split()[4]
|
|
if mysql_version.startswith("8."):
|
|
FLAGS.append("--set-gtid-purged=OFF")
|
|
elif mysql_version.startswith("5.7"):
|
|
FLAGS.append("--set-gtid-purged=OFF")
|
|
elif mysql_version.startswith("5.6"):
|
|
FLAGS.append("--set-gtid-purged=OFF")
|
|
elif "MariaDB" in mysql_version:
|
|
pass
|
|
else:
|
|
logging.error(f"Unknown MySQL version: {mysql_version}")
|
|
|
|
def delete_old_backups():
|
|
for entry in os.listdir(OUTPATH):
|
|
# Check if entry is a file and matches the format "*.sql"
|
|
if os.path.isfile(os.path.join(OUTPATH, entry)) and re.match(r'.*\.sql$', entry):
|
|
os.remove(os.path.join(OUTPATH, entry))
|
|
logging.info(f"Deleted old backup: {entry}")
|
|
|
|
def init_logging():
|
|
logging.basicConfig(filename=MY_LOGF, level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_handler.setLevel(logging.INFO)
|
|
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
|
console_handler.setFormatter(console_formatter)
|
|
logging.getLogger('').addHandler(console_handler)
|
|
|
|
def dump():
|
|
"""Save backups"""
|
|
os.makedirs(OUTPATH, exist_ok=True)
|
|
|
|
for db in DATABASES:
|
|
logging.info(f"Dumping {db}-schema.sql...")
|
|
FLAGS = ["--no-data","--skip-extended-insert","--skip-dump-date"]
|
|
process_flags(FLAGS)
|
|
command = f"{MYSQLDUMP} {' '.join(FLAGS)} -u {MY_USER} -p{MY_PASS} -h {MY_HOST} {db} > {OUTPATH}/{db}-schema.sql"
|
|
subprocess.run(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
for db, tables in TABLES.items():
|
|
logging.info(f"Dumping {db}-data.sql...")
|
|
FLAGS = ["--no-create-info","--skip-extended-insert","--skip-dump-date"]
|
|
process_flags(FLAGS)
|
|
command = f"{MYSQLDUMP} {' '.join(FLAGS)} -u {MY_USER} -p{MY_PASS} -h {MY_HOST} {db} {tables} > {OUTPATH}/{db}-data.sql"
|
|
subprocess.run(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
def recovery():
|
|
"""Recovery backups"""
|
|
for db in DATABASES:
|
|
logging.info(f"Recovering {db}-schema.sql...")
|
|
command = f"mysql -u {MY_USER} -p{MY_PASS} -h {MY_HOST} {db} 2>> {MY_LOGF} < {OUTPATH}/{db}-schema.sql"
|
|
subprocess.run(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
for db, tables in TABLES.items():
|
|
logging.info(f"Recovering {db}-data.sql...")
|
|
command = f"mysql -u {MY_USER} -p{MY_PASS} -h {MY_HOST} {db} 2>> {MY_LOGF} < {OUTPATH}/{db}-data.sql"
|
|
subprocess.run(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
def clean():
|
|
"""Delete all the backups"""
|
|
delete_old_backups()
|
|
|
|
def lclean():
|
|
"""Delete the log file"""
|
|
if os.path.exists(MY_LOGF):
|
|
os.remove(MY_LOGF)
|
|
|
|
def cleanall():
|
|
"""Delete all the backups and logs"""
|
|
clean()
|
|
lclean()
|
|
|
|
actions = {
|
|
"dump": dump,
|
|
"recovery": recovery,
|
|
"clean": clean,
|
|
"lclean": lclean,
|
|
"cleanall": cleanall,
|
|
}
|
|
|
|
# Execute the appropriate function based on user input
|
|
if __name__ == "__main__":
|
|
if not os.path.exists(OUTPATH):
|
|
os.makedirs(OUTPATH)
|
|
|
|
init_logging()
|
|
|
|
if len(sys.argv) < 2:
|
|
print("Available actions:")
|
|
for action in actions.keys():
|
|
print(" {}".format(action))
|
|
|
|
ret = input('Enter an action: ').split()
|
|
if not ret:
|
|
cecho('No argument provided. Quitting.')
|
|
sys.exit()
|
|
|
|
action = ret[0]
|
|
commands = ret[1:] if len(ret) >= 2 else []
|
|
else:
|
|
action = sys.argv[1]
|
|
commands = sys.argv[2:] if len(sys.argv) >= 3 else []
|
|
# print(" ".join(sys.argv[1:]))
|
|
|
|
commands = " ".join(commands)
|
|
|
|
selected_action = actions.get(action)
|
|
if selected_action:
|
|
logging.info(f"Started action {action}")
|
|
selected_action()
|
|
logging.info(f"Stopped action {action}")
|
|
else:
|
|
print("Invalid action. Please choose one of: {}".format(", ".join(actions.keys())))
|