1
0
Fork 0

Replaced python-dmidecode with reading sysfs dmi and dmidecode tool.

Fixes #232
sysfs exports virtual dmi device structs which provides chassis type in numeric form, it should be available in all RHEL6+, so I used it as primary source (not much overhead).
If for some reasons it couldn't be read, dmidecode output will be parsed instead. Removed any references to python-dmidecode module.
chassis type is gathered only once, and only one intance of ProfileRecommender will be created (in daemon).

Signed-off-by: Michal Bajer <outSH@users.noreply.github.com>

Fix whitespace and other minor issues in solution for #232

Signed-off-by: Michal Bajer <outSH@users.noreply.github.com>
This commit is contained in:
Michal Bajer 2020-03-08 12:55:00 +01:00
parent 1d0dd6e5ae
commit d5825a51f4
6 changed files with 71 additions and 31 deletions

View file

@ -10,9 +10,4 @@ Python files are modified to use Python3. If you want tuned to use Python2
instead, set PYTHON to the full path of Python2. Example:
make PYTHON=/usr/bin/python2 install
tuned requires some Python packages in order to run.
Those packages are listed in 'requirements.txt'.
Use: 'pip3 install -r requirements.txt' (or 'pip install -r requirements.txt' for Python2) to install them.

View file

@ -1 +0,0 @@
dmidecode

View file

@ -70,14 +70,14 @@ Requires: %{_py}-schedutils, %{_py}-linux-procfs, %{_py}-perf
BuildRequires: python3-dbus, python3-gobject-base
Requires: python3-dbus, python3-gobject-base
%if 0%{?fedora} > 22 || 0%{?rhel} > 7
Recommends: python3-dmidecode
Recommends: dmidecode
%endif
%else
# BuildRequires for 'make test'
BuildRequires: dbus-python, pygobject3-base
Requires: dbus-python, pygobject3-base
%if 0%{?fedora} > 22 || 0%{?rhel} > 7
Recommends: python-dmidecode
Recommends: dmidecode
%endif
%endif
Requires: virt-what, ethtool, gawk, hdparm

View file

@ -5,7 +5,6 @@ from tuned.exceptions import TunedException
import threading
import tuned.consts as consts
from tuned.utils.commands import commands
from tuned.utils.profile_recommender import ProfileRecommender
__all__ = ["Controller"]
@ -257,7 +256,7 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
def recommend_profile(self, caller = None):
if caller == "":
return ""
return ProfileRecommender().recommend(hardcoded = not self._global_config.get_bool(consts.CFG_RECOMMEND_COMMAND, consts.CFG_DEF_RECOMMEND_COMMAND))
return self._daemon.profile_recommender.recommend()
@exports.export("", "b")
def verify_profile(self, caller = None):

View file

@ -40,6 +40,7 @@ class Daemon(object):
log.info("dynamic tuning is enabled (can be overridden by plugins)")
log.info("using update interval of %d second(s) (%d times of the sleep interval)" % (self._sleep_cycles * self._sleep_interval, self._sleep_cycles))
self._profile_recommender = ProfileRecommender(is_hardcoded = not self._recommend_command)
self._unit_manager = unit_manager
self._profile_loader = profile_loader
self._init_threads()
@ -167,6 +168,10 @@ class Daemon(object):
# the profile is not active, then the value is meaningless.
return self._post_loaded_profile if self._profile else None
@property
def profile_recommender(self):
return self._profile_recommender
@property
def profile_loader(self):
return self._profile_loader
@ -263,7 +268,7 @@ class Daemon(object):
def _get_recommended_profile(self):
log.info("Running in automatic mode, checking what profile is recommended for your configuration.")
profile = ProfileRecommender().recommend(hardcoded = not self._recommend_command)
profile = self._profile_recommender.recommend()
log.info("Using '%s' profile" % profile)
return profile

View file

