00001
00002
00003
00004
00005
00006
00007
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
00076
00077
00078 ]
00079 versions = {}
00080 if lcm is not None:
00081
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
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
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
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
00343 return None
00344 except:
00345 log.exception("Error in parsing file: %s" % filePath)
00346
00347 if not found:
00348
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',
00410 '.exe',
00411 '.xml',
00412 '.vx',
00413 '.o',
00414 '.cfg',
00415 '.f',
00416 '.bat',
00417 '.csh',
00418 '.sh',
00419 '.txt',
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
00442
00443
00444
00445
00446
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
00468
00469
00470
00471
00472
00473
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()