1
0
Fork 0

plugins: tuning units are handled within plugin classes

This change moves a lot of logic into the plugins, namely
the enumeration, seizing, and releasing of the devices.
However it allows the plugins to control the process in
more detail. This will be also utilised when tuning the
devices, which were dynamically added into the system.

WATCH OUT: only cpu plugin has been updated
This commit is contained in:
Jan Vcelak 2013-01-09 16:23:02 +01:00
parent 68992725f2
commit f4cc64a3a9
18 changed files with 535 additions and 390 deletions

View file

@ -21,12 +21,13 @@ class Application(object):
storage_provider = storage.PickleProvider()
storage_factory = storage.Factory(storage_provider)
unit_factory = units.Factory()
device_matcher = units.DeviceMatcher()
hardware_enumerator = hardware.Enumerator()
monitors_repository = monitors.Repository()
plugins_repository = plugins.Repository(storage_factory, monitors_repository, hardware_enumerator)
unit_manager = units.Manager(plugins_repository, monitors_repository, unit_factory, device_matcher)
hardware_inventory = hardware.Inventory()
device_matcher = hardware.DeviceMatcher()
plugin_instance_factory = plugins.instance.Factory()
plugins_repository = plugins.Repository(monitors_repository, storage_factory, hardware_inventory, device_matcher, plugin_instance_factory)
unit_manager = units.Manager(plugins_repository, monitors_repository)
profile_factory = profiles.Factory()
profile_merger = profiles.Merger()

View file

@ -58,16 +58,17 @@ class Daemon(object):
self._unit_manager.create(self._profile.units)
self._save_active_profile(self._profile.name)
self._unit_manager.plugins_repository.do_static_tuning()
self._unit_manager.start_tuning()
self._terminate.clear()
while not self._terminate.wait(10):
log.debug("updating monitors")
self._unit_manager.monitors_repository.update()
self._unit_manager.update_monitors()
log.debug("performing tunings")
self._unit_manager.plugins_repository.update()
self._unit_manager.update_tuning()
self._unit_manager.delete_all()
self._unit_manager.stop_tuning()
self._unit_manager.destroy_all()
def _save_active_profile(self, profile_name):
try:

View file

@ -1 +1,2 @@
from enumerator import *
from inventory import *
from device_matcher import *

View file

