1
0
Fork 0

command decorators: descriptive, auto register function, remove dark magic

This commit is contained in:
Jan Vcelak 2012-05-09 16:14:30 +02:00
parent cfed1abaa8
commit ec6fe3a918
3 changed files with 140 additions and 134 deletions

View file

@ -39,6 +39,12 @@ class Plugin(object):
if options is not None:
self._merge_options(options)
# TODO: cannot be injected now
self._storage = tuned.utils.storage.Storage.get_instance()
self._autoregister_commands()
assert self._commands_are_valid()
@property
def dynamic_tuning(self):
return self._options["dynamic_tuning"] in ["1", "true"]
@ -47,33 +53,99 @@ class Plugin(object):
def static_tuning(self):
return self._options["static_tuning"] in ["1", "true"]
def register_command(self, option, set_fnc, revert_fnc = None, is_per_dev = False):
self._commands[option] = (is_per_dev, set_fnc, revert_fnc)
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["set"] = member
info["per_device"] = member._command["per_device"]
elif "get" in member._command:
info["get"] = member
self._commands["command_name"] = info
def _commands_are_valid(self):
for command in commands:
if "get" not in command or "set" not in command:
return False
return True
# TODO: should be in storage class
def _storage_key(self, command_name, device):
if device is not None:
return "%s@%s" % [command_name, device]
else
return command_name
# TODO: should be in storage class
def _storage_get(self, command_name, device = None):
if not self._storage.data.has_key(self.__class__):
return None
key = self._storage_key(command_name, device)
return self._storage.data[self.__class__].get(key, None)
# TODO: should be in storage class
def _storage_set(self, value, command_name, device = None):
self._storage.data.setdefault(self.__class__, [])
key = self._storage_key(command_name, device)
self._storage.data[self.__class__][key] = value
# TODO: should be in storage class
def _storage_remove(self, command_name, device = None):
self._storage.data.setdefault(self.__class__, [])
key = self._storage_key(command_name, device)
del self._storage.data[self.__class__][key]
def execute_commands(self):
for option, (is_per_dev, set_fnc, revert_fnc) in self._commands.iteritems():
if not self._options.has_key(option):
for command_name, command in self._commands.iteritems():
if not self._options.has_key(command_name):
continue
if is_per_dev:
for dev in self._devices:
set_fnc(dev, self._options[option])
new_value = self._options[command_name]
# FIXME: should we revert old settings before applying the new one?
# can we call cleanup_commands() instead
# TODO: refactor
if command["per_device"]:
for device in self._devices:
current_value = command["get"](device)
self._storage_set(current_value, command_name, device)
command["set"](new_value, device)
else:
set_fnc(self._options[option])
current_value = command["get"]()
self._storage_set(current_valuie, command_name)
command["set"](new_value)
def cleanup_commands(self):
for option, (is_per_dev, set_fnc, revert_fnc) in self._commands.iteritems():
for command_name, command in self._commands.iteritems():
if not self._options.has_key(option):
continue
if revert_fnc:
set_fnc = revert_fnc
if is_per_dev:
for dev in self._devices:
set_fnc(dev, None)
# TODO: refactor
if command["per_device"]:
for device in self._devices:
old_value = self._storage_get(command_name, device)
if old_value is not None:
command["set"](old_value, device)
self._storage_remove(command_name, device)
else:
set_fnc(None)
old_value = self._storage_get(command_name)
if old_value is not None:
commad["set"](old_value)
self._storage_remove(command_name)
def cleanup(self):
pass

View file

@ -0,0 +1,52 @@
# Copyright (C) 2008-2012 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
__all__ = ["command", "command_get"]
# @command_set("scheduler", per_device=True)
# def set_scheduler(self, device, value):
# set_new_scheduler
#
# @command_get("scheduler")
# def get_scheduler(self, device):
# return current_scheduler
#
# @command_set("foo")
# def set_foo(self, value):
# set_new_foo
#
# @command_get("foo")
# def get_foo(self):
# return current_foo
#
def command_set(name, per_device=False):
def wrapper(method):
method._command = {
"set": True,
"name": name,
"per_device": per_device,
}
return method
return wrapper
def command_get(name):
def wrapper(method):
method._command = { "get": True }
return method
return wrapper

View file

@ -26,124 +26,6 @@ from functools import wraps
log = tuned.logs.get()
def command(plugin, key):
"""
This decorator makes adding new commands easier. The only thing you have to do is
to implement method which handles the value of particular option from config file
and returns previosly set value.
Here is example of method like that:
@command("disk", "elevator")
def _set_elevator(self, dev, value):
sys_file = os.path.join("/sys/block/", dev, "queue/scheduler")
old_value = tuned.utils.commands.read_file(sys_file)
tuned.utils.commands.write_to_file(sys_file, value)
return old_value
This decorator works then like this:
1. Tries to revert to previously stored value in Storage class
2. Tries to set the new value
3. Stores old value returned by the original method into Storage
"""
def my_decorator(target):
def wrapper(self, *args, **kwargs):
# Find out if the original method is def method(self, dev, value)
# or just def method(self, value) and set the variables.
dev = ""
value = None
if len(args) == 1:
value = args[0]
else:
dev = "_" + args[0]
value = args[1]
# Check if this plugin has key in Storage cache
storage = tuned.utils.storage.Storage.get_instance()
if not storage.data.has_key(plugin):
log.error("Storage file does not contain item with key %s" % (plugin))
return
# Revert to previous value if it exists
if storage.data[plugin].has_key(key + dev):
old_value = storage.data[plugin][key + dev]
# Plugin could call Plugin.register_command with revert_fnc
# set, so we should try to use specialized method for reverting.
# However, if there's no method like that, use the original method
# this decorator decorates.
revert_fnc = self._commands[key][2]
if not revert_fnc:
if len(dev) == 0:
target(self, old_value)
else:
target(self, dev[1:], old_value)
del storage.data[plugin][key + dev]
else:
if len(dev) == 0:
revert_fnc(old_value)
else:
revert_fnc(dev[1:], old_value)
# set it to new state and store old_value
if not value or len(value) == 0:
return False
old_value = target(self, *args, **kwargs)
if old_value and len(old_value) != 0:
storage.data[plugin][key + dev] = old_value
storage.save()
return True
# Fix the wrapper's call signature
return wraps(target)(wrapper)
return my_decorator
def command_revert(plugin, key):
"""
This decorator makes adding new commands easier. Use this decorator
for method which just reverts the particular setting to the previous
value.
Here is example of method like that:
@command_revert("disk", "elevator")
def _revert_elevator(self, dev, value):
sys_file = os.path.join("/sys/block/", dev, "queue/scheduler")
tuned.utils.commands.write_to_file(sys_file, value)
This decorator works then like this:
1. Tries to revert to previously stored value in Storage class if
it's set
"""
def my_decorator(target):
def wrapper(self, *args, **kwargs):
dev = ""
value = None
if len(args) == 1:
value = args[0]
else:
dev = "_" + args[0]
value = args[1]
# revert to previous state
storage = tuned.utils.storage.Storage.get_instance()
if not storage.data.has_key(plugin):
log.error("Storage file does not contain item with key %s" % (plugin))
return
if storage.data[plugin].has_key(key + dev):
old_value = storage.data[plugin][key + dev]
if len(dev) == 0:
target(self, old_value)
else:
target(self, dev[1:], old_value)
del storage.data[plugin][key + dev]
return True
# Fix the wrapper's call signature
return wraps(target)(wrapper)
return my_decorator
def write_to_file(f, data):
log.debug("Writing to file: %s < %s" % (f, data))
try: