1
0
Fork 0

All changes are related to tests:

- New tests for "tuned-adm" with fake-profiles.
- Common results logging for pluginstest.py and admtest.py.
- Fixed issue with relative paths when started using "make test" or "tests/tuned-test.py".
- A bit code polishing.
- Added README.
- Added TODO.
This commit is contained in:
Jan Vcelak 2009-10-12 15:33:09 +02:00 committed by Petr Lautrbach
parent 07fef467c0
commit 1afcd62f78
15 changed files with 515 additions and 78 deletions

36
tests/README Normal file
View file

@ -0,0 +1,36 @@
Tests can be started with "make test" command from parent directory
or by executing "tuned-test.py" script.
The tests can not be run in completely isolated environment. You need
tuned and tuned-utils packages installed and you have to run the test
as superuser (needed for tuned-adm tests). Please note that your
settings of tuned can be changed.
What is tested
* monitor-plugins have to implement: init(), getLoad(), cleanup()
* tuning-plugins have to implement: init(), setTuning(), cleanup()
* each monitor-plugin has to have it's tuning-plugin with the same
name
* the result of monitor's getLoad() function is passed to setTuning(),
therefore tuning's getLoad() must not return Null
* "tuned-adm list" correctness
* "tuned-adm off" has to:
- stop tuned a ktune services
- remove (disable) tuned a ktune services
- remove "/etc/ktune.d/tunedadm.{sh,conf}"
* there are fake-profiles available for "tuned-adm profile <name>":
- disabled-all
- enabled-all
- enabled-ktune
- enabled-tuned
tuned-adm has to:
- stop/start service(s)
- add/remove/disable service(s)
- set up "/etc/ktune.d/tunedadm.{sh,conf}"
What is NOT tested
* monitor-plugins getLoad() data validity
* tuning-plugins setTuning() system setting changes

2
tests/TODO Normal file
View file

@ -0,0 +1,2 @@
- hiding output of tuned-adm (restarting services, etc..)

183
tests/admtest.py Normal file
View file

@ -0,0 +1,183 @@
#
# Copyright (C) 2008, 2009 Red Hat, Inc.
#
# 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, sys, re
from logging import log
from streamcapture import capture
class AdmTester:
def __init__(self):
self.profiles_dir = os.path.normpath(os.path.dirname(__file__) + "/../tune-profiles")
self.profiles_fake_dir = os.path.normpath(os.path.dirname(__file__) + "/fake-profiles")
def run(self):
import tuned_adm
self.tuned_adm = tuned_adm.tuned_adm
self.tuned_adm.init(self.profiles_dir)
self.__check_profiles()
self.__check_privileges()
# profiles switching
self.tuned_adm.init(self.profiles_fake_dir)
testing_states = [
#[ None, False, False ], # off mode
[ "enabled-all", True, True ],
[ None, False, False ], # off mode
[ "enabled-tuned", True, False ],
[ "enabled-ktune", False, True ],
[ "disabled-all", False, False ]
]
for ts in testing_states:
self.__check_state(ts[0], ts[1], ts[2])
def __check_profiles(self):
log.test("profiles listing")
profiles_tester = os.listdir(self.profiles_dir)
profiles_tester = filter(lambda f: f[0] != ".", profiles_tester)
try:
capture.clean()
capture.capture()
self.tuned_adm.run(["list"])
capture.stdout()
except Exception as e:
log.report_e(e)
return False
# printed profiles (first line "Modes:" is skipped)
profiles_tuned = capture.getcaptured().splitlines()[1:]
# compare profiles_tester and profiles_tuned
error = False
for p in profiles_tester:
if not p in profiles_tuned:
log.info("tune-adm does not report profile '%s'" % p)
error = True
for p in profiles_tuned:
if not p in profiles_tester:
log.info("tune-adm reports extra profile '%s'" % p)
error = True
if error:
log.result("profiles detected by this test differ from these reported by tuned-adm")
else:
log.result()
return True
def __check_privileges(self):
log.test("privileges")
if os.getuid() != 0:
log.result("You have to be root to run all following tests.")
return False
else:
log.result()
return True
def __service_running(self, service):
status = os.system("service %s status 1>/dev/null 2>&1" % service)
# 0 running, 3 stopped
if status == 0:
return True
else:
return False
def __initscript_enabled(self, service):
if os.system('chkconfig | grep -qx "^%s\W.*on.*$"' % service) == 0:
return True
else:
return False
def __check_state(self, profile, tuned_running, ktune_running):
is_ok = True
if profile == None:
self.tuned_adm.run(["off"])
log.test("checking state: off")
else:
self.tuned_adm.run(["profile", profile])
log.test("checking state: %s" % profile)
log.indent()
# services status
log.test("service 'tuned'")
if self.__service_running("tuned") == tuned_running:
log.result()
else:
is_ok = False
log.result("should %s" % ( "be running" if tuned_running else "not be running" ))
log.test("service 'ktune'")
if self.__service_running("ktune") == ktune_running:
log.result()
else:
is_ok = False
log.result("should %s" % ( "be running" if ktune_running else "not be running" ))
# init scripts
log.test("checking 'tuned' initscript")
if self.__initscript_enabled("tuned") == tuned_running:
log.result()
else:
is_ok = False
log.result("%s be enabled" % ( "should" if tuned_running else "should not" ))
log.test("checking 'ktune' initscript")
if self.__initscript_enabled("ktune") == ktune_running:
log.result()
else:
is_ok = False
log.result("%s be enabled" % ( "should" if ktune_running else "should not" ))
# config files
want_tunedadm_sh = False
want_tunedadm_conf = False
if profile != None:
want_tunedadm_sh = os.path.exists("%s/%s/ktune.sh" % (self.profiles_fake_dir, profile))
want_tunedadm_conf = os.path.exists("%s/%s/sysctl.ktune" % (self.profiles_fake_dir, profile))
log.test("checking '/etc/ktune.d/tunedadm.sh'")
if want_tunedadm_sh == os.path.exists("/etc/ktune.d/tunedadm.sh"):
log.result()
else:
is_ok = False
log.result("file should %s" % ( "exist" if want_tunedadm_sh else "not exist" ))
log.test("checking '/etc/ktune.d/tunedadm.conf'")
if want_tunedadm_conf == os.path.exists("/etc/ktune.d/tunedadm.conf"):
log.result()
else:
is_ok = False
log.result("file should %s" % ( "exist" if want_tunedadm_conf else "not exist" ))
log.unindent()
return is_ok

View file

View file

@ -0,0 +1,27 @@
# ktune service configuration
# This is the ktune sysctl file. You can comment this out to prevent ktune
# from applying its sysctl settings.
SYSCTL="/etc/sysctl.ktune"
# Use *.conf files in the ktune configuration directory /etc/ktune.d.
# Value: yes|no, default: yes
# It is useful if you want to load settings from additional files. Set this to
# no if you to prevent ktune from using these additional files.
USE_KTUNE_D="yes"
# This is the custom sysctl configuration file. Any settings in this file will
# be applied after the ktune settings, overriding them. Comment this out to
# use only the ktune settings.
SYSCTL_POST="/etc/sysctl.conf"
# This is the I/O scheduler ktune will use. This will *not* override anything
# explicitly set on the kernel command line, nor will it change the scheduler
# for any block device that is using a non-default scheduler when ktune starts.
# You should probably leave this on "deadline", but "as", "cfq", and "noop" are
# also legal values. Comment this out to prevent ktune from changing I/O
# scheduler settings.
#ELEVATOR="deadline"
# These are the devices, that should be tuned with the ELEVATOR
ELEVATOR_TUNE_DEVS=""

View file

@ -0,0 +1 @@
# ktune sysctl

View file

@ -0,0 +1,2 @@
[main]
interval=10

View file

@ -0,0 +1,27 @@
# ktune service configuration
# This is the ktune sysctl file. You can comment this out to prevent ktune
# from applying its sysctl settings.
SYSCTL="/etc/sysctl.ktune"
# Use *.conf files in the ktune configuration directory /etc/ktune.d.
# Value: yes|no, default: yes
# It is useful if you want to load settings from additional files. Set this to
# no if you to prevent ktune from using these additional files.
USE_KTUNE_D="yes"
# This is the custom sysctl configuration file. Any settings in this file will
# be applied after the ktune settings, overriding them. Comment this out to
# use only the ktune settings.
SYSCTL_POST="/etc/sysctl.conf"
# This is the I/O scheduler ktune will use. This will *not* override anything
# explicitly set on the kernel command line, nor will it change the scheduler
# for any block device that is using a non-default scheduler when ktune starts.
# You should probably leave this on "deadline", but "as", "cfq", and "noop" are
# also legal values. Comment this out to prevent ktune from changing I/O
# scheduler settings.
#ELEVATOR="deadline"
# These are the devices, that should be tuned with the ELEVATOR
ELEVATOR_TUNE_DEVS=""

View file

@ -0,0 +1 @@
# ktune sysctl

View file

@ -0,0 +1,2 @@
[main]
interval=10

92
tests/logging.py Normal file
View file

