#!/usr/bin/python3
"""A simple backup solution."""
-__version__ = "0.1"
+__version__ = "2.0"
__author__ = "Stefan Huber"
import datetime
checksumfn = "checksum"
def __init__(self):
- self.directory = None
- self.format = self.formats[0]
+ self.backupdir = None
+ self.format = self.formats[1]
+ self.tarbin = "/bin/tar"
self.excludes = []
self.sets = []
self.checksum = None
def __repr__(self):
- return "[directory: " + self.directory + \
+ return "[backupdir: " + self.backupdir + \
", format: " + self.format + \
+ ", tarbin: " + self.tarbin + \
", excludes: " + repr(self.excludes) + \
", epochs: " + repr(self.epochs) + \
", sets: " + repr(self.sets) + "]"
return realepochs
- def _read_destination(self, config, sec):
+ def _read_global(self, config, sec):
for opt in config.options(sec):
- if opt=="directory":
- self.directory = config.get(sec, opt)
- if not os.path.isdir(self.directory):
- raise Config.ReadError("Directory '{0}' does not exist.".format(self.directory))
+ 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":
self.format = config.get(sec, opt)
if not self.format in Config.formats:
raise Config.ReadError("Invalid 'format' given.")
- else:
- raise Config.ReadError("Unknown option '{0}'.".format(opt))
-
-
- def _read_global(self, config, sec):
- for opt in config.options(sec):
- if opt.startswith("exclude"):
+ 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))
+ elif opt.startswith("exclude"):
self.excludes += [ config.get(sec, opt) ]
else:
raise Config.ReadError("Unknown option '{0}'.".format(opt))
if not e.mode in Modes:
raise Config.ReadError("Invalid mode '{0}'.".format(e.mode))
- elif opt=="timedelta":
+ elif opt=="timespan":
if name in Epoch.units:
raise Config.ReadError("The time delta of a standard epoch " + \
"is not supposed to be redefined. ")
e.unit = unit
e.mult = mult
except ValueError as e:
- raise Config.ReadError("Invalid timedelta '{0}': {1}".format(td, str(e)))
+ raise Config.ReadError("Invalid timespan '{0}': {1}".format(td, str(e)))
elif opt.startswith("exclude"):
e.excludes += [config.get(sec, opt)]
config = configparser.RawConfigParser()
config.read(filename)
- for reqsec in ["destination"]:
+ for reqsec in ["global"]:
if not config.has_section(reqsec):
raise Config.ReadError("Mandatory section '" + reqsec + "' is missing.")
for sec in config.sections():
- if sec=="destination":
- self._read_destination(config, sec)
-
- elif sec=="global":
+ if sec=="global":
self._read_global(config, sec)
elif sec.startswith("epoch "):
else:
raise Config.ReadError("Unknown section '" + sec + "'.")
- if self.directory == None:
- raise Config.ReadError("No destination directory set.")
+ if self.backupdir == None:
+ raise Config.ReadError("No backup directory set.")
# Compute checksum of config file
f.close()
try:
- f = open(os.path.join(self.directory, self.checksumfn), 'r')
+ f = open(os.path.join(self.backupdir, self.checksumfn), 'r')
self.lastchecksum = f.read().strip()
f.close()
except IOError:
def listAllDirs(self):
- """List all dirs in destination directory"""
+ """List all dirs in backupdir"""
# Get all entries
- basedir = self.conf.directory
+ basedir = self.conf.backupdir
dirs = os.listdir(basedir)
# Filter directories
return [ d for d in dirs if os.path.isdir(os.path.join(basedir, d)) ]
logfile = logging.getLogger('backuplog')
logfile.info("Running file set: " + fileset.name)
- tarpath = "/bin/tar"
fsfn = os.path.join(targetdir, fileset.name) + "." + self.conf.format
-
taropts = []
# Add the since date, if given
taropts += ["-C", "/"] + [ "./" + d.lstrip("/") for d in fileset.dirs]
# Launch the tar process
- tarargs = [tarpath] + ["-cpvaf", fsfn] + taropts
+ tarargs = [self.conf.tarbin] + ["-cpvaf", fsfn] + taropts
logfile.debug("tar call: " + " ".join(tarargs))
tarp = subprocess.Popen( tarargs, bufsize=-1, \
stdout=subprocess.PIPE, stderr=subprocess.PIPE )
if rett != 0:
for l in errmsg.decode().split("\n"):
logfile.error(l)
- logfile.error(tarpath + " returned with exit status " + str(rett) + ".")
+ logfile.error(self.conf.tarbin + " returned with exit status " + \
+ str(rett) + ".")
def backup(self, epoch=None, mode=None):
if yesno == "n":
return
- # Create new target directory
- basedir = self.conf.directory
+ # Create new backup directory
+ basedir = self.conf.backupdir
dirname = Backup.getDirName(now, epoch, mode)
tmpdirname = dirname + ("-%x" % (random.random()*2e16) )
targetdir = os.path.join(basedir, tmpdirname)
logging.info("No stale/outdated entries to remove.")
return
- basedir = self.conf.directory
+ basedir = self.conf.backupdir
yesno = self.ask_user_yesno("Remove entries marked by '*'? [y, N] ")
if yesno == "y":
for d in removeDirs:
print("")
print("Options:")
print(" -h, --help print this usage text")
- print(" -c, --conf <configfile> use given configuration file")
+ print(" -c, --conf FILE use given configuration file")
print(" default: /etc/shbackup.conf")
- print(" -e, --epoch <epoch> force to create backup for given epoch:")
- print(" year, month, week, day, hour, sporadic")
- print(" -m, --mode <mode> override mode: full, diff, or incr")
+ 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(" -v, --verbose be more verbose and interact with user")
print(" --verbosity LEVEL set verbosity to LEVEL, which can be")
print(" error, warning, info, debug")