blob: 09d26dd87029a2ea5e43ac55ec3f97da72bb27ee [file] [log] [blame]
import io
import os
import re
import fnmatch
import sys
import codecs
import configparser
from time import localtime, strftime
from subprocess import Popen, PIPE, STDOUT, call
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
TUNED_CONF = """[sysctl]\n"""
class Config:
# init config, load /etc/tuna.conf (if not exist, create it)
def __init__(self):
self.aliasList = []
self.aliasReverse = []
self.configFile = "/etc/tuna.conf"
try:
self.configParser = configparser.RawConfigParser()
self.configParser.read(self.configFile)
cfg = self.configParser.items('global')
except configparser.Error:
f = open(self.configFile, 'w')
f.write("[global]\n")
f.write("root=/etc/tuna/\n")
f.write("lastFile=\n")
f.close()
self.configParser.read(self.configFile)
cfg = self.configParser.items('global')
self.config = {}
for option, value in cfg:
self.config[option] = value
self.cacheFileName = ''
def FileNameToConfigPath(self, filename):
return filename.replace(".", "\\.").replace("/", ".")
def ConfigPathToFileName(self, configpath):
return configpath.replace(".", "/").replace("\\/", ".")
def updateDefault(self, filename):
if filename.replace("", "temp-direct-load.conf") != filename:
self.temp = configparser.RawConfigParser()
self.temp.read(self.configFile)
self.temp.set('global', 'lastFile', filename)
with open(self.configFile, 'w') as cfgfile:
self.temp.write(cfgfile)
self.config['lastfile'] = filename
def load(self, profileName):
tmp = configparser.RawConfigParser()
tmp.read(self.config['root'] + profileName)
try:
check = tmp.items('categories')
except configparser.NoSectionError:
if self.tuned2Tuna(profileName) < 0:
return -1
return self.loadTuna(profileName)
def tuned2Tuna(self, profileName):
try:
tmp = configparser.RawConfigParser()
tmp.read(self.config['root']+profileName)
content = tmp.items('sysctl')
f = open(self.config['root']+profileName, 'w')
f.write("[categories]\n")
f.write("sysctl=Tuned import\n")
f.write("[sysctl]\n")
for option, value in content:
f.write(option + "=" + value + "\n")
f.close()
return 0
except (configparser.Error, IOError):
dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR,
Gtk.ButtonsType.OK, "%s\n%s" %
(_("Corruputed config file: "), _(self.config['root']+profileName)))
ret = dialog.run()
dialog.destroy()
return -1
def checkTunedDaemon(self):
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
tFile = os.path.join(path, "tuned")
if os.path.isfile(tFile) and os.access(tFile, os.X_OK):
return True
return False
def currentActiveProfile(self):
proc = Popen(["tuned-adm", "active"], stdout=PIPE, stderr=PIPE)
ret = proc.communicate()
profile = ret[0]
if profile and profile.find("Current active profile: ") == 0:
return (profile[len("Current active profile: "):profile.find("\n")], ret[1])
return ("unknown", ret[1])
def setCurrentActiveProfile(self):
call("tuned-adm profile tuna", shell=True)
def saveTuned(self, data):
ldir = "/etc/tuned/tuna"
profile = self.currentActiveProfile()
if profile[1]:
raise RuntimeError(
_("Can't activate tuna profile in tuned daemon\n%s" % profile[1]))
# return False - unreachable code here!
if not os.path.exists(ldir):
try:
os.stat(ldir)
except (IOError, OSError):
os.mkdir(ldir)
f = codecs.open(os.path.join(ldir, "tuned.conf"), "w", "utf-8")
f.write(TUNED_CONF)
for index in data:
for ind in data[index]:
f.write(self.aliasToOriginal(
data[index][ind]["label"])+"="+data[index][ind]["value"]+"\n")
f.close()
if profile[0] != "tuna":
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, "%s%s\n%s" %
(_("Current active profile is: "),
_(profile[0]),
_("Set new created profile as current in tuned daemon?")))
ret = dialog.run()
dialog.destroy()
if ret == Gtk.ResponseType.YES:
self.setCurrentActiveProfile()
if self.currentActiveProfile()[0] != "tuna":
raise RuntimeError("%s %s\n%s" %
(_("Current active profile is: "),
_(profile),
_("Setting of new tuned profile failed! Check if tuned is installed and active")))
dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL
| Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
_("Tuna profile is now active in tuned daemon."))
ret = dialog.run()
dialog.destroy()
return True
def loadTuna(self, profileName):
print(f'profileName = {profileName}') # REMOVE
err = self.checkConfigFile(self.config['root'] + profileName)
if err != '':
raise RuntimeError(_("Config file contain errors: ") + _(err))
try:
self.configParser = configparser.RawConfigParser()
self.configParser.read(self.config['root'] + profileName)
tempCategories = self.configParser.items('categories')
self.catIndex = 0
self.categoriesOrigin = {}
self.categories = {}
self.ctlParams = {}
self.ctlGuiParams = {}
self.aliasList = []
self.aliasReverse = []
for option, value in tempCategories:
if value != "":
oldTempCfg = self.configParser.items(option)
self.ctlParams[self.catIndex] = {}
self.ctlGuiParams[self.catIndex] = {}
tempCfg = []
for index in range(len(oldTempCfg)):
if self.isFnString(oldTempCfg[index][0]):
expanded = self.getFilesByFN("/proc/sys",
self.ConfigPathToFileName(oldTempCfg[index][0]))
for index2 in range(len(expanded)):
expandedData = (self.FileNameToConfigPath(
expanded[index2]), oldTempCfg[index][1])
tempCfg.append(expandedData)
else:
tempCfg.append(oldTempCfg[index])
for opt, val in tempCfg:
if val.find(',') != -1 and val.find(',', val.find(',')) != -1 and len(val.split(",")) > 2:
self.ctlGuiParams[self.catIndex][opt] = val.split(
",")
val = self.ctlGuiParams[self.catIndex][opt][2]
sys = self.getSystemValue(opt)
if val == "" or val == sys:
self.ctlParams[self.catIndex][opt] = sys
else:
self.ctlParams[self.catIndex][opt] = val
if opt in self.ctlGuiParams[self.catIndex]:
if self.ctlGuiParams[self.catIndex][opt][0] == '':
self.ctlGuiParams[self.catIndex][opt][0] = int(
int(self.ctlParams[self.catIndex][opt])/10)
else:
self.ctlGuiParams[self.catIndex][opt][0] = int(
self.ctlGuiParams[self.catIndex][opt][0])
if self.ctlGuiParams[self.catIndex][opt][1] == '':
self.ctlGuiParams[self.catIndex][opt][1] = int(
int(self.ctlParams[self.catIndex][opt])*10)
else:
self.ctlGuiParams[self.catIndex][opt][1] = int(
self.ctlGuiParams[self.catIndex][opt][1])
self.categories[self.catIndex] = value
self.categoriesOrigin[self.catIndex] = option
self.catIndex = self.catIndex + 1
except (configparser.Error, IOError):
print(_("Config file is corrupted"))
return -1
try:
self.aliasList = self.configParser.items('guiAlias')
except configparser.Error:
self.aliasList = []
self.aliasReverse = []
return 0
def updateDescription(self, filename):
try:
self.temp = configparser.RawConfigParser()
self.temp.read(self.config['root'] + filename)
self.description = self.temp.items('fileDescription')
self.description = dict(self.description)['text']
except configparser.Error as e:
self.description = _("Description for this profile not found")
if e != configparser.NoSectionError:
print(e)
return self.description
def fileToCache(self, profileName):
try:
f = open(self.config['root'] + profileName, 'r')
except IOError:
pass
if f is None:
raise RuntimeError(_("Cant open this config file: %s" %
(self.config['root'] + profileName)))
self.cacheFileName = profileName
self.cache = f.read()
f.close()
self.updateDescription(profileName)
def cacheToFile(self, profileName):
try:
f = open(self.config['root'] + profileName, 'w')
f.write(self.cache)
f.close()
except IOError:
print(_("Cant write to config file: %s" %
(self.config['root'] + profileName)))
def loadDirect(self, data):
try:
f = open(self.config['root']+"temp-direct-load.conf", 'w')
except IOError:
raise RuntimeError(
_("Cant open this config file: %stemp-direct-load.conf" % (self.config['root'])))
f.write(data)
f.close()
ret = self.load("temp-direct-load.conf")
os.unlink(self.config['root']+"temp-direct-load.conf")
return ret
def populate(self):
return [files for files in os.listdir(self.config['root']) if files != "temp-direct-load.conf"]
def getSystemValue(self, filename):
filename = self.aliasToOriginal(filename)
try:
buffer = open(
"/proc/sys/" + self.ConfigPathToFileName(filename), 'r').read()
except IOError:
print(_("Invalid item! file: /proc/sys/%s" %
(self.ConfigPathToFileName(filename))))
return ""
return buffer.strip()
def setSystemValue(self, filename, value):
filename = self.aliasToOriginal(filename)
old = self.getSystemValue(filename)
if value == "" or old == value:
return 0
try:
fp = open("/proc/sys/" + self.ConfigPathToFileName(filename), 'w')
fp.write(value)
except IOError:
print("%s%s %s %s" % (_("Cant write to file! path: /proc/sys/"),
self.ConfigPathToFileName(filename), _("value:"), value))
return -1
return 0
def applyChanges(self, data):
for cat in data:
for itemId in data[cat]:
self.setSystemValue(
data[cat][itemId]['label'], data[cat][itemId]['value'])
self.reloadSystemValues(data)
def reloadSystemValues(self, data):
for cat in self.ctlParams:
for param in self.ctlParams[cat]:
sys = self.getSystemValue(param)
self.ctlParams[cat][param] = sys
def aliasToOriginal(self, string):
string = string.replace("*", "")
if string in dict(self.aliasReverse):
return dict(self.aliasReverse)[string]
return string
def originalToAlias(self, string):
tmpString = string
for src, dst in self.aliasList:
tmpString = tmpString.replace(src, dst)
if string != tmpString:
self.aliasReverse[len(self.aliasReverse):] = [
(tmpString, string)]
return tmpString
return string
def saveSnapshot(self, data):
tempconfig = configparser.RawConfigParser()
tempconfig.readfp(io.StringIO(self.cache))
snapcat = tempconfig.items('categories')
out = {}
for opt, val in snapcat:
for index in range(len(data[val])):
data[val][index]['label'] = self.aliasToOriginal(
data[val][index]['label'])
out[data[val][index]['label']] = data[val][index]['value']
for opt, val in snapcat:
snapcontPacked = tempconfig.items(opt)
snapcont = []
for index in range(len(snapcontPacked)):
if self.isFnString(snapcontPacked[index][0]):
expanded = self.getFilesByFN("/proc/sys",
self.ConfigPathToFileName(snapcontPacked[index][0]))
for index2 in range(len(expanded)):
expandedData = (self.FileNameToConfigPath(
expanded[index2]), snapcontPacked[index][1])
snapcont.append(expandedData)
else:
snapcont.append(snapcontPacked[index])
for iopt, ival in snapcont:
if ival == '':
tempconfig.set(opt, iopt, out[iopt])
elif ival == ',,':
tempconfig.set(opt, iopt, ',,' + out[iopt])
else:
reival = ival
pos = [reival.start()
for reival in re.finditer(',', reival)]
if len(pos) == 2:
ival = ival[0:pos[1]+1]
tempconfig.set(opt, iopt, ival + out[iopt])
else:
tempconfig.set(opt, iopt, out[iopt])
if 'lastfile' in self.config:
self.name = self.config['lastfile'].replace('.conf', '')
else:
self.name = 'snapshot'
snapFileName = self.config['root'] + self.name \
+ strftime("-%Y-%m-%d-%H:%M:%S", localtime()) + '.conf'
try:
with open(snapFileName, 'w') as configfile:
tempconfig.write(configfile)
except IOError:
print(_("Cant save snapshot"))
return snapFileName
def checkConfigFile(self, filename):
self.empty = True
try:
msgStack = ''
if not os.path.exists(filename):
msgStack = "%s%s %s %s" % (msgStack, _(
"Error: File"), filename, _("not found\n"))
return msgStack
self.checkParser = configparser.RawConfigParser()
self.checkParser.read(filename)
for option, value in self.checkParser.items('categories'):
if not self.checkParser.items(option):
msgStack = "%s%s %s\n" % (msgStack, _(
"Error: Enabled section is empty:"), option)
return msgStack
current = self.checkParser.items(option)
for opt, val in current:
if not os.path.exists("/proc/sys/" + self.ConfigPathToFileName(opt)) and len(self.getFilesByFN("/proc/sys/", self.ConfigPathToFileName(opt))) == 0:
msgStack = "%s%s%s\n" % (msgStack, _(
"Warning: File not found: /proc/sys/"), opt)
self.empty = False
if self.empty:
msgStack = "%s%s" % (msgStack, _("Empty config File"))
return msgStack
except (configparser.Error, IOError) as e:
return "Error {0}".format(str(e))
def fixConfigFile(self, filename):
try:
self.checkParser = configparser.RawConfigParser()
self.checkParser.read(filename)
for option, value in self.checkParser.items('categories'):
if not self.checkParser.items(option):
self.checkParser.remove_option('categories', option)
self.checkParser.set('categories', '#' + option, value)
current = self.checkParser.items(option)
for opt, val in current:
if not os.path.exists("/proc/sys/" + self.ConfigPathToFileName(opt)) and len(self.getFilesByFN("/proc/sys/", self.ConfigPathToFileName(opt))) == 0:
self.checkParser.remove_option(option, opt)
self.checkParser.set(option, '#' + opt, val)
except (configparser.Error, IOError) as e:
return "Error {0}".format(str(e))
with open(filename, 'w') as configfile:
self.checkParser.write(configfile)
def isFnString(self, string):
regMatch = ['[', '*', '?']
for char in regMatch:
if char in string:
return True
return False
def getFilesByFN(self, troot, fn):
mylist = {}
for root, dirs, files in os.walk(troot, topdown=True):
for cfile in files:
if fnmatch.fnmatch(root + "/" + cfile, "*" + fn):
mylist[len(mylist)] = root.replace(
troot, "")[1:] + "/" + cfile
return mylist