1
0
Fork 0

Store profile selection mode in a separate file

The profile selection mode is now written to /etc/tuned/profile_mode
instead of the second line of /etc/tuned/active_profile.

Here are the rules for interpreting the contents of the files. If
either of the files does not exist, it is treated the same as if
it were empty.

If active_profile is empty:
    If profile_mode contains 'manual':
        Manual mode, no profile will be used, tuned will run without a profile
    else if profile_mode is empty or contains 'auto':
        Automatic mode, recommended profile is used
    else:
        Error
else:
    If profile_mode contains 'manual', or is empty (for compatibility reasons):
        Manual mode, the profile in active_profile will be used
    else if profile_mode contains 'auto':
        Automatic mode, recommended profile is used
    else:
        Error

Resolves: https://github.com/redhat-performance/tuned/issues/64

Signed-off-by: Ondřej Lysoněk <olysonek@redhat.com>
This commit is contained in:
Ondřej Lysoněk 2017-09-18 10:50:41 +02:00
parent ab598cd6ef
commit b2c80cabe5
5 changed files with 116 additions and 91 deletions

View file

@ -3,6 +3,7 @@ import tuned.admin
from tuned.utils.commands import commands from tuned.utils.commands import commands
from tuned.profiles import Locator as profiles_locator from tuned.profiles import Locator as profiles_locator
from exceptions import TunedAdminDBusException from exceptions import TunedAdminDBusException
from tuned.exceptions import TunedException
import tuned.consts as consts import tuned.consts as consts
import os import os
import sys import sys
@ -111,29 +112,14 @@ class Admin(object):
return profile_name return profile_name
def _get_active_profile(self): def _get_active_profile(self):
profile_name = None profile_name, manual = self._cmd.get_active_profile()
contents = str.strip(self._cmd.read_file(consts.ACTIVE_PROFILE_FILE))
if contents == '':
profile_name = ''
else:
arr = contents.split('\n')
profile_name = arr[0]
if profile_name == "":
profile_name = None
return profile_name return profile_name
def _get_profile_mode(self): def _get_profile_mode(self):
contents = str.strip(self._cmd.read_file(consts.ACTIVE_PROFILE_FILE)) (profile, manual) = self._cmd.get_active_profile()
if contents == '': if manual is None:
mode = consts.ACTIVE_PROFILE_AUTO manual = profile is not None
else: return consts.ACTIVE_PROFILE_MANUAL if manual else consts.ACTIVE_PROFILE_AUTO
arr = contents.split('\n')
if len(arr) == 1:
# The file was generated by old Tuned -> manual mode
mode = consts.ACTIVE_PROFILE_MANUAL
else:
mode = arr[1]
return mode
def _print_profile_info(self, profile, profile_info): def _print_profile_info(self, profile, profile_info):
if profile_info[0] == True: if profile_info[0] == True:
@ -157,7 +143,14 @@ class Admin(object):
def _action_profile_info(self, profile = ""): def _action_profile_info(self, profile = ""):
if profile == "": if profile == "":
profile = self._get_active_profile() try:
profile = self._get_active_profile()
if profile is None:
print("No current active profile.")
return False
except TunedException as e:
self._error(str(e))
return False
return self._print_profile_info(profile, self._profiles_locator.get_profile_attrs(profile, [consts.PROFILE_ATTR_SUMMARY, consts.PROFILE_ATTR_DESCRIPTION], ["", ""])) return self._print_profile_info(profile, self._profiles_locator.get_profile_attrs(profile, [consts.PROFILE_ATTR_SUMMARY, consts.PROFILE_ATTR_DESCRIPTION], ["", ""]))
def _print_profile_name(self, profile_name): def _print_profile_name(self, profile_name):
@ -172,7 +165,11 @@ class Admin(object):
return self._controller.exit(self._print_profile_name(self._dbus_get_active_profile())) return self._controller.exit(self._print_profile_name(self._dbus_get_active_profile()))
def _action_active(self): def _action_active(self):
profile_name = self._get_active_profile() try:
profile_name = self._get_active_profile()
except TunedException as e:
self._error(str(e))
return False
if profile_name is not None and not self._tuned_is_running(): if profile_name is not None and not self._tuned_is_running():
print("It seems that tuned daemon is not running, preset profile is not activated.") print("It seems that tuned daemon is not running, preset profile is not activated.")
print("Preset profile: %s" % profile_name) print("Preset profile: %s" % profile_name)
@ -183,14 +180,21 @@ class Admin(object):
print("Profile selection mode: " + mode) print("Profile selection mode: " + mode)
def _action_dbus_profile_mode(self): def _action_dbus_profile_mode(self):
mode = self._controller.profile_mode() mode, error = self._controller.profile_mode()
self._print_profile_mode(mode) self._print_profile_mode(mode)
if error != "":
self._error(error)
return self._controller.exit(False)
return self._controller.exit(True) return self._controller.exit(True)
def _action_profile_mode(self): def _action_profile_mode(self):
mode = self._get_profile_mode() try:
self._print_profile_mode(mode) mode = self._get_profile_mode()
return True self._print_profile_mode(mode)
return True
except TunedException as e:
self._error(str(e))
return False
def _profile_print_status(self, ret, msg): def _profile_print_status(self, ret, msg):
if ret: if ret:
@ -238,16 +242,13 @@ class Admin(object):
def _set_profile(self, profile_name, manual): def _set_profile(self, profile_name, manual):
if profile_name in self._profiles_locator.get_known_names(): if profile_name in self._profiles_locator.get_known_names():
s = profile_name + '\n' try:
if manual: self._cmd.save_active_profile(profile_name, manual)
s += consts.ACTIVE_PROFILE_MANUAL + '\n'
else:
s += consts.ACTIVE_PROFILE_AUTO + '\n'
if self._cmd.write_to_file(consts.ACTIVE_PROFILE_FILE, s):
self._restart_tuned() self._restart_tuned()
return True return True
else: except TunedException as e:
self._error("Unable to switch profile, do you have enough permissions?") self._error(str(e))
self._error("Unable to switch profile.")
return False return False
else: else:
self._error("Requested profile '%s' doesn't exist." % profile_name) self._error("Requested profile '%s' doesn't exist." % profile_name)

