Modes = ["full", "incr", "diff"]
+
+class Options:
+ dryrun = False
+
+
class Epoch:
units = {
def isRipe(self, oldest, now):
- if self.unit==None:
+ if self.unit == None:
return True
delta = now-oldest
@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() + \
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)
self.sets = []
self.checksum = None
self.lastchecksum = None
- self.epochs = Epochs = { "sporadic" : Epoch() }
+ self.epochs = { "sporadic" : Epoch() }
def __repr__(self):
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))
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:
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
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 + "'.")
for sec in config.sections():
- if sec=="global":
+ if sec == "global":
self._read_global(config, sec)
elif sec.startswith("epoch "):
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
# 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:
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
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
# 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)
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) )
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":
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):
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")
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]
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)