Skip to content

Commit ef96a63

Browse files
authored
Merge pull request #400 from yungwine/auto-backup
Auto backups
2 parents 529ffa6 + 1de94e6 commit ef96a63

File tree

5 files changed

+130
-72
lines changed

5 files changed

+130
-72
lines changed

modules/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ class Setting:
5858
'debug': Setting(None, False, 'Debug mtc console mode. Prints Traceback on errors'),
5959
'subscribe_tg_channel': Setting('validator', False, 'Disables warning about subscribing to the `TON STATUS` channel'),
6060
'BotToken': Setting('alert-bot', None, 'Alerting Telegram bot token'),
61-
'ChatId': Setting('alert-bot', None, 'Alerting Telegram chat id')
61+
'ChatId': Setting('alert-bot', None, 'Alerting Telegram chat id'),
62+
'auto_backup': Setting('validator', None, 'Make validator backup every election'),
63+
'auto_backup_path': Setting('validator', '/tmp/mytoncore/auto_backups/', 'Path to store auto-backups'),
6264
}
6365

6466

modules/backups.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import os
2+
import shutil
3+
import subprocess
4+
import time
5+
6+
import pkg_resources
7+
8+
from modules.module import MtcModule
9+
from mypylib.mypylib import color_print, ip2int, run_as_root, parse
10+
from mytoninstaller.config import get_own_ip
11+
12+
13+
class BackupModule(MtcModule):
14+
15+
def create_keyring(self, dir_name):
16+
keyring_dir = dir_name + '/keyring'
17+
self.ton.validatorConsole.Run(f'exportallprivatekeys {keyring_dir}')
18+
19+
def create_tmp_ton_dir(self):
20+
result = self.ton.validatorConsole.Run("getconfig")
21+
text = parse(result, "---------", "--------")
22+
dir_name = self.ton.tempDir + f'/ton_backup_{int(time.time() * 1000)}'
23+
dir_name_db = dir_name + '/db'
24+
os.makedirs(dir_name_db)
25+
with open(dir_name_db + '/config.json', 'w') as f:
26+
f.write(text)
27+
self.create_keyring(dir_name_db)
28+
return dir_name
29+
30+
def create_backup(self, args):
31+
if len(args) > 1:
32+
color_print("{red}Bad args. Usage:{endc} create_backup [filename]")
33+
return
34+
tmp_dir = self.create_tmp_ton_dir()
35+
command_args = ["-m", self.ton.local.buffer.my_work_dir, "-t", tmp_dir]
36+
if len(args) == 1:
37+
command_args += ["-d", args[0]]
38+
backup_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/create_backup.sh')
39+
process = subprocess.run(["bash", backup_script_path] + command_args, timeout=5)
40+
41+
if process.returncode == 0:
42+
color_print("create_backup - {green}OK{endc}")
43+
else:
44+
color_print("create_backup - {red}Error{endc}")
45+
shutil.rmtree(tmp_dir)
46+
return process.returncode
47+
# end define
48+
49+
def restore_backup(self, args):
50+
if len(args) == 0 or len(args) > 2:
51+
color_print("{red}Bad args. Usage:{endc} restore_backup <filename> [-y]")
52+
return
53+
if '-y' not in args:
54+
res = input(
55+
f'This action will overwrite existing configuration with contents of backup archive, please make sure that donor node is not in operation prior to this action. Proceed [y/n]')
56+
if res.lower() != 'y':
57+
print('aborted.')
58+
return
59+
else:
60+
args.pop(args.index('-y'))
61+
print('Before proceeding, mtc will create a backup of current configuration.')
62+
self.create_backup([])
63+
ip = str(ip2int(get_own_ip()))
64+
command_args = ["-m", self.ton.local.buffer.my_work_dir, "-n", args[0], "-i", ip]
65+
66+
restore_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/restore_backup.sh')
67+
if run_as_root(["bash", restore_script_path] + command_args) == 0:
68+
color_print("restore_backup - {green}OK{endc}")
69+
self.local.exit()
70+
else:
71+
color_print("restore_backup - {red}Error{endc}")
72+
# end define
73+
74+
def add_console_commands(self, console):
75+
console.AddItem("create_backup", self.create_backup, self.local.translate("create_backup_cmd"))
76+
console.AddItem("restore_backup", self.restore_backup, self.local.translate("restore_backup_cmd"))

mytoncore/mytoncore.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,21 +1455,47 @@ def ElectionEntry(self, args=None):
14551455
self.local.add_log("ElectionEntry completed. Start work time: " + str(startWorkTime))
14561456

14571457
self.clear_tmp()
1458+
self.make_backup(startWorkTime)
14581459

