summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Oros <poros@redhat.com>2013-04-19 08:04:49 +0200
committerArnaldo Carvalho de Melo <acme@redhat.com>2013-05-22 16:55:37 +0200
commitc135a82bebe0c7ab4ebd621401f26d6b2ea3ac01 (patch)
tree103784c8d1d631f6fa5e7f0bc4fbe060a0c33a94
parentbd1be30c84bd625ba5d591a25a2392966540c67f (diff)
downloadtuna-c135a82bebe0c7ab4ebd621401f26d6b2ea3ac01.tar.gz
tuna: Added support for config files Added example config and default tuna config
Signed-off-by: Petr Oros <poros@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--MANIFEST1
-rw-r--r--etc/tuna.conf3
-rw-r--r--etc/tuna/example.conf54
-rw-r--r--po/POTFILES.in1
-rw-r--r--rpm/SPECS/tuna.spec5
-rw-r--r--tuna/config.py284
6 files changed, 348 insertions, 0 deletions
diff --git a/MANIFEST b/MANIFEST
index 06621ef..324742f 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -6,6 +6,7 @@ tuna/help.py
tuna/sysfs.py
tuna/oscilloscope.py
tuna/tuna.py
+tuna/config.py
tuna/tuna_gui.py
tuna/tuna_gui.glade
tuna/gui/__init__.py
diff --git a/etc/tuna.conf b/etc/tuna.conf
new file mode 100644
index 0000000..ad2eee3
--- /dev/null
+++ b/etc/tuna.conf
@@ -0,0 +1,3 @@
+[global]
+root = /etc/tuna/
+lastfile = example.conf
diff --git a/etc/tuna/example.conf b/etc/tuna/example.conf
new file mode 100644
index 0000000..7448a4e
--- /dev/null
+++ b/etc/tuna/example.conf
@@ -0,0 +1,54 @@
+#List of enabled categories
+[categories]
+kernel=Kernel scheduler
+vm=VM
+ipv4=Network IPv4
+ipv6=Network IPv6
+net=Network Core
+
+[kernel]
+kernel.sched_latency_ns=1000,50000000,
+kernel.sched_min_granularity_ns=,,
+kernel.sched_nr_migrate=0,128,
+kernel.sched_rt_period_us=,,
+kernel.sched_rt_runtime_us= 1000,2000000,
+kernel.sched_tunable_scaling=0,10,
+kernel.sched_wakeup_granularity_ns=1000,10000000,
+kernel.core_pattern =
+
+[vm]
+vm.dirty_ratio=0,100,
+vm.dirty_writeback_centisecs=,,
+vm.dirty_expire_centisecs=,,
+vm.laptop_mode=0,5,
+vm.swappiness =0,100,
+vm.memory_failure_early_kill = 0,1,0
+
+[net]
+net.core.rmem_default=100000,1000000,
+net.core.rmem_max=100000,1000000,
+net.core.wmem_default=100000,1000000,
+net.core.wmem_max=100000,1000000,
+
+[ipv4]
+net.ipv4.tcp_window_scaling=
+net.ipv4.conf.all.forwarding=0,1,
+net.ipv4.conf.all.rp_filter=
+net.ipv4.tcp_congestion_control=
+net.ipv4.tcp_max_syn_backlog=
+net.ipv4.tcp_mem=
+net.ipv4.tcp_slow_start_after_idle=0,1,
+net.ipv4.tcp_window_scaling=0,1,
+
+[ipv6]
+net.ipv6.conf.*.forwarding=0,1,
+
+#special section for gui alias
+[guiAlias]
+net.ipv4=ipv4
+net.ipv6=ipv6
+net.core=core
+
+#special section for this file description
+[fileDescription]
+text=This file contain some features for tunning kernel params.Mainly this is example file for demonstrate tuna new features. Params are set as default or most uses value
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 272affe..1e38f29 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,6 +5,7 @@ tuna/sysfs.py
tuna/oscilloscope.py
tuna/tuna.py
tuna/help.py
+tuna/config.py
tuna/gui/__init__.py
tuna/gui/util.py
tuna/gui/cpuview.py
diff --git a/rpm/SPECS/tuna.spec b/rpm/SPECS/tuna.spec
index 78322e2..109685f 100644
--- a/rpm/SPECS/tuna.spec
+++ b/rpm/SPECS/tuna.spec
@@ -52,12 +52,15 @@ priority is changed, be it using tuna or plain chrt & taskset.
%install
rm -rf %{buildroot}
%{__python} setup.py install --skip-build --root %{buildroot}
+mkdir -p %{buildroot}/%{_sysconfdir}/tuna/
mkdir -p %{buildroot}/{%{_bindir},%{_datadir}/tuna/help/kthreads,%{_mandir}/man8}
install -p -m644 tuna/tuna_gui.glade %{buildroot}/%{_datadir}/tuna/
install -p -m755 tuna-cmd.py %{buildroot}/%{_bindir}/tuna
install -p -m755 oscilloscope-cmd.py %{buildroot}/%{_bindir}/oscilloscope
install -p -m644 help/kthreads/* %{buildroot}/%{_datadir}/tuna/help/kthreads/
install -p -m644 docs/tuna.8 %{buildroot}/%{_mandir}/man8/
+install -p -m644 etc/tuna/example.conf %{buildroot}/%{_sysconfdir}/tuna/
+install -p -m644 etc/tuna.conf %{buildroot}/%{_sysconfdir}/
# l10n-ed message catalogues
for lng in `cat po/LINGUAS`; do
@@ -81,6 +84,8 @@ rm -rf %{buildroot}
%{_datadir}/tuna/
%{python_sitelib}/tuna/
%{_mandir}/man8/tuna.8*
+%{_sysconfdir}/tuna.conf
+%{_sysconfdir}/tuna/*
%files -n oscilloscope
%defattr(-,root,root,-)
diff --git a/tuna/config.py b/tuna/config.py
new file mode 100644
index 0000000..cbee248
--- /dev/null
+++ b/tuna/config.py
@@ -0,0 +1,284 @@
+import io, os, re, fnmatch
+import sys, gtk, pygtk
+import codecs, ConfigParser
+from time import localtime, strftime
+
+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 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, 'wb') as cfgfile:
+ self.temp.write(cfgfile)
+
+ 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 loadTuna(self, profileName):
+ err = self.checkConfigFile(self.config['root'] + profileName)
+ if err != '':
+ raise RuntimeError(_("Config file contain errors: ") + _(err))
+ return -1
+ 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", oldTempCfg[index][0].replace(".", "/"))
+ for index2 in range(len(expanded)):
+ expandedData = (expanded[index2].replace("/", "."), 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)))
+ return False
+ 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'])]
+
+ def getSystemValue(self, filename):
+ filename = self.aliasToOriginal(filename)
+ try:
+ buffer = open("/proc/sys/" + filename.replace(".", "/"), 'r').read()
+ except IOError:
+ print _("Invalid item! file: /proc/sys/%s" %(filename.replace(".", "/")))
+ 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/" + filename.replace(".", "/"), 'w')
+ fp.write(value)
+ except IOError:
+ print "%s%s %s %s" % (_("Cant write to file! path: /proc/sys/"), filename.replace(".","/"), _("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.BytesIO(self.cache))
+ snapcat = tempconfig.items('categories')
+ out = {}
+ cats = {}
+ 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",snapcontPacked[index][0].replace(".","/"))
+ for index2 in range(len(expanded)):
+ expandedData = (expanded[index2].replace("/","."),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])
+ snapFileName = self.config['root']+'snapshot' + 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):
+ 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/" + opt.replace(".","/")) and len(self.getFilesByFN("/proc/sys/",opt.replace(".","/"))) == 0:
+ msgStack = "%s%s%s\n" % (msgStack, _("Warning: File not found: /proc/sys/"), opt)
+ #pos = [val.start() for val in re.finditer(',', val)]
+ #if len(pos) > 0 and len(pos) != 2:
+ # msgStack = msgStack + "Error: slider value format is LOW,HIGH,CURRENT. Value: " + opt + "\n"
+ return msgStack
+ except (ConfigParser.Error, IOError) as e:
+ return "Error {0}".format(str(e))
+
+ 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