Bläddra i källkod

[broken state] first implementation of multisite loader

It's only the begining, I have to make a commit in order to be able
to fix the settings on master
Yann Weber 8 år sedan
förälder
incheckning
97ab8b8ea3
3 ändrade filer med 36 tillägg och 274 borttagningar
  1. 0
    8
      lodel/plugins/multisite/confspecs.py
  2. 25
    34
      lodel/plugins/multisite/loader.py
  3. 11
    232
      lodel/plugins/multisite/run.py

+ 0
- 8
lodel/plugins/multisite/confspecs.py Visa fil

@@ -19,14 +19,6 @@ LODEL2_CONFSPECS = {
19 19
         'sites_emtranslator': ('picklefile',
20 20
             SettingValidator('string', none_is_valid = False)), #Bad validator
21 21
     },
22
-    'lodel2.server': {
23
-        'listen_address': ('127.0.0.1', SettingValidator('dummy')),
24
-        #'listen_address': ('', SettingValidator('ip')), #<-- not implemented
25
-        'listen_port': ( 1337, SettingValidator('int')),
26
-        'uwsgi_workers': (8, SettingValidator('int')),
27
-        'uwsgicmd': ('/usr/bin/uwsgi', SettingValidator('dummy')),
28
-        'virtualenv': (None, SettingValidator('path', none_is_valid = True)),
29
-    },
30 22
     'lodel2.logging.*' : {
31 23
         'level': (  'ERROR',
32 24
                     SettingValidator('loglevel')),

+ 25
- 34
lodel/plugins/multisite/loader.py Visa fil

@@ -34,49 +34,40 @@ from lodel import buildconf
34 34
 LodelContext.init(LodelContext.MULTISITE)
35 35
 LodelContext.set(None) #Loading context creation
36 36
 #Multisite instance settings loading
37
-CONFDIR = os.path.join(os.getcwd(), 'conf.d')
37
+CONFDIR = os.path.join(os.getcwd(), 'server_conf.d')
38 38
 if not os.path.isdir(CONFDIR):
39 39
     warnings.warn('%s do not exists, default settings used' % CONFDIR)
40 40
 LodelContext.expose_modules(globals(), {
41 41
     'lodel.settings.settings': [('Settings', 'settings')],
42 42
     'lodel.plugins.multisite.confspecs': 'multisite_confspecs'})
43 43
 if not settings.started():
44
-    settings('./conf.d', multisite_confspecs.LODEL2_CONFSPECS)
44
+    settings('./server_conf.d', multisite_confspecs.LODEL2_CONFSPECS)
45 45
 
46 46
 LodelContext.expose_modules(globals(), {
47 47
     'lodel.settings': ['Settings']})
48 48
 
49
-##@brief Starts uwsgi in background using settings
50
-def uwsgi_fork():
51
-    
52
-    sockfile = os.path.join(buildconf.LODEL2VARDIR, 'uwsgi_sockets/')
53
-    if not os.path.isdir(sockfile):
54
-        os.mkdir(sockfile)
55
-    sockfile = os.path.join(sockfile, 'uwsgi_lodel2_multisite.sock')
56
-    logfile = os.path.join(
57
-        buildconf.LODEL2LOGDIR, 'uwsgi_lodel2_multisite.log')
58
-        
59
-    cmd='{uwsgi} --plugin python3 --http-socket {addr}:{port} --module \
60
-lodel.plugins.multisite.run --socket {sockfile} --logto {logfile} -p {uwsgiworkers}'
61
-    cmd = cmd.format(
62
-                addr = Settings.server.listen_address,
63
-                port = Settings.server.listen_port,
64
-                uwsgi= Settings.server.uwsgicmd,
65
-                sockfile=sockfile,
66
-                logfile = logfile,
67
-                uwsgiworkers = Settings.server.uwsgi_workers)
68
-    if Settings.server.virtualenv is not None:
69
-        cmd += " --virtualenv %s" % Settings.webui.virtualenv
49
+print(Settings.lodelsites)
70 50
 
71
-    try:
72
-        args = shlex.split(cmd)
73
-        print("Running %s" % cmd)
74
-        exit(os.execl(args[0], *args))
75
-    except Exception as e:
76
-        print("Webui plugin uwsgi execl fails cmd was '%s' error : " % cmd,
77
-            e, file=sys.stderr)
78
-        exit(1)
51
+LodelContext.expose_modules(globals(), {
52
+    'lodel.plugin.hooks': ['LodelHook'],
53
+})
54
+
55
+#If an interface that execl to run was loaded it will be run by following
56
+#hook
57
+LodelHook.call_hook('multisite_execl_interface', '__main__', None)
79 58
 
80
-if __name__ == '__main__':
81
-    uwsgi_fork()
82
-        
59
+#Nothing appened, running default ipython interface
60
+#The interface as to be run by execl to go out of this partial context
61
+PYTHON_EXC = '/usr/bin/python3'
62
+RUNNER_FILE = os.path.join(
63
+    os.path.dirname(os.path.realpath(__file__)),
64
+    'run.py')
65
+cmd = '%s "%s"' % (PYTHON_EXC, RUNNER_FILE)
66
+try:
67
+    args = shlex.split(cmd)
68
+    print("\n\nEND LOADER MULTISITE, execl\n\n")
69
+    exit(os.execl(args[0], *args))
70
+except Exception as e:
71
+    print("Multisite std interface execl fails. Command was : '%s' error \
72
+: %s" % (cmd, e), file=sys.stderr)
73
+    exit(1)

+ 11
- 232
lodel/plugins/multisite/run.py Visa fil

@@ -1,51 +1,4 @@
1
-# -*- coding: utf-8 -*-
2
-import os
3 1
 import os.path
4
-import warnings
5
-
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
32
-
33
-#
34
-#   Constants declarations
35
-#
36
-
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
43
-
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()
49 2
 
50 3
 try:
51 4
     from lodel.context import LodelContext
@@ -54,192 +7,18 @@ except ImportError:
54 7
         os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
55 8
     from lodel.context import LodelContext, ContextError
56 9
 
57
-import os.path
58
-import lodel.buildconf #safe even outside contexts
59
-
10
+LodelContext.init(LodelContext.MULTISITE)
60 11
 
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
157
-    LodelContext.set(ctx_name)
158
-    os.chdir(data_path) #Now the confdir is ./$condir_basename
159
-    #Loading settings for current site
160
-    LodelContext.expose_modules(globals(), {
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
-    #
173
-    LodelContext.expose_modules(globals(), {
174
-        'lodel.plugin': ['Plugin', 'LodelHook'],
175
-        'lodel.logger': 'logger',
176
-        'lodel.plugin.core_hooks': 'core_hooks',
177
-        'lodel.plugin.core_scripts': 'core_scripts'
178
-    })
179
-    Plugin.load_all() #Then all plugins & hooks are loaded
180
-    #triggering dyncode datasource instanciations
181
-    LodelHook.call_hook('lodel2_plugins_loaded', '__main__', None)
182
-    #triggering boostrapped hook
183
-    LodelHook.call_hook('lodel2_bootstraped', '__main__', None)
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
188
-    FAST_APP_EXPOSAL_CACHE[ctx_name] = LodelContext.module(
189
-        'lodel.plugins.webui.run')
190
-    #a dirty & quick attempt to fix context unwanted exite via
191
-    #hooks
192
-    for name in ( 'LodelHook', 'core_hooks', 'core_scripts',
193
-            'Settings', 'settings', 'logger', 'Plugin'):
194
-        del(globals()[name])
195
-    #site fully loaded, switching back to loader context
196
-    LodelContext.set(None)
197
-    #lodel2 multisite instances are loaded and ready to run
198
-
199
-
200
-##@brief Utility function to return quickly an error
201
-def http_error(env, start_response, status = '500 internal server error', \
202
-        extra = None):
203
-    headers = [('Content-type', 'text/plain; charset=utf-8')]
204
-    start_response(status, headers)
205
-    msg = status
206
-    if extra is not None:
207
-        msg = extra
208
-    return [msg.encode('utf-8')]
209
-
210
-
211
-##@brief utility function to extract site id from an url
212
-#@param url str : 
213
-def site_id_from_url(url):
214
-    res = ''
215
-    for c in url[1:]:
216
-        if c == '/':
217
-            break
218
-        res += c
219
-    if len(res) == 0:
220
-        return None
221
-    return res
12
+import lodel.buildconf #safe even outside contexts
13
+from lodel.plugins.multisite.loader_utils import main, site_load, FAST_APP_EXPOSAL_CACHE
222 14
 
223
-##@brief This method is run in a child process by the handler
224
-def application(env, start_response):
225
-    #Attempt to load a context
226
-    site_id = site_id_from_url(env['PATH_INFO'])
227
-    if site_id is None:
228
-        #It can be nice to provide a list of instances here
229
-        return http_error(env, start_response, '404 Not Found')
230
-    try:
231
-        LodelContext.set(site_id)
232
-        #We are in the good context
15
+main() #multisite bootstraping
233 16
 
234
-    except ContextError as e:
235
-        print(e)
236
-        return http_error(env, start_response, '404 Not found',
237
-            "No site named '%s'" % site_id)
238
-    #Calling webui
239
-    return FAST_APP_EXPOSAL_CACHE[site_id].application(env, start_response)
240
-    #LodelContext.expose_modules(globals(), {
241
-    #    'lodel.plugins.webui.run': ['application']})
242
-    #return application(env, start_response)
17
+import code
18
+print("""
19
+    Running interactive python in Lodel2 multisite instance env.
243 20
 
244
-#calling the main function
245
-main()
21
+    Note :  you are in LOAD_CTX environnment.
22
+        use lodel.context.Lodelcontext.set(CONTEXT_NAME) to switch
23
+""")
24
+code.interact(local=locals())

Loading…
Avbryt
Spara