@ -0,0 +1,92 @@
#
# Copyright (C) 2008, 2009 Red Hat, Inc.
#
# 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.
#
class Logging:
__indent = 0
__testFinished = True
__info = []
def __init__(self):
pass
def end(self):
if not self.__testFinished:
print
self.__infoFlush()
self.__indent = 0
print
def section(self, info):
print
print "== %s ==" % info
print
self.__indent = 0
def indent(self):
self.__indent += 1
def unindent(self):
self.__indent -= 1
def test(self, info):
if not self.__testFinished:
print
self.__infoFlush()
self.__testFinished = False
i = self.__indent
self.__info = []
if i == 0: bullet = "* "
elif i == 1: bullet = "+ "
elif i == 2: bullet = "- "
else: bullet = "? "
print (i * " ") + bullet + info,
def info(self, info):
self.__info.append(str(info))
if self.__testFinished:
self.__infoFlush()
def __infoFlush(self):
for i in self.__info:
print ((self.__indent + 1) * " ") + "> " + i
self.__info = []
def result(self, failinfo = None):
if failinfo == None:
print ": success"
else:
print ": failed"
self.info(failinfo)
self.__infoFlush()
self.__testFinished = True
def result_e(self, exception):
print ": failed"
if not exception == None:
self.info("Exception: %s, %s" % (exception, type(exception)))
self.__infoFlush()
self.__testFinished = True
log = Logging()

View file

@ -17,6 +17,7 @@
# #
import os, ConfigParser import os, ConfigParser
from logging import log
class PluginsTester: class PluginsTester:
@ -27,12 +28,14 @@ class PluginsTester:
self.tp_dir = "tuningplugins" self.tp_dir = "tuningplugins"
self.config = ConfigParser.RawConfigParser() self.config = ConfigParser.RawConfigParser()
self.config.add_section("main")
self.config.set("main", "interval", 10)
def run(self): def run(self):
self.__checkPlugins() self.__check_plugins()
def __getPluginsFromDir(self, dir): def __get_plugins_from_dir(self, dir):
files = os.listdir(self.tunedir + "/" + dir); files = os.listdir(self.tunedir + "/" + dir);
plugins = filter(lambda f: f[0] != "." and f[-3:] == ".py" and f != "__init__.py", files) plugins = filter(lambda f: f[0] != "." and f[-3:] == ".py" and f != "__init__.py", files)
@ -41,176 +44,178 @@ class PluginsTester:
return plugins return plugins
### reporting tests progress def __check_plugins(self):
def __reportTestBegin(self, info): monitorplugins = self.__get_plugins_from_dir(self.mp_dir)
print " * %s" % (info) tuningplugins = self.__get_plugins_from_dir(self.tp_dir)
def __reportTestStep(self, info):
self.__testStep = info
def __reportTestResult(self, exception = None):
if exception == None:
print " - %s: success" % (self.__testStep)
else:
print " - %s: failed (%s, %s)" % (self.__testStep, exception, type (exception))
def __reportTestSkip(self, info):
print " - %s: skipped, %s" % (self.__testStep, info)
### testing
def __checkPlugins(self):
monitorplugins = self.__getPluginsFromDir(self.mp_dir)
tuningplugins = self.__getPluginsFromDir(self.tp_dir)
# check plugins availability # check plugins availability
print "checking plugins availablity" self.__check_sibling_plugins(monitorplugins, tuningplugins)
self.__checkSiblingPlugins(monitorplugins, tuningplugins)
print
# monitor plugins test # monitor plugins test
print "monitor plugins test" log.test("monitor plugins test")
if len(monitorplugins) == 0: if len(monitorplugins) == 0:
print " - no plugins found" log.result("no plugins found")
log.indent()
monitor_results = {} monitor_results = {}
for mp in monitorplugins: for mp in monitorplugins:
load = self.__testMonitorPlugin(mp) load = self.__test_monitor_plugin(mp)
monitor_results[mp] = load monitor_results[mp] = load
print log.unindent()
# tuning plugins test # tuning plugins test
print "tunning plugins test" log.test("tunning plugins test")
if len(tuningplugins) == 0: if len(tuningplugins) == 0:
print " - no plugins found" log.result("no plugins found")
log.indent()
for tp in tuningplugins: for tp in tuningplugins:
try: try:
load = monitor_results[tp] load = monitor_results[tp]
except: except:
load = None load = None
self.__testTuningPlugin(tp, load) self.__test_tuning_plugin(tp, load)
def __checkSiblingPlugins(self, monitorplugins, tuningplugins): log.unindent()
def __check_sibling_plugins(self, monitorplugins, tuningplugins):
ok = True ok = True
log.test("monitor and tuning plugins availability")
for mp in monitorplugins: for mp in monitorplugins:
if tuningplugins.count(mp) != 1: if tuningplugins.count(mp) != 1:
ok = False ok = False
print " - monitor plugin '%s' misses tuning plugin" % mp log.info("monitor plugin '%s' misses tuning plugin" % mp)
for tp in tuningplugins: for tp in tuningplugins:
if monitorplugins.count(tp) != 1: if monitorplugins.count(tp) != 1:
ok = False ok = False
print " - tuning plugin '%s' misses monitor plugin" % tp log.info("tuning plugin '%s' misses monitor plugin" % tp)
if ok: if ok:
print " - monitor and tuning plugins match" log.result()
else:
log.result("monitor and tunning plugins do not match")
def __testMonitorPlugin(self, name): def __test_monitor_plugin(self, name):
self.__reportTestBegin("monitor plugin: %s" % (name)) log.test("monitor plugin: %s" % name)
log.indent()
# initialization # initialization
self.__reportTestStep("initialization") log.test("initialization")
try: try:
exec "from %s.%s import _plugin" % (self.mp_dir, name) exec "from %s.%s import _plugin" % (self.mp_dir, name)
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e(e)
log.unindent()
return None return None
self.__reportTestResult()
log.result()
# init() # init()
self.__reportTestStep("call init()") log.test("call init()")
try: try:
_plugin.init(self.config) _plugin.init(self.config)
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e(e)
log.unindent()
return None return None
self.__reportTestResult() log.result()
# getLoad() # getLoad()
self.__reportTestStep("call getLoad()") log.test("call getLoad()")
try: try:
load = _plugin.getLoad() load = _plugin.getLoad()
if load == None: if load == None:
raise Exception("Plugin returned None as a result.") raise Exception("Plugin returned None as a result.")
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e(e)
log.unindent()
return None return None
self.__reportTestResult() log.result()
# cleanup() # cleanup()
self.__reportTestStep("call cleanup()") log.test("call cleanup()")
try: try:
_plugin.cleanup() _plugin.cleanup()
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e(e)
return None log.unindent()
self.__reportTestResult() return load
log.result()
log.unindent()
return load return load
def __testTuningPlugin(self, name, load): def __test_tuning_plugin(self, name, load):
self.__reportTestBegin("tuning plugin: %s" % (name)) log.test("tuning plugin: %s" % name)
log.indent()
# initialization # initialization
self.__reportTestStep("initialization") log.test("initialization")
try: try:
exec "from %s.%s import _plugin" % (self.tp_dir, name) exec "from %s.%s import _plugin" % (self.tp_dir, name)
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e()
log.unindent()
return False return False
self.__reportTestResult() log.result()
# init() # init()
self.__reportTestStep("call init()") log.test("call init()")
try: try:
_plugin.init(self.config) _plugin.init(self.config)
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e(e)
log.unindent()
return False return False
self.__reportTestResult() log.result()
# setTuning() # setTuning()
self.__reportTestStep("call setTuning()") log.test("call setTuning()")
if load == None: if load == None:
self.__reportTestSkip("no data from monitor plugin available") log.info("no data from monitor plugin available")
else: else:
try: try:
_plugin.setTuning(load) _plugin.setTuning(load)
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e(e)
return False log.unindent()
self.__reportTestResult() return False
log.result()
# cleanup() # cleanup()
self.__reportTestStep("call cleanup()") log.test("call cleanup()")
try: try:
_plugin.cleanup() _plugin.cleanup()
except Exception as e: except Exception as e:
self.__reportTestResult(e) log.result_e(e)
log.unindent()
return False return False
self.__reportTestResult() log.result()
log.unindent()
return True return True

