diff --git a/Makefile b/Makefile index 8c75ee3..4bdcce0 100644 --- a/Makefile +++ b/Makefile @@ -162,6 +162,7 @@ install: install-dirs # None profile in the moment, autodetection will be used echo -n > $(DESTDIR)$(SYSCONFDIR)/tuned/active_profile echo -n > $(DESTDIR)$(SYSCONFDIR)/tuned/profile_mode + echo -n > $(DESTDIR)$(SYSCONFDIR)/tuned/post_loaded_profile install -Dpm 0644 bootcmdline $(DESTDIR)$(SYSCONFDIR)/tuned/bootcmdline install -Dpm 0644 modules.conf $(DESTDIR)$(SYSCONFDIR)/modprobe.d/tuned.conf diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/conflicting/tuned.conf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/conflicting/tuned.conf new file mode 100644 index 0000000..c9ef906 --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/conflicting/tuned.conf @@ -0,0 +1,5 @@ +[main] +summary=Profile conflicting with the parent profile + +[sysctl] +vm.swappiness=10 diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/main.fmf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/main.fmf new file mode 100644 index 0000000..c9b95ab --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/main.fmf @@ -0,0 +1,12 @@ +summary: Test for BZ#1798183 (RFE support post-loaded profile) +description: | + Bug summary: RFE: support post-loaded profile + Bugzilla link: https://bugzilla.redhat.com/show_bug.cgi?id=1798183 +contact: +- Ondřej Lysoněk +component: +- tuned +require: +- tuned +duration: 5m +extra-task: /CoreOS/tuned/Regression/bz1798183-RFE-support-post-loaded-profile diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent-vars/tuned.conf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent-vars/tuned.conf new file mode 100644 index 0000000..7aac003 --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent-vars/tuned.conf @@ -0,0 +1,8 @@ +[main] +summary=Parent profile that defines a variable + +[variables] +foo=12 + +[sysctl] +vm.swappiness=${foo} diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent/tuned.conf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent/tuned.conf new file mode 100644 index 0000000..05d8385 --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent/tuned.conf @@ -0,0 +1,5 @@ +[main] +summary=Parent profile + +[sysctl] +vm.swappiness=20 diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent2/tuned.conf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent2/tuned.conf new file mode 100644 index 0000000..87c49e8 --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/parent2/tuned.conf @@ -0,0 +1,5 @@ +[main] +summary=Different parent profile + +[sysctl] +vm.swappiness=30 diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post-vars/tuned.conf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post-vars/tuned.conf new file mode 100644 index 0000000..0bef612 --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post-vars/tuned.conf @@ -0,0 +1,5 @@ +[main] +summary=Post-loaded profile that uses variables from the regular active profile + +[sysctl] +vm.dirty_ratio=${foo} diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post/tuned.conf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post/tuned.conf new file mode 100644 index 0000000..7844ca8 --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post/tuned.conf @@ -0,0 +1,5 @@ +[main] +summary=Post-loaded profile + +[sysctl] +vm.dirty_ratio=8 diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post2/tuned.conf b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post2/tuned.conf new file mode 100644 index 0000000..994db42 --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/post2/tuned.conf @@ -0,0 +1,5 @@ +[main] +summary=Second version of the post-loaded profile + +[sysctl] +vm.dirty_ratio=7 diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/runtest.sh b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/runtest.sh new file mode 100755 index 0000000..60d1a5b --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/runtest.sh @@ -0,0 +1,279 @@ +#!/bin/bash +# vim: dict+=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# runtest.sh of /CoreOS/tuned/Regression/bz1798183-RFE-support-post-loaded-profile +# Description: Test for BZ#1798183 (RFE support post-loaded profile) +# Author: Ondřej Lysoněk +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2020 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Include Beaker environment +. /usr/share/beakerlib/beakerlib.sh || exit 1 + +PACKAGE="tuned" +PROFILE_DIR=/etc/tuned +ACTIVE_PROFILE=/etc/tuned/active_profile +PROFILE_MODE=/etc/tuned/profile_mode +POST_LOADED_PROFILE=/etc/tuned/post_loaded_profile +SWAPPINESS=vm.swappiness +DIRTY_RATIO=vm.dirty_ratio +PID_FILE=/run/tuned/tuned.pid +SERVICE_OVERRIDE_DIR=/etc/systemd/system/tuned.service.d + +function wait_for_tuned() +{ + local timeout=$1 + local elapsed=0 + while ! python3 -c 'import dbus; bus = dbus.SystemBus(); exit(0 if bus.name_has_owner("com.redhat.tuned") else 1)'; do + sleep 1 + elapsed=$(($elapsed + 1)) + if [ "$elapsed" -ge "$timeout" ]; then + return 1 + fi + done + return 0 +} + +function wait_for_tuned_stop() +{ + local timeout=$1 + local elapsed=0 + while test -f "$PID_FILE"; do + sleep 1 + elapsed=$(($elapsed + 1)) + if [ "$elapsed" -ge "$timeout" ]; then + return 1 + fi + done + return 0 +} + + +rlJournalStart + rlPhaseStartSetup + rlAssertRpm $PACKAGE + rlRun "rlFileBackup --clean $PROFILE_DIR" + rlRun "cp -r parent $PROFILE_DIR" + rlRun "cp -r parent2 $PROFILE_DIR" + rlRun "cp -r parent-vars $PROFILE_DIR" + rlRun "cp -r post $PROFILE_DIR" + rlRun "cp -r post2 $PROFILE_DIR" + rlRun "cp -r post-vars $PROFILE_DIR" + rlRun "cp -r conflicting $PROFILE_DIR" + rlRun "TmpDir=\$(mktemp -d)" 0 "Creating tmp directory" + rlRun "cp wait_for_signal.py $TmpDir" + rlRun "pushd $TmpDir" + rlRun "rlFileBackup --clean $SERVICE_OVERRIDE_DIR" + rlRun "mkdir -p $SERVICE_OVERRIDE_DIR" + rlRun "echo -e '[Service]\\nStartLimitBurst=0' > $SERVICE_OVERRIDE_DIR/limit.conf" + rlRun "systemctl daemon-reload" + rlRun "rlServiceStop tuned" + SWAPPINESS_BACKUP=$(sysctl -n $SWAPPINESS) + DIRTY_RATIO_BACKUP=$(sysctl -n $DIRTY_RATIO) + rlRun "rlServiceStart tuned" + rlPhaseEnd + + rlPhaseStartTest "Check that settings from the post-loaded profile are applied" + rlRun "tuned-adm profile parent" + rlRun "echo post > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + rlAssertEquals "Check that swappiness is set correctly" \ + "$(sysctl -n $SWAPPINESS)" 20 + rlAssertEquals "Check that dirty ratio is set correctly" \ + "$(sysctl -n $DIRTY_RATIO)" 8 + rlPhaseEnd + + rlPhaseStartTest "Check that the post-loaded profile name gets reloaded when HUP is received" + rlRun "tuned-adm profile parent" + rlRun "echo post > $POST_LOADED_PROFILE" + rlRun "rlServiceStart tuned" + rlRun "echo parent2 > $ACTIVE_PROFILE" + rlRun "echo post2 > $POST_LOADED_PROFILE" + timeout 25s python3 ./wait_for_signal.py & + pid=$! + # Give the wait_for_signal script a chance to connect to the bus + sleep 1 + rlRun "kill -HUP '$(< $PID_FILE)'" 0 "Send HUP to Tuned" + rlRun "wait $pid" + rlAssertEquals "Check that swappiness is set correctly" \ + "$(sysctl -n $SWAPPINESS)" 30 + rlAssertEquals "Check that dirty ratio is set correctly" \ + "$(sysctl -n $DIRTY_RATIO)" 7 + rlPhaseEnd + + rlPhaseStartTest "Check that 'tuned-adm profile' does not cause Tuned to touch the post-loaded profile" + rlRun "tuned-adm profile parent" + rlRun "echo post > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + # Change the active profile. After this, the profile 'post' must remain applied. + rlRun "tuned-adm profile parent2" + rlAssertEquals "Check that swappiness is set correctly" \ + "$(sysctl -n $SWAPPINESS)" 30 + rlAssertEquals "Check that dirty ratio is set correctly" \ + "$(sysctl -n $DIRTY_RATIO)" 8 + rlPhaseEnd + + rlPhaseStartTest "Check that settings from the post-loaded profile take precedence" + rlRun "tuned-adm profile parent" + rlRun "echo conflicting > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + rlAssertEquals "Check that swappiness is set correctly" \ + "$(sysctl -n $SWAPPINESS)" 10 + rlPhaseEnd + + rlPhaseStartTest "Check that conflicts in the post-loaded profile do not cause verification to fail" + rlRun "tuned-adm profile parent" + rlRun "echo conflicting > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + rlRun "tuned-adm verify" + rlPhaseEnd + + rlPhaseStartTest "Check that 'tuned-adm off' causes Tuned to clear the post-loaded profile" + rlRun "tuned-adm profile parent" + rlRun "echo post > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + rlRun "tuned-adm off" + rlAssertEquals "Check the output of tuned-adm active" \ + "$(tuned-adm active)" \ + "No current active profile." + rlAssertEquals "Check that swappiness has not been changed" \ + "$(sysctl -n $SWAPPINESS)" "$SWAPPINESS_BACKUP" + rlAssertEquals "Check that dirty ratio has not been changed" \ + "$(sysctl -n $DIRTY_RATIO)" "$DIRTY_RATIO_BACKUP" + rlPhaseEnd + + rlPhaseStartTest "Check that the post-loaded profile is applied even if active_profile is empty" + rlRun "> $ACTIVE_PROFILE" + rlRun "echo manual > $PROFILE_MODE" + rlRun "echo post > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + rlAssertEquals "Check the output of tuned-adm active" \ + "$(tuned-adm active)" \ + "Current active profile: post" + rlAssertEquals "Check that dirty ratio is set correctly" \ + "$(sysctl -n $DIRTY_RATIO)" 8 + rlPhaseEnd + + rlPhaseStartTest "Check that the post-loaded profile is listed among active profiles by 'tuned-adm active'" + rlRun "tuned-adm profile parent" + rlRun "echo post > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + rlAssertEquals "Check the output of tuned-adm active" \ + "$(tuned-adm active | grep 'Current active profile')" \ + "Current active profile: parent post" + rlPhaseEnd + + rlPhaseStartTest "Check that 'tuned -p ' applies the post-loaded profile" + rlRun "rlServiceStop tuned" + rlRun "> $ACTIVE_PROFILE" + rlRun "echo post > $POST_LOADED_PROFILE" + rlRun "tuned -p parent &" + rlRun "wait_for_tuned 15" 0 "Wait for the profile to become applied" + rlAssertEquals "Check that swappiness is set correctly" \ + "$(sysctl -n $SWAPPINESS)" 20 + rlAssertEquals "Check that dirty ratio is set correctly" \ + "$(sysctl -n $DIRTY_RATIO)" 8 + rlAssertEquals "Check the output of tuned-adm active" \ + "$(tuned-adm active | grep 'Current active profile')" \ + "Current active profile: parent post" + rlRun "kill '$(< $PID_FILE)'" 0 "Kill Tuned" + rlRun "wait_for_tuned_stop 15" 0 "Wait for Tuned to exit" + rlPhaseEnd + + rlPhaseStartTest "Check that the DBus signal 'profile_changed' contains only the active_profile" + rlRun "rlServiceStop tuned" + rlRun "echo parent > $ACTIVE_PROFILE" + rlRun "echo post > $POST_LOADED_PROFILE" + timeout 25s python3 ./wait_for_signal.py > output & + pid=$! + # If the 'wait $pid' command below fails but everything else + # in this phase succeeds, try adding a sleep here. + rlRun "rlServiceStart tuned" + rlRun "wait $pid" + rlAssertEquals "Check the profiles listed in the signal" \ + "$(< output)" \ + "parent" + + timeout 25s python3 ./wait_for_signal.py > output & + pid=$! + rlRun "tuned-adm profile parent" + rlRun "wait $pid" + rlAssertEquals "Check the profiles listed in the signal" \ + "$(< output)" \ + "parent" + rlPhaseEnd + + rlPhaseStartTest "Check that 'tuned-adm profile' does not cause Tuned to reload the post-loaded profile name from disk" + rlRun "tuned-adm profile parent" + rlRun "echo post > $POST_LOADED_PROFILE" + rlRun "rlServiceStart tuned" + rlRun "echo post2 > $POST_LOADED_PROFILE" + # Change the active profile. After this, the profile 'post' must remain applied. + rlRun "tuned-adm profile parent2" + rlAssertEquals "Check that swappiness is set correctly" \ + "$(sysctl -n $SWAPPINESS)" 30 + rlAssertEquals "Check that dirty ratio is set correctly" \ + "$(sysctl -n $DIRTY_RATIO)" 8 + rlAssertEquals "Check the output of tuned-adm active" \ + "$(tuned-adm active | grep 'Current active profile')" \ + "Current active profile: parent2 post" + rlPhaseEnd + + rlPhaseStartTest "Check that 'tuned-adm profile' overwrites the post-loaded profile on disk" + rlRun "tuned-adm profile parent" + rlRun "echo post > $POST_LOADED_PROFILE" + rlRun "rlServiceStart tuned" + rlRun "echo post2 > $POST_LOADED_PROFILE" + rlRun "tuned-adm profile parent" + rlAssertEquals "Check the post-loaded profile name on disk" \ + "$(< $POST_LOADED_PROFILE)" \ + "post" + rlPhaseEnd + + rlPhaseStartTest "Check that variables are shared among the active_profile and the post-loaded profile" + rlRun "tuned-adm profile parent-vars" + rlRun "echo post-vars > $POST_LOADED_PROFILE" + # Restart Tuned so that the post-loaded profile gets applied + rlRun "rlServiceStart tuned" + rlAssertEquals "Check that swappiness is set correctly" \ + "$(sysctl -n $SWAPPINESS)" 12 + rlAssertEquals "Check that dirty ratio is set correctly" \ + "$(sysctl -n $DIRTY_RATIO)" 12 + rlPhaseEnd + + rlPhaseStartCleanup + rlRun "popd" + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + rlRun "rlFileRestore" + rlRun "systemctl daemon-reload" + rlRun "sysctl $SWAPPINESS=$SWAPPINESS_BACKUP" + rlRun "sysctl $DIRTY_RATIO=$DIRTY_RATIO_BACKUP" + rlRun "rlServiceRestore tuned" + rlPhaseEnd +rlJournalPrintText +rlJournalEnd diff --git a/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/wait_for_signal.py b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/wait_for_signal.py new file mode 100644 index 0000000..2a8085d --- /dev/null +++ b/tests/beakerlib/bz1798183-RFE-support-post-loaded-profile/wait_for_signal.py @@ -0,0 +1,13 @@ +from dbus.mainloop.glib import DBusGMainLoop +import dbus +from gi.repository import GLib + +def handler(profiles, res, err): + print(profiles) + loop.quit() + +DBusGMainLoop(set_as_default=True) +loop = GLib.MainLoop() +bus=dbus.SystemBus() +bus.add_signal_receiver(handler, "profile_changed", "com.redhat.tuned.control", "com.redhat.tuned", "/Tuned") +loop.run() diff --git a/tuned.spec b/tuned.spec index 6a15b89..42554f9 100644 --- a/tuned.spec +++ b/tuned.spec @@ -394,6 +394,7 @@ fi %{_libexecdir}/tuned/defirqaffinity* %config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/active_profile %config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/profile_mode +%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/post_loaded_profile %config(noreplace) %{_sysconfdir}/tuned/tuned-main.conf %config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/tuned/bootcmdline %{_sysconfdir}/dbus-1/system.d/com.redhat.tuned.conf diff --git a/tuned/consts.py b/tuned/consts.py index e68205b..f64f1f1 100644 --- a/tuned/consts.py +++ b/tuned/consts.py @@ -3,6 +3,7 @@ import logging GLOBAL_CONFIG_FILE = "/etc/tuned/tuned-main.conf" ACTIVE_PROFILE_FILE = "/etc/tuned/active_profile" PROFILE_MODE_FILE = "/etc/tuned/profile_mode" +POST_LOADED_PROFILE_FILE = "/etc/tuned/post_loaded_profile" PROFILE_FILE = "tuned.conf" RECOMMEND_CONF_FILE = "/etc/tuned/recommend.conf" DAEMONIZE_PARENT_TIMEOUT = 5 diff --git a/tuned/daemon/controller.py b/tuned/daemon/controller.py index 18e0bb6..88e4438 100644 --- a/tuned/daemon/controller.py +++ b/tuned/daemon/controller.py @@ -217,7 +217,8 @@ class Controller(tuned.exports.interfaces.ExportableInterface): if self._daemon.is_running(): self._daemon.stop() if self._daemon.is_enabled(): - self._daemon.set_profile(None, True, save_instantly=True) + self._daemon.set_all_profiles(None, True, None, + save_instantly=True) return True @exports.export("", "b") diff --git a/tuned/daemon/daemon.py b/tuned/daemon/daemon.py index fbf10c7..d0149d9 100644 --- a/tuned/daemon/daemon.py +++ b/tuned/daemon/daemon.py @@ -65,42 +65,93 @@ class Daemon(object): def _init_profile(self, profile_names): manual = True + post_loaded_profile = self._cmd.get_post_loaded_profile() 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.") + msg = "No profile is preset, running in manual mode. " + if post_loaded_profile: + msg += "Only post-loaded profile will be enabled" + else: + msg += "No profile will be enabled." + log.info(msg) # Passed through '-p' cmdline option elif profile_names == "": - log.info("No profile will be enabled.") + if post_loaded_profile: + log.info("Only post-loaded profile will be enabled") + else: + log.info("No profile will be enabled.") self._profile = None self._manual = None - self.set_profile(profile_names, manual) + self._active_profiles = [] + self._post_loaded_profile = None + self.set_all_profiles(profile_names, manual, post_loaded_profile) - def set_profile(self, profile_names, manual, save_instantly=False): - if self.is_running(): - raise TunedException(self._notify_profile_changed(profile_names, False, "Cannot set profile while the daemon is running.")) + def _load_profiles(self): + profile_list = self._active_profiles + if self._post_loaded_profile: + log.info("Using post-loaded profile '%s'" + % self._post_loaded_profile) + profile_list = profile_list + [self._post_loaded_profile] + for profile in profile_list: + if profile not in self.profile_loader.profile_locator.get_known_names(): + errstr = "Requested profile '%s' doesn't exist." % profile + profile_names = " ".join(self._active_profiles) + self._notify_profile_changed(profile_names, False, errstr) + raise TunedException(errstr) + try: + if profile_list: + self._profile = self._profile_loader.load(profile_list) + else: + self._profile = None + except InvalidProfileException as e: + errstr = "Cannot load profile(s) '%s': %s" % (" ".join(profile_list), e) + profile_names = " ".join(self._active_profiles) + self._notify_profile_changed(profile_names, False, errstr) + raise TunedException(errstr) - if profile_names == "" or profile_names is None: - self._profile = None - self._manual = manual + def _set_profile(self, profile_names, manual): + self._manual = manual + if profile_names: + self._active_profiles = profile_names.split() 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_names) - self._manual = manual - except InvalidProfileException as e: - raise TunedException(self._notify_profile_changed(profile_names, False, "Cannot load profile(s) '%s': %s" % (profile_names, e))) + self._active_profiles = [] + + def set_profile(self, profile_names, manual): + if self.is_running(): + errstr = "Cannot set profile while the daemon is running." + self._notify_profile_changed(profile_names, False, + errstr) + raise TunedException(errstr) + + self._set_profile(profile_names, manual) + self._load_profiles() + + def _set_post_loaded_profile(self, profile_name): + if not profile_name: + self._post_loaded_profile = None + elif len(profile_name.split()) > 1: + errstr = "Whitespace is not allowed in profile names; only a single post-loaded profile is allowed." + raise TunedException(errstr) + else: + self._post_loaded_profile = profile_name + + def set_all_profiles(self, active_profiles, manual, post_loaded_profile, + save_instantly=False): + if self.is_running(): + errstr = "Cannot set profile while the daemon is running." + self._notify_profile_changed(active_profiles, False, + errstr) + raise TunedException(errstr) + + self._set_profile(active_profiles, manual) + self._set_post_loaded_profile(post_loaded_profile) + self._load_profiles() if save_instantly: - if profile_names is None: - profile_names = "" - self._save_active_profile(profile_names, manual) + self._save_active_profile(active_profiles, manual) + self._save_post_loaded_profile(post_loaded_profile) @property def profile(self): @@ -135,13 +186,16 @@ class Daemon(object): raise TunedException("Cannot start the daemon without setting a profile.") self._unit_manager.create(self._profile.units) - self._save_active_profile(self._profile.name, self._manual) + self._save_active_profile(" ".join(self._active_profiles), + self._manual) + self._save_post_loaded_profile(self._post_loaded_profile) 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") + profile_names = " ".join(self._active_profiles) + self._notify_profile_changed(profile_names, True, "OK") if self._daemon: # In python 2 interpreter with applied patch for rhbz#917709 we need to periodically @@ -195,6 +249,12 @@ class Daemon(object): except TunedException as e: log.error(str(e)) + def _save_post_loaded_profile(self, profile_name): + try: + self._cmd.save_post_loaded_profile(profile_name) + except TunedException as e: + log.error(str(e)) + def _get_recommended_profile(self): log.info("Running in automatic mode, checking what profile is recommended for your configuration.") profile = ProfileRecommender().recommend(hardcoded = not self._recommend_command) diff --git a/tuned/utils/commands.py b/tuned/utils/commands.py index 10d8f53..6d9569e 100644 --- a/tuned/utils/commands.py +++ b/tuned/utils/commands.py @@ -471,3 +471,25 @@ class commands: f.write(mode + "\n") except (OSError,IOError) as e: raise TunedException("Failed to save profile mode: %s" % e.strerror) + + def get_post_loaded_profile(self): + profile_name = "" + try: + with open(consts.POST_LOADED_PROFILE_FILE, "r") as f: + profile_name = f.read().strip() + except IOError as e: + if e.errno != errno.ENOENT: + raise TunedException("Failed to read the active post-loaded profile: %s" % e) + except (OSError, EOFError) as e: + raise TunedException("Failed to read the active post-loaded profile: %s" % e) + if profile_name == "": + profile_name = None + return profile_name + + def save_post_loaded_profile(self, profile_name): + try: + with open(consts.POST_LOADED_PROFILE_FILE, "w") as f: + if profile_name is not None: + f.write(profile_name + "\n") + except (OSError,IOError) as e: + raise TunedException("Failed to save the active post-loaded profile: %s" % e.strerror)