@ -1,6 +1,8 @@
import fnmatch
import re
__all__ = ["DeviceMatcher"]
class DeviceMatcher(object):
"""
Device name matching against the devices specification in tuning profiles.

View file

@ -1,48 +0,0 @@
import pyudev
import re
__all__ = ["Enumerator"]
class Enumerator(object):
"""
Class for system devices enumeration.
"""
def __init__(self):
self._context = pyudev.Context()
def _device_fulfills_requirements(self, device, requirements):
for attribute, requirement in requirements.items():
if hasattr(device, attribute):
value = getattr(device, attribute)
if requirement is None:
pass
elif isinstance(requirement, str):
if value != requirement:
return False
elif isinstance(requirement, re._pattern_type):
if requirement.match(value) is None:
return False
else:
raise TypeError("Invalid type of a requirement for a device.")
else:
# custom callbacks with non-existing attributes
if callable(requirement):
if not requirement(device):
return False
else:
return False
else:
return True
def get_devices(self, **requirements):
subsystem = requirements.get("subsystem", None)
if subsystem is not None:
del requirements["subsystem"]
devices = self._context.list_devices(subsystem = subsystem)
return [dev for dev in devices if self._device_fulfills_requirements(dev, requirements)]

View file

@ -0,0 +1,22 @@
import pyudev
class Inventory(object):
"""
Inventory object can handle information about available hardware devices. It also informs the plugins
about related hardware events.
"""
def __init__(self, udev_context=None):
if udev_context is not None:
self._udev_context = udev_context
else:
self._udev_context = pyudev.Context()
def get_devices(self, subsystem):
return self._udev_context.list_devices(subsystem=subsystem)
def subscribe(self, plugin, subsystem, callback):
pass
def unsubscribe(self, plugin, subsystem=None):
pass

View file

@ -12,6 +12,10 @@ class Repository(PluginLoader):
super(self.__class__, self).__init__()
self._monitors = set()
@property
def monitors(self):
return self._monitors
def _set_loader_parameters(self):
self._namespace = "tuned.monitors"
self._prefix = "monitor_"
@ -28,8 +32,3 @@ class Repository(PluginLoader):
assert isinstance(monitor, self._interface)
monitor.cleanup()
self._monitors.remove(monitor)
def update(self):
for monitor in self._monitors:
log.debug("updating %s" % monitor)
monitor.update()

View file

@ -1 +1,2 @@
from repository import *
import instance

View file

@ -1,4 +1,5 @@
import tuned.logs
import collections
log = tuned.logs.get()
@ -9,182 +10,47 @@ class Plugin(object):
Plugins change various system settings in order to get desired performance or power
saving. Plugins use Monitor objects to get information from the running system.
Methods requiring reimplementation:
- device_requirements(cls)
- update_tuning(self)
Intentionally a lot of logic is included in the plugin to increase plugin flexibility.
"""
_has_dynamic_options = False
def __init__(self, monitors_repository, storage_factory, hardware_inventory, device_matcher, instance_factory):
"""Plugin constructor."""
# class methods
@classmethod
def _get_default_options(cls):
return {}
@classmethod
def device_requirements(cls):
raise Exception("device_requirements not implemented")
return None
@classmethod
def is_supported(cls):
return True
# instance methods
def __init__(self, monitors_repository, storage_factory, devices=None, options=None):
"""
Plugin instance constructor. Plugins should not override this function in general,
_post_init method should be used instead.
"""
self._monitors_repository = monitors_repository
self._storage = storage_factory.create(self.__class__.__name__)
self._devices = devices
self._options = self._get_default_options()
if options is not None:
self._merge_options(options)
self._dynamic_tuning = True
self._monitors_repository = monitors_repository
self._hardware_inventory = hardware_inventory
self._device_matcher = device_matcher
self._instance_factory = instance_factory
self._instances = collections.OrderedDict()
self._init_commands()
self._post_init()
def _init_commands(self):
self._commands = {}
self._autoregister_commands()
for command_name, command in self._commands.iteritems():
if command.get("custom", False):
continue
if "get" not in command or "set" not in command:
raise TypeError("Plugin command '%s' is not defined correctly" % command_name)
def _post_init(self):
pass
@property
def dynamic_tuning(self):
return self._dynamic_tuning
@property
def devices(self):
return self._devices
def _autoregister_commands(self):
"""
Register all commands marked using @command_set and @command_get decorators.
"""
for member_name in self.__class__.__dict__:
if member_name.startswith("__"):
continue
member = getattr(self, member_name)
if not hasattr(member, "_command"):
continue
command_name = member._command["name"]
info = self._commands.get(command_name, {})
if "set" in member._command:
info["custom"] = None
info["set"] = member
info["per_device"] = member._command["per_device"]
elif "get" in member._command:
info["get"] = member
elif "custom" in member._command:
info["custom"] = member
info["per_device"] = member._command["per_device"]
self._commands[command_name] = info
def _storage_key(self, command_name, device=None):
if device is not None:
return "%s@%s" % (command_name, device)
else:
return command_name
def _execute_command(self, command_name, command):
if not self._options.has_key(command_name):
raise ValueError("Command is not supported.")
new_value = self._options[command_name]
if new_value is None:
return
if not command["per_device"]:
if command["custom"] is not None:
command["custom"](True, new_value)
else:
current_value = command["get"]()
storage_key = self._storage_key(command_name)
self._storage.set(command_name, current_value)
command["set"](new_value)
return
if self._devices is None:
raise TypeError("No devices were specified.")
for device in self._devices:
if command["custom"] is not None:
command["custom"](True, new_value, device)
else:
current_value = command["get"](device)
storage_key = self._storage_key(command_name, device)
self._storage.set(storage_key, current_value)
command["set"](new_value, device)
def _cleanup_command(self, command_name, command):
if not self._options.has_key(command_name):
raise ValueError("Command is not supported.")
if self._options[command_name] is None:
return
if not command["per_device"]:
if command["custom"] is not None:
command["custom"](False, None)
else:
storage_key = self._storage_key(command_name)
old_value = self._storage.get(storage_key)
if old_value is not None:
command["set"](old_value)
self._storage.unset(storage_key)
return
if self._devices is None:
raise TypeError("No devices were specified.")
for device in self._devices:
if command["custom"] is not None:
command["custom"](False, None, device)
else:
storage_key = self._storage_key(command_name, device)
old_value = self._storage.get(storage_key)
if old_value is not None:
command["set"](old_value, device)
self._storage.unset(storage_key)
def execute_commands(self):
for command_name, command in self._commands.iteritems():
self._execute_command(command_name, command)
def cleanup_commands(self):
for command_name, command in self._commands.iteritems():
self._cleanup_command(command_name, command)
self._init_devices()
def cleanup(self):
pass
self._destroy_all_instances()
def _merge_options(self, options):
@property
def name(self):
return self.__class__.__module__.split(".")[-1].lstrip("plugin_")
#
# Plugin configuration manipulation and helpers.
#
def _get_config_options(self):
"""Default configuration options for the plugin."""
return {}
def _get_effective_options(self, options):
"""Merge provided options with plugin default options."""
# TODO: _has_dynamic_options is a hack
effective = self._get_config_options().copy()
for key in options:
if key in self._options or self.__class__._has_dynamic_options:
self._options[key] = options[key]
if key in effective or self._has_dynamic_options:
effective[key] = options[key]
else:
log.warn("Unknown option '%s' for plugin '%s'." % (key, self.__class__.__name__))
def update_tuning(self):
raise NotImplementedError()
return effective
def _option_bool(self, value):
if type(value) is bool:
@ -199,3 +65,269 @@ class Plugin(object):
return false_value
else:
return None
#
# Interface for manipulation with instances of the plugin.
#
def create_instance(self, name, devices_expression, 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)
self._instances[name] = instance
self._instance_init(instance)
return instance
def destroy_instance(self, instance):
"""Destroy existing instance."""
if instance._plugin != self:
raise Exception("Plugin instance '%s' does not belong to this plugin '%s'." % (instance, self))
if instance.name not in self._instances:
raise Exception("Plugin instance '%s' was already destroyed." % instance)
instance = self._instances[instance.name]
self._destroy_instance(instance)
del self._instances[instance.name]
def _destroy_instance(self, instance):
log.debug("destroying instance %s (%s)" % (instance.name, self.name))
self.release_devices(instance)
self._instance_cleanup(instance)
def _destroy_all_instances(self):
for instance in self._instances.values():
self._destroy_instance(instance)
self._instances.clear()
def _instance_init(self, instance):
raise NotImplementedError()
def _instance_cleanup(self, instance):
raise NotImplementedError()
#
# Devices handling
#
def _init_devices(self):
self._devices = None
self._assigned_devices = set()
self._free_devices = set()
def assign_free_devices(self, instance):
# devices are not supported
if self._devices is None:
return
assert(isinstance(self._devices, set))
assert(isinstance(self._assigned_devices, set))
assert(isinstance(self._free_devices, set))
to_assign = set(self._device_matcher.match_list(instance.devices_expression, self._free_devices))
if len(to_assign) == 0:
log.warn("instance '%s': no matching devices available" % instance.name)
instance.active = False
return
log.info("instance '%s': assigning devices %s" % (instance.name, ", ".join(to_assign)))
instance.active = True
instance.devices.update(to_assign) # cannot use |=
self._assigned_devices |= to_assign
self._free_devices -= to_assign
def release_devices(self, instance):
# devices are not supported
if self._devices is None:
return
to_release = instance.devices & self._devices
instance.active = False
instance.devices.clear()
self._assigned_devices -= to_release
self._free_devices |= to_release
#
# Tuning activation and deactivation.
#
def instance_apply_tuning(self, instance):
"""
Apply static and dynamic tuning if the plugin instance is active.
"""
if not instance.active:
return
if instance.has_static_tuning:
self._instance_apply_static(instance)
if instance.has_dynamic_tuning:
self._instance_update_dynamic(instance)
def instance_update_tuning(self, instance):
"""
Apply dynamic tuning if the plugin instance is active.
"""
if not instance.active:
return
if instance.has_dynamic_tuning:
self._instance_update_dynamic(instance)
def instance_unapply_tuning(self, instance):
"""
Remove all tunings applied by the plugin instance.
"""
if instance.has_dynamic_tuning:
self._instance_unapply_dynamic(instance)
if instance.has_static_tuning:
self._instance_unapply_static(instance)
def _instance_apply_static(self, instance):
for command in self._commands.values():
self._execute_command(instance, command)
def _instance_unapply_static(self, instance):
for command in self._commands.values():
self._cleanup_command(instance, command)
def _instance_apply_dynamic(self, instance):
raise NotImplementedError()
def _instance_unapply_dynamic(self, instance):
raise NotImplementedError()
#
# Registration of commands for static plugins.
#
def _init_commands(self):
"""
Initialize commands.
"""
self._commands = {}
self._autoregister_commands()
self._check_commands()
def _autoregister_commands(self):
"""
Register all commands marked using @command_set, @command_get, and @command_custom decorators.
"""
for member_name in self.__class__.__dict__:
if member_name.startswith("__"):
continue
member = getattr(self, member_name)
if not hasattr(member, "_command"):
continue
command_name = member._command["name"]
info = self._commands.get(command_name, {"name": command_name})
if "set" in member._command:
info["custom"] = None
info["set"] = member
info["per_device"] = member._command["per_device"]
elif "get" in member._command:
info["get"] = member
elif "custom" in member._command:
info["custom"] = member
info["per_device"] = member._command["per_device"]
self._commands[command_name] = info
def _check_commands(self):
"""
Check if all commands are defined correctly.
"""
for command_name, command in self._commands.iteritems():
# do not check custom commands
if command.get("custom", False):
continue
# automatic commands should have 'get' and 'set' functions
if "get" not in command or "set" not in command:
raise TypeError("Plugin command '%s' is not defined correctly" % command_name)
#
# Operations with persistent storage for status data.
#
def _storage_key(self, instance_name, command_name, device_name=None):
if device_name is not None:
return "%s/%s/%s" % (command_name, instance_name, device_name)
else:
return "%s/%s" % (command_name, instance_name)
def _storage_set(self, instance, command, value, device_name=None):
key = self._storage_key(instance.name, command["name"], device_name)
self._storage.set(key, value)
def _storage_get(self, instance, command, device_name=None):
key = self._storage_key(instance.name, command["name"], device_name)
return self._storage.get(key)
def _storage_unset(self, instance, command, device_name=None):
key = self._storage_key(instance.name, command["name"], device_name)
return self._storage.unset(key)
#
# Command execution and cleanup.
#
def _execute_command(self, instance, command):
new_value = instance.options.get(command["name"], None)
if new_value is None:
return
if command["per_device"]:
for device in instance.devices:
self._execute_device_command(instance, command, device, new_value)
else:
self._execute_non_device_command(instance, command, new_value)
def _execute_device_command(self, instance, command, device, new_value):
if command["custom"] is not None:
command["custom"](True, new_value, device)
else:
current_value = command["get"](device)
self._storage_set(instance, command, current_value, device)
command["set"](new_value, device)
def _execute_non_device_command(self, instance, command, new_value):
if command["custom"] is not None:
command["custom"](True, new_value)
else:
current_value = command["get"]()
self._storage_set(instance, command_name, current_value)
command["set"](new_value)
def _cleanup_command(self, instance, command):
if instance.options.get(command["name"], None) is None:
return
if command["per_device"]:
for device in instance.devices:
self._cleanup_device_command(instance, command, device)
else:
self._cleanup_non_device_command(instance, command)
def _cleanup_device_command(self, instance, command, device):
if command["custom"] is not None:
command["custom"](False, None, device)
else:
old_value = self._storage_get(instance, command, device)
if old_value is not None:
command["set"](old_value, device)
self._storage_unset(instance, command, device)
def _cleanup_non_device_command(self, instance, command):
if command["custom"] is not None:
command["custom"](False, None)
else:
old_value = self._storage_get(instance, command)
if old_value is not None:
command["set"](old_value)
self._storage_unset(instance, command)

