Commit 34a9bc42 authored by Jaroslav Škarvada's avatar Jaroslav Škarvada

Added tuned-gui

Main implementation was done by Marek Staňa.
Signed-off-by: default avatarJaroslav Škarvada <jskarvad@redhat.com>
parent 99381f22
......@@ -17,7 +17,7 @@ archive: clean
cp tuned.py tuned.spec tuned.service tuned.tmpfiles Makefile tuned-adm.py \
tuned.bash dbus.conf recommend.conf tuned-main.conf 00_tuned bootcmdline \
$(VERSIONED_NAME)
org.tuned.gui.policy tuned-gui.py tuned-gui.glade $(VERSIONED_NAME)
cp -a doc experiments libexec man profiles systemtap tuned $(VERSIONED_NAME)
tar cjf $(VERSIONED_NAME).tar.bz2 $(VERSIONED_NAME)
......@@ -40,9 +40,13 @@ install:
# binaries
install -Dpm 0755 tuned.py $(DESTDIR)/usr/sbin/tuned
install -Dpm 0755 tuned-adm.py $(DESTDIR)/usr/sbin/tuned-adm
install -Dpm 0755 tuned-gui.py $(DESTDIR)/usr/sbin/tuned-gui
$(foreach file, $(wildcard systemtap/*), \
install -Dpm 0755 $(file) $(DESTDIR)/usr/sbin/$(notdir $(file));)
# glade
install -Dpm 0755 tuned-gui.glade $(DESTDIR)/usr/share/tuned/ui/tuned-gui.glade
# tools
install -Dpm 0755 experiments/powertop2tuned.py $(DESTDIR)/usr/bin/powertop2tuned
......@@ -76,6 +80,9 @@ install:
# grub template
install -Dpm 0755 00_tuned $(DESTDIR)/etc/grub.d/00_tuned
# polkit configuration
install -Dpm 0644 org.tuned.gui.policy $(DESTDIR)/usr/share/polkit-1/actions/org.tuned.gui.policy
# manual pages
$(foreach man_section, 5 7 8, $(foreach file, $(wildcard man/*.$(man_section)), \
install -Dpm 0644 $(file) $(DESTDIR)/usr/share/man/man$(man_section)/$(notdir $(file));))
......
This diff is collapsed.
This diff is collapsed.
......@@ -21,10 +21,18 @@ Based on that information components will then be put into lower or higher
power saving modes to adapt to the current usage. Currently only ethernet
network and ATA harddisk devices are implemented.
%package gtk
Summary: GTK GUI for tuned
Requires: %{name} = %{version}-%{release}
Requires: powertop, pygobject3-base, polkit
%description gtk
GTK GUI that can control tuned and provide simple profile editor.
%package utils
Requires: %{name} = %{version}-%{release}
Summary: Various tuned utilities
Requires: powertop
Summary: Various tuned utilities
%description utils
This package contains utilities that can help you to fine tune and
......@@ -113,6 +121,7 @@ sed -i 's|.*/\([^/]\+\)/[^\.]\+\.conf|\1|' /etc/tuned/active_profile
%doc README
%doc doc/TIPS.txt
%{_datadir}/bash-completion/completions/tuned
%exclude %{python_sitelib}/tuned/gtk
%{python_sitelib}/tuned
%{_sbindir}/tuned
%{_sbindir}/tuned-adm
......@@ -142,9 +151,16 @@ sed -i 's|.*/\([^/]\+\)/[^\.]\+\.conf|\1|' /etc/tuned/active_profile
%{_mandir}/man5/tuned*
%{_mandir}/man7/tuned-profiles.7*
%{_mandir}/man8/tuned*
%dir %{_datadir}/tuned
%files utils
%files gtk
%defattr(-,root,root,-)
%{_sbindir}/tuned-gui
%{python_sitelib}/tuned/gtk
%{_datadir}/tuned/ui
%{_datadir}/polkit-1/actions/org.tuned.gui.policy
%files utils
%doc COPYING
%{_bindir}/powertop2tuned
%{_libexecdir}/tuned/pmqos-static*
......
......@@ -29,6 +29,10 @@ LOG_FILE = "/var/log/tuned/tuned.log"
PID_FILE = "/run/tuned/tuned.pid"
SYSTEM_RELEASE_FILE = "/etc/system-release-cpe"
# tuned-gui
PREFIX_PROFILE_FACTORY = "Factory"
PREFIX_PROFILE_USER = "User"
# default configuration
CFG_DEF_DYNAMIC_TUNING = True
# how long to sleep before checking for events (in seconds)
......
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2008-2014 Red Hat, Inc.
# Authors: Marek Staňa, Jaroslav Škarvada <jskarvad@redhat.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
'''
Created on Mar 30, 2014
@author: mstana
'''
import os
from validate import Validator
import tuned.plugins.base
import tuned.consts as consts
import tuned.logs
import tuned.plugins.repository as repository
import configobj as ConfigObj
import tuned.exceptions as TunedException
from tuned import plugins
from tuned.utils.plugin_loader import PluginLoader
from tuned import storage, units, monitors, plugins, profiles, exports, \
hardware
import tuned.plugins as Plugins
__all__ = ['GTKPluginLoader']
global_config_spec = ['dynamic_tuning = boolean(default=%s)'
% consts.CFG_DEF_DYNAMIC_TUNING,
'sleep_interval = integer(default=%s)'
% consts.CFG_DEF_SLEEP_INTERVAL,
'update_interval = integer(default=%s)'
% consts.CFG_DEF_UPDATE_INTERVAL]
class GuiPluginLoader(PluginLoader):
'''
Class for scan, import and load actual avaible plugins.
'''
def __init__(self):
'''
Constructor
'''
self._plugins = set()
self.plugins_doc = {}
storage_provider = storage.PickleProvider()
storage_factory = storage.Factory(storage_provider)
monitors_repository = monitors.Repository()
hardware_inventory = hardware.Inventory()
device_matcher = hardware.DeviceMatcher()
plugin_instance_factory = plugins.instance.Factory()
self.repo = repository.Repository(
monitors_repository,
storage_factory,
hardware_inventory,
device_matcher,
plugin_instance_factory,
self._set_loader_parameters(),
)
self.create_all(self._import_plugin_names())
@property
def plugins(self):
return self.repo.plugins
def _set_loader_parameters(self):
'''
Sets private atributes.
'''
self._namespace = 'tuned.plugins'
self._prefix = 'plugin_'
self._sufix = '.py'
self._interface = tuned.plugins.base.Plugin
def _import_plugin_names(self):
'''
Scan directories and find names to load
'''
names = []
for name in os.listdir(Plugins.__path__[0]):
file = name.split(self._prefix).pop()
if file.endswith(self._sufix):
(file_name, file_extension) = os.path.splitext(file)
names.append(file_name)
return names
def create_all(self, names):
for plugin_name in names:
try:
self._plugins.add(self.repo.create(plugin_name))
except ImportError:
pass
except tuned.plugins.exceptions.NotSupportedPluginException:
# problem with importing of plugin
# print str(ImportError) + plugin_name
pass
# print plugin_name + " is not supported!"
def get_plugin(self, plugin_name):
for plugin in self.plugins:
if plugin_name == plugin.name:
return plugin
def _load_global_config(self, file_name=consts.GLOBAL_CONFIG_FILE):
"""
Loads global configuration file.
"""
try:
config = ConfigObj(file_name,
configspec=global_config_spec,
raise_errors=True, file_error=True)
except IOError, e:
raise TunedException("Global tuned configuration file '%s' not found."
% file_name)
except ConfigObj.ConfigObjError, e:
raise TunedException("Error parsing global tuned configuration file '%s'."
% file_name)
vdt = Validator()
if not config.validate(vdt, copy=True):
raise TunedException("Global tuned configuration file '%s' is not valid."
% file_name)
return config
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2008-2014 Red Hat, Inc.
# Authors: Marek Staňa, Jaroslav Škarvada <jskarvad@redhat.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
'''
Created on Mar 13, 2014
@author: mstana
'''
import configobj
import os
import tuned.profiles.profile as p
import tuned.consts
import shutil
import tuned.gtk.managerException as managerException
class GuiProfileLoader(object):
"""
Profiles loader for GUI Gtk purposes.
"""
profiles = {}
def __init__(self, directories):
self.directories = directories
self._load_all_profiles()
def get_raw_profile(self, profile_name):
file = self._locate_profile_path(profile_name) + '/' \
+ profile_name + '/' + 'tuned.conf'
with open(file, 'r') as f:
return f.read()
def set_raw_profile(self, profile_name, config):
profilePath = self._locate_profile_path(profile_name)
if profilePath == tuned.consts.LOAD_DIRECTORIES[1]:
file = profilePath + '/' + profile_name + '/' + 'tuned.conf'
with open(file, 'w') as f:
f.write(config)
else:
raise managerException.ManagerException(profile_name
+ ' profile is stored in ' + profilePath
+ ' and can not be storet do this location')
def load_profile_config(self, profile_name, path):
conf_path = path + '/' + profile_name + '/tuned.conf'
profile_config = configobj.ConfigObj(conf_path)
return profile_config
def _locate_profile_path(self, profile_name):
for d in self.directories:
for profile in os.listdir(d):
if os.path.isdir(d + '/' + profile) and profile \
== profile_name:
path = d
return path
def _load_all_profiles(self):
for d in self.directories:
for profile in os.listdir(d):
if os.path.isdir(d + '/' + profile):
try:
self.profiles[profile] = p.Profile(profile,
self.load_profile_config(profile, d))
except configobj.ParseError:
pass
# print "can not make \""+ profile +"\" profile without correct config on path: " + d
# except:
# raise managerException.ManagerException("Can not make profile")
# print "can not make \""+ profile +"\" profile without correct config with path: " + d
def _refresh_profiles(self):
self.profiles = {}
self._load_all_profiles()
def save_profile(self, profile):
path = tuned.consts.LOAD_DIRECTORIES[1] + '/' + profile.name
config = configobj.ConfigObj()
config.filename = path + tuned.consts.CONF_PROFILE_FILE
config.initial_comment = ('#', 'tuned configuration', '#')
try:
config['main'] = profile.options
except KeyError:
config['main'] = ''
# profile dont have main section
pass
for (name, unit) in profile.units.items():
config[name] = unit.options
if not os.path.exists(path):
os.makedirs(path)
else:
# you cant rewrite profile!
raise managerException.ManagerException('Profile with name '
+ profile.name + 'exists already')
config.write()
self._refresh_profiles()
def _refresh_profiles(self):
self.profiles = {}
self._load_all_profiles()
def update_profile(
self,
old_profile_name,
profile,
is_admin,
):
if old_profile_name not in self.get_names():
raise managerException.ManagerException('Profile: '
+ old_profile_name + ' is not in profiles')
if self.is_profile_factory(old_profile_name):
path = tuned.consts.LOAD_DIRECTORIES[0] + '/' + profile.name
else:
path = tuned.consts.LOAD_DIRECTORIES[1] + '/' + profile.name
if old_profile_name != profile.name:
self.remove_profile(old_profile_name, is_admin=is_admin)
config = configobj.ConfigObj()
config.filename = path + '/tuned.conf'
config.initial_comment = ('#', 'tuned configuration', '#')
try:
config['main'] = profile.options
except KeyError:
# profile dont have main section
pass
for (name, unit) in profile.units.items():
config[name] = unit.options
if not os.path.exists(path):
os.makedirs(path)
config.write()
self._refresh_profiles()
def get_names(self):
return self.profiles.keys()
def get_profile(self, profile):
return self.profiles[profile]
def add_profile(self, profile):
self.profiles[profile.name] = profile
self.save_profile(profile)
def remove_profile(self, profile_name, is_admin):
profile_path = self._locate_profile_path(profile_name)
if self.is_profile_removable(profile_name) or is_admin:
shutil.rmtree(profile_path + '/' + profile_name)
self._load_all_profiles()
else:
raise managerException.ManagerException(profile_name
+ ' profile is stored in ' + profile_path)
def is_profile_removable(self, profile_name):
# profile is in /etc/profile
profile_path = self._locate_profile_path(profile_name)
if profile_path == tuned.consts.LOAD_DIRECTORIES[1]:
return True
else:
return False
def is_profile_factory(self, profile_name):
# profile is in /usr/lib/tuned
return not self.is_profile_removable(profile_name)
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2008-2014 Red Hat, Inc.
# Authors: Marek Staňa, Jaroslav Škarvada <jskarvad@redhat.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
import os
import sys
import tuned.plugins.repository
from tuned.exceptions import TunedException
class Manager(object):
def __init__(self):
self.repository = tuned.plugins.repository.Repository()
if __name__ == '__main__':
if os.geteuid() != 0:
os.error('Superuser permissions are required to run the daemon.'
)
sys.exit(1)
man = Manager()
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2008-2014 Red Hat, Inc.
# Authors: Marek Staňa, Jaroslav Škarvada <jskarvad@redhat.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
'''
Created on Apr 6, 2014
@author: mstana
'''
class ManagerException(Exception):
"""
"""
def __init__(self, code):
self.code = code
def __str__(self):
return repr(self.code)
def profile_already_exists(self, text):
return repr(text)
def profile_already_exists(self):
return repr(self.code)
......@@ -24,6 +24,8 @@ class MountsPlugin(base.Plugin):
stdout, stderr = Popen(["/usr/bin/lsblk", "-rno", "TYPE,RM,KNAME,FSTYPE,MOUNTPOINT"], stdout=PIPE, stderr=PIPE, close_fds=True).communicate()
for columns in map(lambda line: line.split(), stdout.splitlines()):
if len(columns) < 3:
continue
device_type, device_removable, device_name = columns[:3]
filesystem = columns[3] if len(columns) > 3 else None
mountpoint = columns[4] if len(columns) > 4 else None
......
......@@ -31,7 +31,7 @@ class NetTuningPlugin(base.Plugin):
self._devices.add(device.sys_name)
self._free_devices = self._devices.copy()
log.info("devices: %s" % str(self._devices));
log.debug("devices: %s" % str(self._devices));
def _instance_init(self, instance):
instance._has_static_tuning = True
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment