1
0
Fork 0

Merge pull request #20 from olysonek/udev_pr

Implement udev-based device matching
This commit is contained in:
Jaroslav Škarvada 2017-03-01 10:09:41 +01:00 committed by GitHub
commit f775a3cab0
17 changed files with 86 additions and 10 deletions

View file

@ -24,6 +24,7 @@ class Application(object):
monitors_repository = monitors.Repository()
hardware_inventory = hardware.Inventory()
device_matcher = hardware.DeviceMatcher()
device_matcher_udev = hardware.DeviceMatcherUdev()
plugin_instance_factory = plugins.instance.Factory()
self.variables = profiles.variables.Variables()
@ -33,7 +34,8 @@ class Application(object):
else:
log.info("dynamic tuning is globally disabled")
plugins_repository = plugins.Repository(monitors_repository, storage_factory, hardware_inventory, device_matcher, plugin_instance_factory, self.config, self.variables)
plugins_repository = plugins.Repository(monitors_repository, storage_factory, hardware_inventory,\
device_matcher, device_matcher_udev, plugin_instance_factory, self.config, self.variables)
def_instance_priority = int(self.config.get(consts.CFG_DEFAULT_INSTANCE_PRIORITY, consts.CFG_DEF_DEFAULT_INSTANCE_PRIORITY))
unit_manager = units.Manager(plugins_repository, monitors_repository, def_instance_priority)

View file

