1
0
Fork 0

Removing dependency on python-configobj.

Resolves: rhbz#1936386

Signed-off-by: Jan Zerdik <jzerdik@redhat.com>
This commit is contained in:
Jan Zerdik 2021-07-30 11:48:59 +02:00
parent 7ba8d5d694
commit 063277a05b
16 changed files with 286 additions and 117 deletions

View file

@ -29,7 +29,7 @@
# Limitation: # Limitation:
# Each profile can be specified only once, because there cannot be # Each profile can be specified only once, because there cannot be
# multiple sections in the configuration file with the same name # multiple sections in the configuration file with the same name
# (ConfigObj limitation). # (ConfigParser limitation).
# If there is a need to specify the profile multiple times, unique # If there is a need to specify the profile multiple times, unique
# suffix like ',ANYSTRING' can be used. Everything after the last ',' # suffix like ',ANYSTRING' can be used. Everything after the last ','
# is stripped by the parser, e.g.: # is stripped by the parser, e.g.:

View file

@ -46,6 +46,8 @@ class LoaderTestCase(unittest.TestCase):
f.write('file_path=${i:PROFILE_DIR}/whatever\n') f.write('file_path=${i:PROFILE_DIR}/whatever\n')
f.write('script=random_name.sh\n') f.write('script=random_name.sh\n')
f.write('[test_unit]\ntest_option=hello world\n') f.write('[test_unit]\ntest_option=hello world\n')
f.write('devices=/dev/${variable1},/dev/${variable2}\n')
f.write('[variables]\nvariable1=net\nvariable2=cpu')
def setUp(self): def setUp(self):
locator = profiles.Locator([self._profiles_dir]) locator = profiles.Locator([self._profiles_dir])
@ -105,6 +107,11 @@ class LoaderTestCase(unittest.TestCase):
self.assertEqual(config['test_unit']['test_option'],\ self.assertEqual(config['test_unit']['test_option'],\
'hello world') 'hello world')
def test_variables(self):
config = self._loader.load(['dummy4'])
self.assertEqual(config.units['test_unit'].devices,\
'/dev/net,/dev/cpu')
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
shutil.rmtree(cls._test_dir) shutil.rmtree(cls._test_dir)

View file

@ -30,7 +30,10 @@ class LocatorTestCase(unittest.TestCase):
conf_name = os.path.join(profile_dir, "tuned.conf") conf_name = os.path.join(profile_dir, "tuned.conf")
os.mkdir(profile_dir) os.mkdir(profile_dir)
with open(conf_name, "w") as conf_file: with open(conf_name, "w") as conf_file:
pass if profile_name != "custom":
conf_file.write("[main]\nsummary=this is " + profile_name + "\n")
else:
conf_file.write("summary=this is " + profile_name + "\n")
def test_init(self): def test_init(self):
Locator([]) Locator([])
@ -65,3 +68,16 @@ class LocatorTestCase(unittest.TestCase):
self.assertEqual(balanced, os.path.join(self._tmp_load_dirs[0], "balanced", "tuned.conf")) self.assertEqual(balanced, os.path.join(self._tmp_load_dirs[0], "balanced", "tuned.conf"))
known = locator.get_known_names() known = locator.get_known_names()
self.assertListEqual(known, ["balanced", "powersafe"]) self.assertListEqual(known, ["balanced", "powersafe"])
def test_get_known_names_summary(self):
self.assertEqual(("balanced", "this is balanced"), sorted(self.locator.get_known_names_summary())[0])
def test_get_profile_attrs(self):
attrs = self.locator.get_profile_attrs("balanced", ["summary", "wrong_attr"], ["this is default", "this is wrong attr"])
self.assertEqual([True, "balanced", "this is balanced", "this is wrong attr"], attrs)
attrs = self.locator.get_profile_attrs("custom", ["summary"], ["wrongly writen profile"])
self.assertEqual([True, "custom", "wrongly writen profile"], attrs)
attrs = self.locator.get_profile_attrs("different", ["summary"], ["non existing profile"])
self.assertEqual([False, "", "", ""], attrs)

View file

