Merge, filter, and sort file entries from multiple files

This commit is contained in:
Eric Torres 2019-04-12 10:04:18 -07:00
parent ef129cb288
commit 95289cbc68

View File

@ -17,8 +17,12 @@ files are hardlinked into the new snapshot.
import argparse import argparse
import logging import logging
import os import os
import re
import sys import sys
from contextlib import contextmanager
from pathlib import Path
from subprocess import CalledProcessError from subprocess import CalledProcessError
from tempfile import NamedTemporaryFile
from rbackup.rsync import rsync from rbackup.rsync import rsync
from rbackup.struct.repository import Repository from rbackup.struct.repository import Repository
@ -46,10 +50,9 @@ EXTRA_RSYNC_OPTS = {
# ----- File Options ----- # ----- File Options -----
CONFIG_DIR = "/etc/rbackup" CONFIG_DIR = "/etc/rbackup"
FILE_OPTS = [ FILE_OPTS = [f"--exclude-from={CONFIG_DIR}/home-exclude.conf"]
f"--files-from={CONFIG_DIR}/include-paths.conf", INCLUDE_PATHS = [f"{CONFIG_DIR}/etc-include.conf", "{CONFIG_DIR}/system-include.conf"]
f"--exclude-from={CONFIG_DIR}/home-exclude.conf", COMMENT_REGEX = r"^[^#; ]+"
]
# ----- Error Codes ----- # ----- Error Codes -----
@ -116,6 +119,43 @@ def parse_cmdline_arguments(**kwargs):
return parser.parse_args(**kwargs) return parser.parse_args(**kwargs)
@contextmanager
def merge_files(*config_files):
"""Parse, filter, and sort through config files to create a single
--files-from argument.
Any files included that do not exist send a warning to the log.
>>> merge_files('/etc/rbackup/etc-include.conf', '/etc/rbackup/system-include.conf') # doctest: +ELLIPSIS
>>> '/tmp/...'
:param config_files: files including paths to read from
:type config_files: iterable of str
:returns: path to file that lists include paths
:rtype: str
"""
file_paths = [Path(p) for p in config_files]
include_lines = []
for config_file in file_paths:
if config_file.exists():
with config_file.open(mode="r") as opened_file:
include_lines.extend(
l for l in opened_file.readlines() if re.match(COMMENT_REGEX, l)
)
else:
syslog.warning(f"{config_file} does not exist, ignoring")
include_lines.sort()
with NamedTemporaryFile(mode="w", delete=False) as include_paths:
include_paths.writelines(include_lines)
tmpfile = Path(include_paths.name)
yield tmpfile
tmpfile.unlink()
# ========== Main Script ========== # ========== Main Script ==========
if __name__ == "__main__": if __name__ == "__main__":
args = parse_cmdline_arguments() args = parse_cmdline_arguments()
@ -139,8 +179,16 @@ if __name__ == "__main__":
curr_snapshot = None curr_snapshot = None
try: try:
with merge_files(INCLUDE_PATHS) as include_file:
curr_snapshot = repo.create_snapshot(args.name) curr_snapshot = repo.create_snapshot(args.name)
rsync(*rsync_opts, *FILE_OPTS, *link_dests, "/", str(curr_snapshot.path)) rsync(
*rsync_opts,
*FILE_OPTS,
f"--files-from={include_file}",
*link_dests,
"/",
str(curr_snapshot.path),
)
except ValueError as e: except ValueError as e:
syslog.critical(e) syslog.critical(e)
exit(E_INVALID_SNAPSHOT_NAME) exit(E_INVALID_SNAPSHOT_NAME)