14591460
#end define
14601461

1461-
def clear_tmp(self):
1462+
def clear_dir(self, dir_name):
14621463
start = time.time()
14631464
count = 0
14641465
week_ago = 60 * 60 * 24 * 7
1465-
dir = self.tempDir
1466-
for f in os.listdir(dir):
1467-
ts = os.path.getmtime(os.path.join(dir, f))
1466+
for f in os.listdir(dir_name):
1467+
ts = os.path.getmtime(os.path.join(dir_name, f))
14681468
if ts < time.time() - week_ago:
14691469
count += 1
1470-
os.remove(os.path.join(dir, f))
1470+
if os.path.isfile(os.path.join(dir_name, f)):
1471+
os.remove(os.path.join(dir_name, f))
1472+
self.local.add_log(f"Removed {count} old files from {dir_name} directory for {int(time.time() - start)} seconds", "info")
1473+
1474+
def clear_tmp(self):
1475+
self.clear_dir(self.tempDir)
14711476

1472-
self.local.add_log(f"Removed {count} old files from tmp dir for {int(time.time() - start)} seconds", "info")
1477+
def make_backup(self, election_id: str):
1478+
if not self.local.db.get("auto_backup"):
1479+
return
1480+
from modules.backups import BackupModule
1481+
module = BackupModule(self, self.local)
1482+
args = []
1483+
name = f"/mytonctrl_backup_elid{election_id}.zip"
1484+
backups_dir = self.tempDir + "/auto_backups"
1485+
if self.local.db.get("auto_backup_path"):
1486+
backups_dir = self.local.db.get("auto_backup_path")
1487+
os.makedirs(backups_dir, exist_ok=True)
1488+
args.append(backups_dir + name)
1489+
self.clear_dir(backups_dir)
1490+
exit_code = module.create_backup(args)
1491+
if exit_code != 0:
1492+
self.local.add_log(f"Backup failed with exit code {exit_code}", "error")
1493+
# try one more time
1494+
exit_code = module.create_backup(args)
1495+
if exit_code != 0:
1496+
self.local.add_log(f"Backup failed with exit code {exit_code}", "error")
1497+
if exit_code == 0:
1498+
self.local.add_log(f"Backup created successfully", "info")
14731499

14741500
def GetValidatorKeyByTime(self, startWorkTime, endWorkTime):
14751501
self.local.add_log("start GetValidatorKeyByTime function", "debug")

mytonctrl/mytonctrl.py

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,17 @@ def inject_globals(func):
8585
console.AddItem("get", inject_globals(GetSettings), local.translate("get_cmd"))
8686
console.AddItem("set", inject_globals(SetSettings), local.translate("set_cmd"))
8787
console.AddItem("rollback", inject_globals(rollback_to_mtc1), local.translate("rollback_cmd"))
88-
console.AddItem("create_backup", inject_globals(create_backup), local.translate("create_backup_cmd"))
89-
console.AddItem("restore_backup", inject_globals(restore_backup), local.translate("restore_backup_cmd"))
9088

9189
#console.AddItem("xrestart", inject_globals(Xrestart), local.translate("xrestart_cmd"))
9290
#console.AddItem("xlist", inject_globals(Xlist), local.translate("xlist_cmd"))
9391
#console.AddItem("gpk", inject_globals(GetPubKey), local.translate("gpk_cmd"))
9492
#console.AddItem("ssoc", inject_globals(SignShardOverlayCert), local.translate("ssoc_cmd"))
9593
#console.AddItem("isoc", inject_globals(ImportShardOverlayCert), local.translate("isoc_cmd"))
9694

95+
from modules.backups import BackupModule
96+
module = BackupModule(ton, local)
97+
module.add_console_commands(console)
98+
9799
from modules.custom_overlays import CustomOverlayModule
98100
module = CustomOverlayModule(ton, local)
99101
module.add_console_commands(console)
@@ -932,53 +934,6 @@ def disable_mode(local, ton, args):
932934
#end define
933935

934936