@ -0,0 +1,32 @@
import unittest
import tempfile
import shutil
from tuned.profiles import variables, profile
class VariablesTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.test_dir = tempfile.mkdtemp()
with open(cls.test_dir + "/variables", 'w') as f:
f.write("variable1=var1\n")
def test_from_file(self):
v = variables.Variables()
v.add_from_file(self.test_dir + "/variables")
self.assertEqual("This is var1", v.expand("This is ${variable1}"))
def test_from_unit(self):
mock_unit = {
"include": self.test_dir + "/variables",
"variable2": "var2"
}
v = variables.Variables()
v.add_from_cfg(mock_unit)
self.assertEqual("This is var1 and this is var2", v.expand("This is ${variable1} and this is ${variable2}"))
@classmethod
def tearDownClass(cls):
shutil.rmtree(cls.test_dir)

View file

@ -12,7 +12,8 @@ class GlobalConfigTestCase(unittest.TestCase):
cls.test_dir = tempfile.mkdtemp() cls.test_dir = tempfile.mkdtemp()
with open(cls.test_dir + '/test_config','w') as f: with open(cls.test_dir + '/test_config','w') as f:
f.write('test_option = hello\ntest_bool = 1\ntest_size = 12MB\n'\ f.write('test_option = hello\ntest_bool = 1\ntest_size = 12MB\n'\
+ 'false_bool=0\n') + 'false_bool=0\n'\
+ consts.CFG_LOG_FILE_COUNT + " = " + str(consts.CFG_DEF_LOG_FILE_COUNT) + "1\n")
cls._global_config = global_config.GlobalConfig(\ cls._global_config = global_config.GlobalConfig(\
cls.test_dir + '/test_config') cls.test_dir + '/test_config')
@ -28,10 +29,18 @@ class GlobalConfigTestCase(unittest.TestCase):
self.assertEqual(self._global_config.get_size('test_size'),\ self.assertEqual(self._global_config.get_size('test_size'),\
12*1024*1024) 12*1024*1024)
self._global_config.set('test_size','bad_value') self._global_config.set('test_size', 'bad_value')
self.assertIsNone(self._global_config.get_size('test_size')) self.assertIsNone(self._global_config.get_size('test_size'))
def test_default(self):
daemon = self._global_config.get(consts.CFG_DAEMON)
self.assertEqual(daemon, consts.CFG_DEF_DAEMON)
log_file_count = self._global_config.get(consts.CFG_LOG_FILE_COUNT)
self.assertIsNotNone(log_file_count)
self.assertNotEqual(log_file_count, consts.CFG_DEF_LOG_FILE_COUNT)
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
shutil.rmtree(cls.test_dir) shutil.rmtree(cls.test_dir)

View file

@ -48,7 +48,7 @@ except ImportError:
import sys import sys
import os import os
import time import time
import configobj import collections
import subprocess import subprocess
import tuned.logs import tuned.logs
@ -508,8 +508,7 @@ class Base(object):
def data_to_profile_config(self): def data_to_profile_config(self):
name = self._gobj('entryProfileName').get_text() name = self._gobj('entryProfileName').get_text()
config = configobj.ConfigObj(list_values = False, config = collections.OrderedDict()
interpolation = False)
activated = self._gobj('comboboxIncludeProfile').get_active() activated = self._gobj('comboboxIncludeProfile').get_active()
model = self._gobj('comboboxIncludeProfile').get_model() model = self._gobj('comboboxIncludeProfile').get_model()

View file

@ -66,9 +66,8 @@ BuildRequires: %{_py}, %{_py}-devel
%if %{without python3} && ( ! 0%{?rhel} || 0%{?rhel} >= 8 ) %if %{without python3} && ( ! 0%{?rhel} || 0%{?rhel} >= 8 )
BuildRequires: %{_py}-mock BuildRequires: %{_py}-mock
%endif %endif
BuildRequires: %{_py}-configobj
BuildRequires: %{_py}-pyudev BuildRequires: %{_py}-pyudev
Requires: %{_py}-pyudev, %{_py}-configobj Requires: %{_py}-pyudev
Requires: %{_py}-linux-procfs, %{_py}-perf Requires: %{_py}-linux-procfs, %{_py}-perf
%if %{without python3} %if %{without python3}
Requires: %{_py}-schedutils Requires: %{_py}-schedutils

View file

