[FIX] module.loading: ensure installed modules are all loaded before installing new ones

After the recent change to make module install
atomically (code *and* data), we ran into issues
when installing a new module indirectly triggers
code of a not-yet-loaded-but-installed module,
via its data that is already in the database
(e.g. worflows or reports modified by this module
within another module, that now refer to its
code).
To avoid this, we now make sure that we only
install new modules on top of a consistent system
(code *and* data), by loading all installed or
'to upgrade' modules *before* starting to install
new ones.

lp bug: https://launchpad.net/bugs/809168 fixed

bzr revid: odo@openerp.com-20110712133343-unf610k23fa6d3pk
This commit is contained in:
Olivier Dony 2011-07-12 15:33:43 +02:00
parent 65190b7f8c
commit 8874fb058f
1 changed files with 34 additions and 26 deletions

View File

@ -152,6 +152,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
status = {}
processed_modules = []
loaded_modules = []
statusi = 0
pool = pooler.get_pool(cr.dbname)
migrations = openerp.modules.migration.MigrationManager(cr, graph)
@ -169,6 +170,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
migrations.migrate_module(package, 'pre')
register_module_classes(package.name)
models = pool.instanciate(package.name, cr)
loaded_modules.append(package.name)
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
init_module_models(cr, package.name, models)
@ -226,7 +228,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
cr.commit()
return processed_modules
return loaded_modules, processed_modules
def _check_module_names(cr, module_names):
mod_names = set(module_names)
@ -242,11 +244,26 @@ def _check_module_names(cr, module_names):
incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
logging.getLogger('init').warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules):
"""Loads modules marked with ``states``, adding them to ``graph`` and
``loaded_modules`` and returns a list of installed/upgraded modules."""
processed_modules = []
while True:
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(states),))
module_list = [name for (name,) in cr.fetchall() if name not in graph]
new_modules_in_graph = graph.add_modules(cr, module_list, force)
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules)
processed_modules.extend(processed)
loaded_modules.extend(loaded)
if not processed: break
return processed_modules
def load_modules(db, force_demo=False, status=None, update_module=False):
# TODO status['progress'] reporting is broken: used twice (and reset each
# time to zero) in load_module_graph, not fine-grained enough.
# It should be a method exposed by the pool.
initialize_sys_path()
open_openerp_namespace()
@ -268,10 +285,9 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
# This is a brand new pool, just created in pooler.get_db_and_pool()
pool = pooler.get_pool(cr.dbname)
processed_modules = []
processed_modules = [] # for cleanup step after install
loaded_modules = [] # to avoid double loading
report = tools.assertion_report()
# NOTE: Try to also load the modules that have been marked as uninstallable previously...
STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable']
if 'base' in tools.config['update'] or 'all' in tools.config['update']:
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
@ -281,7 +297,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
if not graph:
logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')
raise osv.osv.except_osv(_('Could not load base module'), _('module base cannot be loaded! (hint: verify addons-path)'))
processed_modules.extend(load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report))
loaded, processed = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
processed_modules.extend(processed)
if tools.config['load_language']:
for lang in tools.config['load_language'].split(','):
@ -310,28 +327,19 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
STATES_TO_LOAD += ['to install']
# STEP 3: Load marked modules (skipping base which was done in STEP 1)
loop_guardrail = 0
while True:
loop_guardrail += 1
if loop_guardrail > 100:
raise ValueError('Possible recursive module tree detected, aborting.')
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(STATES_TO_LOAD),))
module_list = [name for (name,) in cr.fetchall() if name not in graph]
if not module_list:
break
new_modules_in_graph = graph.add_modules(cr, module_list, force)
if new_modules_in_graph == 0:
# nothing to load
break
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
processed_modules.extend(load_module_graph(cr, graph, status, report=report, skip_modules=processed_modules))
# IMPORTANT: this is done in two parts, first loading all installed or
# partially installed modules (i.e. installed/to upgrade), to
# offer a consistent system to the second part: installing
# newly selected modules.
states_to_load = ['installed', 'to upgrade']
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules)
processed_modules.extend(processed)
if update_module:
states_to_load = ['to install']
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules)
processed_modules.extend(processed)
# load custom models
cr.execute('select model from ir_model where state=%s', ('manual',))