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:
parent
d49469f711
commit
c24e2ea0ca
32 changed files with 178 additions and 11 deletions
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize virtual guests based on the Atomic variant
|
||||
include=virtual-guest
|
||||
|
||||
[selinux]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize bare metal systems running the Atomic variant
|
||||
include=throughput-performance
|
||||
|
||||
[selinux]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
# tuned configuration
|
||||
#
|
||||
|
||||
[main]
|
||||
summary=General non-specialized tuned profile
|
||||
|
||||
[cpu]
|
||||
governor=conservative
|
||||
energy_perf_bias=normal
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
# tuned configuration
|
||||
#
|
||||
|
||||
[main]
|
||||
summary=Legacy default tuned profile
|
||||
|
||||
[cpu]
|
||||
|
||||
[disk]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optmize for the desktop use-case with power saving.
|
||||
include=server-powersave
|
||||
|
||||
[video]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optmize for the desktop use-case
|
||||
include=balanced
|
||||
|
||||
[sysctl]
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Legacy profile for RHEL6. For RHEL7, please use throughput-performance profile.
|
||||
include=throughput-performance
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for laptop with power savings
|
||||
include=desktop-powersave
|
||||
|
||||
[script]
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize laptop profile with more aggressive power saving
|
||||
include=powersave
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for streaming network throughput. Generally only necessary on older CPUs or 40G+ networks.
|
||||
include=throughput-performance
|
||||
|
||||
[sysctl]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for Oracle RDBMS
|
||||
include=throughput-performance
|
||||
|
||||
[sysctl]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
# tuned configuration
|
||||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for low power consumption
|
||||
|
||||
[cpu]
|
||||
governor=ondemand
|
||||
energy_perf_bias=powersave
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for realtime workloads running within a KVM guest
|
||||
include=realtime
|
||||
|
||||
[variables]
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
|
||||
[main]
|
||||
summary=Optimize for KVM guests running realtime workloads
|
||||
include=realtime
|
||||
|
||||
[variables]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
# https://docs.redhat.com
|
||||
|
||||
[main]
|
||||
summary=Optimize for realtime workloads
|
||||
include = network-latency
|
||||
|
||||
[variables]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for SAP running inside a VMware guest
|
||||
include=throughput-performance
|
||||
|
||||
[cpu]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for SAP
|
||||
include=throughput-performance
|
||||
|
||||
[cpu]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for SAP NetWeaver
|
||||
include=throughput-performance
|
||||
|
||||
[sysctl]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
# tuned configuration
|
||||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for server power savings
|
||||
|
||||
[cpu]
|
||||
|
||||
[disk]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for running inside a virtual guest.
|
||||
include=throughput-performance
|
||||
|
||||
[sysctl]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
[main]
|
||||
summary=Optimize for running KVM guests
|
||||
include=throughput-performance
|
||||
|
||||
[sysctl]
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue