cwconfig.py
changeset 5021 58e89f3dfbae
parent 5013 ad91f93bbb93
child 5022 7e09702aa766
equal deleted inserted replaced
5017:b2cc2a51706f 5021:58e89f3dfbae
    13    another one with `CW_INSTANCES_DIR` environment variable or simply add some
    13    another one with `CW_INSTANCES_DIR` environment variable or simply add some
    14    other directories by using `CW_CUBES_PATH`.
    14    other directories by using `CW_CUBES_PATH`.
    15 
    15 
    16  * cubicweb migration files are by default searched in
    16  * cubicweb migration files are by default searched in
    17    `<CW_SOFTWARE_ROOT>/misc/migration` instead of
    17    `<CW_SOFTWARE_ROOT>/misc/migration` instead of
    18    `/usr/share/cubicweb/migration/`(unless another emplacement is specified
    18    `<install prefix>/share/cubicweb/migration/`
    19    using `CW_MIGRATION_DIR`.
       
    20 
    19 
    21  * Cubicweb will start in 'user' mode (see below)
    20  * Cubicweb will start in 'user' mode (see below)
    22 
    21 
    23 
    22 
    24 On startup, Cubicweb is using a specific *mode*. A mode corresponds to some
    23 On startup, Cubicweb is using a specific *mode*. A mode corresponds to some
    64    Directory where cubicweb instances data will be written
    63    Directory where cubicweb instances data will be written
    65 
    64 
    66 .. envvar:: CW_RUNTIME_DIR
    65 .. envvar:: CW_RUNTIME_DIR
    67    Directory where pid files will be written
    66    Directory where pid files will be written
    68 
    67 
    69 .. envvar:: CW_MIGRATION_DIR
       
    70    Directory where cubicweb migration files will be found
       
    71 
       
    72 
    68 
    73 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
    69 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
    74 """
    70 """
    75 __docformat__ = "restructuredtext en"
    71 __docformat__ = "restructuredtext en"
    76 _ = unicode
    72 _ = unicode
    77 
    73 
    78 import sys
    74 import sys
    79 import os
    75 import os
    80 import logging
    76 import logging
    81 import tempfile
       
    82 from smtplib import SMTP
    77 from smtplib import SMTP
    83 from threading import Lock
    78 from threading import Lock
    84 from os.path import exists, join, expanduser, abspath, normpath, basename, isdir
    79 from os.path import (exists, join, expanduser, abspath, normpath,
       
    80                      basename, isdir, dirname)
    85 from warnings import warn
    81 from warnings import warn
    86 
       
    87 from logilab.common.decorators import cached, classproperty
    82 from logilab.common.decorators import cached, classproperty
    88 from logilab.common.deprecation import deprecated
    83 from logilab.common.deprecation import deprecated
    89 from logilab.common.logging_ext import set_log_methods, init_log
    84 from logilab.common.logging_ext import set_log_methods, init_log
    90 from logilab.common.configuration import (Configuration, Method,
    85 from logilab.common.configuration import (Configuration, Method,
    91                                           ConfigurationMixIn, merge_options)
    86                                           ConfigurationMixIn, merge_options)
   129     if len(modes) != 1:
   124     if len(modes) != 1:
   130         raise ConfigurationError('unable to guess configuration from %r %s'
   125         raise ConfigurationError('unable to guess configuration from %r %s'
   131                                  % (directory, modes))
   126                                  % (directory, modes))
   132     return modes[0]
   127     return modes[0]
   133 
   128 
       
   129 def _find_prefix(start_path=CW_SOFTWARE_ROOT):
       
   130     """Runs along the parent directories of *start_path* (default to cubicweb source directory)
       
   131     looking for one containing a 'share/cubicweb' directory.
       
   132     The first matching directory is assumed as the prefix installation of cubicweb
       
   133 
       
   134     Returns the matching prefix or None.
       
   135     """
       
   136     prefix = start_path
       
   137     old_prefix = None
       
   138     if not isdir(start_path):
       
   139         prefix = dirname(start_path)
       
   140     while not isdir(join(prefix, 'share', 'cubicweb')) and prefix != old_prefix:
       
   141         old_prefix = prefix
       
   142         prefix = dirname(prefix)
       
   143     if isdir(join(prefix, 'share', 'cubicweb')):
       
   144         return prefix
       
   145     return sys.prefix
   134 
   146 
   135 # persistent options definition
   147 # persistent options definition
   136 PERSISTENT_OPTIONS = (
   148 PERSISTENT_OPTIONS = (
   137     ('encoding',
   149     ('encoding',
   138      {'type' : 'string',
   150      {'type' : 'string',
   201 _forced_mode = os.environ.get('CW_MODE')
   213 _forced_mode = os.environ.get('CW_MODE')
   202 assert _forced_mode in (None, 'system', 'user')
   214 assert _forced_mode in (None, 'system', 'user')
   203 
   215 
   204 CWDEV = exists(join(CW_SOFTWARE_ROOT, '.hg'))
   216 CWDEV = exists(join(CW_SOFTWARE_ROOT, '.hg'))
   205 
   217 
       
   218 try:
       
   219     _INSTALL_PREFIX = os.environ['CW_INSTALL_PREFIX']
       
   220 except KeyError:
       
   221     _INSTALL_PREFIX = _find_prefix()
       
   222 
   206 class CubicWebNoAppConfiguration(ConfigurationMixIn):
   223 class CubicWebNoAppConfiguration(ConfigurationMixIn):
   207     """base class for cubicweb configuration without a specific instance directory
   224     """base class for cubicweb configuration without a specific instance directory
   208     """
   225     """
   209     __metaclass__ = metaconfiguration
   226     __metaclass__ = metaconfiguration
   210     # to set in concrete configuration
   227     # to set in concrete configuration
   214     # nor remove appobjects based on unused interface
   231     # nor remove appobjects based on unused interface
   215     cleanup_interface_sobjects = True
   232     cleanup_interface_sobjects = True
   216     # debug mode
   233     # debug mode
   217     debugmode = False
   234     debugmode = False
   218 
   235 
   219     if os.environ.get('APYCOT_ROOT'):
   236 
   220         mode = 'test'
   237     if (CWDEV and _forced_mode != 'system'):
   221         # allow to test cubes within apycot using cubicweb not installed by
       
   222         # apycot
       
   223         if __file__.startswith(os.environ['APYCOT_ROOT']):
       
   224             CUBES_DIR = '%(APYCOT_ROOT)s/local/share/cubicweb/cubes/' % os.environ
       
   225             # create __init__ file
       
   226             file(join(CUBES_DIR, '__init__.py'), 'w').close()
       
   227         else:
       
   228             CUBES_DIR = '/usr/share/cubicweb/cubes/'
       
   229     elif (CWDEV and _forced_mode != 'system'):
       
   230         mode = 'user'
   238         mode = 'user'
   231         CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes')))
   239         _CUBES_DIR = join(CW_SOFTWARE_ROOT, '../cubes')
   232     else:
   240     else:
   233         if _forced_mode == 'user':
   241         mode = _forced_mode or 'system'
   234             mode = 'user'
   242         _CUBES_DIR = join(_INSTALL_PREFIX, 'cubes')
   235         else:
   243 
   236             mode = 'system'
   244     CUBES_DIR = env_path('CW_CUBES_DIR', _CUBES_DIR, 'cubes', checkexists=False)
   237         CUBES_DIR = '/usr/share/cubicweb/cubes/'
   245     CUBES_PATH = os.environ.get('CW_CUBES_PATH', '').split(os.pathsep)
   238 
   246 
   239     options = (
   247     options = (
   240        ('log-threshold',
   248        ('log-threshold',
   241          {'type' : 'string', # XXX use a dedicated type?
   249          {'type' : 'string', # XXX use a dedicated type?
   242           'default': 'WARNING',
   250           'default': 'WARNING',
   294 this option is set to yes",
   302 this option is set to yes",
   295           'group': 'email', 'inputlevel': 2,
   303           'group': 'email', 'inputlevel': 2,
   296           }),
   304           }),
   297         )
   305         )
   298     # static and class methods used to get instance independant resources ##
   306     # static and class methods used to get instance independant resources ##
   299 
       
   300     @staticmethod
   307     @staticmethod
   301     def cubicweb_version():
   308     def cubicweb_version():
   302         """return installed cubicweb version"""
   309         """return installed cubicweb version"""
   303         from logilab.common.changelog import Version
   310         from logilab.common.changelog import Version
   304         from cubicweb import __pkginfo__
   311         from cubicweb import __pkginfo__
   341 
   348 
   342     @classmethod
   349     @classmethod
   343     def cubes_search_path(cls):
   350     def cubes_search_path(cls):
   344         """return the path of directories where cubes should be searched"""
   351         """return the path of directories where cubes should be searched"""
   345         path = []
   352         path = []
   346         try:
   353         for directory in cls.CUBES_PATH:
   347             for directory in os.environ['CW_CUBES_PATH'].split(os.pathsep):
   354             directory = abspath(normpath(directory))
   348                 directory = abspath(normpath(directory))
   355             if exists(directory) and not directory in path:
   349                 if exists(directory) and not directory in path:
   356                 path.append(directory)
   350                     path.append(directory)
       
   351         except KeyError:
       
   352             pass
       
   353         if not cls.CUBES_DIR in path and exists(cls.CUBES_DIR):
   357         if not cls.CUBES_DIR in path and exists(cls.CUBES_DIR):
   354             path.append(cls.CUBES_DIR)
   358             path.append(cls.CUBES_DIR)
   355         return path
   359         return path
   356 
   360 
   357     @classproperty
   361     @classproperty
   363         return extrapath
   367         return extrapath
   364 
   368 
   365     @classmethod
   369     @classmethod
   366     def cube_dir(cls, cube):
   370     def cube_dir(cls, cube):
   367         """return the cube directory for the given cube id,
   371         """return the cube directory for the given cube id,
   368         raise ConfigurationError if it doesn't exists
   372         raise `ConfigurationError` if it doesn't exists
   369         """
   373         """
   370         for directory in cls.cubes_search_path():
   374         for directory in cls.cubes_search_path():
   371             cubedir = join(directory, cube)
   375             cubedir = join(directory, cube)
   372             if exists(cubedir):
   376             if exists(cubedir):
   373                 return cubedir
   377                 return cubedir
   381     @classmethod
   385     @classmethod
   382     def cube_pkginfo(cls, cube):
   386     def cube_pkginfo(cls, cube):
   383         """return the information module for the given cube"""
   387         """return the information module for the given cube"""
   384         cube = CW_MIGRATION_MAP.get(cube, cube)
   388         cube = CW_MIGRATION_MAP.get(cube, cube)
   385         try:
   389         try:
   386             return getattr(__import__('cubes.%s.__pkginfo__' % cube), cube).__pkginfo__
   390             parent = __import__('cubes.%s.__pkginfo__' % cube)
       
   391             return getattr(parent, cube).__pkginfo__
   387         except Exception, ex:
   392         except Exception, ex:
   388             raise ConfigurationError('unable to find packaging information for '
   393             raise ConfigurationError(
   389                                      'cube %s (%s: %s)' % (cube, ex.__class__.__name__, ex))
   394                 'unable to find packaging information for cube %s (%s: %s)'
       
   395                 % (cube, ex.__class__.__name__, ex))
   390 
   396 
   391     @classmethod
   397     @classmethod
   392     def cube_version(cls, cube):
   398     def cube_version(cls, cube):
   393         """return the version of the cube located in the given directory
   399         """return the version of the cube located in the given directory
   394         """
   400         """
   586             pass
   592             pass
   587         else:
   593         else:
   588             cw_rest_init()
   594             cw_rest_init()
   589 
   595 
   590     def adjust_sys_path(self):
   596     def adjust_sys_path(self):
       
   597         # overriden in CubicWebConfiguration
   591         self.cls_adjust_sys_path()
   598         self.cls_adjust_sys_path()
   592 
   599 
   593     def init_log(self, logthreshold=None, debug=False,
   600     def init_log(self, logthreshold=None, debug=False,
   594                  logfile=None, syslog=False):
   601                  logfile=None, syslog=False):
   595         """init the log service"""
   602         """init the log service"""
   635         """return the instance identifier, useful for option which need this
   642         """return the instance identifier, useful for option which need this
   636         as default value
   643         as default value
   637         """
   644         """
   638         return None
   645         return None
   639 
   646 
       
   647 
   640 class CubicWebConfiguration(CubicWebNoAppConfiguration):
   648 class CubicWebConfiguration(CubicWebNoAppConfiguration):
   641     """base class for cubicweb server and web configurations"""
   649     """base class for cubicweb server and web configurations"""
   642 
   650 
   643     INSTANCES_DATA_DIR = None
   651     if CubicWebNoAppConfiguration.mode == 'user':
       
   652         _INSTANCES_DIR = expanduser('~/etc/cubicweb.d/')
       
   653     else: #mode = 'system'
       
   654         if _INSTALL_PREFIX == '/usr':
       
   655             _INSTANCES_DIR = '/etc/cubicweb.d/'
       
   656         else:
       
   657             _INSTANCES_DIR = join(_INSTALL_PREFIX, 'etc', 'cubicweb.d')
       
   658 
   644     if os.environ.get('APYCOT_ROOT'):
   659     if os.environ.get('APYCOT_ROOT'):
   645         root = os.environ['APYCOT_ROOT']
   660         _cubes_init = join(CubicWebNoAppConfiguration.CUBES_DIR, '__init__.py')
   646         REGISTRY_DIR = '%s/etc/cubicweb.d/' % root
   661         if not exists(_cubes_init):
   647         if not exists(REGISTRY_DIR):
   662             file(join(_cubes_init), 'w').close()
   648             os.makedirs(REGISTRY_DIR)
   663         if not exists(_INSTANCES_DIR):
   649         RUNTIME_DIR = tempfile.gettempdir()
   664             os.makedirs(_INSTANCES_DIR)
   650         # allow to test cubes within apycot using cubicweb not installed by
       
   651         # apycot
       
   652         if __file__.startswith(os.environ['APYCOT_ROOT']):
       
   653             MIGRATION_DIR = '%s/local/share/cubicweb/migration/' % root
       
   654         else:
       
   655             MIGRATION_DIR = '/usr/share/cubicweb/migration/'
       
   656     else:
       
   657         if CubicWebNoAppConfiguration.mode == 'user':
       
   658             REGISTRY_DIR = expanduser('~/etc/cubicweb.d/')
       
   659             RUNTIME_DIR = tempfile.gettempdir()
       
   660             INSTANCES_DATA_DIR = REGISTRY_DIR
       
   661         else: #mode = 'system'
       
   662             REGISTRY_DIR = '/etc/cubicweb.d/'
       
   663             RUNTIME_DIR = '/var/run/cubicweb/'
       
   664             INSTANCES_DATA_DIR = '/var/lib/cubicweb/instances/'
       
   665         if CWDEV:
       
   666             MIGRATION_DIR = join(CW_SOFTWARE_ROOT, 'misc', 'migration')
       
   667         else:
       
   668             MIGRATION_DIR = '/usr/share/cubicweb/migration/'
       
   669 
   665 
   670     # for some commands (creation...) we don't want to initialize gettext
   666     # for some commands (creation...) we don't want to initialize gettext
   671     set_language = True
   667     set_language = True
   672     # set this to true to avoid false error message while creating an instance
   668     # set this to true to avoid false error message while creating an instance
   673     creating = False
   669     creating = False
   709           'group': 'email', 'inputlevel': 1,
   705           'group': 'email', 'inputlevel': 1,
   710           }),
   706           }),
   711         )
   707         )
   712 
   708 
   713     @classmethod
   709     @classmethod
   714     def runtime_dir(cls):
   710     def instances_dir(cls):
   715         """run time directory for pid file..."""
       
   716         return env_path('CW_RUNTIME_DIR', cls.RUNTIME_DIR, 'run time')
       
   717 
       
   718     @classmethod
       
   719     def registry_dir(cls):
       
   720         """return the control directory"""
   711         """return the control directory"""
   721         return env_path('CW_INSTANCES_DIR', cls.REGISTRY_DIR, 'registry')
   712         return env_path('CW_INSTANCES_DIR', cls._INSTANCES_DIR, 'registry')
   722 
       
   723     @classmethod
       
   724     def instance_data_dir(cls):
       
   725         """return the instance data directory"""
       
   726         return env_path('CW_INSTANCES_DATA_DIR', cls.INSTANCES_DATA_DIR,
       
   727                         'additional data')
       
   728 
   713 
   729     @classmethod
   714     @classmethod
   730     def migration_scripts_dir(cls):
   715     def migration_scripts_dir(cls):
   731         """cubicweb migration scripts directory"""
   716         """cubicweb migration scripts directory"""
   732         return env_path('CW_MIGRATION_DIR', cls.MIGRATION_DIR, 'migration')
   717         if CWDEV:
       
   718             return join(CW_SOFTWARE_ROOT, 'misc', 'migration')
       
   719         mdir = join(_INSTALL_PREFIX, 'share', 'cubicweb', 'migration')
       
   720         if not exists(path):
       
   721             raise ConfigurationError('migration path %s doesn\'t exist' % mdir)
       
   722         return mdir
   733 
   723 
   734     @classmethod
   724     @classmethod
   735     def config_for(cls, appid, config=None):
   725     def config_for(cls, appid, config=None):
   736         """return a configuration instance for the given instance identifier
   726         """return a configuration instance for the given instance identifier
   737         """
   727         """
   750     @classmethod
   740     @classmethod
   751     def instance_home(cls, appid):
   741     def instance_home(cls, appid):
   752         """return the home directory of the instance with the given
   742         """return the home directory of the instance with the given
   753         instance id
   743         instance id
   754         """
   744         """
   755         home = join(cls.registry_dir(), appid)
   745         home = join(cls.instances_dir(), appid)
   756         if not exists(home):
   746         if not exists(home):
   757             raise ConfigurationError('no such instance %s (check it exists with "cubicweb-ctl list")' % appid)
   747             raise ConfigurationError('no such instance %s (check it exists with'
       
   748                                      ' "cubicweb-ctl list")' % appid)
   758         return home
   749         return home
   759 
   750 
   760     MODES = ('common', 'repository', 'Any', 'web')
   751     MODES = ('common', 'repository', 'Any', 'web')
   761     MCOMPAT = {'all-in-one': MODES,
   752     MCOMPAT = {'all-in-one': MODES,
   762                'repository': ('common', 'repository', 'Any'),
   753                'repository': ('common', 'repository', 'Any'),
   775         return self.appid
   766         return self.appid
   776 
   767 
   777     def default_log_file(self):
   768     def default_log_file(self):
   778         """return default path to the log file of the instance'server"""
   769         """return default path to the log file of the instance'server"""
   779         if self.mode == 'user':
   770         if self.mode == 'user':
   780             basepath = join(tempfile.gettempdir(), '%s-%s' % (basename(self.appid), self.name))
   771             import tempfile
       
   772             basepath = join(tempfile.gettempdir(), '%s-%s' % (
       
   773                 basename(self.appid), self.name))
   781             path = basepath + '.log'
   774             path = basepath + '.log'
   782             i = 1
   775             i = 1
   783             while exists(path) and i < 100: # arbitrary limit to avoid infinite loop
   776             while exists(path) and i < 100: # arbitrary limit to avoid infinite loop
   784                 try:
   777                 try:
   785                     file(path, 'a')
   778                     file(path, 'a')
   790             return path
   783             return path
   791         return '/var/log/cubicweb/%s-%s.log' % (self.appid, self.name)
   784         return '/var/log/cubicweb/%s-%s.log' % (self.appid, self.name)
   792 
   785 
   793     def default_pid_file(self):
   786     def default_pid_file(self):
   794         """return default path to the pid file of the instance'server"""
   787         """return default path to the pid file of the instance'server"""
   795         return join(self.runtime_dir(), '%s-%s.pid' % (self.appid, self.name))
   788         if self.mode == 'system':
       
   789             # XXX not under _INSTALL_PREFIX, right?
       
   790             rtdir = env_path('CW_RUNTIME_DIR', '/var/run/cubicweb/', 'run time')
       
   791         else:
       
   792             import tempfile
       
   793             rtdir = env_path('CW_RUNTIME_DIR', tempfile.gettempdir(), 'run time')
       
   794         return join(rtdir, '%s-%s.pid' % (self.appid, self.name))
   796 
   795 
   797     # instance methods used to get instance specific resources #############
   796     # instance methods used to get instance specific resources #############
   798 
   797 
   799     def __init__(self, appid):
   798     def __init__(self, appid):
   800         self.appid = appid
   799         self.appid = appid
   810         if self.apphome and not self.apphome in sys.path:
   809         if self.apphome and not self.apphome in sys.path:
   811             sys.path.insert(0, self.apphome)
   810             sys.path.insert(0, self.apphome)
   812 
   811 
   813     @property
   812     @property
   814     def apphome(self):
   813     def apphome(self):
   815         return join(self.registry_dir(), self.appid)
   814         return join(self.instances_dir(), self.appid)
   816 
   815 
   817     @property
   816     @property
   818     def appdatahome(self):
   817     def appdatahome(self):
   819         return join(self.instance_data_dir(), self.appid)
   818         if self.mode == 'system':
       
   819             # XXX not under _INSTALL_PREFIX, right?
       
   820             iddir = '/var/lib/cubicweb/instances/'
       
   821         else:
       
   822             iddir = self.instances_dir()
       
   823         iddir = env_path('CW_INSTANCES_DATA_DIR', iddir, 'additional data')
       
   824         return join(iddir, self.appid)
   820 
   825 
   821     def init_cubes(self, cubes):
   826     def init_cubes(self, cubes):
   822         assert self._cubes is None, self._cubes
   827         assert self._cubes is None, self._cubes
   823         self._cubes = self.reorder_cubes(cubes)
   828         self._cubes = self.reorder_cubes(cubes)
   824         # load cubes'__init__.py file first
   829         # load cubes'__init__.py file first