@ -16,6 +16,8 @@ DEFAULT_STORAGE_FILE = "/run/tuned/save.pickle"
LOAD_DIRECTORIES = ["/usr/lib/tuned", "/etc/tuned"] LOAD_DIRECTORIES = ["/usr/lib/tuned", "/etc/tuned"]
PERSISTENT_STORAGE_DIR = "/var/lib/tuned" PERSISTENT_STORAGE_DIR = "/var/lib/tuned"
PLUGIN_MAIN_UNIT_NAME = "main" PLUGIN_MAIN_UNIT_NAME = "main"
# Magic section header because ConfigParser does not support "headerless" config
MAGIC_HEADER_NAME = "this_is_some_magic_section_header_because_of_compatibility"
RECOMMEND_DIRECTORIES = ["/usr/lib/tuned/recommend.d", "/etc/tuned/recommend.d"] RECOMMEND_DIRECTORIES = ["/usr/lib/tuned/recommend.d", "/etc/tuned/recommend.d"]
TMP_FILE_SUFFIX = ".tmp" TMP_FILE_SUFFIX = ".tmp"
@ -79,6 +81,10 @@ ENV_PREFIX = "TUNED_"
PREFIX_PROFILE_FACTORY = "System" PREFIX_PROFILE_FACTORY = "System"
PREFIX_PROFILE_USER = "User" PREFIX_PROFILE_USER = "User"
# After adding new option to tuned-main.conf add here its name with CFG_ prefix
# and eventually default value with CFG_DEF_ prefix (default is None)
# and function for check with CFG_FUNC_ prefix
# (see configobj for methods, default is get for string)
CFG_DAEMON = "daemon" CFG_DAEMON = "daemon"
CFG_DYNAMIC_TUNING = "dynamic_tuning" CFG_DYNAMIC_TUNING = "dynamic_tuning"
CFG_SLEEP_INTERVAL = "sleep_interval" CFG_SLEEP_INTERVAL = "sleep_interval"
@ -87,25 +93,40 @@ CFG_RECOMMEND_COMMAND = "recommend_command"
CFG_REAPPLY_SYSCTL = "reapply_sysctl" CFG_REAPPLY_SYSCTL = "reapply_sysctl"
CFG_DEFAULT_INSTANCE_PRIORITY = "default_instance_priority" CFG_DEFAULT_INSTANCE_PRIORITY = "default_instance_priority"
CFG_UDEV_BUFFER_SIZE = "udev_buffer_size" CFG_UDEV_BUFFER_SIZE = "udev_buffer_size"
CFG_LOG_FILE_COUNT = "log_file_count"
CFG_LOG_FILE_MAX_SIZE = "log_file_max_size"
CFG_UNAME_STRING = "uname_string" CFG_UNAME_STRING = "uname_string"
CFG_CPUINFO_STRING = "cpuinfo_string" CFG_CPUINFO_STRING = "cpuinfo_string"
# no_daemon mode # no_daemon mode
CFG_DEF_DAEMON = True CFG_DEF_DAEMON = True
CFG_FUNC_DAEMON = "getboolean"
# default configuration # default configuration
CFG_DEF_DYNAMIC_TUNING = True CFG_DEF_DYNAMIC_TUNING = True
CFG_FUNC_DYNAMIC_TUNING = "getboolean"
# how long to sleep before checking for events (in seconds) # how long to sleep before checking for events (in seconds)
CFG_DEF_SLEEP_INTERVAL = 1 CFG_DEF_SLEEP_INTERVAL = 1
CFG_FUNC_SLEEP_INTERVAL = "getint"
# update interval for dynamic tuning (in seconds) # update interval for dynamic tuning (in seconds)
CFG_DEF_UPDATE_INTERVAL = 10 CFG_DEF_UPDATE_INTERVAL = 10
CFG_FUNC_UPDATE_INTERVAL = "getint"
# recommend command availability # recommend command availability
CFG_DEF_RECOMMEND_COMMAND = True CFG_DEF_RECOMMEND_COMMAND = True
CFG_FUNC_RECOMMEND_COMMAND = "getboolean"
# reapply system sysctl # reapply system sysctl
CFG_DEF_REAPPLY_SYSCTL = True CFG_DEF_REAPPLY_SYSCTL = True
CFG_FUNC_REAPPLY_SYSCTL = "getboolean"
# default instance priority # default instance priority
CFG_DEF_DEFAULT_INSTANCE_PRIORITY = 0 CFG_DEF_DEFAULT_INSTANCE_PRIORITY = 0
CFG_FUNC_DEFAULT_INSTANCE_PRIORITY = "getint"
# default pyudev.Monitor buffer size # default pyudev.Monitor buffer size
CFG_DEF_UDEV_BUFFER_SIZE = 1024 * 1024 CFG_DEF_UDEV_BUFFER_SIZE = 1024 * 1024
# default log file count
CFG_DEF_LOG_FILE_COUNT = 2
CFG_FUNC_LOG_FILE_COUNT = "getint"
# default log file max size
CFG_DEF_LOG_FILE_MAX_SIZE = 1024 * 1024
PATH_CPU_DMA_LATENCY = "/dev/cpu_dma_latency" PATH_CPU_DMA_LATENCY = "/dev/cpu_dma_latency"