@ -71,6 +71,7 @@ class GuiPluginLoader(PluginLoader):
monitors_repository = monitors.Repository()
hardware_inventory = hardware.Inventory()
device_matcher = hardware.DeviceMatcher()
device_matcher_udev = hardware.DeviceMatcherUdev()
plugin_instance_factory = plugins.instance.Factory()
self.repo = repository.Repository(
@ -78,6 +79,7 @@ class GuiPluginLoader(PluginLoader):
storage_factory,
hardware_inventory,
device_matcher,
device_matcher_udev,
plugin_instance_factory,
None,
None

View file

@ -1,2 +1,3 @@
from inventory import *
from device_matcher import *
from device_matcher_udev import *

View file

@ -0,0 +1,18 @@
import device_matcher
import re
__all__ = ["DeviceMatcherUdev"]
class DeviceMatcherUdev(device_matcher.DeviceMatcher):
def match(self, regex, device):
"""
Match a device against the udev regex in tuning profiles.
device is a pyudev.Device object
"""
properties = ''
for key, val in device.items():
properties += key + '=' + val + '\n'
return re.search(regex, properties, re.MULTILINE) is not None

View file

@ -28,6 +28,10 @@ class Inventory(object):
self._subscriptions = {}
def get_device(self, subsystem, sys_name):
"""Get a pyudev.Device object for the sys_name (e.g. 'sda')."""
return pyudev.Devices.from_name(self._udev_context, subsystem, sys_name)
def get_devices(self, subsystem):
"""Get list of devices on a given subsystem."""
return self._udev_context.list_devices(subsystem=subsystem)

View file

@ -17,13 +17,14 @@ class Plugin(object):
Intentionally a lot of logic is included in the plugin to increase plugin flexibility.
"""
def __init__(self, monitors_repository, storage_factory, hardware_inventory, device_matcher, instance_factory, global_cfg, variables):
def __init__(self, monitors_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, instance_factory, global_cfg, variables):
"""Plugin constructor."""
self._storage = storage_factory.create(self.__class__.__name__)
self._monitors_repository = monitors_repository
self._hardware_inventory = hardware_inventory
self._device_matcher = device_matcher
self._device_matcher_udev = device_matcher_udev
self._instance_factory = instance_factory
self._instances = collections.OrderedDict()
@ -80,13 +81,13 @@ class Plugin(object):
# Interface for manipulation with instances of the plugin.
#
def create_instance(self, name, devices_expression, options):
def create_instance(self, name, devices_expression, devices_udev_regex, options):
"""Create new instance of the plugin and seize the devices."""
if name in self._instances:
raise Exception("Plugin instance with name '%s' already exists." % name)
effective_options = self._get_effective_options(options)
instance = self._instance_factory.create(self, name, devices_expression, effective_options)
instance = self._instance_factory.create(self, name, devices_expression, devices_udev_regex, effective_options)
self._instances[name] = instance
return instance
@ -133,8 +134,21 @@ class Plugin(object):
self._assigned_devices = set()
self._free_devices = set()
def _get_device_objects(self, devices):
"""Override this in a subclass to transform a list of device names (e.g. ['sda'])
to a list of pyudev.Device objects, if your plugin supports it"""
return None
def _get_matching_devices(self, instance, devices):
return set(self._device_matcher.match_list(instance.devices_expression, devices))
if instance.devices_udev_regex is None:
return set(self._device_matcher.match_list(instance.devices_expression, devices))
else:
udev_devices = self._get_device_objects(devices)
if udev_devices is None:
log.error("Plugin '%s' does not support the 'devices_udev_regex' option", self.name)
return set()
udev_devices = self._device_matcher_udev.match_list(instance.devices_udev_regex, udev_devices)
return set(map(lambda x: x.sys_name, udev_devices))
def assign_free_devices(self, instance):
if not self._devices_supported:

View file

@ -2,10 +2,11 @@ class Instance(object):
"""
"""
def __init__(self, plugin, name, devices_expression, options):
def __init__(self, plugin, name, devices_expression, devices_udev_regex, options):
self._plugin = plugin
self._name = name
self._devices_expression = devices_expression
self._devices_udev_regex = devices_udev_regex
self._options = options
self._active = True
@ -40,6 +41,10 @@ class Instance(object):
def devices(self):
return self._devices
@property
def devices_udev_regex(self):
return self._devices_udev_regex
@property
def options(self):
return self._options

View file

@ -40,6 +40,9 @@ class CPULatencyPlugin(base.Plugin):
self._assigned_devices = set()
def _get_device_objects(self, devices):
return map(lambda x: self._hardware_inventory.get_device("cpu", x), devices)
@classmethod
def _get_config_options(self):
return {

View file

@ -33,6 +33,9 @@ class DiskPlugin(hotplug.Plugin):
self._assigned_devices = set()
def _get_device_objects(self, devices):
return map(lambda x: self._hardware_inventory.get_device("block", x), devices)
@classmethod
def _device_is_supported(cls, device):
return device.device_type == "disk" and \

View file

@ -33,6 +33,9 @@ class NetTuningPlugin(base.Plugin):
log.debug("devices: %s" % str(self._free_devices));
def _get_device_objects(self, devices):
return map(lambda x: self._hardware_inventory.get_device("net", x), devices)
def _instance_init(self, instance):
instance._has_static_tuning = True
instance._has_dynamic_tuning = True

View file

@ -28,6 +28,9 @@ class DiskPlugin(hotplug.Plugin):
self._assigned_devices = set()
def _get_device_objects(self, devices):
return map(lambda x: self._hardware_inventory.get_device("scsi", x), devices)
@classmethod
def _device_is_supported(cls, device):
return device.device_type == "scsi_host"

View file

@ -21,6 +21,9 @@ class USBPlugin(base.Plugin):
self._cmd = commands()
def _get_device_objects(self, devices):
return map(lambda x: self._hardware_inventory.get_device("usb", x), devices)
@classmethod
def _get_config_options(self):
return {

View file

@ -22,6 +22,9 @@ class VideoPlugin(base.Plugin):
self._cmd = commands()
def _get_device_objects(self, devices):
return map(lambda x: self._hardware_inventory.get_device("drm", x), devices)
@classmethod
def _get_config_options(self):
return {

View file

@ -8,13 +8,14 @@ __all__ = ["Repository"]
class Repository(PluginLoader):
def __init__(self, monitor_repository, storage_factory, hardware_inventory, device_matcher, plugin_instance_factory, global_cfg, variables):
def __init__(self, monitor_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, plugin_instance_factory, global_cfg, variables):
super(self.__class__, self).__init__()
self._plugins = set()
self._monitor_repository = monitor_repository
self._storage_factory = storage_factory
self._hardware_inventory = hardware_inventory
self._device_matcher = device_matcher
self._device_matcher_udev = device_matcher_udev
self._plugin_instance_factory = plugin_instance_factory
self._global_cfg = global_cfg
self._variables = variables
@ -32,7 +33,7 @@ class Repository(PluginLoader):
log.debug("creating plugin %s" % plugin_name)
plugin_cls = self.load_plugin(plugin_name)
plugin_instance = plugin_cls(self._monitor_repository, self._storage_factory, self._hardware_inventory, self._device_matcher,\
self._plugin_instance_factory, self._global_cfg, self._variables)
self._device_matcher_udev, self._plugin_instance_factory, self._global_cfg, self._variables)
self._plugins.add(plugin_instance)
return plugin_instance

View file

@ -32,6 +32,8 @@ class Merger(object):
profile_a.units[unit_name].type = unit.type
profile_a.units[unit_name].enabled = unit.enabled
profile_a.units[unit_name].devices = unit.devices
if unit.devices_udev_regex != None:
profile_a.units[unit_name].devices_udev_regex = unit.devices_udev_regex
if unit_name == "script" and profile_a.units[unit_name].options.get("script", None) is not None:
script = profile_a.units[unit_name].options.get("script", None)
profile_a.units[unit_name].options.update(unit.options)

View file

@ -3,7 +3,7 @@ class Unit(object):
Unit description.
"""
__slots__ = [ "_name", "_type", "_enabled", "_replace", "_devices", "_options" ]
__slots__ = [ "_name", "_type", "_enabled", "_replace", "_devices", "_devices_udev_regex", "_options" ]
def __init__(self, name, config):
self._name = name
@ -11,6 +11,7 @@ class Unit(object):
self._enabled = config.pop("enabled", True) in [True, "true", 1]
self._replace = config.pop("replace", False) in [True, "true", 1]
self._devices = config.pop("devices", "*")
self._devices_udev_regex = config.pop("devices_udev_regex", None)
self._options = dict(config)
@property
@ -45,6 +46,14 @@ class Unit(object):
def devices(self, value):
self._devices = value
@property
def devices_udev_regex(self):
return self._devices_udev_regex
@devices_udev_regex.setter
def devices_udev_regex(self, value):
self._devices_udev_regex = value
@property
def options(self):
return self._options

View file

@ -62,7 +62,7 @@ class Manager(object):
if plugin is None:
continue
log.debug("creating '%s' (%s)" % (instance_info.name, instance_info.type))
new_instance = plugin.create_instance(instance_info.name, instance_info.devices, instance_info.options)
new_instance = plugin.create_instance(instance_info.name, instance_info.devices, instance_info.devices_udev_regex, instance_info.options)
plugin.assign_free_devices(new_instance)
plugin.initialize_instance(new_instance)
self._instances.append(new_instance)