From 707fb71476c006e27d813f98984a3d1043ac7d55 Mon Sep 17 00:00:00 2001 From: lars Date: Thu, 24 Aug 2006 07:36:47 +0000 Subject: [PATCH] logging cleaned up unified errorExit function instead of explicite sys.exit calls add config_file argument to CryptoBoxProps add getCapacity method to CryptoBoxContainer removed "classes.txt" --- pythonrewrite/bin2/CryptoBox.py | 110 +++++++++++++--------- pythonrewrite/bin2/CryptoBoxContainer.py | 13 +++ pythonrewrite/bin2/classes.txt | 6 -- pythonrewrite/bin2/test.py | 16 ++++ pythonrewrite/bin2/unittests.CryptoBox.py | 108 ++++++++++++++------- 5 files changed, 168 insertions(+), 85 deletions(-) delete mode 100644 pythonrewrite/bin2/classes.txt diff --git a/pythonrewrite/bin2/CryptoBox.py b/pythonrewrite/bin2/CryptoBox.py index cbc7d9d..eeec39e 100755 --- a/pythonrewrite/bin2/CryptoBox.py +++ b/pythonrewrite/bin2/CryptoBox.py @@ -5,9 +5,6 @@ This is the web interface for a fileserver managing encrypted filesystems. It was originally written in bash/perl. Now a complete rewrite is in progress. So things might be confusing here. Hopefully not for long. :) - - -TODO: replace all "sys.exit"-calls by a unified handler ''' # check python version @@ -30,7 +27,6 @@ CONF_LOCATIONS = [ "~/.cryptobox.conf", "/etc/cryptobox/cryptobox.conf"] -SUDO_WRAPPER = ["sudo", "/home/lars/subversion/cryptobox/branches/pythonrewrite/bin2/CryptoBoxRootActions.py"] class CryptoBox: '''this class rules them all! @@ -56,24 +52,22 @@ class CryptoBox: to the configured destination''' ## basicConfig(...) needs python >= 2.4 try: - logging.basicConfig( - format='%(asctime)s %(module)s %(levelname)s %(message)s', - stream = sys.stderr) self.log = logging.getLogger("CryptoBox") - self.log.setLevel(logging.INFO) + logging.basicConfig( + format='%(asctime)s %(module)s %(levelname)s %(message)s', + stderr=sys.stderr) + self.log.setLevel(logging.ERROR) self.log.info("loggingsystem is up'n running") ## from now on everything can be logged via self.log... except: - sys.stderr.write("Something with the loggingsystem went wrong. I give up.") - sys.exit(1) + sys.errorExit("SystemError","Something with the loggingsystem went wrong. I give up.") def __initPreferences(self, config_file): try: import configobj ## needed for reading and writing of the config file except: - self.log.error("Could't import 'configobj'! Try 'apt-get install python-configobj'.") - sys.exit(1) + self.errorExit("SystemError", "Could't import 'configobj'! Try 'apt-get install python-configobj'.") # search for the configuration file if config_file is None: # no config file was specified - we will look for it in the ususal locations @@ -82,57 +76,56 @@ class CryptoBox: if os.path.exists(os.path.expanduser(f))] if not conf_file_list: # no possible config file found in the usual locations - self.log.error("No configuration file found - sorry!") - sys.exit(1) + self.errorExit("ConfigError", "No configuration file found - sorry!") config_file = conf_file_list[0] else: # a config file was specified (e.g. via command line) + if type(config_file) != types.StringType: + self.errorExit("ConfigError","Invalid config file specified: %s" % config_file) if not os.path.exists(config_file): - self.log.error("Could not find the specified configuration file (%s)" % config_file) - sys.exit(1) + self.errorExit("ConfigError","Could not find the specified configuration file (%s)" % config_file) try: self.cbxPrefs = configobj.ConfigObj(config_file) if self.cbxPrefs: self.log.info("found config: %s" % self.cbxPrefs.items()) else: - FileNotFound = "could not find %s" % config_file - raise FileNotFound - except FileNotFound: - self.log.error(FileNotFound) - sys.exit(1) + self.errorExit("ConfigError","failed to load the config file: %s" % config_file) + except IOError: + self.errorExit("ConfigError","unable to open the config file: %s" % config_file) try: - nameDB_file = os.path.join( - self.cbxPrefs["Main"]["DataDir"], - self.cbxPrefs["Main"]["NameDatabase"]) + try: + nameDB_file = os.path.join( + self.cbxPrefs["Main"]["DataDir"], + self.cbxPrefs["Main"]["NameDatabase"]) + except KeyError: + self.errorExit("ConfigError","could not find one of these configuration settings: [Main]->DataDir and [Main]->NameDatabase - please check your config file(%s)" % config_file) if os.path.exists(nameDB_file): self.nameDB = configobj.ConfigObj(nameDB_file) else: self.nameDB = configobj.ConfigObj(nameDB_file, create_empty=True) except SyntaxError: - self.log.error("Error during parsing of name database file (%s).\n" % (nameDB_file, )) - sys.exit(1) + self.errorExit("ConfigError","Error during parsing of name database file (%s).\n" % (nameDB_file, )) # TODO: check if nameDB file was created successfully? # get the loglevel try: log_level = self.cbxPrefs["Log"]["Level"].upper() log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"] - #if not log_level in ["DEBUG", "INFO", "WARN", "ERROR"]: if not log_level in log_level_avail: - self.log.error("invalid log level: %s is not in %s" % (self.cbxPrefs["Log"]["Level"], log_level_avail)) - sys.exit(1) + self.errorExit("ConfigError","invalid log level: %s is not in %s" % (self.cbxPrefs["Log"]["Level"], log_level_avail)) except TypeError: - self.log.error("invalid log level: %s" % self.cbxPrefs["Log"]["Level"]) - sys.exit(1) + self.errorExit("ConfigError","invalid log level: %s" % self.cbxPrefs["Log"]["Level"]) try: - new_handler = logging.FileHandler(self.cbxPrefs["Log"]["Details"]) - # TODO: loglevel doesn't change after first initialisation - new_handler.setLevel(getattr(logging, log_level)) - # TODO: this formatter does not work for now - new_handler.setFormatter = '%(asctime)s %(module)s %(levelname)s %(message)s' + try: + new_handler = logging.FileHandler(self.cbxPrefs["Log"]["Details"]) + except KeyError: + self.errorExit("ConfigError","could not find a configuration setting: [Log]->Details - please check your config file(%s)" % config_file) + new_handler.setFormatter(logging.Formatter('%(asctime)s %(module)s %(levelname)s: %(message)s')) self.log.addHandler(new_handler) + # do not call parent's handlers + self.log.propagate = False + self.log.setLevel(getattr(logging,log_level)) except IOError: - self.log.error("could not open logfile: %s" % self.cbxPrefs["Log"]["Details"]) - sys.exit(1) + self.errorExit("ConfigError","could not open logfile: %s" % self.cbxPrefs["Log"]["Details"]) # do some initial checks @@ -140,8 +133,7 @@ class CryptoBox: try: devnull = open(os.devnull, "w") except IOError: - self.log.error("Could not open %s for writing!" % os.devnull) - sys.exit(1) + self.errorExit("SystemError","Could not open %s for writing!" % os.devnull) try: proc = subprocess.Popen( shell = False, @@ -151,17 +143,37 @@ class CryptoBox: self.cbxPrefs["Programs"]["CryptoBoxRootActions"], "check"]) except OSError: - self.log.error("could not find: %s" % self.cbxPrefs["Programs"]["super"]) - sys.exit(1) + self.errorExit("ConfigError","could not find: %s" % self.cbxPrefs["Programs"]["super"]) proc.wait() if proc.returncode != 0: - self.log.error("Could not call CryptoBoxRootActions by 'super' - maybe you did not add the appropriate line to /etc/super.tab?") - sys.exit(1) + self.errorExit("ConfigError","Could not call CryptoBoxRootActions by 'super' - maybe you did not add the appropriate line to /etc/super.tab?") # this method just demonstrates inheritance effects - may be removed def cbx_inheritance_test(self, string="you lucky widow"): self.log.info(string) + + + def errorExitMod(self, title, msg, code=1): + """output an error message and quit by raising an exception + + this function should be used if this class was used as a module instead + of as a standalone program + """ + self.log.error(msg) + raise title, msg + + + + def errorExitProg(self, title, msg, code=1): + """output an error message and quit by calling sys.exit + + this function should be used if this class was used as a standalone + program + """ + self.log.error(msg) + sys.exit(code) + # RFC: why should CryptoBoxProps inherit CryptoBox? [l] @@ -173,9 +185,9 @@ class CryptoBoxProps(CryptoBox): All properties of the cryptobox can be accessed by this class. ''' - def __init__(self): + def __init__(self, config_file=None): '''read config and fill class variables''' - CryptoBox.__init__(self) + CryptoBox.__init__(self, config_file) #self.cbx_inheritance_test() #print self.cbxPrefs.items() @@ -186,6 +198,7 @@ class CryptoBoxProps(CryptoBox): if self.isDeviceAllowed(device): self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self)) + def isDeviceAllowed(self, devicename): "check if a device is white-listed for being used as cryptobox containers" allowed = self.cbxPrefs["Main"]["AllowedDevices"] @@ -336,6 +349,11 @@ class CryptoBoxProps(CryptoBox): except OSError: return [] +# choose an exit function +if __name__ == "__main__": + CryptoBox.errorExit = CryptoBox.errorExitProg +else: + CryptoBox.errorExit = CryptoBox.errorExitMod if __name__ == "__main__": cb = CryptoBox() diff --git a/pythonrewrite/bin2/CryptoBoxContainer.py b/pythonrewrite/bin2/CryptoBoxContainer.py index ec21d6d..13766a5 100755 --- a/pythonrewrite/bin2/CryptoBoxContainer.py +++ b/pythonrewrite/bin2/CryptoBoxContainer.py @@ -83,6 +83,19 @@ class CryptoBoxContainer: def isMounted(self): return os.path.ismount(self.__getMountPoint()) + + + def getCapacity(self): + """return the current capacity state of the volume + + the result is a tuple of values in megabyte: + (size, available, used) + """ + info = os.statvfs(self.__getMountPoint()) + return ( + int(info.f_bsize*info.f_blocks/1024/1024), + int(info.f_bsize*info.f_bavail/1024/1024), + int(info.f_bsize*(info.f_blocks-info.f_bavail)/1024/1024)) def create(self, type, password=None): diff --git a/pythonrewrite/bin2/classes.txt b/pythonrewrite/bin2/classes.txt deleted file mode 100644 index 255e0ec..0000000 --- a/pythonrewrite/bin2/classes.txt +++ /dev/null @@ -1,6 +0,0 @@ -this is the future plan for the cbx python classes, you can find the actual classes under ./doc (generated by happydoc) - -Classes and Methods: - CryptoBoxContainer - getCapacity - diff --git a/pythonrewrite/bin2/test.py b/pythonrewrite/bin2/test.py index 957423d..cdaeb50 100755 --- a/pythonrewrite/bin2/test.py +++ b/pythonrewrite/bin2/test.py @@ -44,6 +44,12 @@ def main(): " ***************** some functions ******************** " def luks_tests(e): + # umount if necessary + try: + e.umount() + except "MountError": + pass + e.create(e.Types["luks"], "alt") print "\tluks create:\tok" @@ -60,6 +66,8 @@ def luks_tests(e): if e.isMounted(): print "\tluks mount:\tok" else: print "\tluks mount:\tfailed" + print "\tCapacity (size, free, used) [MB]:\t%s" % (e.getCapacity(), ) + try: e.umount() except "MountError": @@ -72,6 +80,12 @@ def luks_tests(e): def plain_tests(e): + # umount if necessary + try: + e.umount() + except "MountError": + pass + e.create(e.Types["plain"]) print "\tplain create:\tok" @@ -85,6 +99,8 @@ def plain_tests(e): if e.isMounted(): print "\tplain mount:\tok" else: print "\tplain mount:\tfailed" + print "\tCapacity (size, free, used) [MB]:\t%s" % (e.getCapacity(), ) + try: e.umount() except "MountError": diff --git a/pythonrewrite/bin2/unittests.CryptoBox.py b/pythonrewrite/bin2/unittests.CryptoBox.py index 8152f21..b2d3a3a 100755 --- a/pythonrewrite/bin2/unittests.CryptoBox.py +++ b/pythonrewrite/bin2/unittests.CryptoBox.py @@ -14,7 +14,7 @@ class CryptoBoxPropsDeviceTests(unittest.TestCase): self.assertTrue(self.cb.isDeviceAllowed("/dev/usb/../loop1")) def testDeniedDevices(self): - '''isDeviceAllowed should fail with not explecitly allowed devices''' + '''isDeviceAllowed should fail with not explicitly allowed devices''' self.assertFalse(self.cb.isDeviceAllowed("/dev/hda")) self.assertFalse(self.cb.isDeviceAllowed("/dev/loopa/../hda")) self.assertFalse(self.cb.isDeviceAllowed("/")) @@ -22,56 +22,98 @@ class CryptoBoxPropsDeviceTests(unittest.TestCase): class CryptoBoxPropsConfigTests(unittest.TestCase): '''test here if everything with the config turns right''' - import filehandling import os import CryptoBox - files = { "configFile" : "cbox-test.conf", - "nameDBFile" : "cryptobox_names.db", - "logFile" : "cryptobox.log", - "tmpdir" : "cryptobox-mnt" } + files = { + "configFileOK" : "cbox-test_ok.conf", + "configFileBroken" : "cbox-test_broken.conf", + "nameDBFile" : "cryptobox_names.db", + "logFile" : "cryptobox.log", + "tmpdir" : "cryptobox-mnt" } tmpdirname = "" filenames = {} - configContent = """ -[Main] -AllowedDevices = /dev/loop -DefaultVolumePrefix = "Data " -DataDir = /tmp -NameDatabase = cryptobox_names.db -[System] -User = 1000 -Group = 1000 -MountParentDir = /tmp/mnt -DefaultCipher = aes-cbc-essiv:sha256 -[Log] -Level = debug -Facility = file -Destination = /tmp/cryptobox.log -[Programs] -blkid = /sbin/blkid -cryptsetup = /sbin/cryptsetup -""" + configContentOK = """ + [Main] + AllowedDevices = /dev/loop + DefaultVolumePrefix = "Data " + DataDir = %s + NameDatabase = cryptobox_names.db + [System] + User = 1000 + Group = 1000 + MountParentDir = %s/mnt + DefaultCipher = aes-cbc-essiv:sha256 + [Log] + Level = debug + Destination = file + #Details = %s/cryptobox.log + Details = /tmp/cryptobox.log + [Programs] + blkid = /sbin/blkid + cryptsetup = /sbin/cryptsetup + super = /usr/bin/super + CryptoBoxRootActions = CryptoBoxRootActions""" + + configContentBroken = """ + [Main] + AllowedDevices = /dev/loop + DefaultVolumePrefix = "Data " + #DataDir = %s + NameDatabase = cryptobox_names.db + [System] + User = 1000 + Group = 1000 + MountParentDir = %s/mnt + DefaultCipher = aes-cbc-essiv:sha256 + [Log] + Level = debug + Destination = file + #Details = %s/cryptobox.log + Details = /tmp/cryptobox.log + [Programs] + blkid = /sbin/blkid + cryptsetup = /sbin/cryptsetup + super = /usr/bin/super + CryptoBoxRootActions = CryptoBoxRootActions""" def setUp(self): '''generate all files in tmp and remember the names''' + import tempfile os = self.os - self.tmpdirname = self.filehandling.gen_temp_dir(self.files["tmpdir"]) - os.chdir(self.tmpdirname) + self.tmpdirname = tempfile.mkdtemp(prefix="cbox-") for file in self.files.keys(): - #TODO: files are not really created in tmpdirname - self.filenames[file] = self.filehandling.gen_temp_file(self.files[file]) + self.filenames[file] = os.path.join(self.tmpdirname, self.files[file]) + cf = open(self.filenames["configFileOK"], "w") + cf.write(self.configContentOK % (self.tmpdirname, self.tmpdirname, self.tmpdirname)) + cf.close() + cf = open(self.filenames["configFileBroken"], "w") + cf.write(self.configContentBroken % (self.tmpdirname, self.tmpdirname, self.tmpdirname)) + cf.close() + def tearDown(self): '''remove the created tmpfiles''' os = self.os os.chdir(self.tmpdirname) + # remove temp files for file in self.filenames.values(): if os.path.exists(file): - #os.remove(file) - pass + os.remove(file) + # remove temp dir + os.rmdir(self.tmpdirname) - def testConfigFile(self): - '''testConfigFile TODO''' + + def testConfigInit(self): + '''Check various branches of config file loading''' + self.assertRaises("ConfigError", self.CryptoBox.CryptoBoxProps,"/invalid/path/to/config/file") + self.assertRaises("ConfigError", self.CryptoBox.CryptoBoxProps,"/etc/shadow") + self.CryptoBox.CryptoBoxProps() + self.CryptoBox.CryptoBoxProps(self.filenames["configFileOK"]) + self.assertRaises("ConfigError", self.CryptoBox.CryptoBoxProps,[]) + self.assertRaises("ConfigError", self.CryptoBox.CryptoBoxProps,self.filenames["configFileBroken"]) + # TODO: check details of different ConfigError-exceptions + # TODO: use different kind of broken setups ... self.assertTrue(1)