1
0
Fork 0

Added support for 'summary' and 'description' of profiles

Added new tuned-adm functionality 'profile_info'. It has optional parameter
'profile_name'. If called without parameter, active profile is used.
It outputs name of the profile it is querying, profile summary, and profile
description. If there is no profile summary or description available it
outputs empty string(s).

Extended tuned-adm 'list' functionality. It now displays profile summary
in the list (if available).

Added summary to profiles.

Extended D-Bus API to support the new feature and for better Cockpit support:
 - Added 'profile_info' method. It gets profile_name argument and it queries
   the requested profile for information. If profile_name is empty string it
   queries the active/selected profile. It returns tuple of the following
   format (status, profile_name, summary, description), where status is
   boolean. If it is False, the query mechanism failed and the other fields
   don't contain valid data
 - Added 'profiles2' method. It's extension of 'profiles' method. It returns
   list of tuples. Tuples have following format:
   (profile_name, profile_summary)

Related: rhbz#1228356
Fixes: #46

Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
This commit is contained in:
Jaroslav Škarvada 2016-01-05 11:28:25 +01:00
parent d49469f711
commit c24e2ea0ca
32 changed files with 178 additions and 11 deletions

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize virtual guests based on the Atomic variant
include=virtual-guest
[selinux]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize bare metal systems running the Atomic variant
include=throughput-performance
[selinux]

View file

@ -2,6 +2,9 @@
# tuned configuration
#
[main]
summary=General non-specialized tuned profile
[cpu]
governor=conservative
energy_perf_bias=normal

View file

@ -2,6 +2,9 @@
# tuned configuration
#
[main]
summary=Legacy default tuned profile
[cpu]
[disk]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optmize for the desktop use-case with power saving.
include=server-powersave
[video]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optmize for the desktop use-case
include=balanced
[sysctl]

View file

@ -3,4 +3,5 @@
#
[main]
summary=Legacy profile for RHEL6. For RHEL7, please use throughput-performance profile.
include=throughput-performance

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for laptop with power savings
include=desktop-powersave
[script]

View file

@ -3,4 +3,5 @@
#
[main]
summary=Optimize laptop profile with more aggressive power saving
include=powersave

View file

@ -2,6 +2,9 @@
# tuned configuration
#
[main]
summary=Optimize for deterministic performance at the cost of increased power consumption
[cpu]
force_latency=1
governor=performance

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for deterministic performance at the cost of increased power consumption, focused on low latency network performance
include=latency-performance
[vm]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for streaming network throughput. Generally only necessary on older CPUs or 40G+ networks.
include=throughput-performance
[sysctl]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for Oracle RDBMS
include=throughput-performance
[sysctl]

View file

@ -2,6 +2,9 @@
# tuned configuration
#
[main]
summary=Optimize for low power consumption
[cpu]
governor=ondemand
energy_perf_bias=powersave

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for realtime workloads running within a KVM guest
include=realtime
[variables]

View file

@ -20,6 +20,7 @@
[main]
summary=Optimize for KVM guests running realtime workloads
include=realtime
[variables]

View file

@ -4,6 +4,7 @@
# https://docs.redhat.com
[main]
summary=Optimize for realtime workloads
include = network-latency
[variables]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for SAP running inside a VMware guest
include=throughput-performance
[cpu]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for SAP
include=throughput-performance
[cpu]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for SAP NetWeaver
include=throughput-performance
[sysctl]

View file

@ -2,6 +2,9 @@
# tuned configuration
#
[main]
summary=Optimize for server power savings
[cpu]
[disk]

View file

@ -16,6 +16,9 @@
# Bluetooth will be switch off.
# Wifi will be switch into power safe mode.
[main]
summary=Optimize for power saving by spinning-down rotational disks
[disk]
apm=128
spindown=6

View file

@ -2,6 +2,9 @@
# tuned configuration
#
[main]
summary=Broadly applicable tuning that provides excellent performance across a variety of common server workloads. This is the default profile for RHEL7.
[cpu]
governor=performance
energy_perf_bias=performance

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for running inside a virtual guest.
include=throughput-performance
[sysctl]

View file

@ -3,6 +3,7 @@
#
[main]
summary=Optimize for running KVM guests
include=throughput-performance
[sysctl]

View file

