From c24e2ea0caf30bae96096b3117482ef73de3fbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= Date: Tue, 5 Jan 2016 11:28:25 +0100 Subject: [PATCH] Added support for 'summary' and 'description' of profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- profiles/atomic-guest/tuned.conf | 1 + profiles/atomic-host/tuned.conf | 1 + profiles/balanced/tuned.conf | 3 + profiles/default/tuned.conf | 3 + profiles/desktop-powersave/tuned.conf | 1 + profiles/desktop/tuned.conf | 1 + profiles/enterprise-storage/tuned.conf | 1 + profiles/laptop-ac-powersave/tuned.conf | 1 + profiles/laptop-battery-powersave/tuned.conf | 1 + profiles/latency-performance/tuned.conf | 3 + profiles/network-latency/tuned.conf | 1 + profiles/network-throughput/tuned.conf | 1 + profiles/oracle/tuned.conf | 1 + profiles/powersave/tuned.conf | 3 + profiles/realtime-virtual-guest/tuned.conf | 1 + profiles/realtime-virtual-host/tuned.conf | 1 + profiles/realtime/tuned.conf | 1 + profiles/sap-hana-vmware/tuned.conf | 1 + profiles/sap-hana/tuned.conf | 1 + profiles/sap-netweaver/tuned.conf | 1 + profiles/server-powersave/tuned.conf | 3 + profiles/spindown-disk/tuned.conf | 3 + profiles/throughput-performance/tuned.conf | 3 + profiles/virtual-guest/tuned.conf | 1 + profiles/virtual-host/tuned.conf | 1 + tuned-adm.py | 4 ++ tuned/admin/admin.py | 58 +++++++++++++++--- tuned/admin/dbus_controller.py | 6 ++ tuned/consts.py | 4 ++ tuned/daemon/controller.py | 10 +++ tuned/profiles/locator.py | 64 +++++++++++++++++++- tuned/utils/commands.py | 4 ++ 32 files changed, 178 insertions(+), 11 deletions(-) diff --git a/profiles/atomic-guest/tuned.conf b/profiles/atomic-guest/tuned.conf index aff05f2..6691f1d 100644 --- a/profiles/atomic-guest/tuned.conf +++ b/profiles/atomic-guest/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize virtual guests based on the Atomic variant include=virtual-guest [selinux] diff --git a/profiles/atomic-host/tuned.conf b/profiles/atomic-host/tuned.conf index ad223bd..0010077 100644 --- a/profiles/atomic-host/tuned.conf +++ b/profiles/atomic-host/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize bare metal systems running the Atomic variant include=throughput-performance [selinux] diff --git a/profiles/balanced/tuned.conf b/profiles/balanced/tuned.conf index a53ef55..2d61775 100644 --- a/profiles/balanced/tuned.conf +++ b/profiles/balanced/tuned.conf @@ -2,6 +2,9 @@ # tuned configuration # +[main] +summary=General non-specialized tuned profile + [cpu] governor=conservative energy_perf_bias=normal diff --git a/profiles/default/tuned.conf b/profiles/default/tuned.conf index 3e6e3bb..ebdd412 100644 --- a/profiles/default/tuned.conf +++ b/profiles/default/tuned.conf @@ -2,6 +2,9 @@ # tuned configuration # +[main] +summary=Legacy default tuned profile + [cpu] [disk] diff --git a/profiles/desktop-powersave/tuned.conf b/profiles/desktop-powersave/tuned.conf index 7e80a4d..d0171d9 100644 --- a/profiles/desktop-powersave/tuned.conf +++ b/profiles/desktop-powersave/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optmize for the desktop use-case with power saving. include=server-powersave [video] diff --git a/profiles/desktop/tuned.conf b/profiles/desktop/tuned.conf index 95de98b..4b47ce6 100644 --- a/profiles/desktop/tuned.conf +++ b/profiles/desktop/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optmize for the desktop use-case include=balanced [sysctl] diff --git a/profiles/enterprise-storage/tuned.conf b/profiles/enterprise-storage/tuned.conf index 0863334..9f0d143 100644 --- a/profiles/enterprise-storage/tuned.conf +++ b/profiles/enterprise-storage/tuned.conf @@ -3,4 +3,5 @@ # [main] +summary=Legacy profile for RHEL6. For RHEL7, please use throughput-performance profile. include=throughput-performance diff --git a/profiles/laptop-ac-powersave/tuned.conf b/profiles/laptop-ac-powersave/tuned.conf index 691047c..8088760 100644 --- a/profiles/laptop-ac-powersave/tuned.conf +++ b/profiles/laptop-ac-powersave/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for laptop with power savings include=desktop-powersave [script] diff --git a/profiles/laptop-battery-powersave/tuned.conf b/profiles/laptop-battery-powersave/tuned.conf index 0fd2542..a43f089 100644 --- a/profiles/laptop-battery-powersave/tuned.conf +++ b/profiles/laptop-battery-powersave/tuned.conf @@ -3,4 +3,5 @@ # [main] +summary=Optimize laptop profile with more aggressive power saving include=powersave diff --git a/profiles/latency-performance/tuned.conf b/profiles/latency-performance/tuned.conf index 04abfbe..054c554 100644 --- a/profiles/latency-performance/tuned.conf +++ b/profiles/latency-performance/tuned.conf @@ -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 diff --git a/profiles/network-latency/tuned.conf b/profiles/network-latency/tuned.conf index f4ec2de..a2ca91a 100644 --- a/profiles/network-latency/tuned.conf +++ b/profiles/network-latency/tuned.conf @@ -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] diff --git a/profiles/network-throughput/tuned.conf b/profiles/network-throughput/tuned.conf index fbc2b72..e2703ee 100644 --- a/profiles/network-throughput/tuned.conf +++ b/profiles/network-throughput/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for streaming network throughput. Generally only necessary on older CPUs or 40G+ networks. include=throughput-performance [sysctl] diff --git a/profiles/oracle/tuned.conf b/profiles/oracle/tuned.conf index e047fa1..0e4cdfe 100644 --- a/profiles/oracle/tuned.conf +++ b/profiles/oracle/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for Oracle RDBMS include=throughput-performance [sysctl] diff --git a/profiles/powersave/tuned.conf b/profiles/powersave/tuned.conf index 589f194..d74200b 100644 --- a/profiles/powersave/tuned.conf +++ b/profiles/powersave/tuned.conf @@ -2,6 +2,9 @@ # tuned configuration # +[main] +summary=Optimize for low power consumption + [cpu] governor=ondemand energy_perf_bias=powersave diff --git a/profiles/realtime-virtual-guest/tuned.conf b/profiles/realtime-virtual-guest/tuned.conf index eaaf4ab..4ccfc6f 100644 --- a/profiles/realtime-virtual-guest/tuned.conf +++ b/profiles/realtime-virtual-guest/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for realtime workloads running within a KVM guest include=realtime [variables] diff --git a/profiles/realtime-virtual-host/tuned.conf b/profiles/realtime-virtual-host/tuned.conf index 51c182e..0106590 100644 --- a/profiles/realtime-virtual-host/tuned.conf +++ b/profiles/realtime-virtual-host/tuned.conf @@ -20,6 +20,7 @@ [main] +summary=Optimize for KVM guests running realtime workloads include=realtime [variables] diff --git a/profiles/realtime/tuned.conf b/profiles/realtime/tuned.conf index 4273ad2..ed50213 100644 --- a/profiles/realtime/tuned.conf +++ b/profiles/realtime/tuned.conf @@ -4,6 +4,7 @@ # https://docs.redhat.com [main] +summary=Optimize for realtime workloads include = network-latency [variables] diff --git a/profiles/sap-hana-vmware/tuned.conf b/profiles/sap-hana-vmware/tuned.conf index 71fbccf..1ec933b 100644 --- a/profiles/sap-hana-vmware/tuned.conf +++ b/profiles/sap-hana-vmware/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for SAP running inside a VMware guest include=throughput-performance [cpu] diff --git a/profiles/sap-hana/tuned.conf b/profiles/sap-hana/tuned.conf index 403a6a4..9b94c54 100644 --- a/profiles/sap-hana/tuned.conf +++ b/profiles/sap-hana/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for SAP include=throughput-performance [cpu] diff --git a/profiles/sap-netweaver/tuned.conf b/profiles/sap-netweaver/tuned.conf index 95326f3..6190051 100644 --- a/profiles/sap-netweaver/tuned.conf +++ b/profiles/sap-netweaver/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for SAP NetWeaver include=throughput-performance [sysctl] diff --git a/profiles/server-powersave/tuned.conf b/profiles/server-powersave/tuned.conf index 47b60a9..d67774d 100644 --- a/profiles/server-powersave/tuned.conf +++ b/profiles/server-powersave/tuned.conf @@ -2,6 +2,9 @@ # tuned configuration # +[main] +summary=Optimize for server power savings + [cpu] [disk] diff --git a/profiles/spindown-disk/tuned.conf b/profiles/spindown-disk/tuned.conf index ec9f2dd..6b54e53 100644 --- a/profiles/spindown-disk/tuned.conf +++ b/profiles/spindown-disk/tuned.conf @@ -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 diff --git a/profiles/throughput-performance/tuned.conf b/profiles/throughput-performance/tuned.conf index 4d6c607..62776c1 100644 --- a/profiles/throughput-performance/tuned.conf +++ b/profiles/throughput-performance/tuned.conf @@ -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 diff --git a/profiles/virtual-guest/tuned.conf b/profiles/virtual-guest/tuned.conf index 0d802ef..89fe021 100644 --- a/profiles/virtual-guest/tuned.conf +++ b/profiles/virtual-guest/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for running inside a virtual guest. include=throughput-performance [sysctl] diff --git a/profiles/virtual-host/tuned.conf b/profiles/virtual-host/tuned.conf index c508cdd..2ef5031 100644 --- a/profiles/virtual-host/tuned.conf +++ b/profiles/virtual-host/tuned.conf @@ -3,6 +3,7 @@ # [main] +summary=Optimize for running KVM guests include=throughput-performance [sysctl] diff --git a/tuned-adm.py b/tuned-adm.py index 74042db..029a076 100755 --- a/tuned-adm.py +++ b/tuned-adm.py @@ -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") diff --git a/tuned/admin/admin.py b/tuned/admin/admin.py index 1180785..c8af967 100644 --- a/tuned/admin/admin.py +++ b/tuned/admin/admin.py @@ -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"]) diff --git a/tuned/admin/dbus_controller.py b/tuned/admin/dbus_controller.py index 7592fc2..8385b58 100644 --- a/tuned/admin/dbus_controller.py +++ b/tuned/admin/dbus_controller.py @@ -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") diff --git a/tuned/consts.py b/tuned/consts.py index fe3b588..aaf3d32 100644 --- a/tuned/consts.py +++ b/tuned/consts.py @@ -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" diff --git a/tuned/daemon/controller.py b/tuned/daemon/controller.py index a7b657a..7508a53 100644 --- a/tuned/daemon/controller.py +++ b/tuned/daemon/controller.py @@ -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)) diff --git a/tuned/profiles/locator.py b/tuned/profiles/locator.py index 1bfa99d..c04153d 100644 --- a/tuned/profiles/locator.py +++ b/tuned/profiles/locator.py @@ -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())) diff --git a/tuned/utils/commands.py b/tuned/utils/commands.py index 2392a89..17375cf 100644 --- a/tuned/utils/commands.py +++ b/tuned/utils/commands.py @@ -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