View file

@ -0,0 +1,2 @@
from instance import Instance
from factory import Factory

View file

@ -0,0 +1,6 @@
from instance import Instance
class Factory(object):
def create(self, *args, **kwargs):
instance = Instance(*args, **kwargs)
return instance

View file

@ -0,0 +1,64 @@
class Instance(object):
"""
"""
def __init__(self, plugin, name, devices_expression, options):
self._plugin = plugin
self._name = name
self._devices_expression = devices_expression
self._options = options
self._active = True
self._has_static_tuning = False
self._has_dynamic_tuning = False
self._devices = set()
# properties
@property
def name(self):
return self._name
@property
def active(self):
"""The instance performs some tuning (otherwise it is suspended)."""
return self._active
@active.setter
def active(self, value):
self._active = value
@property
def devices_expression(self):
return self._devices_expression
@property
def devices(self):
return self._devices
@property
def options(self):
return self._options
@property
def has_static_tuning(self):
return self._has_static_tuning
@property
def has_dynamic_tuning(self):
return self._has_dynamic_tuning
# methods
def apply_tuning(self):
self._plugin.instance_apply_tuning(self)
def update_tuning(self):
self._plugin.instance_update_tuning(self)
def unapply_tuning(self):
self._plugin.instance_unapply_tuning(self)
def destroy(self):
self.unapply_tuning()
self._plugin.destroy_instance(self)

