Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

RunControlCommon.py

00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2003
00004 #                                     by
00005 #                        The Board of Trustees of the
00006 #                     Leland Stanford Junior University.
00007 #                            All rights reserved.
00008 #
00009 
00010 __facility__ = "Online"
00011 __abstract__ = "GLAST LAT run control common class"
00012 __author__   = "R. Claus <Claus@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__     = "2/5/2003"
00014 __version__  = "$Revision: 2.46 $"
00015 __credits__  = "SLAC"
00016 
00017 import LATTE.copyright_SLAC
00018 
00019 
00020 import sys
00021 import os
00022 import time
00023 import types
00024 import atexit
00025 import socket
00026 import logging as log
00027 import logging.handlers
00028 from rcLoginImpl          import rcLoginImpl
00029 from rcSecurityMan        import rcSecurityMan
00030 from rcPreferencesManager import rcPreferencesManager
00031 from rcPreferencesGUIImpl import rcPreferencesGUIImpl
00032 
00033 from LATTE.client.gOptions import Options
00034 from LATTE.client.gCmdCli import CmdCli
00035 from LATTE.client.gEvtCli import EvtCli
00036 from LATTE.database.gLAT import GLAT
00037 from LATTE.database.gXBR import GXBRD
00038 from LATTE.database.gOCS import GOCS
00039 from LATTE.logger.MSGLogGUIImpl import MSGLogGUIImpl
00040 from LATTE.database.gVersions import Gversions
00041 
00042 
00043 class GDBN(dict):
00044   """\brief Database node record.
00045 
00046      The database node contains session level variables in addition
00047      to preferences and entries used for diagnostics purposes.
00048   """
00049   def __init__(self):
00050     """\brief Initialize the dictionary.
00051 
00052     """
00053     dict.__init__(self)
00054 
00055 
00056 class RunControlSocketHandler(logging.handlers.SocketHandler):
00057   def __init__(self, *args):
00058     apply(logging.handlers.SocketHandler.__init__, (self,) + args)
00059 
00060   def handleError(self, record):
00061     if self.closeOnError and self.sock:
00062       self.sock.shutdown(2)
00063       self.sock.close()
00064       self.sock = None        #try to reconnect next time
00065     else:
00066       raise IOError, sys.exc_info()[1]
00067 
00068 class ErrorLogCounterHandler(logging.Handler):
00069   def __init__(self, common):
00070     logging.Handler.__init__(self)
00071     self.__common = common
00072 
00073   def emit(self, record):
00074     self.__common.incrementErrorLogCount()
00075 
00076 class RunControlFileHandler(log.FileHandler):
00077   """\brief FileHandler class for handling multiple line log entries
00078 
00079   If a log entry contains multiple lines, such as the case for
00080   tracebacks or exceptions a begin and end delimiter is added
00081   to the log entry so that it can be parsed by the MessageLogger
00082   """
00083   def __init__(self, filename, mode):
00084     log.FileHandler.__init__(self, filename, mode)
00085 
00086   def emit(self, record):
00087     try:
00088       msg = self.format(record)
00089       (msg1, msg2) = msg.split(' - ',1)
00090       if msg2.find('\n') > 0:
00091         msg = msg1 + " - <//" + msg2+"//>"
00092       if not hasattr(types, "UnicodeType"): #if no unicode support...
00093         self.stream.write("%s\n" % msg)
00094       else:
00095         try:
00096           self.stream.write("%s\n" % msg)
00097         except UnicodeError:
00098           self.stream.write("%s\n" % msg.encode("UTF-8"))
00099       self.flush()
00100     except:
00101       self.handleError(record)
00102 
00103 class RunControlCommon(object):
00104   """Common class for providing a run control helper functions
00105   """
00106   def __init__(self):
00107     self.MSGLogger = None
00108     self.__startGMT = time.gmtime()
00109     self.__options  = Options(['config', 'server'],
00110                               ['appdir','cmddebug','evtdebug','fswmsglevel',
00111                                'noreload','schema','securedir','app',
00112                                'ocsport'],
00113                               ['initforce','noerrfile','norcoverride',
00114                                'ocsverbose','oesforce',
00115                                'playback','paramverify','noplumbing'])
00116     if len(sys.argv) > 1 and sys.argv[1] == '--help':
00117       self.__options.usage()
00118       print self.__usage()
00119       sys.exit(0)
00120     self.__lat = None
00121     self.__xbrd = None
00122     self.__dds = None
00123     self.__server = None
00124     try:
00125       self.__options.parse()
00126     except Exception, msg:
00127       self.__options.usage(self.__usage())
00128       raise Exception, msg
00129 
00130     if self.__options.ocsport is not None:
00131       self.__cmdCli = CmdCli(port=int(self.__options.ocsport))
00132       self.__evtCli = EvtCli(port=int(self.__options.ocsport))
00133     else:
00134       self.__cmdCli = CmdCli()
00135       self.__evtCli = EvtCli()
00136     self.__ocs = GOCS(self.__cmdCli)
00137     self.__securityMan = None
00138     self.__dbn = GDBN()
00139     self.__prefMan = None
00140     self.__prefGUI = None
00141     self.__errLogCnt = 0
00142     self.__hwVersions = None
00143 
00144     try:
00145       import qt
00146       self.__qApp = qt.QApplication([])
00147       self.__qApp.addLibraryPath(os.path.join(os.environ['ONLINE_ROOT'],
00148                                                 'LATTE/ext/plugins'))
00149     except ImportError:
00150       self.__qApp = None
00151 
00152     # Arrange for logging to be properly shutdown at program exit
00153     atexit.register(log.shutdown)
00154 
00155   def initialize(self, startup=False):
00156     self.__prefMan = rcPreferencesManager(self.__options.config)
00157     if (self.__prefMan.loadConfig()):
00158       if self.__options.cmddebug is not None:
00159         self.__cmdCli.setDebug(int(self.__options.cmddebug))
00160 
00161       if self.__options.evtdebug is not None:
00162         self.__evtCli.setDebug(int(self.__options.evtdebug))
00163 
00164       self.startLogger(startup)
00165 
00166       if self.__options.securedir is not None:
00167         self.__securityMan = rcSecurityMan(os.path.abspath(self.__options.securedir),
00168                                            self.__prefMan.preferences()["users"])
00169         self.__securityMan.readPermissions()
00170         self.__dbn['_securityMan'] = self.__securityMan
00171       if self.__options.noreload is not None:
00172         import string
00173         self.__dbn["_dontReload"] = map(string.strip, file(self.__options.noreload).readlines())
00174     else:
00175       raise RuntimeError, "Can not find configuration file: %s" % self.__options.config
00176 
00177   def __usage(self):
00178     return """
00179 
00180 Detailed explanation:
00181 
00182 Mandatory options:
00183 --server to specify the hostname or the IP address of the teststand crate
00184    to connect to.
00185 --config to specify the run control configuration file to use
00186 
00187 Optional options:
00188 --app Specify the standalone application or suite to run.
00189    Typically used by test or suite runners.
00190 --appdir Specify the application directory
00191 --cmddebug Specify command client debug mode (defaults to 0).
00192    If this option is specified then all register accesses from the
00193    command client is logged to the logger.
00194 --evtdebug Specify event client debug mode (defaults to 0)
00195    The following debug modes are available and can be added together
00196    for combinations:
00197      0: No Debug
00198      1: Debug All
00199      2: Event Dump
00200      4: Warnings
00201      8: Parser Output
00202     16: Error Output
00203 --fswmsglevel Specify the FSW message level for the OCS manager task.
00204 --initforce Force command-response and event fabrics initialization on the SBC.
00205 --noerrfile Disable error archive file output.
00206 --norcoverride Prevents the run conditions to be overridden with NOT-DEFINED.
00207 --noreload Specify a file that contains prefixes of modules
00208    which should not be reloaded when a user script is selected.
00209    This list gets appended to the default list which currently contains
00210    the following prefixes 'scipy', 'weave', 'qwt', 'xml', 'pyexpat', 'win32'.
00211    The file should contain one prefix per line.
00212 --ocsverbose Enable fabrics initialization messages on the SBC.
00213 --noplumbing Do not automatically plumb the LAT upon connection.
00214 --ocsport Define the port used to connect to OCS
00215 --oesforce Force our OES socket to be the default event server for the SBC.
00216 --paramverify Force the parameter verifier to come up even if running in non-secure mode
00217 --playback Indicate that Run Control should command the
00218    event server to play back the next event from its file. Since
00219    this is done using the GLT's self trigger command, the state of
00220    the internal trigger enable bit deterimines whether the
00221    application or Run Control will issue the self trigger command.
00222    No events will be delivered if the application has the internal
00223    trigger enabled, but doesn't issue CMD_SELF_TRIGGERs.
00224 --schema Specify the schema file to load.
00225 --securedir The full path to the secure directory where the password file
00226    ("passwords") and the user permissions file ("security.cfg") is stored.
00227    If this switch is specified then RunControl is started in secure mode.
00228   """
00229 
00230 
00231 
00232 
00233   def connect(self, force=False):
00234     self.__cmdCli.connect(self.__options.server)
00235     self.__evtCli.connect(self.__options.server, force)
00236     self.__evtCli.purge()
00237 
00238   def disconnect(self):
00239     self.__cmdCli.disconnect()
00240     self.__evtCli.disconnect()
00241 
00242   def readSchema(self, filename):
00243     self.__lat = GLAT(self.__cmdCli)
00244     self.__lat.readSchema(filename)
00245     # Do not load the schema again if the GXBRD node does not exist.
00246     # See LTE-325 for more info.
00247     if self.__lat.xbrdExists():
00248       self.__xbrd = GXBRD(self.__cmdCli)
00249       self.__xbrd = self.__xbrd.readSchema(filename)
00250     else:
00251       self.__xbrd = None
00252     self.__dbn['_LATSchema'] = self.__lat
00253     self.__dbn['_LATSchemaFile'] = filename
00254     self.__dbn['_XBRDSchema'] = self.__xbrd
00255     self.__dbn['_XBRDchemaFile'] = filename
00256 
00257   def elogWebsite(self):
00258     import webbrowser
00259     webbrowser.open(url=self.__prefMan.preferences()["elogurl"], new=1)
00260 
00261   def EBFdistribution(self):
00262     if int(self.__prefMan.preferences()["servernbl"]) == 0:
00263       if self.__dds is not None:
00264         self.__dds.disconnect()
00265         log.info("Disconnected from the data distribution server since it was disabled in preferences.")
00266         self.__dds = None
00267       return
00268     dataType   = 'LAT_EBFdata'
00269     server     = self.__prefMan.preferences()["server"].strip()
00270     if server != '':
00271       try:
00272         import LATTE.tools.DataDistributor as DD
00273       except:
00274         log.error("Can't find DataDistributor module, is it in PYTHONPATH?")
00275       else:
00276         if self.__server is None:
00277           self.__dds = DD.DataDistributorServer(dataType)
00278           try:
00279             self.__dds.connect(server)
00280             log.info("Connected to the data distribution server at %s." % server)
00281             #atexit.register(self.__dds.disconnect)  # Already done somewhere?
00282           except:
00283             log.warn("Unable to connect to data distribution server %s: serving of %s disabled" % (self.__prefMan.preferences()["server"], dataType))
00284             self.__dds = None
00285         elif server != self.__server:
00286           if self.__dds is not None:
00287             self.__dds.disconnect()
00288             log.info("Disconnected from the data distribution server.")
00289           self.__dds = DD.DataDistributorServer(dataType)
00290           try:
00291             self.__dds.connect(server)
00292             log.info("Reconnected to the data distribution server since the server changed in preferences.")
00293             #atexit.register(self.__dds.disconnect)  # Already done somewhere?
00294           except:
00295             log.warn("Unable to connect to data distribution server %s: serving of %s disabled" % (self.__prefMan.preferences()["server"], dataType))
00296             self.__dds = None
00297       self.__server = self.__prefMan.preferences()["server"].strip()
00298 
00299   def setLoggingLevel(self, logLevel=None):
00300     if logLevel is None: logLevel = self.__prefMan.preferences()["loglevel"]
00301     # Set the log levels at the handler level so that
00302     # file based handlers can still record DEBUG logs.
00303     log.getLogger("").setLevel(0)
00304     # First handler is always the default stdout handler
00305     log.getLogger("").handlers[0].setLevel(eval("log."+logLevel))
00306     for hdlr in log.getLogger("").handlers[1:]:
00307       if hdlr.__class__.__name__ == 'RunControlSocketHandler':
00308         hdlr.setLevel(eval("log."+logLevel))
00309       elif hdlr.__class__.__name__ == 'ErrorLogCounterHandler':
00310         hdlr.setLevel(eval("log.ERROR"))
00311 
00312   def startLogger(self, startup=True):
00313     # Start up the message logger
00314     log.basicConfig()
00315     loghost = self.__prefMan.preferences()["loghost"]
00316     logport = self.__prefMan.preferences()["logport"]
00317     local = socket.gethostname().lower() == loghost.lower() or socket.gethostbyname(loghost) == '127.0.0.1'
00318     if int(self.__prefMan.preferences()["lognbl"]) == 1:
00319       if local and startup:
00320         if self.MSGLogger is not None:
00321           return
00322         self.MSGLogger = MSGLogGUIImpl(logport)
00323         self.MSGLogger.startListenerThread()
00324       msgPath = self.__prefMan.preferences()["logdir"]
00325       # Note that if the file already exists, it will be appended to
00326       ts   = time.strftime('%y%m%d%H%M%S', self.__startGMT)
00327       fileName = "msg" + ts + ".log"
00328       self.__prefMan.preferences()["logfilename"] = fileName
00329       fmt = log.Formatter("%(asctime)s(UT) %(filename)s:%(lineno)d %(levelname)-5s - %(message)s")
00330       fmt.converter = time.gmtime
00331       self.fileHdlr = RunControlFileHandler(os.path.join(msgPath, fileName), "a")
00332       self.fileHdlr.setFormatter(fmt)
00333       log.getLogger("").addHandler(self.fileHdlr)
00334       self.sockHdlr = RunControlSocketHandler(loghost,
00335                                               logport)
00336       self.sockHdlr.setFormatter(fmt)
00337       log.getLogger("").addHandler(self.sockHdlr)
00338       self.errLogCounter = ErrorLogCounterHandler(self)
00339       log.getLogger("").addHandler(self.errLogCounter)
00340       self.setLoggingLevel()
00341       try:
00342         log.info('Trying to connect to the message logger')
00343       except:
00344         log.getLogger("").removeHandler(self.sockHdlr)
00345         log.exception("Can't connect to the message logger, disabling logging to socket")
00346       else:
00347         log.info('Successfully connected to the message logger')
00348     elif self.MSGLogger is not None:
00349       log.info('Disconnecting from the message logger')
00350       if local:
00351         self.MSGLogger.close()
00352         self.MSGLogger = None
00353       log.getLogger("").removeHandler(self.errLogCounter)
00354       log.getLogger("").removeHandler(self.sockHdlr)
00355       log.getLogger("").removeHandler(self.fileHdlr)
00356       log.shutdown()
00357     else:
00358       # Make sure error log counter handler is added
00359       # even when logs are disabled
00360       self.errLogCounter = ErrorLogCounterHandler(self)
00361       log.getLogger("").addHandler(self.errLogCounter)
00362       self.setLoggingLevel()
00363 
00364 
00365   def stopRunLog(self, fileHdlr):
00366     fileHdlr.close()
00367     log.getLogger("").removeHandler(fileHdlr)
00368 
00369   def startRunLog(self, fileName):
00370     fileHdlr = RunControlFileHandler(fileName, "w")
00371     fmt = log.Formatter("%(asctime)s(UT) %(filename)s:%(lineno)d %(levelname)-5s - %(message)s")
00372     fmt.converter = time.gmtime
00373     fileHdlr.setFormatter(fmt)
00374     log.getLogger("").addHandler(fileHdlr)
00375     return fileHdlr
00376 
00377   def options(self):
00378     return self.__options
00379 
00380   def prefMan(self):
00381     return self.__prefMan
00382 
00383   def prefGUI(self):
00384     if self.__prefGUI is None:
00385       self.__prefGUI = rcPreferencesGUIImpl(self)
00386     return self.__prefGUI
00387 
00388   def getMSGLogger(self):
00389     return self.MSGLogger
00390 
00391   def getDDS(self):
00392     return self.__dds
00393 
00394   def getCmdCli(self):
00395     return self.__cmdCli
00396 
00397   def getEvtCli(self):
00398     return self.__evtCli
00399 
00400   def getLAT(self):
00401     return self.__lat
00402 
00403   def getXBRD(self):
00404     return self.__xbrd
00405 
00406   def getOCS(self):
00407     return self.__ocs
00408 
00409   def getDBN(self):
00410     return self.__dbn
00411 
00412   def getSecurityMan(self):
00413     return self.__securityMan
00414 
00415   def getQtApp(self):
00416     return self.__qApp
00417 
00418   def getLoginId(self):
00419     """Returns the login id for the last successful login
00420     """
00421     return self.__loginId
00422 
00423   def getErrorLogCount(self):
00424     return self.__errLogCnt
00425 
00426   def incrementErrorLogCount(self):
00427     self.__errLogCnt += 1
00428 
00429   def resetErrorLogCount(self):
00430     self.__errLogCnt = 0
00431 
00432   def showLogin(self, parent):
00433     login = rcLoginImpl(parent=parent)
00434     status = login.exec_loop()
00435     if status == login.Accepted:
00436       loginId = str(login.txtLoginId.text()).strip()
00437       password = str(login.txtPassword.text()).strip()
00438       authResult = self.__securityMan.authenticateUser(loginId, password)
00439       if authResult == 0:
00440         self.__loginId = loginId
00441         log.info("User %s successfully logged in" % loginId)
00442         return 1
00443       elif authResult == -1:
00444         log.error("User authentication failed, loginId=%s" % loginId)
00445         return authResult
00446       else:
00447         log.error("Password database is not accessible.")
00448         return authResult
00449     else:
00450       return None
00451 
00452   def getHardwareVersions(self, lat=None, xbrd=None):
00453     if self.__hwVersions is None:
00454       if lat is None: lat = self.getLAT()
00455       if xbrd is None: xbrd = self.getXBRD()
00456       self.__hwVersions = Gversions.getHardwareVersions(lat, xbrd)
00457     return self.__hwVersions
00458 
00459   def clearHardwareVersionsCache(self):
00460     self.__hwVersions = None

Generated on Fri Jul 21 13:26:32 2006 for LATTE R04-12-00 by doxygen 1.4.3