View file

@ -1,5 +1,6 @@
GLOBAL_CONFIG_FILE = "/etc/tuned/tuned-main.conf" GLOBAL_CONFIG_FILE = "/etc/tuned/tuned-main.conf"
ACTIVE_PROFILE_FILE = "/etc/tuned/active_profile" ACTIVE_PROFILE_FILE = "/etc/tuned/active_profile"
PROFILE_MODE_FILE = "/etc/tuned/profile_mode"
PROFILE_FILE = "tuned.conf" PROFILE_FILE = "tuned.conf"
RECOMMEND_CONF_FILE = "/etc/tuned/recommend.conf" RECOMMEND_CONF_FILE = "/etc/tuned/recommend.conf"
DAEMONIZE_PARENT_TIMEOUT = 5 DAEMONIZE_PARENT_TIMEOUT = 5
@ -104,7 +105,7 @@ STR_VERIFY_PROFILE_FAIL = "verify: failed: '%s'"
# timout for tuned-adm operations in seconds # timout for tuned-adm operations in seconds
ADMIN_TIMEOUT = 600 ADMIN_TIMEOUT = 600
# Strings for /etc/tuned/active_profile specifying if the active profile # Strings for /etc/tuned/profile_mode specifying if the active profile
# was set automatically or manually # was set automatically or manually
ACTIVE_PROFILE_AUTO = "auto" ACTIVE_PROFILE_AUTO = "auto"
ACTIVE_PROFILE_MANUAL = "manual" ACTIVE_PROFILE_MANUAL = "manual"

View file

@ -1,6 +1,7 @@
from tuned import exports from tuned import exports
import tuned.logs import tuned.logs
import tuned.exceptions import tuned.exceptions
from tuned.exceptions import TunedException
import threading import threading
import tuned.consts as consts import tuned.consts as consts
from tuned.utils.commands import commands from tuned.utils.commands import commands
@ -128,14 +129,23 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
else: else:
return "" return ""
@exports.export("", "s") @exports.export("", "(ss)")
def profile_mode(self, caller = None): def profile_mode(self, caller = None):
if caller == "": if caller == "":
return "" return "unknown", "Unauthorized"
if self._daemon.manual: manual = self._daemon.manual
return consts.ACTIVE_PROFILE_MANUAL if manual is None:
else: # This means no profile is applied. Check the preset value.
return consts.ACTIVE_PROFILE_AUTO try:
profile, manual = self._cmd.get_active_profile()
if manual is None:
manual = profile is not None
except TunedException as e:
mode = "unknown"
error = str(e)
return mode, error
mode = consts.ACTIVE_PROFILE_MANUAL if manual else consts.ACTIVE_PROFILE_AUTO
return mode, ""
@exports.export("", "b") @exports.export("", "b")
def disable(self, caller = None): def disable(self, caller = None):

View file

