[IMP] wsgi: added new (still to be refined) routes for XMLRPC dispatching.

bzr revid: vmt@openerp.com-20110901130450-ub41ey4s0060vucg
This commit is contained in:
Vo Minh Thu 2011-09-01 15:04:50 +02:00
parent efd281a21f
commit 3c8571ead0
3 changed files with 117 additions and 16 deletions

View File

@ -3,7 +3,7 @@ import openerp
bind = '127.0.0.1:8069'
pidfile = '.gunicorn.pid'
# This is the big TODO: safely use more than a single worker.
workers = 1
workers = 2
# Some application-wide initialization is needed.
on_starting = openerp.wsgi.on_starting
when_ready = openerp.wsgi.when_ready

View File

@ -96,7 +96,8 @@ class db(netsvc.ExportService):
def dispatch(self, method, auth, params):
if method in [ 'create', 'get_progress', 'drop', 'dump',
'restore', 'rename',
'change_admin_password', 'migrate_databases' ]:
'change_admin_password', 'migrate_databases',
'create_database' ]:
passwd = params[0]
params = params[1:]
security.check_super(passwd)
@ -135,6 +136,20 @@ class db(netsvc.ExportService):
self.actions[id]['thread'] = create_thread
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):
if self.actions[id]['thread'].isAlive():
# return openerp.modules.init_progress[db_name]
@ -640,13 +655,55 @@ class report_spool(netsvc.ExportService):
def dispatch(self, method, auth, params):
(db, uid, passwd ) = params[0: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)
security.check(db,uid,passwd)
fn = getattr(self, 'exp_' + method)
res = fn(db, uid, *params)
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):
if not datas:
datas={}

View File

@ -37,45 +37,89 @@ import time
import openerp
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):
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'])
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 OpenERPDispatcher for some othe handling (in particular, auth things).
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)))])
return [response]
path = environ['PATH_INFO'][len('/openerp/xmlrpc'):]
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):
pass
def application(environ, start_response):
""" WSGI entry point."""
# 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:
result = handler(environ, start_response)
if result is None:
continue
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'
start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))])
return [response]
# Serve XMLRPC via wsgiref's simple_server.
# Blocking, should probably be called in its own process.
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.serve_forever()
# Master process id, can be used for signaling.
arbiter_pid = None
# Application setup before we can spawn any worker process.
@ -84,8 +128,8 @@ def on_starting(server):
global arbiter_pid
arbiter_pid = os.getpid() # TODO check if this is true even after replacing the executable
config = openerp.tools.config
config['addons_path'] = '/home/openerp/repos/addons/trunk/' # need a config file
openerp.tools.cache = kill_workers_cache
config['addons_path'] = '/home/openerp/repos/addons/trunk-xmlrpc-no-osv-memory' # need a config file
#openerp.tools.cache = kill_workers_cache
openerp.netsvc.init_logger()
openerp.osv.osv.start_object_proxy()
openerp.service.web_services.start_web_services()
@ -112,7 +156,7 @@ def kill_workers():
return
raise
class kill_workers_cache(openerp.tools.real_cache):
class kill_workers_cache(openerp.tools.ormcache):
def clear(self, dbname, *args, **kwargs):
kill_workers()