View file

@ -25,25 +25,23 @@ Created on Mar 30, 2014
''' '''
import importlib import importlib
from validate import Validator
import tuned.consts as consts import tuned.consts as consts
import tuned.logs import tuned.logs
try:
import configobj as ConfigObj from configparser import ConfigParser, Error
from io import StringIO
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser, Error
from StringIO import StringIO
from tuned.exceptions import TunedException from tuned.exceptions import TunedException
from tuned.utils.global_config import GlobalConfig
from tuned.admin.dbus_controller import DBusController from tuned.admin.dbus_controller import DBusController
__all__ = ['GuiPluginLoader'] __all__ = ['GuiPluginLoader']
global_config_spec = ['dynamic_tuning = boolean(default=%s)'
% consts.CFG_DEF_DYNAMIC_TUNING,
'sleep_interval = integer(default=%s)'
% consts.CFG_DEF_SLEEP_INTERVAL,
'update_interval = integer(default=%s)'
% consts.CFG_DEF_UPDATE_INTERVAL]
class GuiPluginLoader(): class GuiPluginLoader():
@ -84,19 +82,26 @@ class GuiPluginLoader():
""" """
try: try:
config = ConfigObj.ConfigObj(file_name, config_parser = ConfigParser()
configspec=global_config_spec, config_parser.optionxform = str
raise_errors = True, file_error = True, list_values = False, interpolation = False) with open(file_name) as f:
config_parser.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
config, functions = GlobalConfig.get_global_config_spec()
for option in config_parser.options(consts.MAGIC_HEADER_NAME):
if option in config:
try:
func = getattr(config_parser, functions[option])
config[option] = func(consts.MAGIC_HEADER_NAME, option)
except Error:
raise TunedException("Global TuneD configuration file '%s' is not valid."
% file_name)
else:
config[option] = config_parser.get(consts.MAGIC_HEADER_NAME, option, raw=True)
except IOError as e: except IOError as e:
raise TunedException("Global TuneD configuration file '%s' not found." raise TunedException("Global TuneD configuration file '%s' not found."
% file_name) % file_name)
except ConfigObj.ConfigObjError as e: except Error as e:
raise TunedException("Error parsing global TuneD configuration file '%s'." raise TunedException("Error parsing global TuneD configuration file '%s'."
% file_name) % file_name)
vdt = Validator()
if not config.validate(vdt, copy=True):
raise TunedException("Global TuneD configuration file '%s' is not valid."
% file_name)
return config return config

View file

