|
@@ -3,15 +3,49 @@ import os
|
3
|
3
|
import os.path
|
4
|
4
|
import warnings
|
5
|
5
|
|
6
|
|
-#This file expose common function to process a wsgi request and the
|
7
|
|
-#uWSGI application callback
|
|
6
|
+##@brief File designed to be executed by an UWSGI process in order to
|
|
7
|
+#run a multisite instance process
|
|
8
|
+#
|
|
9
|
+#@warning In this module we have to be VERY carrefull about module exposure !
|
|
10
|
+#in fact here we will go through every context at least once and the globals
|
|
11
|
+#will be shared all among the module. So ANY exposed module will be accessible
|
|
12
|
+#for a short time outside its context !! A good way be protected about that
|
|
13
|
+#is to del(globals()[exposed_module_name]) after use. But it's really not
|
|
14
|
+#sure that this way of doing is a real safe protection !
|
|
15
|
+#
|
|
16
|
+#@par Expected context when called
|
|
17
|
+#- cwd has to be the lodelsites directory (the directory containing the
|
|
18
|
+#conf.d folder)
|
|
19
|
+#
|
|
20
|
+#This file is divided in two blocks :
|
|
21
|
+#
|
|
22
|
+#@par Run at load
|
|
23
|
+#The main function will be called at import.
|
|
24
|
+#This piece of code handles :
|
|
25
|
+#- loading the lodelsites site (the site that handles sites ;) )
|
|
26
|
+#- fetch the list of handled lodel sites
|
|
27
|
+#- loading the whole list of lodel sites
|
|
28
|
+#
|
|
29
|
+#@par WSGI processing
|
|
30
|
+#The other functions are here to handles WSGI request. The entry function
|
|
31
|
+#is application(), following the PEP 3333 specifications
|
8
|
32
|
|
|
33
|
+#
|
|
34
|
+# Constants declarations
|
|
35
|
+#
|
9
|
36
|
|
10
|
|
-#preloading all instances
|
11
|
|
-FAST_APP_EXPOSAL_CACHE = dict()
|
|
37
|
+##@brief basename of multisite process conf folder
|
|
38
|
+#@todo find a better place to declare it
|
|
39
|
+SERVER_CONFD = 'server.conf.d' #Should be accessible elsewhere
|
|
40
|
+##@brief basename of lodelsites site conf folder
|
|
41
|
+#@todo find a better place to declare it
|
|
42
|
+LODELSITES_CONFD = 'lodelsites.conf.d' #Should be accessible elsewhere
|
12
|
43
|
|
13
|
|
-LODEL2_INSTANCES_DIR = '.'
|
14
|
|
-EXCLUDE_DIR = {'conf.d', '__pycache__'}
|
|
44
|
+##@brief A cache allowing a fast application exposure
|
|
45
|
+#
|
|
46
|
+#This dict contains reference on interface module of each handled site in
|
|
47
|
+#order to quickly call the application (PEP 3333) function of concerned site
|
|
48
|
+FAST_APP_EXPOSAL_CACHE = dict()
|
15
|
49
|
|
16
|
50
|
try:
|
17
|
51
|
from lodel.context import LodelContext
|
|
@@ -20,71 +54,147 @@ except ImportError:
|
20
|
54
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
21
|
55
|
from lodel.context import LodelContext, ContextError
|
22
|
56
|
|
23
|
|
-LodelContext.init(LodelContext.MULTISITE)
|
24
|
|
-LodelContext.set(None) #Loading context creation
|
25
|
|
-
|
26
|
|
-#Multisite instance settings loading
|
27
|
|
-CONFDIR = os.path.join(os.getcwd(), 'conf.d')
|
28
|
|
-if not os.path.isdir(CONFDIR):
|
29
|
|
- warnings.warn('%s do not exists, default settings used' % CONFDIR)
|
30
|
|
-LodelContext.expose_modules(globals(), {
|
31
|
|
- 'lodel.settings.settings': [('Settings', 'settings')],
|
32
|
|
- 'lodel.plugins.multisite.confspecs': 'multisite_confspecs'})
|
33
|
|
-if not settings.started():
|
34
|
|
- settings('./conf.d', multisite_confspecs.LODEL2_CONFSPECS)
|
35
|
|
-
|
36
|
|
-#Fetching insrtance list from subdirectories
|
37
|
|
-lodelsites_list = [ os.path.realpath(os.path.join(LODEL2_INSTANCES_DIR,sitename))
|
38
|
|
- for sitename in os.listdir(LODEL2_INSTANCES_DIR)
|
39
|
|
- if os.path.isdir(sitename) and sitename not in EXCLUDE_DIR]
|
40
|
|
-
|
41
|
|
-#Bootstraping instances
|
42
|
|
-for lodelsite_path in lodelsites_list:
|
43
|
|
- ctx_name = LodelContext.from_path(lodelsite_path)
|
44
|
|
- #Switch to new context
|
|
57
|
+import os.path
|
|
58
|
+import lodel.buildconf #safe even outside contexts
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+##@brief Function that will run at import time
|
|
62
|
+#
|
|
63
|
+#Handles lodelsites site loading, handled site list fecth & load
|
|
64
|
+#@note called at end of file
|
|
65
|
+#
|
|
66
|
+#@todo evaluate if it is safe to assume that lodelsites_datapath = os.getcwd()
|
|
67
|
+#@todo get rid of hardcoded stuff (like shortname fieldname)
|
|
68
|
+#@todo use the dyncode getter when it will be available (replaced by
|
|
69
|
+#the string SUPERDYNCODE_ACCESSOR.Lodelsite for the moment)
|
|
70
|
+def main():
|
|
71
|
+ LodelContext.init(LodelContext.MULTISITE)
|
|
72
|
+ #Set current context to reserved loader context
|
|
73
|
+ LodelContext.set(None)
|
|
74
|
+ LodelContext.expose_modules(globals(), {
|
|
75
|
+ 'lodel.logger': 'logger',
|
|
76
|
+ 'lodel.exceptions': ['LodelFatalError'],
|
|
77
|
+ })
|
|
78
|
+
|
|
79
|
+ CONFDIR = os.path.join(os.getcwd(), SERVER_CONFD)
|
|
80
|
+ if not os.path.isdir(CONFDIR):
|
|
81
|
+ logger.critical('Multisite process bootstraping fails : unable to \
|
|
82
|
+find the %s folder' % SERVER_CONFD)
|
|
83
|
+
|
|
84
|
+ #Settings bootstraping for mutlisite process
|
|
85
|
+ LodelContext.expose_modules(globals(), {
|
|
86
|
+ 'lodel.settings.settings': [('Settings', 'settings')],
|
|
87
|
+ 'lodel.plugins.multisite.confspecs': 'multisite_confspecs'})
|
|
88
|
+ settings(CONFDIR, mutisite_confspecs.LODEL2_CONFSPECS)
|
|
89
|
+ #Loading settings
|
|
90
|
+ del(globals()['settings']) #useless but may be safer
|
|
91
|
+ #Exposing "real" settings object in loader context
|
|
92
|
+ LodelContext.expose_modules(globals(), {
|
|
93
|
+ 'lodel.settings': ['Settings']})
|
|
94
|
+ #Fetching lodelsites informations
|
|
95
|
+ lodelsites_name = Settings.sites_handler_name
|
|
96
|
+ #Following path construction is kind of dirty ! We should be able
|
|
97
|
+ #to assume that the lodelsites_datapath == os.getcwd()....
|
|
98
|
+ lodelsites_datapath = os.path.join(LODEL2VARDIR, lodelsites_name)
|
|
99
|
+ #loading lodelsites site
|
|
100
|
+ load_site(lodelsites_datapath, LODELSITES_CONFD)
|
|
101
|
+
|
|
102
|
+ #Fetching handled sites list
|
|
103
|
+ #WARNING ! Here we assert that context name == basename(lodelsites_datapath)
|
|
104
|
+ LodelContext.set(lodelsites_name)
|
|
105
|
+ #in lodelsites context
|
|
106
|
+ Lodelsite_leo = SUPERDYNCODE_ACCESSOR.Lodelsite #hardcoded leo name
|
|
107
|
+ LodelContext.expose_modules(globals(), {
|
|
108
|
+ 'lodel.leapi.query': ['LeGetQuery'],
|
|
109
|
+ })
|
|
110
|
+ #the line bellow you will find another harcoded thing : the shortname
|
|
111
|
+ #fieldname for a lodelsite
|
|
112
|
+ handled_sites = LeGetQuery(lodelsite_leo, query_filters = [],
|
|
113
|
+ field_list = ['shortname'])
|
|
114
|
+ #Now that we have the handled sitenames list we can go back to
|
|
115
|
+ #loader context and clean it
|
|
116
|
+ LodelContext.set(None)
|
|
117
|
+ for mname in ['LeGetQuery', 'Settings']:
|
|
118
|
+ del(globals()[mname])
|
|
119
|
+ #Loading handled sites
|
|
120
|
+ for handled_sitename in [s['shortname'] for s in handled_sites]:
|
|
121
|
+ datapath = os.path.join(lodelsites_datapath, handled_sitename)
|
|
122
|
+ site_load(datapath) #using default conf.d configuration dirname
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+##@brief Load a site
|
|
126
|
+#
|
|
127
|
+#Apply a common (as MONOSITE) loading process to a site :
|
|
128
|
+#1. Conf preload
|
|
129
|
+#2. Plugins preload
|
|
130
|
+#3. Conf loading
|
|
131
|
+#4. starting plugins & hooks
|
|
132
|
+#@warning At this point we need a uniq identifier for the site (using it
|
|
133
|
+#as key for contexts & FAST_APP_EXPOSAL_CACHE). To achieve this we use
|
|
134
|
+#the data_path basename. It should works for handled sites and for the
|
|
135
|
+#lodelsites instance
|
|
136
|
+#@param data_path str : path to the datas directory (containing the confdir)
|
|
137
|
+#@param confdir_basename str : the basename of the site confdir
|
|
138
|
+#
|
|
139
|
+#@todo For now the interface plugin name for sites is hardcoded (set to
|
|
140
|
+#webui). It HAS TO be loaded from settings. But it is a bit complicated,
|
|
141
|
+#we have to get the plugin's module name abstracted from context :
|
|
142
|
+#lodel.something but if we ask directly to Plugin class the module name
|
|
143
|
+#it will return something like : lodelsites.sitename.something...
|
|
144
|
+#
|
|
145
|
+#@todo there is a quick & dirty workarround with comments saying that it
|
|
146
|
+#avoid context escape via hooks. We have to understand why and how and then
|
|
147
|
+#replace the workarround by a real solution !
|
|
148
|
+def site_load(data_path, confdir_basename = 'conf.d'):
|
|
149
|
+ #args check
|
|
150
|
+ if confdir_basename != os.path.basename(confdir_basename):
|
|
151
|
+ LodelFatalError('Bad argument given to site_load(). This really \
|
|
152
|
+sux !')
|
|
153
|
+ #Determining uniq sitename from data_path
|
|
154
|
+ data_path = data_path.rstrip('/') #else basename returns ''
|
|
155
|
+ ctx_name = os.path.basename(data_path)
|
|
156
|
+ #Immediately switching to the context
|
45
|
157
|
LodelContext.set(ctx_name)
|
46
|
|
- os.chdir(lodelsite_path)
|
47
|
|
- # Loading settings
|
|
158
|
+ os.chdir(data_path) #Now the confdir is ./$condir_basename
|
|
159
|
+ #Loading settings for current site
|
48
|
160
|
LodelContext.expose_modules(globals(), {
|
49
|
|
- 'lodel.settings.settings': [('Settings', 'settings')]})
|
50
|
|
- if not settings.started():
|
51
|
|
- settings('./conf.d')
|
52
|
|
- LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']})
|
53
|
|
-
|
54
|
|
- # Loading hooks & plugins
|
|
161
|
+ 'lodel.settings.settings': [('Settings', 'settings_preloader')]})
|
|
162
|
+ if settings_preloader.started():
|
|
163
|
+ msg = 'Settings seems to be allready started for "%s". \
|
|
164
|
+This should not append !' % ctx_name
|
|
165
|
+ #switch back to loader context in order to log & raise
|
|
166
|
+ LodelContext.set(None)
|
|
167
|
+ logger.critical(msg)
|
|
168
|
+ raise LodelFatalError(msg)
|
|
169
|
+ settings(os.path.join('./', confdir_basename)
|
|
170
|
+ #
|
|
171
|
+ #Loading hooks & plugins
|
|
172
|
+ #
|
55
|
173
|
LodelContext.expose_modules(globals(), {
|
56
|
|
- 'lodel.plugin': ['LodelHook'],
|
|
174
|
+ 'lodel.plugin': ['Plugin', 'LodelHook'],
|
|
175
|
+ 'lodel.logger': 'logger',
|
57
|
176
|
'lodel.plugin.core_hooks': 'core_hooks',
|
58
|
177
|
'lodel.plugin.core_scripts': 'core_scripts'
|
59
|
178
|
})
|
60
|
|
-
|
61
|
|
- #Load plugins
|
62
|
|
- LodelContext.expose_modules(globals(), {
|
63
|
|
- 'lodel.logger': 'logger',
|
64
|
|
- 'lodel.plugin': ['Plugin']})
|
65
|
|
- logger.debug("Loader.start() called")
|
66
|
|
- Plugin.load_all()
|
67
|
|
- #Import & expose dyncode
|
68
|
|
- LodelContext.expose_dyncode(globals())
|
69
|
|
- #Next hook triggers dyncode datasource instanciations
|
|
179
|
+ Plugin.load_all() #Then all plugins & hooks are loaded
|
|
180
|
+ #triggering dyncode datasource instanciations
|
70
|
181
|
LodelHook.call_hook('lodel2_plugins_loaded', '__main__', None)
|
71
|
|
- #Next hook triggers call of interface's main loop
|
|
182
|
+ #triggering boostrapped hook
|
72
|
183
|
LodelHook.call_hook('lodel2_bootstraped', '__main__', None)
|
73
|
|
- #FAST_APP_EXPOSAL_CACHE populate
|
|
184
|
+ #Populating FAST_APP_EXPOSAL_CACHE
|
|
185
|
+ #
|
|
186
|
+ #WARNING !!!! Hardcoded interface name ! Here we have to find the
|
|
187
|
+ #interface plugin name in order to populate the cache properly
|
74
|
188
|
FAST_APP_EXPOSAL_CACHE[ctx_name] = LodelContext.module(
|
75
|
|
- 'lodel.plugins.webui.run')
|
76
|
|
- LodelContext
|
|
189
|
+ 'lodel.plugins.webui.run')
|
77
|
190
|
#a dirty & quick attempt to fix context unwanted exite via
|
78
|
191
|
#hooks
|
79
|
192
|
for name in ( 'LodelHook', 'core_hooks', 'core_scripts',
|
80
|
193
|
'Settings', 'settings', 'logger', 'Plugin'):
|
81
|
194
|
del(globals()[name])
|
82
|
|
- #switch back to loader context
|
|
195
|
+ #site fully loaded, switching back to loader context
|
83
|
196
|
LodelContext.set(None)
|
84
|
|
-
|
85
|
|
-#
|
86
|
|
-# From here lodel2 multisite instances are loaded and ready to run
|
87
|
|
-#
|
|
197
|
+ #lodel2 multisite instances are loaded and ready to run
|
88
|
198
|
|
89
|
199
|
|
90
|
200
|
##@brief Utility function to return quickly an error
|
|
@@ -131,3 +241,5 @@ def application(env, start_response):
|
131
|
241
|
# 'lodel.plugins.webui.run': ['application']})
|
132
|
242
|
#return application(env, start_response)
|
133
|
243
|
|
|
244
|
+#calling the main function
|
|
245
|
+main()
|