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:
parent
1d0dd6e5ae
commit
d5825a51f4
6 changed files with 71 additions and 31 deletions
5
INSTALL
5
INSTALL
|
|
@ -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:
|
instead, set PYTHON to the full path of Python2. Example:
|
||||||
make PYTHON=/usr/bin/python2 install
|
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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
dmidecode
|
|
||||||
|
|
@ -70,14 +70,14 @@ Requires: %{_py}-schedutils, %{_py}-linux-procfs, %{_py}-perf
|
||||||
BuildRequires: python3-dbus, python3-gobject-base
|
BuildRequires: python3-dbus, python3-gobject-base
|
||||||
Requires: python3-dbus, python3-gobject-base
|
Requires: python3-dbus, python3-gobject-base
|
||||||
%if 0%{?fedora} > 22 || 0%{?rhel} > 7
|
%if 0%{?fedora} > 22 || 0%{?rhel} > 7
|
||||||
Recommends: python3-dmidecode
|
Recommends: dmidecode
|
||||||
%endif
|
%endif
|
||||||
%else
|
%else
|
||||||
# BuildRequires for 'make test'
|
# BuildRequires for 'make test'
|
||||||
BuildRequires: dbus-python, pygobject3-base
|
BuildRequires: dbus-python, pygobject3-base
|
||||||
Requires: dbus-python, pygobject3-base
|
Requires: dbus-python, pygobject3-base
|
||||||
%if 0%{?fedora} > 22 || 0%{?rhel} > 7
|
%if 0%{?fedora} > 22 || 0%{?rhel} > 7
|
||||||
Recommends: python-dmidecode
|
Recommends: dmidecode
|
||||||
%endif
|
%endif
|
||||||
%endif
|
%endif
|
||||||
Requires: virt-what, ethtool, gawk, hdparm
|
Requires: virt-what, ethtool, gawk, hdparm
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ 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
|
||||||
from tuned.utils.profile_recommender import ProfileRecommender
|
|
||||||
|
|
||||||
__all__ = ["Controller"]
|
__all__ = ["Controller"]
|
||||||
|
|
||||||
|
|
@ -257,7 +256,7 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
|
||||||
def recommend_profile(self, caller = None):
|
def recommend_profile(self, caller = None):
|
||||||
if caller == "":
|
if caller == "":
|
||||||
return ""
|
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")
|
@exports.export("", "b")
|
||||||
def verify_profile(self, caller = None):
|
def verify_profile(self, caller = None):
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class Daemon(object):
|
||||||
log.info("dynamic tuning is enabled (can be overridden by plugins)")
|
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))
|
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._unit_manager = unit_manager
|
||||||
self._profile_loader = profile_loader
|
self._profile_loader = profile_loader
|
||||||
self._init_threads()
|
self._init_threads()
|
||||||
|
|
@ -167,6 +168,10 @@ class Daemon(object):
|
||||||
# the profile is not active, then the value is meaningless.
|
# the profile is not active, then the value is meaningless.
|
||||||
return self._post_loaded_profile if self._profile else None
|
return self._post_loaded_profile if self._profile else None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def profile_recommender(self):
|
||||||
|
return self._profile_recommender
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def profile_loader(self):
|
def profile_loader(self):
|
||||||
return self._profile_loader
|
return self._profile_loader
|
||||||
|
|
@ -263,7 +268,7 @@ class Daemon(object):
|
||||||
|
|
||||||
def _get_recommended_profile(self):
|
def _get_recommended_profile(self):
|
||||||
log.info("Running in automatic mode, checking what profile is recommended for your configuration.")
|
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)
|
log.info("Using '%s' profile" % profile)
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,9 @@ import os
|
||||||
import re
|
import re
|
||||||
import errno
|
import errno
|
||||||
import procfs
|
import procfs
|
||||||
import platform
|
import subprocess
|
||||||
from configobj import ConfigObj, ConfigObjError
|
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:
|
try:
|
||||||
import syspurpose.files
|
import syspurpose.files
|
||||||
have_syspurpose = True
|
have_syspurpose = True
|
||||||
|
|
@ -26,13 +19,16 @@ log = tuned.logs.get()
|
||||||
|
|
||||||
class ProfileRecommender:
|
class ProfileRecommender:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, is_hardcoded = False):
|
||||||
|
self._is_hardcoded = is_hardcoded
|
||||||
self._commands = commands()
|
self._commands = commands()
|
||||||
|
self._chassis_type = None
|
||||||
|
|
||||||
def recommend(self, hardcoded = False):
|
def recommend(self):
|
||||||
profile = consts.DEFAULT_PROFILE
|
profile = consts.DEFAULT_PROFILE
|
||||||
if hardcoded:
|
if self._is_hardcoded:
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
has_root = os.geteuid() == 0
|
has_root = os.geteuid() == 0
|
||||||
if not has_root:
|
if not has_root:
|
||||||
log.warning("Profile recommender is running without root privileges. Profiles with virt recommendation condition will be omitted.")
|
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:
|
if len(ps.find_by_regex(re.compile(value))) == 0:
|
||||||
match = False
|
match = False
|
||||||
elif option == "chassis_type":
|
elif option == "chassis_type":
|
||||||
if have_dmidecode:
|
chassis_type = self._get_chassis_type()
|
||||||
for chassis in dmidecode.chassis().values():
|
|
||||||
chassis_type = chassis["data"]["Type"].decode(
|
if chassis_type:
|
||||||
"ascii")
|
if not re.match(value, chassis_type, re.IGNORECASE):
|
||||||
if re.match(value, chassis_type, re.IGNORECASE):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
match = False
|
match = False
|
||||||
else:
|
else:
|
||||||
log.debug("Ignoring 'chassis_type' in '%s',\
|
log.debug("Ignoring 'chassis_type' in '%s', could not read DMI value." % fname)
|
||||||
dmidecode is not available." % fname)
|
|
||||||
elif option == "syspurpose_role":
|
elif option == "syspurpose_role":
|
||||||
if have_syspurpose:
|
if have_syspurpose:
|
||||||
s = syspurpose.files.SyspurposeStore(
|
s = syspurpose.files.SyspurposeStore(
|
||||||
|
|
@ -131,3 +123,53 @@ class ProfileRecommender:
|
||||||
except (IOError, OSError, ConfigObjError) as e:
|
except (IOError, OSError, ConfigObjError) as e:
|
||||||
log.error("error processing '%s', %s" % (fname, e))
|
log.error("error processing '%s', %s" % (fname, e))
|
||||||
return matching_profile
|
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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue