1
0
Fork 0

plugins: support cpuinfo_regex and uname_regex matching

Also added cpuinfo_check builtin function.

It's possible to write now:

[variables]
cannonalake_cpuinfo=.*\bGenuineIntel\b.*
cannonlake_uname=x86_64

[cpu_cannonlake]
type=cpu
cpuinfo_regex=${cannonlake_cpuinfo}
uname_regex=${cannonlake_uname}
force_latency=C1

[disk_cannonlake]
type=disk
cpuinfo_regex=${cannonlake_cpuinfo}
uname_regex=${cannonlake_uname}
readahead=4092

...

And the tuning will be applied only on machines which have
'GenuineIntel' string in the /proc/cpuinfo and x86_64 architecture (from
uname). Both 'cpuinfo_regex' and 'uname_regex' are optional - if not
used it has the same effect as if matching regex is used. This example
used variables but it also works without it.

Also cpuinfo_check builtin function was added, so it's possible to write
the following now:

[main]
include=${f:cpuinfo_check:\bGenuineIntel\b:intel_profile:other_profile}

It tries to match the regex '\bGenuineIntel\b' in the /proc/cpuinfo
and if it matches it includes 'intel_profile', if not it includes
'other_profile'. If colon ':' needs to be used in the regex, it needs
to be escaped, i.e.: '\:'

It's even possible to combine it with the generic profiles, e.g.:
[main]
include=base_profile${f:cpuinfo_check:\bGenuineIntel\b:,intel_profile}

It will always include 'base_profile' and if the cpuinfo matches
GenuineIntel it also adds the intel_profile. More variants are possible,
the generic syntax is:

${f:cpuinfo_check:REGEX1:STR1:REGEX2:STR2:...[:FALLBACK_STR]}

It returns STR1 if REGEX1 matches, STR2 if REGEX2 matches, and
FALLBACK_STR if nothing matches. If there is no FALLBACK_STR it returns
empty string on no match. It exits on the first match found and
no more regexes are processed in such case

TODO:
Current limitation: Variables are not expanded in the include, so it's
not possible to write:

[main]
include=${f:cpuinfo_check:${regex}:test}

[variables]
regex=TEST

Resolves: rhbz#1748965

Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
This commit is contained in:
Jaroslav Škarvada 2019-11-22 14:56:27 +01:00
parent 3792d6320f
commit 8d9cd00387
No known key found for this signature in database
GPG key ID: D8E1C00E076E840B
5 changed files with 86 additions and 1 deletions

View file

@ -0,0 +1,32 @@
import re
import tuned.logs
from . import base
log = tuned.logs.get()
class cpuinfo_check(base.Function):
"""
Checks regexes against /proc/cpuinfo. Accepts arguments in the
following form: REGEX1, STR1, REGEX2, STR2, ...[, STR_FALLBACK]
If REGEX1 matches something in /proc/cpuinfo it expands to STR1,
if REGEX2 matches it expands to STR2. It stops on the first match,
i.e. if REGEX1 matches, no more regexes are processed. If none
regex matches it expands to STR_FALLBACK. If there is no fallback,
it expands to empty string.
"""
def __init__(self):
# unlimited number of arguments, min 2 arguments
super(cpuinfo_check, self).__init__("cpuinfo_check", 0, 2)
def execute(self, args):
if not super(cpuinfo_check, self).execute(args):
return None
cpuinfo = self._cmd.read_file("/proc/cpuinfo")
for i in range(0, len(args), 2):
if i + 1 < len(args):
if re.search(args[i], cpuinfo, re.MULTILINE):
return args[i + 1]
if len(args) % 2:
return args[-1]
else:
return ""

View file