View file

@ -3,34 +3,32 @@ from decorators import *
import tuned.logs
import tuned.utils.commands
import fnmatch
import os
import struct
log = tuned.logs.get()
# TODO: force_latency -> command
class CPULatencyPlugin(base.Plugin):
"""
Plugin for tuning CPU options. Powersaving, governor, required latency, etc.
"""
@classmethod
def device_requirements(self):
return {"subsystem": "cpu"}
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self._init_devices()
def _post_init(self):
self._latency = None
self._load_monitor = None
self._cpu_latency_fd = os.open("/dev/cpu_dma_latency", os.O_WRONLY)
def _init_devices(self):
self._devices = set()
# current list of devices
for device in self._hardware_inventory.get_devices("cpu"):
self._devices.add(device.sys_name)
if self._options["force_latency"] is None:
self._load_monitor = self._monitors_repository.create("load", None)
else:
self._dynamic_tuning = False
self._set_latency(self._options["force_latency"])
self._assigned_devices = set()
self._free_devices = self._devices.copy()
@classmethod
def _get_default_options(cls):
def _get_config_options(self):
return {
"load_threshold" : 0.2,
"latency_low" : 100,
@ -40,13 +38,44 @@ class CPULatencyPlugin(base.Plugin):
"multicore_powersave" : None,
}
def cleanup(self):
if self._load_monitor is not None:
self._monitors_repository.delete(self._load_monitor)
def _instance_init(self, instance):
instance._has_static_tuning = True
instance._has_dynamic_tuning = False
os.close(self._cpu_latency_fd)
# only the first instance of the plugin can control the latency
if self._instances.values()[0] == instance:
instance._controls_latency = True
self._cpu_latency_fd = os.open("/dev/cpu_dma_latency", os.O_WRONLY)
self._latency = None
def update_tuning(self):
if instance.options["force_latency"] is None:
instance._load_monitor = self._monitors_repository.create("load", None)
instance._dynamic_tuning = True
else:
instance._load_monitor = None
else:
instance._controls_latency = False
log.info("Latency settings from non-first CPU plugin instance '%s' will be ignored." % instance.name)
def _instance_cleanup(self, instance):
if instance._controls_latency:
os.close(self._cpu_latency_fd)
if instance._load_monitor is not None:
self._monitors_repository.delete(instance._load_monitor)
def _instance_apply_static(self, instance):
super(self.__class__, self)._instance_apply_static(instance)
if not instance._controls_latency:
return
force_latency_value = instance.options["force_latency"]
if force_latency_value is not None:
self._set_latency(force_latency_value)
def _instance_apply_dynamic(self, instance):
assert(instance._controls_latency)
load = self._load_monitor.get_load()["system"]
if load < self._options["load_threshold"]:
self._set_latency(self._options["latency_high"])
@ -56,7 +85,7 @@ class CPULatencyPlugin(base.Plugin):
def _set_latency(self, latency):
latency = int(latency)
if self._latency != latency:
log.info("new cpu latency %d" % latency)
log.info("setting new cpu latency %d" % latency)
latency_bin = struct.pack("i", latency)
os.write(self._cpu_latency_fd, latency_bin)
self._latency = latency
@ -64,13 +93,15 @@ class CPULatencyPlugin(base.Plugin):
@command_set("governor", per_device=True)
def _set_governor(self, governor, device):
log.info("setting governor '%s' on cpu '%s'" % (governor, device))
tuned.utils.commands.execute(["cpupower", "-c", str(device), "frequency-set", "-g", str(governor)])
cpu_id = device.lstrip("cpu")
tuned.utils.commands.execute(["cpupower", "-c", cpu_id, "frequency-set", "-g", str(governor)])
@command_get("governor")
def _get_governor(self, device):
governor = None
try:
lines = tuned.utils.commands.execute(["cpupower", "-c", str(device), "frequency-info", "-p"]).splitlines()
cpu_id = device.lstrip("cpu")
lines = tuned.utils.commands.execute(["cpupower", "-c", cpu_id, "frequency-info", "-p"]).splitlines()
for line in lines:
if line.startswith("analyzing"):
continue

