Add backup.
parent
ae04fab683
commit
21bd8791cf
|
@ -12,6 +12,13 @@ def main(args):
|
|||
parser = argparse.ArgumentParser("home-backup", description="Manage backup tools")
|
||||
sub_parser = parser.add_subparsers(dest="action")
|
||||
|
||||
parser_backup_add = sub_parser.add_parser("backup-add", help="Add backup.")
|
||||
parser_backup_add.add_argument("--type", nargs="?", type=str, default="borgbackup", help="The backup type.\nDefault: borgbackup")
|
||||
parser_backup_add.add_argument("--remote", nargs="?", type=str, default=None, help="The remote of the backup (required by borgbackup).")
|
||||
parser_backup_add.add_argument("name", nargs=1, type=str, help="The name of the backup.")
|
||||
parser_backup_list = sub_parser.add_parser("backup-list", help="List all backups.")
|
||||
parser_backup_delete = sub_parser.add_parser("backup-delete", help="Delete a backup.")
|
||||
|
||||
parser_remote_add = sub_parser.add_parser("remote-add", help="Add remote for backups.")
|
||||
parser_remote_add.add_argument("--type", nargs="?", type=str, default="borgbackup", help="The remote type.\nDefault: borgbackup")
|
||||
parser_remote_add.add_argument("--target", nargs="?", type=str, default=None, help="The target of the remote (required by borgbackup).")
|
||||
|
@ -43,8 +50,27 @@ def main(args):
|
|||
elif result.action == "user-server":
|
||||
asyncio.run(user_service.rpc.run_deamon(fork=result.fork)) # TODO: Change default path of user and system socket
|
||||
|
||||
# Client actions
|
||||
if result.action == "remote-add":
|
||||
# Backup
|
||||
if result.action == "backup-add":
|
||||
# Check backup type
|
||||
backup_type = result.type
|
||||
if backup_type not in ("borgbackup",):
|
||||
raise ValueError("Unknown backup type %s." & repr(backup_type))
|
||||
|
||||
# Parse type
|
||||
info = {}
|
||||
if backup_type == "borgbackup":
|
||||
if result.remote is None:
|
||||
raise ValueError("Remote is required for borg backup.")
|
||||
info["remote"] = result.remote
|
||||
else:
|
||||
raise NotImplementedError("Type %s isn't supported well." % repr(backup_type))
|
||||
|
||||
# Add backup
|
||||
client.run_command(client.backup_add_gen(name=result.name[0], btype=backup_type, info=info))
|
||||
|
||||
# Remote
|
||||
elif result.action == "remote-add":
|
||||
# Check remote type
|
||||
remote_type = result.type
|
||||
if remote_type not in ("borgbackup",):
|
||||
|
|
|
@ -19,6 +19,19 @@ def run_command(async_func, user_path:str=None):
|
|||
await async_func(sock)
|
||||
asyncio.run(runner())
|
||||
|
||||
#
|
||||
# Backups
|
||||
#
|
||||
def backup_add_gen(name:str, btype:str, info):
|
||||
async def backup_add(con:utils.Connection):
|
||||
result = await con.call({"operation": "backup-add", "name": name, "type": btype, "info": info})
|
||||
if result["status"] != "success":
|
||||
if result["status"] == "fail-already-exists":
|
||||
print("Backup %s already exists." % name)
|
||||
exit(1)
|
||||
else:
|
||||
raise RuntimeError("Wasn't able to add remote.") # TODO: Show error
|
||||
return backup_add
|
||||
|
||||
#
|
||||
# Remotes
|
||||
|
|
|
@ -15,4 +15,4 @@ async def create_snapshot(name:str, user:str):
|
|||
# List subvolumes
|
||||
subvols = btrfs.list_path_subvolumes(mount, user_path)
|
||||
print(repr(subvols))
|
||||
raise
|
||||
raise NotImplementedError()
|
|
@ -1,15 +1,18 @@
|
|||
import asyncio
|
||||
from . import remotes
|
||||
from .. import utils
|
||||
|
||||
|
||||
class Backup():
|
||||
name:str
|
||||
btype:str
|
||||
periode:int
|
||||
blocked:set
|
||||
|
||||
to_backup:list
|
||||
remote:remotes.Remote
|
||||
|
||||
def __init__(self, name:str, periode:int=None, blocked:list=[], to_backup:list=[]):
|
||||
def __init__(self, name:str, btype:str="borgbackup", periode:int=None, blocked:list=[], to_backup:list=[], remote:remotes.Remote=None):
|
||||
# Check args
|
||||
utils.valid_name_check(name)
|
||||
if periode is not None and not isinstance(periode, int):
|
||||
|
@ -25,12 +28,29 @@ class Backup():
|
|||
raise TypeError("to_backup have to be a string.")
|
||||
if ":" in i:
|
||||
raise ValueError(": isn't allowed a char.")
|
||||
if not isinstance(btype, str):
|
||||
raise TypeError("btype has to be a string.")
|
||||
if btype not in ("borgbackup",):
|
||||
raise ValueError("%s is an unknown backup tool." % repr(btype))
|
||||
if remote is not None and not isinstance(remote, remotes.Remote):
|
||||
raise TypeError("remote has to be an remote object.")
|
||||
|
||||
# Check type
|
||||
if btype == "borgbackup":
|
||||
if remote is None:
|
||||
raise ValueError("remote is required for borg backup.")
|
||||
if remote.rtype != "borgbackup":
|
||||
raise ValueError("remote has to be an borg backup target.")
|
||||
else:
|
||||
raise NotImplementedError("%s isn't an implemented type." % btype)
|
||||
|
||||
# Set values
|
||||
self.name = name
|
||||
self.periode = periode
|
||||
self.blocks = blocked
|
||||
self.blocked = blocked
|
||||
self.to_backup = to_backup
|
||||
self.btype = btype
|
||||
self.remote = remote
|
||||
|
||||
def get_next_scedule(self, latest, zero):
|
||||
if self.periode is not None:
|
||||
|
@ -41,38 +61,111 @@ class Backup():
|
|||
|
||||
def dump_config(self):
|
||||
result = {}
|
||||
result["type"] = self.btype
|
||||
if self.blocked:
|
||||
result["blocked"] = ",".join(self.blocked)
|
||||
if self.periode is not None:
|
||||
result["periode"] = str(self.periode)
|
||||
if self.to_backup:
|
||||
result["to_backup"] = ":".join(self.to_backup)
|
||||
if self.remote:
|
||||
result["remote"] = self.remote.name
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def load_backup(name:str, config):
|
||||
def load_backup(name:str, conf, remotes):
|
||||
# Load informations
|
||||
config = dict(config.items())
|
||||
conf = dict(conf.items())
|
||||
|
||||
btype = conf["type"]
|
||||
del conf["type"]
|
||||
|
||||
periode = None
|
||||
if "periode" in config:
|
||||
periode = int(config["periode"])
|
||||
del config["periode"]
|
||||
if "periode" in conf:
|
||||
periode = int(conf["periode"])
|
||||
del conf["periode"]
|
||||
|
||||
blocked = []
|
||||
if "blocked" in config:
|
||||
blocked = config["blocked"].split(",")
|
||||
del config["blocked"]
|
||||
if "blocked" in conf:
|
||||
blocked = conf["blocked"].split(",")
|
||||
del conf["blocked"]
|
||||
|
||||
to_backup = []
|
||||
if "to_backup" in config:
|
||||
to_backup = config["to_backup"].split(":")
|
||||
del config["to_backup"]
|
||||
if "to_backup" in conf:
|
||||
to_backup = conf["to_backup"].split(":")
|
||||
del conf["to_backup"]
|
||||
|
||||
remote = None
|
||||
if "remote" in conf:
|
||||
remote = conf["remote"]
|
||||
if remote not in remotes.keys():
|
||||
raise ValueError("Remote %s doesn't exists but is required." & remote)
|
||||
remote = remotes[remote]
|
||||
del conf["remote"]
|
||||
|
||||
# Generate backup
|
||||
utils.check_empty_data_dict(config)
|
||||
utils.check_empty_data_dict(conf)
|
||||
|
||||
return Backup(name=name, periode=periode, blocked=blocked, to_backup=to_backup)
|
||||
return Backup(name=name, btype=btype, periode=periode, blocked=blocked, to_backup=to_backup, remote=remote)
|
||||
|
||||
async def run_backup(self, subvolumes:list):
|
||||
print("Subvolumes: %s" % repr(subvolumes))
|
||||
print("Subvolumes: %s" % repr(subvolumes))
|
||||
|
||||
# RPC implementations
|
||||
async def add_backup(data):
|
||||
# Import config
|
||||
from . import config
|
||||
|
||||
# Load base values
|
||||
name = data["name"]
|
||||
del data["name"]
|
||||
|
||||
info = data["info"]
|
||||
if not isinstance(info, dict):
|
||||
raise TypeError("info has to be an object.")
|
||||
del data["info"]
|
||||
|
||||
btype = data["type"]
|
||||
del data["type"]
|
||||
utils.check_empty_data_dict(data)
|
||||
|
||||
# Load info
|
||||
periode = None
|
||||
if "periode" in info:
|
||||
periode = int(info["periode"])
|
||||
del info["periode"]
|
||||
|
||||
blocked = []
|
||||
if "blocked" in info:
|
||||
blocked = info["blocked"]
|
||||
del info["blocked"]
|
||||
|
||||
to_backup = []
|
||||
if "to_backup" in info:
|
||||
to_backup = info["to_backup"]
|
||||
del info["to_backup"]
|
||||
|
||||
remote = None
|
||||
if "remote" in info:
|
||||
remote = info["remote"]
|
||||
del info["remote"]
|
||||
|
||||
utils.check_empty_data_dict(info)
|
||||
|
||||
# Add backup
|
||||
async with config.config_lock:
|
||||
# Check if backup exists
|
||||
for _ in filter(lambda x: x.name == name, config.backups):
|
||||
return {"status": "fail-already-exists"}
|
||||
|
||||
# Search for remote
|
||||
if remote is not None:
|
||||
if remote not in config.remotes:
|
||||
return {"status": "fail-remote-missing"}
|
||||
remote = config.remotes[remote]
|
||||
|
||||
# Add backup
|
||||
backup = Backup(name=name, btype=btype, periode=periode, blocked=blocked, to_backup=to_backup, remote=remote)
|
||||
config.backups.append(backup)
|
||||
await config.save_config()
|
||||
return {"status": "success"}
|
|
@ -26,18 +26,19 @@ if os.path.exists(CONFIG_FILE):
|
|||
config = configparser.ConfigParser()
|
||||
config.read(CONFIG_FILE)
|
||||
|
||||
backups = []
|
||||
remotes = {}
|
||||
for iID, i in filter(lambda x: x[0] != "DEFAULT", config.items()):
|
||||
if iID.startswith("BACKUP|"): # Parse backup config
|
||||
iID = iID[len("BACKUP|"):]
|
||||
backups.append(Backup.load_backup(iID, i))
|
||||
elif iID.startswith("REMOTE|"): # Parse remote config
|
||||
if iID.startswith("REMOTE|"): # Parse remote config
|
||||
iID = iID[len("REMOTE|"):]
|
||||
tmp = Remote.load_remote(iID, i)
|
||||
remotes[tmp.name] = tmp
|
||||
else:
|
||||
raise ValueError("Unknown config part %s." % repr(iID))
|
||||
|
||||
backups = []
|
||||
for iID, i in filter(lambda x: x[0] != "DEFAULT", config.items()):
|
||||
if iID.startswith("BACKUP|"): # Parse backup config
|
||||
iID = iID[len("BACKUP|"):]
|
||||
backups.append(Backup.load_backup(iID, i, remotes))
|
||||
|
||||
return backups, remotes
|
||||
backups, remotes = _parse_config()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import asyncio
|
||||
import os
|
||||
from . import config, remotes
|
||||
from . import backups, config, remotes
|
||||
from .. import defaults, utils
|
||||
|
||||
|
||||
|
@ -36,7 +36,9 @@ def gen_callback_func(master:BackupManager):
|
|||
del data["operation"]
|
||||
|
||||
# Run operation
|
||||
if operation == "remote-add":
|
||||
if operation == "backup-add":
|
||||
return await backups.add_backup(data)
|
||||
elif operation == "remote-add":
|
||||
return await remotes.add_remote(data)
|
||||
elif operation == "remote-list":
|
||||
return await remotes.remote_list(data)
|
||||
|
|
Loading…
Reference in New Issue