52
tests/streamcapture.py Normal file
View file

@ -0,0 +1,52 @@
#
# Copyright (C) 2008, 2009 Red Hat, Inc.
#
# 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 sys, StringIO
class StreamCapture:
__capture = None
__stdout = None
def __init__(self):
self.__stdout = sys.stdout
self.__capture = StringIO.StringIO()
def capture(self):
sys.stdout = self.__capture
def stdout(self):
sys.stdout = self.__stdout
def getcaptured(self):
return self.__capture.getvalue()
def clean(self):
self.__capture.close()
self.__capture = StringIO.StringIO()
def close(self):
self.__capture.close()
def __del__(self):
try: sys.stdout = self.__stdout
except: pass
try: self.__capture.close()
except: pass
capture = StreamCapture()

View file

@ -17,12 +17,19 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
import sys import sys, os
import pluginstest import pluginstest, admtest
from logging import log
# importing modules - parent directory # importing modules from parent directory
tunedir = sys.path[0] + "/.." tunedir = os.path.normpath(os.path.dirname(os.path.abspath(__file__)) + "/..")
sys.path.insert(1, tunedir); sys.path.insert(1, tunedir);
log.section("monitoring and tuning plugins")
pt = pluginstest.PluginsTester(tunedir) pt = pluginstest.PluginsTester(tunedir)
pt.run() pt.run()
log.section("tuned-adm tests")
at = admtest.AdmTester()
at.run()