|
1 """server.serverconfig definition |
|
2 |
|
3 :organization: Logilab |
|
4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
6 """ |
|
7 __docformat__ = "restructuredtext en" |
|
8 |
|
9 import os |
|
10 from os.path import join, exists |
|
11 |
|
12 from logilab.common.configuration import Method |
|
13 from logilab.common.decorators import wproperty, cached, clear_cache |
|
14 |
|
15 from cubicweb import CW_SOFTWARE_ROOT, RegistryNotFound |
|
16 from cubicweb.toolsutils import env_path, read_config |
|
17 from cubicweb.cwconfig import CubicWebConfiguration, merge_options |
|
18 |
|
19 |
|
20 class ServerConfiguration(CubicWebConfiguration): |
|
21 """standalone RQL server""" |
|
22 name = 'repository' |
|
23 if os.environ.get('APYCOT_ROOT'): |
|
24 root = os.environ['APYCOT_ROOT'] |
|
25 SCHEMAS_LIB_DIR = '%s/local/share/cubicweb/schemas/' % root |
|
26 elif CubicWebConfiguration.mode == 'dev': |
|
27 SCHEMAS_LIB_DIR = join(CW_SOFTWARE_ROOT, 'schemas') |
|
28 BACKUP_DIR = CubicWebConfiguration.RUNTIME_DIR |
|
29 else: |
|
30 SCHEMAS_LIB_DIR = '/usr/share/cubicweb/schemas/' |
|
31 BACKUP_DIR = '/var/lib/cubicweb/backup/' |
|
32 |
|
33 cubicweb_vobject_path = CubicWebConfiguration.cubicweb_vobject_path | set(['sobjects']) |
|
34 cube_vobject_path = CubicWebConfiguration.cube_vobject_path | set(['sobjects', 'hooks']) |
|
35 |
|
36 options = merge_options(( |
|
37 # ctl configuration |
|
38 ('host', |
|
39 {'type' : 'string', |
|
40 'default': None, |
|
41 'help': 'host name if not correctly detectable through gethostname', |
|
42 'group': 'main', 'inputlevel': 1, |
|
43 }), |
|
44 ('pid-file', |
|
45 {'type' : 'string', |
|
46 'default': Method('default_pid_file'), |
|
47 'help': 'repository\'s pid file', |
|
48 'group': 'main', 'inputlevel': 2, |
|
49 }), |
|
50 ('uid', |
|
51 {'type' : 'string', |
|
52 'default': None, |
|
53 'help': 'if this option is set, use the specified user to start \ |
|
54 the repository rather than the user running the command', |
|
55 'group': 'main', 'inputlevel': 0, |
|
56 }), |
|
57 ('session-time', |
|
58 {'type' : 'int', |
|
59 'default': 30*60, |
|
60 'help': 'session expiration time, default to 30 minutes', |
|
61 'group': 'main', 'inputlevel': 1, |
|
62 }), |
|
63 ('connections-pool-size', |
|
64 {'type' : 'int', |
|
65 'default': 4, |
|
66 'help': 'size of the connections pools. Each source supporting multiple \ |
|
67 connections will have this number of opened connections.', |
|
68 'group': 'main', 'inputlevel': 1, |
|
69 }), |
|
70 ('rql-cache-size', |
|
71 {'type' : 'int', |
|
72 'default': 300, |
|
73 'help': 'size of the parsed rql cache size.', |
|
74 'group': 'main', 'inputlevel': 1, |
|
75 }), |
|
76 # email configuration |
|
77 ('default-recipients-mode', |
|
78 {'type' : 'choice', |
|
79 'choices' : ('default-dest-addrs', 'users', 'none'), |
|
80 'default': 'default-dest-addrs', |
|
81 'help': 'when a notification should be sent with no specific rules \ |
|
82 to find recipients, recipients will be found according to this mode. Available \ |
|
83 modes are "default-dest-addrs" (emails specified in the configuration \ |
|
84 variable with the same name), "users" (every users which has activated \ |
|
85 account with an email set), "none" (no notification).', |
|
86 'group': 'email', 'inputlevel': 1, |
|
87 }), |
|
88 ('default-dest-addrs', |
|
89 {'type' : 'csv', |
|
90 'default': (), |
|
91 'help': 'comma separated list of email addresses that will be used \ |
|
92 as default recipient when an email is sent and the notification has no \ |
|
93 specific recipient rules.', |
|
94 'group': 'email', 'inputlevel': 1, |
|
95 }), |
|
96 ('supervising-addrs', |
|
97 {'type' : 'csv', |
|
98 'default': (), |
|
99 'help': 'comma separated list of email addresses that will be \ |
|
100 notified of every changes.', |
|
101 'group': 'email', 'inputlevel': 2, |
|
102 }), |
|
103 # pyro server.serverconfig |
|
104 ('pyro-port', |
|
105 {'type' : 'int', |
|
106 'default': None, |
|
107 'help': 'Pyro server port. If not set, it will be choosen randomly', |
|
108 'group': 'pyro-server', 'inputlevel': 2, |
|
109 }), |
|
110 ('pyro-id', # XXX reuse pyro-application-id |
|
111 {'type' : 'string', |
|
112 'default': None, |
|
113 'help': 'identifier of the repository in the pyro name server', |
|
114 'group': 'pyro-server', 'inputlevel': 2, |
|
115 }), |
|
116 ) + CubicWebConfiguration.options) |
|
117 |
|
118 # read the schema from the database |
|
119 read_application_schema = True |
|
120 bootstrap_schema = True |
|
121 |
|
122 # check user's state at login time |
|
123 consider_user_state = True |
|
124 |
|
125 # hooks registration configuration |
|
126 # all hooks should be activated during normal execution |
|
127 core_hooks = True |
|
128 usergroup_hooks = True |
|
129 schema_hooks = True |
|
130 notification_hooks = True |
|
131 security_hooks = True |
|
132 application_hooks = True |
|
133 |
|
134 # should some hooks be deactivated during [pre|post]create script execution |
|
135 free_wheel = False |
|
136 |
|
137 # list of enables sources when sources restriction is necessary |
|
138 # (eg repository initialization at least) |
|
139 _enabled_sources = None |
|
140 @wproperty |
|
141 def enabled_sources(self, sourceuris=None): |
|
142 self._enabled_sources = sourceuris |
|
143 clear_cache(self, 'sources') |
|
144 |
|
145 @classmethod |
|
146 def schemas_lib_dir(cls): |
|
147 """application schema directory""" |
|
148 return env_path('CW_SCHEMA_LIB', cls.SCHEMAS_LIB_DIR, 'schemas') |
|
149 |
|
150 @classmethod |
|
151 def backup_dir(cls): |
|
152 """backup directory where a stored db backups before migration""" |
|
153 return env_path('CW_BACKUP', cls.BACKUP_DIR, 'run time') |
|
154 |
|
155 def bootstrap_cubes(self): |
|
156 from logilab.common.textutils import get_csv |
|
157 for line in file(join(self.apphome, 'bootstrap_cubes')): |
|
158 line = line.strip() |
|
159 if not line or line.startswith('#'): |
|
160 continue |
|
161 self.init_cubes(self.expand_cubes(get_csv(line))) |
|
162 break |
|
163 else: |
|
164 # no cubes |
|
165 self.init_cubes(()) |
|
166 |
|
167 def write_bootstrap_cubes_file(self, cubes): |
|
168 stream = file(join(self.apphome, 'bootstrap_cubes'), 'w') |
|
169 stream.write('# this is a generated file only used for bootstraping\n') |
|
170 stream.write('# you should not have to edit this\n') |
|
171 stream.write('%s\n' % ','.join(cubes)) |
|
172 stream.close() |
|
173 |
|
174 def sources_file(self): |
|
175 return join(self.apphome, 'sources') |
|
176 |
|
177 # this method has to be cached since when the server is running using a |
|
178 # restricted user, this user usually don't have access to the sources |
|
179 # configuration file (#16102) |
|
180 @cached |
|
181 def sources(self): |
|
182 """return a dictionnaries containing sources definitions indexed by |
|
183 sources'uri |
|
184 """ |
|
185 allsources = read_config(self.sources_file()) |
|
186 if self._enabled_sources is None: |
|
187 return allsources |
|
188 return dict((uri, config) for uri, config in allsources.items() |
|
189 if uri in self._enabled_sources or uri == 'admin') |
|
190 |
|
191 def pyro_enabled(self): |
|
192 """pyro is always enabled in standalone repository configuration""" |
|
193 return True |
|
194 |
|
195 def load_hooks(self, vreg): |
|
196 hooks = {} |
|
197 for path in reversed([self.apphome] + self.cubes_path()): |
|
198 hooksfile = join(path, 'application_hooks.py') |
|
199 if exists(hooksfile): |
|
200 self.warning('application_hooks.py is deprecated, use dynamic ' |
|
201 'objects to register hooks (%s)', hooksfile) |
|
202 context = {} |
|
203 # Use execfile rather than `load_module_from_name` because |
|
204 # the latter gets fooled by the `sys.modules` cache when |
|
205 # loading different configurations one after the other |
|
206 # (another fix would have been to do : |
|
207 # sys.modules.pop('applications_hooks') |
|
208 # or to modify load_module_from_name so that it provides |
|
209 # a use_cache optional parameter |
|
210 execfile(hooksfile, context, context) |
|
211 for event, hooksdef in context['HOOKS'].items(): |
|
212 for ertype, hookcbs in hooksdef.items(): |
|
213 hooks.setdefault(event, {}).setdefault(ertype, []).extend(hookcbs) |
|
214 try: |
|
215 apphookdefs = vreg.registry_objects('hooks') |
|
216 except RegistryNotFound: |
|
217 return hooks |
|
218 for hookdef in apphookdefs: |
|
219 for event, ertype in hookdef.register_to(): |
|
220 if ertype == 'Any': |
|
221 ertype = '' |
|
222 cb = hookdef.make_callback(event) |
|
223 hooks.setdefault(event, {}).setdefault(ertype, []).append(cb) |
|
224 return hooks |
|
225 |
|
226 def load_schema(self, expand_cubes=False): |
|
227 from cubicweb.schema import CubicWebSchemaLoader |
|
228 if expand_cubes: |
|
229 # in case some new dependencies have been introduced, we have to |
|
230 # reinitialize cubes so the full filesystem schema is read |
|
231 origcubes = self.cubes() |
|
232 self._cubes = None |
|
233 self.init_cubes(self.expand_cubes(origcubes)) |
|
234 schema = CubicWebSchemaLoader().load(self) |
|
235 if expand_cubes: |
|
236 # restaure original value |
|
237 self._cubes = origcubes |
|
238 return schema |
|
239 |
|
240 def load_bootstrap_schema(self): |
|
241 from cubicweb.schema import BootstrapSchemaLoader |
|
242 schema = BootstrapSchemaLoader().load(self) |
|
243 schema.name = 'bootstrap' |
|
244 return schema |
|
245 |
|
246 def set_sources_mode(self, sources): |
|
247 if 'migration' in sources: |
|
248 from cubicweb.server.sources import source_adapter |
|
249 assert len(sources) == 1 |
|
250 enabled_sources = [] |
|
251 for uri, config in self.sources().iteritems(): |
|
252 if uri == 'admin': |
|
253 continue |
|
254 if source_adapter(config).connect_for_migration: |
|
255 enabled_sources.append(uri) |
|
256 else: |
|
257 print 'not connecting to source', uri, 'during migration' |
|
258 elif 'all' in sources: |
|
259 assert len(sources) == 1 |
|
260 enabled_sources= None |
|
261 else: |
|
262 known_sources = self.sources() |
|
263 for uri in sources: |
|
264 assert uri in known_sources, uri |
|
265 enabled_sources = sources |
|
266 self._enabled_sources = enabled_sources |
|
267 clear_cache(self, 'sources') |
|
268 |
|
269 def migration_handler(self, schema=None, interactive=True, |
|
270 cnx=None, repo=None, connect=True): |
|
271 """return a migration handler instance""" |
|
272 from cubicweb.server.migractions import ServerMigrationHelper |
|
273 return ServerMigrationHelper(self, schema, interactive=interactive, |
|
274 cnx=cnx, repo=repo, connect=connect, |
|
275 verbosity=getattr(self, 'verbosity', 0)) |