@ -2,16 +2,9 @@ import os
import re
import errno
import procfs
import platform
import subprocess
from configobj import ConfigObj, ConfigObjError
have_dmidecode = False
try:
if (os.geteuid() == 0 and platform.machine() in ["i386", "i486", "i586", "i686", "x86_64"]):
import dmidecode
have_dmidecode = True
except:
pass
try:
import syspurpose.files
have_syspurpose = True
@ -26,13 +19,16 @@ log = tuned.logs.get()
class ProfileRecommender:
def __init__(self):
def __init__(self, is_hardcoded = False):
self._is_hardcoded = is_hardcoded
self._commands = commands()
self._chassis_type = None
def recommend(self, hardcoded = False):
def recommend(self):
profile = consts.DEFAULT_PROFILE
if hardcoded:
if self._is_hardcoded:
return profile
has_root = os.geteuid() == 0
if not has_root:
log.warning("Profile recommender is running without root privileges. Profiles with virt recommendation condition will be omitted.")
@ -93,17 +89,13 @@ class ProfileRecommender:
if len(ps.find_by_regex(re.compile(value))) == 0:
match = False
elif option == "chassis_type":
if have_dmidecode:
for chassis in dmidecode.chassis().values():
chassis_type = chassis["data"]["Type"].decode(
"ascii")
if re.match(value, chassis_type, re.IGNORECASE):
break
else:
chassis_type = self._get_chassis_type()
if chassis_type:
if not re.match(value, chassis_type, re.IGNORECASE):
match = False
else:
log.debug("Ignoring 'chassis_type' in '%s',\
dmidecode is not available." % fname)
log.debug("Ignoring 'chassis_type' in '%s', could not read DMI value." % fname)
elif option == "syspurpose_role":
if have_syspurpose:
s = syspurpose.files.SyspurposeStore(
@ -131,3 +123,53 @@ class ProfileRecommender:
except (IOError, OSError, ConfigObjError) as e:
log.error("error processing '%s', %s" % (fname, e))
return matching_profile
def _get_chassis_type(self):
if self._chassis_type is not None:
log.debug("returning cached chassis type '%s'" % self._chassis_type)
return self._chassis_type
# Check DMI sysfs first
# Based on SMBios 3.3.0 specs (https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.3.0.pdf)
DMI_CHASSIS_TYPES = ["", "Other", "Unknown", "Desktop", "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower",
"Portable", "Laptop", "Notebook", "Hand Held", "Docking Station", "All In One", "Sub Notebook",
"Space-saving", "Lunch Box", "Main Server Chassis", "Expansion Chassis", "Sub Chassis",
"Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis", "Rack Mount Chassis", "Sealed-case PC",
"Multi-system", "CompactPCI", "AdvancedTCA", "Blade", "Blade Enclosing", "Tablet",
"Convertible", "Detachable", "IoT Gateway", "Embedded PC", "Mini PC", "Stick PC"]
try:
with open('/sys/devices/virtual/dmi/id/chassis_type', 'r') as sysfs_chassis_type:
chassis_type_id = int(sysfs_chassis_type.read())
self._chassis_type = DMI_CHASSIS_TYPES[chassis_type_id]
except IndexError:
log.error("Unknown chassis type id read from dmi sysfs: %d" % chassis_type_id)
except (OSError, IOError) as e:
log.warn("error accessing dmi sysfs file: %s" % e)
if self._chassis_type:
log.debug("chassis type - %s" % self._chassis_type)
return self._chassis_type
# Fallback - try parsing dmidecode output
try:
p_dmi = subprocess.Popen(['dmidecode', '-s', 'chassis-type'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
close_fds=True)
(dmi_output, dmi_error) = p_dmi.communicate()
if p_dmi.returncode:
log.error("dmidecode finished with error (ret %d): '%s'" % (p_dmi.returncode, dmi_error))
else:
self._chassis_type = dmi_output.strip().decode()
except (OSError, IOError) as e:
log.warn("error executing dmidecode tool : %s" % e)
if not self._chassis_type:
log.debug("could not determine chassis type.")
self._chassis_type = ""
else:
log.debug("chassis type - %s" % self._chassis_type)
return self._chassis_type