Browse Source

Adapting plugins to implements a multisite web interface

Yann Weber 7 years ago
parent
commit
236861083f

+ 2
- 3
lodel/plugins/multisite/loader_utils.py View File

@@ -36,6 +36,7 @@ import lodel.buildconf
36 36
 #@todo get rid of hardcoded stuff (like shortname fieldname)
37 37
 #@todo use the dyncode getter when it will be available (replaced by
38 38
 #the string SUPERDYNCODE_ACCESSOR.Lodelsite for the moment)
39
+#@return lodelsites instance name
39 40
 def main():
40 41
     #Set current context to reserved loader context
41 42
     from lodel import bootstrap
@@ -69,7 +70,6 @@ def main():
69 70
     Plugin.load_all()
70 71
     LodelHook.call_hook('lodel2_bootstraped', '__main__', None)
71 72
 
72
-
73 73
     lodelsite_leo = leapi_dyncode.Lodelsite #hardcoded leo name
74 74
     LodelContext.expose_modules(globals(), {
75 75
         'lodel.leapi.query': ['LeGetQuery'],
@@ -91,7 +91,7 @@ def main():
91 91
     else:
92 92
         logger.warning("No handled sites !")
93 93
     LodelContext.set(None)
94
-        
94
+    return lodelsites_name
95 95
     
96 96
 
97 97
 ##@brief Load a site
@@ -130,7 +130,6 @@ sux !')
130 130
     #Immediately switching to the context
131 131
     LodelContext.new(ctx_name)
132 132
     LodelContext.set(ctx_name)
133
-    print(LodelContext.get())
134 133
     os.chdir(data_path) #Now the confdir is ./$condir_basename
135 134
     #Loading settings for current site
136 135
     LodelContext.expose_modules(globals(), {

+ 11
- 1
lodel/plugins/multisite/run.py View File

@@ -12,8 +12,18 @@ import lodel.buildconf #safe even outside contexts
12 12
 """
13 13
 
14 14
 from lodel.plugins.multisite.loader_utils import main, site_load, FAST_APP_EXPOSAL_CACHE
15
+from lodel.context import LodelContext
15 16
 
16
-main() #multisite bootstraping
17
+lodelsites_name = main() #multisite bootstraping
18
+
19
+#Switching back to lodelsites context in order to trigger hooks
20
+LodelContext.set(lodelsites_name)
21
+LodelContext.expose_modules(globals(), {
22
+    'lodel.plugin.hooks': ['LodelHook']})
23
+LodelHook.call_hook('lodel2_loader_main', '__main__', None)
24
+LodelContext.set(None)
25
+#If a hook is registered in lodelsites context for lodel2_loader_main
26
+#this function never returns
17 27
 
18 28
 import code
19 29
 print("""

+ 55
- 0
lodel/plugins/multisite_web/__init__.py View File

@@ -0,0 +1,55 @@
1
+##@brief
2
+#
3
+#Lodel2 multisite uwsgi plugin
4
+
5
+from lodel.context import LodelContext, ContextError
6
+try:
7
+    LodelContext.expose_modules(globals(), {
8
+        'lodel.validator.validator': ['Validator']})
9
+
10
+    __plugin_type__ = 'ui' #Warning !!! It's a MULTISITE interface
11
+    __plugin_name__ = 'multisite_web'
12
+    __version__ = '0.0.1'
13
+    __loader__ = 'main.py'
14
+    #__plugin_deps__ = ['multisite']
15
+
16
+    CONFSPEC = {
17
+        'lodel2.server': {
18
+                'listen_address': ('127.0.0.1', Validator('dummy')),
19
+                #'listen_address': ('', Validator('ip')), #<-- not implemented
20
+                'listen_port': ( 1337, Validator('int')),
21
+                'uwsgi_workers': (8, Validator('int')),
22
+                'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')),
23
+                'virtualenv': (None, Validator('path', none_is_valid = True)),
24
+            },
25
+        #A copy from webui confspec
26
+        ##@todo automatic merging of webuig confspecs
27
+        'lodel2.webui': {
28
+            'standalone': ( 'False',
29
+                            Validator('string')),
30
+            'listen_address': ( '127.0.0.1',
31
+                                Validator('dummy')),
32
+            'listen_port': (    '9090',
33
+                                Validator('int')),
34
+            'static_url': (     'http://127.0.0.1/static/',
35
+                                Validator('regex', pattern =  r'^https?://[^/].*$')),
36
+            'virtualenv': (None,
37
+                           Validator('path', none_is_valid=True)),
38
+            'uwsgicmd': ('/usr/bin/uwsgi', Validator('dummy')),
39
+            'cookie_secret_key': ('ConfigureYourOwnCookieSecretKey', Validator('dummy')),
40
+            'cookie_session_id': ('lodel', Validator('dummy')),
41
+            'uwsgi_workers': (2, Validator('int'))
42
+        },
43
+        'lodel2.webui.sessions': {
44
+            'directory': (  '/tmp',
45
+                            Validator('path')),
46
+            'expiration': ( 900,
47
+                            Validator('int')),
48
+            'file_template': (  'lodel2_%s.sess',
49
+                                Validator('dummy')),
50
+        }
51
+    }
52
+
53
+except ContextError:
54
+    #This case append when uwsgi call the run.py directly
55
+    pass

+ 1
- 0
lodel/plugins/multisite_web/loader_utils.py View File

@@ -0,0 +1 @@
1
+../multisite/loader_utils.py

+ 50
- 0
lodel/plugins/multisite_web/main.py View File

@@ -0,0 +1,50 @@
1
+import os
2
+import shlex
3
+from lodel.context import LodelContext
4
+import lodel.buildconf
5
+
6
+LodelContext.expose_modules(globals(), {
7
+    'lodel.plugin.hooks': ['LodelHook'],
8
+})
9
+
10
+##@brief Starts uwsgi in background using settings
11
+@LodelHook('lodel2_loader_main')
12
+def uwsgi_fork(hook_name, caller, payload):
13
+    
14
+    sockfile = os.path.join(lodel.buildconf.LODEL2VARDIR, 'uwsgi_sockets/')
15
+    if not os.path.isdir(sockfile):
16
+        os.mkdir(sockfile)
17
+    sockfile = os.path.join(sockfile, 'uwsgi_lodel2_multisite.sock')
18
+    logfile = os.path.join(
19
+        lodel.buildconf.LODEL2LOGDIR, 'uwsgi_lodel2_multisite.log')
20
+    
21
+    #Switching to __loader__ context
22
+    print("PASS1")
23
+    LodelContext.set(None)
24
+    print("PASS2")
25
+    LodelContext.expose_modules(globals(), {
26
+        'lodel.settings': ['Settings']})
27
+    print("PASS3")
28
+
29
+    cmd='{uwsgi} --plugin python3 --http-socket {addr}:{port} --module \
30
+lodel.plugins.multisite_web.run --socket {sockfile} --logto {logfile} -p {uwsgiworkers}'
31
+    cmd = cmd.format(
32
+                addr = Settings.server.listen_address,
33
+                port = Settings.server.listen_port,
34
+                uwsgi= Settings.server.uwsgicmd,
35
+                sockfile=sockfile,
36
+                logfile = logfile,
37
+                uwsgiworkers = Settings.server.uwsgi_workers)
38
+    if Settings.server.virtualenv is not None:
39
+        cmd += " --virtualenv %s" % Settings.webui.virtualenv
40
+
41
+    print("PASS4")
42
+    try:
43
+        args = shlex.split(cmd)
44
+        print("Running %s" % cmd)
45
+        exit(os.execl(args[0], *args))
46
+    except Exception as e:
47
+        print("Webui plugin uwsgi execl fails cmd was '%s' error : " % cmd,
48
+            e, file=sys.stderr)
49
+        exit(1)
50
+

+ 92
- 0
lodel/plugins/multisite_web/run.py View File

@@ -0,0 +1,92 @@
1
+# -*- coding: utf-8 -*-
2
+import os
3
+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
+try:
34
+    from lodel.context import LodelContext
35
+except ImportError:
36
+    LODEL_BASE_DIR = os.path.dirname(
37
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
38
+    from lodel.context import LodelContext, ContextError
39
+
40
+import lodel.buildconf #safe even outside contexts
41
+from lodel.plugins.multisite.loader_utils import main, site_load, FAST_APP_EXPOSAL_CACHE #UNSAFE ??!!
42
+lodelsites_name = main()
43
+LodelContext.set(lodelsites_name)
44
+FAST_APP_EXPOSAL_CACHE[lodelsites_name] = LodelContext.module(
45
+    'lodel.plugins.webui.run')
46
+LodelContext.set(None)
47
+
48
+##@brief Utility function to return quickly an error
49
+def http_error(env, start_response, status = '500 internal server error', \
50
+        extra = None):
51
+    headers = [('Content-type', 'text/plain; charset=utf-8')]
52
+    start_response(status, headers)
53
+    msg = status
54
+    if extra is not None:
55
+        msg = extra
56
+    return [msg.encode('utf-8')]
57
+
58
+
59
+##@brief utility function to extract site id from an url
60
+#@param url str : 
61
+def site_id_from_url(url):
62
+    res = ''
63
+    for c in url[1:]:
64
+        if c == '/':
65
+            break
66
+        res += c
67
+    if len(res) == 0:
68
+        return None
69
+    return res
70
+
71
+##@brief This method is run in a child process by the handler
72
+def application(env, start_response):
73
+    LodelContext.set(None) #Ensure context switching
74
+    #Attempt to load a context
75
+    site_id = site_id_from_url(env['PATH_INFO'])
76
+    if site_id is None:
77
+        #It can be nice to provide a list of instances here
78
+        return http_error(env, start_response, '404 Not Found')
79
+    try:
80
+        LodelContext.set(site_id)
81
+        #We are in the good context
82
+
83
+    except ContextError as e:
84
+        print(e)
85
+        return http_error(env, start_response, '404 Not found',
86
+            "No site named '%s'" % site_id)
87
+    #Calling webui
88
+    return FAST_APP_EXPOSAL_CACHE[site_id].application(env, start_response)
89
+    #LodelContext.expose_modules(globals(), {
90
+    #    'lodel.plugins.webui.run': ['application']})
91
+    #return application(env, start_response)
92
+

+ 13
- 3
progs/slim/multisite_install_model/lodelsites.conf.d/lodel2.ini View File

@@ -3,6 +3,7 @@ debug = False
3 3
 sitename = @@@INSTANCE_NAME@@@
4 4
 datasource_connectors = dummy_datasource lodelsite_datasource
5 5
 session_handler = filesystem_session
6
+interface = multisite_web
6 7
 
7 8
 [lodel2.auth]
8 9
 login_classfield = lodelsite.shortname
@@ -30,15 +31,24 @@ identifier = dummy_datasource.default
30 31
 [lodel2.datasource.dummy_datasource.default]
31 32
 dummy =
32 33
 
33
-#[lodel2.datasources.default]
34
-#identifier = lodelsite_datasource.default
34
+#Example using a real datasource
35
+#
36
+#[lodel2.lodelsites]
37
+#sites_emfile = sites_em.pickle
38
+#lodelsites_emfile = editorial_model.pickle
39
+#
40
+#[lodel2.datasource.lodelsite_datasource.default]
41
+#db_datasource = mongodb.datasource.default
35 42
 #
36
-#[lodel2.datasources.mongo]
43
+#[lodel2.datasources.default]
37 44
 #identifier = mongodb_datasource.default
38 45
 #
39 46
 #[lodel2.datasources.dummy2]
40 47
 #identifier = mongodb_datasource.default
41 48
 #
49
+#[lodel2.datasource.dummy_datasource.default]
50
+#dummy =
51
+#
42 52
 #[lodel2.datasource.mongodb_datasource.default]
43 53
 #host = HOSTNAME
44 54
 #username = USERNAME

Loading…
Cancel
Save