X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=tools%2Fmodule_manager.py;h=dd1d92beed17f3345d9e5e268ecebee5ae5c964c;hb=2cfae5e4569bf595e238a4ccb56a6ef5544a3265;hp=39ad5cf403671700014d8cd13c9db0d9d8fbfe77;hpb=0bb85c9c85b7aeb74761c6a565e554674a7661c1;p=vswitchperf.git diff --git a/tools/module_manager.py b/tools/module_manager.py index 39ad5cf4..dd1d92be 100644 --- a/tools/module_manager.py +++ b/tools/module_manager.py @@ -1,4 +1,4 @@ -# Copyright 2015 Intel Corporation. +# Copyright 2015-2017 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,9 +14,10 @@ """Simple kernel module manager implementation. """ - +import os import subprocess import logging + from tools import tasks class ModuleManager(object): @@ -28,62 +29,138 @@ class ModuleManager(object): def __init__(self): """Initializes data """ - self._modules = None + self._modules = [] + + def insert_module(self, module, auto_remove=True): + """Method inserts given module. + + In case that module name ends with .ko suffix then insmod will + be used for its insertion. Otherwise modprobe will be called. + + :param module: a name of kernel module + :param auto_remove: if True (by default), then module will be + automatically removed by remove_modules() method + """ + module_base_name = os.path.basename(os.path.splitext(module)[0]) + + if self.is_module_inserted(module): + self._logger.info('Module already loaded \'%s\'.', module_base_name) + # add it to internal list, so we can try to remove it at the end + if auto_remove: + self._modules.append(module) + return + + try: + if module.endswith('.ko'): + # load module dependecies first, but suppress automatic + # module removal at the end; Just for case, that module + # depends on generic module + for depmod in self.get_module_dependecies(module): + self.insert_module(depmod, auto_remove=False) + + tasks.run_task(['sudo', 'insmod', module], self._logger, + 'Insmod module \'%s\'...' % module_base_name, True) + else: + tasks.run_task(['sudo', 'modprobe', module], self._logger, + 'Modprobe module \'%s\'...' % module_base_name, True) + if auto_remove: + self._modules.append(module) + except subprocess.CalledProcessError: + # in case of error, show full module name + self._logger.error('Unable to insert module \'%s\'.', module) + raise # fail catastrophically def insert_modules(self, modules): - """Method inserts list of modules. In case that module name ends - with .ko suffix then insmod will be used for its insertion. Otherwise - modprobe will be called. + """Method inserts list of modules. - :returns: None + :param modules: a list of modules to be inserted """ - self._modules = modules for module in modules: - if ModuleManager.is_module_inserted(module): - continue + self.insert_module(module) - try: - if module.endswith('.ko'): - tasks.run_task(['sudo', 'insmod', module], self._logger, - 'Insmod module \'%s\'...' % module, True) - else: - tasks.run_task(['sudo', 'modprobe', module], self._logger, - 'Modprobe module \'%s\'...' % module, True) + def remove_module(self, module): + """Removes a single module. - except subprocess.CalledProcessError: - self._logger.error('Unable to insert module \'%s\'.', module) - raise # fail catastrophically - - def remove_modules(self): - """Removes all modules that have been previously instereted. + :param module: a name of kernel module """ - for module in self._modules: - # first check if module is loaded - if not ModuleManager.is_module_inserted(module): - continue + if self.is_module_inserted(module): + # get module base name, i.e strip path and .ko suffix if possible + module_base_name = os.path.basename(os.path.splitext(module)[0]) try: - # rmmod supports both simple module name and full module path - # with .ko suffix - tasks.run_task(['sudo', 'rmmod', module], self._logger, - 'Removing module \'%s\'...' % module, True) + self._logger.info('Removing module \'%s\'...', module_base_name) + subprocess.check_call('sudo rmmod {}'.format(module_base_name), + shell=True, stderr=subprocess.DEVNULL) + # in case that module was loaded automatically by modprobe + # to solve dependecies, then it is not in internal list of modules + if module in self._modules: + self._modules.remove(module) except subprocess.CalledProcessError: - self._logger.error('Unable to remove module \'%s\'.', module) - continue + # in case of error, show full module name... + self._logger.info('Unable to remove module \'%s\'.', module) + # ...and list of dependend modules, if there are any + module_details = self.get_module_details(module_base_name) + if module_details: + mod_dep = module_details.split(' ')[3].rstrip(',') + if mod_dep[0] != '-': + self._logger.debug('Module \'%s\' is used by module(s) \'%s\'.', + module_base_name, mod_dep) - @staticmethod - def is_module_inserted(module): + def remove_modules(self): + """Removes all modules that have been previously inserted. + """ + # remove modules in reverse order to respect their dependencies + for module in reversed(self._modules): + self.remove_module(module) + + def is_module_inserted(self, module): """Check if a module is inserted on system. + + :param module: a name of kernel module """ - # get module base name, i.e strip path and .ko suffix if possible - module_base_name = module.split('.')[0].split('/').pop() + module_base_name = os.path.basename(os.path.splitext(module)[0]) + + return self.get_module_details(module_base_name) != None + + @staticmethod + def get_module_details(module): + """Return details about given module + :param module: a name of kernel module + :returns: In case that module is loaded in OS, then corresponding + line from /proc/modules will be returned. Otherwise it returns None. + """ # get list of modules from kernel with open('/proc/modules') as mod_file: loaded_mods = mod_file.readlines() - # first check if module is loaded + # check if module is loaded for line in loaded_mods: - if line.startswith(module_base_name): - return True - return False + # underscores '_' and dashes '-' in module names are interchangeable, so we + # have to normalize module names before comparision + if line.split(' ')[0].replace('-', '_') == module.replace('-', '_'): + return line + + return None + + def get_module_dependecies(self, module): + """Return list of modules, which must be loaded before module itself + + :param module: a name of kernel module + :returns: In case that module has any dependencies, then list of module + names will be returned. Otherwise it returns empty list, i.e. []. + """ + deps = '' + try: + # get list of module dependecies from kernel + deps = subprocess.check_output('modinfo -F depends {}'.format(module), + shell=True).decode().rstrip('\n') + except subprocess.CalledProcessError: + # in case of error, show full module name... + self._logger.info('Unable to get list of dependecies for module \'%s\'.', module) + # ...and try to continue, just for case that dependecies are already loaded + + if len(deps): + return deps.split(',') + else: + return []