import tuned-2.16.0-3.el9
This commit is contained in:
commit
e2f0e915a5
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
SOURCES/tuned-2.16.0.tar.gz
|
1
.tuned.metadata
Normal file
1
.tuned.metadata
Normal file
@ -0,0 +1 @@
|
||||
e20fcfb734f869fb175cb88dc7ef6e5eb3cd5946 SOURCES/tuned-2.16.0.tar.gz
|
61
SOURCES/recommend.conf
Normal file
61
SOURCES/recommend.conf
Normal file
@ -0,0 +1,61 @@
|
||||
# Tuned rules for recommend_profile.
|
||||
#
|
||||
# Syntax:
|
||||
# [PROFILE1]
|
||||
# KEYWORD11=RE11
|
||||
# KEYWORD21=RE12
|
||||
#
|
||||
# [PROFILE2]
|
||||
# KEYWORD21=RE21
|
||||
# KEYWORD22=RE22
|
||||
|
||||
# KEYWORD can be:
|
||||
# virt - for RE to match output of virt-what
|
||||
# system - for RE to match content of /etc/system-release-cpe
|
||||
# process - for RE to match running processes. It can have arbitrary
|
||||
# suffix, all process* lines have to match for the PROFILE
|
||||
# to match (i.e. the AND operator)
|
||||
# /FILE - for RE to match content of the FILE, e.g.:
|
||||
# '/etc/passwd=.+'. If file doesn't exist, its RE will not
|
||||
# match.
|
||||
# chassis_type - for RE to match the chassis type as reported by dmidecode
|
||||
# syspurpose_role - for RE to match the system role as reported by syspurpose
|
||||
|
||||
# All REs for all KEYWORDs have to match for PROFILE to match (i.e. the AND operator).
|
||||
# If 'virt' or 'system' is not specified, it matches for every string.
|
||||
# If 'virt' or 'system' is empty, i.e. 'virt=', it matches only empty string (alias for '^$').
|
||||
# If several profiles matched, the first match is taken.
|
||||
#
|
||||
# Limitation:
|
||||
# Each profile can be specified only once, because there cannot be
|
||||
# multiple sections in the configuration file with the same name
|
||||
# (ConfigObj limitation).
|
||||
# If there is a need to specify the profile multiple times, unique
|
||||
# suffix like ',ANYSTRING' can be used. Everything after the last ','
|
||||
# is stripped by the parser, e.g.:
|
||||
#
|
||||
# [balanced,1]
|
||||
# /FILE1=RE1
|
||||
#
|
||||
# [balanced,2]
|
||||
# /FILE2=RE2
|
||||
#
|
||||
# This will set 'balanced' profile in case there is FILE1 matching RE1 or
|
||||
# FILE2 matching RE2 or both.
|
||||
|
||||
[atomic-host]
|
||||
virt=
|
||||
syspurpose_role=.*atomic.*
|
||||
|
||||
[atomic-guest]
|
||||
virt=.+
|
||||
syspurpose_role=.*atomic.*
|
||||
|
||||
[virtual-guest]
|
||||
virt=.+
|
||||
|
||||
[balanced]
|
||||
syspurpose_role=(.*(desktop|workstation).*)|^$
|
||||
chassis_type=.*(Notebook|Laptop|Portable).*
|
||||
|
||||
[throughput-performance]
|
829
SOURCES/tuned-2.16.0-configobj-drop.patch
Normal file
829
SOURCES/tuned-2.16.0-configobj-drop.patch
Normal file
@ -0,0 +1,829 @@
|
||||
From 063277a05b3a174f9265d36032ca097ee5b7cc9c Mon Sep 17 00:00:00 2001
|
||||
From: Jan Zerdik <jzerdik@redhat.com>
|
||||
Date: Fri, 30 Jul 2021 11:48:59 +0200
|
||||
Subject: [PATCH] Removing dependency on python-configobj.
|
||||
|
||||
Resolves: rhbz#1936386
|
||||
|
||||
Signed-off-by: Jan Zerdik <jzerdik@redhat.com>
|
||||
---
|
||||
recommend.conf | 2 +-
|
||||
tests/unit/profiles/test_loader.py | 7 +++
|
||||
tests/unit/profiles/test_locator.py | 18 ++++++-
|
||||
tests/unit/profiles/test_variables.py | 32 ++++++++++++
|
||||
tests/unit/utils/test_global_config.py | 13 ++++-
|
||||
tuned-gui.py | 5 +-
|
||||
tuned.spec | 3 +-
|
||||
tuned/consts.py | 21 ++++++++
|
||||
tuned/gtk/gui_plugin_loader.py | 43 +++++++++-------
|
||||
tuned/gtk/gui_profile_loader.py | 71 ++++++++++++++++++--------
|
||||
tuned/gtk/gui_profile_saver.py | 28 ++++++----
|
||||
tuned/profiles/loader.py | 38 ++++++--------
|
||||
tuned/profiles/locator.py | 25 ++++++---
|
||||
tuned/profiles/variables.py | 27 +++++-----
|
||||
tuned/utils/global_config.py | 55 +++++++++++++++-----
|
||||
tuned/utils/profile_recommender.py | 19 ++++---
|
||||
16 files changed, 288 insertions(+), 119 deletions(-)
|
||||
create mode 100644 tests/unit/profiles/test_variables.py
|
||||
|
||||
diff --git a/recommend.conf b/recommend.conf
|
||||
index f3442ca8..7561696c 100644
|
||||
--- a/recommend.conf
|
||||
+++ b/recommend.conf
|
||||
@@ -29,7 +29,7 @@
|
||||
# Limitation:
|
||||
# Each profile can be specified only once, because there cannot be
|
||||
# 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
|
||||
# suffix like ',ANYSTRING' can be used. Everything after the last ','
|
||||
# is stripped by the parser, e.g.:
|
||||
diff --git a/tests/unit/profiles/test_loader.py b/tests/unit/profiles/test_loader.py
|
||||
index b6ea76e9..149353d8 100644
|
||||
--- a/tests/unit/profiles/test_loader.py
|
||||
+++ b/tests/unit/profiles/test_loader.py
|
||||
@@ -46,6 +46,8 @@ def setUpClass(cls):
|
||||
f.write('file_path=${i:PROFILE_DIR}/whatever\n')
|
||||
f.write('script=random_name.sh\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):
|
||||
locator = profiles.Locator([self._profiles_dir])
|
||||
@@ -105,6 +107,11 @@ def test_load_config_data(self):
|
||||
self.assertEqual(config['test_unit']['test_option'],\
|
||||
'hello world')
|
||||
|
||||
+ def test_variables(self):
|
||||
+ config = self._loader.load(['dummy4'])
|
||||
+ self.assertEqual(config.units['test_unit'].devices,\
|
||||
+ '/dev/net,/dev/cpu')
|
||||
+
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
shutil.rmtree(cls._test_dir)
|
||||
diff --git a/tests/unit/profiles/test_locator.py b/tests/unit/profiles/test_locator.py
|
||||
index cce88daa..bf2906d7 100644
|
||||
--- a/tests/unit/profiles/test_locator.py
|
||||
+++ b/tests/unit/profiles/test_locator.py
|
||||
@@ -30,7 +30,10 @@ def _create_profile(cls, load_dir, profile_name):
|
||||
conf_name = os.path.join(profile_dir, "tuned.conf")
|
||||
os.mkdir(profile_dir)
|
||||
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):
|
||||
Locator([])
|
||||
@@ -65,3 +68,16 @@ def test_ignore_nonexistent_dirs(self):
|
||||
self.assertEqual(balanced, os.path.join(self._tmp_load_dirs[0], "balanced", "tuned.conf"))
|
||||
known = locator.get_known_names()
|
||||
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)
|
||||
diff --git a/tests/unit/profiles/test_variables.py b/tests/unit/profiles/test_variables.py
|
||||
new file mode 100644
|
||||
index 00000000..47fff2c1
|
||||
--- /dev/null
|
||||
+++ b/tests/unit/profiles/test_variables.py
|
||||
@@ -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)
|
||||
diff --git a/tests/unit/utils/test_global_config.py b/tests/unit/utils/test_global_config.py
|
||||
index 5b93888c..8981d544 100644
|
||||
--- a/tests/unit/utils/test_global_config.py
|
||||
+++ b/tests/unit/utils/test_global_config.py
|
||||
@@ -12,7 +12,8 @@ def setUpClass(cls):
|
||||
cls.test_dir = tempfile.mkdtemp()
|
||||
with open(cls.test_dir + '/test_config','w') as f:
|
||||
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.test_dir + '/test_config')
|
||||
@@ -28,10 +29,18 @@ def test_get_size(self):
|
||||
self.assertEqual(self._global_config.get_size('test_size'),\
|
||||
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'))
|
||||
|
||||
+ 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
|
||||
def tearDownClass(cls):
|
||||
shutil.rmtree(cls.test_dir)
|
||||
diff --git a/tuned-gui.py b/tuned-gui.py
|
||||
index a2792792..3953f82f 100755
|
||||
--- a/tuned-gui.py
|
||||
+++ b/tuned-gui.py
|
||||
@@ -48,7 +48,7 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
-import configobj
|
||||
+import collections
|
||||
import subprocess
|
||||
|
||||
import tuned.logs
|
||||
@@ -508,8 +508,7 @@ def on_click_button_confirm_profile_update(self, data):
|
||||
|
||||
def data_to_profile_config(self):
|
||||
name = self._gobj('entryProfileName').get_text()
|
||||
- config = configobj.ConfigObj(list_values = False,
|
||||
- interpolation = False)
|
||||
+ config = collections.OrderedDict()
|
||||
|
||||
activated = self._gobj('comboboxIncludeProfile').get_active()
|
||||
model = self._gobj('comboboxIncludeProfile').get_model()
|
||||
diff --git a/tuned.spec b/tuned.spec
|
||||
index e3a494fd..7afe1935 100644
|
||||
--- a/tuned.spec
|
||||
+++ b/tuned.spec
|
||||
@@ -66,9 +66,8 @@ BuildRequires: %{_py}, %{_py}-devel
|
||||
%if %{without python3} && ( ! 0%{?rhel} || 0%{?rhel} >= 8 )
|
||||
BuildRequires: %{_py}-mock
|
||||
%endif
|
||||
-BuildRequires: %{_py}-configobj
|
||||
BuildRequires: %{_py}-pyudev
|
||||
-Requires: %{_py}-pyudev, %{_py}-configobj
|
||||
+Requires: %{_py}-pyudev
|
||||
Requires: %{_py}-linux-procfs, %{_py}-perf
|
||||
%if %{without python3}
|
||||
Requires: %{_py}-schedutils
|
||||
diff --git a/tuned/consts.py b/tuned/consts.py
|
||||
index 58cbf4a3..8eb075ba 100644
|
||||
--- a/tuned/consts.py
|
||||
+++ b/tuned/consts.py
|
||||
@@ -16,6 +16,8 @@
|
||||
LOAD_DIRECTORIES = ["/usr/lib/tuned", "/etc/tuned"]
|
||||
PERSISTENT_STORAGE_DIR = "/var/lib/tuned"
|
||||
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"]
|
||||
|
||||
TMP_FILE_SUFFIX = ".tmp"
|
||||
@@ -79,6 +81,10 @@
|
||||
PREFIX_PROFILE_FACTORY = "System"
|
||||
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_DYNAMIC_TUNING = "dynamic_tuning"
|
||||
CFG_SLEEP_INTERVAL = "sleep_interval"
|
||||
@@ -87,25 +93,40 @@
|
||||
CFG_REAPPLY_SYSCTL = "reapply_sysctl"
|
||||
CFG_DEFAULT_INSTANCE_PRIORITY = "default_instance_priority"
|
||||
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_CPUINFO_STRING = "cpuinfo_string"
|
||||
|
||||
# no_daemon mode
|
||||
CFG_DEF_DAEMON = True
|
||||
+CFG_FUNC_DAEMON = "getboolean"
|
||||
# default configuration
|
||||
CFG_DEF_DYNAMIC_TUNING = True
|
||||
+CFG_FUNC_DYNAMIC_TUNING = "getboolean"
|
||||
# how long to sleep before checking for events (in seconds)
|
||||
CFG_DEF_SLEEP_INTERVAL = 1
|
||||
+CFG_FUNC_SLEEP_INTERVAL = "getint"
|
||||
# update interval for dynamic tuning (in seconds)
|
||||
CFG_DEF_UPDATE_INTERVAL = 10
|
||||
+CFG_FUNC_UPDATE_INTERVAL = "getint"
|
||||
# recommend command availability
|
||||
CFG_DEF_RECOMMEND_COMMAND = True
|
||||
+CFG_FUNC_RECOMMEND_COMMAND = "getboolean"
|
||||
# reapply system sysctl
|
||||
CFG_DEF_REAPPLY_SYSCTL = True
|
||||
+CFG_FUNC_REAPPLY_SYSCTL = "getboolean"
|
||||
# default instance priority
|
||||
CFG_DEF_DEFAULT_INSTANCE_PRIORITY = 0
|
||||
+CFG_FUNC_DEFAULT_INSTANCE_PRIORITY = "getint"
|
||||
# default pyudev.Monitor buffer size
|
||||
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"
|
||||
|
||||
diff --git a/tuned/gtk/gui_plugin_loader.py b/tuned/gtk/gui_plugin_loader.py
|
||||
index d364602d..f943a220 100644
|
||||
--- a/tuned/gtk/gui_plugin_loader.py
|
||||
+++ b/tuned/gtk/gui_plugin_loader.py
|
||||
@@ -25,25 +25,23 @@
|
||||
'''
|
||||
|
||||
import importlib
|
||||
-from validate import Validator
|
||||
|
||||
import tuned.consts as consts
|
||||
import tuned.logs
|
||||
-
|
||||
-import configobj as 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
|
||||
from tuned.exceptions import TunedException
|
||||
+from tuned.utils.global_config import GlobalConfig
|
||||
|
||||
from tuned.admin.dbus_controller import DBusController
|
||||
|
||||
__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():
|
||||
|
||||
@@ -84,19 +82,26 @@ def _load_global_config(self, file_name=consts.GLOBAL_CONFIG_FILE):
|
||||
"""
|
||||
|
||||
try:
|
||||
- config = ConfigObj.ConfigObj(file_name,
|
||||
- configspec=global_config_spec,
|
||||
- raise_errors = True, file_error = True, list_values = False, interpolation = False)
|
||||
+ config_parser = ConfigParser()
|
||||
+ config_parser.optionxform = str
|
||||
+ 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:
|
||||
raise TunedException("Global TuneD configuration file '%s' not found."
|
||||
% file_name)
|
||||
- except ConfigObj.ConfigObjError as e:
|
||||
+ except Error as e:
|
||||
raise TunedException("Error parsing global TuneD configuration file '%s'."
|
||||
% 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
|
||||
|
||||
-
|
||||
diff --git a/tuned/gtk/gui_profile_loader.py b/tuned/gtk/gui_profile_loader.py
|
||||
index c50dd9ff..dcd16b72 100644
|
||||
--- a/tuned/gtk/gui_profile_loader.py
|
||||
+++ b/tuned/gtk/gui_profile_loader.py
|
||||
@@ -25,10 +25,17 @@
|
||||
'''
|
||||
|
||||
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 json
|
||||
import sys
|
||||
+import collections
|
||||
|
||||
import tuned.profiles.profile as p
|
||||
import tuned.consts
|
||||
@@ -59,14 +66,21 @@ def set_raw_profile(self, profile_name, config):
|
||||
|
||||
profilePath = self._locate_profile_path(profile_name)
|
||||
|
||||
- config_lines = config.split('\n')
|
||||
-
|
||||
if profilePath == tuned.consts.LOAD_DIRECTORIES[1]:
|
||||
file_path = profilePath + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
|
||||
-
|
||||
- config_obj = configobj.ConfigObj(infile=config_lines,list_values = False, interpolation = False)
|
||||
- config_obj.filename = file_path
|
||||
- config_obj.initial_comment = ('#', 'tuned configuration', '#')
|
||||
+ config_parser = ConfigParser()
|
||||
+ config_parser.optionxform = str
|
||||
+ config_parser.readfp(StringIO(config))
|
||||
+
|
||||
+ config_obj = {
|
||||
+ 'main': collections.OrderedDict(),
|
||||
+ '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._refresh_profiles()
|
||||
else:
|
||||
@@ -76,8 +90,15 @@ def set_raw_profile(self, profile_name, config):
|
||||
|
||||
def load_profile_config(self, profile_name, path):
|
||||
conf_path = path + '/' + profile_name + '/' + tuned.consts.PROFILE_FILE
|
||||
- profile_config = configobj.ConfigObj(conf_path, list_values = False,
|
||||
- interpolation = False)
|
||||
+ config = ConfigParser()
|
||||
+ 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
|
||||
|
||||
def _locate_profile_path(self, profile_name):
|
||||
@@ -95,11 +116,11 @@ def _load_all_profiles(self):
|
||||
try:
|
||||
self.profiles[profile] = p.Profile(profile,
|
||||
self.load_profile_config(profile, d))
|
||||
- except configobj.ParseError:
|
||||
+ except Error:
|
||||
pass
|
||||
|
||||
# print "can not make \""+ profile +"\" profile without correct config on path: " + d
|
||||
-# except:
|
||||
+# except:StringIO
|
||||
# raise managerException.ManagerException("Can not make profile")
|
||||
# print "can not make \""+ profile +"\" profile without correct config with path: " + d
|
||||
|
||||
@@ -113,20 +134,24 @@ def _refresh_profiles(self):
|
||||
|
||||
def save_profile(self, profile):
|
||||
path = tuned.consts.LOAD_DIRECTORIES[1] + '/' + profile.name
|
||||
- config = configobj.ConfigObj(list_values = False, interpolation = False)
|
||||
- config.filename = path + '/' + tuned.consts.PROFILE_FILE
|
||||
- config.initial_comment = ('#', 'tuned configuration', '#')
|
||||
+ config = {
|
||||
+ 'main': collections.OrderedDict(),
|
||||
+ 'filename': path + '/' + tuned.consts.PROFILE_FILE,
|
||||
+ 'initial_comment': ('#', 'tuned configuration', '#')
|
||||
+ }
|
||||
+ config['filename'] = path + '/' + tuned.consts.PROFILE_FILE
|
||||
+ config['initial_comment'] = ('#', 'tuned configuration', '#')
|
||||
|
||||
try:
|
||||
- config['main'] = profile.options
|
||||
+ config['main']['main'] = profile.options
|
||||
except KeyError:
|
||||
- config['main'] = ''
|
||||
+ config['main']['main'] = {}
|
||||
|
||||
# profile dont have main section
|
||||
|
||||
pass
|
||||
for (name, unit) in list(profile.units.items()):
|
||||
- config[name] = unit.options
|
||||
+ config['main'][name] = unit.options
|
||||
|
||||
self._save_profile(config)
|
||||
|
||||
@@ -148,18 +173,20 @@ def update_profile(
|
||||
if old_profile_name != profile.name:
|
||||
self.remove_profile(old_profile_name, is_admin=is_admin)
|
||||
|
||||
- config = configobj.ConfigObj(list_values = False, interpolation = False)
|
||||
- config.filename = path + '/' + tuned.consts.PROFILE_FILE
|
||||
- config.initial_comment = ('#', 'tuned configuration', '#')
|
||||
+ config = {
|
||||
+ 'main': collections.OrderedDict(),
|
||||
+ 'filename': path + '/' + tuned.consts.PROFILE_FILE,
|
||||
+ 'initial_comment': ('#', 'tuned configuration', '#')
|
||||
+ }
|
||||
try:
|
||||
- config['main'] = profile.options
|
||||
+ config['main']['main'] = profile.options
|
||||
except KeyError:
|
||||
|
||||
# profile dont have main section
|
||||
|
||||
pass
|
||||
for (name, unit) in list(profile.units.items()):
|
||||
- config[name] = unit.options
|
||||
+ config['main'][name] = unit.options
|
||||
|
||||
self._save_profile(config)
|
||||
|
||||
diff --git a/tuned/gtk/gui_profile_saver.py b/tuned/gtk/gui_profile_saver.py
|
||||
index b339cba1..24b0fe3a 100644
|
||||
--- a/tuned/gtk/gui_profile_saver.py
|
||||
+++ b/tuned/gtk/gui_profile_saver.py
|
||||
@@ -1,7 +1,11 @@
|
||||
import os
|
||||
import sys
|
||||
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__":
|
||||
@@ -11,13 +15,19 @@
|
||||
if not os.path.exists(profile_dict['filename']):
|
||||
os.makedirs(os.path.dirname(profile_dict['filename']))
|
||||
|
||||
- profile_configobj = ConfigObj()
|
||||
- for section in profile_dict['sections']:
|
||||
- profile_configobj[section] = profile_dict['main'][section]
|
||||
-
|
||||
- profile_configobj.filename = os.path.join('/etc','tuned',os.path.dirname(os.path.abspath(profile_dict['filename'])),'tuned.conf')
|
||||
- profile_configobj.initial_comment = profile_dict['initial_comment']
|
||||
-
|
||||
- profile_configobj.write()
|
||||
+ profile_configobj = ConfigParser()
|
||||
+ profile_configobj.optionxform = str
|
||||
+ 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)
|
||||
+
|
||||
+ path = os.path.join('/etc','tuned',os.path.dirname(os.path.abspath(profile_dict['filename'])),'tuned.conf')
|
||||
+ with open(path, 'w') as f:
|
||||
+ profile_configobj.write(f)
|
||||
+ 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)
|
||||
diff --git a/tuned/profiles/loader.py b/tuned/profiles/loader.py
|
||||
index 7f132b4f..31037182 100644
|
||||
--- a/tuned/profiles/loader.py
|
||||
+++ b/tuned/profiles/loader.py
|
||||
@@ -1,6 +1,10 @@
|
||||
import tuned.profiles.profile
|
||||
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 os.path
|
||||
import collections
|
||||
@@ -96,30 +100,22 @@ def _expand_profile_dir(self, profile_dir, string):
|
||||
|
||||
def _load_config_data(self, file_name):
|
||||
try:
|
||||
- config_obj = ConfigObj(file_name, raise_errors = True, list_values = False, interpolation = False)
|
||||
- except ConfigObjError as e:
|
||||
+ config_obj = ConfigParser()
|
||||
+ 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)
|
||||
|
||||
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)
|
||||
- # TODO: Could we do this in the same place as the expansion of other functions?
|
||||
- for section in config:
|
||||
- for option in config[section]:
|
||||
+ for section in list(config_obj.sections()):
|
||||
+ config[section] = collections.OrderedDict()
|
||||
+ 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])
|
||||
-
|
||||
- # TODO: HACK, this needs to be solved in a better way (better config parser)
|
||||
- for unit_name in config:
|
||||
- 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)]
|
||||
+ if config[section].get("script") is not None:
|
||||
+ script_path = os.path.join(dir_name, config[section]["script"])
|
||||
+ config[section]["script"] = [os.path.normpath(script_path)]
|
||||
|
||||
return config
|
||||
diff --git a/tuned/profiles/locator.py b/tuned/profiles/locator.py
|
||||
index 3fd46916..994bdfb5 100644
|
||||
--- a/tuned/profiles/locator.py
|
||||
+++ b/tuned/profiles/locator.py
|
||||
@@ -1,6 +1,12 @@
|
||||
import os
|
||||
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):
|
||||
"""
|
||||
@@ -48,8 +54,12 @@ def parse_config(self, profile_name):
|
||||
if config_file is None:
|
||||
return None
|
||||
try:
|
||||
- return ConfigObj(config_file, list_values = False, interpolation = False)
|
||||
- except (IOError, OSError, ConfigObjError) as e:
|
||||
+ config = ConfigParser()
|
||||
+ 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
|
||||
|
||||
# Get profile attributes (e.g. summary, description), attrs is list of requested attributes,
|
||||
@@ -75,17 +85,16 @@ def get_profile_attrs(self, profile_name, attrs, defvals = None):
|
||||
config = self.parse_config(profile_name)
|
||||
if config is None:
|
||||
return [False, "", "", ""]
|
||||
- if consts.PLUGIN_MAIN_UNIT_NAME in config:
|
||||
- d = config[consts.PLUGIN_MAIN_UNIT_NAME]
|
||||
- else:
|
||||
- d = dict()
|
||||
+ main_unit_in_config = consts.PLUGIN_MAIN_UNIT_NAME in config.sections()
|
||||
vals = [True, profile_name]
|
||||
for (attr, defval) in zip(attrs, defvals):
|
||||
if attr == "" or attr is None:
|
||||
vals[0] = False
|
||||
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:
|
||||
- vals = vals + [d.get(attr, defval)]
|
||||
+ vals = vals + [defval]
|
||||
return vals
|
||||
|
||||
def list_profiles(self):
|
||||
diff --git a/tuned/profiles/variables.py b/tuned/profiles/variables.py
|
||||
index 2e101661..a9e27aea 100644
|
||||
--- a/tuned/profiles/variables.py
|
||||
+++ b/tuned/profiles/variables.py
|
||||
@@ -4,7 +4,13 @@
|
||||
from .functions import functions as functions
|
||||
import tuned.consts as consts
|
||||
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()
|
||||
|
||||
@@ -40,24 +46,21 @@ def add_variable(self, variable, value):
|
||||
self._lookup_re[r'(?<!\\)\${' + re.escape(s) + r'}'] = 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):
|
||||
if not os.path.exists(filename):
|
||||
log.error("unable to find variables_file: '%s'" % filename)
|
||||
return
|
||||
try:
|
||||
- config = ConfigObj(filename, raise_errors = True, file_error = True, list_values = False, interpolation = False)
|
||||
- except ConfigObjError:
|
||||
+ config = ConfigParser()
|
||||
+ 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)
|
||||
return
|
||||
- for item in config:
|
||||
- if isinstance(config[item], dict):
|
||||
- self.add_dict(config[item])
|
||||
- else:
|
||||
- self.add_variable(item, config[item])
|
||||
+ for s in config.sections():
|
||||
+ for o in config.options(s):
|
||||
+ self.add_variable(o, config.get(s, o, raw=True))
|
||||
|
||||
def add_from_cfg(self, cfg):
|
||||
for item in cfg:
|
||||
diff --git a/tuned/utils/global_config.py b/tuned/utils/global_config.py
|
||||
index 039dc9a4..f342700f 100644
|
||||
--- a/tuned/utils/global_config.py
|
||||
+++ b/tuned/utils/global_config.py
|
||||
@@ -1,6 +1,11 @@
|
||||
import tuned.logs
|
||||
-from configobj import ConfigObj, ConfigObjError
|
||||
-from validate import Validator
|
||||
+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
|
||||
from tuned.exceptions import TunedException
|
||||
import tuned.consts as consts
|
||||
from tuned.utils.commands import commands
|
||||
@@ -11,31 +16,55 @@
|
||||
|
||||
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):
|
||||
self._cfg = {}
|
||||
self.load_config(file_name=config_file)
|
||||
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):
|
||||
"""
|
||||
Loads global configuration file.
|
||||
"""
|
||||
log.debug("reading and parsing global configuration file '%s'" % file_name)
|
||||
try:
|
||||
- self._cfg = ConfigObj(file_name, configspec = self.global_config_spec, raise_errors = True, \
|
||||
- file_error = True, list_values = False, interpolation = False)
|
||||
+ config_parser = ConfigParser()
|
||||
+ 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:
|
||||
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)
|
||||
- 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):
|
||||
return self._cfg.get(key, default)
|
||||
diff --git a/tuned/utils/profile_recommender.py b/tuned/utils/profile_recommender.py
|
||||
index 580465bb..7300277b 100644
|
||||
--- a/tuned/utils/profile_recommender.py
|
||||
+++ b/tuned/utils/profile_recommender.py
|
||||
@@ -3,7 +3,11 @@
|
||||
import errno
|
||||
import procfs
|
||||
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:
|
||||
import syspurpose.files
|
||||
@@ -59,11 +63,14 @@ def process_config(self, fname, has_root=True):
|
||||
try:
|
||||
if not os.path.isfile(fname):
|
||||
return None
|
||||
- config = ConfigObj(fname, list_values = False, interpolation = False)
|
||||
- for section in list(config.keys()):
|
||||
+ config = ConfigParser()
|
||||
+ config.optionxform = str
|
||||
+ with open(fname) as f:
|
||||
+ config.readfp(f)
|
||||
+ for section in config.sections():
|
||||
match = True
|
||||
- for option in list(config[section].keys()):
|
||||
- value = config[section][option]
|
||||
+ for option in config.options(section):
|
||||
+ value = config.get(section, option, raw=True)
|
||||
if value == "":
|
||||
value = r"^$"
|
||||
if option == "virt":
|
||||
@@ -117,7 +124,7 @@ def process_config(self, fname, has_root=True):
|
||||
r = re.compile(r",[^,]*$")
|
||||
matching_profile = r.sub("", section)
|
||||
break
|
||||
- except (IOError, OSError, ConfigObjError) as e:
|
||||
+ except (IOError, OSError, Error) as e:
|
||||
log.error("error processing '%s', %s" % (fname, e))
|
||||
return matching_profile
|
||||
|
121
SOURCES/tuned-2.16.0-scheduler-cgroups-exclude.patch
Normal file
121
SOURCES/tuned-2.16.0-scheduler-cgroups-exclude.patch
Normal file
@ -0,0 +1,121 @@
|
||||
From 438ff4f899f5eb4bc2ea679fdd2d3611f8e0d8ea Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= <jskarvad@redhat.com>
|
||||
Date: Thu, 15 Jul 2021 20:48:54 +0200
|
||||
Subject: [PATCH] scheduler: new option cgroup_ps_blacklist
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This option allows skipping processes belonging to the blacklisted
|
||||
cgroups. It matches the regular expression against items from the
|
||||
/proc/PID/cgroups. Items/lines from the /proc/PID/cgroups are separated
|
||||
by commas ','. Each item consists of the:
|
||||
hierarchy-ID:controller-list:cgroup-path
|
||||
|
||||
Example of the content on which the regular expression is run:
|
||||
10:hugetlb:/,9:perf_event:/,8:blkio:/
|
||||
|
||||
For cgroups v2 the hierarchy-ID is 0 and the controller-list is ''.
|
||||
For details see man cgroups.7. The only difference from the man
|
||||
cgroups.7 is that it uses commas for separation of the items instead
|
||||
of the new lines. The commas are added by the python-linux-procfs
|
||||
(it's the behavior of the python-linux-procfs-0.6.3).
|
||||
|
||||
Multiple regular expressions can be separated by the semicolon ';'.
|
||||
|
||||
Examples:
|
||||
[scheduler]
|
||||
isolated_cores=1
|
||||
cgroup_ps_blacklist=:/daemons\b
|
||||
|
||||
It will move all processes away from the core 1 except processes which
|
||||
belongs to the cgroup '/daemons'. The '\b' is regular expression
|
||||
metacharacter that matches word boundary (i.e. it matches only
|
||||
'/daemons', not e.g. '/daemonset' or '/group/daemons'). In this example
|
||||
we do not care about the hierarchy-ID and the controller-list.
|
||||
|
||||
[scheduler]
|
||||
isolated_cores=1
|
||||
cgroup_ps_blacklist=\b8:blkio:/,|$
|
||||
|
||||
In this example it skips processes belonging to the cgroup '/',
|
||||
with hierarchy-ID 8 and controller-list blkio. The ',|$' is needed
|
||||
because the '\b' matches word boundary and the non-alphanumeric
|
||||
character '/' is not taken as a word, thus the '\b' will not match there.
|
||||
|
||||
[scheduler]
|
||||
isolated_cores=1
|
||||
cgroup_ps_blacklist=:/daemons\b;:/test\b
|
||||
|
||||
In this example two regular expressions are used which tries to match
|
||||
'/daemons' and '/test' cgroup-path. If either matches (i.e. the OR operator),
|
||||
the process is skipped (i.e. not moved away from the core 1).
|
||||
|
||||
Resolves: rhbz#1980715
|
||||
|
||||
Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
|
||||
---
|
||||
tuned/plugins/plugin_scheduler.py | 19 +++++++++++++++++++
|
||||
1 file changed, 19 insertions(+)
|
||||
|
||||
diff --git a/tuned/plugins/plugin_scheduler.py b/tuned/plugins/plugin_scheduler.py
|
||||
index e2f7ca2..8e77417 100644
|
||||
--- a/tuned/plugins/plugin_scheduler.py
|
||||
+++ b/tuned/plugins/plugin_scheduler.py
|
||||
@@ -156,6 +156,7 @@ class SchedulerPlugin(base.Plugin):
|
||||
# default is to whitelist all and blacklist none
|
||||
self._ps_whitelist = ".*"
|
||||
self._ps_blacklist = ""
|
||||
+ self._cgroup_ps_blacklist_re = ""
|
||||
self._cpus = perf.cpu_map()
|
||||
self._scheduler_storage_key = self._storage_key(
|
||||
command_name = "scheduler")
|
||||
@@ -251,6 +252,7 @@ class SchedulerPlugin(base.Plugin):
|
||||
"cgroup_mount_point_init": False,
|
||||
"cgroup_groups_init": True,
|
||||
"cgroup_for_isolated_cores": None,
|
||||
+ "cgroup_ps_blacklist": None,
|
||||
"ps_whitelist": None,
|
||||
"ps_blacklist": None,
|
||||
"default_irq_smp_affinity": "calc",
|
||||
@@ -811,6 +813,14 @@ class SchedulerPlugin(base.Plugin):
|
||||
elif event.type == perf.RECORD_EXIT:
|
||||
self._remove_pid(instance, int(event.tid))
|
||||
|
||||
+ @command_custom("cgroup_ps_blacklist", per_device = False)
|
||||
+ def _cgroup_ps_blacklist(self, enabling, value, verify, ignore_missing):
|
||||
+ # currently unsupported
|
||||
+ if verify:
|
||||
+ return None
|
||||
+ if enabling and value is not None:
|
||||
+ self._cgroup_ps_blacklist_re = "|".join(["(%s)" % v for v in re.split(r"(?<!\\);", str(value))])
|
||||
+
|
||||
@command_custom("ps_whitelist", per_device = False)
|
||||
def _ps_whitelist(self, enabling, value, verify, ignore_missing):
|
||||
# currently unsupported
|
||||
@@ -886,6 +896,9 @@ class SchedulerPlugin(base.Plugin):
|
||||
if self._ps_blacklist != "":
|
||||
psl = [v for v in psl if re.search(self._ps_blacklist,
|
||||
self._get_stat_comm(v)) is None]
|
||||
+ if self._cgroup_ps_blacklist_re != "":
|
||||
+ psl = [v for v in psl if re.search(self._cgroup_ps_blacklist_re,
|
||||
+ self._get_stat_cgroup(v)) is None]
|
||||
psd = dict([(v.pid, v) for v in psl])
|
||||
for pid in psd:
|
||||
try:
|
||||
@@ -911,6 +924,12 @@ class SchedulerPlugin(base.Plugin):
|
||||
psd[pid]["threads"].values(),
|
||||
affinity, True)
|
||||
|
||||
+ def _get_stat_cgroup(self, o):
|
||||
+ try:
|
||||
+ return o["cgroups"]
|
||||
+ except (OSError, IOError, KeyError):
|
||||
+ return ""
|
||||
+
|
||||
def _get_stat_comm(self, o):
|
||||
try:
|
||||
return o["stat"]["comm"]
|
||||
--
|
||||
2.31.1
|
||||
|
1372
SPECS/tuned.spec
Normal file
1372
SPECS/tuned.spec
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user