gVersions.py

Go to the documentation of this file.
00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2005
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__ = "Version retrieval utility"
00012 __author__   = "Selim Tuvi <stuvi@slac.stanford.edu> SLAC - GLAST I&T/Online"
00013 __date__     = "2005/07/23 00:08:27"
00014 __updated__  = "$Date: 2006/03/22 01:34:28 $"
00015 __version__  = "$Revision: 1.19 $"
00016 __release__  = "$Name: HEAD $"
00017 __credits__  = "SLAC"
00018 
00019 import LICOS.copyright_SLAC
00020 
00021 import sha
00022 import marshal
00023 import inspect
00024 import os
00025 import sys
00026 import glob
00027 import logging as log
00028 import compiler
00029 
00030 from LICOS.lib.LATconstants import *
00031 
00032 class Gversions(object):
00033   """!\brief Version retrieval utility class.
00034 
00035   Contents:
00036 
00037     Provides accessor methods to retrieve version information from
00038     both the software and hardware.
00039     Displays version info from loaded libraries.
00040     Used for troubleshooting problems.
00041 
00042   Overview:
00043 
00044     Each driver on VxWorks side and each module on the client side
00045     provides its version number through functions. When the function
00046     cmd.getVersions() is called the command server packetizes this
00047     information and sends it over the socket to the command client
00048     which gets decoded into a tuple. The getModuleVersions() function
00049     converts this tuple to a dictionary and adds the python module
00050     versions to it.
00051 
00052   Usage:
00053 
00054     To retrieve version information one can write:
00055 
00056   \code
00057       python versions.py
00058 
00059       or
00060 
00061       import versions
00062       versions.Gversions.getModuleVersions()
00063   \endcode
00064   """
00065 
00066   def getModuleVersions(lcm=None):
00067     """!\brief Static class method which returns a dictionary of modules
00068        with their versions.
00069 
00070     \param lcm LCM instance
00071 
00072     \return Dictionary of modules with their versions
00073     """
00074     ignoreList =  [ 'scipy.gplt','ROOT'
00075                     #'__main__','_xmlplus','cPickle','pickle','sre','urllib',
00076                     #'xml','_xmlplus','xml.parsers.expat', 'xml.parsers.pyexpat',
00077                     #'pyexpat'
00078                   ]
00079     versions = {}
00080     if lcm is not None:
00081       # TODO: For now just collect SIU module info -ST
00082       if not lcm.modListPopulated():
00083         log.info("Retrieving FSW module versions from unit %s..."
00084                 % FSW_UNIT_NAMES[FSW_UNIT_SIU])
00085       fswModList = lcm.cmxAsBuilt(FSW_UNIT_SIU)
00086       if fswModList is not None:
00087         unitModList = {}
00088         for (package, constituent, version) in fswModList:
00089           unitModList['%s[%s]' % (package.strip(), constituent.strip())] = version.strip()
00090         versions[FSW_UNIT_NAMES[FSW_UNIT_SIU]] = unitModList
00091       if not lcm.modListPopulated():
00092         log.info("FSW module dump complete.")
00093     mstrList = sys.modules.keys()
00094     mstrList.sort()
00095     for mStr in mstrList:
00096       if mStr in ignoreList: continue
00097       m = sys.modules[mStr]
00098       if hasattr(m, '__version__'):
00099         v = Gversions.extractVersion(str(getattr(m, '__version__')))
00100         if v.strip() != '' or Gversions.isModuleInReleases(m):
00101           versions[mStr] = v
00102       elif Gversions.isModuleInReleases(m):
00103         versions[mStr] = ''
00104 
00105     versions['Python'] = str(hex(sys.hexversion))
00106     try:
00107       import qt
00108       versions['Qt']     = qt.QT_VERSION_STR
00109     except: pass
00110     try:
00111       import sip
00112       if 'SIP_VERSION_STR' in dir(sip):
00113         versions['SIP'] = sip.SIP_VERSION_STR
00114       elif 'SIP_VERSION' in dir(sip):
00115         versions['SIP'] = hex(sip.SIP_VERSION)
00116     except: pass
00117     try:
00118       if 'PYQT_VERSION_STR' in dir(qt):
00119         versions['PyQt'] = qt.PYQT_VERSION_STR
00120       else:
00121         versions['PyQt']   = qt.PYQT_VERSION
00122     except: pass
00123     return versions
00124   getModuleVersions = staticmethod(getModuleVersions)
00125 
00126   def isModuleInReleases(m, rootDirs=None):
00127     """!\brief Method to determine if a loaded module is in the
00128     specified list of root directories.
00129 
00130     \param m Module object
00131     \param rootDirs List of root directories relative to $ONLINE_ROOT.
00132 
00133     \return Boolean that determines whether the module originated from the list of
00134             root directories.
00135     """
00136     if rootDirs is None:
00137       rootDirs = ROOT_DIRS
00138     if hasattr(m, '__file__'):
00139       srcFile = inspect.getsourcefile(m)
00140       if srcFile is not None:
00141         srcFile = os.path.normpath(os.path.normcase(srcFile))
00142         for rootDir in rootDirs:
00143           rootPath = os.path.normpath(os.path.normcase(os.path.join(ONLINE_ROOT, rootDir)))
00144           if srcFile.startswith(rootPath + os.sep):
00145             return True
00146     return False
00147   isModuleInReleases = staticmethod(isModuleInReleases)
00148 
00149 
00150   def extractVersion(verStr):
00151     """!\brief Extract the version string from the CVS keyword string.
00152 
00153     if that's what it appears to be.  If it doesn't, just
00154     return the input.
00155 
00156     \param verStr CVS keyword string
00157 
00158     \return Version string
00159     """
00160     colon  = verStr.find(':')
00161     if colon != -1:
00162       dollar = verStr.find('$', colon)
00163       if dollar != -1:
00164         return verStr[colon+2:dollar-1]
00165     return verStr
00166   extractVersion = staticmethod(extractVersion)
00167 
00168   def calcDigest(filePath):
00169     """!\brief Calculate the digest for a given file.
00170 
00171     \param filePath Full path of the file
00172 
00173     \return Digest data.
00174     """
00175     digest = sha.new()
00176     f = file(filePath, 'rb')
00177     data = f.read(65536)
00178     while len(data) != 0:
00179       digest.update(data)
00180       data = f.read(65536)
00181     f.close()
00182     #return digest.hexdigest()
00183     return digest.digest()
00184   calcDigest = staticmethod(calcDigest)
00185 
00186 
00187   def getModuleDigests():
00188     """!\brief Retrieve digests of all loaded modules.
00189 
00190     All modules which do not have __version__ or __file__
00191     attributes are ignored.
00192 
00193     \return A dictionary of digest data keyed by module name
00194     """
00195     ignoreList = ['scipy.gplt','ROOT']
00196     digests = {}
00197     mstrList = sys.modules.keys()
00198     mstrList.sort()
00199     for mStr in mstrList:
00200       if mStr in ignoreList: continue
00201       m = sys.modules[mStr]
00202       if hasattr(m, '__version__') and hasattr(m, '__file__'):
00203         srcFile = inspect.getsourcefile(m)
00204         digest_value = Gversions.calcDigest(srcFile)
00205         if digest_value is not None:
00206           digests[mStr] = digest_value
00207     return digests
00208   getModuleDigests = staticmethod(getModuleDigests)
00209 
00210   def getReleaseFromDigest(digestDataFile=None, rootDir='LICOS'):
00211     """!\brief Given a root directory extract the release tag.
00212 
00213     The root directory defaults to LICOS. This method can be used
00214     to extract release tag info from sub-system root directories where
00215     the digestData file is stored.
00216 
00217     \param digestDataFile The path pointing to the digestData file.
00218                           If none $ONLINE_ROOT/rootDir/digestData is assumed
00219     \param rootDir        Root directory relative to $ONLINE_ROOT
00220 
00221     \return Release tag
00222     """
00223     rootPath = os.path.join(ONLINE_ROOT, rootDir)
00224     digestDataFile = os.path.join(rootPath, 'digestData')
00225     if not os.path.exists(digestDataFile):
00226       raise RuntimeError, "Can not find the digest database file at the indicated location: %s" % rootPath
00227     f = file(digestDataFile,'rb')
00228     try:
00229       dc = marshal.load(f)
00230     except ValueError:
00231       log.exception("")
00232       Gversions.logBadMarshalDataMessage()
00233       return
00234     exec(dc)
00235     return _digestData._release
00236   getReleaseFromDigest = staticmethod(getReleaseFromDigest)
00237 
00238   def logBadMarshalDataMessage():
00239     """!\brief Message that is logged when an exception occurs during the
00240     marshalling of the digest data file.
00241 
00242     """
00243     log.error("If the above exception is a 'bad marshal data' error,")
00244     log.error("it is likely that you need to update one or more of the")
00245     log.error("digestData files pointed to by the root directory")
00246     log.error("variables. To create the digestData file, enter the")
00247     log.error("following command for each of the root directories that you have:")
00248     log.error(os.path.expandvars(
00249      "$ONLINE_ROOT\\LICOS\\setup\\createDigestData root_dir %s XXX_00_00_00"
00250      ) % sys.prefix)
00251   logBadMarshalDataMessage = staticmethod(logBadMarshalDataMessage)
00252 
00253   def verifyModuleDigests(digestDataFile=None, rootDir='LICOS', additionalFilesToVerify=[]):
00254     """!\brief Compare loaded modules and any additional files
00255     against the digest data.
00256 
00257     \param digestDataFile          The path pointing to the digestData file.
00258                                    If none $\a $ONLINE_ROOT/rootDir/digestData is assumed
00259     \param rootDir                 Root directory
00260     \param additionalFilesToVerify A list of file paths to check
00261 
00262     \return A tuple containing the release tag and a list of module names
00263             which failed the verification
00264     """
00265     ignoreList = ['scipy.gplt','ROOT']
00266     failedModules = []
00267     rootPath = os.path.join(ONLINE_ROOT, rootDir)
00268     if digestDataFile is None:
00269       digestDataFile = os.path.join(rootPath, 'digestData')
00270     if not os.path.exists(digestDataFile):
00271       raise RuntimeError, "Can not find the digest database file at the indicated location: %s" % rootPath
00272     f = file(digestDataFile,'rb')
00273     try:
00274       dc = marshal.load(f)
00275     except ValueError:
00276       log.exception("")
00277       Gversions.logBadMarshalDataMessage()
00278       return (None, [])
00279     exec(dc)
00280     mstrList = sys.modules.keys()
00281     mstrList.sort()
00282     for mStr in mstrList:
00283       if mStr in ignoreList: continue
00284       #raw_input("Entering %s}" % mStr)
00285       m = sys.modules[mStr]
00286       if hasattr(m, '__file__'):
00287         srcFile = inspect.getsourcefile(m)
00288         if srcFile is not None:
00289           srcFile = os.path.normpath(os.path.normcase(srcFile))
00290           if srcFile.startswith(os.path.normpath(os.path.normcase(rootPath)) + os.sep):
00291             found = False
00292             for (filename, digest) in _digestData._digests.items():
00293               if srcFile == os.path.normpath(os.path.normcase(filename)):
00294                 found = True
00295                 digest_value = Gversions.calcDigest(srcFile)
00296                 if digest_value is None or digest != digest_value:
00297                   failedModules.append(mStr)
00298                 break
00299             if not found:
00300               failedModules.append(mStr)
00301       #raw_input("Exiting %s}" % mStr)
00302 
00303     for fileSpec in additionalFilesToVerify:
00304       for filePath in glob.glob(fileSpec):
00305         normPath = os.path.normpath(os.path.normcase(filePath))
00306         if normPath.startswith(os.path.normpath(os.path.normcase(rootPath)) + os.sep):
00307           for (filename, digest) in _digestData._digests.items():
00308             if normPath == os.path.normpath(os.path.normcase(filename)):
00309               digest_value = Gversions.calcDigest(normPath)
00310               if digest_value is None or digest != digest_value:
00311                 failedModules.append(os.path.split(filePath)[1])
00312               break
00313 
00314     return (_digestData._release, failedModules)
00315   verifyModuleDigests = staticmethod(verifyModuleDigests)
00316 
00317   def findVersionSymbol(filePath):
00318     """!\brief Find the __version__ symbol in the file.
00319 
00320     \param filePath Full path of the module source code
00321 
00322     \return Version number or None if not found.
00323     """
00324     found = False
00325     try:
00326       ast = compiler.parseFile(filePath)
00327       for node in ast.node.nodes:
00328         if node.__class__.__name__ == 'Assign':
00329           if sys.hexversion > 0x20305f0:
00330             if node.asList()[0].asList()[0] == '__version__':
00331               version = node.asList()[1].asList()[0]
00332             else:
00333               continue
00334           else:
00335             if node.asList()[0][0] == '__version__':
00336               version = node.asList()[1][0]
00337             else:
00338               continue
00339           if type(version) is str and version.startswith('$Revision:'):
00340             return Gversions.extractVersion(version)
00341           else:
00342             #print "*** %s: __version__ symbol found but does not begin with $Revision:" % filePath
00343             return None
00344     except:
00345       log.exception("Error in parsing file: %s" % filePath)
00346 
00347     if not found:
00348       #print "*** %s: Missing __version__" % filePath
00349       return None
00350   findVersionSymbol = staticmethod(findVersionSymbol)
00351 
00352   def verifyFileDigests(rootDir='LICOS', digestDataFile=None):
00353     """!\brief Verify the files under a root directory against the digest data file.
00354 
00355     \param rootDir                 Root directory
00356     \param digestDataFile          The path pointing to the digestData file.
00357                                    If none $ONLINE_ROOT/rootDir/digestData is assumed
00358 
00359     \return A tuple containing the release tag and a list of module names
00360             which failed the verification
00361     """
00362     skipVersionCheck = {'LICOS': ['sipconfig.py', 'pyqtconfig.py', 'testAppNoVersion.py']}
00363     failedFiles = []
00364 
00365     rootPath = os.path.join(ONLINE_ROOT, rootDir)
00366     if digestDataFile is None:
00367       digestDataFile = os.path.join(rootPath, 'digestData')
00368     if not os.path.exists(digestDataFile):
00369       raise RuntimeError, "Can not find the digest database file at the indicated location: %s" % rootPath
00370     f = file(digestDataFile,'rb')
00371     try:
00372       dc = marshal.load(f)
00373     except ValueError:
00374       log.exception("")
00375       Gversions.logBadMarshalDataMessage()
00376       return (None, [])
00377     exec(dc)
00378     for (filename, digest) in _digestData._digests.items():
00379       if os.path.exists(filename):
00380         fileDigest = Gversions.calcDigest(filename)
00381         if digest != fileDigest:
00382           failedFiles.append(filename)
00383         if os.path.splitext(filename)[1] == '.py':
00384           if rootDir in skipVersionCheck:
00385             if os.path.split(filename)[1] in skipVersionCheck[rootDir]:
00386               continue
00387           if Gversions.findVersionSymbol(filename) is None:
00388             failedFiles.append(filename)
00389       else:
00390         failedFiles.append(filename)
00391     return (_digestData._release, failedFiles)
00392   verifyFileDigests = staticmethod(verifyFileDigests)
00393 
00394   def collectDigests(path):
00395     """!\brief Given a path, collect digests from all files.
00396 
00397     \param path Root directory
00398 
00399     \return Dictionary of digests
00400     """
00401     def collectDigests2(digests, dir, files):
00402       """!\brief os.path.walk handler method.
00403 
00404       \param digests Dictionary of digests keyed by the full path name
00405       \param dir     Current directory being processed
00406       \param files   List of files in the current directory
00407       """
00408       ignoredDirs = ['CVS']
00409       validExtensions = ['.py',  # Python source
00410                          '.exe', # Windows executables
00411                          '.xml', # Schema and other XML files
00412                          '.vx',  # VxWorks scripts
00413                          '.o',   # C/C++ Object modules
00414                          '.cfg', # Configuration files
00415                          '.f',   # LHK configuration file binaries
00416                          '.bat', # Windows batch files
00417                          '.csh', # Shell scripts
00418                          '.sh',  # Shell scripts
00419                          '.txt', # Configuration files
00420                         ]
00421       l = len(files)
00422       for i in range(l-1,-1,-1):
00423         if files[i] in ignoredDirs:
00424           del files[i]
00425         else:
00426           fullFilename = os.path.normpath(os.path.join(dir, files[i]))
00427           if not os.path.isdir(fullFilename) and not os.path.islink(fullFilename):
00428             if os.path.splitext(files[i])[1].lower() in validExtensions:
00429               digest = Gversions.calcDigest(fullFilename)
00430               if digest is not None:
00431                 if digests.has_key(fullFilename):
00432                   log.error("DUPLICATE ENTRY DETECTED: %s" % fullFilename)
00433                 else:
00434                   digests[fullFilename] = digest
00435               else:
00436                 log.error("NO DIGEST FOR %s" % files[i])
00437     digests = {}
00438     path = os.path.normpath(os.path.abspath(path))
00439     if os.path.isdir(path):
00440       os.path.walk(path, collectDigests2, digests)
00441     #~ for (file, digest) in digests.items():
00442       #~ print file #, repr(digest)
00443     #~ x = digests.keys()
00444     #~ x.sort()
00445     #~ for f in x:
00446       #~ print f
00447       return digests
00448     else:
00449       return None
00450   collectDigests = staticmethod(collectDigests)
00451 
00452   def printVersions():
00453     """!\brief Print module versions to the console.
00454 
00455     """
00456     print "\nCurrent Release: %s\n" % Gversions.getReleaseFromDigest()
00457     print "%-45s %30.30s" % ("Module", "Version")
00458     print "%-45s %30.30s" % ("--------", "-------")
00459     versions = Gversions.getModuleVersions()
00460     modList = versions.keys()
00461     modList.sort()
00462     for module in modList:
00463       print "%-45s %30.30s" % (module, versions[module])
00464   printVersions = staticmethod(printVersions)
00465 
00466 if __name__ == '__main__':
00467   # Imports here are specified so that the versions for
00468   # all loaded modules can be retrieved. Add any additional
00469   # module imports here if they are to be reported in the
00470   # output of versions.bat
00471   # In order for all module versions to be reported, the __all__
00472   # variable in __init__.py of each package needs to be fully
00473   # populated.
00474   from LICOS.core.archive import *
00475   from LICOS.core.dispatch import *
00476   from LICOS.core.distributor import *
00477   from LICOS.core.export import *
00478   from LICOS.core.thread import *
00479   from LICOS.lib import *
00480   from LICOS.lib.cmdTlmDb import *
00481   from LICOS.lib.currValTable import *
00482   from LICOS.lib.scriptEngine import *
00483   from LICOS.lib.testReport import *
00484   from LICOS.science import *
00485   from LICOS.scriptEngine import *
00486   from LICOS.start import *
00487   from LICOS.telemetry import *
00488   from LICOS.tests.lib import *
00489   from LICOS.tests.scriptEngine import *
00490   from LICOS.tests.standalone import *
00491   from LICOS.tools.alarm import *
00492   from LICOS.tools.EnvDataXfer import *
00493   from LICOS.tools.LATc import *
00494   from LICOS.tools.logger import *
00495   from LICOS.tools.monitor import *
00496   from LICOS.tools.proc import *
00497   from LICOS.tools.proxy import *
00498   from LICOS.tools.teleCmdGUI import *
00499   from LICOS.tools.utcClock import *
00500   from LICOS.util import *
00501 
00502   import LDF
00503   import qt
00504   import Ft.Xml
00505   Gversions.printVersions()

Generated on Thu Apr 27 20:52:41 2006 for LICOS L02-01-00 by doxygen 1.4.6-NO