cura-te-ipsum/spqr/curateipsum/backup.py

94 lines
2.9 KiB
Python
Raw Normal View History

"""
Module with backup functions.
"""
import logging
import os
import pathlib
2021-06-21 05:27:18 +00:00
import shutil
2021-06-21 07:22:37 +00:00
import time
from datetime import datetime
from typing import Optional
2021-06-21 07:20:45 +00:00
import spqr.curateipsum.fs as fs
BACKUP_ENT_FMT = "%y%m%d_%H%M"
_lg = logging.getLogger(__name__)
def _is_backup_entity(entity_path: pathlib.Path) -> bool:
""" Check if entity_path is a single backup dir. """
try:
datetime.strptime(entity_path.name, BACKUP_ENT_FMT)
return True
except ValueError:
return False
def _get_latest_backup(backup_dir: pathlib.Path) -> Optional[pathlib.Path]:
""" Returns path to latest backup created in backup_dir or None. """
backups = sorted(os.listdir(backup_dir), reverse=True)
for b_ent in backups:
2021-06-21 07:25:13 +00:00
b_ent_abs = backup_dir / b_ent
if not _is_backup_entity(b_ent_abs):
continue
if not os.listdir(b_ent_abs):
_lg.info("Removing empty backup entity: %s", b_ent_abs.name)
_lg.debug("Removing directory %s", b_ent_abs)
os.rmdir(b_ent_abs)
continue
return b_ent_abs
return None
2021-06-23 17:11:42 +00:00
def initiate_backup(sources,
backup_dir: pathlib.Path,
dry_run: bool = False,
external_rsync: bool = False):
""" Main backup function """
2021-06-21 07:22:37 +00:00
start_time = time.time()
start_time_fmt = datetime.fromtimestamp(start_time).strftime(BACKUP_ENT_FMT)
cur_backup = backup_dir / start_time_fmt
_lg.debug("Current backup dir: %s", cur_backup)
latest_backup = _get_latest_backup(backup_dir)
if cur_backup == latest_backup:
2021-06-21 07:25:13 +00:00
_lg.warning("Latest backup %s was created less than minute ago, exiting",
latest_backup.name)
return
if latest_backup is None:
_lg.info("Creating empty directory for current backup: %s", cur_backup.name)
os.mkdir(cur_backup)
else:
2021-06-21 07:25:13 +00:00
_lg.info("Copying data from latest backup %s to current backup %s",
latest_backup.name, cur_backup.name)
2021-06-19 12:28:42 +00:00
2021-06-21 07:20:45 +00:00
hl_res = fs.hardlink_dir(latest_backup, cur_backup)
if not hl_res:
_lg.error("Something went wrong during copying data from latest backup,"
" removing created %s", cur_backup.name)
shutil.rmtree(cur_backup, ignore_errors=True)
return
2021-06-21 07:24:53 +00:00
2021-06-23 17:11:42 +00:00
rsync_func = fs.rsync_ext if external_rsync else fs.rsync
2021-06-21 07:24:53 +00:00
for src in sources:
src_abs = pathlib.Path(os.path.abspath(src))
dst_abs = cur_backup / src_abs.name
_lg.info("Backing up directory %s to %s backup", src_abs, cur_backup.name)
2021-06-23 17:11:42 +00:00
rsync_func(src_abs, dst_abs, dry_run=dry_run)
2021-06-21 07:24:53 +00:00
2021-06-21 05:27:18 +00:00
if dry_run:
_lg.info("Dry-run, removing created backup: %s", cur_backup.name)
shutil.rmtree(cur_backup, ignore_errors=True)
else:
_lg.info("Backup created: %s", cur_backup.name)
2021-06-21 07:22:37 +00:00
end_time = time.time()
spend_time = end_time - start_time
_lg.info("Finished, time spent: %.3fs", spend_time)