@ -25,10 +25,17 @@ Created on Mar 13, 2014
''' '''
import os import os
import configobj try:
from configparser import ConfigParser, Error
from io import StringIO
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser, Error
from StringIO import StringIO
import subprocess import subprocess
import json import json
import sys import sys
import collections
import tuned.profiles.profile as p import tuned.profiles.profile as p
import tuned.consts import tuned.consts
@ -59,14 +66,21 @@ class GuiProfileLoader(object):
profilePath = self._locate_profile_path(profile_name) profilePath = self._locate_profile_path(profile_name)
config_lines = config.split('\n')
if profilePath == tuned.consts.LOAD_DIRECTORIES[1]: if profilePath == tuned.consts.LOAD_DIRECTORIES[1]:
file_path = profilePath + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE file_path = profilePath + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
config_parser = ConfigParser()
config_parser.optionxform = str
config_parser.readfp(StringIO(config))
config_obj = configobj.ConfigObj(infile=config_lines,list_values = False, interpolation = False) config_obj = {
config_obj.filename = file_path 'main': collections.OrderedDict(),
config_obj.initial_comment = ('#', 'tuned configuration', '#') 'filename': file_path,
'initial_comment': ('#', 'tuned configuration', '#')
}
for s in config_parser.sections():
config_obj['main'][s] = collections.OrderedDict()
for o in config_parser.options(s):
config_obj['main'][s][o] = config_parser.get(s, o, raw=True)
self._save_profile(config_obj) self._save_profile(config_obj)
self._refresh_profiles() self._refresh_profiles()
else: else:
@ -76,8 +90,15 @@ class GuiProfileLoader(object):
def load_profile_config(self, profile_name, path): def load_profile_config(self, profile_name, path):
conf_path = path + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE conf_path = path + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
profile_config = configobj.ConfigObj(conf_path, list_values = False, config = ConfigParser()
interpolation = False) config.optionxform = str
profile_config = collections.OrderedDict()
with open(conf_path) as f:
config.readfp(f)
for s in config.sections():
profile_config[s] = collections.OrderedDict()
for o in config.options(s):
profile_config[s][o] = config.get(s, o, raw=True)
return profile_config return profile_config
def _locate_profile_path(self, profile_name): def _locate_profile_path(self, profile_name):
@ -95,11 +116,11 @@ class GuiProfileLoader(object):
try: try:
self.profiles[profile] = p.Profile(profile, self.profiles[profile] = p.Profile(profile,
self.load_profile_config(profile, d)) self.load_profile_config(profile, d))
except configobj.ParseError: except Error:
pass pass
# print "can not make \""+ profile +"\" profile without correct config on path: " + d # print "can not make \""+ profile +"\" profile without correct config on path: " + d
# except: # except:StringIO
# raise managerException.ManagerException("Can not make profile") # raise managerException.ManagerException("Can not make profile")
# print "can not make \""+ profile +"\" profile without correct config with path: " + d # print "can not make \""+ profile +"\" profile without correct config with path: " + d
@ -113,20 +134,24 @@ class GuiProfileLoader(object):
def save_profile(self, profile): def save_profile(self, profile):
path = tuned.consts.LOAD_DIRECTORIES[1] + '/' + profile.name path = tuned.consts.LOAD_DIRECTORIES[1] + '/' + profile.name
config = configobj.ConfigObj(list_values = False, interpolation = False) config = {
config.filename = path + '/' + tuned.consts.PROFILE_FILE 'main': collections.OrderedDict(),
config.initial_comment = ('#', 'tuned configuration', '#') 'filename': path + '/' + tuned.consts.PROFILE_FILE,
'initial_comment': ('#', 'tuned configuration', '#')
}
config['filename'] = path + '/' + tuned.consts.PROFILE_FILE
config['initial_comment'] = ('#', 'tuned configuration', '#')
try: try:
config['main'] = profile.options config['main']['main'] = profile.options
except KeyError: except KeyError:
config['main'] = '' config['main']['main'] = {}
# profile dont have main section # profile dont have main section
pass pass
for (name, unit) in list(profile.units.items()): for (name, unit) in list(profile.units.items()):
config[name] = unit.options config['main'][name] = unit.options
self._save_profile(config) self._save_profile(config)
@ -148,18 +173,20 @@ class GuiProfileLoader(object):
if old_profile_name != profile.name: if old_profile_name != profile.name:
self.remove_profile(old_profile_name, is_admin=is_admin) self.remove_profile(old_profile_name, is_admin=is_admin)
config = configobj.ConfigObj(list_values = False, interpolation = False) config = {
config.filename = path + '/' + tuned.consts.PROFILE_FILE 'main': collections.OrderedDict(),
config.initial_comment = ('#', 'tuned configuration', '#') 'filename': path + '/' + tuned.consts.PROFILE_FILE,
'initial_comment': ('#', 'tuned configuration', '#')
}
try: try:
config['main'] = profile.options config['main']['main'] = profile.options
except KeyError: except KeyError:
# profile dont have main section # profile dont have main section
pass pass
for (name, unit) in list(profile.units.items()): for (name, unit) in list(profile.units.items()):
config[name] = unit.options config['main'][name] = unit.options
self._save_profile(config) self._save_profile(config)

View file

