Implement udev-based device matching
A new option 'devices_udev_regex' can be used in profile configuration to specify devices to which a plugin instance should be applied. The option can contain a python regular expression, as specified in https://docs.python.org/2/library/re.html#regular-expression-syntax. The expression is effectively matched against the output of udevadm info --query=property -n <device_path> If the option 'devices_udev_regex' is specified, the 'devices' option is ignored. If it is not specified, then the matching is done the same way as previously, i.e. against 'devices'. Signed-off-by: Ondřej Lysoněk <olysonek@redhat.com>
This commit is contained in:
parent
4c1ed69f4a
commit
f14ae65bd2
17 changed files with 86 additions and 10 deletions
|
|
@ -24,6 +24,7 @@ class Application(object):
|
||||||
monitors_repository = monitors.Repository()
|
monitors_repository = monitors.Repository()
|
||||||
hardware_inventory = hardware.Inventory()
|
hardware_inventory = hardware.Inventory()
|
||||||
device_matcher = hardware.DeviceMatcher()
|
device_matcher = hardware.DeviceMatcher()
|
||||||
|
device_matcher_udev = hardware.DeviceMatcherUdev()
|
||||||
plugin_instance_factory = plugins.instance.Factory()
|
plugin_instance_factory = plugins.instance.Factory()
|
||||||
self.variables = profiles.variables.Variables()
|
self.variables = profiles.variables.Variables()
|
||||||
|
|
||||||
|
|
@ -33,7 +34,8 @@ class Application(object):
|
||||||
else:
|
else:
|
||||||
log.info("dynamic tuning is globally disabled")
|
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))
|
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)
|
unit_manager = units.Manager(plugins_repository, monitors_repository, def_instance_priority)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ class GuiPluginLoader(PluginLoader):
|
||||||
monitors_repository = monitors.Repository()
|
monitors_repository = monitors.Repository()
|
||||||
hardware_inventory = hardware.Inventory()
|
hardware_inventory = hardware.Inventory()
|
||||||
device_matcher = hardware.DeviceMatcher()
|
device_matcher = hardware.DeviceMatcher()
|
||||||
|
device_matcher_udev = hardware.DeviceMatcherUdev()
|
||||||
plugin_instance_factory = plugins.instance.Factory()
|
plugin_instance_factory = plugins.instance.Factory()
|
||||||
|
|
||||||
self.repo = repository.Repository(
|
self.repo = repository.Repository(
|
||||||
|
|
@ -78,6 +79,7 @@ class GuiPluginLoader(PluginLoader):
|
||||||
storage_factory,
|
storage_factory,
|
||||||
hardware_inventory,
|
hardware_inventory,
|
||||||
device_matcher,
|
device_matcher,
|
||||||
|
device_matcher_udev,
|
||||||
plugin_instance_factory,
|
plugin_instance_factory,
|
||||||
None,
|
None,
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
from inventory import *
|
from inventory import *
|
||||||
from device_matcher import *
|
from device_matcher import *
|
||||||
|
from device_matcher_udev import *
|
||||||
|
|
|
||||||
18
tuned/hardware/device_matcher_udev.py
Normal file
18
tuned/hardware/device_matcher_udev.py
Normal 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
|
||||||
|
|
@ -28,6 +28,10 @@ class Inventory(object):
|
||||||
|
|
||||||
self._subscriptions = {}
|
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):
|
def get_devices(self, subsystem):
|
||||||
"""Get list of devices on a given subsystem."""
|
"""Get list of devices on a given subsystem."""
|
||||||
return self._udev_context.list_devices(subsystem=subsystem)
|
return self._udev_context.list_devices(subsystem=subsystem)
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,14 @@ class Plugin(object):
|
||||||
Intentionally a lot of logic is included in the plugin to increase plugin flexibility.
|
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."""
|
"""Plugin constructor."""
|
||||||
|
|
||||||
self._storage = storage_factory.create(self.__class__.__name__)
|
self._storage = storage_factory.create(self.__class__.__name__)
|
||||||
self._monitors_repository = monitors_repository
|
self._monitors_repository = monitors_repository
|
||||||
self._hardware_inventory = hardware_inventory
|
self._hardware_inventory = hardware_inventory
|
||||||
self._device_matcher = device_matcher
|
self._device_matcher = device_matcher
|
||||||
|
self._device_matcher_udev = device_matcher_udev
|
||||||
self._instance_factory = instance_factory
|
self._instance_factory = instance_factory
|
||||||
|
|
||||||
self._instances = collections.OrderedDict()
|
self._instances = collections.OrderedDict()
|
||||||
|
|
@ -80,13 +81,13 @@ class Plugin(object):
|
||||||
# Interface for manipulation with instances of the plugin.
|
# 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."""
|
"""Create new instance of the plugin and seize the devices."""
|
||||||
if name in self._instances:
|
if name in self._instances:
|
||||||
raise Exception("Plugin instance with name '%s' already exists." % name)
|
raise Exception("Plugin instance with name '%s' already exists." % name)
|
||||||
|
|
||||||
effective_options = self._get_effective_options(options)
|
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
|
self._instances[name] = instance
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
@ -133,8 +134,21 @@ class Plugin(object):
|
||||||
self._assigned_devices = set()
|
self._assigned_devices = set()
|
||||||
self._free_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):
|
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):
|
def assign_free_devices(self, instance):
|
||||||
if not self._devices_supported:
|
if not self._devices_supported:
|
||||||
|
|
|
||||||
|
|
@ -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._plugin = plugin
|
||||||
self._name = name
|
self._name = name
|
||||||
self._devices_expression = devices_expression
|
self._devices_expression = devices_expression
|
||||||
|
self._devices_udev_regex = devices_udev_regex
|
||||||
self._options = options
|
self._options = options
|
||||||
|
|
||||||
self._active = True
|
self._active = True
|
||||||
|
|
@ -40,6 +41,10 @@ class Instance(object):
|
||||||
def devices(self):
|
def devices(self):
|
||||||
return self._devices
|
return self._devices
|
||||||
|
|
||||||
|
@property
|
||||||
|
def devices_udev_regex(self):
|
||||||
|
return self._devices_udev_regex
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def options(self):
|
def options(self):
|
||||||
return self._options
|
return self._options
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ class CPULatencyPlugin(base.Plugin):
|
||||||
|
|
||||||
self._assigned_devices = set()
|
self._assigned_devices = set()
|
||||||
|
|
||||||
|
def _get_device_objects(self, devices):
|
||||||
|
return map(lambda x: self._hardware_inventory.get_device("cpu", x), devices)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_config_options(self):
|
def _get_config_options(self):
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ class DiskPlugin(hotplug.Plugin):
|
||||||
|
|
||||||
self._assigned_devices = set()
|
self._assigned_devices = set()
|
||||||
|
|
||||||
|
def _get_device_objects(self, devices):
|
||||||
|
return map(lambda x: self._hardware_inventory.get_device("block", x), devices)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _device_is_supported(cls, device):
|
def _device_is_supported(cls, device):
|
||||||
return device.device_type == "disk" and \
|
return device.device_type == "disk" and \
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ class NetTuningPlugin(base.Plugin):
|
||||||
|
|
||||||
log.debug("devices: %s" % str(self._free_devices));
|
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):
|
def _instance_init(self, instance):
|
||||||
instance._has_static_tuning = True
|
instance._has_static_tuning = True
|
||||||
instance._has_dynamic_tuning = True
|
instance._has_dynamic_tuning = True
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ class DiskPlugin(hotplug.Plugin):
|
||||||
|
|
||||||
self._assigned_devices = set()
|
self._assigned_devices = set()
|
||||||
|
|
||||||
|
def _get_device_objects(self, devices):
|
||||||
|
return map(lambda x: self._hardware_inventory.get_device("scsi", x), devices)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _device_is_supported(cls, device):
|
def _device_is_supported(cls, device):
|
||||||
return device.device_type == "scsi_host"
|
return device.device_type == "scsi_host"
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ class USBPlugin(base.Plugin):
|
||||||
|
|
||||||
self._cmd = commands()
|
self._cmd = commands()
|
||||||
|
|
||||||
|
def _get_device_objects(self, devices):
|
||||||
|
return map(lambda x: self._hardware_inventory.get_device("usb", x), devices)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_config_options(self):
|
def _get_config_options(self):
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ class VideoPlugin(base.Plugin):
|
||||||
|
|
||||||
self._cmd = commands()
|
self._cmd = commands()
|
||||||
|
|
||||||
|
def _get_device_objects(self, devices):
|
||||||
|
return map(lambda x: self._hardware_inventory.get_device("drm", x), devices)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_config_options(self):
|
def _get_config_options(self):
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,14 @@ __all__ = ["Repository"]
|
||||||
|
|
||||||
class Repository(PluginLoader):
|
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__()
|
super(self.__class__, self).__init__()
|
||||||
self._plugins = set()
|
self._plugins = set()
|
||||||
self._monitor_repository = monitor_repository
|
self._monitor_repository = monitor_repository
|
||||||
self._storage_factory = storage_factory
|
self._storage_factory = storage_factory
|
||||||
self._hardware_inventory = hardware_inventory
|
self._hardware_inventory = hardware_inventory
|
||||||
self._device_matcher = device_matcher
|
self._device_matcher = device_matcher
|
||||||
|
self._device_matcher_udev = device_matcher_udev
|
||||||
self._plugin_instance_factory = plugin_instance_factory
|
self._plugin_instance_factory = plugin_instance_factory
|
||||||
self._global_cfg = global_cfg
|
self._global_cfg = global_cfg
|
||||||
self._variables = variables
|
self._variables = variables
|
||||||
|
|
@ -32,7 +33,7 @@ class Repository(PluginLoader):
|
||||||
log.debug("creating plugin %s" % plugin_name)
|
log.debug("creating plugin %s" % plugin_name)
|
||||||
plugin_cls = self.load_plugin(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,\
|
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)
|
self._plugins.add(plugin_instance)
|
||||||
return plugin_instance
|
return plugin_instance
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ class Merger(object):
|
||||||
profile_a.units[unit_name].type = unit.type
|
profile_a.units[unit_name].type = unit.type
|
||||||
profile_a.units[unit_name].enabled = unit.enabled
|
profile_a.units[unit_name].enabled = unit.enabled
|
||||||
profile_a.units[unit_name].devices = unit.devices
|
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:
|
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)
|
script = profile_a.units[unit_name].options.get("script", None)
|
||||||
profile_a.units[unit_name].options.update(unit.options)
|
profile_a.units[unit_name].options.update(unit.options)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ class Unit(object):
|
||||||
Unit description.
|
Unit description.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = [ "_name", "_type", "_enabled", "_replace", "_devices", "_options" ]
|
__slots__ = [ "_name", "_type", "_enabled", "_replace", "_devices", "_devices_udev_regex", "_options" ]
|
||||||
|
|
||||||
def __init__(self, name, config):
|
def __init__(self, name, config):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
@ -11,6 +11,7 @@ class Unit(object):
|
||||||
self._enabled = config.pop("enabled", True) in [True, "true", 1]
|
self._enabled = config.pop("enabled", True) in [True, "true", 1]
|
||||||
self._replace = config.pop("replace", False) in [True, "true", 1]
|
self._replace = config.pop("replace", False) in [True, "true", 1]
|
||||||
self._devices = config.pop("devices", "*")
|
self._devices = config.pop("devices", "*")
|
||||||
|
self._devices_udev_regex = config.pop("devices_udev_regex", None)
|
||||||
self._options = dict(config)
|
self._options = dict(config)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -45,6 +46,14 @@ class Unit(object):
|
||||||
def devices(self, value):
|
def devices(self, value):
|
||||||
self._devices = 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
|
@property
|
||||||
def options(self):
|
def options(self):
|
||||||
return self._options
|
return self._options
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ class Manager(object):
|
||||||
if plugin is None:
|
if plugin is None:
|
||||||
continue
|
continue
|
||||||
log.debug("creating '%s' (%s)" % (instance_info.name, instance_info.type))
|
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.assign_free_devices(new_instance)
|
||||||
plugin.initialize_instance(new_instance)
|
plugin.initialize_instance(new_instance)
|
||||||
self._instances.append(new_instance)
|
self._instances.append(new_instance)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue