diff --git a/tuned/admin/admin.py b/tuned/admin/admin.py index 8d1b107..faf215f 100644 --- a/tuned/admin/admin.py +++ b/tuned/admin/admin.py @@ -222,7 +222,7 @@ class Admin(object): return self._action_dbus_list() profile_name = " ".join(profiles) if profile_name == "": - return False + return self._controller.exit(False) self._daemon_action_finished.clear() (ret, msg) = self._controller.switch_profile(profile_name) if self._async or not ret: diff --git a/tuned/daemon/application.py b/tuned/daemon/application.py index db2ba34..618d1ce 100644 --- a/tuned/daemon/application.py +++ b/tuned/daemon/application.py @@ -186,9 +186,7 @@ class Application(object): # override global config if ran from command line with daemon option (-d) if daemon: self.config.set(consts.CFG_DAEMON, True) - if self.config.get_bool(consts.CFG_DAEMON, consts.CFG_DEF_DAEMON): - exports.start() - else: + if not self.config.get_bool(consts.CFG_DAEMON, consts.CFG_DEF_DAEMON): log.warn("Using one shot no deamon mode, most of the functionality will be not available, it can be changed in global config") result = self._controller.run() if self.config.get_bool(consts.CFG_DAEMON, consts.CFG_DEF_DAEMON): diff --git a/tuned/daemon/controller.py b/tuned/daemon/controller.py index 59a55b1..c0adb75 100644 --- a/tuned/daemon/controller.py +++ b/tuned/daemon/controller.py @@ -28,9 +28,12 @@ class Controller(tuned.exports.interfaces.ExportableInterface): Controller main loop. The call is blocking. """ log.info("starting controller") - self.start() + res = self.start() + daemon = self._global_config.get_bool(consts.CFG_DAEMON, consts.CFG_DEF_DAEMON) + if not res and daemon: + exports.start() - if self._global_config.get_bool(consts.CFG_DAEMON, consts.CFG_DEF_DAEMON): + if daemon: self._terminate.clear() # we have to pass some timeout, otherwise signals will not work while not self._cmd.wait(self._terminate, 3600): diff --git a/tuned/daemon/daemon.py b/tuned/daemon/daemon.py index 58d1340..52f6725 100644 --- a/tuned/daemon/daemon.py +++ b/tuned/daemon/daemon.py @@ -6,13 +6,14 @@ from tuned.exceptions import TunedException from tuned.profiles.exceptions import InvalidProfileException import tuned.consts as consts from tuned.utils.commands import commands +from tuned import exports import re log = tuned.logs.get() class Daemon(object): - def __init__(self, unit_manager, profile_loader, profile_name=None, config=None, application=None): + def __init__(self, unit_manager, profile_loader, profile_names=None, config=None, application=None): log.debug("initializing daemon") self._daemon = consts.CFG_DEF_DAEMON self._sleep_interval = int(consts.CFG_DEF_SLEEP_INTERVAL) @@ -43,7 +44,7 @@ class Daemon(object): self._init_threads() self._cmd = commands() try: - self._init_profile(profile_name) + self._init_profile(profile_names) except TunedException as e: log.error("Cannot set initial profile. No tunings will be enabled: %s" % e) @@ -57,40 +58,44 @@ class Daemon(object): self._not_used.set() self._profile_applied = threading.Event() - def _init_profile(self, profile_name): + def _init_profile(self, profile_names): manual = True - if profile_name is None: - (profile_name, manual) = self._get_startup_profile() - if profile_name is None: + if profile_names is None: + (profile_names, manual) = self._get_startup_profile() + if profile_names is None: log.info("No profile is preset, running in manual mode. No profile will be enabled.") # Passed through '-p' cmdline option - elif profile_name == "": + elif profile_names == "": log.info("No profile will be enabled.") self._profile = None self._manual = None - self.set_profile(profile_name, manual) + self.set_profile(profile_names, manual) - def set_profile(self, profile_name, manual, save_instantly=False): + def set_profile(self, profile_names, manual, save_instantly=False): if self.is_running(): - raise TunedException(self._notify_profile_changed(profile_name, False, "Cannot set profile while the daemon is running.")) + raise TunedException(self._notify_profile_changed(profile_names, False, "Cannot set profile while the daemon is running.")) - if profile_name == "" or profile_name is None: + if profile_names == "" or profile_names is None: self._profile = None self._manual = manual - elif profile_name not in self.profile_loader.profile_locator.get_known_names(): - raise TunedException(self._notify_profile_changed(profile_name, False, "Requested profile '%s' doesn't exist." % profile_name)) else: + profile_list = profile_names.split() + for profile in profile_list: + if profile not in self.profile_loader.profile_locator.get_known_names(): + raise TunedException(self._notify_profile_changed(\ + profile_names, False,\ + "Requested profile '%s' doesn't exist." % profile)) try: - self._profile = self._profile_loader.load(profile_name) + self._profile = self._profile_loader.load(profile_names) self._manual = manual except InvalidProfileException as e: - raise TunedException(self._notify_profile_changed(profile_name, False, "Cannot load profile '%s': %s" % (profile_name, e))) + raise TunedException(self._notify_profile_changed(profile_names, False, "Cannot load profile(s) '%s': %s" % (profile_names, e))) if save_instantly: - if profile_name is None: - profile_name = "" - self._save_active_profile(profile_name, manual) + if profile_names is None: + profile_names = "" + self._save_active_profile(profile_names, manual) @property def profile(self): @@ -106,17 +111,19 @@ class Daemon(object): # send notification when profile is changed (everything is setup) or if error occured # result: True - OK, False - error occured - def _notify_profile_changed(self, profile_name, result, errstr): + def _notify_profile_changed(self, profile_names, result, errstr): if self._application is not None and self._application._dbus_exporter is not None: - self._application._dbus_exporter.send_signal(consts.DBUS_SIGNAL_PROFILE_CHANGED, profile_name, result, errstr) + self._application._dbus_exporter.send_signal(consts.DBUS_SIGNAL_PROFILE_CHANGED, profile_names, result, errstr) return errstr - def _system_shutting_down(self): + def _full_rollback_required(self): retcode, out = self._cmd.execute(["systemctl", "is-system-running"], no_errors = [0]) + if retcode < 0: + return False if out[:8] == "stopping": - return True + return False retcode, out = self._cmd.execute(["systemctl", "list-jobs"], no_errors = [0]) - return re.search(r"\b(shutdown|reboot|halt|poweroff)\.target.*start", out) is not None + return re.search(r"\b(shutdown|reboot|halt|poweroff)\.target.*start", out) is None def _thread_code(self): if self._profile is None: @@ -127,6 +134,8 @@ class Daemon(object): self._unit_manager.start_tuning() self._profile_applied.set() log.info("static tuning from profile '%s' applied" % self._profile.name) + if self._daemon: + exports.start() self._notify_profile_changed(self._profile.name, True, "OK") if self._daemon: @@ -163,18 +172,18 @@ class Daemon(object): # stopped by user and in such case do full cleanup, without systemd never # do full cleanup full_rollback = False - if self._system_shutting_down(): - log.info("terminating Tuned due to system shutdown / reboot") - else: + if self._full_rollback_required(): log.info("terminating Tuned, rolling back all changes") full_rollback = True + else: + log.info("terminating Tuned due to system shutdown / reboot") if self._daemon: self._unit_manager.stop_tuning(full_rollback) self._unit_manager.destroy_all() - def _save_active_profile(self, profile_name, manual): + def _save_active_profile(self, profile_names, manual): try: - self._cmd.save_active_profile(profile_name, manual) + self._cmd.save_active_profile(profile_names, manual) except TunedException as e: log.error(str(e)) diff --git a/tuned/exports/dbus_exporter.py b/tuned/exports/dbus_exporter.py index 65ee969..959275e 100644 --- a/tuned/exports/dbus_exporter.py +++ b/tuned/exports/dbus_exporter.py @@ -56,6 +56,9 @@ class DBusExporter(interfaces.ExporterInterface): def object_name(self): return self._object_name + def running(self): + return self._thread is not None + def export(self, method, in_signature, out_signature): if not inspect.ismethod(method): raise Exception("Only bound methods can be exported.") @@ -129,6 +132,8 @@ class DBusExporter(interfaces.ExporterInterface): self._dbus_object_cls = cls def start(self): + if self.running(): + return if self._dbus_object_cls is None: self._construct_dbus_object_class() diff --git a/tuned/plugins/plugin_net.py b/tuned/plugins/plugin_net.py index c597000..d37ef3c 100644 --- a/tuned/plugins/plugin_net.py +++ b/tuned/plugins/plugin_net.py @@ -38,11 +38,16 @@ class NetTuningPlugin(base.Plugin): def _instance_init(self, instance): instance._has_static_tuning = True - instance._has_dynamic_tuning = True - - instance._load_monitor = self._monitors_repository.create("net", instance.devices) - instance._idle = {} - instance._stats = {} + if self._option_bool(instance.options["dynamic"]): + instance._has_dynamic_tuning = True + instance._load_monitor = self._monitors_repository.create("net", instance.devices) + instance._idle = {} + instance._stats = {} + else: + instance._has_dynamic_tuning = False + instance._load_monitor = None + instance._idle = None + instance._stats = None def _instance_cleanup(self, instance): if instance._load_monitor is not None: @@ -120,6 +125,7 @@ class NetTuningPlugin(base.Plugin): @classmethod def _get_config_options(cls): return { + "dynamic": True, "wake_on_lan": None, "nf_conntrack_hashsize": None, "features": None,