@ -1,7 +1,11 @@
import os import os
import sys import sys
import json import json
from configobj import ConfigObj try:
from configparser import ConfigParser
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser
if __name__ == "__main__": if __name__ == "__main__":
@ -11,13 +15,19 @@ if __name__ == "__main__":
if not os.path.exists(profile_dict['filename']): if not os.path.exists(profile_dict['filename']):
os.makedirs(os.path.dirname(profile_dict['filename'])) os.makedirs(os.path.dirname(profile_dict['filename']))
profile_configobj = ConfigObj() profile_configobj = ConfigParser()
for section in profile_dict['sections']: profile_configobj.optionxform = str
profile_configobj[section] = profile_dict['main'][section] for section, options in profile_dict['main'].items():
profile_configobj.add_section(section)
for option, value in options.items():
profile_configobj.set(section, option, value)
profile_configobj.filename = os.path.join('/etc','tuned',os.path.dirname(os.path.abspath(profile_dict['filename'])),'tuned.conf') path = os.path.join('/etc','tuned',os.path.dirname(os.path.abspath(profile_dict['filename'])),'tuned.conf')
profile_configobj.initial_comment = profile_dict['initial_comment'] with open(path, 'w') as f:
profile_configobj.write(f)
profile_configobj.write() with open(path, 'r+') as f:
content = f.read()
f.seek(0, 0)
f.write("\n".join(profile_dict['initial_comment']) + "\n" + content)
sys.exit(0) sys.exit(0)

View file

@ -1,6 +1,10 @@
import tuned.profiles.profile import tuned.profiles.profile
import tuned.profiles.variables import tuned.profiles.variables
from configobj import ConfigObj, ConfigObjError try:
from configparser import ConfigParser, Error
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser, Error
import tuned.consts as consts import tuned.consts as consts
import os.path import os.path
import collections import collections
@ -96,30 +100,22 @@ class Loader(object):
def _load_config_data(self, file_name): def _load_config_data(self, file_name):
try: try:
config_obj = ConfigObj(file_name, raise_errors = True, list_values = False, interpolation = False) config_obj = ConfigParser()
except ConfigObjError as e: config_obj.optionxform=str
with open(file_name) as f:
config_obj.readfp(f)
except Error as e:
raise InvalidProfileException("Cannot parse '%s'." % file_name, e) raise InvalidProfileException("Cannot parse '%s'." % file_name, e)
config = collections.OrderedDict() config = collections.OrderedDict()
for section in list(config_obj.keys()):
config[section] = collections.OrderedDict()
try:
keys = list(config_obj[section].keys())
except AttributeError:
raise InvalidProfileException("Error parsing section '%s' in file '%s'." % (section, file_name))
for option in keys:
config[section][option] = config_obj[section][option]
dir_name = os.path.dirname(file_name) dir_name = os.path.dirname(file_name)
# TODO: Could we do this in the same place as the expansion of other functions? for section in list(config_obj.sections()):
for section in config: config[section] = collections.OrderedDict()
for option in config[section]: for option in config_obj.options(section):
config[section][option] = config_obj.get(section, option, raw=True)
config[section][option] = self._expand_profile_dir(dir_name, config[section][option]) config[section][option] = self._expand_profile_dir(dir_name, config[section][option])
if config[section].get("script") is not None:
# TODO: HACK, this needs to be solved in a better way (better config parser) script_path = os.path.join(dir_name, config[section]["script"])
for unit_name in config: config[section]["script"] = [os.path.normpath(script_path)]
if "script" in config[unit_name] and config[unit_name].get("script", None) is not None:
script_path = os.path.join(dir_name, config[unit_name]["script"])
config[unit_name]["script"] = [os.path.normpath(script_path)]
return config return config

View file