View file

@ -8,53 +8,33 @@ __all__ = ["Repository"]
class Repository(PluginLoader):
def __init__(self, storage_factory, monitor_repository, hardware_enumerator):
def __init__(self, monitor_repository, storage_factory, hardware_inventory, device_matcher, plugin_instance_factory):
super(self.__class__, self).__init__()
self._plugins = set()
self._storage_factory = storage_factory
self._monitor_repository = monitor_repository
self._hardware_enumerator = hardware_enumerator
self._storage_factory = storage_factory
self._hardware_inventory = hardware_inventory
self._device_matcher = device_matcher
self._plugin_instance_factory = plugin_instance_factory
@property
def plugins(self):
return self._plugins
def _set_loader_parameters(self):
self._namespace = "tuned.plugins"
self._prefix = "plugin_"
self._interface = tuned.plugins.base.Plugin
def create(self, plugin_name, devices, options):
def create(self, plugin_name):
log.debug("creating plugin %s" % plugin_name)
plugin_cls = self.load_plugin(plugin_name)
plugin_instance = plugin_cls(self._monitor_repository, self._storage_factory, devices, options)
plugin_instance = plugin_cls(self._monitor_repository, self._storage_factory, self._hardware_inventory, self._device_matcher, self._plugin_instance_factory)
self._plugins.add(plugin_instance)
return plugin_instance
def tunable_devices(self, plugin_name):
plugin_cls = self.load_plugin(plugin_name)
device_requirements = plugin_cls.device_requirements()
devices = self._hardware_enumerator.get_devices(**device_requirements)
return map(lambda device: device.sys_name, devices)
def is_supported(self, plugin_name):
plugin_cls = self.load_plugin(plugin_name)
return plugin_cls.is_supported()
def do_static_tuning(self):
for plugin in self._plugins:
# TODO: plugin to str conversion, not ideal now
log.debug("running static tuning for plugin '%s'" % plugin)
plugin.cleanup_commands()
plugin.execute_commands()
def delete(self, plugin):
assert isinstance(plugin, self._interface)
log.debug("removing plugin %s" % plugin)
plugin.cleanup_commands()
plugin.cleanup()
self._plugins.remove(plugin)
def update(self):
for plugin in self._plugins:
if not plugin.dynamic_tuning:
continue
log.debug("updating %s" % plugin)
plugin.update_tuning()

