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 |