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 registry. initialize_sys_path() force = [] if force_demo: force.append('demo') cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): _logger.info("init db") openerp.modules.db.initialize(cr) tools.config["init"]["all"] = 1 tools.config['update']['all'] = 1 if not tools.config['without_demo']: tools.config["demo"]['all'] = 1 # This is a brand new registry, just created in # openerp.modules.registry.RegistryManager.new(). registry = openerp.registry(cr.dbname) 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')) # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) graph = openerp.modules.graph.Graph() graph.add_module(cr, 'base', force) if not graph: _logger.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: for cleanup step after install # loaded_modules: to avoid double loading report = registry._assertion_report loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report) if tools.config['load_language']: for lang in tools.config['load_language'].split(','): tools.load_language(cr, lang) # STEP 2: Mark other modules to be loaded/updated if update_module: modobj = registry['ir.module.module'] if ('base' in tools.config['init']) or ('base' in tools.config['update']): _logger.info('updating modules list') modobj.update_list(cr, SUPERUSER_ID) _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) mods = [k for k in tools.config['init'] if tools.config['init'][k]] if mods: ids = modobj.search(cr, SUPERUSER_ID, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) if ids: modobj.button_install(cr, SUPERUSER_ID, ids) mods = [k for k in tools.config['update'] if tools.config['update'][k]] if mods: ids = modobj.search(cr, SUPERUSER_ID, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) if ids: modobj.button_upgrade(cr, SUPERUSER_ID, ids) cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) # STEP 3: Load marked modules (skipping base which was done in STEP 1) # 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. # We include the modules 'to remove' in the first step, because # they are part of the "currently installed" modules. They will # be dropped in STEP 6 later, before restarting the loading # process. # IMPORTANT 2: We have to loop here until all relevant modules have been # processed, because in some rare cases the dependencies have # changed, and modules that depend on an uninstalled module # will not be processed on the first pass. # It's especially useful for migrations. previously_processed = -1 while previously_processed < len(processed_modules): previously_processed = len(processed_modules) processed_modules += load_marked_modules(cr, graph, ['installed', 'to upgrade', 'to remove'], force, status, report, loaded_modules, update_module) if update_module: processed_modules += load_marked_modules(cr, graph, ['to install'], force, status, report, loaded_modules, update_module) # load custom models cr.execute('select model from ir_model where state=%s', ('manual',)) for model in cr.dictfetchall(): registry['ir.model'].instanciate(cr, SUPERUSER_ID, model['model'], {}) # STEP 4: Finish and cleanup installations if processed_modules: cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""") for (model, name) in cr.fetchall(): if model in registry and not registry[model].is_transient() and not isinstance(registry[model], openerp.osv.orm.AbstractModel): _logger.warning('The model %s has no access rules, consider adding one. E.g. access_%s,access_%s,model_%s,,1,1,1,1', model, model.replace('.', '_'), model.replace('.', '_'), model.replace('.', '_')) # Temporary warning while we remove access rights on osv_memory objects, as they have # been replaced by owner-only access rights cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""") for (model, name) in cr.fetchall(): if model in registry and registry[model].is_transient(): _logger.warning('The transient model %s (%s) should not have explicit access rules!', model, name) cr.execute("SELECT model from ir_model") for (model,) in cr.fetchall(): if model in registry: registry[model]._check_removed_columns(cr, log=True) else: _logger.warning("Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model) # Cleanup orphan records registry['ir.model.data']._process_end(cr, SUPERUSER_ID, processed_modules) for kind in ('init', 'demo', 'update'): tools.config[kind] = {} cr.commit() # STEP 5: Cleanup menus # Remove menu items that are not referenced by any of other # (child) menu item, ir_values, or ir_model_data. # TODO: This code could be a method of ir_ui_menu. Remove menu without actions of children if update_module: while True: cr.execute('''delete from ir_ui_menu where (id not IN (select parent_id from ir_ui_menu where parent_id is not null)) and (id not IN (select res_id from ir_values where model='ir.ui.menu')) and (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''') cr.commit() if not cr.rowcount: break else: _logger.info('removed %d unused menus', cr.rowcount) # STEP 6: Uninstall modules to remove if update_module: # Remove records referenced from ir_model_data for modules to be # removed (and removed the references from ir_model_data). cr.execute("SELECT name, id FROM ir_module_module WHERE state=%s", ('to remove',)) modules_to_remove = dict(cr.fetchall()) if modules_to_remove: pkgs = reversed([p for p in graph if p.name in modules_to_remove]) for pkg in pkgs: uninstall_hook = pkg.info.get('uninstall_hook') if uninstall_hook: py_module = sys.modules['openerp.addons.%s' % (pkg.name,)] getattr(py_module, uninstall_hook)(cr, registry) registry['ir.module.module'].module_uninstall(cr, SUPERUSER_ID, modules_to_remove.values()) # Recursive reload, should only happen once, because there should be no # modules to remove next time cr.commit() _logger.info('Reloading registry once more after uninstalling modules') return openerp.modules.registry.RegistryManager.new(cr.dbname, force_demo, status, update_module) # STEP 7: verify custom views on every model if update_module: Views = registry['ir.ui.view'] custom_view_test = True for model in registry.models.keys(): if not Views._validate_custom_views(cr, SUPERUSER_ID, model): custom_view_test = False _logger.error('invalid custom view(s) for model %s', model) report.record_result(custom_view_test) if report.failures: _logger.error('At least one test failed when loading the modules.') else: _logger.info('Modules loaded.') # STEP 8: call _register_hook on every model for model in registry.models.values(): model._register_hook(cr) # STEP 9: Run the post-install tests cr.commit() t0 = time.time() t0_sql = openerp.sql_db.sql_counter if openerp.tools.config['test_enable']: cr.execute("SELECT name FROM ir_module_module WHERE state='installed'") for module_name in cr.fetchall(): report.record_result(openerp.modules.module.run_unit_tests(module_name[0], cr.dbname, position=runs_post_install)) _logger.log(25, "All post-tested in %.2fs, %s queries", time.time() - t0, openerp.sql_db.sql_counter - t0_sql) finally: cr.close()
def load_modules(db, force_demo=False, status=None, update_module=False): initialize_sys_path() force = [] if force_demo: force.append("demo") cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): _logger.info("init db") openerp.modules.db.initialize(cr) update_module = True # process auto-installed modules tools.config["init"]["all"] = 1 tools.config["update"]["all"] = 1 if not tools.config["without_demo"]: tools.config["demo"]["all"] = 1 # This is a brand new registry, just created in # openerp.modules.registry.RegistryManager.new(). registry = openerp.registry(cr.dbname) 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") ) # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) graph = openerp.modules.graph.Graph() graph.add_module(cr, "base", force) if not graph: _logger.critical("module base cannot be loaded! (hint: verify addons-path)") raise ImportError("Module `base` cannot be loaded! (hint: verify addons-path)") # processed_modules: for cleanup step after install # loaded_modules: to avoid double loading report = registry._assertion_report loaded_modules, processed_modules = load_module_graph( cr, graph, status, perform_checks=update_module, report=report ) if tools.config["load_language"] or update_module: # some base models are used below, so make sure they are set up registry.setup_models(cr, partial=True) if tools.config["load_language"]: for lang in tools.config["load_language"].split(","): tools.load_language(cr, lang) # STEP 2: Mark other modules to be loaded/updated if update_module: modobj = registry["ir.module.module"] if ("base" in tools.config["init"]) or ("base" in tools.config["update"]): _logger.info("updating modules list") modobj.update_list(cr, SUPERUSER_ID) _check_module_names(cr, itertools.chain(tools.config["init"].keys(), tools.config["update"].keys())) mods = [k for k in tools.config["init"] if tools.config["init"][k]] if mods: ids = modobj.search(cr, SUPERUSER_ID, ["&", ("state", "=", "uninstalled"), ("name", "in", mods)]) if ids: modobj.button_install(cr, SUPERUSER_ID, ids) mods = [k for k in tools.config["update"] if tools.config["update"][k]] if mods: ids = modobj.search(cr, SUPERUSER_ID, ["&", ("state", "=", "installed"), ("name", "in", mods)]) if ids: modobj.button_upgrade(cr, SUPERUSER_ID, ids) cr.execute("update ir_module_module set state=%s where name=%s", ("installed", "base")) modobj.invalidate_cache(cr, SUPERUSER_ID, ["state"]) # STEP 3: Load marked modules (skipping base which was done in STEP 1) # 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. # We include the modules 'to remove' in the first step, because # they are part of the "currently installed" modules. They will # be dropped in STEP 6 later, before restarting the loading # process. # IMPORTANT 2: We have to loop here until all relevant modules have been # processed, because in some rare cases the dependencies have # changed, and modules that depend on an uninstalled module # will not be processed on the first pass. # It's especially useful for migrations. previously_processed = -1 while previously_processed < len(processed_modules): previously_processed = len(processed_modules) processed_modules += load_marked_modules( cr, graph, ["installed", "to upgrade", "to remove"], force, status, report, loaded_modules, update_module, ) if update_module: processed_modules += load_marked_modules( cr, graph, ["to install"], force, status, report, loaded_modules, update_module ) registry.setup_models(cr) # STEP 4: Finish and cleanup installations if processed_modules: cr.execute( """select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""" ) for (model, name) in cr.fetchall(): if ( model in registry and not registry[model].is_transient() and not isinstance(registry[model], openerp.osv.orm.AbstractModel) ): _logger.warning( "The model %s has no access rules, consider adding one. E.g. access_%s,access_%s,model_%s,,1,0,0,0", model, model.replace(".", "_"), model.replace(".", "_"), model.replace(".", "_"), ) # Temporary warning while we remove access rights on osv_memory objects, as they have # been replaced by owner-only access rights cr.execute( """select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""" ) for (model, name) in cr.fetchall(): if model in registry and registry[model].is_transient(): _logger.warning("The transient model %s (%s) should not have explicit access rules!", model, name) cr.execute("SELECT model from ir_model") for (model,) in cr.fetchall(): if model in registry: registry[model]._check_removed_columns(cr, log=True) else: _logger.warning( "Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model, ) # Cleanup orphan records registry["ir.model.data"]._process_end(cr, SUPERUSER_ID, processed_modules) for kind in ("init", "demo", "update"): tools.config[kind] = {} cr.commit() # STEP 5: Uninstall modules to remove if update_module: # Remove records referenced from ir_model_data for modules to be # removed (and removed the references from ir_model_data). cr.execute("SELECT name, id FROM ir_module_module WHERE state=%s", ("to remove",)) modules_to_remove = dict(cr.fetchall()) if modules_to_remove: pkgs = reversed([p for p in graph if p.name in modules_to_remove]) for pkg in pkgs: uninstall_hook = pkg.info.get("uninstall_hook") if uninstall_hook: py_module = sys.modules["openerp.addons.%s" % (pkg.name,)] getattr(py_module, uninstall_hook)(cr, registry) registry["ir.module.module"].module_uninstall(cr, SUPERUSER_ID, modules_to_remove.values()) # Recursive reload, should only happen once, because there should be no # modules to remove next time cr.commit() _logger.info("Reloading registry once more after uninstalling modules") openerp.api.Environment.reset() return openerp.modules.registry.RegistryManager.new(cr.dbname, force_demo, status, update_module) # STEP 6: verify custom views on every model if update_module: Views = registry["ir.ui.view"] custom_view_test = True for model in registry.models.keys(): if not Views._validate_custom_views(cr, SUPERUSER_ID, model): custom_view_test = False _logger.error("invalid custom view(s) for model %s", model) report.record_result(custom_view_test) if report.failures: _logger.error("At least one test failed when loading the modules.") else: _logger.info("Modules loaded.") # STEP 8: call _register_hook on every model for model in registry.models.values(): model._register_hook(cr) # STEP 9: Run the post-install tests cr.commit() t0 = time.time() t0_sql = openerp.sql_db.sql_counter if openerp.tools.config["test_enable"]: cr.execute("SELECT name FROM ir_module_module WHERE state='installed'") for module_name in cr.fetchall(): report.record_result( openerp.modules.module.run_unit_tests(module_name[0], cr.dbname, position=runs_post_install) ) _logger.log( 25, "All post-tested in %.2fs, %s queries", time.time() - t0, openerp.sql_db.sql_counter - t0_sql ) finally: cr.close()
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() force = [] if force_demo: force.append('demo') cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): _logger.info("init db") openerp.modules.db.initialize(cr) tools.config["init"]["all"] = 1 tools.config['update']['all'] = 1 if not tools.config['without_demo']: tools.config["demo"]['all'] = 1 # This is a brand new pool, just created in pooler.get_db_and_pool() pool = pooler.get_pool(cr.dbname) 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')) # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) graph = openerp.modules.graph.Graph() graph.add_module(cr, 'base', force) if not graph: _logger.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: for cleanup step after install # loaded_modules: to avoid double loading report = pool._assertion_report loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report) if tools.config['load_language']: for lang in tools.config['load_language'].split(','): tools.load_language(cr, lang) # STEP 2: Mark other modules to be loaded/updated if update_module: modobj = pool.get('ir.module.module') if ('base' in tools.config['init']) or ('base' in tools.config['update']): _logger.info('updating modules list') modobj.update_list(cr, SUPERUSER_ID) _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) mods = [k for k in tools.config['init'] if tools.config['init'][k]] if mods: ids = modobj.search(cr, SUPERUSER_ID, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) if ids: modobj.button_install(cr, SUPERUSER_ID, ids) mods = [k for k in tools.config['update'] if tools.config['update'][k]] if mods: ids = modobj.search(cr, SUPERUSER_ID, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) if ids: modobj.button_upgrade(cr, SUPERUSER_ID, ids) cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) # STEP 3: Load marked modules (skipping base which was done in STEP 1) # 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. # We include the modules 'to remove' in the first step, because # they are part of the "currently installed" modules. They will # be dropped in STEP 6 later, before restarting the loading # process. states_to_load = ['installed', 'to upgrade', 'to remove'] processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules, update_module) 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, update_module) processed_modules.extend(processed) # load custom models cr.execute('select model from ir_model where state=%s', ('manual',)) for model in cr.dictfetchall(): pool.get('ir.model').instanciate(cr, SUPERUSER_ID, model['model'], {}) # STEP 4: Finish and cleanup installations if processed_modules: cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""") for (model, name) in cr.fetchall(): model_obj = pool.get(model) if model_obj and not model_obj.is_transient(): _logger.warning('The model %s has no access rules, consider adding one. E.g. access_%s,access_%s,model_%s,,1,1,1,1', model, model.replace('.', '_'), model.replace('.', '_'), model.replace('.', '_')) # Temporary warning while we remove access rights on osv_memory objects, as they have # been replaced by owner-only access rights cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""") for (model, name) in cr.fetchall(): model_obj = pool.get(model) if model_obj and model_obj.is_transient(): _logger.warning('The transient model %s (%s) should not have explicit access rules!', model, name) cr.execute("SELECT model from ir_model") for (model,) in cr.fetchall(): obj = pool.get(model) if obj: obj._check_removed_columns(cr, log=True) else: _logger.warning("Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model) # Cleanup orphan records pool.get('ir.model.data')._process_end(cr, SUPERUSER_ID, processed_modules) for kind in ('init', 'demo', 'update'): tools.config[kind] = {} cr.commit() # STEP 5: Cleanup menus # Remove menu items that are not referenced by any of other # (child) menu item, ir_values, or ir_model_data. # TODO: This code could be a method of ir_ui_menu. Remove menu without actions of children if update_module: while True: cr.execute('''delete from ir_ui_menu where (id not IN (select parent_id from ir_ui_menu where parent_id is not null)) and (id not IN (select res_id from ir_values where model='ir.ui.menu')) and (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''') cr.commit() if not cr.rowcount: break else: _logger.info('removed %d unused menus', cr.rowcount) # STEP 6: Uninstall modules to remove if update_module: # Remove records referenced from ir_model_data for modules to be # removed (and removed the references from ir_model_data). cr.execute("SELECT id FROM ir_module_module WHERE state=%s", ('to remove',)) mod_ids_to_remove = [x[0] for x in cr.fetchall()] if mod_ids_to_remove: pool.get('ir.module.module').module_uninstall(cr, SUPERUSER_ID, mod_ids_to_remove) # Recursive reload, should only happen once, because there should be no # modules to remove next time cr.commit() _logger.info('Reloading registry once more after uninstalling modules') return pooler.restart_pool(cr.dbname, force_demo, status, update_module) if report.failures: _logger.error('At least one test failed when loading the modules.') else: _logger.info('Modules loaded.') # STEP 7: call _register_hook on every model for model in pool.models.values(): model._register_hook(cr) finally: cr.close()
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() force = [] if force_demo: force.append('demo') cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): _logger.info("init db") openerp.modules.db.initialize(cr) tools.config["init"]["all"] = 1 tools.config['update']['all'] = 1 if not tools.config['without_demo']: tools.config["demo"]['all'] = 1 # This is a brand new pool, just created in pooler.get_db_and_pool() pool = pooler.get_pool(cr.dbname) 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')) # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) graph = openerp.modules.graph.Graph() graph.add_module(cr, 'base', force) if not graph: _logger.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: for cleanup step after install # loaded_modules: to avoid double loading report = pool._assertion_report loaded_modules, processed_modules = load_module_graph( cr, graph, status, perform_checks=update_module, report=report) if tools.config['load_language']: for lang in tools.config['load_language'].split(','): tools.load_language(cr, lang) # STEP 2: Mark other modules to be loaded/updated if update_module: modobj = pool.get('ir.module.module') if ('base' in tools.config['init']) or ('base' in tools.config['update']): _logger.info('updating modules list') modobj.update_list(cr, SUPERUSER_ID) _check_module_names( cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) mods = [k for k in tools.config['init'] if tools.config['init'][k]] if mods: ids = modobj.search( cr, SUPERUSER_ID, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) if ids: modobj.button_install(cr, SUPERUSER_ID, ids) mods = [ k for k in tools.config['update'] if tools.config['update'][k] ] if mods: ids = modobj.search( cr, SUPERUSER_ID, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) if ids: modobj.button_upgrade(cr, SUPERUSER_ID, ids) cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) # STEP 3: Load marked modules (skipping base which was done in STEP 1) # 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. # We include the modules 'to remove' in the first step, because # they are part of the "currently installed" modules. They will # be dropped in STEP 6 later, before restarting the loading # process. states_to_load = ['installed', 'to upgrade', 'to remove'] processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules, update_module) 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, update_module) processed_modules.extend(processed) # load custom models cr.execute('select model from ir_model where state=%s', ('manual', )) for model in cr.dictfetchall(): pool.get('ir.model').instanciate(cr, SUPERUSER_ID, model['model'], {}) # STEP 4: Finish and cleanup installations if processed_modules: cr.execute( """select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""" ) for (model, name) in cr.fetchall(): model_obj = pool.get(model) if model_obj and not model_obj.is_transient(): _logger.warning( 'The model %s has no access rules, consider adding one. E.g. access_%s,access_%s,model_%s,,1,1,1,1', model, model.replace('.', '_'), model.replace('.', '_'), model.replace('.', '_')) # Temporary warning while we remove access rights on osv_memory objects, as they have # been replaced by owner-only access rights cr.execute( """select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""" ) for (model, name) in cr.fetchall(): model_obj = pool.get(model) if model_obj and model_obj.is_transient(): _logger.warning( 'The transient model %s (%s) should not have explicit access rules!', model, name) cr.execute("SELECT model from ir_model") for (model, ) in cr.fetchall(): obj = pool.get(model) if obj: obj._check_removed_columns(cr, log=True) else: _logger.warning( "Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model) # Cleanup orphan records pool.get('ir.model.data')._process_end(cr, SUPERUSER_ID, processed_modules) for kind in ('init', 'demo', 'update'): tools.config[kind] = {} cr.commit() # STEP 5: Cleanup menus # Remove menu items that are not referenced by any of other # (child) menu item, ir_values, or ir_model_data. # TODO: This code could be a method of ir_ui_menu. Remove menu without actions of children if update_module: while True: cr.execute('''delete from ir_ui_menu where (id not IN (select parent_id from ir_ui_menu where parent_id is not null)) and (id not IN (select res_id from ir_values where model='ir.ui.menu')) and (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''' ) cr.commit() if not cr.rowcount: break else: _logger.info('removed %d unused menus', cr.rowcount) # STEP 6: Uninstall modules to remove if update_module: # Remove records referenced from ir_model_data for modules to be # removed (and removed the references from ir_model_data). cr.execute("SELECT id FROM ir_module_module WHERE state=%s", ('to remove', )) mod_ids_to_remove = [x[0] for x in cr.fetchall()] if mod_ids_to_remove: pool.get('ir.module.module').module_uninstall( cr, SUPERUSER_ID, mod_ids_to_remove) # Recursive reload, should only happen once, because there should be no # modules to remove next time cr.commit() _logger.info( 'Reloading registry once more after uninstalling modules') return pooler.restart_pool(cr.dbname, force_demo, status, update_module) if report.failures: _logger.error('At least one test failed when loading the modules.') else: _logger.info('Modules loaded.') # STEP 7: call _register_hook on every model for model in pool.models.values(): model._register_hook(cr) finally: cr.close()
def load_modules(db, force_demo=False, status=None, update_module=False): initialize_sys_path() force = [] if force_demo: force.append('demo') models_to_check = set() cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): if not update_module: _logger.error( "Database %s not initialized, you can force it with `-i base`", cr.dbname) return _logger.info("init db") openerp.modules.db.initialize(cr) update_module = True # process auto-installed modules tools.config["init"]["all"] = 1 tools.config['update']['all'] = 1 if not tools.config['without_demo']: tools.config["demo"]['all'] = 1 # This is a brand new registry, just created in # openerp.modules.registry.RegistryManager.new(). registry = openerp.registry(cr.dbname) 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')) # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) graph = openerp.modules.graph.Graph() graph.add_module(cr, 'base', force) if not graph: _logger.critical( 'module base cannot be loaded! (hint: verify addons-path)') raise ImportError( 'Module `base` cannot be loaded! (hint: verify addons-path)') # processed_modules: for cleanup step after install # loaded_modules: to avoid double loading report = registry._assertion_report loaded_modules, processed_modules = load_module_graph( cr, graph, status, perform_checks=update_module, report=report, models_to_check=models_to_check) load_lang = tools.config.pop('load_language') if load_lang or update_module: # some base models are used below, so make sure they are set up registry.setup_models(cr, partial=True) if load_lang: for lang in load_lang.split(','): tools.load_language(cr, lang) # STEP 2: Mark other modules to be loaded/updated if update_module: modobj = registry['ir.module.module'] _logger.info('updating modules list') modobj.update_list(cr, SUPERUSER_ID) _check_module_names( cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) mods = [k for k in tools.config['init'] if tools.config['init'][k]] if mods: ids = modobj.search( cr, SUPERUSER_ID, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) if ids: modobj.button_install(cr, SUPERUSER_ID, ids) mods = [ k for k in tools.config['update'] if tools.config['update'][k] ] if mods: ids = modobj.search( cr, SUPERUSER_ID, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) if ids: modobj.button_upgrade(cr, SUPERUSER_ID, ids) cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) modobj.invalidate_cache(cr, SUPERUSER_ID, ['state']) # STEP 3: Load marked modules (skipping base which was done in STEP 1) # 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. # We include the modules 'to remove' in the first step, because # they are part of the "currently installed" modules. They will # be dropped in STEP 6 later, before restarting the loading # process. # IMPORTANT 2: We have to loop here until all relevant modules have been # processed, because in some rare cases the dependencies have # changed, and modules that depend on an uninstalled module # will not be processed on the first pass. # It's especially useful for migrations. previously_processed = -1 while previously_processed < len(processed_modules): previously_processed = len(processed_modules) processed_modules += load_marked_modules( cr, graph, ['installed', 'to upgrade', 'to remove'], force, status, report, loaded_modules, update_module, models_to_check) if update_module: processed_modules += load_marked_modules( cr, graph, ['to install'], force, status, report, loaded_modules, update_module, models_to_check) registry.setup_models(cr) # STEP 3.5: execute migration end-scripts migrations = openerp.modules.migration.MigrationManager(cr, graph) for package in graph: migrations.migrate_module(package, 'end') # STEP 4: Finish and cleanup installations if processed_modules: cr.execute( """select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""" ) for (model, name) in cr.fetchall(): if model in registry and not registry[model].is_transient( ) and not isinstance(registry[model], openerp.osv.orm.AbstractModel): _logger.warning( 'The model %s has no access rules, consider adding one. E.g. access_%s,access_%s,model_%s,base.group_user,1,0,0,0', model, model.replace('.', '_'), model.replace('.', '_'), model.replace('.', '_')) # Temporary warning while we remove access rights on osv_memory objects, as they have # been replaced by owner-only access rights cr.execute( """select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""" ) for (model, name) in cr.fetchall(): if model in registry and registry[model].is_transient(): _logger.warning( 'The transient model %s (%s) should not have explicit access rules!', model, name) cr.execute("SELECT model from ir_model") for (model, ) in cr.fetchall(): if model in registry: registry[model]._check_removed_columns(cr, log=True) elif _logger.isEnabledFor( logging.INFO): # more an info that a warning... _logger.warning( "Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model) # Cleanup orphan records registry['ir.model.data']._process_end(cr, SUPERUSER_ID, processed_modules) for kind in ('init', 'demo', 'update'): tools.config[kind] = {} cr.commit() # STEP 5: Uninstall modules to remove if update_module: # Remove records referenced from ir_model_data for modules to be # removed (and removed the references from ir_model_data). cr.execute("SELECT name, id FROM ir_module_module WHERE state=%s", ('to remove', )) modules_to_remove = dict(cr.fetchall()) if modules_to_remove: pkgs = reversed( [p for p in graph if p.name in modules_to_remove]) for pkg in pkgs: uninstall_hook = pkg.info.get('uninstall_hook') if uninstall_hook: py_module = sys.modules['openerp.addons.%s' % (pkg.name, )] getattr(py_module, uninstall_hook)(cr, registry) registry['ir.module.module'].module_uninstall( cr, SUPERUSER_ID, modules_to_remove.values()) # Recursive reload, should only happen once, because there should be no # modules to remove next time cr.commit() _logger.info( 'Reloading registry once more after uninstalling modules') openerp.api.Environment.reset() return openerp.modules.registry.RegistryManager.new( cr.dbname, force_demo, status, update_module) # STEP 5.5: Verify extended fields on every model # This will fix the schema of all models in a situation such as: # - module A is loaded and defines model M; # - module B is installed/upgraded and extends model M; # - module C is loaded and extends model M; # - module B and C depend on A but not on each other; # The changes introduced by module C are not taken into account by the upgrade of B. if models_to_check: module_objs_to_check = [ registry.models[m] for m in models_to_check ] init_module_models(cr, 'verification in progress', module_objs_to_check) # STEP 6: verify custom views on every model if update_module: Views = registry['ir.ui.view'] for model in registry.models.keys(): try: Views._validate_custom_views(cr, SUPERUSER_ID, model) except Exception as e: _logger.warning('invalid custom view(s) for model %s: %s', model, tools.ustr(e)) if report.failures: _logger.error('At least one test failed when loading the modules.') else: _logger.info('Modules loaded.') # STEP 8: call _register_hook on every model for model in registry.models.values(): model._register_hook(cr) # STEP 9: Run the post-install tests cr.commit() t0 = time.time() t0_sql = openerp.sql_db.sql_counter if openerp.tools.config['test_enable']: if update_module: cr.execute( "SELECT name FROM ir_module_module WHERE state='installed' and name = ANY(%s)", (processed_modules, )) else: cr.execute( "SELECT name FROM ir_module_module WHERE state='installed'" ) for module_name in cr.fetchall(): report.record_result( openerp.modules.module.run_unit_tests( module_name[0], cr.dbname, position=runs_post_install)) _logger.log(25, "All post-tested in %.2fs, %s queries", time.time() - t0, openerp.sql_db.sql_counter - t0_sql) finally: cr.close()
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() force = [] if force_demo: force.append('demo') cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): logger.notifyChannel("init", netsvc.LOG_INFO, "init db") openerp.modules.db.initialize(cr) tools.config["init"]["all"] = 1 tools.config['update']['all'] = 1 if not tools.config['without_demo']: tools.config["demo"]['all'] = 1 # This is a brand new pool, just created in pooler.get_db_and_pool() pool = pooler.get_pool(cr.dbname) processed_modules = [] # for cleanup step after install loaded_modules = [] # to avoid double loading report = tools.assertion_report() 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')) # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) graph = openerp.modules.graph.Graph() graph.add_module(cr, 'base', force) 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)')) 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(','): tools.load_language(cr, lang) # STEP 2: Mark other modules to be loaded/updated if update_module: modobj = pool.get('ir.module.module') if ('base' in tools.config['init']) or ('base' in tools.config['update']): logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list') modobj.update_list(cr, 1) _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) mods = [k for k in tools.config['init'] if tools.config['init'][k]] if mods: ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) if ids: modobj.button_install(cr, 1, ids) mods = [k for k in tools.config['update'] if tools.config['update'][k]] if mods: ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) if ids: modobj.button_upgrade(cr, 1, ids) cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) # STEP 3: Load marked modules (skipping base which was done in STEP 1) # 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',)) for model in cr.dictfetchall(): pool.get('ir.model').instanciate(cr, 1, model['model'], {}) # STEP 4: Finish and cleanup if processed_modules: cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""") for (model, name) in cr.fetchall(): model_obj = pool.get(model) if model_obj and not isinstance(model_obj, osv.osv.osv_memory): logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name)) # Temporary warning while we remove access rights on osv_memory objects, as they have # been replaced by owner-only access rights cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""") for (model, name) in cr.fetchall(): model_obj = pool.get(model) if isinstance(model_obj, osv.osv.osv_memory): logger.notifyChannel('init', netsvc.LOG_WARNING, 'In-memory object %s (%s) should not have explicit access rules!' % (model, name)) cr.execute("SELECT model from ir_model") for (model,) in cr.fetchall(): obj = pool.get(model) if obj: obj._check_removed_columns(cr, log=True) else: logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model) # Cleanup orphan records pool.get('ir.model.data')._process_end(cr, 1, processed_modules) if report.get_report(): logger.notifyChannel('init', netsvc.LOG_INFO, report) for kind in ('init', 'demo', 'update'): tools.config[kind] = {} cr.commit() if update_module: # Remove records referenced from ir_model_data for modules to be # removed (and removed the references from ir_model_data). cr.execute("select id,name from ir_module_module where state=%s", ('to remove',)) for mod_id, mod_name in cr.fetchall(): cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,)) for rmod, rid in cr.fetchall(): uid = 1 rmod_module= pool.get(rmod) if rmod_module: # TODO group by module so that we can delete multiple ids in a call rmod_module.unlink(cr, uid, [rid]) else: logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not locate %s to remove res=%d' % (rmod,rid)) cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,)) cr.commit() # Remove menu items that are not referenced by any of other # (child) menu item, ir_values, or ir_model_data. # This code could be a method of ir_ui_menu. # TODO: remove menu without actions of children while True: cr.execute('''delete from ir_ui_menu where (id not IN (select parent_id from ir_ui_menu where parent_id is not null)) and (id not IN (select res_id from ir_values where model='ir.ui.menu')) and (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''') cr.commit() if not cr.rowcount: break else: logger.notifyChannel('init', netsvc.LOG_INFO, 'removed %d unused menus' % (cr.rowcount,)) # Pretend that modules to be removed are actually uninstalled. cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',)) cr.commit() finally: cr.close()