diff --git a/com.redhat.tuned.policy b/com.redhat.tuned.policy index 8bee088b8..cc41fd3ed 100644 --- a/com.redhat.tuned.policy +++ b/com.redhat.tuned.policy @@ -177,6 +177,16 @@ + + Dump TuneD profile + Authentication is required to dump TuneD profile + + yes + yes + yes + + + Verify TuneD profile, ignore missing values Authentication is required to verify TuneD profile diff --git a/tuned-adm.py b/tuned-adm.py index 83ed56984..835640969 100755 --- a/tuned-adm.py +++ b/tuned-adm.py @@ -91,6 +91,9 @@ def check_log_level(value): parser_verify.set_defaults(action="verify_profile") parser_verify.add_argument("--ignore-missing", "-i", action="store_true", help="do not treat missing/non-supported tunings as errors") + parser_dump = subparsers.add_parser("dump", help="dump current profile") + parser_dump.set_defaults(action="dump") + parser_auto_profile = subparsers.add_parser("auto_profile", help="enable automatic profile selection mode, switch to the recommended profile") parser_auto_profile.set_defaults(action="auto_profile") diff --git a/tuned/admin/admin.py b/tuned/admin/admin.py index 8f8682f39..932ed1dd0 100644 --- a/tuned/admin/admin.py +++ b/tuned/admin/admin.py @@ -375,6 +375,18 @@ def _action_verify_profile(self, ignore_missing): print("Not supported in no_daemon mode.") return False + def _action_dbus_dump(self): + (ret, msg) = self._controller.dump() + if ret: + print(msg) + else: + self._error("Unable to dump profile: '%s'" % msg) + return self._controller.exit(ret) + + def _action_dump(self): + print("Not supported in no_daemon mode.") + return False + def _action_dbus_off(self): # 25 seconds default DBus timeout + 5 secs safety margin timeout = 25 + 5 diff --git a/tuned/admin/dbus_controller.py b/tuned/admin/dbus_controller.py index 22c8b8d3b..273c66f2e 100644 --- a/tuned/admin/dbus_controller.py +++ b/tuned/admin/dbus_controller.py @@ -134,6 +134,9 @@ def recommend_profile(self): def verify_profile(self): return self._call("verify_profile") + def dump(self): + return self._call("dump_profile") + def verify_profile_ignore_missing(self): return self._call("verify_profile_ignore_missing") diff --git a/tuned/daemon/controller.py b/tuned/daemon/controller.py index 6a59a1d86..b43810989 100644 --- a/tuned/daemon/controller.py +++ b/tuned/daemon/controller.py @@ -279,6 +279,12 @@ def verify_profile_ignore_missing(self, caller = None): return False return self._daemon.verify_profile(ignore_missing = True) + @exports.export("", "(bs)") + def dump_profile(self, caller = None): + if caller == "": + return False + return self._daemon.dump_profile() + @exports.export("", "a{sa{ss}}") def get_all_plugins(self, caller = None): """Return dictionary with accesible plugins diff --git a/tuned/daemon/daemon.py b/tuned/daemon/daemon.py index 81582d05d..cc3e2a23b 100644 --- a/tuned/daemon/daemon.py +++ b/tuned/daemon/daemon.py @@ -360,6 +360,30 @@ def verify_profile(self, ignore_missing): self._not_used.set() return ret + def dump_profile(self): + if not self.is_running(): + s = "TuneD is not running" + log.error(s) + return (False, s) + + if self._profile is None: + s = "no profile is set" + log.error(s) + return (False, s) + + if not self._profile_applied.is_set(): + s = "profile is not applied" + log.error(s) + return (False, s) + + # using daemon, the main loop mustn't exit before our completion + self._not_used.clear() + log.info("dumping profile(s): %s" % self._profile.name) + s = self._unit_manager.dump_tuning() + # main loop is allowed to exit + self._not_used.set() + return (True, s) + # profile_switch is helper telling plugins whether the stop is due to profile switch def stop(self, profile_switch = False): if not self.is_running(): diff --git a/tuned/plugins/base.py b/tuned/plugins/base.py index 8eaf48ada..539bc2d8d 100644 --- a/tuned/plugins/base.py +++ b/tuned/plugins/base.py @@ -290,6 +290,27 @@ def instance_verify_tuning(self, instance, ignore_missing): else: return None + def instance_dump_tuning(self, instance): + """ + Dump tuning if the plugin instance is active. + """ + if not instance.active: + return "" + + s = "[%s]\n" % instance.name + s += "type = %s\n" % self.name + if instance._devices_expression: + s += "devices = %s\n" % instance._devices_expression + if instance._devices_udev_regex: + s += "devices_udev_regex = %s\n" % instance._devices_udev_regex + if instance._script_pre: + s += "script_pre = %s\n" % instance._script_pre + if instance._script_post: + s += "script_post = %s\n" % instance._script_post + for option, val in instance._options.items(): + s += "%s = %s\n" % (option, val) + return s + def instance_update_tuning(self, instance): """ Apply dynamic tuning if the plugin instance is active. diff --git a/tuned/plugins/instance/instance.py b/tuned/plugins/instance/instance.py index 1e286729d..65f1722f8 100644 --- a/tuned/plugins/instance/instance.py +++ b/tuned/plugins/instance/instance.py @@ -82,6 +82,9 @@ def apply_tuning(self): def verify_tuning(self, ignore_missing): return self._plugin.instance_verify_tuning(self, ignore_missing) + def dump_tuning(self): + return self._plugin.instance_dump_tuning(self) + def update_tuning(self): self._plugin.instance_update_tuning(self) diff --git a/tuned/units/manager.py b/tuned/units/manager.py index 2612c1c4e..6225f509c 100644 --- a/tuned/units/manager.py +++ b/tuned/units/manager.py @@ -156,6 +156,13 @@ def verify_tuning(self, ignore_missing): ret = False return ret + def dump_tuning(self): + s = "" + for instance in self._instances: + s += self._try_call("dump_tuning", None, + instance.dump_tuning) + "\n" + return s[:-1] + def update_tuning(self): for instance in self._instances: self._try_call("update_tuning", None,