935-
def create_backup(local, ton, args):
936-
if len(args) > 2:
937-
color_print("{red}Bad args. Usage:{endc} create_backup [path_to_archive] [-y]")
938-
return
939-
if '-y' not in args:
940-
res = input(f'Mytoncore service will be stopped for few seconds while backup is created, Proceed [y/n]?')
941-
if res.lower() != 'y':
942-
print('aborted.')
943-
return
944-
else:
945-
args.pop(args.index('-y'))
946-
command_args = ["-m", ton.local.buffer.my_work_dir]
947-
if len(args) == 1:
948-
command_args += ["-d", args[0]]
949-
backup_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/create_backup.sh')
950-
if run_as_root(["bash", backup_script_path] + command_args) == 0:
951-
color_print("create_backup - {green}OK{endc}")
952-
else:
953-
color_print("create_backup - {red}Error{endc}")
954-
#end define
955-
956-
957-
def restore_backup(local, ton, args):
958-
if len(args) == 0 or len(args) > 2:
959-
color_print("{red}Bad args. Usage:{endc} restore_backup <path_to_archive> [-y]")
960-
return
961-
if '-y' not in args:
962-
res = input(f'This action will overwrite existing configuration with contents of backup archive, please make sure that donor node is not in operation prior to this action. Proceed [y/n]')
963-
if res.lower() != 'y':
964-
print('aborted.')
965-
return
966-
else:
967-
args.pop(args.index('-y'))
968-
print('Before proceeding, mtc will create a backup of current configuration.')
969-
create_backup(local, ton, ['-y'])
970-
ip = str(ip2int(get_own_ip()))
971-
command_args = ["-m", ton.local.buffer.my_work_dir, "-n", args[0], "-i", ip]
972-
973-
restore_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/restore_backup.sh')
974-
if run_as_root(["bash", restore_script_path] + command_args) == 0:
975-
color_print("restore_backup - {green}OK{endc}")
976-
local.exit()
977-
else:
978-
color_print("restore_backup - {red}Error{endc}")
979-
#end define
980-
981-
982937
def Xrestart(inputArgs):
983938
if len(inputArgs) < 2:
984939
color_print("{red}Bad args. Usage:{endc} xrestart <timestamp> <args>")

mytonctrl/scripts/create_backup.sh

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
dest="mytonctrl_backup_$(hostname)_$(date +%s).tar.gz"
22
mtc_dir="$HOME/.local/share/mytoncore"
33
user=$(logname)
4+
ton_dir="/var/ton-work"
5+
keys_dir="/var/ton-work/keys"
46
# Get arguments
5-
while getopts d:m: flag
7+
while getopts d:m:t:k: flag
68
do
79
case "${flag}" in
810
d) dest=${OPTARG};;
911
m) mtc_dir=${OPTARG};;
12+
t) ton_dir=${OPTARG};;
13+
k) keys_dir=${OPTARG};;
1014
*)
1115
echo "Flag -${flag} is not recognized. Aborting"
1216
exit 1 ;;
@@ -16,32 +20,27 @@ done
1620
COLOR='\033[92m'
1721
ENDC='\033[0m'
1822

19-
systemctl stop mytoncore
20-
21-
echo -e "${COLOR}[1/4]${ENDC} Stopped mytoncore service"
22-
23-
24-
tmp_dir="/tmp/mytoncore/backup"
23+
tmp_dir="/tmp/mytoncore/backupv2"
2524
rm -rf $tmp_dir
2625
mkdir $tmp_dir
2726
mkdir $tmp_dir/db
2827

29-
cp /var/ton-work/db/config.json ${tmp_dir}/db
30-
cp -r /var/ton-work/db/keyring ${tmp_dir}/db
31-
cp -r /var/ton-work/keys ${tmp_dir}
28+
cp $ton_dir/db/config.json ${tmp_dir}/db
29+
cp -r $ton_dir/db/keyring ${tmp_dir}/db
30+
cp -r $keys_dir ${tmp_dir}
3231
cp -r $mtc_dir $tmp_dir
3332

3433
python3 -c "import json;f=open('${tmp_dir}/db/config.json');json.load(f);f.close()" || exit 1 # Check if config.json is copied correctly
34+
python3 -c "import json;f=open('${tmp_dir}/mytoncore/mytoncore.db');json.load(f);f.close()" || exit 2 # Check if mytoncore.db is copied correctly
3535

36-
echo -e "${COLOR}[2/4]${ENDC} Copied files to ${tmp_dir}"
37-
38-
systemctl start mytoncore
39-
40-
echo -e "${COLOR}[3/4]${ENDC} Started mytoncore service"
36+
echo -e "${COLOR}[1/2]${ENDC} Copied files to ${tmp_dir}"
4137

4238
tar -zcf $dest -C $tmp_dir .
4339

4440
chown $user:$user $dest
4541

46-
echo -e "${COLOR}[4/4]${ENDC} Backup successfully created in ${dest}!"
42+
echo -e "${COLOR}[2/2]${ENDC} Backup successfully created in ${dest}!"
43+
44+
rm -rf $tmp_dir
45+
4746
echo -e "If you wish to use archive package to migrate node to different machine please make sure to stop validator and mytoncore on donor (this) host prior to migration."

0 commit comments

Comments
 (0)