@ -62,12 +62,18 @@ class Loader(object):
del(final_profile.units["variables"])
# FIXME hack, do all variable expansions in one place
self._expand_vars_in_devices(final_profile)
self._expand_vars_in_regexes(final_profile)
return final_profile
def _expand_vars_in_devices(self, profile):
for unit in profile.units:
profile.units[unit].devices = self._variables.expand(profile.units[unit].devices)
def _expand_vars_in_regexes(self, profile):
for unit in profile.units:
profile.units[unit].cpuinfo_regex = self._variables.expand(profile.units[unit].cpuinfo_regex)
profile.units[unit].uname_regex = self._variables.expand(profile.units[unit].uname_regex)
def _load_profile(self, profile_names, profiles, processed_files):
for name in profile_names:
filename = self._profile_locator.get_config(name, processed_files)

View file

@ -35,6 +35,10 @@ class Merger(object):
profile_a.units[unit_name].devices = unit.devices
if unit.devices_udev_regex is not None:
profile_a.units[unit_name].devices_udev_regex = unit.devices_udev_regex
if unit.cpuinfo_regex is not None:
profile_a.units[unit_name].cpuinfo_regex = unit.cpuinfo_regex
if unit.uname_regex is not None:
profile_a.units[unit_name].uname_regex = unit.uname_regex
if unit.script_pre is not None:
profile_a.units[unit_name].script_pre = unit.script_pre
if unit.script_post is not None:

View file

@ -6,7 +6,7 @@ class Unit(object):
"""
__slots__ = [ "_name", "_type", "_enabled", "_replace", "_devices", "_devices_udev_regex", \
"_script_pre", "_script_post", "_options" ]
"_cpuinfo_regex", "_uname_regex", "_script_pre", "_script_post", "_options" ]
def __init__(self, name, config):
self._name = name
@ -15,6 +15,8 @@ class Unit(object):
self._replace = config.pop("replace", False) in [True, "true", 1, "1"]
self._devices = config.pop("devices", "*")
self._devices_udev_regex = config.pop("devices_udev_regex", None)
self._cpuinfo_regex = config.pop("cpuinfo_regex", None)
self._uname_regex = config.pop("uname_regex", None)
self._script_pre = config.pop("script_pre", None)
self._script_post = config.pop("script_post", None)
self._options = collections.OrderedDict(config)
@ -59,6 +61,22 @@ class Unit(object):
def devices_udev_regex(self, value):
self._devices_udev_regex = value
@property
def cpuinfo_regex(self):
return self._cpuinfo_regex
@cpuinfo_regex.setter
def cpuinfo_regex(self, value):
self._cpuinfo_regex = value
@property
def uname_regex(self):
return self._uname_regex
@uname_regex.setter
def uname_regex(self, value):
self._uname_regex = value
@property
def script_pre(self):
return self._script_pre

View file

@ -1,9 +1,12 @@
import collections
import os
import re
import traceback
import tuned.exceptions
import tuned.logs
import tuned.plugins.exceptions
import tuned.consts as consts
from tuned.utils.commands import commands
log = tuned.logs.get()
@ -23,6 +26,7 @@ class Manager(object):
self._hardware_inventory = hardware_inventory
self._instances = []
self._plugins = []
self._cmd = commands()
@property
def plugins(self):
@ -36,12 +40,33 @@ class Manager(object):
def plugins_repository(self):
return self._plugins_repository
def _unit_matches_cpuinfo(self, unit):
if unit.cpuinfo_regex is None:
return True
return re.search(unit.cpuinfo_regex,
self._cmd.read_file("/proc/cpuinfo"),
re.MULTILINE) is not None
def _unit_matches_uname(self, unit):
if unit.uname_regex is None:
return True
return re.search(unit.uname_regex,
" ".join(os.uname()),
re.MULTILINE) is not None
def create(self, instances_config):
instance_info_list = []
for instance_name, instance_info in list(instances_config.items()):
if not instance_info.enabled:
log.debug("skipping disabled instance '%s'" % instance_name)
continue
if not self._unit_matches_cpuinfo(instance_info):
log.debug("skipping instance '%s', cpuinfo does not match" % instance_name)
continue
if not self._unit_matches_uname(instance_info):
log.debug("skipping instance '%s', uname does not match" % instance_name)
continue
instance_info.options.setdefault("priority", self._def_instance_priority)
instance_info.options["priority"] = int(instance_info.options["priority"])
instance_info_list.append(instance_info)