Mini Shell
import os
import re
import errno
import procfs
import subprocess
from tuned.utils.config_parser import ConfigParser, Error
try:
import syspurpose.files
have_syspurpose = True
except:
have_syspurpose = False
import tuned.consts as consts
import tuned.logs
from tuned.utils.commands import commands
log = tuned.logs.get()
class ProfileRecommender:
def __init__(self, is_hardcoded = False):
self._is_hardcoded = is_hardcoded
self._commands = commands()
self._chassis_type = None
def recommend(self):
profile = consts.DEFAULT_PROFILE
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.")
matching = self.process_config(consts.RECOMMEND_CONF_FILE,
has_root=has_root)
if matching is not None:
return matching
files = {}
for directory in consts.RECOMMEND_DIRECTORIES:
contents = []
try:
contents = os.listdir(directory)
except OSError as e:
if e.errno != errno.ENOENT:
log.error("error accessing %s: %s" % (directory, e))
for name in contents:
path = os.path.join(directory, name)
files[name] = path
for name in sorted(files.keys()):
path = files[name]
matching = self.process_config(path, has_root=has_root)
if matching is not None:
return matching
return profile
def process_config(self, fname, has_root=True):
matching_profile = None
syspurpose_error_logged = False
try:
if not os.path.isfile(fname):
return None
config = ConfigParser(delimiters=('='), inline_comment_prefixes=('#'), strict=False)
config.optionxform = str
with open(fname) as f:
config.read_file(f, fname)
for section in config.sections():
match = True
for option in config.options(section):
value = config.get(section, option, raw=True)
if value == "":
value = r"^$"
if option == "virt":
if not has_root:
match = False
break
if not re.match(value,
self._commands.execute(["virt-what"])[1], re.S):
match = False
elif option == "system":
if not re.match(value,
self._commands.read_file(
consts.SYSTEM_RELEASE_FILE,
no_error = True), re.S):
match = False
elif option[0] == "/":
if not os.path.exists(option) or not re.match(value,
self._commands.read_file(option), re.S):
match = False
elif option[0:7] == "process":
ps = procfs.pidstats()
ps.reload_threads()
if len(ps.find_by_regex(re.compile(value))) == 0:
match = False
elif option == "chassis_type":
chassis_type = self._get_chassis_type()
if not re.match(value, chassis_type, re.IGNORECASE):
match = False
elif option == "syspurpose_role":
role = ""
if have_syspurpose:
s = syspurpose.files.SyspurposeStore(
syspurpose.files.USER_SYSPURPOSE,
raise_on_error = True)
try:
s.read_file()
role = s.contents["role"]
except (IOError, OSError, KeyError) as e:
if hasattr(e, "errno") and e.errno != errno.ENOENT:
log.error("Failed to load the syspurpose\
file: %s" % e)
else:
if not syspurpose_error_logged:
log.error("Failed to process 'syspurpose_role' in '%s'\
, the syspurpose module is not available" % fname)
syspurpose_error_logged = True
if re.match(value, role, re.IGNORECASE) is None:
match = False
if match:
# remove the ",.*" suffix
r = re.compile(r",[^,]*$")
matching_profile = r.sub("", section)
break
except (IOError, OSError, Error) 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
Zerion Mini Shell 1.0