import types
import openerp
+ import openerp.modules.registry
from openerp import SUPERUSER_ID
-from openerp import netsvc, pooler, tools
+from openerp import tools
from openerp.osv import fields,osv
from openerp.osv.orm import Model
from openerp.tools.safe_eval import safe_eval as eval
if not context.get(MODULE_UNINSTALL_FLAG):
# only reload pool for normal unlink. For module uninstall the
# reload is done independently in openerp.modules.loading
+ cr.commit() # must be committed before reloading registry in new cursor
- pooler.restart_pool(cr.dbname)
+ openerp.modules.registry.RegistryManager.new(cr.dbname)
+ openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res
field_name=vals['name'],
field_state='manual',
select=vals.get('select_level', '0'))
- self.pool.get(vals['model'])._auto_init(cr, ctx)
+ self.pool[vals['model']]._auto_init(cr, ctx)
+ openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res
def instanciate(self, cr, user, model, context=None):
field_state='manual',
select=vals.get('select_level', '0'),
update_custom_fields=True)
- self.pool.get(vals['model'])._auto_init(cr, ctx)
+ self.pool[vals['model']]._auto_init(cr, ctx)
+ openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res
# Successfully logged in as admin!
# Attempt to guess the web base url...
if user_agent_env and user_agent_env.get('base_location'):
- cr = pooler.get_db(db).cursor()
+ cr = self.pool.db.cursor()
try:
base = user_agent_env['base_location']
- self.pool['ir.config_parameter'].set_param(cr, uid, 'web.base.url', base)
- ICP = self.pool.get('ir.config_parameter')
++ ICP = self.pool['ir.config_parameter']
+ if not ICP.get_param(cr, uid, 'web.base.url.freeze'):
+ ICP.set_param(cr, uid, 'web.base.url', base)
cr.commit()
except Exception:
_logger.exception("Failed to update web.base.url configuration parameter")
config = openerp.tools.config
- configure_babel_localedata_path()
-
- setup_signal_handlers()
+ setup_signal_handlers(signal_handler)
if config["test_file"]:
run_test_file(config['db_name'], config['test_file'])
import openerp.modules.db
import openerp.modules.graph
import openerp.modules.migration
+ import openerp.modules.registry
import openerp.osv as osv
-import openerp.pooler as pooler
import openerp.tools as tools
from openerp import SUPERUSER_ID
processed_modules = []
loaded_modules = []
- pool = pooler.get_pool(cr.dbname)
+ registry = openerp.registry(cr.dbname)
migrations = openerp.modules.migration.MigrationManager(cr, graph)
- _logger.debug('loading %d packages...', len(graph))
+ _logger.info('loading %d modules...', len(graph))
# Query manual fields for all models at once and save them on the registry
# so the initialization code for each model does not have to do it
self.name_get(cr, user, [id_new], context=context)[0][1] + \
"' " + _("created.")
self.log(cr, user, id_new, message, True, context=context)
+ self.check_access_rule(cr, user, [id_new], 'create', context=context)
- self._workflow_trigger(cr, user, [id_new], 'trg_create', context=context)
+ self.create_workflow(cr, user, [id_new], context=context)
return id_new
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None):
--- /dev/null
+# -*- coding: utf-8 -*-
+
+import base64
+import contextlib
+import logging
+import os
+import threading
+import traceback
+
+import openerp
+from openerp import SUPERUSER_ID
+import openerp.release
+import openerp.sql_db
+import openerp.tools
+
+import security
+
+_logger = logging.getLogger(__name__)
+
+self_actions = {}
+self_id = 0
+self_id_protect = threading.Semaphore()
+
+# This should be moved to openerp.modules.db, along side initialize().
+def _initialize_db(id, db_name, demo, lang, user_password):
+ try:
+ cr = None
+ try:
+ self_actions[id]['progress'] = 0
+ cr = openerp.sql_db.db_connect(db_name).cursor()
+ openerp.modules.db.initialize(cr) # TODO this should be removed as it is done by RegistryManager.new().
+ openerp.tools.config['lang'] = lang
+ cr.commit()
+ finally:
+ if cr:
+ cr.close()
+ cr = None
+
+ registry = openerp.modules.registry.RegistryManager.new(
+ db_name, demo, self_actions[id], update_module=True)
+
+ try:
+ cr = openerp.sql_db.db_connect(db_name).cursor()
+
+ if lang:
+ modobj = registry['ir.module.module']
+ mids = modobj.search(cr, SUPERUSER_ID, [('state', '=', 'installed')])
+ modobj.update_translations(cr, SUPERUSER_ID, mids, lang)
+
+ # update admin's password and lang
+ values = {'password': user_password, 'lang': lang}
+ registry['res.users'].write(cr, SUPERUSER_ID, [SUPERUSER_ID], values)
+
+ cr.execute('SELECT login, password FROM res_users ORDER BY login')
+ self_actions[id].update(users=cr.dictfetchall(), clean=True)
+ cr.commit()
+ finally:
+ if cr:
+ cr.close()
+ except Exception, e:
+ self_actions[id].update(clean=False, exception=e)
+ _logger.exception('CREATE DATABASE failed:')
+ self_actions[id]['traceback'] = traceback.format_exc()
+
+def dispatch(method, params):
+ if method in [ 'create', 'get_progress', 'drop', 'dump',
+ 'restore', 'rename',
+ 'change_admin_password', 'migrate_databases',
+ 'create_database', 'duplicate_database' ]:
+ passwd = params[0]
+ params = params[1:]
+ security.check_super(passwd)
+ elif method in [ 'db_exist', 'list', 'list_lang', 'server_version' ]:
+ # params = params
+ # No security check for these methods
+ pass
+ else:
+ raise KeyError("Method not found: %s" % method)
+ fn = globals()['exp_' + method]
+ return fn(*params)
+
+def _create_empty_database(name):
+ db = openerp.sql_db.db_connect('postgres')
+ cr = db.cursor()
+ chosen_template = openerp.tools.config['db_template']
+ cr.execute("""SELECT datname
+ FROM pg_database
+ WHERE datname = %s """,
+ (name,))
+ if cr.fetchall():
+ raise openerp.exceptions.Warning(" %s database already exists!" % name )
+ try:
+ cr.autocommit(True) # avoid transaction block
+ cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (name, chosen_template))
+ finally:
+ cr.close()
+
+def exp_create(db_name, demo, lang, user_password='admin'):
+ self_id_protect.acquire()
+ global self_id
+ self_id += 1
+ id = self_id
+ self_id_protect.release()
+
+ self_actions[id] = {'clean': False}
+
+ _create_empty_database(db_name)
+
+ _logger.info('CREATE DATABASE %s', db_name.lower())
+ create_thread = threading.Thread(target=_initialize_db,
+ args=(id, db_name, demo, lang, user_password))
+ create_thread.start()
+ self_actions[id]['thread'] = create_thread
+ return id
+
+def exp_create_database(db_name, demo, lang, user_password='admin'):
+ """ Similar to exp_create but blocking."""
+ self_id_protect.acquire()
+ global self_id
+ self_id += 1
+ id = self_id
+ self_id_protect.release()
+
+ self_actions[id] = {'clean': False}
+
+ _logger.info('Create database `%s`.', db_name)
+ _create_empty_database(db_name)
+ _initialize_db(id, db_name, demo, lang, user_password)
+ return True
+
+def exp_duplicate_database(db_original_name, db_name):
+ _logger.info('Duplicate database `%s` to `%s`.', db_original_name, db_name)
+ openerp.sql_db.close_db(db_original_name)
+ db = openerp.sql_db.db_connect('postgres')
+ cr = db.cursor()
+ try:
+ cr.autocommit(True) # avoid transaction block
+ cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (db_name, db_original_name))
+ finally:
+ cr.close()
+ return True
+
+def exp_get_progress(id):
+ if self_actions[id]['thread'].isAlive():
+# return openerp.modules.init_progress[db_name]
+ return min(self_actions[id].get('progress', 0),0.95), []
+ else:
+ clean = self_actions[id]['clean']
+ if clean:
+ users = self_actions[id]['users']
+ for user in users:
+ # Remove the None passwords as they can't be marshalled by XML-RPC.
+ if user['password'] is None:
+ user['password'] = ''
+ self_actions.pop(id)
+ return 1.0, users
+ else:
+ e = self_actions[id]['exception'] # TODO this seems wrong: actions[id]['traceback'] is set, but not 'exception'.
+ self_actions.pop(id)
+ raise Exception, e
+
+def exp_drop(db_name):
+ if not exp_db_exist(db_name):
+ return False
+ openerp.modules.registry.RegistryManager.delete(db_name)
+ openerp.sql_db.close_db(db_name)
+
+ db = openerp.sql_db.db_connect('postgres')
+ cr = db.cursor()
+ cr.autocommit(True) # avoid transaction block
+ try:
+ # Try to terminate all other connections that might prevent
+ # dropping the database
+ try:
+
+ # PostgreSQL 9.2 renamed pg_stat_activity.procpid to pid:
+ # http://www.postgresql.org/docs/9.2/static/release-9-2.html#AEN110389
+ pid_col = 'pid' if cr._cnx.server_version >= 90200 else 'procpid'
+
+ cr.execute("""SELECT pg_terminate_backend(%(pid_col)s)
+ FROM pg_stat_activity
+ WHERE datname = %%s AND
+ %(pid_col)s != pg_backend_pid()""" % {'pid_col': pid_col},
+ (db_name,))
+ except Exception:
+ pass
+
+ try:
+ cr.execute('DROP DATABASE "%s"' % db_name)
+ except Exception, e:
+ _logger.error('DROP DB: %s failed:\n%s', db_name, e)
+ raise Exception("Couldn't drop database %s: %s" % (db_name, e))
+ else:
+ _logger.info('DROP DB: %s', db_name)
+ finally:
+ cr.close()
+ return True
+
+@contextlib.contextmanager
- def _set_pg_password_in_environment():
- """ On Win32, pg_dump (and pg_restore) require that
- :envvar:`PGPASSWORD` be set
++def _set_pg_password_in_environment(self):
++ """ On systems where pg_restore/pg_dump require an explicit
++ password (i.e. when not connecting via unix sockets, and most
++ importantly on Windows), it is necessary to pass the PG user
++ password in the environment or in a special .pgpass file.
+
+ This context management method handles setting
- :envvar:`PGPASSWORD` iif win32 and the envvar is not already
++ :envvar:`PGPASSWORD` if it is not already
+ set, and removing it afterwards.
++
++ See also http://www.postgresql.org/docs/8.4/static/libpq-envars.html
++
++ .. note:: This is not thread-safe, and should never be enabled for
++ SaaS (giving SaaS users the super-admin password is not a good idea
++ anyway)
+ """
- if os.name != 'nt' or os.environ.get('PGPASSWORD'):
++ if os.environ.get('PGPASSWORD') or not tools.config['db_password']:
+ yield
+ else:
- os.environ['PGPASSWORD'] = openerp.tools.config['db_password']
++ os.environ['PGPASSWORD'] = tools.config['db_password']
+ try:
+ yield
+ finally:
+ del os.environ['PGPASSWORD']
+
+
+def exp_dump(db_name):
+ with _set_pg_password_in_environment():
+ cmd = ['pg_dump', '--format=c', '--no-owner']
+ if openerp.tools.config['db_user']:
+ cmd.append('--username=' + openerp.tools.config['db_user'])
+ if openerp.tools.config['db_host']:
+ cmd.append('--host=' + openerp.tools.config['db_host'])
+ if openerp.tools.config['db_port']:
+ cmd.append('--port=' + str(openerp.tools.config['db_port']))
+ cmd.append(db_name)
+
+ stdin, stdout = openerp.tools.exec_pg_command_pipe(*tuple(cmd))
+ stdin.close()
+ data = stdout.read()
+ res = stdout.close()
+
+ if not data or res:
+ _logger.error(
+ 'DUMP DB: %s failed! Please verify the configuration of the database password on the server. '
- 'It should be provided as a -w <PASSWD> command-line option, or as `db_password` in the '
++ 'You may need to create a .pgpass file for authentication, or specify `db_password` in the '
+ 'server configuration file.\n %s', db_name, data)
+ raise Exception, "Couldn't dump database"
+ _logger.info('DUMP DB successful: %s', db_name)
+
+ return base64.encodestring(data)
+
+def exp_restore(db_name, data):
+ with _set_pg_password_in_environment():
+ if exp_db_exist(db_name):
+ _logger.warning('RESTORE DB: %s already exists', db_name)
+ raise Exception, "Database already exists"
+
+ _create_empty_database(db_name)
+
+ cmd = ['pg_restore', '--no-owner']
+ if openerp.tools.config['db_user']:
+ cmd.append('--username=' + openerp.tools.config['db_user'])
+ if openerp.tools.config['db_host']:
+ cmd.append('--host=' + openerp.tools.config['db_host'])
+ if openerp.tools.config['db_port']:
+ cmd.append('--port=' + str(openerp.tools.config['db_port']))
+ cmd.append('--dbname=' + db_name)
+ args2 = tuple(cmd)
+
+ buf=base64.decodestring(data)
+ if os.name == "nt":
+ tmpfile = (os.environ['TMP'] or 'C:\\') + os.tmpnam()
+ file(tmpfile, 'wb').write(buf)
+ args2=list(args2)
+ args2.append(tmpfile)
+ args2=tuple(args2)
+ stdin, stdout = openerp.tools.exec_pg_command_pipe(*args2)
+ if not os.name == "nt":
+ stdin.write(base64.decodestring(data))
+ stdin.close()
+ res = stdout.close()
+ if res:
+ raise Exception, "Couldn't restore database"
+ _logger.info('RESTORE DB: %s', db_name)
+
+ return True
+
+def exp_rename(old_name, new_name):
+ openerp.modules.registry.RegistryManager.delete(old_name)
+ openerp.sql_db.close_db(old_name)
+
+ db = openerp.sql_db.db_connect('postgres')
+ cr = db.cursor()
+ cr.autocommit(True) # avoid transaction block
+ try:
+ try:
+ cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name))
+ except Exception, e:
+ _logger.error('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e)
+ raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e))
+ else:
+ fs = os.path.join(openerp.tools.config['root_path'], 'filestore')
+ if os.path.exists(os.path.join(fs, old_name)):
+ os.rename(os.path.join(fs, old_name), os.path.join(fs, new_name))
+
+ _logger.info('RENAME DB: %s -> %s', old_name, new_name)
+ finally:
+ cr.close()
+ return True
+
+def exp_db_exist(db_name):
+ ## Not True: in fact, check if connection to database is possible. The database may exists
+ return bool(openerp.sql_db.db_connect(db_name))
+
+def exp_list(document=False):
+ if not openerp.tools.config['list_db'] and not document:
+ raise openerp.exceptions.AccessDenied()
+ chosen_template = openerp.tools.config['db_template']
+ templates_list = tuple(set(['template0', 'template1', 'postgres', chosen_template]))
+ db = openerp.sql_db.db_connect('postgres')
+ cr = db.cursor()
+ try:
+ try:
+ db_user = openerp.tools.config["db_user"]
+ if not db_user and os.name == 'posix':
+ import pwd
+ db_user = pwd.getpwuid(os.getuid())[0]
+ if not db_user:
+ cr.execute("select usename from pg_user where usesysid=(select datdba from pg_database where datname=%s)", (openerp.tools.config["db_name"],))
+ res = cr.fetchone()
+ db_user = res and str(res[0])
+ if db_user:
+ cr.execute("select datname from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in %s order by datname", (db_user, templates_list))
+ else:
+ cr.execute("select datname from pg_database where datname not in %s order by datname", (templates_list,))
+ res = [str(name) for (name,) in cr.fetchall()]
+ except Exception:
+ res = []
+ finally:
+ cr.close()
+ res.sort()
+ return res
+
+def exp_change_admin_password(new_password):
+ openerp.tools.config['admin_passwd'] = new_password
+ openerp.tools.config.save()
+ return True
+
+def exp_list_lang():
+ return openerp.tools.scan_languages()
+
+def exp_server_version():
+ """ Return the version of the server
+ Used by the client to verify the compatibility with its own version
+ """
+ return openerp.release.version
+
+def exp_migrate_databases(databases):
+ for db in databases:
+ _logger.info('migrate database %s', db)
+ openerp.tools.config['update']['base'] = True
+ openerp.modules.registry.RegistryManager.new(db, force_demo=False, update_module=True)
+ return True
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- coding: utf-8 -*-
+
+from functools import wraps
+import logging
+from psycopg2 import IntegrityError, errorcodes
++import random
+import threading
++import time
+
+import openerp
+from openerp.tools.translate import translate
+from openerp.osv.orm import except_orm
+
+import security
+
+_logger = logging.getLogger(__name__)
+
++PG_CONCURRENCY_ERRORS_TO_RETRY = (errorcodes.LOCK_NOT_AVAILABLE, errorcodes.SERIALIZATION_FAILURE, errorcodes.DEADLOCK_DETECTED)
++MAX_TRIES_ON_CONCURRENCY_FAILURE = 5
++
+def dispatch(method, params):
+ (db, uid, passwd ) = params[0:3]
++
++ # set uid tracker - cleaned up at the WSGI
++ # dispatching phase in openerp.service.wsgi_server.application
+ threading.current_thread().uid = uid
++
+ params = params[3:]
+ if method == 'obj_list':
+ raise NameError("obj_list has been discontinued via RPC as of 6.0, please query ir.model directly!")
+ if method not in ['execute', 'execute_kw', 'exec_workflow']:
+ raise NameError("Method not available %s" % method)
+ security.check(db,uid,passwd)
+ openerp.modules.registry.RegistryManager.check_registry_signaling(db)
+ fn = globals()[method]
+ res = fn(db, uid, *params)
+ openerp.modules.registry.RegistryManager.signal_caches_change(db)
+ return res
+
+def check(f):
+ @wraps(f)
+ def wrapper(dbname, *args, **kwargs):
+ """ Wraps around OSV functions and normalises a few exceptions
+ """
+
+ def tr(src, ttype):
+ # We try to do the same as the _(), but without the frame
+ # inspection, since we aready are wrapping an osv function
+ # trans_obj = self.get('ir.translation') cannot work yet :(
+ ctx = {}
+ if not kwargs:
+ if args and isinstance(args[-1], dict):
+ ctx = args[-1]
+ elif isinstance(kwargs, dict):
+ ctx = kwargs.get('context', {})
+
+ uid = 1
+ if args and isinstance(args[0], (long, int)):
+ uid = args[0]
+
+ lang = ctx and ctx.get('lang')
+ if not (lang or hasattr(src, '__call__')):
+ return src
+
+ # We open a *new* cursor here, one reason is that failed SQL
+ # queries (as in IntegrityError) will invalidate the current one.
+ cr = False
+
+ if hasattr(src, '__call__'):
+ # callable. We need to find the right parameters to call
+ # the orm._sql_message(self, cr, uid, ids, context) function,
+ # or we skip..
+ # our signature is f(registry, dbname [,uid, obj, method, args])
+ try:
+ if args and len(args) > 1:
+ # TODO self doesn't exist, but was already wrong before (it was not a registry but just the object_service.
+ obj = self.get(args[1])
+ if len(args) > 3 and isinstance(args[3], (long, int, list)):
+ ids = args[3]
+ else:
+ ids = []
+ cr = openerp.sql_db.db_connect(dbname).cursor()
+ return src(obj, cr, uid, ids, context=(ctx or {}))
+ except Exception:
+ pass
+ finally:
+ if cr: cr.close()
+
+ return False # so that the original SQL error will
+ # be returned, it is the best we have.
+
+ try:
+ cr = openerp.sql_db.db_connect(dbname).cursor()
+ res = translate(cr, name=False, source_type=ttype,
+ lang=lang, source=src)
+ if res:
+ return res
+ else:
+ return src
+ finally:
+ if cr: cr.close()
+
+ def _(src):
+ return tr(src, 'code')
+
- try:
- if openerp.registry(dbname)._init:
- raise openerp.exceptions.Warning('Currently, this database is not fully loaded and can not be used.')
- return f(dbname, *args, **kwargs)
- except IntegrityError, inst:
- registry = openerp.registry(dbname)
- for key in registry._sql_error.keys():
- if key in inst[0]:
- raise openerp.osv.orm.except_orm(_('Constraint Error'), tr(registry._sql_error[key], 'sql_constraint') or inst[0])
- if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
- msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
- _logger.debug("IntegrityError", exc_info=True)
- try:
- errortxt = inst.pgerror.replace('«','"').replace('»','"')
- if '"public".' in errortxt:
- context = errortxt.split('"public".')[1]
- model_name = table = context.split('"')[1]
- else:
- last_quote_end = errortxt.rfind('"')
- last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
- model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
- model = table.replace("_",".")
- model_obj = registry.get(model)
- if model_obj:
- model_name = model_obj._description or model_obj._name
- msg += _('\n\n[object with reference: %s - %s]') % (model_name, model)
- except Exception:
- pass
- raise openerp.osv.orm.except_orm(_('Integrity Error'), msg)
- else:
- raise openerp.osv.orm.except_orm(_('Integrity Error'), inst[0])
++ tries = 0
++ while True:
++ try:
++ if openerp.registry(dbname)._init:
++ raise openerp.exceptions.Warning('Currently, this database is not fully loaded and can not be used.')
++ return f(dbname, *args, **kwargs)
++ except OperationalError, e:
++ # Automatically retry the typical transaction serialization errors
++ if e.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY:
++ raise
++ if tries >= MAX_TRIES_ON_CONCURRENCY_FAILURE:
++ _logger.warning("%s, maximum number of tries reached" % errorcodes.lookup(e.pgcode))
++ raise
++ wait_time = random.uniform(0.0, 2 ** tries)
++ tries += 1
++ _logger.info("%s, retry %d/%d in %.04f sec..." % (errorcodes.lookup(e.pgcode), tries, MAX_TRIES_ON_CONCURRENCY_FAILURE, wait_time))
++ time.sleep(wait_time)
++ except IntegrityError, inst:
++ registry = openerp.registry(dbname)
++ for key in registry._sql_error.keys():
++ if key in inst[0]:
++ raise openerp.osv.orm.except_orm(_('Constraint Error'), tr(registry._sql_error[key], 'sql_constraint') or inst[0])
++ if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
++ msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
++ _logger.debug("IntegrityError", exc_info=True)
++ try:
++ errortxt = inst.pgerror.replace('«','"').replace('»','"')
++ if '"public".' in errortxt:
++ context = errortxt.split('"public".')[1]
++ model_name = table = context.split('"')[1]
++ else:
++ last_quote_end = errortxt.rfind('"')
++ last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
++ model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
++ model = table.replace("_",".")
++ model_obj = registry.get(model)
++ if model_obj:
++ model_name = model_obj._description or model_obj._name
++ msg += _('\n\n[object with reference: %s - %s]') % (model_name, model)
++ except Exception:
++ pass
++ raise openerp.osv.orm.except_orm(_('Integrity Error'), msg)
++ else:
++ raise openerp.osv.orm.except_orm(_('Integrity Error'), inst[0])
+
+ return wrapper
+
+def execute_cr(cr, uid, obj, method, *args, **kw):
+ object = openerp.registry(cr.dbname).get(obj)
+ if not object:
+ raise except_orm('Object Error', 'Object %s doesn\'t exist' % str(obj))
+ return getattr(object, method)(cr, uid, *args, **kw)
+
+def execute_kw(db, uid, obj, method, args, kw=None):
+ return execute(db, uid, obj, method, *args, **kw or {})
+
+@check
+def execute(db, uid, obj, method, *args, **kw):
+ threading.currentThread().dbname = db
+ cr = openerp.registry(db).db.cursor()
+ try:
+ try:
+ if method.startswith('_'):
+ raise except_orm('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
+ res = execute_cr(cr, uid, obj, method, *args, **kw)
+ if res is None:
+ _logger.warning('The method %s of the object %s can not return `None` !', method, obj)
+ cr.commit()
+ except Exception:
+ cr.rollback()
+ raise
+ finally:
+ cr.close()
+ return res
+
+def exec_workflow_cr(cr, uid, obj, signal, *args):
+ object = openerp.registry(cr.dbname).get(obj)
+ if not object:
+ raise except_orm('Object Error', 'Object %s doesn\'t exist' % str(obj))
+ res_id = args[0]
+ return object.signal_workflow(cr, uid, [res_id], signal)[res_id]
+
+@check
+def exec_workflow(db, uid, obj, signal, *args):
+ cr = openerp.registry(db).db.cursor()
+ try:
+ try:
+ res = exec_workflow_cr(cr, uid, obj, signal, *args)
+ cr.commit()
+ except Exception:
+ cr.rollback()
+ raise
+ finally:
+ cr.close()
+ return res
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
if config['db_name']:
db_names = config['db_name'].split(',')
else:
- db_names = openerp.netsvc.ExportService._services['db'].exp_list(True)
+ db_names = openerp.service.db.exp_list(True)
- for db_name in db_names:
+ if len(db_names):
+ self.db_index = (self.db_index + 1) % len(db_names)
+ db_name = db_names[self.db_index]
if rpc_request_flag:
start_time = time.time()
start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info()
"""
module_handlers.append(handler)
+def register_rpc_endpoint(endpoint, handler):
+ """ Register a handler for a given RPC enpoint.
+ """
+ rpc_handlers[endpoint] = handler
+
def application_unproxied(environ, start_response):
""" WSGI entry point."""
+ # cleanup db/uid trackers - they're set at HTTP dispatch in
+ # web.session.OpenERPSession.send() and at RPC dispatch in
+ # openerp.service.web_services.objects_proxy.dispatch().
+ # /!\ The cleanup cannot be done at the end of this `application`
+ # method because werkzeug still produces relevant logging afterwards
+ if hasattr(threading.current_thread(), 'uid'):
+ del threading.current_thread().uid
+ if hasattr(threading.current_thread(), 'dbname'):
+ del threading.current_thread().dbname
+
openerp.service.start_internal()
# Try all handlers until one returns some result (i.e. not None).
if avoid_if_small and image.size[0] <= size[0] and image.size[1] <= size[1]:
return base64_source
- if image.size <> size:
+ if image.size != size:
# If you need faster thumbnails you may use use Image.NEAREST
image = ImageOps.fit(image, size, Image.ANTIALIAS)
+ if image.mode not in ["1", "L", "P", "RGB", "RGBA"]:
+ image = image.convert("RGB")
background_stream = StringIO.StringIO()
image.save(background_stream, filetype)
--- /dev/null
+"""
+Run an OpenERP cron process.
+"""
+
+import os
+
+import common
+
+def run(args):
+ import openerp
+ import openerp.cli.server
+ import openerp.tools.config
+ import openerp.service.cron
+ config = openerp.tools.config
+
+ os.environ["TZ"] = "UTC"
+ common.set_addons(args)
+ args.database = args.database or []
+
+ config['log_handler'] = [':WARNING', 'openerp.addons.base.ir.ir_cron:DEBUG']
+
+ openerp.multi_process = True
+ common.setproctitle('openerp-cron [%s]' % ', '.join(args.database))
+
+ openerp.cli.server.check_root_user()
+ openerp.netsvc.init_logger()
+ #openerp.cli.server.report_configuration()
- openerp.cli.server.configure_babel_localedata_path()
+ openerp.cli.server.setup_signal_handlers(openerp.cli.server.signal_handler)
+ import openerp.addons.base
+ if args.database:
+ for db in args.database:
+ openerp.cli.server.preload_registry(db)
+ openerp.service.cron.start_service()
+ openerp.cli.server.quit_on_signals()
+ else:
+ print "No database given."
+
+
+def add_parser(subparsers):
+ parser = subparsers.add_parser('cron',
+ description='Run an OpenERP cron process.')
+ common.add_addons_argument(parser)
+ parser.add_argument('--database', action='append',
+ help='Database for which cron jobs are processed (can be repeated)')
+
+ parser.set_defaults(run=run)
--- /dev/null
+"""
+Run a normal OpenERP HTTP process.
+"""
+
+import logging
+import os
+import signal
+
+import common
+
+_logger = logging.getLogger(__name__)
+
+def mk_signal_handler(server):
+ def signal_handler(sig, frame):
+ """
+ Specialized signal handler for the evented process.
+ """
+ print "\n\n\nStopping gevent HTTP server...\n\n\n"
+ server.stop()
+ return signal_handler
+
+def setup_signal_handlers(signal_handler):
+ SIGNALS = (signal.SIGINT, signal.SIGTERM)
+ map(lambda sig: signal.signal(sig, signal_handler), SIGNALS)
+
+def run(args):
+ # Note that gevent monkey patching must be done before importing the
+ # `threading` module, see http://stackoverflow.com/questions/8774958/.
+ if args.gevent:
+ import gevent
+ import gevent.monkey
+ import gevent.wsgi
+ import gevent_psycopg2
+ gevent.monkey.patch_all()
+ gevent_psycopg2.monkey_patch()
+ import threading
+ import openerp
+ import openerp.cli.server
+ import openerp.service.wsgi_server
+ import openerp.tools.config
+ config = openerp.tools.config
+
+ os.environ["TZ"] = "UTC"
+ common.set_addons(args)
+
+ openerp.multi_process = True
+ common.setproctitle('openerp-web')
+
+ openerp.cli.server.check_root_user()
+ openerp.netsvc.init_logger()
+ #openerp.cli.server.report_configuration()
- openerp.cli.server.configure_babel_localedata_path()
+
+ target = openerp.service.wsgi_server.serve
+ if not args.gevent:
+ openerp.evented = False
+ openerp.cli.server.setup_signal_handlers(openerp.cli.server.signal_handler)
+ # TODO openerp.multi_process with a multi-threaded process probably
+ # doesn't work very well (e.g. waiting for all threads to complete
+ # before killing the process is not implemented).
+ arg = (args.interface, int(args.port), args.threaded)
+ threading.Thread(target=target, args=arg).start()
+ openerp.cli.server.quit_on_signals()
+ else:
+ openerp.evented = True
+
+ app = openerp.service.wsgi_server.application
+ server = gevent.wsgi.WSGIServer((args.interface, int(args.port)), app)
+ setup_signal_handlers(mk_signal_handler(server))
+ try:
+ server.serve_forever()
+ except KeyboardInterrupt:
+ try:
+ server.stop()
+ gevent.shutdown()
+ except KeyboardInterrupt:
+ sys.stderr.write("Forced shutdown.\n")
+ gevent.shutdown()
+
+def add_parser(subparsers):
+ parser = subparsers.add_parser('web',
+ description='Run a normal OpenERP HTTP process. By default a '
+ 'singly-threaded Werkzeug server is used.')
+ common.add_addons_argument(parser)
+ parser.add_argument('--interface', default='0.0.0.0',
+ help='HTTP interface to listen on (default is %(default)s)')
+ parser.add_argument('--port', metavar='INT', default=8069,
+ help='HTTP port to listen on (default is %(default)s)')
+ parser.add_argument('--threaded', action='store_true',
+ help='Use a multithreaded Werkzeug server (incompatible with --gevent)')
+ parser.add_argument('--gevent', action='store_true',
+ help="Use gevent's WSGI server (incompatible with --threaded)")
+
+ parser.set_defaults(run=run)
'mock',
'PIL', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
'psutil', # windows binary code.google.com/p/psutil/downloads/list
- 'psycopg2',
+ 'psycopg2 >= 2.2',
'pydot',
+ 'pyparsing < 2',
'python-dateutil < 2',
'python-ldap', # optional
'python-openid',