@ -60,6 +60,11 @@ class Daemon(object):
manual = True manual = True
if profile_name is None: if profile_name is None:
(profile_name, manual) = self._get_startup_profile() (profile_name, manual) = self._get_startup_profile()
if profile_name 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 == "":
log.info("No profile will be enabled.")
self._profile = None self._profile = None
self._manual = None self._manual = None
@ -71,7 +76,7 @@ class Daemon(object):
if profile_name == "" or profile_name is None: if profile_name == "" or profile_name is None:
self._profile = None self._profile = None
self._manual = None self._manual = manual
elif profile_name not in self.profile_loader.profile_locator.get_known_names(): 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)) raise TunedException(self._notify_profile_changed(profile_name, False, "Requested profile '%s' doesn't exist." % profile_name))
else: else:
@ -92,8 +97,7 @@ class Daemon(object):
@property @property
def manual(self): def manual(self):
# manual == None means /etc/tuned/active_profile is empty -> automatic mode return self._manual
return self._manual == True or self._manual is None
@property @property
def profile_loader(self): def profile_loader(self):
@ -164,58 +168,23 @@ class Daemon(object):
def _save_active_profile(self, profile_name, manual): def _save_active_profile(self, profile_name, manual):
try: try:
with open(consts.ACTIVE_PROFILE_FILE, "w") as f: self._cmd.save_active_profile(profile_name, manual)
if len(profile_name) > 0: except TunedException as e:
f.write(profile_name + "\n") log.error(str(e))
if manual:
f.write(consts.ACTIVE_PROFILE_MANUAL + "\n")
else:
f.write(consts.ACTIVE_PROFILE_AUTO + "\n")
except (OSError,IOError) as e:
log.error("Cannot write active profile into %s: %s" % (consts.ACTIVE_PROFILE_FILE, str(e)))
def _set_recommended_profile(self): def _get_recommended_profile(self):
log.info("no profile preset, checking what is recommended for your configuration") log.info("Running in automatic mode, checking what profile is recommended for your configuration.")
profile = self._cmd.recommend_profile(hardcoded = not self._recommend_command) profile = self._cmd.recommend_profile(hardcoded = not self._recommend_command)
log.info("using '%s' profile and setting it as active" % profile) log.info("Using '%s' profile" % profile)
self._save_active_profile(profile, False)
return profile return profile
def _get_startup_profile(self): def _get_startup_profile(self):
manual = False profile, manual = self._cmd.get_active_profile()
try: if manual is None:
with open(consts.ACTIVE_PROFILE_FILE, "r") as f: manual = profile is not None
content = f.read().strip() if not manual:
if content == "": profile = self._get_recommended_profile()
profile = self._set_recommended_profile() return profile, manual
else:
arr = content.split('\n')
if len(arr) > 2 or (len(arr) == 2 and arr[1] != consts.ACTIVE_PROFILE_AUTO and arr[1] != consts.ACTIVE_PROFILE_MANUAL):
profile = self._set_recommended_profile()
log.error("cannot read active profile from '%s': bad format. Falling back to '%s' profile."
% consts.ACTIVE_PROFILE_FILE, profile)
else:
profile = arr[0]
if len(arr) == 2:
manual = arr[1] == consts.ACTIVE_PROFILE_MANUAL
if not manual:
profile = self._set_recommended_profile()
else:
# The file has only one line - generated by previous Tuned version.
# Treat the profile as manually set.
manual = True
return (profile, manual)
except IOError as e:
if e.errno == errno.ENOENT:
# No such file or directory
profile = self._set_recommended_profile()
else:
profile = consts.DEFAULT_PROFILE
log.error("error reading active profile from '%s', falling back to '%s' profile" % (consts.ACTIVE_PROFILE_FILE, profile))
return (profile, manual)
except (OSError, EOFError) as e:
log.error("cannot read active profile, falling back to '%s' profile" % consts.DEFAULT_PROFILE)
return (consts.DEFAULT_PROFILE, manual)
def is_enabled(self): def is_enabled(self):
return self._profile is not None return self._profile is not None

View file

@ -8,6 +8,7 @@ from configobj import ConfigObj, ConfigObjError
import re import re
import procfs import procfs
from subprocess import * from subprocess import *
from tuned.exceptions import TunedException
log = tuned.logs.get() log = tuned.logs.get()
@ -463,3 +464,46 @@ class commands:
return val return val
except ValueError: except ValueError:
return None return None
def get_active_profile(self):
profile_name = ""
mode = ""
try:
with open(consts.ACTIVE_PROFILE_FILE, "r") as f:
profile_name = f.read().strip()
except IOError as e:
if e.errno != errno.ENOENT:
raise TunedException("Failed to read active profile: %s" % e)
except (OSError, EOFError) as e:
raise TunedException("Failed to read active profile: %s" % e)
try:
with open(consts.PROFILE_MODE_FILE, "r") as f:
mode = f.read().strip()
if mode not in ["", consts.ACTIVE_PROFILE_AUTO, consts.ACTIVE_PROFILE_MANUAL]:
raise TunedException("Invalid value in file %s." % consts.PROFILE_MODE_FILE)
except IOError as e:
if e.errno != errno.ENOENT:
raise TunedException("Failed to read profile mode: %s" % e)
except (OSError, EOFError) as e:
raise TunedException("Failed to read profile mode: %s" % e)
if mode == "":
manual = None
else:
manual = mode == consts.ACTIVE_PROFILE_MANUAL
if profile_name == "":
profile_name = None
return (profile_name, manual)
def save_active_profile(self, profile_name, manual):
try:
with open(consts.ACTIVE_PROFILE_FILE, "w") as f:
if profile_name is not None:
f.write(profile_name + "\n")
except (OSError,IOError) as e:
raise TunedException("Cannot write active profile into %s: %s" % (consts.ACTIVE_PROFILE_FILE, str(e)))
try:
with open(consts.PROFILE_MODE_FILE, "w") as f:
mode = consts.ACTIVE_PROFILE_MANUAL if manual else consts.ACTIVE_PROFILE_AUTO
f.write(mode + "\n")
except (OSError,IOError) as e:
raise TunedException("Cannot write profile mode into %s: %s" % (consts.PROFILE_MODE_FILE, str(e)))