@ -1,6 +1,12 @@
import os import os
import tuned.consts as consts import tuned.consts as consts
from configobj import ConfigObj, ConfigObjError try:
from configparser import ConfigParser, Error
from io import StringIO
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser, Error
from StringIO import StringIO
class Locator(object): class Locator(object):
""" """
@ -48,8 +54,12 @@ class Locator(object):
if config_file is None: if config_file is None:
return None return None
try: try:
return ConfigObj(config_file, list_values = False, interpolation = False) config = ConfigParser()
except (IOError, OSError, ConfigObjError) as e: config.optionxform = str
with open(config_file) as f:
config.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
return config
except (IOError, OSError, Error) as e:
return None return None
# Get profile attributes (e.g. summary, description), attrs is list of requested attributes, # Get profile attributes (e.g. summary, description), attrs is list of requested attributes,
@ -75,17 +85,16 @@ class Locator(object):
config = self.parse_config(profile_name) config = self.parse_config(profile_name)
if config is None: if config is None:
return [False, "", "", ""] return [False, "", "", ""]
if consts.PLUGIN_MAIN_UNIT_NAME in config: main_unit_in_config = consts.PLUGIN_MAIN_UNIT_NAME in config.sections()
d = config[consts.PLUGIN_MAIN_UNIT_NAME]
else:
d = dict()
vals = [True, profile_name] vals = [True, profile_name]
for (attr, defval) in zip(attrs, defvals): for (attr, defval) in zip(attrs, defvals):
if attr == "" or attr is None: if attr == "" or attr is None:
vals[0] = False vals[0] = False
vals = vals + [""] vals = vals + [""]
elif main_unit_in_config and attr in config.options(consts.PLUGIN_MAIN_UNIT_NAME):
vals = vals + [config.get(consts.PLUGIN_MAIN_UNIT_NAME, attr, raw=True)]
else: else:
vals = vals + [d.get(attr, defval)] vals = vals + [defval]
return vals return vals
def list_profiles(self): def list_profiles(self):

View file

@ -4,7 +4,13 @@ import tuned.logs
from .functions import functions as functions from .functions import functions as functions
import tuned.consts as consts import tuned.consts as consts
from tuned.utils.commands import commands from tuned.utils.commands import commands
from configobj import ConfigObj, ConfigObjError try:
from configparser import ConfigParser, Error
from io import StringIO
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser, Error
from StringIO import StringIO
log = tuned.logs.get() log = tuned.logs.get()
@ -40,24 +46,21 @@ class Variables():
self._lookup_re[r'(?<!\\)\${' + re.escape(s) + r'}'] = v self._lookup_re[r'(?<!\\)\${' + re.escape(s) + r'}'] = v
self._lookup_env[self._add_env_prefix(s, consts.ENV_PREFIX)] = v self._lookup_env[self._add_env_prefix(s, consts.ENV_PREFIX)] = v
def add_dict(self, d):
for item in d:
self.add_variable(item, d[item])
def add_from_file(self, filename): def add_from_file(self, filename):
if not os.path.exists(filename): if not os.path.exists(filename):
log.error("unable to find variables_file: '%s'" % filename) log.error("unable to find variables_file: '%s'" % filename)
return return
try: try:
config = ConfigObj(filename, raise_errors = True, file_error = True, list_values = False, interpolation = False) config = ConfigParser()
except ConfigObjError: config.optionxform = str
with open(filename) as f:
config.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
except Error:
log.error("error parsing variables_file: '%s'" % filename) log.error("error parsing variables_file: '%s'" % filename)
return return
for item in config: for s in config.sections():
if isinstance(config[item], dict): for o in config.options(s):
self.add_dict(config[item]) self.add_variable(o, config.get(s, o, raw=True))
else:
self.add_variable(item, config[item])
def add_from_cfg(self, cfg): def add_from_cfg(self, cfg):
for item in cfg: for item in cfg:

View file

