Early prototype of backup script

This commit is contained in:
Eric Torres 2019-03-17 18:27:20 -07:00
parent ed9b88a1ef
commit 59e31016a8

View File

@ -4,22 +4,33 @@ Run a backup.
Command-Line Arguments Command-Line Arguments
====================== ======================
* -c, --use-checksums use rsync's checksum feature to detect file changes
* -d, --dry-run make this backup a dry run
* -s, --run-post-sync run sync syscall after backup
* -v, --verbose increase script verbosity
On each run of this script, a new snapshot is made and any unchanged
files are hardlinked into this new snapshot.
""" """
import argparse import argparse
import logging import logging
import os.path import os
import rbackup.rsync as rsync import sys
from rbackup.hierarchy import Repository, Snapshot from rbackup.hierarchy.repository import Repository
from rbackup.rsync import rsync
# ========== Constants ========== # ========== Constants ==========
LOGFORMAT = "==> %(levelname)s %(message)s"
RSYNC_DEFAULT_OPTS = [ RSYNC_DEFAULT_OPTS = [
"--acls",
"--archive", "--archive",
"--hard-links",
"--prune-dirs",
"--backup", "--backup",
"--xattrs", "--hard-links",
"--ignore-missing-args", "--ignore-missing-args",
"--prune-dirs",
"--suffix=.old",
"--xattrs",
] ]
EXTRA_RSYNC_OPTS = { EXTRA_RSYNC_OPTS = {
"dry_run": "--dry-run", "dry_run": "--dry-run",
@ -28,9 +39,11 @@ EXTRA_RSYNC_OPTS = {
"update": "--update", "update": "--update",
} }
ETC_INCLUDE_FILE = "/etc/rbackup/etc-include.conf"
# ========== Logging Setup ========== # ========== Logging Setup ==========
console_formatter = logging.Formatter(LOGFORMAT) console_formatter = logging.Formatter(LOGFORMAT)
syslog = logging.getLogger("packaging_scripts") syslog = logging.getLogger("rbackup")
syslog.setLevel(logging.DEBUG) syslog.setLevel(logging.DEBUG)
stdout_handler = logging.StreamHandler(sys.stdout) stdout_handler = logging.StreamHandler(sys.stdout)
@ -45,6 +58,48 @@ stderr_handler.setFormatter(console_formatter)
syslog.addHandler(stdout_handler) syslog.addHandler(stdout_handler)
syslog.addHandler(stderr_handler) syslog.addHandler(stderr_handler)
# ========== Functions ==========
def backup_all(s, rsync_opts):
raise NotImplementedError
def backup_etc(s, rsync_opts):
"""Create a backup of /etc
:param s: snapshot to back up to
:type s: Snapshot object
:param opts: options to pass to rsync
:type opts: list
"""
local_opts = rsync_opts
local_opts.append(f"--files-from={ETC_INCLUDE_FILE}")
syslog.debug("Creating directory {s.etc_path}")
s.etc_dir.mkdir(parents=True, exist_ok=False)
rsync(*local_opts, "/etc/", str(s.etc_path))
def backup_home():
raise NotImplementedError
def backup_pkgmanager():
raise NotImplementedError
def backup_system():
raise NotImplementedError
DISPATCHER = {
"all": backup_all,
"etc": backup_etc,
"home": backup_home,
"system": backup_system,
}
# ========== Main Script ========== # ========== Main Script ==========
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@ -54,7 +109,7 @@ if __name__ == "__main__":
action="store_const", action="store_const",
dest="extra_rsync_opts", dest="extra_rsync_opts",
const=EXTRA_RSYNC_OPTS["checksum"], const=EXTRA_RSYNC_OPTS["checksum"],
help="use rsync's checksumming feature to look for changed files" help="use rsync's checksumming feature to look for changed files",
) )
parser.add_argument( parser.add_argument(
"-d", "-d",
@ -65,25 +120,60 @@ if __name__ == "__main__":
help="pass --dry-run to rsync", help="pass --dry-run to rsync",
) )
parser.add_argument( parser.add_argument(
"-v", "--verbose", action="store_true", help="increase script verbosity" "-s",
"--run-post-sync",
action="store_true",
help="run sync operation after backup is complete",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="increase script verbosity",
)
parser.add_argument(
"operation",
choices=["all", "etc", "home", "pkg", "system"],
help="the operation to perform",
metavar="op",
)
parser.add_argument(
"repository", help="repository to back up to", metavar="repo"
) )
parser.add_argument("operation", help="the operation to perform", metavar="op")
parser.add_argument("repository", help="repository to back up to", metavar="repo")
args = parser.parser_args() args = parser.parse_args()
rsync_opts = []
if args.extra_rsync_opts is not None:
rsync_opts.extend(args.extra_rsync_opts)
args = parser.parse_args()
repo = Repository(args.repository) repo = Repository(args.repository)
link_dest = hier.prev_snapshot
if args.verbose: if args.verbose:
stdout_handler.setLevel(logging.DEBUG) stdout_handler.setLevel(logging.DEBUG)
# Handle empty repository if repo.empty:
prev_snapshot = None
else:
prev_snapshot = repo.curr_snapshot
# else handle regular repository repo.create_snapshot()
curr_snapshot = repo.curr_snapshot
# Backup to hier.curr_snapshot rsync_opts.append(f"--link-dest={prev_snapshot}")
rsync_opts.append("--backup-dir=backup")
# Relink 'prev' to point to latest snapshot DISPATCHER[args.operation](curr_snapshot, rsync_opts)
os.remove(hier.prev_snapshot_link)
os.symlink(curr_snapshot, hier.prev_snapshot_link, target_is_directory=True) snapshot_symlink = repo.path / "current"
try:
snapshot_symlink.unlink()
except FileNotFoundError:
pass
snapshot_symlink.symlink_to(curr_snapshot, target_is_directory=True)
if args.run_post_sync:
os.sync()