VMM/*: Moved some methods from classes to modules __init__.
- Adjusted many import statements.
- Small adjustments and whitespace cosmetics in Config.py
# -*- coding: UTF-8 -*-# Copyright (c) 2007 - 2010, Pascal Volk# See COPYING for distribution information."""vmm's configuration module for simplified configuration access.This module defines a few classes:``LazyConfig`` This class provides the following additonal methods * `LazyConfig.pget()` polymorphic getter which returns the value with the appropriate type. * `LazyConfig.dget()` like *pget()*, but checks additonal for default values in `LazyConfig._cfg`. * `LazyConfig.set()` like `RawConfigParser.set()`, but converts the new value to the appropriate type/class and optional validates the new value. * `LazyConfig.bool_new()` converts data from raw_input into boolean values. * `LazyConfig.get_boolean()` like `RawConfigParser.getboolean()`, but doesn't fail on real `bool` values.``Config`` The Config class used by vmm.``LazyConfigOption`` The class for the configuration objects in the ``Config`` class' ``_cfg`` dictionary."""fromshutilimportcopy2fromConfigParserimport(Error,MissingSectionHeaderError,NoOptionError,NoSectionError,ParsingError,RawConfigParser)fromcStringIOimportStringIO# TODO: move interactive stff to cliimportVirtualMailManager.constants.ERRORasERRfromVirtualMailManagerimportENCODING,exec_ok,get_unicode,is_dirfromVirtualMailManager.cliimportw_std# move to clifromVirtualMailManager.ExceptionsimportVMMConfigExceptionclassBadOptionError(Error):"""Raised when a option isn't in the format 'section.option'."""passclassConfigValueError(Error):"""Raised when creating or validating of new values fails."""passclassNoDefaultError(Error):"""Raised when the requested option has no default value."""def__init__(self,section,option):Error.__init__(self,'Option %r in section %r has no default value'%(option,section))classLazyConfig(RawConfigParser):"""The **lazy** derivate of the `RawConfigParser`. There are two additional getters: `LazyConfig.pget()` The polymorphic getter, which returns a option's value with the appropriate type. `LazyConfig.dget()` Like `LazyConfig.pget()`, but returns the option's default, from `LazyConfig._cfg['sectionname']['optionname'].default`, if the option is not configured in a ini-like configuration file. `LazyConfig.set()` differs from ``RawConfigParser``'s ``set()`` method. ``LazyConfig.set()`` takes the ``section`` and ``option`` arguments combined to a single string in the form "``section``\ **.**\ ``option``". """def__init__(self):RawConfigParser.__init__(self)self._modified=Falseself._cfg={'sectionname':{'optionname':LazyConfigOption(int,1,self.getint)}}"""sample _cfg dictionary. Create your own in your derived class."""defbool_new(self,value):"""Converts the string `value` into a `bool` and returns it. | '1', 'on', 'yes' and 'true' will become ``True`` | '0', 'off', 'no' and 'false' will become ``False`` Throws a `ConfigValueError` for all other values, except ``bool``\ s. """ifisinstance(value,bool):returnvalueifvalue.lower()inself._boolean_states:returnself._boolean_states[value.lower()]else:raiseConfigValueError(_(u'Not a boolean: “%s”')% \get_unicode(value))defget_boolean(self,section,option):# if the setting was not written to the configuration file, it may# be still a boolean value - lets seeifself._modified:tmp=self.get(section,option)ifisinstance(tmp,bool):returntmpreturnself.getboolean(section,option)def__get_section_option(self,section_option):"""splits ``section_option`` (section\ **.**\ option) in two parts and returns them as list ``[section, option]``, if: * it likes the format of ``section_option`` * the ``section`` is known * the ``option`` is known Else one of the following exceptions will be thrown: * `BadOptionError` * `NoSectionError` * `NoOptionError` """sect_opt=section_option.lower().split('.')iflen(sect_opt)!=2:# do we need a regexp to check the format?raiseBadOptionError(_(u'Bad format: “%s” - expected: section.option')% \get_unicode(section_option))ifnotsect_opt[0]inself._cfg:raiseNoSectionError(sect_opt[0])ifnotsect_opt[1]inself._cfg[sect_opt[0]]:raiseNoOptionError(sect_opt[1],sect_opt[0])returnsect_optdefitems(self,section):"""returns an iterable that returns key, value ``tuples`` from the given ``section``."""ifsectioninself._sections:# check if the section was parsedd2=self._sections[section]elifnotsectioninself._cfg:raiseNoSectionError(section)else:return((k,self._cfg[section][k].default) \forkinself._cfg[section].iterkeys())# still here? Get defaults and merge defaults with configured settingd=dict((k,self._cfg[section][k].default) \forkinself._cfg[section].iterkeys())d.update(d2)if'__name__'ind:deld['__name__']returnd.iteritems()defdget(self,option):"""Returns the value of the `option`. If the option could not be found in the configuration file, the configured default value, from ``LazyConfig._cfg`` will be returned. Arguments: `option` : string the configuration option in the form "``section``\ **.**\ ``option``" Throws a `NoDefaultError`, if no default value was passed to `LazyConfigOption.__init__()` for the `option`. """section,option=self.__get_section_option(option)try:returnself._cfg[section][option].getter(section,option)except(NoSectionError,NoOptionError):ifnotself._cfg[section][option].defaultisNone:returnself._cfg[section][option].defaultelse:raiseNoDefaultError(section,option)defpget(self,option):"""Returns the value of the `option`."""section,option=self.__get_section_option(option)returnself._cfg[section][option].getter(section,option)defset(self,option,value):"""Set the value of an option. Throws a ``ValueError`` if `value` couldn't be converted to ``LazyConfigOption.cls``"""section,option=self.__get_section_option(option)val=self._cfg[section][option].cls(value)ifnotself._cfg[section][option].validateisNone:val=self._cfg[section][option].validate(val)ifnotRawConfigParser.has_section(self,section):self.add_section(section)RawConfigParser.set(self,section,option,val)self._modified=Truedefhas_section(self,section):"""Checks if ``section`` is a known configuration section."""returnsection.lower()inself._cfgdefhas_option(self,option):"""Checks if the option (section\ **.**\ option) is a known configuration option."""try:self.__get_section_option(option)returnTrueexcept(BadOptionError,NoSectionError,NoOptionError):returnFalseclassLazyConfigOption(object):"""A simple container class for configuration settings. ``LazyConfigOption`` instances are required by `LazyConfig` instances, and instances of classes derived from ``LazyConfig``, like the `Config` class. """__slots__=('cls','default','getter','validate')def__init__(self,cls,default,getter,validate=None):"""Creates a new ``LazyConfigOption`` instance. Arguments: ``cls`` : type The class/type of the option's value ``default`` Default value of the option. Use ``None`` if the option should not have a default value. ``getter`` : callable A method's name of `RawConfigParser` and derived classes, to get a option's value, e.g. `self.getint`. ``validate`` : NoneType or a callable None or any method, that takes one argument, in order to check the value, when `LazyConfig.set()` is called. """self.cls=cls"""The class of the option's value e.g. `str`, `unicode` or `bool`"""self.default=default"""The option's default value, may be ``None``"""ifcallable(getter):self.getter=getter"""The getter method to get the option's value"""else:raiseTypeError('getter has to be a callable, got a %r'\%getter.__class__.__name__)ifvalidateisNoneorcallable(validate):self.validate=validate"""A method to validate the value"""else:raiseTypeError('validate has to be callable or None, got a %r'\%validate.__class__.__name__)classConfig(LazyConfig):"""This class is for reading and modifying vmm's configuration file."""def__init__(self,filename):"""Creates a new Config instance Arguments: ``filename`` path to the configuration file """LazyConfig.__init__(self)self.__cfgFileName=filenameself.__cfgFile=Noneself.__missing={}LCO=LazyConfigOptionbool_t=self.bool_newself._cfg={'account':{'delete_directory':LCO(bool_t,False,self.get_boolean),'directory_mode':LCO(int,448,self.getint),'disk_usage':LCO(bool_t,False,self.get_boolean),'password_length':LCO(int,8,self.getint),'random_password':LCO(bool_t,False,self.get_boolean),'imap':LCO(bool_t,True,self.get_boolean),'pop3':LCO(bool_t,True,self.get_boolean),'sieve':LCO(bool_t,True,self.get_boolean),'smtp':LCO(bool_t,True,self.get_boolean),},'bin':{'dovecotpw':LCO(str,'/usr/sbin/dovecotpw',self.get,exec_ok),'du':LCO(str,'/usr/bin/du',self.get,exec_ok),'postconf':LCO(str,'/usr/sbin/postconf',self.get,exec_ok),},'database':{'host':LCO(str,'localhost',self.get),'name':LCO(str,'mailsys',self.get),'pass':LCO(str,None,self.get),'user':LCO(str,None,self.get),},'domain':{'auto_postmaster':LCO(bool_t,True,self.get_boolean),'delete_directory':LCO(bool_t,False,self.get_boolean),'directory_mode':LCO(int,504,self.getint),'force_deletion':LCO(bool_t,False,self.get_boolean),},'maildir':{'folders':LCO(str,'Drafts:Sent:Templates:Trash',self.get),'name':LCO(str,'Maildir',self.get),},'misc':{'base_directory':LCO(str,'/srv/mail',self.get,is_dir),'dovecot_version':LCO(int,12,self.getint),'gid_mail':LCO(int,8,self.getint),'password_scheme':LCO(str,'CRAM-MD5',self.get,self.known_scheme),'transport':LCO(str,'dovecot:',self.get),},}defload(self):"""Loads the configuration, read only. Raises a VMMConfigException if the configuration syntax is invalid. """try:self.__cfgFile=open(self.__cfgFileName,'r')self.readfp(self.__cfgFile)except(MissingSectionHeaderError,ParsingError),e:raiseVMMConfigException(str(e),ERR.CONF_ERROR)finally:ifnotself.__cfgFileisNoneandnotself.__cfgFile.closed:self.__cfgFile.close()defcheck(self):"""Performs a configuration check. Raises a VMMConfigException if the check fails. """# TODO: There are only two settings w/o defaults.# So there is no need for cStringIOifnotself.__chkCfg():errmsg=StringIO()errmsg.write(_(u'Missing options, which have no default value.\n'))errmsg.write(_(u'Using configuration file: %s\n')%\self.__cfgFileName)forsection,optionsinself.__missing.iteritems():errmsg.write(_(u'* Section: %s\n')%section)foroptioninoptions:errmsg.write((u' %s\n')%option)raiseVMMConfigException(errmsg.getvalue(),ERR.CONF_ERROR)defgetsections(self):"""Returns an iterator object for all configuration sections."""returnself._cfg.iterkeys()defknown_scheme(self,scheme):"""Converts ``scheme`` to upper case and checks if is known by Dovecot (listed in VirtualMailManager.SCHEMES). Throws a `ConfigValueError` if the scheme is not listed in VirtualMailManager.SCHEMES. """scheme=scheme.upper()# TODO: VMM.SCHEMESdefunicode(self,section,option):"""Returns the value of the ``option`` from ``section``, converted to Unicode. """returnget_unicode(self.get(section,option))defconfigure(self,sections):"""Interactive method for configuring all options in the given sections Arguments: sections -- list of strings with section names """# TODO: Derivate CliConfig from Config an move the interactive# stuff to CliConfiginput_fmt=_(u'Enter new value for option %(option)s\[%(current_value)s]: ')failures=0w_std(_(u'Using configuration file: %s\n')%self.__cfgFileName)forsinsections:w_std(_(u'* Configuration section: “%s”')%s)foropt,valinself.items(s):failures=0whileTrue:newval=raw_input(input_fmt.encode(ENCODING,'replace')%{'option':opt,'current_value':val})ifnewvalandnewval!=val:try:self.set('%s.%s'%(s,opt),newval)breakexcept(ValueError,ConfigValueError),e:w_std(_(u'Warning: %s')%e)failures+=1iffailures>2:raiseVMMConfigException(_(u'Too many failures - try again later.'),ERR.VMM_TOO_MANY_FAILURES)else:breakprintifself._modified:self.__saveChanges()def__saveChanges(self):"""Writes changes to the configuration file."""# TODO: Move interactive stuff to CliConfigcopy2(self.__cfgFileName,self.__cfgFileName+'.bak')self.__cfgFile=open(self.__cfgFileName,'w')self.write(self.__cfgFile)self.__cfgFile.close()def__chkCfg(self):"""Checks all section's options for settings w/o default values. Returns ``True`` if everything is fine, else ``False``."""errors=Falseforsectioninself._cfg.iterkeys():missing=[]foroption,valueinself._cfg[section].iteritems():if(value.defaultisNoneandnotRawConfigParser.has_option(self,section,option)):missing.append(option)errors=Trueiflen(missing):self.__missing[section]=missingreturnnoterrors