View file

@ -1,4 +1 @@
from manager import *
from device_matcher import *
from factory import *
from unit import *

View file

@ -1,5 +0,0 @@
import tuned.units.unit
class Factory(object):
def create(self, name, type, plugin):
return tuned.units.unit.Unit(name, type, plugin)

View file

@ -1,7 +1,5 @@
import tuned.exceptions
import tuned.logs
import tuned.patterns
import tuned.units.unit
log = tuned.logs.get()
@ -9,91 +7,85 @@ __all__ = ["Manager"]
class Manager(object):
"""
Manager instantiates Unit objects, and keeps track of them.
Manager creates plugin instances and keeps a track of them.
"""
__slots__ = ["_units", "_plugins_repository", "_monitors_repository", "_unit_factory", "_device_matcher"]
def __init__(self, plugins_repository, monitors_repository, unit_factory, device_matcher):
def __init__(self, plugins_repository, monitors_repository):
super(self.__class__, self).__init__()
self._units = set()
self._monitors_repository = monitors_repository
self._plugins_repository = plugins_repository
self._unit_factory = unit_factory
self._device_matcher = device_matcher
self._monitors_repository = monitors_repository
self._instances = []
self._plugins = []
@property
def units(self):
return self._units
def plugins(self):
return self._plugins
@property
def plugins_repository(self):
return self._plugins_repository
def instances(self):
return self._instances
@property
def monitors_repository(self):
return self._monitors_repository
def create(self, instances_config):
def create(self, units):
# reverse order, newer units have priority to claim a device
for unit_name in reversed(units):
unit_info = units[unit_name]
if not unit_info.enabled:
log.debug("skipping disabled unit '%s'" % unit_info.name)
# group instances by plugin
instances_by_plugin = {}
for instance_name, instance_info in instances_config.items():
if not instance_info.enabled:
log.debug("skipping disabled instance '%s'" % instance_name)
continue
instances_by_plugin.setdefault(instance_info.type, [])
instances_by_plugin[instance_info.type].append(instance_info)
if not self._plugins_repository.is_supported(unit_info.type):
log.info("skipping unit '%s', plugin not supported on your system" % unit_info.type)
continue
# create all plugin instances at once
devices = self._possible_devices(unit_info)
if devices is None:
continue
log.info("creating unit '%s' (devices: %s)" % (unit_info.name, ", ".join(devices)))
for plugin_name, instances_info in instances_by_plugin.items():
try:
self._create_unit(unit_info, devices)
except tuned.exceptions.TunedException as e:
log.error("unable to create unit '%s' (trace follows)" % unit_info.name)
e.log()
except Exception as E:
log.error("unable to create unit '%s' (%s)" % (unit_info.name, str(e)))
plugin = self._plugins_repository.create(plugin_name)
self._plugins.append(plugin)
#except NotSupportedPlugin:
# log.info("skipping plugin '%s', not supported on your system" % plugin_name)
# continue
except Exception as e:
log.error("failed to initialize plugin %s (%s)" % (plugin_name, e))
continue
def _possible_devices(self, unit_info):
tunable_devices = self._plugins_repository.tunable_devices(unit_info.type)
if not tunable_devices:
log.info("skipping unit '%s', no devices available" % unit_info.name)
return None
# create instances, seize devices in reverse order
# FIXME: hmm, is this rational?
available_devices = [dev for dev in tunable_devices if dev not in self._seized_devices(unit_info.type)]
if not available_devices:
log.info("skipping unit '%s', all devices are already claimed by another unit" % unit_info.name)
return None
created_instances = []
for instance_info in instances_info:
log.debug("creating '%s' instance '%s'" % (instance_info.type, instance_info.name))
new_instance = plugin.create_instance(instance_info.name, instance_info.devices, instance_info.options)
created_instances.append(new_instance)
devices = self._device_matcher.match_list(unit_info.devices, available_devices)
if not devices:
log.info("skipping unit '%s', no matching devices available" % unit_info.name)
return None
for instance in reversed(created_instances):
log.debug("assigning devices to '%s'" % instance.name)
plugin.assign_free_devices(instance)
return devices
self._instances.extend(created_instances)
def _seized_devices(self, type):
devices = []
for unit in self._units:
if unit.type == type:
devices.extend(unit.devices)
return set(devices)
def destroy_all(self):
for plugin in self._plugins:
log.debug("cleaning plugin '%s'" % plugin.name)
plugin.cleanup()
def _create_unit(self, unit_info, devices):
plugin = self._plugins_repository.create(unit_info.type, devices, unit_info.options)
unit = self._unit_factory.create(unit_info.name, unit_info.type, plugin)
self._units.add(unit)
del self._plugins[:]
del self._instances[:]
def delete(self, unit):
self._plugins_repository.delete(unit.plugin)
self._units.delete(unit)
def update_monitors(self):
for monitor in self._monitors_repository.monitors:
log.debug("updating monitor %s" % monitor)
monitor.update()
def delete_all(self):
for unit in self._units:
self._plugins_repository.delete(unit.plugin)
self._units.clear()
def start_tuning(self):
for instance in self._instances:
instance.apply_tuning()
def update_tuning(self):
for instance in self._instances:
instance.update_tuning()
def stop_tuning(self):
for instance in self._instances:
instance.unapply_tuning()

View file

@ -1,33 +0,0 @@
import tuned.plugins
class Unit(object):
"""
Unit is the smallest tuning component. Units have to be instantiated using UnitManager.
One unit utilizes one plugin. The tuning can be limited to certain devices with specific
options. Multiple units can utilize one plugin, but they should not control the same
device.
"""
__slots__ = ["_name", "_type", "_plugin"]
def __init__(self, name, type, plugin):
self._name = name
self._type = type
self._plugin = plugin
@property
def name(self):
return self._name
@property
def type(self):
return self._type
@property
def devices(self):
return self._plugin.devices
@property
def plugin(self):
return self._plugin