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:
|
||||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue