import random, re
+Mode = ["full", "incr", "diff"]
+
+Epoch = { "hour" : datetime.timedelta(0, 3600), \
+ "day" : datetime.timedelta(1), \
+ "week" : datetime.timedelta(7), \
+ "month" : datetime.timedelta(30), \
+ "year" : datetime.timedelta(365) }
+
+class Backup:
+ """A single backup has a date, an epoch and a mode."""
+
+ def __init__(self, date, epoch, mode):
+ self.date = date
+ self.epoch = epoch
+ self.mode = mode
+
+ def __str__(self):
+ return "[date: " + self.date.ctime() + \
+ ", epoch: " + self.epoch + \
+ ", mode: " + self.mode + "]"
+
+ @staticmethod
+ def getDirName(date, epoch, mode):
+ """Get directory name of backup by given properties."""
+ return date.strftime("%Y%m%d-%H%M") + "-" + epoch + "-" + mode
+
+ @staticmethod
+ def isBackupDir(dirname):
+ """Is directory a backup directory?"""
+ p = re.compile(r'^\d\d\d\d\d\d\d\d-\d\d\d\d-\w+-\w+$')
+ return p.match(dirname)
+
+
+
+
class Config:
"""Encapsules the configuration for the backup program."""
", keeps: " + str(self.epochkeeps) + \
", modes: " + str(self.epochmodes) + \
", exclpatterns: " + str(self.exclpatterns) + \
- ", sets: " + str([str(s) for s in self.sets]) + "]";
+ ", sets: " + str([str(s) for s in self.sets]) + "]"
def read(self, filename):
"""Read configuration from file"""
+ if not os.path.isfile(filename):
+ raise Config.ReadException("No file '" + filename + "'.")
config = configparser.RawConfigParser()
config.read(filename)
self.lastchecksum = None
-Mode = ["full", "incr", "diff"]
-
-Epoch = { "hour" : datetime.timedelta(0, 3600), \
- "day" : datetime.timedelta(1), \
- "week" : datetime.timedelta(7), \
- "month" : datetime.timedelta(30), \
- "year" : datetime.timedelta(365) }
-
-class Backup:
- """A single backup has a date, an epoch and a mode."""
-
- def __init__(self, date, epoch, mode):
- self.date = date
- self.epoch = epoch
- self.mode = mode
-
- def __str__(self):
- return "[date: " + self.date.ctime() + \
- ", epoch: " + self.epoch + \
- ", mode: " + self.mode + "]"
-
- @staticmethod
- def getDirName(date, epoch, mode):
- """Get directory name of backup by given properties."""
- return date.strftime("%Y%m%d-%H%M") + "-" + epoch + "-" + mode
-
- @staticmethod
- def isBackupDir(dirname):
- """Is directory a backup directory?"""
- p = re.compile(r'^\d\d\d\d\d\d\d\d-\d\d\d\d-\w+-\w+$')
- return p.match(dirname)
-
-
class BackupManager:
"""List and create backups"""
basedir = self.conf.directory
dirs = os.listdir(basedir)
# Filter directories
- return filter( lambda d: os.path.isdir(os.path.join(basedir, d)), dirs)
+ return [ d for d in dirs if os.path.isdir(os.path.join(basedir, d)) ]
def listOldBackups(self):
"""Returns a list of old backups."""
backups = []
- for entry in filter(Backup.isBackupDir, self.listAllDirs()):
+ for entry in [ b for b in self.listAllDirs() if Backup.isBackupDir(b) ]:
[strdate, strtime, epoch, mode] = entry.split("-")
if not epoch in Epoch.keys():
continue
# Get backups of that epoch
- byepoch = list(sorted(filter( lambda b: b.epoch==e, backups), \
+ byepoch = list(sorted( [ b for b in backups if b.epoch==e], \
key=lambda b: b.date))
# If there are any, determine the latest
mode = self.conf.epochmodes[epoch]
print("Making a backup. Epoch: " + epoch + ", mode: " + mode)
- oldfullbackups = list(filter(lambda b: b.mode=="full", oldbackups))
+ oldfullbackups = [ b for b in oldbackups if b.mode == "full" ]
# No old full backups existing
if mode != "full" and len(oldfullbackups)==0:
def prune(self):
"""Prune old backup files"""
- noBackupDir = lambda d: not Backup.isBackupDir(d)
- dirs = list(filter(noBackupDir, self.listAllDirs()))
+ # Collect all directories not matching backup name
+ dirs = [ d for d in self.listAllDirs() if not Backup.isBackupDir(d) ]
+ # Get all directories which are outdated
backups = self.listOldBackups()
- byepoch = { e : list(reversed(sorted(filter(lambda b: b.epoch==e, backups), \
- key=lambda b : b.date))) for e in Epoch }
+ byepoch = { e : list(sorted( [ b for b in backups if b.epoch == e ], \
+ key=lambda b : b.date, reverse=True)) for e in Epoch }
for e in byepoch:
keep = self.conf.epochkeeps[e]
old = byepoch[e][keep:]
print(" " + d)
basedir = self.conf.directory
- yesno = input("Remove listed entries? [y,N] ")
+ yesno = input("Remove listed entries? [y, N] ")
if yesno == "y":
for d in dirs:
- shutil.rmtree(os.path.join(basedir,d))
+ shutil.rmtree(os.path.join(basedir, d))
+
+
+def printUsage():
+ """Print --help text"""
+
+ print("shbackup - a simple backup solution.")
+ print("")
+ print("Usage:")
+ print(" " + sys.argv[0] + " [-C <configfile>")
+ print(" " + sys.argv[0] + " --help")
+ print("")
+ print("Options:")
+ print(" -C <configfile> default: /etc/shbackup.conf")
if __name__ == "__main__":
- conffn = "shbackup.conf"
+ conffn = "/etc/shbackup.conf"
+
+ i = 0
+ while i < len(sys.argv)-1:
+ i += 1
+ opt = sys.argv[i]
+
+ if opt in ["-h", "--help"]:
+ printUsage()
+ exit(0)
+
+ elif opt in ["-C", "--config"]:
+ i += 1
+ conffn = sys.argv[i]
+ continue
+
+ else:
+ print("Unknown option: " + opt)
+ exit(1)
- if len(sys.argv) > 1:
- conffn = sys.argv[1]
+ try:
+ man = BackupManager(conffn)
+ man.backup()
+ man.prune()
- man = BackupManager(conffn)
- man.backup()
- man.prune()
+ except Config.ReadException as e:
+ print("Error reading config file: ", end="")
+ for a in e.args:
+ print(a, end=" ")
+ print()