[IMP] wsgi: added new (still to be refined) routes for XMLRPC dispatching.
bzr revid: vmt@openerp.com-20110901130450-ub41ey4s0060vucg
This commit is contained in:
parent
efd281a21f
commit
3c8571ead0
|
@ -3,7 +3,7 @@ import openerp
|
||||||
bind = '127.0.0.1:8069'
|
bind = '127.0.0.1:8069'
|
||||||
pidfile = '.gunicorn.pid'
|
pidfile = '.gunicorn.pid'
|
||||||
# This is the big TODO: safely use more than a single worker.
|
# This is the big TODO: safely use more than a single worker.
|
||||||
workers = 1
|
workers = 2
|
||||||
# Some application-wide initialization is needed.
|
# Some application-wide initialization is needed.
|
||||||
on_starting = openerp.wsgi.on_starting
|
on_starting = openerp.wsgi.on_starting
|
||||||
when_ready = openerp.wsgi.when_ready
|
when_ready = openerp.wsgi.when_ready
|
||||||
|
|
|
@ -96,7 +96,8 @@ class db(netsvc.ExportService):
|
||||||
def dispatch(self, method, auth, params):
|
def dispatch(self, method, auth, params):
|
||||||
if method in [ 'create', 'get_progress', 'drop', 'dump',
|
if method in [ 'create', 'get_progress', 'drop', 'dump',
|
||||||
'restore', 'rename',
|
'restore', 'rename',
|
||||||
'change_admin_password', 'migrate_databases' ]:
|
'change_admin_password', 'migrate_databases',
|
||||||
|
'create_database' ]:
|
||||||
passwd = params[0]
|
passwd = params[0]
|
||||||
params = params[1:]
|
params = params[1:]
|
||||||
security.check_super(passwd)
|
security.check_super(passwd)
|
||||||
|
@ -135,6 +136,20 @@ class db(netsvc.ExportService):
|
||||||
self.actions[id]['thread'] = create_thread
|
self.actions[id]['thread'] = create_thread
|
||||||
return id
|
return id
|
||||||
|
|
||||||
|
def exp_create_database(self, db_name, demo, lang, user_password='admin'):
|
||||||
|
""" Similar to exp_create but blocking."""
|
||||||
|
self.id_protect.acquire()
|
||||||
|
self.id += 1
|
||||||
|
id = self.id
|
||||||
|
self.id_protect.release()
|
||||||
|
|
||||||
|
self.actions[id] = {'clean': False}
|
||||||
|
|
||||||
|
logging.getLogger('db.create').info('CREATE DATABASE %s', db_name.lower())
|
||||||
|
self._create_empty_database(db_name)
|
||||||
|
_initialize_db(self, id, db_name, demo, lang, user_password)
|
||||||
|
return True
|
||||||
|
|
||||||
def exp_get_progress(self, id):
|
def exp_get_progress(self, id):
|
||||||
if self.actions[id]['thread'].isAlive():
|
if self.actions[id]['thread'].isAlive():
|
||||||
# return openerp.modules.init_progress[db_name]
|
# return openerp.modules.init_progress[db_name]
|
||||||
|
@ -640,13 +655,55 @@ class report_spool(netsvc.ExportService):
|
||||||
def dispatch(self, method, auth, params):
|
def dispatch(self, method, auth, params):
|
||||||
(db, uid, passwd ) = params[0:3]
|
(db, uid, passwd ) = params[0:3]
|
||||||
params = params[3:]
|
params = params[3:]
|
||||||
if method not in ['report','report_get']:
|
if method not in ['report', 'report_get', 'render_report']:
|
||||||
raise KeyError("Method not supported %s" % method)
|
raise KeyError("Method not supported %s" % method)
|
||||||
security.check(db,uid,passwd)
|
security.check(db,uid,passwd)
|
||||||
fn = getattr(self, 'exp_' + method)
|
fn = getattr(self, 'exp_' + method)
|
||||||
res = fn(db, uid, *params)
|
res = fn(db, uid, *params)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def exp_render_report(self, db, uid, object, ids, datas=None, context=None):
|
||||||
|
if not datas:
|
||||||
|
datas={}
|
||||||
|
if not context:
|
||||||
|
context={}
|
||||||
|
|
||||||
|
self.id_protect.acquire()
|
||||||
|
self.id += 1
|
||||||
|
id = self.id
|
||||||
|
self.id_protect.release()
|
||||||
|
|
||||||
|
self._reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None}
|
||||||
|
|
||||||
|
cr = pooler.get_db(db).cursor()
|
||||||
|
import traceback
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
obj = netsvc.LocalService('report.'+object)
|
||||||
|
(result, format) = obj.create(cr, uid, ids, datas, context)
|
||||||
|
if not result:
|
||||||
|
tb = sys.exc_info()
|
||||||
|
self._reports[id]['exception'] = ExceptionWithTraceback('RML is not available at specified location or not enough data to print!', tb)
|
||||||
|
self._reports[id]['result'] = result
|
||||||
|
self._reports[id]['format'] = format
|
||||||
|
self._reports[id]['state'] = True
|
||||||
|
except Exception, exception:
|
||||||
|
|
||||||
|
tb = sys.exc_info()
|
||||||
|
tb_s = "".join(traceback.format_exception(*tb))
|
||||||
|
logger = netsvc.Logger()
|
||||||
|
logger.notifyChannel('web-services', netsvc.LOG_ERROR,
|
||||||
|
'Exception: %s\n%s' % (str(exception), tb_s))
|
||||||
|
if hasattr(exception, 'name') and hasattr(exception, 'value'):
|
||||||
|
self._reports[id]['exception'] = ExceptionWithTraceback(tools.ustr(exception.name), tools.ustr(exception.value))
|
||||||
|
else:
|
||||||
|
self._reports[id]['exception'] = ExceptionWithTraceback(tools.exception_to_unicode(exception), tb)
|
||||||
|
self._reports[id]['state'] = True
|
||||||
|
cr.commit()
|
||||||
|
cr.close()
|
||||||
|
|
||||||
|
return self._check_report(id)
|
||||||
|
|
||||||
def exp_report(self, db, uid, object, ids, datas=None, context=None):
|
def exp_report(self, db, uid, object, ids, datas=None, context=None):
|
||||||
if not datas:
|
if not datas:
|
||||||
datas={}
|
datas={}
|
||||||
|
|
|
@ -37,45 +37,89 @@ import time
|
||||||
import openerp
|
import openerp
|
||||||
import openerp.tools.config as config
|
import openerp.tools.config as config
|
||||||
|
|
||||||
|
def xmlrpc_return(start_response, service, method, params):
|
||||||
|
""" Helper to call a service's method with some params, using a
|
||||||
|
wsgi-supplied ``start_response`` callback."""
|
||||||
|
result = openerp.netsvc.ExportService.getService(service).dispatch(method, None, params)
|
||||||
|
response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
|
||||||
|
start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
|
||||||
|
return [response]
|
||||||
|
|
||||||
def wsgi_xmlrpc(environ, start_response):
|
def wsgi_xmlrpc(environ, start_response):
|
||||||
if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
|
""" The main OpenERP WSGI handler."""
|
||||||
|
if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/openerp/xmlrpc'):
|
||||||
length = int(environ['CONTENT_LENGTH'])
|
length = int(environ['CONTENT_LENGTH'])
|
||||||
data = environ['wsgi.input'].read(length)
|
data = environ['wsgi.input'].read(length)
|
||||||
path = environ['PATH_INFO'][len('/xmlrpc/'):] # expected to be one of db, object, ... TODO should we expect a possible trailing '/' ?
|
|
||||||
|
|
||||||
# TODO see SimpleXMLRPCDispatcher._marshaled_dispatch() for some necessary handling.
|
# TODO see SimpleXMLRPCDispatcher._marshaled_dispatch() for some necessary handling.
|
||||||
# TODO see OpenERPDispatcher for some othe handling (in particular, auth things).
|
# TODO see OpenERPDispatcher for some othe handling (in particular, auth things).
|
||||||
params, method = xmlrpclib.loads(data)
|
params, method = xmlrpclib.loads(data)
|
||||||
result = openerp.netsvc.ExportService.getService(path).dispatch(method, None, params)
|
|
||||||
response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
|
|
||||||
|
|
||||||
start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)))])
|
path = environ['PATH_INFO'][len('/openerp/xmlrpc'):]
|
||||||
return [response]
|
if path.startswith('/'): path = path[1:]
|
||||||
|
if path.endswith('/'): p = path[:-1]
|
||||||
|
path = path.split('/')
|
||||||
|
|
||||||
|
# All routes are hard-coded. Need a way to register addons-supplied handlers.
|
||||||
|
|
||||||
|
# No need for a db segment.
|
||||||
|
if len(path) == 1:
|
||||||
|
service = path[0]
|
||||||
|
|
||||||
|
if service == 'common':
|
||||||
|
if method in ('create_database', 'list', 'server_version'):
|
||||||
|
return xmlrpc_return(start_response, 'db', method, params)
|
||||||
|
else:
|
||||||
|
return xmlrpc_return(start_response, 'common', method, params)
|
||||||
|
# A db segment must be given.
|
||||||
|
elif len(path) == 2:
|
||||||
|
service, db_name = path
|
||||||
|
params = (db_name,) + params
|
||||||
|
|
||||||
|
if service == 'model':
|
||||||
|
return xmlrpc_return(start_response, 'object', method, params)
|
||||||
|
elif service == 'report':
|
||||||
|
return xmlrpc_return(start_response, 'report', method, params)
|
||||||
|
|
||||||
|
def legacy_wsgi_xmlrpc(environ, start_response):
|
||||||
|
if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
|
||||||
|
length = int(environ['CONTENT_LENGTH'])
|
||||||
|
data = environ['wsgi.input'].read(length)
|
||||||
|
path = environ['PATH_INFO'][len('/xmlrpc/'):] # expected to be one of db, object, ...
|
||||||
|
|
||||||
|
# TODO see SimpleXMLRPCDispatcher._marshaled_dispatch() for some necessary handling.
|
||||||
|
# TODO see OpenERPDispatcher for some othe handling (in particular, auth things).
|
||||||
|
params, method = xmlrpclib.loads(data)
|
||||||
|
return xmlrpc_return(start_response, path, method, params)
|
||||||
|
|
||||||
def wsgi_jsonrpc(environ, start_response):
|
def wsgi_jsonrpc(environ, start_response):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def application(environ, start_response):
|
def application(environ, start_response):
|
||||||
|
""" WSGI entry point."""
|
||||||
|
|
||||||
# Try all handlers until one returns some result (i.e. not None).
|
# Try all handlers until one returns some result (i.e. not None).
|
||||||
wsgi_handlers = [wsgi_xmlrpc, wsgi_jsonrpc]
|
wsgi_handlers = [wsgi_xmlrpc, wsgi_jsonrpc, legacy_wsgi_xmlrpc]
|
||||||
for handler in wsgi_handlers:
|
for handler in wsgi_handlers:
|
||||||
result = handler(environ, start_response)
|
result = handler(environ, start_response)
|
||||||
if result is None:
|
if result is None:
|
||||||
continue
|
continue
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# We never returned from the loop.
|
# We never returned from the loop. Needs something else than 200 OK.
|
||||||
response = 'No handler found.\n'
|
response = 'No handler found.\n'
|
||||||
start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))])
|
start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))])
|
||||||
return [response]
|
return [response]
|
||||||
|
|
||||||
# Serve XMLRPC via wsgiref's simple_server.
|
|
||||||
# Blocking, should probably be called in its own process.
|
|
||||||
def serve():
|
def serve():
|
||||||
|
""" Serve XMLRPC requests via wsgiref's simple_server.
|
||||||
|
|
||||||
|
Blocking, should probably be called in its own process.
|
||||||
|
"""
|
||||||
httpd = make_server('localhost', config['xmlrpc_port'], application)
|
httpd = make_server('localhost', config['xmlrpc_port'], application)
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|
||||||
|
# Master process id, can be used for signaling.
|
||||||
arbiter_pid = None
|
arbiter_pid = None
|
||||||
|
|
||||||
# Application setup before we can spawn any worker process.
|
# Application setup before we can spawn any worker process.
|
||||||
|
@ -84,8 +128,8 @@ def on_starting(server):
|
||||||
global arbiter_pid
|
global arbiter_pid
|
||||||
arbiter_pid = os.getpid() # TODO check if this is true even after replacing the executable
|
arbiter_pid = os.getpid() # TODO check if this is true even after replacing the executable
|
||||||
config = openerp.tools.config
|
config = openerp.tools.config
|
||||||
config['addons_path'] = '/home/openerp/repos/addons/trunk/' # need a config file
|
config['addons_path'] = '/home/openerp/repos/addons/trunk-xmlrpc-no-osv-memory' # need a config file
|
||||||
openerp.tools.cache = kill_workers_cache
|
#openerp.tools.cache = kill_workers_cache
|
||||||
openerp.netsvc.init_logger()
|
openerp.netsvc.init_logger()
|
||||||
openerp.osv.osv.start_object_proxy()
|
openerp.osv.osv.start_object_proxy()
|
||||||
openerp.service.web_services.start_web_services()
|
openerp.service.web_services.start_web_services()
|
||||||
|
@ -112,7 +156,7 @@ def kill_workers():
|
||||||
return
|
return
|
||||||
raise
|
raise
|
||||||
|
|
||||||
class kill_workers_cache(openerp.tools.real_cache):
|
class kill_workers_cache(openerp.tools.ormcache):
|
||||||
def clear(self, dbname, *args, **kwargs):
|
def clear(self, dbname, *args, **kwargs):
|
||||||
kill_workers()
|
kill_workers()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue