Added the backup procedure and added some more config checks.
This commit is contained in:
parent
fc09d9b176
commit
6787229160
89
pybackup.py
89
pybackup.py
@ -1,10 +1,14 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import json
|
import json
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
# CONFIGURATION_PATH = "/etc/pybackup.conf" # for "production"
|
# CONFIGURATION_PATH = "/etc/pybackup.conf" # for "production"
|
||||||
CONFIGURATION_PATH = "pybackup.conf" # for testing
|
CONFIGURATION_PATH = "pybackup.conf" # for testing
|
||||||
|
BACKUP_NAME_PATTERN = "%m%d%Y_%H-%M-%S.pybackup.tar" # DON'T CHANGE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING!
|
||||||
|
|
||||||
|
|
||||||
def log(message: str, log_level: int) -> bool:
|
def log(message: str, log_level: int) -> bool:
|
||||||
@ -19,12 +23,18 @@ def log(message: str, log_level: int) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def make_backup(path_from: str, path_to: str):
|
||||||
|
backup_name = datetime.now().strftime(BACKUP_NAME_PATTERN)
|
||||||
|
tar_backup = subprocess.run(["tar", "-cf", f"{os.path.abspath(path_to)}/{backup_name}", path_from])
|
||||||
|
if tar_backup.returncode != 0:
|
||||||
|
log(f"Something went wrong handling the backup of {path_from}", 3)
|
||||||
|
|
||||||
|
|
||||||
# just to make sure this program isn't used as a module
|
# just to make sure this program isn't used as a module
|
||||||
if not __name__ == "__main__":
|
if not __name__ == "__main__":
|
||||||
print(f"[{sys.argv[0]}]: THIS BACKUP PROGRAM IS NOT A PYTHON MODULE!")
|
print(f"[{sys.argv[0]}]: THIS BACKUP PROGRAM IS NOT A PYTHON MODULE!")
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
conf_f = open(CONFIGURATION_PATH, "r")
|
conf_f = open(CONFIGURATION_PATH, "r")
|
||||||
try: # load the config if possible
|
try: # load the config if possible
|
||||||
conf = json.load(conf_f)
|
conf = json.load(conf_f)
|
||||||
@ -46,6 +56,7 @@ for _location in conf["locations"]:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
log(f"The {counter + 1}th element of locations in the config has no name. Skipping this backup path...", 3)
|
log(f"The {counter + 1}th element of locations in the config has no name. Skipping this backup path...", 3)
|
||||||
conf["locations"].pop(counter)
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_location["path"]
|
_location["path"]
|
||||||
@ -53,7 +64,81 @@ for _location in conf["locations"]:
|
|||||||
_location["frequency"]
|
_location["frequency"]
|
||||||
_location["versions"]
|
_location["versions"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log(f"The {counter + 1}th element with the name \"{_location['name']}\" of locations in the config is corrupted. Skipping this backup path...", 3)
|
log(f"The {counter + 1}th element with the name \"{_location['name']}\" of locations in the config is "
|
||||||
|
f"corrupted. Skipping this backup path...", 3)
|
||||||
conf["locations"].pop(counter)
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if type(_location["versions"]) != int:
|
||||||
|
log(f"The value of \"versions\" must be an integer, which isn't given in {_location['name']}. Skipping it.", 3)
|
||||||
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if type(_location["frequency"]) != int:
|
||||||
|
log(f"The value of \"frequency\" must be an integer, which isn't given in {_location['name']}. Skipping it.", 3)
|
||||||
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not os.path.exists(_location["path"]) or not os.path.exists(_location["backup_path"]):
|
||||||
|
log(f"The {counter + 1}th element with the name \"{_location['name']}\" of locations in the config has a "
|
||||||
|
f"wrong path and/or backup path set. Maybe you spelled it wrong? Skipping this backup...", 3)
|
||||||
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not os.path.isdir(_location["backup_path"]):
|
||||||
|
log(f"The backup configuration \"{_location['backup_path']}\" has no folder set as a backup path. "
|
||||||
|
f"Skipping it...", 3)
|
||||||
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not os.access(_location["backup_path"], os.R_OK | os.X_OK | os.W_OK) or not os.access(_location["path"], os.R_OK):
|
||||||
|
log(f"Backup configuration \"{_location['name']}\": no access to the path and/or backup path. "
|
||||||
|
f"Skipping it...", 3)
|
||||||
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if _location["frequency"] == 0:
|
||||||
|
log(f"Frequency can't be zero (0), but it seems {_location['name']} has this frequency! Skipping it...", 3)
|
||||||
|
conf["locations"].pop(counter)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for entry in os.listdir(_location["backup_path"]):
|
||||||
|
try:
|
||||||
|
datetime.strptime(entry, BACKUP_NAME_PATTERN)
|
||||||
|
except ValueError:
|
||||||
|
log(f"The backup folder {_location['backup_path']} contains files different to the backup files which is"
|
||||||
|
f" not supported. Skipping this backup...", 3)
|
||||||
|
conf["locations"].pop(counter)
|
||||||
|
break
|
||||||
|
|
||||||
counter += 1
|
counter += 1
|
||||||
|
|
||||||
|
|
||||||
|
# the REAL backup procedure now!
|
||||||
|
for location in conf["locations"]:
|
||||||
|
path = location["path"]
|
||||||
|
backup_path = location["backup_path"]
|
||||||
|
frequency = location["frequency"]
|
||||||
|
versions = location["versions"]
|
||||||
|
|
||||||
|
# backup if there's no backup yet
|
||||||
|
if (backup_folder := os.listdir(backup_path)) == []:
|
||||||
|
make_backup(path, backup_path)
|
||||||
|
else:
|
||||||
|
# backup if the last backup is 'frequency' days old
|
||||||
|
backup = True
|
||||||
|
now = datetime.now()
|
||||||
|
for entry in backup_folder:
|
||||||
|
timedelta = now - datetime.strptime(entry, BACKUP_NAME_PATTERN)
|
||||||
|
if timedelta.total_seconds() <= 60*60*24*frequency: # if the backup was made before less than frequency
|
||||||
|
# days
|
||||||
|
backup = False
|
||||||
|
break
|
||||||
|
if backup:
|
||||||
|
make_backup(path, backup_path)
|
||||||
|
|
||||||
|
# last, if there are more than version backups in the folder, remove the oldest
|
||||||
|
if (backup_folder_len := len(os.listdir(backup_path))) > versions:
|
||||||
|
files_to_be_deleted = sorted(backup_folder)[0:backup_folder_len-versions]
|
||||||
|
for file in files_to_be_deleted:
|
||||||
|
os.remove(f"{backup_path}/{file}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user