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"
This commit is contained in:
lars 2006-08-24 07:36:47 +00:00
parent bb16749b81
commit 707fb71476
5 changed files with 168 additions and 85 deletions

View file

@ -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 It was originally written in bash/perl. Now a complete rewrite is in
progress. So things might be confusing here. Hopefully not for long. progress. So things might be confusing here. Hopefully not for long.
:) :)
TODO: replace all "sys.exit"-calls by a unified handler
''' '''
# check python version # check python version
@ -30,7 +27,6 @@ CONF_LOCATIONS = [
"~/.cryptobox.conf", "~/.cryptobox.conf",
"/etc/cryptobox/cryptobox.conf"] "/etc/cryptobox/cryptobox.conf"]
SUDO_WRAPPER = ["sudo", "/home/lars/subversion/cryptobox/branches/pythonrewrite/bin2/CryptoBoxRootActions.py"]
class CryptoBox: class CryptoBox:
'''this class rules them all! '''this class rules them all!
@ -56,24 +52,22 @@ class CryptoBox:
to the configured destination''' to the configured destination'''
## basicConfig(...) needs python >= 2.4 ## basicConfig(...) needs python >= 2.4
try: try:
self.log = logging.getLogger("CryptoBox")
logging.basicConfig( logging.basicConfig(
format='%(asctime)s %(module)s %(levelname)s %(message)s', format='%(asctime)s %(module)s %(levelname)s %(message)s',
stream = sys.stderr) stderr=sys.stderr)
self.log = logging.getLogger("CryptoBox") self.log.setLevel(logging.ERROR)
self.log.setLevel(logging.INFO)
self.log.info("loggingsystem is up'n running") self.log.info("loggingsystem is up'n running")
## from now on everything can be logged via self.log... ## from now on everything can be logged via self.log...
except: except:
sys.stderr.write("Something with the loggingsystem went wrong. I give up.") sys.errorExit("SystemError","Something with the loggingsystem went wrong. I give up.")
sys.exit(1)
def __initPreferences(self, config_file): def __initPreferences(self, config_file):
try: try:
import configobj ## needed for reading and writing of the config file import configobj ## needed for reading and writing of the config file
except: except:
self.log.error("Could't import 'configobj'! Try 'apt-get install python-configobj'.") self.errorExit("SystemError", "Could't import 'configobj'! Try 'apt-get install python-configobj'.")
sys.exit(1)
# search for the configuration file # search for the configuration file
if config_file is None: if config_file is None:
# no config file was specified - we will look for it in the ususal locations # 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 os.path.exists(os.path.expanduser(f))]
if not conf_file_list: if not conf_file_list:
# no possible config file found in the usual locations # no possible config file found in the usual locations
self.log.error("No configuration file found - sorry!") self.errorExit("ConfigError", "No configuration file found - sorry!")
sys.exit(1)
config_file = conf_file_list[0] config_file = conf_file_list[0]
else: else:
# a config file was specified (e.g. via command line) # 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): if not os.path.exists(config_file):
self.log.error("Could not find the specified configuration file (%s)" % config_file) self.errorExit("ConfigError","Could not find the specified configuration file (%s)" % config_file)
sys.exit(1)
try: try:
self.cbxPrefs = configobj.ConfigObj(config_file) self.cbxPrefs = configobj.ConfigObj(config_file)
if self.cbxPrefs: if self.cbxPrefs:
self.log.info("found config: %s" % self.cbxPrefs.items()) self.log.info("found config: %s" % self.cbxPrefs.items())
else: else:
FileNotFound = "could not find %s" % config_file self.errorExit("ConfigError","failed to load the config file: %s" % config_file)
raise FileNotFound except IOError:
except FileNotFound: self.errorExit("ConfigError","unable to open the config file: %s" % config_file)
self.log.error(FileNotFound) try:
sys.exit(1)
try: try:
nameDB_file = os.path.join( nameDB_file = os.path.join(
self.cbxPrefs["Main"]["DataDir"], self.cbxPrefs["Main"]["DataDir"],
self.cbxPrefs["Main"]["NameDatabase"]) 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): if os.path.exists(nameDB_file):
self.nameDB = configobj.ConfigObj(nameDB_file) self.nameDB = configobj.ConfigObj(nameDB_file)
else: else:
self.nameDB = configobj.ConfigObj(nameDB_file, create_empty=True) self.nameDB = configobj.ConfigObj(nameDB_file, create_empty=True)
except SyntaxError: except SyntaxError:
self.log.error("Error during parsing of name database file (%s).\n" % (nameDB_file, )) self.errorExit("ConfigError","Error during parsing of name database file (%s).\n" % (nameDB_file, ))
sys.exit(1)
# TODO: check if nameDB file was created successfully? # TODO: check if nameDB file was created successfully?
# get the loglevel # get the loglevel
try: try:
log_level = self.cbxPrefs["Log"]["Level"].upper() log_level = self.cbxPrefs["Log"]["Level"].upper()
log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"] log_level_avail = ["DEBUG", "INFO", "WARN", "ERROR"]
#if not log_level in ["DEBUG", "INFO", "WARN", "ERROR"]:
if not log_level in log_level_avail: 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)) self.errorExit("ConfigError","invalid log level: %s is not in %s" % (self.cbxPrefs["Log"]["Level"], log_level_avail))
sys.exit(1)
except TypeError: except TypeError:
self.log.error("invalid log level: %s" % self.cbxPrefs["Log"]["Level"]) self.errorExit("ConfigError","invalid log level: %s" % self.cbxPrefs["Log"]["Level"])
sys.exit(1) try:
try: try:
new_handler = logging.FileHandler(self.cbxPrefs["Log"]["Details"]) new_handler = logging.FileHandler(self.cbxPrefs["Log"]["Details"])
# TODO: loglevel doesn't change after first initialisation except KeyError:
new_handler.setLevel(getattr(logging, log_level)) self.errorExit("ConfigError","could not find a configuration setting: [Log]->Details - please check your config file(%s)" % config_file)
# TODO: this formatter does not work for now new_handler.setFormatter(logging.Formatter('%(asctime)s %(module)s %(levelname)s: %(message)s'))
new_handler.setFormatter = '%(asctime)s %(module)s %(levelname)s %(message)s'
self.log.addHandler(new_handler) self.log.addHandler(new_handler)
# do not call parent's handlers
self.log.propagate = False
self.log.setLevel(getattr(logging,log_level))
except IOError: except IOError:
self.log.error("could not open logfile: %s" % self.cbxPrefs["Log"]["Details"]) self.errorExit("ConfigError","could not open logfile: %s" % self.cbxPrefs["Log"]["Details"])
sys.exit(1)
# do some initial checks # do some initial checks
@ -140,8 +133,7 @@ class CryptoBox:
try: try:
devnull = open(os.devnull, "w") devnull = open(os.devnull, "w")
except IOError: except IOError:
self.log.error("Could not open %s for writing!" % os.devnull) self.errorExit("SystemError","Could not open %s for writing!" % os.devnull)
sys.exit(1)
try: try:
proc = subprocess.Popen( proc = subprocess.Popen(
shell = False, shell = False,
@ -151,12 +143,10 @@ class CryptoBox:
self.cbxPrefs["Programs"]["CryptoBoxRootActions"], self.cbxPrefs["Programs"]["CryptoBoxRootActions"],
"check"]) "check"])
except OSError: except OSError:
self.log.error("could not find: %s" % self.cbxPrefs["Programs"]["super"]) self.errorExit("ConfigError","could not find: %s" % self.cbxPrefs["Programs"]["super"])
sys.exit(1)
proc.wait() proc.wait()
if proc.returncode != 0: 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?") self.errorExit("ConfigError","Could not call CryptoBoxRootActions by 'super' - maybe you did not add the appropriate line to /etc/super.tab?")
sys.exit(1)
# this method just demonstrates inheritance effects - may be removed # this method just demonstrates inheritance effects - may be removed
@ -164,6 +154,28 @@ class CryptoBox:
self.log.info(string) 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] # RFC: why should CryptoBoxProps inherit CryptoBox? [l]
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l] # RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l]
class CryptoBoxProps(CryptoBox): class CryptoBoxProps(CryptoBox):
@ -173,9 +185,9 @@ class CryptoBoxProps(CryptoBox):
All properties of the cryptobox can be accessed by this class. 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''' '''read config and fill class variables'''
CryptoBox.__init__(self) CryptoBox.__init__(self, config_file)
#self.cbx_inheritance_test() #self.cbx_inheritance_test()
#print self.cbxPrefs.items() #print self.cbxPrefs.items()
@ -186,6 +198,7 @@ class CryptoBoxProps(CryptoBox):
if self.isDeviceAllowed(device): if self.isDeviceAllowed(device):
self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self)) self.containers.append(CryptoBoxContainer.CryptoBoxContainer(device, self))
def isDeviceAllowed(self, devicename): def isDeviceAllowed(self, devicename):
"check if a device is white-listed for being used as cryptobox containers" "check if a device is white-listed for being used as cryptobox containers"
allowed = self.cbxPrefs["Main"]["AllowedDevices"] allowed = self.cbxPrefs["Main"]["AllowedDevices"]
@ -336,6 +349,11 @@ class CryptoBoxProps(CryptoBox):
except OSError: except OSError:
return [] return []
# choose an exit function
if __name__ == "__main__":
CryptoBox.errorExit = CryptoBox.errorExitProg
else:
CryptoBox.errorExit = CryptoBox.errorExitMod
if __name__ == "__main__": if __name__ == "__main__":
cb = CryptoBox() cb = CryptoBox()

View file

@ -85,6 +85,19 @@ class CryptoBoxContainer:
return os.path.ismount(self.__getMountPoint()) 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): def create(self, type, password=None):
if type == self.Types["luks"]: if type == self.Types["luks"]:
self.__createLuks(password) self.__createLuks(password)

View file

@ -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

View file

@ -44,6 +44,12 @@ def main():
" ***************** some functions ******************** " " ***************** some functions ******************** "
def luks_tests(e): def luks_tests(e):
# umount if necessary
try:
e.umount()
except "MountError":
pass
e.create(e.Types["luks"], "alt") e.create(e.Types["luks"], "alt")
print "\tluks create:\tok" print "\tluks create:\tok"
@ -60,6 +66,8 @@ def luks_tests(e):
if e.isMounted(): print "\tluks mount:\tok" if e.isMounted(): print "\tluks mount:\tok"
else: print "\tluks mount:\tfailed" else: print "\tluks mount:\tfailed"
print "\tCapacity (size, free, used) [MB]:\t%s" % (e.getCapacity(), )
try: try:
e.umount() e.umount()
except "MountError": except "MountError":
@ -72,6 +80,12 @@ def luks_tests(e):
def plain_tests(e): def plain_tests(e):
# umount if necessary
try:
e.umount()
except "MountError":
pass
e.create(e.Types["plain"]) e.create(e.Types["plain"])
print "\tplain create:\tok" print "\tplain create:\tok"
@ -85,6 +99,8 @@ def plain_tests(e):
if e.isMounted(): print "\tplain mount:\tok" if e.isMounted(): print "\tplain mount:\tok"
else: print "\tplain mount:\tfailed" else: print "\tplain mount:\tfailed"
print "\tCapacity (size, free, used) [MB]:\t%s" % (e.getCapacity(), )
try: try:
e.umount() e.umount()
except "MountError": except "MountError":

View file

@ -14,7 +14,7 @@ class CryptoBoxPropsDeviceTests(unittest.TestCase):
self.assertTrue(self.cb.isDeviceAllowed("/dev/usb/../loop1")) self.assertTrue(self.cb.isDeviceAllowed("/dev/usb/../loop1"))
def testDeniedDevices(self): 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/hda"))
self.assertFalse(self.cb.isDeviceAllowed("/dev/loopa/../hda")) self.assertFalse(self.cb.isDeviceAllowed("/dev/loopa/../hda"))
self.assertFalse(self.cb.isDeviceAllowed("/")) self.assertFalse(self.cb.isDeviceAllowed("/"))
@ -22,56 +22,98 @@ class CryptoBoxPropsDeviceTests(unittest.TestCase):
class CryptoBoxPropsConfigTests(unittest.TestCase): class CryptoBoxPropsConfigTests(unittest.TestCase):
'''test here if everything with the config turns right''' '''test here if everything with the config turns right'''
import filehandling
import os import os
import CryptoBox import CryptoBox
files = { "configFile" : "cbox-test.conf", files = {
"configFileOK" : "cbox-test_ok.conf",
"configFileBroken" : "cbox-test_broken.conf",
"nameDBFile" : "cryptobox_names.db", "nameDBFile" : "cryptobox_names.db",
"logFile" : "cryptobox.log", "logFile" : "cryptobox.log",
"tmpdir" : "cryptobox-mnt" } "tmpdir" : "cryptobox-mnt" }
tmpdirname = "" tmpdirname = ""
filenames = {} filenames = {}
configContent = """ configContentOK = """
[Main] [Main]
AllowedDevices = /dev/loop AllowedDevices = /dev/loop
DefaultVolumePrefix = "Data " DefaultVolumePrefix = "Data "
DataDir = /tmp DataDir = %s
NameDatabase = cryptobox_names.db NameDatabase = cryptobox_names.db
[System] [System]
User = 1000 User = 1000
Group = 1000 Group = 1000
MountParentDir = /tmp/mnt MountParentDir = %s/mnt
DefaultCipher = aes-cbc-essiv:sha256 DefaultCipher = aes-cbc-essiv:sha256
[Log] [Log]
Level = debug Level = debug
Facility = file Destination = file
Destination = /tmp/cryptobox.log #Details = %s/cryptobox.log
Details = /tmp/cryptobox.log
[Programs] [Programs]
blkid = /sbin/blkid blkid = /sbin/blkid
cryptsetup = /sbin/cryptsetup 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): def setUp(self):
'''generate all files in tmp and remember the names''' '''generate all files in tmp and remember the names'''
import tempfile
os = self.os os = self.os
self.tmpdirname = self.filehandling.gen_temp_dir(self.files["tmpdir"]) self.tmpdirname = tempfile.mkdtemp(prefix="cbox-")
os.chdir(self.tmpdirname)
for file in self.files.keys(): for file in self.files.keys():
#TODO: files are not really created in tmpdirname self.filenames[file] = os.path.join(self.tmpdirname, self.files[file])
self.filenames[file] = self.filehandling.gen_temp_file(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): def tearDown(self):
'''remove the created tmpfiles''' '''remove the created tmpfiles'''
os = self.os os = self.os
os.chdir(self.tmpdirname) os.chdir(self.tmpdirname)
# remove temp files
for file in self.filenames.values(): for file in self.filenames.values():
if os.path.exists(file): if os.path.exists(file):
#os.remove(file) os.remove(file)
pass # 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) self.assertTrue(1)