X-Git-Url: https://git.sthu.org/?p=sitarba.git;a=blobdiff_plain;f=sitarba;h=6f7cf5b69242c641ef61ab22c3d63e2cbe480b34;hp=b67925fd27435b8c236de566f5bfc58f175ca8c2;hb=HEAD;hpb=766966681b508aa0455fa1c538346fce422256bb diff --git a/sitarba b/sitarba index b67925f..6f7cf5b 100755 --- a/sitarba +++ b/sitarba @@ -15,6 +15,11 @@ import logging Modes = ["full", "incr", "diff"] + +class Options: + dryrun = False + + class Epoch: units = { @@ -45,7 +50,7 @@ class Epoch: def isRipe(self, oldest, now): - if self.unit==None: + if self.unit == None: return True delta = now-oldest @@ -115,16 +120,16 @@ class Backup: @staticmethod def fromDirName(dirname): - [strdate, strtime, epoch, mode] = dirname.split("-") + [strdate, strtime, epoch, mode] = dirname.split("-") - if not mode in Modes: - raise ValueError("Invalid mode: " + mode) + if not mode in Modes: + raise ValueError("Invalid mode: " + mode) - date = datetime.datetime(int(strdate[0:4]), - int(strdate[4:6]), int(strdate[6:8]),\ - int(strtime[0:2]), int(strtime[2:4])) + date = datetime.datetime(int(strdate[0:4]), + int(strdate[4:6]), int(strdate[6:8]),\ + int(strtime[0:2]), int(strtime[2:4])) - return Backup(date, epoch, mode) + return Backup(date, epoch, mode) def __repr__(self): return "[date: " + self.date.ctime() + \ @@ -138,7 +143,7 @@ class Backup: agestr = "(%s h)" % int(total_hours) else: agestr = "(%s d)" % age.days - return "%16s %7s %10s %4s" % ( + return "%16s %9s %10s %4s" % ( self.date.strftime("%Y-%m-%d %H:%M"), agestr, self.epoch, self.mode) @@ -178,7 +183,7 @@ class Config: self.sets = [] self.checksum = None self.lastchecksum = None - self.epochs = Epochs = { "sporadic" : Epoch() } + self.epochs = { "sporadic" : Epoch() } def __repr__(self): @@ -201,15 +206,15 @@ class Config: def _read_global(self, config, sec): for opt in config.options(sec): - if opt=="backupdir": + if opt == "backupdir": self.backupdir = config.get(sec, opt) if not os.path.isdir(self.backupdir): raise Config.ReadError("Backupdir '{0}' does not exist.".format(self.backupdir)) - elif opt=="format": + elif opt == "format": self.format = config.get(sec, opt) if not self.format in Config.formats: raise Config.ReadError("Invalid 'format' given.") - elif opt=="tarbin": + elif opt == "tarbin": self.tarbin = config.get(sec, opt) if not os.path.isfile(self.tarbin): raise Config.ReadError("Tar binary '{0}' does not exist.".format(self.tarbin)) @@ -232,7 +237,7 @@ class Config: e.unit = name for opt in config.options(sec): - if opt=="numkeeps": + if opt == "numkeeps": try: e.numkeeps = int(config.getint(sec, opt)) except ValueError: @@ -240,16 +245,16 @@ class Config: if e.numkeeps <= 0: raise Config.ReadError("Non-positive numkeeps '{0}' given.".format(e.numkeeps)) - elif opt=="mode": + elif opt == "mode": e.mode = config.get(sec, opt) if not e.mode in Modes: raise Config.ReadError("Invalid mode '{0}'.".format(e.mode)) - elif opt=="timespan": + elif opt == "timespan": if name in Epoch.units: raise Config.ReadError("The time delta of a standard epoch " + \ "is not supposed to be redefined. ") - td = config.get(sec,opt) + td = config.get(sec, opt) try: mult, unit = Epoch.parseTimedelta(td) e.unit = unit @@ -283,7 +288,7 @@ class Config: if opt.startswith("dir"): dirs += [config.get(sec, opt)] elif opt.startswith("exclude"): - excludes += [config.get(sec,opt)] + excludes += [config.get(sec, opt)] else: raise Config.ReadError("Unknown option '" + opt + "'.") @@ -305,7 +310,7 @@ class Config: for sec in config.sections(): - if sec=="global": + if sec == "global": self._read_global(config, sec) elif sec.startswith("epoch "): @@ -378,7 +383,7 @@ class BackupManager: continue # Get backups of that epoch - byepoch = list(sorted( [ b for b in backups if b.epoch==e], \ + byepoch = list(sorted( [ b for b in backups if b.epoch == e], \ key=lambda b: b.date)) # If there are any, determine the latest @@ -437,7 +442,7 @@ class BackupManager: # Read stdout and stderr of tarp errmsg = b"" while tarp.poll() == None: - rd,wr,ex = select.select([tarp.stdout, tarp.stderr], [], [], 0.05) + rd, wr, ex = select.select([tarp.stdout, tarp.stderr], [], [], 0.05) if tarp.stdout in rd: logging.debug( tarp.stdout.readline()[:-1].decode() ) if tarp.stderr in rd: @@ -480,7 +485,7 @@ class BackupManager: oldfullbackups = [ b for b in oldbackups if b.mode == "full" ] # No old full backups existing - if mode != "full" and len(oldfullbackups)==0: + if mode != "full" and len(oldfullbackups) == 0: logging.info("No full backups existing. Making a full backup.") # Checksum changed -> self.config file changed @@ -498,6 +503,9 @@ class BackupManager: if since != None: logging.debug("Making backup relative to " + since.ctime()) + if Options.dryrun: + return + yesno = self.ask_user_yesno("Proceed? [Y, n] ") if yesno == "n": return @@ -505,14 +513,14 @@ class BackupManager: # Create new backup directory basedir = self.conf.backupdir dirname = Backup.getDirName(now, epoch, mode) - tmpdirname = dirname + ("-%x" % (random.random()*2e16) ) + tmpdirname = dirname + ("-%x" % int(random.random()*2e16) ) targetdir = os.path.join(basedir, tmpdirname) - os.mkdir( targetdir ) + os.mkdir(targetdir) # Add file logger logfile = logging.getLogger("backuplog") - fil = logging.FileHandler( os.path.join(targetdir, "log") ) + fil = logging.FileHandler(os.path.join(targetdir, "log")) fil.setLevel(logging.DEBUG) logfile.addHandler(fil) @@ -524,6 +532,7 @@ class BackupManager: self.backupFileSet(s, targetdir, excludes, since) logfile.info("Stopped: " + datetime.datetime.now().ctime()) + fil.close() # Rename backup directory to final name os.rename( targetdir, os.path.join(basedir, dirname) ) @@ -579,6 +588,9 @@ class BackupManager: logging.info("No stale/outdated entries to remove.") return + if Options.dryrun: + return + basedir = self.conf.backupdir yesno = self.ask_user_yesno("Remove entries marked by '*'? [y, N] ") if yesno == "y": @@ -586,7 +598,7 @@ class BackupManager: try: shutil.rmtree(os.path.join(basedir, d)) except OSError as e: - logging.error("Error when removing '%s': %s" % (d,e.strerror) ) + logging.error("Error when removing '%s': %s" % (d, e.strerror) ) def ask_user_yesno(self, question): @@ -617,6 +629,7 @@ def printUsage(): print(" -e, --epoch EPOCH force to create backup for given epoch, which") print(" can be 'sporadic' or one of the configured epochs") print(" -m, --mode MODE override mode: full, diff, or incr") + print(" -n, --dry-run don't do anything, just tell what would be done") print(" -v, --verbose be more verbose and interact with user") print(" --verbosity LEVEL set verbosity to LEVEL, which can be") print(" error, warning, info, debug") @@ -686,6 +699,9 @@ if __name__ == "__main__": logging.error("Unknown mode '" + mode + "'.") exit(1) + elif opt in ["-n", "--dry-run"]: + Options.dryrun = True + elif opt in ["-e", "--epoch"]: i += 1 epoch = sys.argv[i] @@ -702,7 +718,7 @@ if __name__ == "__main__": logging.debug("Config: " + str(man.conf)) - if epoch!=None and not epoch in man.conf.epochs.keys(): + if epoch != None and not epoch in man.conf.epochs.keys(): logging.error("Unknown epoch '" + epoch + "'.") exit(1)