@ -47,6 +47,10 @@ if __name__ == "__main__":
parser_profile.set_defaults(action="profile")
parser_profile.add_argument("profiles", metavar="profile", type=str, nargs="+", help="profile name")
parser_profile_info = subparsers.add_parser("profile_info", help="show information/description of given profile or current profile if no profile is specified")
parser_profile_info.set_defaults(action="profile_info")
parser_profile_info.add_argument("profile", metavar="profile", type=str, nargs="?", default="", help="profile name, current profile if not specified")
if config.get(consts.CFG_RECOMMEND_COMMAND, consts.CFG_DEF_RECOMMEND_COMMAND):
parser_off = subparsers.add_parser("recommend", help="recommend profile")
parser_off.set_defaults(action="recommend_profile")

View file

@ -11,6 +11,7 @@ class Admin(object):
self._controller = controller
self._debug = debug
self._cmd = commands(debug)
self._profiles_locator = profiles_locator(consts.LOAD_DIRECTORIES)
def _error(self, message):
print >>sys.stderr, message
@ -28,18 +29,27 @@ class Admin(object):
no_dbus = self._controller is None
if not no_dbus:
try:
profile_names = self._controller.profiles()
profile_names = self._controller.profiles2()
except TunedAdminDBusException as e:
self._error(e)
no_dbus = True
# fallback to older API
try:
profile_names = self._controller.profiles()
except TunedAdminDBusException as e:
self._error(e)
no_dbus = True
profile_names = map(lambda profile:(profile, ""), profile_names)
if no_dbus:
profile_names = profiles_locator(consts.LOAD_DIRECTORIES).get_known_names()
profile_names = self._profiles_locator.get_known_names_summary()
print "Available profiles:"
for profile in profile_names:
print "- %s" % profile
if profile[1] is not None and profile[1] != "":
print self._cmd.align_str("- %s" % profile[0], 30, "- %s" % profile[1])
else:
print "- %s" % profile[0]
self.active()
def active(self):
def _get_active_profile(self):
profile_name = None
no_dbus = self._controller is None
if not no_dbus:
try:
@ -49,7 +59,39 @@ class Admin(object):
no_dbus = True
if no_dbus:
profile_name = str.strip(self._cmd.read_file(consts.ACTIVE_PROFILE_FILE, None))
if profile_name is not None and profile_name != "":
if profile_name == "":
profile_name = None
return profile_name
def profile_info(self, profile = ""):
no_dbus = self._controller is None
if profile == "":
profile = self._get_active_profile()
if not no_dbus:
try:
ret = self._controller.profile_info(profile)
except TunedAdminDBusException as e:
self._error(e)
no_dbus = True
if no_dbus:
ret = self._profiles_locator.get_profile_attrs(profile, [consts.PROFILE_ATTR_SUMMARY, consts.PROFILE_ATTR_DESCRIPTION], ["", ""])
if ret[0] == True:
print "Profile name:"
print ret[1]
print
print "Profile summary:"
print ret[2]
print
print "Profile description:"
print ret[3]
return True
else:
print "Unable to get information about profile '%s'" % profile
return False
def active(self):
profile_name = self._get_active_profile()
if profile_name is not None:
if self._controller is not None and self._tuned_is_running():
print "Current active profile: %s" % profile_name
else:
@ -73,7 +115,7 @@ class Admin(object):
self._error(e)
no_dbus = True
if no_dbus:
if profile_name in profiles_locator(consts.LOAD_DIRECTORIES).get_known_names():
if profile_name in self._profiles_locator.get_known_names():
if self._cmd.write_to_file(consts.ACTIVE_PROFILE_FILE, profile_name):
print "Trying to (re)start tuned..."
(ret, out) = self._cmd.execute(["service", "tuned", "restart"])

View file

@ -44,6 +44,12 @@ class DBusController(object):
def profiles(self):
return self._call("profiles")
def profiles2(self):
return self._call("profiles2")
def profile_info(self, profile_name):
return self._call("profile_info", profile_name)
def active_profile(self):
return self._call("active_profile")

View file

@ -57,6 +57,10 @@ CFG_DEF_UPDATE_INTERVAL = 10
# recommend command availability
CFG_DEF_RECOMMEND_COMMAND = True
# profile attributes which can be specified in the main section
PROFILE_ATTR_SUMMARY = "summary"
PROFILE_ATTR_DESCRIPTION = "description"
STR_VERIFY_PROFILE_DEVICE_VALUE_OK = "verify: passed: device %s: %s = %s"
STR_VERIFY_PROFILE_VALUE_OK = "verify: passed: %s = %s"
STR_VERIFY_PROFILE_OK = "verify: passed: %s"

