summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sommerseth <davids@redhat.com>2012-12-17 15:30:27 +0100
committerDavid Sommerseth <davids@redhat.com>2012-12-17 15:30:27 +0100
commit189b3a3cbbc156c20fe9bf7a76aca6f35b53e2e9 (patch)
treecd920a76201c14440f040e6ef38691f0d1f46ab5
parentfe0c14d6ea7d9e3390f34f62511048e0d9feaca4 (diff)
downloadrteval-189b3a3cbbc156c20fe9bf7a76aca6f35b53e2e9.tar.gz
rteval-189b3a3cbbc156c20fe9bf7a76aca6f35b53e2e9.tar.xz
rteval-189b3a3cbbc156c20fe9bf7a76aca6f35b53e2e9.zip
Split up rteval.py into a pure measurement module and the command line tool
This cleans up rteval further by making the RtEval class a pure measurement feature which receives all the needed setup and controls loads and measurment modules, in addition to the final reporting. The command line and config parsing is kept inside the command line tool, where it will set up everything before passing it all to the RtEval calss. Signed-off-by: David Sommerseth <davids@redhat.com>
-rwxr-xr-xrteval-cmd311
-rw-r--r--rteval/__init__.py243
-rw-r--r--rteval/modules/__init__.py8
-rw-r--r--rteval/modules/loads/__init__.py6
-rw-r--r--rteval/modules/loads/hackbench.py4
-rw-r--r--rteval/modules/loads/kcompile.py4
-rw-r--r--rteval/modules/measurement/__init__.py2
-rw-r--r--rteval/modules/measurement/cyclictest.py4
-rw-r--r--rteval/rteval.py483
-rw-r--r--rteval/sysinfo/__init__.py2
-rw-r--r--rteval/sysinfo/dmi.py2
-rw-r--r--rteval/sysinfo/kernel.py4
-rw-r--r--rteval/sysinfo/osinfo.py2
-rw-r--r--rteval/sysinfo/services.py4
14 files changed, 573 insertions, 506 deletions
diff --git a/rteval-cmd b/rteval-cmd
new file mode 100755
index 0000000..8d1e30d
--- /dev/null
+++ b/rteval-cmd
@@ -0,0 +1,311 @@
+#!/usr/bin/python -tt
+#
+# rteval - script for evaluating platform suitability for RT Linux
+#
+# This program is used to determine the suitability of
+# a system for use in a Real Time Linux environment.
+# It starts up various system loads and measures event
+# latency while the loads are running. A report is generated
+# to show the latencies encountered during the run.
+#
+# Copyright 2009,2010,2011,2012 Clark Williams <williams@redhat.com>
+# Copyright 2009,2010,2011,2012 David Sommerseth <davids@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# For the avoidance of doubt the "preferred form" of this code is one which
+# is in an open unpatent encumbered format. Where cryptographic key signing
+# forms part of the process of creating an executable the information
+# including keys needed to generate an equivalently functional executable
+# are deemed to be part of the source code.
+#
+
+import sys, os, time, optparse, tempfile
+import libxml2, libxslt
+from datetime import datetime
+from rteval.Log import Log
+from rteval import RtEval, rtevalConfig
+from rteval.modules.loads import LoadModules
+from rteval.modules.measurement import MeasurementModules
+
+
+
+def summarize(repfile, xslt):
+ isarchive = False
+ summaryfile = repfile
+ if repfile.endswith(".tar.bz2"):
+ import tarfile
+ try:
+ t = tarfile.open(repfile)
+ except:
+ print "Don't know how to summarize %s (tarfile open failed)" % repfile
+ return
+ element = None
+ for f in t.getnames():
+ if f.find('summary.xml') != -1:
+ element = f
+ break
+ if element == None:
+ print "No summary.xml found in tar archive %s" % repfile
+ return
+ tmp = tempfile.gettempdir()
+ t.extract(element, path=tmp)
+ summaryfile = os.path.join(tmp, element)
+ isarchive = True
+
+ # Load the XSLT template
+ xsltdoc = libxml2.parseFile(xslt)
+ xsltprs = libxslt.parseStylesheetDoc(xsltdoc)
+
+ # Load the summay.xml report - with some simple sanity checks
+ xmldoc = libxml2.parseFile(summaryfile)
+ if xmldoc.name != summaryfile:
+ raise RuntimeError("Failed to load the report")
+ if xmldoc.children.name != 'rteval':
+ raise RuntimeError("The report doesn't seem like a rteval summary report")
+
+ # Parse the report through the XSLT template - preserve proper encoding
+ resdoc = xsltprs.applyStylesheet(xmldoc, None)
+ report = xsltprs.saveResultToString(resdoc).encode(xsltprs.encoding())
+
+ # Dump the report to stdout - as UTF-8
+ print report.encode('UTF-8')
+
+ # Clean up
+ del report
+ del resdoc
+ del xmldoc
+ del xsltprs
+ del xsltdoc
+
+ if isarchive:
+ os.unlink(summaryfile)
+
+
+
+def parse_options(cfg, cmdargs):
+ '''parse the command line arguments'''
+
+ rtevcfg = cfg.GetSection('rteval')
+ parser = optparse.OptionParser()
+ parser.add_option("-d", "--duration", dest="duration",
+ type="string", default=rtevcfg.duration,
+ help="specify length of test run (default: %default)")
+ parser.add_option("-v", "--verbose", dest="verbose",
+ action="store_true", default=rtevcfg.verbose,
+ help="turn on verbose prints (default: %default)")
+ parser.add_option("-w", "--workdir", dest="workdir",
+ type="string", default=rtevcfg.workdir,
+ help="top directory for rteval data (default: %default)")
+ parser.add_option("-l", "--loaddir", dest="srcdir",
+ type="string", default=rtevcfg.srcdir,
+ help="directory for load source tarballs (default: %default)")
+ parser.add_option("-i", "--installdir", dest="installdir",
+ type="string", default=rtevcfg.installdir,
+ help="place to locate installed templates (default: %default)")
+ parser.add_option("-s", "--sysreport", dest="sysreport",
+ action="store_true", default=rtevcfg.sysreport,
+ help='run sysreport to collect system data (default: %default)')
+ parser.add_option("-D", '--debug', dest='debugging',
+ action='store_true', default=rtevcfg.debugging,
+ help='turn on debug prints (default: %default)')
+ parser.add_option("-X", '--xmlrpc-submit', dest='xmlrpc',
+ action='store', default=rtevcfg.xmlrpc, metavar='HOST',
+ help='Hostname to XML-RPC server to submit reports')
+ parser.add_option("-P", "--xmlrpc-no-abort", dest="xmlrpc_noabort",
+ action='store_true', default=False,
+ help="Do not abort if XML-RPC server do not respond to ping request");
+ parser.add_option("-Z", '--summarize', dest='summarize',
+ action='store_true', default=False,
+ help='summarize an already existing XML report')
+ parser.add_option("-H", '--raw-histogram', dest='rawhistogram',
+ action='store_true', default=False,
+ help='Generate raw histogram data for an already existing XML report')
+ parser.add_option("-f", "--inifile", dest="inifile",
+ type='string', default=None,
+ help="initialization file for configuring loads and behavior")
+ parser.add_option("-a", "--annotate", dest="annotate",
+ type="string", default=None,
+ help="Add a little annotation which is stored in the report")
+ parser.add_option("-L", "--logging", dest="logging",
+ action='store_true', default=False,
+ help='log the output of the loads in the report directory')
+ parser.add_option("-O", "--onlyload", dest="onlyload",
+ action='store_true', default=False,
+ help="only run the loads (don't run measurement threads)")
+
+ (cmd_opts, cmd_args) = parser.parse_args(args = cmdargs)
+ if cmd_opts.duration:
+ mult = 1.0
+ v = cmd_opts.duration.lower()
+ if v.endswith('s'):
+ v = v[:-1]
+ elif v.endswith('m'):
+ v = v[:-1]
+ mult = 60.0
+ elif v.endswith('h'):
+ v = v[:-1]
+ mult = 3600.0
+ elif v.endswith('d'):
+ v = v[:-1]
+ mult = 3600.0 * 24.0
+ cmd_opts.duration = float(v) * mult
+ return (cmd_opts, cmd_args)
+
+
+
+if __name__ == '__main__':
+ from rteval.sysinfo import dmi
+
+ dmi.ProcessWarnings()
+
+ try:
+ default_config = {
+ 'rteval': {
+ 'quiet' : False,
+ 'verbose' : False,
+ 'keepdata' : True,
+ 'debugging' : False,
+ 'duration' : '60',
+ 'sysreport' : False,
+ 'reportdir' : None,
+ 'reportfile' : None,
+ 'workdir' : os.getcwd(),
+ 'installdir' : '/usr/share/rteval',
+ 'srcdir' : '/usr/share/rteval/loadsource',
+ 'xmlrpc' : None,
+ 'xslt_report': '/usr/share/rteval/rteval_text.xsl',
+ 'report_interval': '600',
+ 'logging' : False,
+ },
+ 'loads' : {
+ 'kcompile' : 'module',
+ 'hackbench' : 'module',
+ },
+ 'kcompile' : {
+ 'source' : 'linux-2.6.21.tar.bz2',
+ 'jobspercore': '2',
+ },
+ 'hackbench' : {
+ 'source' : 'hackbench.tar.bz2',
+ 'jobspercore': '5',
+ },
+ 'cyclictest' : {
+ 'interval' : '100',
+ 'buckets' : '2000',
+ 'distance' : '25',
+ },
+ 'measurement' : {
+ 'cyclictest' : 'module',
+ }
+ }
+
+ # Prepare logging
+ logger = Log()
+ logger.SetLogVerbosity(Log.INFO)
+
+ # setup initial configuration
+ config = rtevalConfig.rtevalConfig(default_config, logger=logger)
+
+ # Before really parsing options, see if we have been given a config file in the args
+ # and load it - just so that default values are according to the config file
+ try:
+ cfgfile = sys.argv[sys.argv.index('-f')+1]
+ config.Load(cfgfile)
+ except IndexError:
+ # Missing file argument
+ raise RuntimeError('The -f option requires a file name to the configuration file')
+ except ValueError:
+ # No configuration file given
+ pass
+
+ loadmods = LoadModules(config, logger=logger)
+ measuremods = MeasurementModules(config, logger=logger)
+
+ # parse command line options
+ (cmd_opts, cmd_args) = parse_options(config, sys.argv[1:])
+
+ # copy the command line options into the rteval config section
+ # (cmd line overrides config file values)
+ config.AppendConfig('rteval', vars(cmd_opts))
+ rtevcfg = config.GetSection('rteval')
+
+ rtevcfg.workdir = os.path.abspath(cmd_opts.workdir)
+
+ # Update log level, based on config/command line args
+ loglev = (not rtevcfg.quiet and (Log.ERR | Log.WARN)) \
+ | (rtevcfg.verbose and Log.INFO) \
+ | (rtevcfg.debugging and Log.DEBUG)
+ logger.SetLogVerbosity(loglev)
+
+ logger.log(Log.DEBUG, "workdir: %s" % rtevcfg.workdir)
+
+ # if --summarize was specified then just parse the XML, print it and exit
+ if cmd_opts.summarize or cmd_opts.rawhistogram:
+ if len(cmd_args) < 1:
+ raise RuntimeError, "Must specify at least one XML file with --summarize!"
+
+ for x in cmd_args:
+ if cmd_opts.summarize:
+ summarize(x, '%s/rteval_text.xsl' % rtevcfg.installdir)
+ elif cmd_opts.rawhistogram:
+ summarize(x, '%s/rteval_histogram_raw.xsl' % rtevcfg.installdir)
+
+ sys.exit(0)
+
+ if os.getuid() != 0:
+ print "Must be root to run rteval!"
+ sys.exit(-1)
+
+ logger.log(Log.DEBUG, '''rteval options:
+ workdir: %s
+ loaddir: %s
+ reportdir: %s
+ verbose: %s
+ debugging: %s
+ logging: %s
+ duration: %f
+ sysreport: %s''' % (
+ rtevcfg.workdir, rtevcfg.srcdir,
+ rtevcfg.reportdir, rtevcfg.verbose,
+ rtevcfg.debugging, rtevcfg.logging,
+ rtevcfg.duration, rtevcfg.sysreport))
+
+ if not os.path.isdir(rtevcfg.workdir):
+ raise RuntimeError, "work directory %d does not exist" % rtevcfg.workdir
+
+
+ rteval = RtEval(config, loadmods, measuremods, logger)
+ rteval.Prepare(cmd_opts.onlyload)
+
+ if cmd_opts.onlyload:
+ # If --onlyload were given, just kick off the loads and nothing more
+ # No reports will be created.
+ loadmods.Start()
+ nthreads = loadmods.Unleash()
+ logger.log(Log.INFO, "Started %i load threads - will run for %f seconds" % (
+ nthreads, rtevcfg.duration))
+ logger.log(Log.INFO, "No measurements will be performed, due to the --onlyload option")
+ time.sleep(rtevcfg.duration)
+ loadmods.Stop()
+ ec = 0
+ else:
+ # ... otherwise, run the full measurement suite with loads
+ ec = rteval.Measure()
+ logger.log(Log.DEBUG, "exiting with exit code: %d" % ec)
+
+ sys.exit(ec)
+ except KeyboardInterrupt:
+ sys.exit(0)
diff --git a/rteval/__init__.py b/rteval/__init__.py
index e1c8289..f3d93fe 100644
--- a/rteval/__init__.py
+++ b/rteval/__init__.py
@@ -1,7 +1,246 @@
"""
-Copyright (c) 2008,2009,2010 Red Hat Inc.
+Copyright (c) 2008-2012 Red Hat Inc.
Realtime verification utility
"""
-__author__ = "Clark Williams <williams@redhat.com>"
+__author__ = "Clark Williams <williams@redhat.com>, David Sommerseth <davids@redhat.com>"
__license__ = "GPLv2 License"
+
+import os, signal, sys, threading, time
+from datetime import datetime
+from distutils import sysconfig
+from sysinfo import SystemInfo
+from modules.loads import LoadModules
+from modules.measurement import MeasurementModules, MeasurementProfile
+from rtevalReport import rtevalReport
+from rtevalXMLRPC import rtevalXMLRPC
+from Log import Log
+import rtevalConfig, rtevalMailer
+
+
+
+sigint_received = False
+def sigint_handler(signum, frame):
+ global sigint_received
+ sigint_received = True
+ print "*** SIGINT received - stopping rteval run ***"
+
+
+
+def sigterm_handler(signum, frame):
+ raise RuntimeError, "SIGTERM received!"
+
+
+
+class RtEval(rtevalReport):
+ def __init__(self, config, loadmods, measuremods, logger):
+ self.__version = "2.0_pre"
+
+ if not isinstance(config, rtevalConfig.rtevalConfig):
+ raise TypeError("config variable is not an rtevalConfig object")
+
+ if not isinstance(loadmods, LoadModules):
+ raise TypeError("loadmods variable is not a LoadModules object")
+
+ if not isinstance(measuremods, MeasurementModules):
+ raise TypeError("measuremods variable is not a MeasurementModules object")
+
+ if not isinstance(logger, Log):
+ raise TypeError("logger variable is not an Log object")
+
+ self.__cfg = config
+ self.__logger = logger
+ self._loadmods = loadmods
+ self._measuremods = measuremods
+
+ self.__rtevcfg = self.__cfg.GetSection('rteval')
+ self.__reportdir = None
+
+ self._sysinfo = SystemInfo(self.__rtevcfg, logger=self.__logger)
+
+ # prepare a mailer, if that's configured
+ if self.__cfg.HasSection('smtp'):
+ self.__mailer = rtevalMailer.rtevalMailer(self.__cfg.GetSection('smtp'))
+ else:
+ self.__mailer = None
+
+ # Prepare XSLT processing, if enabled
+ if not self.__rtevcfg.xslt_report.startswith(self.__rtevcfg.installdir):
+ self.__rtevcfg.xslt_report = os.path.join(self.__rtevcfg.installdir, "rteval_text.xsl")
+
+ if not os.path.exists(self.__rtevcfg.xslt_report):
+ raise RuntimeError("can't find XSL template (%s)!" % self.__rtevcfg.xslt_report)
+
+ # Add rteval directory into module search path
+ sys.path.insert(0, '%s/rteval' % sysconfig.get_python_lib())
+
+ # Initialise the report module
+ rtevalReport.__init__(self, self.__version,
+ self.__rtevcfg.installdir, self.__rtevcfg.annotate)
+
+ # If --xmlrpc-submit is given, check that we can access the server
+ if self.__rtevcfg.xmlrpc:
+ self.__xmlrpc = rtevalXMLRPC(self.__rtevcfg.xmlrpc, self.__logger, self.__mailer)
+ if not self.__xmlrpc.Ping():
+ if not self.__rtevcfg.xmlrpc_noabort:
+ print "ERROR: Could not reach XML-RPC server '%s'. Aborting." % \
+ self.__rtevcfg.xmlrpc
+ sys.exit(2)
+ else:
+ print "WARNING: Could not ping the XML-RPC server. Will continue anyway."
+ else:
+ self.__xmlrpc = None
+
+
+ def __show_remaining_time(self, remaining):
+ r = int(remaining)
+ days = r / 86400
+ if days: r = r - (days * 86400)
+ hours = r / 3600
+ if hours: r = r - (hours * 3600)
+ minutes = r / 60
+ if minutes: r = r - (minutes * 60)
+ print "rteval time remaining: %d days, %d hours, %d minutes, %d seconds" % (days, hours, minutes, r)
+
+
+ def Prepare(self, onlyload = False):
+ builddir = os.path.join(self.__rtevcfg.workdir, 'rteval-build')
+ if not os.path.isdir(builddir): os.mkdir(builddir)
+
+ # create our report directory
+ try:
+ # Only create a report dir if we're doing measurements
+ # or the loads logging is enabled
+ if not onlyload or self.__rtevcfg.logging:
+ self.__reportdir = self._make_report_dir(self.__rtevcfg.workdir, "summary.xml")
+ except Exception, e:
+ raise RuntimeError("Cannot create report directory (NFS with rootsquash on?) [%s]", str(e))
+
+ self.__logger.log(Log.INFO, "Preparing load modules")
+ params = {'workdir':self.__rtevcfg.workdir,
+ 'reportdir':self.__reportdir,
+ 'builddir':builddir,
+ 'srcdir':self.__rtevcfg.srcdir,
+ 'verbose': self.__rtevcfg.verbose,
+ 'debugging': self.__rtevcfg.debugging,
+ 'numcores':self._sysinfo.cpu_getCores(True),
+ 'logging':self.__rtevcfg.logging,
+ 'memsize':self._sysinfo.mem_get_size(),
+ 'numanodes':self._sysinfo.mem_get_numa_nodes(),
+ 'duration':self.__rtevcfg.duration,
+ }
+ self._loadmods.Setup(params)
+
+ self.__logger.log(Log.INFO, "Preparing measurement modules")
+ self._measuremods.Setup(params)
+
+
+ def __RunMeasurementProfile(self, measure_profile):
+ if not isinstance(measure_profile, MeasurementProfile):
+ raise Exception("measure_profile is not an MeasurementProfile object")
+
+ measure_start = None
+ (with_loads, run_parallel) = measure_profile.GetProfile()
+ self.__logger.log(Log.INFO, "Using measurement profile [loads: %s parallel: %s]" % (
+ with_loads, run_parallel))
+ try:
+ nthreads = 0
+
+ # start the loads
+ if with_loads:
+ self._loadmods.Start()
+
+ print "rteval run on %s started at %s" % (os.uname()[2], time.asctime())
+ print "started %d loads on %d cores" % (self._loadmods.ModulesLoaded(), self._sysinfo.cpu_getCores(True)),
+ if self._sysinfo.mem_get_numa_nodes() > 1:
+ print " with %d numa nodes" % self._sysinfo.mem_get_numa_nodes()
+ else:
+ print ""
+ print "Run duration: %d seconds" % self.__rtevcfg.duration
+
+ # start the cyclictest thread
+ measure_profile.Start()
+
+ # Uleash the loads and measurement threads
+ report_interval = int(self.__rtevcfg.report_interval)
+ nthreads = with_loads and self._loadmods.Unleash() or None
+ measure_profile.Unleash()
+ measure_start = datetime.now()
+
+ # wait for time to expire or thread to die
+ signal.signal(signal.SIGINT, sigint_handler)
+ signal.signal(signal.SIGTERM, sigterm_handler)
+ self.__logger.log(Log.INFO, "waiting for duration (%f)" % self.__rtevcfg.duration)
+ stoptime = (time.time() + self.__rtevcfg.duration)
+ currtime = time.time()
+ rpttime = currtime + report_interval
+ load_avg_checked = 5
+ while (currtime <= stoptime) and not sigint_received:
+ time.sleep(1.0)
+ if not measure_profile.isAlive():
+ stoptime = currtime
+ self.__logger.log(Log.WARN,
+ "Measurement threads did not use the full time slot. Doing a controlled stop.")
+
+ if with_loads:
+ if len(threading.enumerate()) < nthreads:
+ raise RuntimeError, "load thread died!"
+
+ if not load_avg_checked:
+ self._loadmods.SaveLoadAvg()
+ load_avg_checked = 5
+ else:
+ load_avg_checked -= 1
+
+ if currtime >= rpttime:
+ left_to_run = stoptime - currtime
+ self.__show_remaining_time(left_to_run)
+ rpttime = currtime + report_interval
+ print "load average: %.2f" % self._loadmods.GetLoadAvg()
+ currtime = time.time()
+
+ self.__logger.log(Log.DEBUG, "out of measurement loop")
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ signal.signal(signal.SIGTERM, signal.SIG_DFL)
+
+ except RuntimeError, e:
+ print "Runtime error during measurement: %s", e
+ raise
+
+ finally:
+ # stop measurement threads
+ measure_profile.Stop()
+
+ # stop the loads
+ if with_loads:
+ self._loadmods.Stop()
+
+ print "stopping run at %s" % time.asctime()
+
+ # wait for measurement modules to finish calculating stats
+ measure_profile.WaitForCompletion()
+
+ return measure_start
+
+
+ def Measure(self):
+ # Run the full measurement suite with reports
+ rtevalres = 0
+ measure_start = None
+ for meas_prf in self._measuremods:
+ mstart = self.__RunMeasurementProfile(meas_prf)
+ if measure_start is None:
+ measure_start = mstart
+
+ self._report(measure_start, self.__rtevcfg.xslt_report)
+ if self.__rtevcfg.sysreport:
+ self._sysinfo.run_sysreport(self.__reportdir)
+
+ # if --xmlrpc-submit | -X was given, send our report to the given host
+ if self.__xmlrpc:
+ retvalres = self.__xmlrpc.SendReport(self._XMLreport())
+
+ self._sysinfo.copy_dmesg(self.__reportdir)
+ self._tar_results()
+ return rtevalres
+
diff --git a/rteval/modules/__init__.py b/rteval/modules/__init__.py
index 6b656e8..1d927d2 100644
--- a/rteval/modules/__init__.py
+++ b/rteval/modules/__init__.py
@@ -22,8 +22,8 @@
# are deemed to be part of the source code.
#
-from Log import Log
-from rtevalConfig import rtevalCfgSection
+from rteval.Log import Log
+from rteval.rtevalConfig import rtevalCfgSection
import time, libxml2, threading
__all__ = ["rtevalModulePrototype", "ModuleContainer", "RtEvalModules"]
@@ -202,8 +202,8 @@ reference from the first import"""
return self.__modsloaded[idxname]
except KeyError:
self.__logger.log(Log.INFO, "importing module %s" % modname)
- mod = __import__("%s.%s" % (modroot, modname),
- fromlist=modroot)
+ mod = __import__("rteval.%s.%s" % (modroot, modname),
+ fromlist="rteval.%s" % modroot)
self.__modsloaded[idxname] = mod
return mod
diff --git a/rteval/modules/loads/__init__.py b/rteval/modules/loads/__init__.py
index 66dad57..f4d4de4 100644
--- a/rteval/modules/loads/__init__.py
+++ b/rteval/modules/loads/__init__.py
@@ -27,9 +27,9 @@ import os
import time
import threading
import libxml2
-from Log import Log
-from rtevalConfig import rtevalCfgSection
-from modules import RtEvalModules, rtevalModulePrototype
+from rteval.Log import Log
+from rteval.rtevalConfig import rtevalCfgSection
+from rteval.modules import RtEvalModules, rtevalModulePrototype
class LoadThread(rtevalModulePrototype):
def __init__(self, name, config, logger=None):
diff --git a/rteval/modules/loads/hackbench.py b/rteval/modules/loads/hackbench.py
index cf09cfa..e040470 100644
--- a/rteval/modules/loads/hackbench.py
+++ b/rteval/modules/loads/hackbench.py
@@ -27,8 +27,8 @@
import sys, os, time, glob, subprocess, errno
from signal import SIGTERM, SIGKILL
-from modules.loads import CommandLineLoad
-from Log import Log
+from rteval.modules.loads import CommandLineLoad
+from rteval.Log import Log
class Hackbench(CommandLineLoad):
diff --git a/rteval/modules/loads/kcompile.py b/rteval/modules/loads/kcompile.py
index d0fb444..bcb033a 100644
--- a/rteval/modules/loads/kcompile.py
+++ b/rteval/modules/loads/kcompile.py
@@ -24,8 +24,8 @@
#
import sys, os, glob, subprocess
from signal import SIGTERM
-from modules.loads import CommandLineLoad
-from Log import Log
+from rteval.modules.loads import CommandLineLoad
+from rteval.Log import Log
kernel_prefix="linux-2.6"
diff --git a/rteval/modules/measurement/__init__.py b/rteval/modules/measurement/__init__.py
index df96dfb..a12eba2 100644
--- a/rteval/modules/measurement/__init__.py
+++ b/rteval/modules/measurement/__init__.py
@@ -23,7 +23,7 @@
#
import libxml2
-from modules import RtEvalModules, ModuleContainer
+from rteval.modules import RtEvalModules, ModuleContainer
class MeasurementProfile(RtEvalModules):
diff --git a/rteval/modules/measurement/cyclictest.py b/rteval/modules/measurement/cyclictest.py
index a04b52d..4be0238 100644
--- a/rteval/modules/measurement/cyclictest.py
+++ b/rteval/modules/measurement/cyclictest.py
@@ -26,8 +26,8 @@
#
import os, sys, subprocess, signal, libxml2
-from Log import Log
-from modules import rtevalModulePrototype
+from rteval.Log import Log
+from rteval.modules import rtevalModulePrototype
class RunData(object):
diff --git a/rteval/rteval.py b/rteval/rteval.py
deleted file mode 100644
index 9dbd608..0000000
--- a/rteval/rteval.py
+++ /dev/null
@@ -1,483 +0,0 @@
-#!/usr/bin/python -tt
-#
-# rteval - script for evaluating platform suitability for RT Linux
-#
-# This program is used to determine the suitability of
-# a system for use in a Real Time Linux environment.
-# It starts up various system loads and measures event
-# latency while the loads are running. A report is generated
-# to show the latencies encountered during the run.
-#
-# Copyright 2009,2010,2011,2012 Clark Williams <williams@redhat.com>
-# Copyright 2009,2010,2011,2012 David Sommerseth <davids@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-# For the avoidance of doubt the "preferred form" of this code is one which
-# is in an open unpatent encumbered format. Where cryptographic key signing
-# forms part of the process of creating an executable the information
-# including keys needed to generate an equivalently functional executable
-# are deemed to be part of the source code.
-#
-
-import sys
-import os
-import time
-import threading
-import optparse
-import tempfile
-import signal
-from datetime import datetime
-from distutils import sysconfig
-from Log import Log
-from sysinfo import SystemInfo
-from modules.loads import LoadModules
-from modules.measurement import MeasurementModules, MeasurementProfile
-from rtevalReport import rtevalReport
-from rtevalXMLRPC import rtevalXMLRPC
-
-# put local path at start of list to overide installed methods
-sys.path.insert(0, "./rteval")
-import rtevalConfig
-import rtevalMailer
-
-
-sigint_received = False
-def sigint_handler(signum, frame):
- global sigint_received
- sigint_received = True
- print "*** SIGINT received - stopping rteval run ***"
-
-def sigterm_handler(signum, frame):
- raise RuntimeError, "SIGTERM received!"
-
-class RtEval(rtevalReport):
- def __init__(self, cmdargs):
- self.__version = "2.0_pre"
- self.__workdir = os.getcwd()
- self.__reportdir = None
- self.__inifile = None
- self.__cmd_opts = {}
-
- default_config = {
- 'rteval': {
- 'quiet' : False,
- 'verbose' : False,
- 'keepdata' : True,
- 'debugging' : False,
- 'duration' : '60',
- 'sysreport' : False,
- 'reportdir' : None,
- 'reportfile' : None,
- 'installdir' : '/usr/share/rteval',
- 'srcdir' : '/usr/share/rteval/loadsource',
- 'xmlrpc' : None,
- 'xslt_report': '/usr/share/rteval/rteval_text.xsl',
- 'report_interval': '600',
- 'logging' : False,
- },
- 'loads' : {
- 'kcompile' : 'module',
- 'hackbench' : 'module',
- },
- 'kcompile' : {
- 'source' : 'linux-2.6.21.tar.bz2',
- 'jobspercore': '2',
- },
- 'hackbench' : {
- 'source' : 'hackbench.tar.bz2',
- 'jobspercore': '5',
- },
- 'cyclictest' : {
- 'interval' : '100',
- 'buckets' : '2000',
- 'distance' : '25',
- },
- 'measurement' : {
- 'cyclictest' : 'module',
- }
- }
-
- # Prepare logging
- self.__logger = Log()
- self.__logger.SetLogVerbosity(Log.INFO)
-
- # setup initial configuration
- self.__cfgobj = rtevalConfig.rtevalConfig(default_config, logger=self.__logger)
-
- # parse command line options
- self.__cfg = self.__cfgobj.GetSection('rteval')
- self.parse_options(cmdargs)
-
- # read in config file info
- self.__inifile = self.__cfgobj.Load(self.__cmd_opts.inifile)
-
- # copy the command line options into the rteval config section
- # (cmd line overrides config file values)
- self.__cfgobj.AppendConfig('rteval', vars(self.__cmd_opts))
- self.__cfg = self.__cfgobj.GetSection('rteval')
-
- # Update log level, based on config/command line args
- loglev = (not self.__cfg.quiet and (Log.ERR | Log.WARN)) \
- | (self.__cfg.verbose and Log.INFO) \
- | (self.__cfg.debugging and Log.DEBUG)
- self.__logger.SetLogVerbosity(loglev)
-
- self.__logger.log(Log.DEBUG, "workdir: %s" % self.__workdir)
-
- # prepare a mailer, if that's configured
- if self.__cfgobj.HasSection('smtp'):
- self.__mailer = rtevalMailer.rtevalMailer(self.__cfgobj.GetSection('smtp'))
- else:
- self.__mailer = None
-
- self._sysinfo = SystemInfo(self.__cfg, logger=self.__logger)
- self._loadmods = LoadModules(self.__cfgobj, logger=self.__logger)
- self._measuremods = MeasurementModules(self.__cfgobj, logger=self.__logger)
-
- if not self.__cfg.xslt_report.startswith(self.__cfg.installdir):
- self.__cfg.xslt_report = os.path.join(self.__cfg.installdir, "rteval_text.xsl")
-
- if not os.path.exists(self.__cfg.xslt_report):
- raise RuntimeError, "can't find XSL template (%s)!" % self.__cfg.xslt_report
-
- # Add rteval directory into module search path
- sys.path.insert(0, '%s/rteval' % sysconfig.get_python_lib())
-
- # Initialise the report module
- rtevalReport.__init__(self, self.__version, self.__cfg.installdir, self.__cmd_opts.annotate)
-
- # If --xmlrpc-submit is given, check that we can access the server
- if self.__cfg.xmlrpc:
- self.__xmlrpc = rtevalXMLRPC(self.__cfg.xmlrpc, self.__logger, self.__mailer)
- if not self.__xmlrpc.Ping():
- if not self.__cmd_opts.xmlrpc_noabort:
- print "ERROR: Could not reach XML-RPC server '%s'. Aborting." % self.__cfg.xmlrpc
- sys.exit(2)
- else:
- print "WARNING: Could not ping the XML-RPC server. Will continue anyway."
- else:
- self.__xmlrpc = None
-
-
- def parse_options(self, cmdargs):
- '''parse the command line arguments'''
- parser = optparse.OptionParser()
- parser.add_option("-d", "--duration", dest="duration",
- type="string", default=self.__cfg.duration,
- help="specify length of test run (default: %default)")
- parser.add_option("-v", "--verbose", dest="verbose",
- action="store_true", default=self.__cfg.verbose,
- help="turn on verbose prints (default: %default)")
- parser.add_option("-w", "--workdir", dest="workdir",
- type="string", default=self.__workdir,
- help="top directory for rteval data (default: %default)")
- parser.add_option("-l", "--loaddir", dest="srcdir",
- type="string", default=self.__cfg.srcdir,
- help="directory for load source tarballs (default: %default)")
- parser.add_option("-i", "--installdir", dest="installdir",
- type="string", default=self.__cfg.installdir,
- help="place to locate installed templates (default: %default)")
- parser.add_option("-s", "--sysreport", dest="sysreport",
- action="store_true", default=self.__cfg.sysreport,
- help='run sysreport to collect system data (default: %default)')
- parser.add_option("-D", '--debug', dest='debugging',
- action='store_true', default=self.__cfg.debugging,
- help='turn on debug prints (default: %default)')
- parser.add_option("-X", '--xmlrpc-submit', dest='xmlrpc',
- action='store', default=self.__cfg.xmlrpc, metavar='HOST',
- help='Hostname to XML-RPC server to submit reports')
- parser.add_option("-P", "--xmlrpc-no-abort", dest="xmlrpc_noabort",
- action='store_true', default=False,
- help="Do not abort if XML-RPC server do not respond to ping request");
- parser.add_option("-Z", '--summarize', dest='summarize',
- action='store_true', default=False,
- help='summarize an already existing XML report')
- parser.add_option("-H", '--raw-histogram', dest='rawhistogram',
- action='store_true', default=False,
- help='Generate raw histogram data for an already existing XML report')
- parser.add_option("-f", "--inifile", dest="inifile",
- type='string', default=None,
- help="initialization file for configuring loads and behavior")
- parser.add_option("-a", "--annotate", dest="annotate",
- type="string", default=None,
- help="Add a little annotation which is stored in the report")
- parser.add_option("-L", "--logging", dest="logging",
- action='store_true', default=False,
- help='log the output of the loads in the report directory')
-
- parser.add_option("-O", "--onlyload", dest="onlyload",
- action='store_true', default=False,
- help="only run the loads (don't run measurement threads)")
-
- (self.__cmd_opts, self.__cmd_args) = parser.parse_args(args = cmdargs)
- if self.__cmd_opts.duration:
- mult = 1.0
- v = self.__cmd_opts.duration.lower()
- if v.endswith('s'):
- v = v[:-1]
- elif v.endswith('m'):
- v = v[:-1]
- mult = 60.0
- elif v.endswith('h'):
- v = v[:-1]
- mult = 3600.0
- elif v.endswith('d'):
- v = v[:-1]
- mult = 3600.0 * 24.0
- self.__cmd_opts.duration = float(v) * mult
- self.__workdir = os.path.abspath(self.__cmd_opts.workdir)
-
-
- def show_remaining_time(self, remaining):
- r = int(remaining)
- days = r / 86400
- if days: r = r - (days * 86400)
- hours = r / 3600
- if hours: r = r - (hours * 3600)
- minutes = r / 60
- if minutes: r = r - (minutes * 60)
- print "rteval time remaining: %d days, %d hours, %d minutes, %d seconds" % (days, hours, minutes, r)
-
-
- def prepare(self, onlyload = False):
- builddir = os.path.join(self.__workdir, 'rteval-build')
- if not os.path.isdir(builddir): os.mkdir(builddir)
-
- # create our report directory
- try:
- # Only create a report dir if we're doing measurements
- # or the loads logging is enabled
- if not onlyload or self.__cfg.logging:
- self.__reportdir = self._make_report_dir(self.__workdir, "summary.xml")
- except Exception, e:
- raise RuntimeError("Cannot create report directory (NFS with rootsquash on?) [%s]", str(e))
-
- self.__logger.log(Log.INFO, "Preparing load modules")
- params = {'workdir':self.__workdir,
- 'reportdir':self.__reportdir,
- 'builddir':builddir,
- 'srcdir':self.__cfg.srcdir,
- 'verbose': self.__cfg.verbose,
- 'debugging': self.__cfg.debugging,
- 'numcores':self._sysinfo.cpu_getCores(True),
- 'logging':self.__cfg.logging,
- 'memsize':self._sysinfo.mem_get_size(),
- 'numanodes':self._sysinfo.mem_get_numa_nodes(),
- 'duration':self.__cfg.duration,
- }
- self._loadmods.Setup(params)
-
- self.__logger.log(Log.INFO, "Preparing measurement modules")
- self._measuremods.Setup(params)
-
-
- def measure(self, measure_profile):
- if not isinstance(measure_profile, MeasurementProfile):
- raise Exception("measure_profile is not an MeasurementProfile object")
-
- measure_start = None
- (with_loads, run_parallel) = measure_profile.GetProfile()
- self.__logger.log(Log.INFO, "Using measurement profile [loads: %s parallel: %s]" % (
- with_loads, run_parallel))
- try:
- nthreads = 0
-
- # start the loads
- if with_loads:
- self._loadmods.Start()
-
- print "rteval run on %s started at %s" % (os.uname()[2], time.asctime())
- print "started %d loads on %d cores" % (self._loadmods.ModulesLoaded(), self._sysinfo.cpu_getCores(True)),
- if self._sysinfo.mem_get_numa_nodes() > 1:
- print " with %d numa nodes" % self._sysinfo.mem_get_numa_nodes()
- else:
- print ""
- print "Run duration: %d seconds" % self.__cfg.duration
-
- # start the cyclictest thread
- measure_profile.Start()
-
-
- # Uleash the loads and measurement threads
- report_interval = int(self.__cfg.report_interval)
- nthreads = with_loads and self._loadmods.Unleash() or None
- measure_profile.Unleash()
- measure_start = datetime.now()
-
- # wait for time to expire or thread to die
- signal.signal(signal.SIGINT, sigint_handler)
- signal.signal(signal.SIGTERM, sigterm_handler)
- self.__logger.log(Log.INFO, "waiting for duration (%f)" % self.__cfg.duration)
- stoptime = (time.time() + self.__cfg.duration)
- currtime = time.time()
- rpttime = currtime + report_interval
- load_avg_checked = 5
- while (currtime <= stoptime) and not sigint_received:
- time.sleep(1.0)
- if not measure_profile.isAlive():
- stoptime = currtime
- self.__logger.log(Log.WARN,
- "Measurement threads did not use the full time slot. Doing a controlled stop.")
-
- if with_loads:
- if len(threading.enumerate()) < nthreads:
- raise RuntimeError, "load thread died!"
-
- if not load_avg_checked:
- self._loadmods.SaveLoadAvg()
- load_avg_checked = 5
- else:
- load_avg_checked -= 1
-
- if currtime >= rpttime:
- left_to_run = stoptime - currtime
- self.show_remaining_time(left_to_run)
- rpttime = currtime + report_interval
- print "load average: %.2f" % self._loadmods.GetLoadAvg()
- currtime = time.time()
-
- self.__logger.log(Log.DEBUG, "out of measurement loop")
- signal.signal(signal.SIGINT, signal.SIG_DFL)
- signal.signal(signal.SIGTERM, signal.SIG_DFL)
-
- except RuntimeError, e:
- print "Runtime error during measurement: %s", e
- raise
-
- finally:
- # stop measurement threads
- measure_profile.Stop()
-
- # stop the loads
- if with_loads:
- self._loadmods.Stop()
-
- print "stopping run at %s" % time.asctime()
-
- # wait for measurement modules to finish calculating stats
- measure_profile.WaitForCompletion()
-
- return measure_start
-
-
- def summarize(self, file):
- isarchive = False
- summary = file
- if file.endswith(".tar.bz2"):
- import tarfile
- try:
- t = tarfile.open(file)
- except:
- print "Don't know how to summarize %s (tarfile open failed)" % file
- return
- element = None
- for f in t.getnames():
- if f.find('summary.xml') != -1:
- element = f
- break
- if element == None:
- print "No summary.xml found in tar archive %s" % file
- return
- tmp = tempfile.gettempdir()
- self.__logger.log(Log.DEBUG, "extracting %s from %s for summarizing" % (element, file))
- t.extract(element, path=tmp)
- summary = os.path.join(tmp, element)
- isarchive = True
- self._show_report(summary, 'rteval_text.xsl')
- if isarchive:
- os.unlink(summary)
-
- def rteval(self):
- ''' main function for rteval'''
- retval = 0;
-
- # if --summarize was specified then just parse the XML, print it and exit
- if self.__cmd_opts.summarize or self.__cmd_opts.rawhistogram:
- if len(self.cmd_arguments) < 1:
- raise RuntimeError, "Must specify at least one XML file with --summarize!"
-
- for x in self.__cmd_args:
- if self.__cmd_opts.summarize:
- self.summarize(x)
- elif self.__cmd_opts.rawhistogram:
- self._show_report(x, 'rteval_histogram_raw.xsl')
-
- sys.exit(0)
-
- if os.getuid() != 0:
- print "Must be root to run rteval!"
- sys.exit(-1)
-
- self.__logger.log(Log.DEBUG, '''rteval options:
- workdir: %s
- loaddir: %s
- reportdir: %s
- verbose: %s
- debugging: %s
- logging: %s
- duration: %f
- sysreport: %s
- inifile: %s''' % (self.__workdir, self.__cfg.srcdir, self.__reportdir, self.__cfg.verbose,
- self.__cfg.debugging, self.__cfg.logging, self.__cfg.duration,
- self.__cfg.sysreport, self.__inifile))
-
- if not os.path.isdir(self.__workdir):
- raise RuntimeError, "work directory %d does not exist" % self.__workdir
-
- self.prepare(self.__cmd_opts.onlyload)
-
- if self.__cmd_opts.onlyload:
- # If --onlyload were given, just kick off the loads and nothing more
- # No reports will be created.
- self._loadmods.Start()
- nthreads = self._loadmods.Unleash()
- self.__logger.log(Log.INFO, "Started %i load threads - will run for %f seconds" % (
- nthreads, self.__cfg.duration))
- self.__logger.log(Log.INFO, "No measurements will be performed, due to the --onlyload option")
- time.sleep(self.__cfg.duration)
- self._loadmods.Stop()
- retval = 0
- else:
- # ... otherwise, run the full measurement suite with reports
- measure_start = None
- for meas_prf in self._measuremods:
- mstart = self.measure(meas_prf)
- if measure_start is None:
- measure_start = mstart
- self._report(measure_start, self.__cfg.xslt_report)
- if self.__cfg.sysreport:
- self._sysinfo.run_sysreport(self.__reportdir)
-
- # if --xmlrpc-submit | -X was given, send our report to this host
- if self.__xmlrpc:
- retval = self.__xmlrpc.SendReport(self._XMLreport())
-
- self._sysinfo.copy_dmesg(self.__reportdir)
- self._tar_results()
-
- self.__logger.log(Log.DEBUG, "exiting with exit code: %d" % retval)
-
- return retval
-
-if __name__ == '__main__':
- import pwd, grp
-
- try:
- rteval = RtEval(sys.argv[1:])
- ec = rteval.rteval()
- sys.exit(ec)
- except KeyboardInterrupt:
- sys.exit(0)
diff --git a/rteval/sysinfo/__init__.py b/rteval/sysinfo/__init__.py
index 05a6042..0523c9f 100644
--- a/rteval/sysinfo/__init__.py
+++ b/rteval/sysinfo/__init__.py
@@ -25,7 +25,7 @@
#
import sys, libxml2
-from Log import Log
+from rteval.Log import Log
from glob import glob
from kernel import KernelInfo
from services import SystemServices
diff --git a/rteval/sysinfo/dmi.py b/rteval/sysinfo/dmi.py
index d4079ad..2ab690f 100644
--- a/rteval/sysinfo/dmi.py
+++ b/rteval/sysinfo/dmi.py
@@ -27,7 +27,7 @@
import sys, os
import libxml2, libxslt
-from Log import Log
+from rteval.Log import Log
try:
import dmidecode
diff --git a/rteval/sysinfo/kernel.py b/rteval/sysinfo/kernel.py
index aa482eb..0db69cc 100644
--- a/rteval/sysinfo/kernel.py
+++ b/rteval/sysinfo/kernel.py
@@ -27,8 +27,8 @@
#
import sys, subprocess, os, libxml2
-from sysinfo.tools import getcmdpath
-from Log import Log
+from rteval.sysinfo.tools import getcmdpath
+from rteval.Log import Log
class KernelInfo(object):
diff --git a/rteval/sysinfo/osinfo.py b/rteval/sysinfo/osinfo.py
index 271ada2..7eb271a 100644
--- a/rteval/sysinfo/osinfo.py
+++ b/rteval/sysinfo/osinfo.py
@@ -27,7 +27,7 @@
import os, shutil, subprocess, libxml2
from glob import glob
-from Log import Log
+from rteval.Log import Log
class OSInfo(object):
def __init__(self, logger):
diff --git a/rteval/sysinfo/services.py b/rteval/sysinfo/services.py
index f38086e..de0dd7b 100644
--- a/rteval/sysinfo/services.py
+++ b/rteval/sysinfo/services.py
@@ -27,8 +27,8 @@
#
import sys, subprocess, os, glob, fnmatch, libxml2
-from sysinfo.tools import getcmdpath
-from Log import Log
+from rteval.sysinfo.tools import getcmdpath
+from rteval.Log import Log
class SystemServices(object):