00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 __facility__ = "Online"
00011 __abstract__ = "Script engine application base class"
00012 __author__ = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__ = "2005/07/15 00:08:27"
00014 __updated__ = "$Date: 2006/04/26 21:52:26 $"
00015 __version__ = "$Revision: 1.26 $"
00016 __release__ = "$Name: HEAD $"
00017 __credits__ = "SLAC"
00018
00019 import LICOS.copyright_SLAC
00020
00021 import logging as log
00022 import os
00023 import time
00024 import types
00025
00026
00027 from threading import Thread, Event, Lock
00028
00029 from LICOS.scriptEngine.FSM import FSM
00030 from LICOS.scriptEngine.ScriptEngineCommon import ScriptEngineCommon
00031 from LICOS.scriptEngine import rcUtil, rcActivitiesInterface
00032
00033 from LICOS.scriptEngine import rcReportGen, rcRunIdHelper, rcComplStatus
00034 from LICOS.lib.testReport import rcTestReport
00035
00036 from LICOS.util import FreeSpace
00037 from LICOS.core.export import DataExport
00038 from LICOS.util.gVersions import Gversions, ROOT_DIRS
00039 from LICOS.lib.LATconstants import *
00040
00041
00042 class seAppBase(FSM):
00043 """!\brief Base class for all script engine tests.
00044
00045 All script engine test applications should inherit from this base class.
00046 This class in turn inherits from the FSM (a finite state machine) class
00047 which allows the scriptRun method to go through its transitions and
00048 execute the test.
00049
00050 getRunSequence() method notifies the script engine as to the order of the
00051 transitions to be processed. If any of the transition methods return a
00052 value other than None, then the transition is rejected and the test is
00053 put back to the RESET state.
00054
00055
00056 """
00057 def __init__(self, common):
00058 """!\brief seAppBase constructor.
00059
00060 \param common ScriptEngineCommon instance.
00061 Contains session helper methods such as
00062 preferences, command line options, etc.
00063
00064 """
00065 FSM.__init__(self, 'RESET', None)
00066 self.set_default_transition( self.error, None )
00067 self.add_transition ('SETUP', 'RESET', self.seSetup, 'STOPPED')
00068 self.add_transition ('START_RUN', 'STOPPED', self.seStartRun,'RUNNING')
00069 self.add_transition ('RUN', 'RUNNING', self.seRun, 'COMPLETE')
00070 self.add_transition ('STOP_RUN', 'COMPLETE', self.seStopRun, 'STOPPED')
00071 self.add_transition ('ABORT', 'RUNNING', self.seAbort, 'STOPPED')
00072 self.add_transition ('TEARDOWN', 'STOPPED', self.seTeardown,'RESET')
00073
00074 self.common = common
00075 self.gui = common.getGUI()
00076 self.rc = self.gui
00077 self.__stopRequested = False
00078
00079
00080 self.__complStatus = rcComplStatus.rcCompletionStatus()
00081
00082 self.prefs = self.common.preferences()
00083 self.vsc = self.common.getCmdCli()
00084
00085 self.__archiver = None
00086 self.__archiverClass = None
00087 self.__errArchiver = None
00088 self.__badArchiver = None
00089
00090 self.__pauseCount = 0
00091 self.__pausedTime = 0
00092 self.__pauseTime = None
00093 self.__startTime = None
00094 self.__strobeTime = None
00095 self.__elapsedTime = 0
00096 self.__endTime = None
00097 self.__excludedTime = 0
00098 self.__excludeTime = 0
00099 self.__excluding = False
00100 self.__excludeLock = Lock()
00101 self.__tstamp = None
00102 self.__tstampRaw = None
00103 self.__statMon = None
00104
00105
00106 self.__parentSuite = None
00107
00108
00109 self.__lastSuiteScript = False
00110
00111 self.__EBFplayback = self.common.options().playback is not None
00112
00113 self.__runIdHelper = rcRunIdHelper.rcRunIdHelper(self.prefs)
00114 self.runId = 'N/A'
00115
00116 self.__customReportData = {}
00117
00118 if self.gui is not None:
00119 self.__paramVerifier = self.gui.parameterVerifier()
00120
00121 self.__verifyStatus = None
00122
00123 self.__appStatus = None
00124 self.__completionStatus = self.COMPL_STATUS_UNDEFINED
00125
00126 self.__dds = self.common.getDDS()
00127
00128
00129 self.__runMsgLogFile = None
00130
00131
00132 self.__runLcmLogFile = None
00133
00134
00135 self.__additionalInputFiles = []
00136
00137 if self.gui is not None and 'statusMonitor' in dir(self.gui):
00138 statMon = self.gui.statusMonitor()
00139 statMon.addWatchItem('Run Id', self.getRunId)
00140 statMon.addWatchItem('Test Name', self.getName)
00141 statMon.addWatchItem('Suite Name', self.getParentSuiteName)
00142 statMon.addWatchItem('Additional Input Files', self.getAddlInputFiles)
00143 statMon.addWatchItem('Completion Status', self.getCompletionStatusStr,
00144 alarmExpr="@!='" + self.__complStatus.getCompletionStatusStr(self.COMPL_STATUS_PASSED)+"'")
00145 statMon.addWatchItem('Start Time', self.getStartTime)
00146 statMon.addWatchItem('End Time', self.getEndTime)
00147 statMon.addWatchItem('Elapsed Time', self.getElapsedTime)
00148 statMon.addWatchItem('Run Message Log File', self.getRunMsgLogFile)
00149 statMon.addWatchItem('Error Log Count', self.common.getErrorLogCount, alarmExpr='!=0')
00150 statMon.addWatchItem('Free Disk Space', FreeSpace.FreeSpace,
00151 alarmExpr='<'+ str(self.DISKSPACE_WARN_LEVEL))
00152 statMon.addWatchItem('Suite State', self.getSuiteState)
00153 statMon.addWatchItem('Application State', self.getAppState)
00154 self.__statMon = statMon
00155
00156 self.__reportGen = rcReportGen.rcReportGen(os.path.join(self.prefs["reportdir"], 'rcReport.out'), self.prefs)
00157 try:
00158 if self.prefs is not None and int(self.prefs["elognbl"]):
00159 self.__activitiesObj = rcActivitiesInterface.ActivitiesInterface()
00160 else:
00161 self.__activitiesObj = None
00162 except:
00163 self.__activitiesObj = None
00164
00165
00166 def getRunSequence(self):
00167 """!\brief Return the order of transitions for this type of application.
00168
00169 \return A list of transitions
00170 """
00171 return ['SETUP','START_RUN','RUN','STOP_RUN','TEARDOWN']
00172
00173 def doAbort(self):
00174 """!\brief Request an abort.
00175
00176 """
00177 if self.get_transition("ABORT", self.current_state)[1] is not None:
00178 return self.process("ABORT")
00179
00180 def doReset(self):
00181 """!\brief Reset the application.
00182
00183 """
00184 curState = self.current_state
00185 if curState == "STOPPED":
00186 self.process("TEARDOWN")
00187 elif curState in ("RUNNING", "PAUSED"):
00188 status = self.doAbort()
00189 if status is None:
00190 self.process("TEARDOWN")
00191 self.__updateGUI()
00192
00193 def error(self):
00194 """!\brief This method is called when an undefined transition is requested.
00195
00196 """
00197 log.error(self.__class__.__name__ + " - Undefined transition: %s from state %s" % \
00198 (self.input_symbol, self.current_state))
00199
00200 def getSuiteState(self):
00201 suite = self.getParentSuite()
00202 if suite is not None:
00203 return suite.current_state
00204
00205 def getAppState(self):
00206 return self.current_state
00207
00208 def getActivitiesObject(self):
00209 """\brief Return the activities table interface
00210
00211 """
00212 return self.__activitiesObj
00213
00214 def seSetup(self):
00215 """!\brief SETUP transition method.
00216
00217 Calls self.setup()
00218
00219 \return None for success or
00220 False if there is an exception or
00221 status returned by the user script.
00222 """
00223 fn = "seAppBase.seSetup"
00224 log.debug("%s -- %s" % (fn, self.getName()))
00225 self.__additionalInputFiles = []
00226 self.__updateGUI()
00227
00228
00229 try:
00230 status = self.setup()
00231 if status is not None: return status
00232 except:
00233 log.exception("Exception in " + self.getName() + ".setup()")
00234 self.__appStatus = self.COMPL_STATUS_FAILED
00235 return False
00236
00237 def seTeardown(self):
00238 """!\brief TEARDOWN transition method.
00239
00240 Calls self.teardown()
00241
00242 \return None for success or status returned by the user script.
00243 """
00244 fn = "seAppBase.seTeardown"
00245 log.debug("%s -- %s" % (fn, self.getName()))
00246 self.__updateGUI()
00247
00248
00249 self.__strobeTime = None
00250
00251
00252 try:
00253 status = self.teardown()
00254 except:
00255 log.exception("Exception in " + self.getName() + ".teardown()")
00256 self.__appStatus = self.COMPL_STATUS_FAILED
00257 status = None
00258 return status
00259
00260 def seStartRun(self):
00261 """!\brief START_RUN transition method
00262
00263 Calls self.startRun()
00264
00265 \return None for success or
00266 False if there is an exception or
00267 status returned by the user script.
00268 """
00269 fn = "seAppBase.seStartRun"
00270 log.debug("%s -- %s" % (fn, self.getName()))
00271
00272 self.__updateGUI()
00273 self.__stopRequested = False
00274
00275 self.__appStatus = None
00276 self.__completionStatus = self.COMPL_STATUS_UNDEFINED
00277 self.__endTime = None
00278 self.__startTime = None
00279 self.__excludeLock.acquire()
00280 self.__elapsedTime = 0
00281 self.__strobeTime = None
00282 self.__excluding = False
00283 self.__excludedTime = 0
00284 self.__excludeLock.release()
00285 self.__pauseCount = 0
00286 self.__pausedTime = 0
00287
00288 self.common.resetErrorLogCount()
00289
00290
00291
00292
00293 while True:
00294
00295
00296
00297 self.__tstampRaw = time.gmtime()
00298 self.__tstamp = time.strftime('%y%m%d%H%M%S', self.__tstampRaw)
00299 if self.prefs.has_key("logfilename"):
00300 logfilename = self.prefs["logfilename"]
00301 if self.__tstamp != logfilename[3:15]:
00302 break
00303 else:
00304 break
00305 time.sleep(1)
00306
00307 self.__exportedFiles = []
00308
00309
00310 self.__testReport = None
00311
00312 self.__testReportFile = None
00313
00314 self.runId = self.__runIdHelper.nextRunId()
00315
00316
00317 if int(self.prefs["dataexport"]) == 1:
00318 exportDir = os.path.abspath(self.prefs["exportdir"])
00319 if not os.path.exists(exportDir):
00320 msg = "Export directory %s does not exist. Aborting run" % exportDir
00321 log.fatal(msg)
00322 return False
00323
00324 if self.gui is not None:
00325 self.__paramVerifier.initialize(self.getName())
00326 self.__verifyStatus = None
00327
00328 now = time.time()
00329
00330
00331 self.__endTime = None
00332 self.__startTime = now
00333 self.__excludeLock.acquire()
00334 self.__elapsedTime = 0
00335 self.__strobeTime = now
00336 self.__excludeLock.release()
00337
00338 try:
00339 if self.prefs is not None and int(self.prefs["elognbl"]):
00340 self.__genActivity()
00341 except Exception, e:
00342 log.exception(e)
00343 raise
00344
00345
00346
00347 try:
00348 status = self.startRun()
00349 except:
00350 log.exception("Exception in " + self.getName() + ".startRun()")
00351 self.__appStatus = self.COMPL_STATUS_FAILED
00352 status = False
00353
00354 if self.common.options().securedir is not None or self.common.options().paramverify is not None:
00355
00356
00357
00358 scriptSeq = 0
00359 if self.isRunFromSuite():
00360 suiteInst = self.getParentSuite()
00361 if suiteInst.getParentSuite() is None:
00362 scriptSeq = suiteInst.getScriptSequence()
00363 else:
00364 scriptSeq = -1
00365 if scriptSeq == 0 or scriptSeq == 1:
00366 self.__paramVerifier.add("Particle Type", self.prefs["lastparticletype"])
00367 self.__paramVerifier.add("Orientation", self.prefs["lastorientation"])
00368 self.__paramVerifier.add("Site", self.prefs["lastsite"])
00369 self.__paramVerifier.add("Instrument Type", self.prefs["lastinstrumenttype"])
00370 self.__paramVerifier.add("Phase", self.prefs["lastphase"])
00371 self.__verifyStatus = self.gui.execGUImethod(self.__paramVerifier.verify)
00372 if self.__verifyStatus == 1:
00373 pass
00374 elif self.__verifyStatus == 0:
00375 log.error("Parameter verification failed because the user cancelled verification.")
00376 return self.__verifyStatus
00377 else:
00378 if self.__verifyStatus is None: self.__verifyStatus = -1
00379 log.error("Parameter verification failed due to an unknown reason.")
00380 return self.__verifyStatus
00381
00382 self.__runMsgLogFile = None
00383 self.__runLcmLogFile = None
00384 if int(self.prefs["lognbl"]) == 1:
00385 self.__runMsgLogFile = os.path.join(self.prefs["logdir"],
00386 "msg" + self.__tstamp + ".log")
00387 self.__runMsgLog = self.common.startRunLog(self.__runMsgLogFile)
00388 self.__runLcmLogFile = os.path.join(self.prefs["logdir"],
00389 "lcm" + self.__tstamp + ".log")
00390 self.common.startLcmLog(self.__runLcmLogFile)
00391 log.info("""%s -- Test %s starting run, Operator: %s,"""
00392 """ Run Id: %s, Time Started: %s -- %s"""
00393 % (fn, self.getName(), self.prefs["operator"],
00394 self.runId, self.__tstamp, time.asctime(self.__tstampRaw)))
00395 return status
00396
00397 def seRun(self):
00398 """!\brief RUN transition method
00399
00400 Calls self.run()
00401
00402 \return None for success or
00403 False if there is an exception or
00404 status returned by the user script.
00405 """
00406 fn = "seAppBase.seStartRun"
00407 log.debug("%s -- %s" % (fn, self.getName()))
00408
00409 self.__updateGUI()
00410 try:
00411 status = self.__jacket(self.run)
00412 except:
00413 log.exception("Exception in " + self.getName() + ".startRun()")
00414 self.__appStatus = self.COMPL_STATUS_FAILED
00415 self.doReset()
00416 status = False
00417 return status
00418
00419 def seStopRun(self):
00420 """!\brief STOP_RUN transition method
00421
00422 Calls self.stopRun()
00423 """
00424 fn = "seAppBase.seStopRun"
00425 log.debug("%s -- %s" % (fn, self.getName()))
00426 self.__updateGUI()
00427
00428
00429 try:
00430 self.stopRun()
00431 except:
00432 log.exception("Exception in " + self.getName() + ".stopRun()")
00433 self.__appStatus = self.COMPL_STATUS_FAILED
00434
00435
00436 self.__stopRun()
00437 self.__logEndRun(fn)
00438 curDir = os.getcwd()
00439 self.__exportData()
00440 if os.path.exists(curDir):
00441 os.chdir(curDir)
00442
00443 def seAbort(self):
00444 """!\brief ABORT transition method
00445
00446 Calls self.abort()
00447
00448 \return None for success or
00449 False if there is an exception or
00450 status returned by the user script.
00451 """
00452 fn = "seAppBase.seAbort"
00453 log.debug("%s -- %s" % (fn, self.getName()))
00454
00455 self.__updateGUI()
00456 self.setStopRequested()
00457 return self.abort()
00458
00459 def __updateGUI(self):
00460 """!\brief Update the state of the run on the GUI.
00461
00462 \param transition Transition string such as ('SETUP', 'START_RUN')
00463 """
00464 if self.gui is not None:
00465 self.gui.execGUImethodNR(self.gui.updateState, self.current_state)
00466
00467 def setup(self):
00468 """ \brief Transition method to be overridden by the user script.
00469
00470 """
00471 pass
00472
00473 def teardown(self):
00474 """ \brief Transition method to be overridden by the user script.
00475
00476 """
00477 pass
00478
00479 def startRun(self):
00480 """ \brief Transition method to be overridden by the user script.
00481
00482 """
00483 pass
00484
00485 def run(self):
00486 """ \brief Transition method to be overridden by the user script.
00487
00488 """
00489 pass
00490
00491 def stopRun(self):
00492 """ \brief Transition method to be overridden by the user script.
00493
00494 """
00495 pass
00496
00497 def abort(self):
00498 """ \brief Transition method to be overridden by the user script.
00499
00500 """
00501 pass
00502
00503 def getName(self):
00504 """!\brief Return the test application name
00505
00506 \return Test application class name.
00507 """
00508 return self.__class__.__name__
00509
00510 def getType(self):
00511 """!\brief Return the type of test based on this class.
00512
00513 Used for logging purposes.
00514
00515 \return Test type
00516 """
00517 return 'Application'
00518
00519 def isStopRequested(self):
00520 """!\brief Return stop requested flag.
00521
00522 \return Stop requested flag.
00523 """
00524 return self.__stopRequested
00525
00526 def setStopRequested(self):
00527 """!\brief Signal stop requested.
00528
00529 """
00530 self.__stopRequested = True
00531
00532 def setParentSuite(self, suite):
00533 """!\brief Set the parent suite instance.
00534
00535 \param Suite instance
00536 """
00537 self.__parentSuite = suite
00538
00539 def getParentSuite(self):
00540 """!\brief Retrieve the parent suite instance.
00541
00542 \return Suite instance
00543 """
00544 return self.__parentSuite
00545
00546 def getParentSuiteName(self):
00547 """!\brief Retrieve the parent suite name.
00548
00549 \return Parent suite name.
00550 """
00551 if self.__parentSuite is not None:
00552 return self.__parentSuite.getName()
00553
00554 def isRunFromSuite(self):
00555 """!\brief Check if the script is being run from a suite.
00556
00557 \return True If the script is being run from a suite,
00558 False otherwise.
00559 """
00560 return self.__parentSuite is not None
00561
00562 def setLastSuiteScript(self):
00563 """!\brief Set the script as the last one in the suite.
00564
00565 Called from rcSuite.
00566 """
00567 self.__lastSuiteScript = True
00568
00569 def isLastSuiteScript(self):
00570 """!\brief Check if the script is the last one.
00571
00572 """
00573 return self.__lastSuiteScript
00574
00575 class StandaloneLauncher(object):
00576 """!\brief Class to facilitate the running of the test scripts and
00577 suites in standalone mode.
00578
00579 This class allows all tests to be run unmodified in standalone
00580 mode. Everything is available in standalone mode except the
00581 GUI object (ie. self.common().getGUI() will be None)
00582
00583 See testRunner
00584 """
00585 def __init__(self, logLevel="INFO"):
00586 """!\brief StandaloneLauncher constructor.
00587
00588 \param logLevel Initial loglevel.
00589 """
00590 self.__common = ScriptEngineCommon()
00591 self.__common.initialize()
00592 self.__common.setLoggingLevel(logLevel)
00593 self.__common.connect()
00594
00595 def launch(self, appClass, callbacks=None):
00596 """!\brief Launch the specified application script.
00597
00598 This method launches the application passed in \a appClass or
00599 if the \a appClass parameter is None the application whose name
00600 is passed as a parameter in the --app command line argument.
00601
00602 \param appClass Application instance.
00603 \param callbacks Not implemented yet.
00604
00605 \return Result from rcUtil.scriptRun()
00606 """
00607 if appClass is None:
00608 module = self.__common.options().app
00609 (module, filename, app) = rcUtil.scriptLoad(module, self.__common)
00610 if app is None:
00611 return
00612 else:
00613 return rcUtil.scriptRun(app)
00614 else:
00615 app= appClass(self.__common)
00616 return rcUtil.scriptRun(app)
00617
00618 def getCommon(self):
00619 """!\brief Accessor method for the testRunner script
00620
00621 \return ScriptEngineCommon instance
00622 """
00623 return self.__common
00624
00625
00626 COMPL_STATUS_UNDEFINED = \
00627 rcComplStatus.rcCompletionStatus.getCompletionStatus('UNDEFINED')
00628 COMPL_STATUS_ABORTED = \
00629 rcComplStatus.rcCompletionStatus.getCompletionStatus('ABORTED')
00630 COMPL_STATUS_ERROR = \
00631 rcComplStatus.rcCompletionStatus.getCompletionStatus('FAILED')
00632 COMPL_STATUS_SUCCESS = \
00633 rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED')
00634 COMPL_STATUS_FAILED = \
00635 rcComplStatus.rcCompletionStatus.getCompletionStatus('FAILED')
00636 COMPL_STATUS_PASSED = \
00637 rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED')
00638 COMPL_STATUS_PASSED_CONDITIONALLY = \
00639 rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED CONDITIONALLY')
00640 COMPL_STATUS_MARGINAL = \
00641 rcComplStatus.rcCompletionStatus.getCompletionStatus('MARGINAL')
00642 COMPL_STATUS_OPERATOR_ABORTED = \
00643 rcComplStatus.rcCompletionStatus.getCompletionStatus('OPERATOR ABORTED')
00644 COMPL_STATUS_INCOMPLETE = \
00645 rcComplStatus.rcCompletionStatus.getCompletionStatus('INCOMPLETE')
00646
00647
00648 DISKSPACE_WARN_LEVEL = (200*1024*1024)
00649
00650 def getRunId(self):
00651 """!\brief Return the run id
00652
00653 \return Run id
00654 """
00655 return self.runId
00656
00657 def getAddlInputFiles(self):
00658 """!\brief Return a list containing the additional input files.
00659
00660 """
00661 return self.__additionalInputFiles
00662
00663 def addAddlInputFile(self, filename):
00664 """!\brief Add an additional input file.
00665
00666 \param filename Name of the file
00667 """
00668 self.addExportedFile(filename, delete=False)
00669 self.__additionalInputFiles.append(os.path.split(filename)[1])
00670
00671
00672 def getRunMsgLogFile(self):
00673 """!\brief Return the filename for the message log for the current run.
00674
00675 \return Message log filename.
00676 """
00677 if self.__runMsgLogFile is not None:
00678 return os.path.basename(self.__runMsgLogFile)
00679 else:
00680 return None
00681
00682 def getRunLcmLogFile(self):
00683 """!\brief Return the filename for the LCM log for the current run.
00684
00685 \return LCM log filename.
00686 """
00687 if self.__runLcmLogFile is not None:
00688 return os.path.basename(self.__runLcmLogFile)
00689 else:
00690 return None
00691
00692 def getElapsedTime(self):
00693 """!\brief Return the elapsed time spent during test execution.
00694
00695 \return Elapsed time.
00696 """
00697 return "%.2f seconds" % self.__getElapsedTime()
00698
00699 def __getElapsedTime(self):
00700 """!\brief Return the elapsed time (raw)
00701
00702 \return Elapsed time.
00703 """
00704 self.__excludeLock.acquire()
00705 if self.__strobeTime is not None and \
00706 self.__pauseTime is None and not self.__excluding:
00707 elapsedTime = self.__elapsedTime + (time.time() - self.__strobeTime)
00708 else:
00709 elapsedTime = self.__elapsedTime
00710 self.__excludeLock.release()
00711 return elapsedTime
00712
00713 def getStartTime(self):
00714 """!\brief Return the test start time
00715
00716 \return Test start time in GMT as a tuple
00717 """
00718 if self.__startTime is not None:
00719 return time.asctime(time.gmtime(self.__startTime))
00720 else:
00721 return None
00722
00723 def getEndTime(self):
00724 """!\brief Return the test end time
00725
00726 \return Test end time in GMT as a tuple
00727 """
00728 if self.__endTime is not None:
00729 return time.asctime(time.gmtime(self.__endTime))
00730 else:
00731 return None
00732
00733 def getDataDistributor(self):
00734 """!\brief Return the data distribution server instance.
00735
00736 \return Data distribution server instance.
00737 """
00738 return self.__dds
00739
00740 def setSessionVar(self, key, value):
00741 """!\brief Set session variable \a key to value \a value.
00742
00743 Session variables are data that is shared between different runs.
00744 They exist as long as the run control session they are defined in.
00745 Typical uses of session variables are to pass data from one suite
00746 script to another or to maintain state during a multiple step
00747 test run.
00748
00749 \param key Session variable identifier.
00750 \param value Session variable value.
00751 """
00752 sessionVars = self.common.getDBN()
00753 if sessionVars is not None:
00754 sessionVars[key] = value
00755 else:
00756 raise RuntimeError, "Database node does not exist"
00757
00758 def getSessionVar(self, key):
00759 """!\brief Return the session variable identified by \a key.
00760
00761 \param key Session variable identifier.
00762
00763 \return Value of the session variable if it exists,
00764 otherwise None.
00765 """
00766 sessionVars = self.common.getDBN()
00767 if sessionVars is not None and sessionVars.has_key(key):
00768 return sessionVars[key]
00769 else:
00770 return None
00771
00772 def setPrefs(self, prefs):
00773 """!\brief Explicitly set preferences.
00774
00775 When scripts are run in standalone mode it may be
00776 necessary to pass in a script defined set of preferences.
00777
00778 The preferences specified in \a prefs get appended to
00779 the set of preferences read from the configuration file.
00780
00781 \param prefs Preferences as a dictionary.
00782 """
00783 for (key, val) in prefs.items():
00784 self.prefs[key] = val
00785
00786 def setCompletionStatus(self, completionStatus, completionStatusStr=None):
00787 """!\brief Sets the completion status of the test.
00788
00789 Please use one of the following:
00790
00791 - COMPL_STATUS_ABORTED = -2
00792 - COMPL_STATUS_ERROR = -3
00793 - COMPL_STATUS_FAILED = -3
00794 - COMPL_STATUS_SUCCESS = 0
00795 - COMPL_STATUS_PASSED = 0
00796 - COMPL_STATUS_PASSED_CONDITIONALLY = -4
00797 - COMPL_STATUS_MARGINAL = -5
00798
00799 Or a user defined completion status in which case
00800 a description needs to be supplied along with the code.
00801
00802 \param completionStatus Numeric completion status code
00803 \param completionStatusStr Completion status description
00804
00805 """
00806 if not self.__complStatus.isCompletionStatus(completionStatus):
00807 if completionStatusStr is None:
00808 raise RuntimeError, "Need to specify a completion status description " \
00809 "when specifying a user defined completion status"
00810 else:
00811 addRes = self.__complStatus.addCompletionStatus(completionStatus, completionStatusStr)
00812 if addRes == -2:
00813 raise RuntimeError, "Completion status description '%s' already exists " \
00814 "under a different status code." % completionStatusStr
00815 self.__completionStatus = completionStatus
00816
00817 def getCompletionStatus(self):
00818 """!\brief Return the completion status code.
00819
00820 Returns the code set by the script or
00821 COMPL_STATUS_UNDEFINED if it hasn't been set.
00822
00823 \return Completion status code.
00824 """
00825 if self.__appStatus is None:
00826 return self.__completionStatus
00827 else:
00828 return self.__appStatus
00829
00830 def getCompletionStatusStr(self):
00831 """!\brief Return the description of the completion status.
00832
00833 Returns the code as set by the script or
00834 'UNDEFINED' if it hasn't been set.
00835
00836 \return Completion status description.
00837 """
00838 completionStatus = self.getCompletionStatus()
00839 return self.__complStatus.getCompletionStatusStr(completionStatus)
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865 def addCustomReportData(self, key, value):
00866 """!\brief Add custom report data to the run report.
00867
00868 This method allows the application to add any additional data
00869 that Run Control is not already providing to the run report
00870 (rcReport.out).
00871
00872 When the custom report data gets processed and written to the
00873 database, it will go under the "AdditionFields" column. Please
00874 only provide values that can be converted to a string.
00875
00876 \param key Custom report data identifier.
00877 \param value Custom report data value.
00878 """
00879 self.__customReportData[key] = value
00880
00881 def getParameterVerifier(self):
00882 """!\brief Return the parameter verifier instance
00883
00884 \return Parameter verifier instance
00885 """
00886 return self.__paramVerifier
00887
00888 def addExportedFile(self,file=None,delete=False):
00889 """!\brief Add file to the list of files to be exported.
00890
00891 \param file file to be added
00892 \param delete delete original on export (default False)
00893 """
00894 if file is not None:
00895 (filePath,fileName) = os.path.split(file)
00896 self.__exportedFiles.append( [filePath,fileName,delete] )
00897
00898
00899
00900 if self.__testReport is None:
00901 for objStr in dir(self):
00902 obj = getattr(self, objStr)
00903 if '__class__' in dir(obj):
00904 if rcTestReport.rcTestReport in obj.__class__.__mro__:
00905 self.__testReport = obj
00906 break
00907 if self.__testReport is not None and file in self.__testReport.getReports():
00908 self.__testReportFile = fileName
00909
00910
00911 def getFileTimeStamp(self):
00912 """!\brief Return the formatted timestamp used to name output files.
00913
00914 Using this method, applications can request the formatted timestamp
00915 value used in naming archive, snapshot and housekeeping files.
00916 The format is YYMMDDHHMISS.
00917
00918 \return Formatted GMT timestamp
00919 """
00920 return self.__tstamp
00921
00922 def getRawTimeStamp(self):
00923 """!\brief Return the raw timestamp as returned from gmtime().
00924
00925 \return Raw GMT timestamp tuple.
00926 """
00927 return self.__tstampRaw
00928
00929 def __genActivity(self):
00930 """!\brief generate the activity db entry
00931
00932 Called from sestartRun
00933 """
00934
00935
00936 if self.__activitiesObj is not None:
00937 aObj = self.__activitiesObj
00938 suiteID = None
00939 if self.getParentSuite() is not None:
00940 suiteID = self.getParentSuite().getSuiteID()
00941 aObj.newActivity(self.runId, suiteID)
00942 aObj.setState(self.runId, aObj.CommandingState, aObj.CommandingStarted)
00943 aObj.setTime(self.runId, aObj.DataStart, self.__startTime)
00944 else:
00945 log.debug("Activities logging disabled due to prior exception")
00946
00947 def __endActivity(self):
00948 """!\brief finalize the activities database entry
00949
00950 Called from __genReport
00951 """
00952 if self.__activitiesObj is not None:
00953 aObj = self.__activitiesObj
00954 aObj.setState(self.runId, aObj.CommandingState, aObj.CommandingComplete)
00955 aObj.setTime(self.runId, aObj.DataStop, self.__endTime)
00956 else:
00957 log.debug("Activities logging disabled due to prior exception")
00958
00959 def __genReport(self):
00960 """!\brief Generate the run report
00961
00962 Called through the GUI bridge
00963 """
00964 log.debug("Generating run report...")
00965 self.__reportGen.initialize(self.__tstampRaw)
00966 self.__reportGen.addField("TestName", self.getName())
00967 if self.getParentSuiteName() is not None:
00968 self.__reportGen.addField("SuiteName", self.getParentSuiteName())
00969 self.__reportGen.addField("RunId", self.runId)
00970 if self.prefs.has_key("operator"):
00971 self.__reportGen.addField("Operator", self.prefs["operator"])
00972 if self.prefs.has_key("userid"):
00973 self.__reportGen.addField("OperatorId", self.prefs["userid"], rcReportGen.UNITS_LONG)
00974 self.__reportGen.addField("PauseCount", str(self.__pauseCount), rcReportGen.UNITS_INT)
00975 self.__reportGen.addField("PausedTime", str(self.__pausedTime), rcReportGen.UNITS_FLOAT)
00976 self.__reportGen.addField("StartTime", str(time.gmtime(self.__startTime)))
00977 self.__reportGen.addField("ElapsedTime", str(self.__elapsedTime), rcReportGen.UNITS_FLOAT)
00978 self.__reportGen.addField("EndTime", str(time.gmtime(self.__endTime)))
00979 self.__reportGen.addField("FreeDiskSpace", str(FreeSpace.FreeSpace()), rcReportGen.UNITS_LONG)
00980 self.__reportGen.addField("AdditionalInputFiles", str(self.getAddlInputFiles()))
00981 failed = []
00982 failedNoVersion = []
00983 releases = {}
00984
00985 if int(self.prefs["versionnbl"]) == 1:
00986 try:
00987 self.gui.waitCursor()
00988 versions = Gversions.getModuleVersions(self.common.getLCATcmd().LCM)
00989 for (module, version) in versions.items():
00990 if type(version) is types.StringType:
00991 if version.strip() == '':
00992 failedNoVersion.append(module)
00993 failed.append(module)
00994 self.gui.normalCursor()
00995 except:
00996 log.exception("Error in retrieving version information. Setting VersionData to an empty list.")
00997 versions = {}
00998 self.__reportGen.addField("VersionData", str(versions))
00999
01000 if int(self.prefs["disableverification"]) == 0:
01001 try:
01002 filesToVerify= []
01003 for rootDir in ROOT_DIRS:
01004 if os.path.exists(os.path.join(ONLINE_ROOT, rootDir)):
01005 release = Gversions.verifyModuleDigests(digestDataFile=None, rootDir=rootDir,
01006 additionalFilesToVerify=filesToVerify)
01007 releases[rootDir] = release[0]
01008 for module in release[1]:
01009 if module not in failed:
01010 failed.append(module)
01011 noVersionInRelease = False
01012 for module in failedNoVersion:
01013 if module in release[1]:
01014 noVersionInRelease = True
01015 break
01016 if len(release[1]) > 0 or noVersionInRelease:
01017 log.error("Some modules failed verification for %s release %s. Check Elogbook for the modules that failed." % (rootDir, release[0]))
01018 except:
01019 log.exception("Error in verification. Setting Release and ModulesFailedVerification to empty lists.")
01020 releases = {}
01021 failed = []
01022 self.__reportGen.addField("Release", str(releases))
01023 self.__reportGen.addField("ModulesFailedVerification", str(failed))
01024
01025 if self.prefs.has_key("logfilename"):
01026 self.__reportGen.addField("LogFile", self.prefs["logfilename"])
01027 if self.getRunMsgLogFile() is not None:
01028 self.__reportGen.addField("RunLogFile", self.getRunMsgLogFile())
01029 if self.getRunLcmLogFile() is not None:
01030 self.__reportGen.addField("LcmLogFile", self.getRunLcmLogFile())
01031 comments = []
01032 if self.rc is not None:
01033 cw = self.rc.getCommentWidget()
01034 if cw is not None:
01035 hist = cw.getHistory()
01036 tstamp = cw.getTimestamps()
01037 comments += zip(tstamp, hist)
01038 cw = self.rc.getEndRunComments()
01039 if cw is not None:
01040 hist = cw.getHistory()
01041 tstamp = cw.getTimestamps()
01042 comments += zip(tstamp, hist)
01043 self.__reportGen.addField("Comments", str(comments))
01044 for (key, value) in self.__customReportData.items():
01045 self.__reportGen.addField(key, str(value))
01046 if self.prefs is not None:
01047 prefs = self.prefs
01048 if prefs.has_key("lastsite"):
01049 self.__reportGen.addField("Site", prefs["lastsite"])
01050 if prefs.has_key("lastparticletype"):
01051 self.__reportGen.addField("ParticleType", prefs["lastparticletype"])
01052 if prefs.has_key("lastinstrumenttype"):
01053 self.__reportGen.addField("InstrumentType", prefs["lastinstrumenttype"])
01054 if prefs.has_key("lastorientation"):
01055 self.__reportGen.addField("Orientation", prefs["lastorientation"])
01056 if prefs.has_key("lastphase"):
01057 self.__reportGen.addField("Phase", prefs["lastphase"])
01058 if prefs.has_key("hwConfig"):
01059 self.__reportGen.addField("hwConfig", str(prefs["hwConfig"]))
01060 if self.__verifyStatus is not None:
01061 self.__reportGen.addField("ParamVerify", str(self.__verifyStatus))
01062 if self.__testReportFile is not None:
01063 self.__reportGen.addField("ScriptReport", self.__testReportFile)
01064
01065 errorLogCount = self.common.getErrorLogCount()
01066 self.__reportGen.addField("ErrorLogCount", str(errorLogCount), rcReportGen.UNITS_INT)
01067
01068 if self.getCompletionStatus() == self.COMPL_STATUS_PASSED and errorLogCount != 0:
01069 log.warn("Errors were encountered during the run. Overriding the completion status as FAILED.")
01070 self.__appStatus = self.COMPL_STATUS_FAILED
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080 self.__reportGen.addField("CompletionStatus", str(self.getCompletionStatus()), rcReportGen.UNITS_INT)
01081 self.__reportGen.addField("CompletionStatusStr", self.getCompletionStatusStr())
01082
01083 self.__reportGen.addField("ScriptOptions", str(self.common.getScriptOpts(self.getName())))
01084
01085
01086 self.__reportGen.writeReportXML()
01087 try:
01088 if self.prefs is not None and int(self.prefs["elognbl"]):
01089 self.__endActivity()
01090 except Exception, e:
01091 log.exception(e)
01092 raise
01093
01094 log.debug("Run report generation complete.")
01095
01096 def startExcludeTime(self):
01097 """!\brief Save the start time for exclusion from the run's elapsed time.
01098
01099 This method allows excluding some amount of time from the run's elapsed
01100 time. The start time is the time that the method is called.
01101
01102 Note that time spent in the PAUSED state is also excluded from the elapsed
01103 time value. Overlapping time is excluded from the elapsed time only once.
01104 """
01105 if not self.__excluding:
01106 self.__excludeLock.acquire()
01107 now = time.time()
01108 self.__excluding = True
01109 self.__excludeTime = now
01110 self.__elapsedTime += now - self.__strobeTime
01111 self.__strobeTime = now
01112 self.__excludeLock.release()
01113 elif self.__pauseTime is not None:
01114 pass
01115 else:
01116 assert not self.__excluding, "Error: " + \
01117 "Attempt made to exclude time when it is already being excluded"
01118
01119 def endExcludeTime(self):
01120 """!\brief Strobe the end time for exclusion from the run's elapsed time.
01121
01122 This method allows excluding some amount of time from the run's elapsed
01123 time. The end time is the time that the method is called. The difference
01124 between the end time and the start time is added to the excluded time.
01125 """
01126
01127 if self.__pauseTime is None:
01128 assert self.__excluding, "Error: startExcludeTime was not called"
01129 self.__excludeLock.acquire()
01130 now = time.time()
01131 dExcludedTime = now - self.__excludeTime
01132 self.__excludedTime += dExcludedTime
01133 self.__elapsedTime += now - self.__strobeTime - dExcludedTime
01134 self.__strobeTime = now
01135 self.__excludeTime = None
01136 self.__excluding = False
01137 self.__excludeLock.release()
01138 else:
01139 pass
01140
01141 def adjustExcludeTime(self, dExcludeTime):
01142 """!\brief This method allows adjustment of the amount of time to be
01143 excluded from the run's elapsed time.
01144
01145 The amount of time to be excluded from the run's elapsed time can be
01146 adjusted positivily or negativily with this method. It does not take
01147 into account any other effects on the excluded time, such as whether
01148 the run was in the PAUSED state.
01149
01150 \param dExcludeTime The amount of time to be excluded as given by the
01151 difference of two time.time() values.
01152 """
01153
01154 self.__excludeLock.acquire()
01155 self.__excludedTime += dExcludeTime
01156 self.__excludeLock.require()
01157
01158 def __jacket(self, function, *args, **kwargs):
01159 """!\brief Jacketting method for catching exceptions
01160
01161 This method is used for catching exceptions and initiating graceful
01162 termination of the user application.
01163
01164 \param function The function being jacketted
01165 \param *args Unnamed arguments passed to \a function
01166 \param **kwargs Named arguments passed to \a function
01167 """
01168 try:
01169 function(*args, **kwargs)
01170 except Exception, e:
01171 log.exception("%s: %s" % (function.__name__, e))
01172 self.__appStatus = self.COMPL_STATUS_ABORTED
01173 self.gui.doReset()
01174
01175 def __stopRun(self):
01176 """!\brief Logic executed during a stop run.
01177
01178 This method is responsible for:
01179 - Calculating the final elapsed time
01180 - Bringing up the end run dialog
01181 - Setting the completion status
01182 - Creating the test report
01183 """
01184
01185 now = time.time()
01186 self.__endTime = now
01187 self.__excludeLock.acquire()
01188 if self.__strobeTime is not None:
01189 self.__elapsedTime += now - self.__strobeTime
01190 self.__strobeTime = None
01191 self.__excludeLock.release()
01192
01193 if self.__completionStatus == self.COMPL_STATUS_UNDEFINED:
01194 log.warn("Script completed with an undefined completion status. "
01195 "Please use the setCompletionStatus method.")
01196
01197
01198 if self.common.options().securedir is not None or \
01199 self.common.options().paramverify is not None or \
01200 self.common.options().endRunDlg is not None:
01201 if self.rc is not None and not self.rc.inShutdown:
01202 if not self.isRunFromSuite() or self.isLastSuiteScript():
01203 self.rc.execGUImethod(self.__handleEndRunDlg)
01204
01205
01206 if self.rc is not None:
01207 if not self.rc.inShutdown:
01208 self.rc.execGUImethod(self.__genReport)
01209 else:
01210 self.__genReport()
01211
01212 def __logEndRun(self, fn):
01213 """!\brief Log the end of the run
01214
01215 \param fn The function name it is called from
01216 """
01217 log.info("%s -- Test %s finished, Run Id: %s, Status: %s, Completion Time: %s"
01218 % (fn, self.getName(), self.runId, self.getCompletionStatusStr(),
01219 time.asctime(time.gmtime(self.__endTime))))
01220
01221 if int(self.prefs["lognbl"]) == 1:
01222 self.common.stopRunLog(self.__runMsgLog)
01223 self.common.stopLcmLog()
01224 self.addExportedFile(self.__runMsgLogFile, delete=True)
01225 self.addExportedFile(self.__runLcmLogFile, delete=True)
01226
01227 def __removeZeroLengthFile(self, filePath, fileName):
01228 """!\brief Delete the file if the file size is zero length.
01229
01230 \param filePath
01231 \param fileName
01232
01233 \return 0 If file size is not zero or if the deletion is successful.
01234 -1 If the deletion is not successful.
01235 """
01236 fname = os.path.join(filePath, fileName)
01237 size = os.stat(fname)[6]
01238 if size == 0:
01239 try:
01240 os.remove(fname)
01241 except:
01242 return -1
01243 return 0
01244
01245 def __exportData(self):
01246 """!\brief Export all relevant data from a run.
01247
01248 Routine to export all relevant data from a run to
01249 local storage and possibly to offsite backup.
01250 This routine is designed to execute after the completion of a run,
01251 but before teardown of Run Control.
01252 The name of the export directory is the run ID. This is
01253 to guarantee against unintentional overwrites between multiple
01254 teststands.
01255 """
01256
01257
01258 exportEnabled = (int(self.prefs["dataexport"]) == 1)
01259 if not exportEnabled: return
01260
01261
01262 outDir = self.getRunId()
01263 outDirRoot = self.prefs["datadir"]
01264 outPath = os.path.join(outDirRoot,outDir)
01265
01266
01267
01268 hardExporter = DataExport.DataExport()
01269 safeExporter = DataExport.DataExport()
01270 hardExporter.setPurge(True)
01271 safeExporter.setPurge(False)
01272
01273 if os.access(outDirRoot,os.W_OK):
01274 os.mkdir(outPath)
01275
01276
01277
01278
01279
01280
01281
01282
01283 rcLastLine = self.__rcLastLine()
01284 rcOutFile = open(os.path.join(outPath,"rcReport.out"), 'w')
01285 rcOutFile.write(rcLastLine)
01286 rcOutFile.close()
01287
01288 for path,name,delete in self.__exportedFiles:
01289 try:
01290 if delete:
01291 hardExporter.export(os.path.join(path,name),os.path.join(outPath,name))
01292 else:
01293 safeExporter.export(os.path.join(path,name),os.path.join(outPath,name))
01294 except Exception, e:
01295 log.error("Exception %s in trying to export file %s" % (e, name))
01296
01297 else:
01298 err = "rcTransitions.__exportData: Local directory ", outDirRoot, " is not writable"
01299 log.error(err)
01300 return
01301
01302
01303
01304 if os.path.abspath(self.prefs["datadir"]) == os.path.abspath(self.prefs["exportdir"]):
01305 msg = "Data source and Export directories are identical\n" + \
01306 "source: %s, export: %s" % ( self.prefs["datadir"] , self.prefs["exportdir"] )
01307 log.warn(msg)
01308 exportEnabled = False
01309
01310
01311 if exportEnabled:
01312 if not os.path.exists(os.path.abspath(self.prefs["exportdir"])):
01313 log.fatal("Final export directory %s no longer exists. Something has gone very, very wrong." % self.prefs["exportdir"])
01314 return
01315
01316 destDir = os.path.join(self.prefs["exportdir"] ,outDir)
01317 dbn = self.common.getDBN()
01318 dbn['_LastExportPath'] = destDir
01319 sourceDir = outPath
01320 hardExporter.export(sourceDir, destDir)
01321
01322
01323 done = file(destDir+'/zzz.done','w')
01324 done.close()
01325
01326 def __rcLastLine(self):
01327 """!\brief Get the last line of the run report file.
01328
01329 \return Last line of the run report.
01330 """
01331
01332
01333
01334 filename = ( os.path.join(self.prefs["reportdir"], 'rcReport.out') )
01335
01336 rcFile = open(filename,'r')
01337 charsPerLine = 4096
01338 while 1:
01339 try:
01340 rcFile.seek(-1 * charsPerLine ,2)
01341 except IOError:
01342 rcFile.seek(0)
01343 if rcFile.tell() == 0:
01344 atstart=True
01345 else:
01346 atstart=False
01347
01348 rcLines=rcFile.read().split("\n")
01349 if (len(rcLines) > 2) or atstart:
01350 break
01351
01352 charsPerLine=charsPerLine * 2
01353
01354 rcFile.close()
01355 rcLastLine = rcLines[len(rcLines)-2]
01356
01357
01358 import re
01359 runId = 0
01360 runIdRE = re.compile(r".*<RunId>(\d+)</RunId>.*")
01361 rList = runIdRE.findall(rcLastLine)
01362 if len(rList) > 0:
01363 runId = rList[0]
01364 else:
01365 log.fatal("Could not parse a run ID out of the last line of rcReport.out.")
01366 log.error("Last line = %s" % rcLastLine)
01367 if runId != self.getRunId():
01368 msg = "Run identifier read from rcReport.out (%s) does not match LICOS internal run ID (%s)" % (runId, self.getRunId())
01369 log.fatal(msg)
01370
01371 return rcLastLine
01372
01373
01374 def __handleEndRunDlg(self):
01375 """!\brief Handle the end run dialog.
01376
01377 """
01378 prefs = self.common.preferences()
01379 secMan = self.common.getSecurityMan()
01380 endRunDlg = self.rc.getEndRunDlg()
01381 loginId = prefs["operatorobj"].getLoginId()
01382 endRunDlg.initialize(secMan, loginId, self)
01383 if endRunDlg.exec_loop() == endRunDlg.Accepted:
01384 curComplStatusStr = self.getCompletionStatusStr()
01385 (cv, cs) = endRunDlg.getNewCompletionStatus(curComplStatusStr)
01386 if cv is not None:
01387 self.setCompletionStatus(cv)
01388 msg = ("Completion Status '%s' is overridden with status '%s' by operator %s" %
01389 (curComplStatusStr, cs, prefs["operator"]))
01390 log.info(msg)
01391 endRunDlg.enterComment(msg, addToHistory=1)
01392 else:
01393 endRunDlg.clearHistory()
01394
01395 class StopRequestedException(Exception):
01396 def __init__(self, msg, *args):
01397 self.msg = msg
01398 self.args = (msg,) + args
01399