From bbe0d5015e6e07edc52d3735515cfa0602d18194 Mon Sep 17 00:00:00 2001 From: Stefan Huber Date: Mon, 14 May 2012 22:41:37 +0200 Subject: [PATCH] adding logging system, added --version --- shbackup | 120 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/shbackup b/shbackup index 239dfa3..da28afe 100755 --- a/shbackup +++ b/shbackup @@ -1,12 +1,16 @@ #!/usr/bin/python3 """Stefan Huber's simplistic backup solution.""" +__version__ = "0.1" +__author__ = "Stefan Huber" + import datetime import os, shutil, sys import configparser import hashlib import subprocess import random, re +import logging Mode = ["full", "incr", "diff"] @@ -195,9 +199,8 @@ class Config: class BackupManager: """List and create backups""" - def __init__(self, conffn, alwaysyes): + def __init__(self, conffn): self.conf = Config() - self.alwaysyes = alwaysyes self.conf.read(conffn) @@ -249,10 +252,12 @@ class BackupManager: - def backupFileSet(self, fileset, targetdir, log, since=None): + def backupFileSet(self, fileset, targetdir, since=None): """Create an archive for given fileset at given target directory.""" - print("Running file set: " + fileset.name) + logger = logging.getLogger('backup') + + logger.info("Running file set: " + fileset.name) tarpath = "/bin/tar" fsfn = os.path.join(targetdir, fileset.name) + "." + self.conf.format @@ -265,16 +270,26 @@ class BackupManager: taropts += ["--exclude", pat] tarargs = [tarpath] + taropts + ["-f", fsfn] + fileset.dirs - #print("tarargs: ", tarargs) - print("tar call: " + " ".join(tarargs), file=log) - tarp = subprocess.Popen( tarargs, stderr=subprocess.PIPE ) + logger.debug("tar call: " + " ".join(tarargs)) + tarp = subprocess.Popen( tarargs, bufsize=-1, \ + stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + + # Output stdout of tar + while tarp.poll() == None: + l = tarp.stdout.readline() + if l != "": + logging.debug(l.decode().rstrip()) + + # Output remaining output of tar + for l in tarp.stdout.readlines(): + logging.debug(l.decode().rstrip()) rett = tarp.wait() if rett != 0: - sys.stderr.write( tarp.stderr.read() ) - msg = tarpath + " returned with exit status " + str(rett) + "." - print(msg) - print(msg, log) + for l in tarp.stderr.readlines(): + logger.error( l.decode().strip().rstrip() ) + sys.stderr.write( tarp.stderr.read().decode() ) + logger.error(tarpath + " returned with exit status " + str(rett) + ".") def backup(self, epoch=None, mode=None): @@ -289,25 +304,23 @@ class BackupManager: if epoch == None: epoch = self.getDesiredEpoch(oldbackups, now) if epoch == None: - print("No backup planned.") + logging.info("No backup planned.") return # Get mode of backup if mode == None: mode = self.conf.epochmodes[epoch] - print("Making a backup. Epoch: " + epoch + ", mode: " + mode) + logging.info("Making a backup. Epoch: " + epoch + ", mode: " + mode) oldfullbackups = [ b for b in oldbackups if b.mode == "full" ] # No old full backups existing if mode != "full" and len(oldfullbackups)==0: - print("No full backups existing. Making a full backup.") + logging.info("No full backups existing. Making a full backup.") # Checksum changed -> self.config file changed - if self.conf.checksum != self.conf.lastchecksum: - print("Config file changed since last time.") - if mode != "full": - print("** Warning: full backup recommended!") + if self.conf.checksum != self.conf.lastchecksum and mode != "full": + logging.warning("Full backup recommended as config file has changed.") # If we have a full backup, we backup everything @@ -318,7 +331,7 @@ class BackupManager: since = sorted(oldbackups, key=lambda b: b.date)[-1].date if since != None: - print("Making backup relative to ", since.ctime()) + logging.debug("Making backup relative to " + since.ctime()) yesno = self.ask_user_yesno("Proceed? [Y, n] ") if yesno == "n": @@ -332,15 +345,17 @@ class BackupManager: os.mkdir( targetdir ) - log = open(os.path.join(targetdir, "log.log"), 'w') - print("Started: " + now.ctime(), file=log) + logger = logging.getLogger('backup') + ch = logging.FileHandler( os.path.join(targetdir, "log") ) + ch.setLevel(logging.INFO) + logger.addHandler(ch) + logger.info("Started: " + now.ctime()) # Backup all file sets for s in self.conf.sets: - self.backupFileSet(s, targetdir, log, since) + self.backupFileSet(s, targetdir, since) - print("Stopped: " + datetime.datetime.now().ctime(), file=log) - log.close() + logger.info("Stopped: " + datetime.datetime.now().ctime()) # Rename backup directory to final name os.rename( targetdir, os.path.join(basedir, dirname) ) @@ -356,7 +371,7 @@ class BackupManager: def prune(self): """Prune old backup files""" - allDirs = self.listAllDirs() + allDirs = sorted(self.listAllDirs()) # Collect all directories not matching backup name removeDirs = [ d for d in allDirs if not Backup.isBackupDir(d) ] @@ -371,24 +386,27 @@ class BackupManager: removeDirs += [ Backup.getDirName(b.date, b.epoch, b.mode) for b in old] - print("List of stale/outdated entries:") + logging.info("List of stale/outdated entries:") for d in allDirs: + msg = "" if d in removeDirs: - print("[*] ", end="") + msg = "[*] " else: - print("[ ] ", end="") + msg = "[ ] " if Backup.isBackupDir(d): - print( Backup.fromDirName(d).colAlignedString()) + msg += Backup.fromDirName(d).colAlignedString() else: - print(d) + msg += d + + logging.info(msg) # Check that dirs to be removed is in list of all dirs for d in removeDirs: assert( d in allDirs ) if len(removeDirs) == 0: - print("No stale/outdated entries to remove.") + logging.info("No stale/outdated entries to remove.") return basedir = self.conf.directory @@ -398,11 +416,10 @@ class BackupManager: shutil.rmtree(os.path.join(basedir, d)) def ask_user_yesno(self, question): - if self.alwaysyes: - print(question + " y") - return "y" - else: + if logging.getLogger().isEnabledFor(logging.INFO): return input(question) + else: + return "y" def printUsage(): @@ -426,16 +443,19 @@ def printUsage(): print(" -e, --epoch force to create backup for given epoch:") print(" year, month, week, day, hour, sporadic") print(" -m, --mode override mode: full, diff, or incr") - print(" -y, --yes always assume 'yes' when user is asked") + print(" -v, --verbose be more verbose and interact with user") + print(" --verbosity LEVEL set verbosity to LEVEL, which can be") + print(" warning, info, debug") + print(" -V, --version print version info") if __name__ == "__main__": + logging.basicConfig(format='%(message)s') conffn = "/etc/shbackup.conf" cmd = "list" mode = None epoch = None - yes = False i = 0 while i < len(sys.argv)-1: @@ -450,21 +470,33 @@ if __name__ == "__main__": i += 1 conffn = sys.argv[i] - elif opt in ["-y", "--yes"]: - yes = True + elif opt in ["-V", "--version"]: + print("shbackup " + __version__) + exit(0) + + elif opt in ["-v", "--verbose"]: + logging.getLogger().setLevel(logging.INFO) + + elif opt in ["--verbosity"]: + i += 1 + level = sys.argv[i] + numlevel = getattr(logging, level.upper(), None) + if not isinstance(numlevel, int): + raise ValueError('Invalid verbosity level: %s' % level) + logging.getLogger().setLevel(numlevel) elif opt in ["-m", "--mode"]: i += 1 mode = sys.argv[i] if not mode in Mode: - print("Unknown mode '" + mode + "'.") + logging.error("Unknown mode '" + mode + "'.") exit(1) elif opt in ["-e", "--epoch"]: i += 1 epoch = sys.argv[i] if not epoch in Epoch: - print("Unknown epoch '" + epoch + "'.") + logging.error("Unknown epoch '" + epoch + "'.") exit(1) @@ -472,11 +504,11 @@ if __name__ == "__main__": cmd = opt else: - print("Unknown option: " + opt) + logging.error("Unknown option: " + opt) exit(1) try: - man = BackupManager(conffn, yes) + man = BackupManager(conffn) if cmd == "backup": man.backup(epoch, mode) @@ -489,7 +521,7 @@ if __name__ == "__main__": man.prune() except (Config.ReadError, configparser.DuplicateOptionError) as e: - print("Error reading config file: " + e.message) + logging.error("Error reading config file: " + e.message) -- 2.39.5