121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
import aiofile
|
|
import asyncio
|
|
import configparser
|
|
import io
|
|
import os
|
|
import time
|
|
from .backups import Backup
|
|
from .remotes import Remote
|
|
|
|
|
|
# Config
|
|
HOME_DIR = os.path.abspath(os.environ.get("HOME"))
|
|
if os.environ.get("XDG_CONFIG_HOME", None) is not None:
|
|
CONFIG_FILE = os.environ.get("XDG_CONFIG_HOME")
|
|
else:
|
|
CONFIG_FILE = os.path.join(HOME_DIR, ".config")
|
|
CONFIG_FILE = os.path.join(CONFIG_FILE, "home-backup.conf") # TODO: Set own config dir
|
|
|
|
|
|
backups = []
|
|
remotes = {}
|
|
config_lock = asyncio.Lock()
|
|
if os.path.exists(CONFIG_FILE):
|
|
def _parse_config():
|
|
# Load config
|
|
config = configparser.ConfigParser()
|
|
config.read(CONFIG_FILE)
|
|
|
|
remotes = {}
|
|
for iID, i in filter(lambda x: x[0] != "DEFAULT", config.items()):
|
|
if iID.startswith("REMOTE|"): # Parse remote config
|
|
iID = iID[len("REMOTE|"):]
|
|
tmp = Remote.load_remote(iID, i)
|
|
remotes[tmp.name] = tmp
|
|
|
|
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()
|
|
|
|
|
|
async def save_config():
|
|
# Create config
|
|
config = configparser.ConfigParser()
|
|
|
|
# Add backups
|
|
for i in backups:
|
|
if not isinstance(i, Backup):
|
|
raise ValueError("backups contains a non backup config entry.")
|
|
config["BACKUP|%s" % i.name] = i.dump_config()
|
|
|
|
# Add remotes
|
|
for i in remotes.values():
|
|
if not isinstance(i, Remote):
|
|
raise ValueError("remotes contains a non remote config entry.")
|
|
config["REMOTE|%s" % i.name] = i.dump_config()
|
|
|
|
# Write data
|
|
tmp = io.StringIO()
|
|
config.write(tmp)
|
|
to_write = tmp.getvalue()
|
|
async with aiofile.AIOFile(CONFIG_FILE, "w") as f:
|
|
await f.write(to_write)
|
|
await f.fsync()
|
|
|
|
|
|
# Timer support
|
|
class Timer():
|
|
__zero:int
|
|
__latest:int
|
|
|
|
def __init__(self):
|
|
self.__zero = self.__latest = int(time.time())
|
|
|
|
async def run(self, async_callback):
|
|
tasks = []
|
|
try:
|
|
while True:
|
|
# Delete finished futures
|
|
to_delete = []
|
|
for i in tasks:
|
|
if i.done() or i.cancelled():
|
|
to_delete.append(i)
|
|
for i in to_delete:
|
|
await i
|
|
tasks.remove(i)
|
|
|
|
# Check for new backup
|
|
next_time = int(time.time())
|
|
candiates = {}
|
|
candidate_name = set()
|
|
async with config_lock:
|
|
for i in backups:
|
|
if self.__latest < i.get_next_scedule(self.__latest, self.__zero) <= next_time:
|
|
candiates[i.name] = i
|
|
candidate_name.add(i.name)
|
|
self.__latest = next_time
|
|
|
|
# Filter backups
|
|
to_ignore = set()
|
|
for iID, i in candiates.items():
|
|
if candidate_name.intersection(i.blocks):
|
|
to_ignore.add(iID)
|
|
|
|
# Generate backup task
|
|
backups_to_run = []
|
|
for iID, i in filter(lambda x: x[0] not in to_ignore, candiates.items()):
|
|
backups_to_run.append(i)
|
|
tasks.append(asyncio.create_task(async_callback(backups_to_run)))
|
|
|
|
# Wait for 1 min
|
|
await asyncio.sleep(60)
|
|
|
|
# Stop backup processes
|
|
finally:
|
|
for i in tasks:
|
|
i.cancel() |