@ -1,6 +1,11 @@
import tuned.logs import tuned.logs
from configobj import ConfigObj, ConfigObjError try:
from validate import Validator from configparser import ConfigParser, Error
from io import StringIO
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser, Error
from StringIO import StringIO
from tuned.exceptions import TunedException from tuned.exceptions import TunedException
import tuned.consts as consts import tuned.consts as consts
from tuned.utils.commands import commands from tuned.utils.commands import commands
@ -11,31 +16,55 @@ log = tuned.logs.get()
class GlobalConfig(): class GlobalConfig():
global_config_spec = ["dynamic_tuning = boolean(default=%s)" % consts.CFG_DEF_DYNAMIC_TUNING,
"sleep_interval = integer(default=%s)" % consts.CFG_DEF_SLEEP_INTERVAL,
"update_interval = integer(default=%s)" % consts.CFG_DEF_UPDATE_INTERVAL,
"recommend_command = boolean(default=%s)" % consts.CFG_DEF_RECOMMEND_COMMAND]
def __init__(self,config_file = consts.GLOBAL_CONFIG_FILE): def __init__(self,config_file = consts.GLOBAL_CONFIG_FILE):
self._cfg = {} self._cfg = {}
self.load_config(file_name=config_file) self.load_config(file_name=config_file)
self._cmd = commands() self._cmd = commands()
@staticmethod
def get_global_config_spec():
"""
Easy validation mimicking configobj
Returns two dicts, firts with default values (default None)
global_default[consts.CFG_SOMETHING] = consts.CFG_DEF_SOMETHING or None
second with configobj function for value type (default "get" for string, others eg getboolean, getint)
global_function[consts.CFG_SOMETHING] = consts.CFG_FUNC_SOMETHING or get
}
"""
options = [opt for opt in dir(consts)
if opt.startswith("CFG_") and
not opt.startswith("CFG_FUNC_") and
not opt.startswith("CFG_DEF_")]
global_default = dict((getattr(consts, opt), getattr(consts, "CFG_DEF_" + opt[4:], None)) for opt in options)
global_function = dict((getattr(consts, opt), getattr(consts, "CFG_FUNC_" + opt[4:], "get")) for opt in options)
return global_default, global_function
def load_config(self, file_name = consts.GLOBAL_CONFIG_FILE): def load_config(self, file_name = consts.GLOBAL_CONFIG_FILE):
""" """
Loads global configuration file. Loads global configuration file.
""" """
log.debug("reading and parsing global configuration file '%s'" % file_name) log.debug("reading and parsing global configuration file '%s'" % file_name)
try: try:
self._cfg = ConfigObj(file_name, configspec = self.global_config_spec, raise_errors = True, \ config_parser = ConfigParser()
file_error = True, list_values = False, interpolation = False) config_parser.optionxform = str
with open(file_name) as f:
config_parser.readfp(StringIO("[" + consts.MAGIC_HEADER_NAME + "]\n" + f.read()))
self._cfg, _global_config_func = self.get_global_config_spec()
for option in config_parser.options(consts.MAGIC_HEADER_NAME):
if option in self._cfg:
try:
func = getattr(config_parser, _global_config_func[option])
self._cfg[option] = func(consts.MAGIC_HEADER_NAME, option)
except Error:
raise TunedException("Global TuneD configuration file '%s' is not valid."
% file_name)
else:
log.info("Unknown option '%s' in global config file '%s'." % (option, file_name))
self._cfg[option] = config_parser.get(consts.MAGIC_HEADER_NAME, option, raw=True)
except IOError as e: except IOError as e:
raise TunedException("Global TuneD configuration file '%s' not found." % file_name) raise TunedException("Global TuneD configuration file '%s' not found." % file_name)
except ConfigObjError as e: except Error as e:
raise TunedException("Error parsing global TuneD configuration file '%s'." % file_name) raise TunedException("Error parsing global TuneD configuration file '%s'." % file_name)
vdt = Validator()
if (not self._cfg.validate(vdt, copy=True)):
raise TunedException("Global TuneD configuration file '%s' is not valid." % file_name)
def get(self, key, default = None): def get(self, key, default = None):
return self._cfg.get(key, default) return self._cfg.get(key, default)

View file

@ -3,7 +3,11 @@ import re
import errno import errno
import procfs import procfs
import subprocess import subprocess
from configobj import ConfigObj, ConfigObjError try:
from configparser import ConfigParser, Error
except ImportError:
# python2.7 support, remove RHEL-7 support end
from ConfigParser import ConfigParser, Error
try: try:
import syspurpose.files import syspurpose.files
@ -59,11 +63,14 @@ class ProfileRecommender:
try: try:
if not os.path.isfile(fname): if not os.path.isfile(fname):
return None return None
config = ConfigObj(fname, list_values = False, interpolation = False) config = ConfigParser()
for section in list(config.keys()): config.optionxform = str
with open(fname) as f:
config.readfp(f)
for section in config.sections():
match = True match = True
for option in list(config[section].keys()): for option in config.options(section):
value = config[section][option] value = config.get(section, option, raw=True)
if value == "": if value == "":
value = r"^$" value = r"^$"
if option == "virt": if option == "virt":
@ -117,7 +124,7 @@ class ProfileRecommender:
r = re.compile(r",[^,]*$") r = re.compile(r",[^,]*$")
matching_profile = r.sub("", section) matching_profile = r.sub("", section)
break break
except (IOError, OSError, ConfigObjError) as e: except (IOError, OSError, Error) as e:
log.error("error processing '%s', %s" % (fname, e)) log.error("error processing '%s', %s" % (fname, e))
return matching_profile return matching_profile