View file

@ -106,6 +106,16 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
def profiles(self):
return self._daemon.profile_loader.profile_locator.get_known_names()
@exports.export("", "a(ss)")
def profiles2(self):
return self._daemon.profile_loader.profile_locator.get_known_names_summary()
@exports.export("s", "(bsss)")
def profile_info(self, profile_name):
if profile_name is None or profile_name == "":
profile_name = self.active_profile()
return tuple(self._daemon.profile_loader.profile_locator.get_profile_attrs(profile_name, [consts.PROFILE_ATTR_SUMMARY, consts.PROFILE_ATTR_DESCRIPTION], [""]))
@exports.export("", "s")
def recommend_profile(self):
return self._cmd.recommend_profile(hardcoded = not self._global_config.get_bool(consts.CFG_RECOMMEND_COMMAND, consts.CFG_DEF_RECOMMEND_COMMAND))

View file

@ -1,4 +1,6 @@
import os
import tuned.consts as consts
from configobj import ConfigObj, ConfigObjError
class Locator(object):
"""
@ -23,7 +25,8 @@ class Locator(object):
def get_config(self, profile_name, skip_files=None):
for dir_name in reversed(self._load_directories):
config_file = self._get_config_filename(dir_name, profile_name)
# basename is protection not to get out of the path
config_file = self._get_config_filename(dir_name, os.path.basename(profile_name))
if skip_files is not None and config_file in skip_files:
continue
@ -33,7 +36,57 @@ class Locator(object):
return None
def get_known_names(self):
def check_profile_name_format(self, profile_name):
return profile_name is not None and profile_name != "" and "/" not in profile_name
def parse_config(self, profile_name):
if not self.check_profile_name_format(profile_name):
return None
config_file = self.get_config(profile_name)
if config_file is None:
return None
try:
return ConfigObj(config_file, list_values = False, interpolation = False)
except (IOError, OSError, ConfigObjError) as e:
return None
# Get profile attributes (e.g. summary, description), attrs is list of requested attributes,
# if it is not list it is converted to list, defvals is list of default values to return if
# attribute is not found, it is also converted to list if it is not list.
# Returns list of the following format [status, profile_name, attr1_val, attr2_val, ...],
# status is boolean.
def get_profile_attrs(self, profile_name, attrs, defvals = None):
# check types
try:
attrs_len = len(attrs)
except TypeError:
attrs = [attrs]
attrs_len = 1
try:
defvals_len = len(defvals)
except TypeError:
defvals = [defvals]
defvals_len = 1
# Extend defvals if needed, last value is used for extension
if defvals_len < attrs_len:
defvals = defvals + ([defvals[-1]] * (attrs_len - defvals_len))
config = self.parse_config(profile_name)
if config is None:
return [False, "", "", ""]
if config.has_key("main"):
d = config["main"]
else:
d = dict()
vals = [True, profile_name]
for (attr, defval) in zip(attrs, defvals):
if attr == "" or attr is None:
vals[0] = False
vals = vals + [""]
else:
vals = vals + [d.get(attr, defval)]
return vals
def list_profiles(self):
profiles = set()
for dir_name in self._load_directories:
try:
@ -43,5 +96,10 @@ class Locator(object):
profiles.add(profile_name)
except OSError:
pass
return profiles
return sorted(list(profiles))
def get_known_names(self):
return sorted(self.list_profiles())
def get_known_names_summary(self):
return map(lambda profile: (profile, self.get_profile_attrs(profile, [consts.PROFILE_ATTR_SUMMARY], [""])[2]), sorted(self.list_profiles()))

View file

@ -33,6 +33,10 @@ class commands:
def unquote(self, v):
return re.sub("^\"(.*)\"$", r"\1", v)
# add spaces to align s2 to pos, returns resulting string: s1 + spaces + s2
def align_str(self, s1, pos, s2):
return s1 + " " * (pos - len(s1)) + s2
# convert dictionary 'd' to flat list and return it
# it uses sort on the dictionary items to return consistent results
# for directories with different inserte/delete history