CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
cmsPerfPublish.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #G.Benelli Jan 22 2007, J. Nicolson 12 Aug 2008
3 #A little script to move Simulation Performance Suite
4 #relevant html and log files into our public area
5 #/afs/cern.ch/cms/sdt/web/performance/simulation/
6 #Set here the standard number of events (could become an option... or could be read from the log...)
7 
8 ###############################
9 #
10 # Warning!!! We should use copytree() instead of our self defined copytree4
11 # However, copytree does not use ignore patterns (for filtering files)
12 # before python v2.6, when we upgrade to python 2.6 we should use this
13 # functionality.
14 import tempfile as tmp
15 import optparse as opt
16 import cmsPerfRegress as cpr
17 import re, os, sys, time, glob, socket, fnmatch
18 from shutil import copy2, copystat
19 from stat import *
20 from cmsPerfCommons import CandFname, Step, ProductionSteps, Candles
21 import ROOT
22 #Switching from os.popen4 to subprocess.Popen for Python 2.6 (340_pre5 onwards):
23 import subprocess
24 
25 PROG_NAME = os.path.basename(sys.argv[0])
26 DEF_RELVAL = "/afs/cern.ch/cms/sdt/web/performance/RelVal"
27 DEF_SIMUL = "/afs/cern.ch/cms/sdt/web/performance/simulation"
28 TMP_DIR = ""
29 cpFileFilter = ( "*.root", ) # Unix pattern matching not regex
30 cpDirFilter = ( ) # Unix pattern matching not regex
31 
32 TimeSizeNumOfEvents = -9999
33 IgProfNumOfEvents = -9999
34 CallgrindNumOfEvents = -9999
35 MemcheckNumOfEvents = -9999
36 
37 DirName=( #These need to match the candle directory names ending (depending on the type of profiling)
38  "TimeSize",
39  "IgProf",
40  "IgProf_Perf",
41  "IgProf_Mem",
42  "Callgrind",
43  "Memcheck",
44  #Adding the extra PU directories:
45  "PU_TimeSize",
46  "PU_IgProf",
47  "PU_IgProf_Perf",
48  "PU_IgProf_Mem",
49  "PU_Callgrind",
50  "PU_Memcheck"
51  )
52 #Defining Steps as a union of Step and ProductionSteps:
53 Steps=set(Step+ProductionSteps+["GEN,FASTSIM","GEN,FASTSIM_PILEUP"]) #Adding GEN,FASTSIM too by hand.
54 print Steps
55 
56 ##################
57 #
58 # Small functions
59 #
60 
62  "Relative directory could not be determined"
63 
64 def fail(errstr=""):
65  print errstr
66  delTmpDir()
67  sys.exit()
68 
69 def addtrailingslash(adir):
70  trail = re.compile("/$")
71  if os.path.isdir(adir) and not trail.search(adir):
72  adir = adir + "/"
73  return adir
74 
75 def getDate():
76  return time.ctime()
77 
78 def getcmdBasic(cmd):
79  #Obsolete popen4-> subprocess.Popen
80  #return os.popen4(cmd)[1].read().strip()
81  return subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().strip()
82 
83 def getcmd(command):
84  if _debug > 2:
85  print command
86  #Obsolete popen4-> subprocess.Popen
87  #return os.popen4(command)[1].read().strip()
88  return subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().strip()
89 
90 def prettySize(size):
91  nega = size < 0
92  if nega:
93  size = -size
94  suffixes = [("B",2**10), ("k",2**20), ("M",2**30), ("G",2**40), ("T",2**50)]
95  for suf, lim in suffixes:
96  if size > lim:
97  continue
98  else:
99  if nega:
100  return "-" + round(size/float(lim/2**10),2).__str__() + suf
101  else:
102  return round(size/float(lim/2**10),2).__str__()+suf
103 
104 class Row(object):
105 
106  def __init__(self,table):
107  self.table = table
108  self.coldata = {}
109 
110  def __str__(self):
111  return str(self.coldata)
112 
113  def addEntry(self,colname,value):
114  self.table._newCol(colname)
115  self.coldata[colname] = value
116 
117  def getRowDict(self):
118  return self.coldata
119 
120 class Table(object):
121 
122  def __init__(self):
123  self.colNames = []
124  self.keys = [None]
125  self.rows = {None: self.colNames}
126 
127  def __str__(self):
128  out = self.keys
129  out += "\n" + self.rows
130  return out
131 
132  def _newCol(self,name):
133  if name in self.colNames:
134  pass
135  else:
136  self.colNames.append(name)
137 
138  def getCols(self):
139  return self.colNames
140 
141  def newRow(self,name):
142  if name in self.rows.keys():
143  pass
144  else:
145  self.keys.append(name)
146  self.rows[name] = Row(self)
147 
148  return self.rows[name]
149 
150  def getTable(self,mode=0):
151  name = "Total"
152 
153  for key in self.keys:
154  if key == None:
155  pass
156  else:
157  total1 = 0
158  total2 = 0
159  rowobj = self.rows[key]
160  rowdict = rowobj.getRowDict()
161  for col in self.colNames:
162  if col == None:
163  pass
164  elif rowdict.has_key(col) and not col == name:
165  if mode == 1:
166  total1 += rowdict[col]
167  else:
168  (step_tot1, step_tot2) = rowdict[col]
169  total1 += step_tot1
170  total2 += step_tot2
171  if mode == 1:
172  rowobj.addEntry(name,total1)
173  else:
174  rowobj.addEntry(name,(total1,total2))
175 
176  return (self.keys, self.rows)
177 
178  def addRow(self,row,name):
179  if name in self.rows.keys():
180  pass
181  else:
182  self.keys.append(name)
183  self.rows[name] = row
184 
185  def transpose(self):
186  transp = Table()
187  for col in self.colnames:
188  rowobj = transp.newRow(col)
189  for key in self.keys:
190  if key == None:
191  pass
192  else:
193  row_dict = self.rows[key].getRowDict()
194  if row_dict.has_key(key):
195  rowobj.addEntry(key,row_dict[col])
196  return transp
197 
198 ######################
199 #
200 # Main
201 #
202 def main():
203  global TimeSizeNumOfEvents,IgProfNumOfEvents,CallgrindNumOfEvents,MemcheckNumOfEvents
204  #Bad design... why is this function defined here?
205  #Either no function, or define somewhere else.
206  def _copyReportsToStaging(repdir,LogFiles,cmsScimarkDir,stage):
207  """Use function syscp to copy LogFiles and cmsScimarkDir over to staging area"""
208  if _verbose:
209  print "Copying the logfiles to %s/." % stage
210  print "Copying the cmsScimark2 results to the %s/." % stage
211 
212  syscp(LogFiles , stage + "/")
213  syscp(cmsScimarkDir, stage + "/")
214 
215  #Same comment as above about the opportunity of this function definition here.
216  def _createLogFile(LogFile,date,LocalPath,ShowTagsResult):
217  """Creating a small logfile with basic publication script running information (never used really by other scripts in the suite)."""
218  try:
219  LOG = open(LogFile,"w")
220  if _verbose:
221  print "Writing Production Host, Location, Release and Tags information in %s" % LogFile
222  LOG.write("These performance tests were executed on host %s and published on %s" % (HOST,date))
223  LOG.write("They were run in %s" % LocalPath)
224  LOG.write("Results of showtags -r in the local release:\n%s" % ShowTagsResult)
225  LOG.close()
226  except IOError, detail:
227  print "WARNING: Can't create log file"
228  print detail
229 
230  # Print Program header
231  print_header()
232 
233  # Get environment variables
234  #FIXME: should check into this and make sure the proper variables for the tests being published are read from logfile (case of deprecated releases...)
235  print "\n Getting Environment variables..."
236  (LocalPath, ShowTagsResult) = get_environ()
237 
238  # Parse options
239  (options,args) = optionparse()
240 
241  # Determine program parameters and input/staging locations
242  print "\n Determining locations for input and staging..."
243  (drive,path,remote,stage,port,repdir,prevrev,igprof_remotedir) = getStageRepDirs(options,args)
244 
245  #Get the number of events for each test from logfile:
246  print "\n Getting the number of events for each test..."
247  #Let's do a quick implementation of something that looks at the logfile:
248  cmsPerfSuiteLogfile="%s/cmsPerfSuite.log"%repdir
249 
250  if os.path.exists(cmsPerfSuiteLogfile):
251  try:
252  (TimeSizeNumOfEvents,IgProfNumOfEvents,CallgrindNumOfEvents,MemcheckNumOfEvents)=getNumOfEventsFromLog(cmsPerfSuiteLogfile)
253  #Get the CMSSW version and SCRAM architecture from log (need these for the IgProf new publishing with igprof-navigator)
254  (CMSSW_arch,CMSSW_version)=getArchVersionFromLog(cmsPerfSuiteLogfile)
255  #For now keep the dangerous default? Better set it to a negative number...
256  except:
257  print "There was an issue in reading out the number of events for the various tests or the architecture/CMSSW version using the standard logfile %s"%cmsPerfSuiteLogFile
258  print "Check that the format was not changed: this scripts relies on the initial perfsuite arguments to be dumped in the logfile one per line!"
259  print "For now taking the default values for all tests (0)!"
260 
261  print "\n Scan report directory..."
262  # Retrieve some directories and information about them
263  (ExecutionDate,LogFiles,date,cmsScimarkResults,cmsScimarkDir) = scanReportArea(repdir)
264  print "cmsScimarkResults are %s"%cmsScimarkResults
265  print "\n Copy report files to staging directory..."
266  # Copy reports to staging area
267  _copyReportsToStaging(repdir,LogFiles,cmsScimarkDir,stage)
268 
269  print "\n Creating log file..."
270  # Produce a small logfile with basic info on the Production area
271  _createLogFile("%s/ProductionLog.txt" % stage,date,repdir,ShowTagsResult)
272 
273  #Check if there are IgProf tests:
274  for dirname in os.listdir(repdir):
275  if "IgProf" in dirname:
276  print "\n Handling IgProf reports..."
277  # add a function to handle the IgProf reports
278  stageIgProfReports(igprof_remotedir,CMSSW_arch,CMSSW_version)
279 
280  print "\n Creating HTML files..."
281  # create HTML files
282  createWebReports(stage,repdir,ExecutionDate,LogFiles,cmsScimarkResults,date,prevrev)
283 
284  print "\n Copy profiling logs to staging directory..."
285  # Copy over profiling logs...
286  getDirnameDirs(repdir,stage)
287 
288  # Send files to remote location
289  if remote:
290  print "\n Uploading web report to remote location..."
291  syncToRemoteLoc(stage,drive,path,port)
292  print "\n Finished uploading! Now removing staging directory..."
293  delTmpDir()
294 
295  print "\n Finished!!!"
296 
297 ##########################
298 #
299 # Get require environment variables
300 #
302  global CMSSW_VERSION, CMSSW_RELEASE_BASE, CMSSW_BASE, HOST, USER, BASE_PERFORMANCE, CMSSW_WORK
303  global DEF_RELVAL, DEF_SIMUL
304 
305  try:
306  CMSSW_VERSION=os.environ['CMSSW_VERSION']
307  CMSSW_RELEASE_BASE=os.environ['CMSSW_RELEASE_BASE']
308  CMSSW_BASE=os.environ['CMSSW_BASE']
309  HOST=os.environ['HOST']
310  USER=os.environ['USER']
311  CMSSW_WORK = os.path.join(CMSSW_BASE,"work/Results")
312  except KeyError, detail:
313  fail("ERROR: Could not retrieve some necessary environment variables. Have you ran scramv1 runtime -csh yet?. Details: %s" % detail)
314 
315  LocalPath=getcmdBasic("pwd")
316  ShowTagsResult=getcmdBasic("showtags -r")
317 
318  #Adding a check for a local version of the packages
319  PerformancePkg="%s/src/Validation/Performance" % CMSSW_BASE
320  if (os.path.exists(PerformancePkg)):
321  BASE_PERFORMANCE=PerformancePkg
322  print "**Using LOCAL version of Validation/Performance instead of the RELEASE version**"
323  else:
324  BASE_PERFORMANCE="%s/src/Validation/Performance" % CMSSW_RELEASE_BASE
325 
326  return (LocalPath,ShowTagsResult)
327 
328 ###############
329 #Option parser
330 #
332  global PROG_NAME, _debug, _dryrun, _verbose
333 
334  parser = opt.OptionParser(usage=("""%s [HOST:]DEST_PATH [Options]
335 
336  Arguments:
337  [HOST:]DEST_PATH - This is where the report should be published, you can specify a local path or a directory on a remote machine (HOST is optional)
338 
339  Examples:
340  Publish report to default local directory
341  ./%s
342  Publish report to \"/some/local/dir\"
343  ./%s /some/local/dir
344  Publish report to \"/some/other/dir\" remote host \"hal.cern.ch\"
345  ./%s hal.cern.ch:/some/other/dir
346  Publish report to default relval location (this could be remote or local depending on the hardcoded default)
347  ./%s --relval"""
348  % ( PROG_NAME, PROG_NAME, PROG_NAME, PROG_NAME, PROG_NAME)))
349 
350  devel = opt.OptionGroup(parser, "Developer Options",
351  "Caution: use these options at your own risk."
352  "It is believed that some of them bite.\n")
353  parser.add_option(
354  '--relval',
355  action="store_true",
356  dest='relval',
357  help='Use the default RelVal location',
358  #metavar='<STEPS>',
359  )
360 
361  parser.add_option(
362  '-v',
363  '--verbose',
364  action="store_true",
365  dest='verbose',
366  help='output more information',
367  #metavar='<STEPS>',
368  )
369 
370  parser.add_option(
371  '--simul',
372  action="store_true",
373  dest='simulation',
374  help='Use the default simulation location',
375  #metavar='<STEPS>',
376  )
377 
378  parser.add_option(
379  '--prev',
380  type="string",
381  dest='previousrev',
382  help='The override the name of the previous release. Default is the string obtain from the identification file REGRESSION.<prevrel>.vs.<newrel>',
383  metavar='<NAME>',
384  default="",
385  )
386 
387  parser.add_option(
388  '--input',
389  type="string",
390  dest='repdir',
391  help='The location of the report files to be published',
392  metavar='<DIR>'
393  )
394 
395  parser.add_option(
396  '-p',
397  '--port',
398  type='int',
399  dest='port',
400  help='Use a particular port number to rsync material to a remote server',
401  metavar='<PORT>'
402  )
403  parser.add_option(
404  '--igprof',
405  type='string',
406  dest='ig_remotedir',
407  default='IgProfData', #Reverting to local default publication for now#'/afs/cern.ch/cms/sdt/web/qa/igprof-testbed/data', #For now going straight into AFS... later implement security via local disk on cmsperfvm and cron job there:
408  #default='cmsperfvm:/data/projects/conf/PerfSuiteDB/IgProfData', #This should not change often! In this virtual machine a cron job will run to move stuff to AFS.
409  help='Specify an AFS or host:mydir remote directory instead of default one',
410  metavar='<IGPROF REMOTE DIRECTORY>'
411  )
412 
413  devel.add_option(
414  '-d',
415  '--debug',
416  type='int',
417  dest='debug',
418  help='Show debug output',
419  #metavar='DEBUG',
420  )
421 
422  devel.add_option(
423  '--dry-run',
424  action="store_true",
425  dest='dryrun',
426  help='Do not send files to remote server, but run everything else',
427  #metavar='DEBUG',
428  )
429 
430  repdirdef = os.getcwd()
431  parser.set_defaults(debug=0,simulation=False,relval=False,port=873,pretend=False,repdir=repdirdef,verbose=False)
432  parser.add_option_group(devel)
433 
434  (options, args) = parser.parse_args()
435 
436  _debug = options.debug
437  _dryrun = options.dryrun
438  _verbose = options.verbose
439 
440  numofargs = len(args)
441 
442  if (options.simulation and options.relval) or ((options.simulation or options.relval) and numofargs >= 1):
443  parser.error("You can not specify simulation and relval together. Neither can you specify simulation or relval AND a path")
444  sys.exit()
445 
446  return (options, args)
447 
448 
449 #A function to get the number of events for each test from the logfile:
451  '''A very fragile function to get the Number of events for each test by parsing the logfile of the Suite. This relies on the fact that nobody will turn off the print out of the options in the cmsPerfSuite.py output... ARGH!'''
452  log=open(logfile,"r")
453  TimeSizeEvents=0
454  IgProfEvents=0
455  CallgrindEvents=0
456  MemcheckEvents=0
457  for line in log:
458  #FIXME:
459  #For robustness could read this from the Launching the X tests (.....) with N events each and keep that format decent for parsing.
460  #One more place where XML would seem a better choice to extract information (Timing of the performance suite in general is also, but publishing and harvesting some other info as well).
461  if 'TimeSizeEvents' in line and not TimeSizeEvents:
462  lineitems=line.split()
463  TimeSizeEvents=lineitems[lineitems.index('TimeSizeEvents')+1]
464  if 'IgProfEvents' in line and not IgProfEvents:
465  lineitems=line.split()
466  IgProfEvents=lineitems[lineitems.index('IgProfEvents')+1]
467  if 'CallgrindEvents' in line and not CallgrindEvents:
468  lineitems=line.split()
469  CallgrindEvents=lineitems[lineitems.index('CallgrindEvents')+1]
470  if 'MemcheckEvents' in line and not MemcheckEvents:
471  lineitems=line.split()
472  MemcheckEvents=lineitems[lineitems.index('MemcheckEvents')+1]
473  return (TimeSizeEvents,IgProfEvents,CallgrindEvents,MemcheckEvents)
474 
476  '''Another very fragile function to get the architecture and the CMSSW version parsing the logfile...'''
477  log=open(logfile,"r")
478  arch=re.compile("^Current Architecture is")
479  version=re.compile("^Current CMSSW version is")
480  CMSSW_arch="UNKNOWN_ARCH"
481  CMSSW_version="UNKNOWN_VERSION"
482  for line in log:
483  if arch.search(line):
484  CMSSW_arch=line.split()[3]
485  if version.search(line):
486  CMSSW_version=line.split()[4]
487  return(CMSSW_arch,CMSSW_version)
488 
489 
490 #####################
491 #
492 # Determine locations of staging and report dirs
493 #
494 def getStageRepDirs(options,args):
495  global TMP_DIR, IS_TMP, DEF_LOCAL, CMSSW_VERSION
496  DEF_LOCAL = CMSSW_WORK
497  numofargs = len(args)
498 
499  repdir = os.path.abspath(options.repdir)
500  repdir = addtrailingslash(repdir)
501 
502  if not os.path.exists(repdir):
503  fail("ERROR: The specified report directory %s to retrieve report information from does not exist, exiting" % repdir)
504 
505  previousrev = options.previousrev
506  if previousrev == "":
507  regressfiles = glob.glob("%s/REGRESSION.*.vs.*" % repdir)
508  if not len(regressfiles) == 0:
509  regressID = regressfiles[0]
510  base = os.path.basename(regressID)
511  split = base.split(".")
512  previousrev = split[1]
513  currentrel = split[3]
514  print "Regression Identification file exists, renaming report title for regression report. Old ver: %s" % previousrev
515  else:
516  print "No regression ID file exists and previous release name was not specified. Producing normal report."
517  else:
518  print "Previous release name was specified, renaming report title for regression report. Old ver %s" % previousrev
519 
520  uri = ""
521  defaultlocal = False
522  if options.simulation:
523  uri = DEF_SIMUL
524  elif options.relval:
525  uri = DEF_RELVAL
526  elif numofargs >= 1:
527  uri = args[0] # Add directory CMSSW_VERSION later in temp! Not now, otherwise we get into a mess if this is a remote dir
528  else:
529  defaultlocal = True
530  uri = DEF_LOCAL
531 
532  ####
533  #
534  # Determine if location is remote
535  #
536  # Try not to re-arrange we don't want to assume that default locations are not remote
537  #
538  ####
539 
540  drive = ""
541  path = ""
542  if ":" in uri:
543  drive, path = uri.split(":",1)
544  else:
545  path = uri
546 
547  if drive == "":
548  path = os.path.abspath(path)
549  remote = not drive == ""
550 
551  if remote:
552  unResolved = True
553  try:
554  socket.getaddrinfo(drive,53)
555  unResolved = False
556  except socket.gaierror:
557  unResolved = True
558 
559  # try see if it's an ipaddress
560  if unResolved:
561  try:
562  socket.gethostbyaddr(drive)
563  unResolved = False
564  except socket.gaierror:
565  unResolved = True
566  if unResolved:
567  print "ERROR: Can not determine your hostname or ipv{4,6} address %s" % drive
568  if not (_dryrun or _test):
569  fail("exiting...")
570 
571  if (not remote) and (not options.port == 873) :
572  print "WARNING: Can not use a port if not performing a remote copy, ignoring"
573  port = options.port
574 
575  ###
576  #
577  # Determine Staging Area
578  #
579 
580  StagingArea=""
581  localExists = os.path.exists("%s/%s" % (CMSSW_WORK,CMSSW_VERSION))
582 
583  if remote:
584  #Cannot use this since the /tmp is limited to 2GB on lxbuild machines!
585  #TMP_DIR=tmp.mkdtemp(prefix="/tmp/%s" % PROG_NAME)
586  TMP_DIR=tmp.mkdtemp(prefix="/build/%s" % PROG_NAME)
587  StagingArea = TMP_DIR
588  #Local but dir already exists
589  elif defaultlocal and localExists:
590  TMP_DIR=tmp.mkdtemp(prefix="%s/%s" % (CMSSW_WORK,CMSSW_VERSION))
591  StagingArea = TMP_DIR
592  print "WARNING: %s already exists, creating a temporary staging area %s" % (CMSSW_WORK,TMP_DIR)
593  #Local cases
594  elif defaultlocal:
595  StagingArea = CMSSW_WORK
596  try:
597  os.mkdir(os.path.join(CMSSW_BASE,"work"))
598  os.mkdir(os.path.join(CMSSW_BASE,"work","Results"))
599  except OSError:
600  pass
601  print "**User did not specify location of results, staging in default %s**" % StagingArea
602  else:
603  print "**User chose to publish results in a local directory**"
604  StagingArea = path
605  if not os.path.exists(path):
606  try:
607  os.mkdir("%s" % path)
608  except OSError, detail:
609  if detail.errno == 13:
610  fail("ERROR: Failed to create staging area %s because permission was denied " % StagingArea)
611  elif detail.errno == 17:
612  #If the directory already exists just carry on
613  pass
614  else:
615  fail("ERROR: There was some problem (%s) when creating the staging directory" % detail)
616 
617  IS_TMP = not TMP_DIR == ""
618  ######
619  #
620  # create Version dir
621  # This is why when we create a Tmp dir we get the version(tmpname)/version
622  # structure. We should remove this if we don't want it but i dont think it matters
623  StagingArea="%s/%s" % (StagingArea,CMSSW_VERSION)
624  try:
625  os.mkdir("%s" % StagingArea)
626  except OSError, detail:
627  if detail.errno == 13:
628  fail("ERROR: Failed to create staging area %s because permission was denied " % StagingArea)
629  elif detail.errno == 17:
630  #If the directory already exists just carry on
631  pass
632  else:
633  fail("ERROR: There was some problem (%s) when creating the staging directory" % detail)
634 
635  return (drive,path,remote,StagingArea,port,repdir,previousrev,options.ig_remotedir)
636 
637 ####################
638 #
639 # Scan report area for required things
640 #
641 def scanReportArea(repdir):
642  """Scans the working directory for cms*.logs (cmsPerfSuite.log and cmsScimark*.log, and cmsScimark results.
643  It returns Execution date (completion), current date, list of logfiles and cmsScimark results"""
644  date=getDate()
645  LogFiles = glob.glob(repdir + "cms*.log")
646  if _verbose:
647  print "Found the following log files:"
648  print LogFiles
649 
650  cmsScimarkDir = glob.glob(repdir + "cmsScimarkResults_*")
651  if _verbose:
652  print "Found the following cmsScimark2 results directories:"
653  print cmsScimarkDir
654 
655  cmsScimarkResults = []
656  for adir in cmsScimarkDir:
657  htmlfiles = glob.glob(adir + "/*.html")
658  #FIXME:
659  #Really unnecessary use of map in my opinion
660  #Could do with cmsScimarkResults.extend(htmlfiles)
661  map(cmsScimarkResults.append,htmlfiles)
662 
663  ExecutionDateLast = ""
664  ExecutionDate = ""
665  ExecutionDateSec=0
666  cmsreg = re.compile("^cmsPerfSuite")
667  for logf in LogFiles:
668  if cmsreg.search(logf):
669  ExecutionDateLastSec = os.stat(logf)[ST_CTIME]
670  ExecutionDateLast = os.stat(logf)[ST_MTIME]
671  if _verbose:
672  print "Execution (completion) date for %s was: %s" % (logf,ExecutionDateLast)
673  if (ExecutionDateLastSec > ExecutionDateSec):
674  ExecutionDateSec = ExecutionDateLastSec
675  ExecutionDate = ExecutionDateLast
676 
677  if ExecutionDate == "":
678  ExecutionDate = ExecutionDateLast
679 
680  return (ExecutionDate,LogFiles,date,cmsScimarkResults,cmsScimarkDir)
681 
682 def createRegressHTML(reghtml,repdir,outd,CurrentCandle,htmNames):
683  RegressTmplHTML="%s/doc/regress.html" % (BASE_PERFORMANCE)
684  candnreg = re.compile("CandleName")
685  candhreg = re.compile("CandlesHere")
686  try:
687  REGR = open(reghtml,"w")
688  for line in open(RegressTmplHTML):
689  if candhreg.search(line):
690  html = "<table>"
691 
692  for x in htmNames:
693  abspath = os.path.join(repdir,outd)
694  if os.path.exists(abspath):
695  html += "<tr><td><a href=\"./%s/%s\"><img src=\"./%s/%s\" /></a></td></tr>\n" % (outd,x,outd,x)
696  else:
697  html += "<tr><td> %s does not exist probably because the log file for the previous release was missing</td></tr>" % (abspath)
698  html += "</table>"
699  REGR.write(html)
700  elif candnreg.search(line):
701  REGR.write(CurrentCandle)
702  else:
703  REGR.write(line)
704  except IOError, detail:
705  print "ERROR: Could not write regression html %s because %s" % (os.path.basename(reghtml),detail)
706 
707 def getOutputNames(base,reportName):
708  logreg = re.compile("(.*)\.log$")
709  matches = logreg.search(reportName)
710  logdir = logreg.sub(matches.groups()[0],reportName)
711  outd = os.path.join(base,logdir)
712  nologext = matches.groups()[0]
713  return (nologext,outd)
714 
715 def rootfile_cmp(x,y):
716  (fname,x) = x
717  (fname,y) = y
718  x = os.path.basename(x)
719  y = os.path.basename(y)
720  stepreg = re.compile("%s_(..*)\.root" % fname)
721  if stepreg.search(x):
722  x = stepreg.search(x).groups()[0]
723  if stepreg.search(y):
724  y = stepreg.search(y).groups()[0]
725  return step_cmp(x,y)
726 
727 def dirname_cmp(x,y):
728  (candle,prof,x) = x
729  (candle,prof,y) = y
730  x = os.path.basename(x)
731  y = os.path.basename(y)
732  stepreg = re.compile("%s_(..*)_%s" % (candle,prof))
733  if stepreg.search(x):
734  x = stepreg.search(x).groups()[0]
735  if stepreg.search(y):
736  y = stepreg.search(y).groups()[0]
737  return step_cmp(x,y)
738 
740  (candle,prof,x) = x
741  (candle,prof,y) = y
742  x = os.path.basename(x)
743  y = os.path.basename(y)
744  stepreg = re.compile("%s_(..*)_%s_regression" % (candle,prof))
745  if stepreg.search(x):
746  x = stepreg.search(x).groups()[0]
747  if stepreg.search(y):
748  y = stepreg.search(y).groups()[0]
749  return step_cmp(x,y)
750 
751 def logrep_cmp(x,y):
752  (fname,x) = x
753  (fname,y) = y
754  x = os.path.basename(x)
755  y = os.path.basename(y)
756  stepreg = re.compile("%s_(..*)_TimingReport.log" % fname)
757  if stepreg.search(x):
758  x = stepreg.search(x).groups()[0]
759  if stepreg.search(y):
760  y = stepreg.search(y).groups()[0]
761  return step_cmp(x,y)
762 
763 def timerep_cmp(x,y):
764  (fname,x) = x
765  (fname,y) = y
766  x = os.path.basename(x)
767  y = os.path.basename(y)
768  stepreg = re.compile("%s_(..*)_TimingReport" % fname)
769  if stepreg.search(x):
770  x = stepreg.search(x).groups()[0]
771  if stepreg.search(y):
772  y = stepreg.search(y).groups()[0]
773  return step_cmp(x,y)
774 
775 def step_cmp(x,y):
776  xstr = x
777  ystr = y
778  x_idx = -1
779  y_idx = -1
780  bestx_idx = -1
781  besty_idx = -1
782  sndbst_x = -1
783  sndbst_y = -1
784  last_x = -1
785  last_y = -1
786  for i in range(len(Step)):
787  stepreg = re.compile("^%s.*" % Step[i])
788  # fallback
789  if Step[i] in xstr and sndbst_x == -1:
790  last_x = i
791  if Step[i] in ystr and sndbst_y == -1:
792  last_y = i
793  # second best
794  if xstr in Step[i] and bestx_idx == -1:
795  sndbst_x = i
796  if ystr in Step[i] and besty_idx == -1:
797  sndbst_y = i
798  # next best
799  if stepreg.search(xstr) and x_idx == -1:
800  bestx_idx = i # If an exact match has not been found but something similar has, set x best index
801  if stepreg.search(ystr) and y_idx == -1:
802  besty_idx = i # If an exact match has not been found but something similar has, set y best index
803  # actual
804  if Step[i] == xstr and x_idx == -1:
805  x_idx = i # if an exact match has been found then set x index
806  if Step[i] == ystr and y_idx == -1:
807  y_idx = i # if an exact match has been found then set y index
808  if not ( x_idx == -1 or y_idx == -1):
809  break # if an exact match has been found for both, stop
810 
811  # use best match if we still can't find indices
812  if x_idx == -1:
813  x_idx = bestx_idx
814  if y_idx == -1:
815  y_idx = besty_idx
816 
817  # use second best if we still can't find indices
818  if x_idx == -1:
819  x_idx = sndbst_x
820  if y_idx == -1:
821  y_idx = sndbst_y
822 
823  # use fallback if we still can't find indices
824  if x_idx == -1:
825  x_idx = last_x
826  if y_idx == -1:
827  y_idx = last_y
828 
829  if x_idx == -1 or y_idx == -1:
830  print "WARNING: No valid step names could be found in the logfiles or root filenames being sorted: x: %s y: %s." % (xstr,ystr)
831  print "x", x_idx, "y", y_idx
832 
833  if x_idx < y_idx:
834  return -1
835  elif x_idx == y_idx:
836  return 0
837  elif y_idx < x_idx:
838  return 1
839 
840 
841 ######################
842 #
843 # Create HTML pages for candles
844 
845 def createCandlHTML(tmplfile,candlHTML,CurrentCandle,WebArea,repdir,ExecutionDate,LogFiles,cmsScimarkResults,date,prevrev):
846  global TimeSizeNumOfEvents,IgProfNumOfEvents,CallgrindNumOfEvents,MemcheckNumOfEvents
847  def _stripbase(base, astr):
848  basereg = re.compile("^%s/?(.*)" % base)
849  out = astr
850  found = basereg.search(astr)
851  if found:
852  out = found.groups()[0]
853  return out
854 
855  def _getProfileReportLink(repdir,CurrentCandle,CurDir,step,CurrentProfile,Profiler):
856  #FIXME:
857  #Pileup now has it own directory... should add it in the DirName dictionary at the beginning?
858  ProfileTemplate=os.path.join(repdir, "%s_%s" % (CurrentCandle,CurDir), "*_%s_%s*" % (step,CurrentProfile),Profiler)
859  #print ProfileTemplate
860  #There was the issue of SIM vs sim (same for DIGI) between the previous RelVal based performance suite and the current.
861  ProfileTemplateLowCaps=os.path.join(repdir, "%s_%s" % (CurrentCandle,CurDir), "*_%s_%s*" % (step.lower(),CurrentProfile),Profiler)
862  ProfileReportLink = glob.glob(ProfileTemplate)
863  #Filter out the Pile up directories when step does not contain Pile up
864  #if not ('PILEUP' in step):
865  # print "NPNPNPNP BEFORE %s"%ProfileReportLink
866  # ProfileReportLink=filter(lambda x: "PU" not in x,ProfileReportLink)
867  # print "NPNPNPNP %s"%ProfileReportLink
868  #print ProfileReportLink
869  if len(ProfileReportLink) > 0:
870  #print ProfileReportLink
871  if not reduce(lambda x,y: x or y,map(lambda x: CurrentCandle in x,ProfileReportLink)):# match with caps try low caps
872  ProfileReportLink = glob.glob(ProfileTemplateLowCaps)
873  else:
874  ProfileReportLink = glob.glob(ProfileTemplateLowCaps)
875  ProfileReportLink = map(lambda x: _stripbase(repdir,x),ProfileReportLink)
876  #print ProfileReportLink
877  return ProfileReportLink
878 
879  def _writeReportLink(INDEX,ProfileReportLink,CurrentProfile,step,NumOfEvents,Profiler=""):
880  if Profiler == "":
881  INDEX.write("<li><a href=\"%s\">%s %s (%s events)</a></li>\n" % (ProfileReportLink,CurrentProfile,step,NumOfEvents))
882  else:
883  #FIXME: need to fix this: why do we have the number of Memcheck events hardcoded?
884  if CurrentProfile == "memcheck_valgrind":#FIXME:quick and dirty hack to report we have changed memcheck to 5 events now...
885  INDEX.write("<li><a href=\"%s\">%s %s %s (%s events)</a></li>\n" % (ProfileReportLink,CurrentProfile,Profiler,step,"5"))
886  else:
887  INDEX.write("<li><a href=\"%s\">%s %s %s (%s events)</a></li>\n" % (ProfileReportLink,CurrentProfile,Profiler,step,NumOfEvents))
888  def IgProfDumpsTable(INDEX,ProfileLinks,step):
889  #See if the end of job profiles IgProfMemTotal.res or IgProfMemLive.res are in the list as they should:
890  EndOfJobProfileLink=filter(lambda x: "IgProfMemTotal.res" in x or "IgProfMemLive.res" in x, ProfileLinks)[0] #Assume only one of the two is there, as it should.
891  #Remove it from the list so we can order it:
892  ProfileLinks.remove(EndOfJobProfileLink)
893  #Sort the list in numerical order:
894  ProfileLinks.sort(key=lambda x: int(x.split(".")[-2]))
895  #Prepare regexp to find and replace MemTotal with MemLive for .gz links
896  IgProfMemLive_regexp=re.compile("IgProfMemLive")
897  if IgProfMemLive_regexp.search(EndOfJobProfileLink):
898  MemProfile="IgProf MEM LIVE"
899  else:
900  MemProfile="IgProf MEM TOTAL"
901  #Prepare the table header:
902  INDEX.write("<li>%s"%MemProfile)
903  INDEX.write("<table><tr><td>Profile after event</td><td>Total Memory Size (bytes)</td><td>Total Calls (number)</td><td>Link to gzipped IgProf profile</td></tr>")
904  for link in ProfileLinks:
905  #Build and check the link to the .gz profile that is always called IgProfMemTotal also for MemLive:
906  gzProfile=os.path.join(link.split("/")[-3],link.split("/")[-1])[:-3]+"gz"
907  if IgProfMemLive_regexp.search(gzProfile):
908  gzProfile=IgProfMemLive_regexp.sub(r"IgProfMemTotal",gzProfile)
909  INDEX.write("<tr><td>%s</td><td>%s</td><td>%s</td><td><a href=%s>%s</a></td></tr>"%(link.split(".")[-2],open(link,"r").readlines()[6].split()[1],open(link,"r").readlines()[6].split()[2],gzProfile,os.path.basename(gzProfile)))
910  #Put in the end of job one by hand:
911  gzEndOfJobProfileLink=os.path.join(EndOfJobProfileLink.split("/")[-3],EndOfJobProfileLink.split("/")[-1])[:-3]+"gz"
912  if IgProfMemLive_regexp.search(gzEndOfJobProfileLink):
913  gzEndOfJobProfileLink=IgProfMemLive_regexp.sub(r"IgProfMemTotal",gzEndOfJobProfileLink)
914  INDEX.write("<tr><td>%s</td><td>%s</td><td>%s</td><td><a href=%s>%s</a></td></tr>"%("End of job",open(EndOfJobProfileLink,"r").readlines()[6].split()[1],open(EndOfJobProfileLink,"r").readlines()[6].split()[2],gzEndOfJobProfileLink,os.path.basename(gzEndOfJobProfileLink)))
915  #Closing the table and the list item tags
916  INDEX.write("</table>")
917  INDEX.write("</li>")
918  #FIXME:
919  #These numbers are used in the index.html they are not automatically matched to the actual
920  #ones (one should automate this, by looking into the cmsCreateSimPerfTestPyRelVal.log logfile)
921 
922  NumOfEvents={
923  DirName[0] : TimeSizeNumOfEvents,
924  DirName[1] : IgProfNumOfEvents,
925  DirName[2] : IgProfNumOfEvents,
926  DirName[3] : IgProfNumOfEvents,
927  DirName[4] : CallgrindNumOfEvents,
928  DirName[5] : MemcheckNumOfEvents,
929  DirName[6] : TimeSizeNumOfEvents,
930  DirName[7] : IgProfNumOfEvents,
931  DirName[8] : IgProfNumOfEvents,
932  DirName[9] : IgProfNumOfEvents,
933  DirName[10] : CallgrindNumOfEvents,
934  DirName[11] : MemcheckNumOfEvents
935  }
936 
937  Profile=(
938  #These need to match the profile directory names ending within the candle directories
939  "TimingReport",
940  "TimeReport",
941  "SimpleMemReport",
942  "EdmSize",
943  "IgProfperf",
944  "IgProfMemTotal",
945  "IgProfMemLive",
946  "IgProfMemAnalyse",
947  "valgrind",
948  "memcheck_valgrind"
949  )
950  IgProfMemAnalyseOut=( #This is the special case of IgProfMemAnalyse
951  "doBeginJob_output.html",
952  "doEvent_output.html",
953  "mem_live.html",
954  "mem_total.html"
955  )
956  memcheck_valgrindOut=( #This is the special case of Valgrind MemCheck (published via Giovanni's script)
957  "beginjob.html",
958  "edproduce.html",
959  "esproduce.html"
960  )
961  OutputHtml={ #These are the filenames to be linked in the candle html page for each profile
962  Profile[0] : "*TimingReport.html", #The wildcard spares the need to know the candle name
963  Profile[1] : "TimeReport.html", #This is always the same (for all candles)
964  Profile[2] : "*.html", #This is supposed to be *SimpleMemoryCheck.html, but there is a bug in cmsRelvalreport.py and it is called TimingReport.html!
965  Profile[3] : "objects_pp.html", #This is one of 4 objects_*.html files, it's indifferent which one to pick, just need consistency
966  Profile[4] : "overall.html", #This is always the same (for all candles)
967  Profile[5] : "overall.html", #This is always the same (for all candles)
968  Profile[6] : "overall.html", #This is always the same (for all candles)
969  Profile[7] : "doBeginJob_output.html", #This is complicated... there are 3 html to link... (see IgProf MemAnalyse below)
970  Profile[8] : "overall.html", #This is always the same (for all candles)
971  Profile[9] : "beginjob.html" #This is complicated there are 3 html to link here too... (see Valgrind MemCheck below)
972  }
973 
974 
975  candnreg = re.compile("CandleName")
976  candhreg = re.compile("CandlesHere")
977  try:
978  CAND = open(candlHTML,"w")
979  for line in open(tmplfile):
980  if candhreg.search(line):
981  CAND.write('<div id="content">')
982  CAND.write("<h2>")
983  CAND.write(CurrentCandle)
984  CAND.write("</h2>\n")
985 
986  if _verbose:
987  print "Producing candles html: ", CurrentCandle
988 
989  for CurDir in DirName:
990 
991  LocalPath = os.path.join(repdir,"%s_%s" % (CurrentCandle,CurDir))
992  LocalDirname = os.path.basename(LocalPath)
993 
994  if not prevrev == "":
995 
996  profs = []
997  #FIXME!
998  #Check what this was used for.. it now has different DirName list since we added IgProf_Perf and IgProf_Mem on top of IgProf alone (still there)
999  if CurDir == DirName[0] or CurDir == DirName[6]: #TimeSize tests (with and without PU)
1000  profs = Profile[0:4]
1001  elif CurDir == DirName[1] or CurDir == DirName[7]: #IgProf tests (with and without PU)
1002  profs = Profile[4:8]
1003  elif CurDir == DirName[2] or CurDir == DirName[8]: #IgProf Perf tests (with and without PU)
1004  profs =[Profile[4]] #Keeping format to a list...
1005  elif CurDir == DirName[3] or CurDir == DirName[9]: #IgProf Mem tests (with and without PU)
1006  profs =Profile[5:8] #Keeping format to a list...
1007  elif CurDir == DirName[4] or CurDir == DirName[10]: #Callgrind tests (with and without PU)
1008  profs = [Profile[8]] #Keeping format to a list...
1009  elif CurDir == DirName[5] or CurDir == DirName[11]: #Memcheck tests (with and without PU)
1010  profs = [Profile[9]] #Keeping format to a list...
1011  #This could be optimized, but for now just comment the code:
1012  #This for cycle takes care of the case in which there are regression reports to link to the html:
1013  for prof in profs:
1014  if _verbose:
1015  print "Scanning for profile information for: ", prof
1016 
1017  printed = False
1018  fullprof = (CurrentCandle,prof)
1019  outd = ""
1020  nologext = ""
1021  if prof == "TimingReport":
1022  timeReports = glob.glob(os.path.join(LocalPath,"%s_*_%s.log" % (CandFname[CurrentCandle],prof)))
1023  if len(timeReports) > 0:
1024  if not printed:
1025  CAND.write("<p><strong>%s %s</strong></p>\n" % (prof,"Regression Analysis"))
1026  printed = True
1027  for log in timeReports:
1028  reportName = os.path.basename(log)
1029  (nologext, outd) = getOutputNames(LocalDirname,reportName)
1030  CAND.write("<h4>%s</h4>\n" % reportName)
1031  htmNames = ["changes.png"]
1032  otherNames = ["graphs.png" , "histos.png"]
1033  regressHTML= "%s-regress.html" % nologext
1034  pathsExist = reduce (lambda x,y: x or y,map(os.path.exists,map(lambda x: os.path.join(repdir,outd,x),otherNames)))
1035  html = ""
1036  if pathsExist:
1037  html = "<p>Performance graphs and histograms superimposed for %s are <a href=\"%s\">here</a></p>\n" % (reportName,regressHTML)
1038  else:
1039  html = "<p>No change in performance graph available</p>\n"
1040  regressHTML="%s/%s" % (WebArea,regressHTML)
1041 
1042  for x in htmNames:
1043  abspath = os.path.join(repdir,outd,x)
1044  if os.path.exists(abspath):
1045  html += "<p><a href=\"./%s/%s\"><img src=\"./%s/%s\" /></a></p>\n" % (outd,x,outd,x)
1046  else:
1047  html += "<p>%s does not exist probably because the log file for the previous release was missing</p>" % (abspath)
1048 
1049  createRegressHTML(regressHTML,repdir,outd,CurrentCandle,otherNames)
1050  CAND.write(html)
1051  CAND.write("\n</tr></table>")
1052 
1053 
1054  elif prof == "SimpleMemReport":
1055  simMemReports = glob.glob(os.path.join(LocalPath,"%s_*_%s" % (CandFname[CurrentCandle],prof)))
1056  simMemReports = map(lambda x: (CandFname[CurrentCandle],prof,x), simMemReports )
1057  simMemReports.sort(cmp=dirname_cmp)
1058  simMemReports = map(lambda x: x[2], simMemReports )
1059  if len(simMemReports) > 0:
1060  if not printed:
1061  CAND.write("<p><strong>%s %s</strong></p>\n" % (prof,"Regression Analysis"))
1062  printed = True
1063  for adir in simMemReports:
1064  reportName = os.path.basename(adir)
1065  CAND.write("<h4>%s</h4>\n" % reportName)
1066  htmNames = ["vsize_change.png", "rss_change.png"]
1067  otherNames = ["vsize_graphs.png","rss_graphs.png"]
1068  nologext = reportName
1069  outd = reportName
1070  regressHTML= "%s-regress.html" % nologext
1071  pathsExist = reduce (lambda x,y: x or y,map(os.path.exists,map(lambda x: os.path.join(repdir,LocalDirname,outd,x),otherNames)))
1072  html = ""
1073  if pathsExist:
1074  html = "<p>Superimposed memory performance graphs for %s are <a href=\"%s\">here</a></p>\n" % (reportName,regressHTML)
1075  else:
1076  html = "<p>No change in performance graph available</p>\n"
1077  regressHTML="%s/%s" % (WebArea,regressHTML)
1078 
1079  for x in htmNames:
1080  abspath = os.path.join(repdir,LocalDirname,outd,x)
1081  if os.path.exists(abspath):
1082  html += "<p><a href=\"./%s/%s/%s\"><img src=\"./%s/%s/%s\" /></a></p>\n" % (LocalDirname,outd,x,LocalDirname,outd,x)
1083  else:
1084  html += "<p>%s does not exist probably because the log file for the previous release was missing</p>" % (abspath)
1085 
1086  createRegressHTML(regressHTML,repdir,"%s/%s" % (LocalDirname,outd),CurrentCandle,otherNames)
1087  CAND.write(html)
1088  CAND.write("\n</tr></table>")
1089 
1090  elif prof == "EdmSize" or prof == "IgProfMemTotal" or prof == "IgProfMemLive" or prof== "IgProfperf" or prof == "Callgrind":
1091  regresPath = os.path.join(LocalPath,"%s_*_%s_regression" % (CandFname[CurrentCandle],prof))
1092  regresses = glob.glob(regresPath)
1093  stepreg = re.compile("%s_([^_]*(_PILEUP)?)_%s_regression" % (CandFname[CurrentCandle],prof))
1094  if len(regresses) > 0:
1095  if not printed:
1096  CAND.write("<p><strong>%s %s</strong></p>\n" % (prof,"Regression Analysis"))
1097  printed = True
1098  regresses = map(lambda x: (CandFname[CurrentCandle],prof,x),regresses)
1099  regresses.sort(cmp=reg_dirname_cmp)
1100  regresses = map(lambda x: x[2],regresses)
1101  for rep in regresses:
1102  base = os.path.basename(rep)
1103  found = stepreg.search(base)
1104  step = "Unknown-step"
1105  if found:
1106  step = found.groups()[0]
1107  htmlpage = ""
1108  if prof == "IgProfMemLive" or prof == "IgProfMemTotal" or prof== "IgProfperf" or prof == "Callgrind":
1109  htmlpage = "overall.html"
1110  else:
1111  htmlpage = "objects_pp.html"
1112  CAND.write("<a href=\"%s/%s/%s\">%s %s regression report</a><br/>\n" % (LocalDirname,base,htmlpage,prof,step))
1113  #Here we go back to the "non-regression" reports listing
1114  CandleLogFiles = []
1115  if os.path.exists(LocalPath):
1116  thedir = os.listdir(LocalPath)
1117  CandleLogFiles = filter(lambda x: (x.endswith(".log") or x.endswith("EdmSize")) and not os.path.isdir(x) and os.path.exists(x), map(lambda x: os.path.abspath(os.path.join(LocalPath,x)),thedir))
1118 
1119  if (len(CandleLogFiles)>0):
1120 
1121  syscp(CandleLogFiles,WebArea + "/")
1122  base = os.path.basename(LocalPath)
1123  lfileshtml = ""
1124 
1125  for cand in CandleLogFiles:
1126  cand = os.path.basename(cand)
1127  if _verbose:
1128  print "Found %s in %s\n" % (cand,LocalPath)
1129 
1130  if not "EdmSize" in cand:
1131  lfileshtml += "<a href=\"./%s/%s\">%s </a><br/>" % (base,cand,cand)
1132 
1133  CAND.write("<p><strong>Logfiles for %s</strong></p>\n" % CurDir)
1134  CAND.write(lfileshtml)
1135 
1136  PrintedOnce = False
1137  for CurrentProfile in Profile:
1138  #print Steps
1139  for step in Steps: #Using Steps here that is Step+ProductionSteps!
1140  #print step
1141  #print "DEBUG:%s,%s,%s,%s,%s,%s"%(repdir,CurrentCandle,CurDir,step,CurrentProfile,OutputHtml[CurrentProfile])
1142  ProfileReportLink = _getProfileReportLink(repdir,CurrentCandle,
1143  CurDir,
1144  step,
1145  CurrentProfile,
1146  OutputHtml[CurrentProfile])
1147  #if ProfileReportLink:
1148  # print ProfileReportLink
1149  isProfinLink = False
1150  if len (ProfileReportLink) > 0:
1151  isProfinLink = reduce(lambda x,y: x or y,map(lambda x: CurrentProfile in x,ProfileReportLink))
1152 
1153  if isProfinLink:
1154  #It could also not be there
1155 
1156  if (PrintedOnce==False):
1157  #Making sure it's printed only once per directory (TimeSize, IgProf, Valgrind) each can have multiple profiles
1158 
1159  #This is the "title" of a series of profiles, (TimeSize, IgProf, Valgrind)
1160  CAND.write("<p><strong>%s</strong></p>\n" % CurDir)
1161  CAND.write("<ul>\n")
1162  PrintedOnce=True
1163 
1164  #Special cases first (IgProf MemAnalyse and Valgrind MemCheck)
1165  #Add among the special cases any IgProfMem (5,6,7) since now we added the dumping:
1166  if (CurrentProfile == Profile[5] or CurrentProfile == Profile[6]):
1167  for prolink in ProfileReportLink:
1168  _writeReportLink(CAND,prolink,CurrentProfile,step,NumOfEvents[CurDir])
1169  for igprofmem in ["*IgProfMemTotal*.res","*IgProfMemLive*.res"]:
1170  ProfileReportLink = _getProfileReportLink(repdir,CurrentCandle,
1171  CurDir,
1172  step,
1173  CurrentProfile,
1174  igprofmem)
1175  isProfinLink = False
1176  if len (ProfileReportLink) > 0:
1177  isProfinLink = reduce(lambda x,y: x or y,map(lambda x: CurrentProfile in x,ProfileReportLink))
1178  if isProfinLink :#It could also not be there
1179  #for prolink in ProfileReportLink:
1180  IgProfDumpsTable(CAND,ProfileReportLink,step)
1181  # _writeReportLink(CAND,prolink,CurrentProfile,step,NumOfEvents[CurDir],Profiler=os.path.basename(prolink))
1182 
1183  elif (CurrentProfile == Profile[7]):
1184  for igprof in IgProfMemAnalyseOut:
1185  ProfileReportLink = _getProfileReportLink(repdir,CurrentCandle,
1186  CurDir,
1187  step,
1188  CurrentProfile,
1189  igprof)
1190  isProfinLink = False
1191  if len (ProfileReportLink) > 0:
1192  isProfinLink = reduce(lambda x,y: x or y,map(lambda x: CurrentProfile in x,ProfileReportLink))
1193  if isProfinLink :#It could also not be there
1194  for prolink in ProfileReportLink:
1195  _writeReportLink(CAND,prolink,CurrentProfile,step,NumOfEvents[CurDir],Profiler=igprof)
1196 
1197 
1198  elif (CurrentProfile == Profile[9]):
1199 
1200  for memprof in memcheck_valgrindOut:
1201  #print memprof
1202  ProfileReportLink = _getProfileReportLink(repdir,CurrentCandle,
1203  CurDir,
1204  step,
1205  CurrentProfile,
1206  memprof
1207  )
1208  isProfinLink = False
1209  if len (ProfileReportLink) > 0:
1210  isProfinLink = reduce(lambda x,y: x or y,map(lambda x: CurrentProfile in x,ProfileReportLink))
1211  if isProfinLink :#It could also not be there
1212  for prolink in ProfileReportLink:
1213  _writeReportLink(CAND,prolink,CurrentProfile,step,NumOfEvents[CurDir],Profiler=memprof)
1214 
1215  else:
1216  for prolink in ProfileReportLink:
1217  if "regression" not in prolink: #To avoid duplication of links to regression reports!
1218  _writeReportLink(CAND,prolink,CurrentProfile,step,NumOfEvents[CurDir])
1219  #print "Step is %s, CurrentProfile is %s and ProfileReportLink is %s and prolink is %s"%(step,CurrentProfile,ProfileReportLink,prolink)
1220 
1221 
1222  if PrintedOnce:
1223  CAND.write("</ul>\n")
1224  PrintedOnce=False
1225 
1226  CAND.write("<hr />")
1227  CAND.write("<br />\n")
1228  CAND.write("</div>\n")
1229  elif candnreg.search(line):
1230  CAND.write(CurrentCandle)
1231  else:
1232  CAND.write(line)
1233 
1234  CAND.close()
1235  except IOError, detail:
1236  print "ERROR: Could not write candle html %s because %s" % (os.path.basename(candlHTML),detail)
1237 
1238 
1239 def populateFromTupleRoot(tupname,repdir,rootfile,pureg):
1240  table = Table()
1241  for cand in Candles:
1242  fname = CandFname[cand]
1243  globpath = os.path.join(repdir,"%s_TimeSize" % cand,"%s_*_TimingReport" % fname)
1244  stepDirs = glob.glob(globpath)
1245  stepDirs = map(lambda x: (fname,x), stepDirs)
1246  stepDirs.sort(cmp=timerep_cmp)
1247  stepDirs = map(lambda x: x[1], stepDirs)
1248  stepreg = re.compile("%s_(.*)_TimingReport" % fname)
1249  createNewRow = True
1250  curRow = None
1251  createPURow = True
1252  puRow = None
1253  for stepdir in stepDirs:
1254  base = os.path.basename(stepdir)
1255  found = stepreg.search(base)
1256  step = "Unknown-step"
1257  if found:
1258  step = found.groups()[0]
1259  realstep = "Unknown-step"
1260  if "PILEUP" in step:
1261  found = pureg.search(step)
1262  if found:
1263  realstep = found.groups()[0]
1264  if createPURow:
1265  createPURow = False
1266  puRow = Row(table)
1267  rootf = os.path.join(stepdir,rootfile)
1268 
1269  if os.path.exists(rootf):
1270  f = ROOT.TFile(rootf)
1271 
1272  cpu_time_tree = ROOT.TTree()
1273  f.GetObject("cpu_time_tuple;1",cpu_time_tree)
1274  if cpu_time_tree:
1275  if cpu_time_tree.InheritsFrom("TTree"):
1276  data1 = None
1277  data2 = None
1278  for t in cpu_time_tree:
1279  data1 = t.total1
1280  data2 = t.total2
1281  if data1 and data2:
1282  if createNewRow:
1283  createNewRow = False
1284  curRow = table.newRow(cand)
1285  data_tuple = (data1,data2)
1286 
1287  if "PILEUP" in step:
1288  puRow.addEntry(realstep,data_tuple)
1289  else:
1290  if createNewRow:
1291  createNewRow = False
1292  curRow = table.newRow(cand)
1293 
1294  curRow.addEntry(step,data_tuple)
1295  f.Close()
1296  if puRow == None:
1297  pass
1298  else:
1299  table.addRow(puRow,"%s PILEUP" %cand)
1300  return table
1301 
1302 
1303 def createHTMLtab(INDEX,table_dict,ordered_keys,header,caption,name,mode=0):
1304  cols = len(ordered_keys)
1305  totcols = (cols * 3) + 1
1306  innercol = 3
1307  colspan = totcols - 1
1308  labels = []
1309  if mode == 1:
1310  labels = ["fs1","fs2","&#x0394;"]
1311  elif mode == 2 or mode == 3:
1312  colspan = cols
1313  innercol = 1
1314  else:
1315  labels = ["t1" ,"t2" ,"&#x0394;"]
1316 
1317 
1318  INDEX.write("<h3>%s</h3>\n" % header)
1319  INDEX.write("<table>\n")
1320  INDEX.write("<caption>%s</caption>\n" % caption)
1321  INDEX.write("<thead><tr><th></th><th colspan=\"%s\" scope=\"colgroup\">%s</th></tr></thead>" % (colspan,name))
1322  INDEX.write("<tbody>\n")
1323  for key in ordered_keys:
1324  INDEX.write("<tr>")
1325  if key == None:
1326  INDEX.write("<th></th>")
1327  else:
1328  INDEX.write("<td scope=\"row\">")
1329  INDEX.write(key)
1330  INDEX.write("</td>")
1331  for col in table_dict[None]:
1332  if key == None:
1333  INDEX.write("<th colspan=\"%s\" scope=\"col\">" % innercol)
1334  INDEX.write(col)
1335  INDEX.write("</th>")
1336  else:
1337  rowdict = table_dict[key].getRowDict()
1338  if rowdict.has_key(col):
1339  if mode == 2:
1340  dat = prettySize(rowdict[col])
1341  INDEX.write("<td>")
1342  INDEX.write("%s" % dat)
1343  INDEX.write("</td>")
1344 
1345  elif mode == 3:
1346  dat = rowdict[col]
1347  INDEX.write("<td>")
1348  INDEX.write("%6.2f" % dat)
1349  INDEX.write("</td>")
1350  else:
1351  (data1, data2) = rowdict[col]
1352  diff = data2 - data1
1353 
1354  if mode == 1:
1355  diff = prettySize(diff)
1356  data1 = prettySize(data1)
1357  data2 = prettySize(data2)
1358 
1359  seq = [ data1, data2, diff ]
1360  for dat in seq:
1361  INDEX.write("<td id=\"data\">")
1362  if mode == 1:
1363  INDEX.write("%s" % dat) # %s if
1364  else:
1365  INDEX.write("%6.2f" % dat) # %s if
1366 
1367  INDEX.write("</td>")
1368  else:
1369  if mode == 2 or mode == 3:
1370  INDEX.write("<td>")
1371  INDEX.write("N/A")
1372  INDEX.write("</td>")
1373  else:
1374  for i in range(3):
1375  INDEX.write("<td>")
1376  INDEX.write("N/A")
1377  INDEX.write("</td>")
1378  INDEX.write("</tr>\n")
1379  # write an additional row if this row is the header row
1380  # we need to describe the sub columns
1381  if not (mode == 2 or mode == 3 ):
1382  if key == None:
1383  INDEX.write("<tr>")
1384  INDEX.write("<th>Candles</th>")
1385  for col in table_dict[None]:
1386  INDEX.write("<th>%s</th>" % labels[0])
1387  INDEX.write("<th>%s</th>" % labels[1])
1388  INDEX.write("<th>%s</th>" % labels[2])
1389  INDEX.write("</tr>\n")
1390  INDEX.write("</tbody></table>\n")
1391 
1392  INDEX.write("<br />")
1393 
1394 def stageIgProfReports(remotedir,arch,version):
1395  '''Publish all IgProf files into one remote directory (new naming convention). Can publish to AFS location or to a local directory on a remote (virtual) machine.'''
1396  #FIXME: can eliminate this part if using tar pipes... was done with rsynch in mind
1397  #Compose command to create remote dir:
1398  if ":" in remotedir: #Remote host local directory case
1399  (host,dir)=remotedir.split(":")
1400  mkdir_cmd="ssh %s (mkdir %s;mkdir %s/%s)"%(host,dir,dir,arch)
1401  else: #AFS or local case
1402  mkdir_cmd="mkdir %s;mkdir %s/%s"%(remotedir,remotedir,arch)
1403 
1404  #Create remote dir:
1405  try:
1406  print mkdir_cmd
1407  os.system(mkdir_cmd)
1408  print "Successfully created publication directory"
1409  except:
1410  print "Issues with publication directory existence/creation!"
1411 
1412  #Copy files over to remote dir
1413  #replacing rsync with tar pipes since it can hang on AFS (Andreas' experience):
1414  #rsync_cmd="rsync -avz *_IgProf_*/*.sql3 %s/%s/%s"%(remotedir,arch,version)
1415  if ":" in remotedir:
1416  tarpipe_cmd='tar cf - *_IgProf_*/*.sql3 | ssh %s "cd %s/%s; mkdir %s; cd %s; tar xf -; mv *_IgProf_*/*.sql3 .; rmdir *_IgProf_*"'%(host,dir,arch,version,version)
1417  else:
1418  tarpipe_cmd='tar cf - *_IgProf_*/*.sql3 | (cd %s/%s; mkdir %s; cd %s; tar xf -; mv *_IgProf_*/*.sql3 .; rmdir *_IgProf_*)'%(remotedir,arch,version,version)
1419  try:
1420  # print rsync_cmd
1421  # os.system(rsync_cmd)
1422  print tarpipe_cmd
1423  os.system(tarpipe_cmd)
1424  print "Successfully copied IgProf reports to %s"%remotedir
1425  except:
1426  print "Issues with rsyncing to the remote directory %s!"%remotedir
1427 
1428  #Make sure permissions are set for group to be able to write:
1429  if ":" in remotedir: #Remote host local directory case
1430  chmod_cmd="ssh %s chmod -R 775 %s/%s"%(host,dir,arch)
1431  else:
1432  chmod_cmd="chmod -R 775 %s/%s"%(remotedir,arch)
1433  try:
1434  print chmod_cmd
1435  os.system(chmod_cmd)
1436  print "Successfully set permissions for IgProf reports directory %s"%remotedir
1437  except:
1438  print "(Potential) issues with chmoding the remote directory %s!"%remotedir
1439 
1440  return #Later, report here something like the web link to the reports in igprof-navigator...
1441 
1442 
1443 #####################
1444 #
1445 # Create web report index and create HTML file for each candle
1446 #
1447 def createWebReports(WebArea,repdir,ExecutionDate,LogFiles,cmsScimarkResults,date,prevrev):
1448 
1449  #Some nomenclature
1450 
1451  Candle = Candles #These need to match the directory names in the work area
1452 
1453  CmsDriverCandle = CandFname #{ #These need to match the cmsDriver.py output filenames
1454 
1455 
1456  #Produce a "small" index.html file to navigate the html reports/logs etc
1457  IndexFile="%s/index.html" % WebArea
1458  TemplateHtml="%s/doc/index.html" % BASE_PERFORMANCE
1459 
1460  cmsverreg = re.compile("CMSSW_VERSION")
1461  hostreg = re.compile("HOST")
1462  lpathreg = re.compile("LocalPath")
1463  fsizereg = re.compile("FSizeTable")
1464  cpureg = re.compile("CPUTable")
1465  proddreg = re.compile("ProductionDate")
1466  logfreg = re.compile("LogfileLinks")
1467  dirbreg = re.compile("DirectoryBrowsing")
1468  pubdreg = re.compile("PublicationDate")
1469  candhreg = re.compile("CandlesHere")
1470  #Loop line by line to build our index.html based on the template one
1471  #Copy the perf_style.css file from Validation/Performance/doc
1472 
1473  CandlTmpltHTML="%s/doc/candle.html" % BASE_PERFORMANCE
1474  if _verbose:
1475  print "Copying %s/doc/perf_style.css style file to %s/." % (BASE_PERFORMANCE,WebArea)
1476  print "Template used: %s" % TemplateHtml
1477 
1478  syscp((BASE_PERFORMANCE + "/doc/perf_style.css"),WebArea + "/.")
1479  pureg = re.compile("(.*)_PILEUP")
1480  try:
1481  INDEX = open(IndexFile,"w")
1482  for NewFileLine in open(TemplateHtml) :
1483  if cmsverreg.search(NewFileLine):
1484  if prevrev == "":
1485  INDEX.write("Performance Reports for %s\n" % CMSSW_VERSION)
1486  else:
1487  globpath = os.path.join(repdir,"REGRESSION.%s.vs.*" % (prevrev))
1488  globs = glob.glob(globpath)
1489  if len(globs) < 1:
1490  pass
1491  else:
1492  latestreg = re.compile("REGRESSION.%s.vs.(.*)" % prevrev)
1493  found = latestreg.search(os.path.basename(globs[0]))
1494  if found:
1495  latestrel = found.groups()[0]
1496  INDEX.write("Performance Reports with regression: %s VS %s\n" % (prevrev,latestrel))
1497  else:
1498  INDEX.write("Performance Reports with regression: %s VS %s\n" % (prevrev,CMSSW_VERSION))
1499  elif hostreg.search(NewFileLine):
1500  INDEX.write(HOST + "\n")
1501  #File Size Summary Table
1502  elif fsizereg.search(NewFileLine):
1503  #Case of NO-REGRESSION:
1504  if prevrev == "":
1505  fsize_tab = Table()
1506 
1507  for cand in Candles:
1508  fname = CandFname[cand]
1509  globpath = os.path.join(repdir,"%s*_TimeSize" % cand,"%s_*.root" % fname)
1510  rootfiles = glob.glob(globpath)
1511  rootfiles = map(lambda x: (fname,x), rootfiles)
1512  rootfiles.sort(cmp=rootfile_cmp)
1513  rootfiles = map(lambda x: x[1], rootfiles)
1514  stepreg = re.compile("%s_(.*).root" % fname)
1515  createNewRow = True
1516  curRow = None
1517  createPURow = True
1518  puRow = None
1519  for rootf in rootfiles:
1520  base = os.path.basename(rootf)
1521  found = stepreg.search(base)
1522  step = "Unknown-step"
1523  if found:
1524  step = found.groups()[0]
1525  realstep = "Unknown-step"
1526  if "PILEUP" in step:
1527  found = pureg.search(step)
1528  if found:
1529  realstep = found.groups()[0]
1530  if createPURow:
1531  createPURow = False
1532  puRow = Row(fsize_tab)
1533  try:
1534  statinfo = os.stat(rootf)
1535  fsize = statinfo.st_size
1536  if createNewRow:
1537  createNewRow = False
1538  curRow = fsize_tab.newRow(cand)
1539 
1540  if "PILEUP" in step:
1541  puRow.addEntry(realstep,fsize)
1542  else:
1543  if createNewRow:
1544  createNewRow = False
1545  curRow = fsize_tab.newRow(cand)
1546 
1547  curRow.addEntry(step,fsize)
1548  except IOError, detail:
1549  print detail
1550  except OSError, detail:
1551  print detail
1552  if puRow == None:
1553  pass
1554  else:
1555  fsize_tab.addRow(puRow,"%s PILEUP" %cand)
1556  (ordered_keys,table_dict) = fsize_tab.getTable(1)
1557  cols = len(ordered_keys)
1558 
1559  if len(table_dict) > 1 and cols > 0:
1560  createHTMLtab(INDEX,table_dict,ordered_keys,
1561  "Release ROOT file sizes",
1562  "Table showing current release ROOT filesizes in (k/M/G) bytes.",
1563  "Filesizes",2)
1564  #Case of REGRESSION vs previous release:
1565  else:
1566  try:
1567  globpath = os.path.join(repdir,"REGRESSION.%s.vs.*" % (prevrev))
1568  globs = glob.glob(globpath)
1569  if len(globs) < 1:
1570  raise IOError(2,globpath,"File does not exist","File does not exist")
1571  idfile = open(globs[0])
1572  oldpath = ""
1573  for line in idfile:
1574  oldpath = line
1575  oldpath = oldpath.strip()
1576  #print "##########TABLE DEBUG :oldpath is %s"%oldpath
1577  fsize_tab = Table()
1578 
1579  for cand in Candles:
1580  fname = CandFname[cand]
1581  globpath = os.path.join(repdir,"%s*_TimeSize" % cand,"%s_*.root" % fname)
1582  rootfiles = glob.glob(globpath)
1583  rootfiles = map(lambda x: (fname,x), rootfiles)
1584  rootfiles.sort(cmp=rootfile_cmp)
1585  rootfiles = map(lambda x: x[1], rootfiles)
1586  stepreg = re.compile("%s_(.*).root" % fname)
1587  createNewRow = True
1588  curRow = None
1589  createPURow = True
1590  puRow = None
1591  for rootf in rootfiles:
1592  base = os.path.basename(rootf)
1593  found = stepreg.search(base)
1594  step = "Unknown-step"
1595  if found:
1596  step = found.groups()[0]
1597 
1598  realstep = "Unknown-step"
1599  if "PILEUP" in step:
1600  found = pureg.search(step)
1601  if found:
1602  realstep = found.groups()[0]
1603  if createPURow:
1604  createPURow = False
1605  puRow = Row(fsize_tab)
1606  try:
1607  statinfo = os.stat(rootf)
1608  fsize2 = statinfo.st_size
1609  oldfile = os.path.join(oldpath,"%s_TimeSize" % cand,base)
1610  if "PILEUP" in step:
1611  oldfile = os.path.join(oldpath,"%s_PU_TimeSize" % cand,base)
1612  fsize1 = 0
1613 
1614  if os.path.exists(oldfile):
1615  statinfo = os.stat(oldfile)
1616  fsize1 = statinfo.st_size
1617  else:
1618  print "######DID NOT FIND Previous file (needed for the filesize table): %s"%oldfile
1619 
1620  if createNewRow:
1621  createNewRow = False
1622  curRow = fsize_tab.newRow(cand)
1623 
1624  data_tuple = (fsize1,fsize2)
1625  if "PILEUP" in step:
1626  puRow.addEntry(realstep,data_tuple)
1627  else:
1628  if createNewRow:
1629  createNewRow = False
1630  curRow = fsize_tab.newRow(cand)
1631 
1632  curRow.addEntry(step,data_tuple)
1633  except IOError, detail:
1634  print detail
1635  except OSError, detail:
1636  print detail
1637  if puRow == None:
1638  pass
1639  else:
1640  fsize_tab.addRow(puRow,"%s PILEUP" %cand)
1641 
1642  (ordered_keys,table_dict) = fsize_tab.getTable()
1643  cols = len(ordered_keys)
1644 
1645  if len(table_dict) > 1 and cols > 0:
1646  createHTMLtab(INDEX,table_dict,ordered_keys,
1647  "Release ROOT file sizes",
1648  "Table showing previous release ROOT filesizes, fs1, latest sizes, fs2, and the difference between them &#x0394; in (k/M/G) bytes.",
1649  "Filesizes",1)
1650  except IOError, detail:
1651  print detail
1652  except OSError, detail:
1653  print detail
1654  #CPU Time Summary Table
1655  elif cpureg.search(NewFileLine):
1656  #Case of NO REGRESSION
1657  if prevrev == "":
1658  time_tab = Table()
1659 
1660  for cand in Candles:
1661  fname = CandFname[cand]
1662  globpath = os.path.join(repdir,"%s*_TimeSize" % cand,"%s_*_TimingReport.log" % fname)
1663  logfiles = glob.glob(globpath)
1664  logfiles = map(lambda x: (fname,x), logfiles)
1665  logfiles.sort(cmp=logrep_cmp)
1666  logfiles = map(lambda x: x[1], logfiles)
1667 
1668  stepreg = re.compile("%s_(.*)_TimingReport.log" % fname)
1669  createNewRow = True
1670  curRow = None
1671  createPURow = True
1672  puRow = None
1673  for log in logfiles:
1674  base = os.path.basename(log)
1675  found = stepreg.search(base)
1676  step = "Unknown-step"
1677  if found:
1678  step = found.groups()[0]
1679 
1680  realstep = "Unknown-step"
1681  if "PILEUP" in step:
1682  found = pureg.search(step)
1683  if found:
1684  realstep = found.groups()[0]
1685  if createPURow:
1686  createPURow = False
1687  puRow = Row(time_tab)
1688 
1689  data = cpr.getTimingLogData(log)
1690  mean = 0
1691  i = 0
1692  for evtnum, time in data:
1693  mean += time
1694  i += 1
1695  try:
1696  mean = mean / float(i)
1697  except ZeroDivisionError, detail:
1698  print "WARNING: Could not calculate mean CPU time from log because no events could be parsed", log
1699 
1700  if "PILEUP" in step:
1701  puRow.addEntry(realstep,mean)
1702  else:
1703  if createNewRow:
1704  createNewRow = False
1705  curRow = time_tab.newRow(cand)
1706 
1707  curRow.addEntry(step,mean)
1708  if puRow == None:
1709  pass
1710  else:
1711  time_tab.addRow(puRow,"%s PILEUP" %cand)
1712 
1713  (ordered_keys,table_dict) = time_tab.getTable(1)
1714  cols = len(ordered_keys)
1715 
1716  if len(table_dict) > 1 and cols > 0:
1717  createHTMLtab(INDEX,table_dict,ordered_keys,
1718  "Release CPU times",
1719  "Table showing current release CPU times in secs.",
1720  "CPU Times (s)",3)
1721  #Case of REGRESSION (CPU Time Summary Table)
1722  else:
1723 
1724 
1725  ####
1726  #
1727  # Create the table data structure
1728  #
1729  cpu_time_tab = populateFromTupleRoot("cpu_time_tuple",repdir,"timing-regress.root",pureg)
1730 
1731 
1732  ###########
1733  #
1734  # Create HTML table from table data structure
1735  #
1736 
1737  (ordered_keys,table_dict) = cpu_time_tab.getTable()
1738 
1739  cols = len(ordered_keys)
1740  if len(table_dict) > 1 and cols > 0:
1741  createHTMLtab(INDEX,table_dict,ordered_keys,
1742  "Release CPU times",
1743  "Table showing previous release CPU times, t1, latest times, t2, and the difference between them &#x0394; in secs.",
1744  "CPU Times (s)")
1745 
1746 
1747 
1748  elif lpathreg.search(NewFileLine):
1749  INDEX.write(repdir + "\n")
1750  elif proddreg.search(NewFileLine):
1751  INDEX.write(ExecutionDate + "\n")
1752  elif logfreg.search(NewFileLine):
1753  INDEX.write("<br />\n")
1754  for log in LogFiles:
1755  log = os.path.basename(log)
1756  if _verbose:
1757  print "linking log file %s" % log
1758  INDEX.write("<a href=\"./%s\"> %s </a>" % (log,log))
1759  INDEX.write("<br />\n")
1760  #Add the cmsScimark results here:
1761  INDEX.write("Results for cmsScimark2 benchmark (running on the other cores) available at:\n")
1762  for cmssci in cmsScimarkResults:
1763  scimarkdirs=cmssci.split("/")
1764  localdir=scimarkdirs[-2]
1765  #print localdir
1766  cmssci = os.path.basename(cmssci)
1767  relativelink=os.path.join(localdir,cmssci)
1768  #print relativelink
1769  INDEX.write("<a href=\"%s\"> %s </a>" % (relativelink,cmssci))
1770  INDEX.write("<br />\n")
1771 
1772 
1773  elif dirbreg.search(NewFileLine):
1774  #Create a subdirectory DirectoryBrowsing to circumvent the fact the dir is not browsable if there is an index.html in it.
1775  #Bug! This does not work since it points to index.html automatically!
1776  #os.symlink("./","%s/DirectoryBrowsing" % WebArea)
1777  #Actually all the following is done later...
1778  #Create a physical directory first
1779  #os.mkdir("%s/DirectoryBrowsing" % WebArea)
1780  #Then will populate it with symbolic links(once they have been copied there!) from all WebArea files, except index.html, see down towards the end!
1781  INDEX.write("Click <a href=\"./DirectoryBrowsing/\">here</a> to browse the directory containing all results (except the root files)\n")
1782 
1783  elif pubdreg.search(NewFileLine):
1784  INDEX.write(date + "\n")
1785  elif candhreg.search(NewFileLine):
1786  for acandle in Candle:
1787  globpath = os.path.join(repdir,"%s_*" % acandle)
1788  globs = glob.glob(globpath)
1789  if len(globs) > 0:
1790  candlHTML = "%s.html" % acandle
1791  INDEX.write("<a href=\"./%s\"> %s </a>" % (candlHTML,acandle))
1792  INDEX.write("<br />\n")
1793 
1794  candlHTML=os.path.join(WebArea,candlHTML)
1795  createCandlHTML(CandlTmpltHTML,candlHTML,acandle,WebArea,repdir,ExecutionDate,LogFiles,cmsScimarkResults,date,prevrev)
1796  else:
1797  INDEX.write(NewFileLine)
1798 
1799  #End of while loop on template html file
1800  INDEX.close()
1801  except IOError, detail:
1802  print "Error: Could not create index Html file for some reason, check position. Details : %s" % detail
1803 
1804 ########################
1805 #
1806 # Grab dirs that end in strings defined in DirName
1807 #
1808 def getDirnameDirs(repdir,WebArea):
1809  Dir = os.listdir(repdir)
1810  def _containsDirName(elem):
1811  return reduce(lambda x,y: x or y,map(lambda x: x in elem, DirName))
1812  def _print4Lambda(elem,WebArea):
1813  if _verbose:
1814  print "Copying %s to %s\n" % (elem,WebArea)
1815 
1816  dirstocp = filter(lambda x: _containsDirName(x),map(lambda x: repdir + x,Dir))
1817  map(lambda x: _print4Lambda(x,WebArea),dirstocp)
1818  syscp(dirstocp,WebArea + "/")
1819  os.mkdir("%s/DirectoryBrowsing" % WebArea)
1820  for file in os.listdir(WebArea):
1821  if file != "index.html": #Potential maintenance issue if the index.html changes name to something the server automatically displays when pointing to the directory...
1822  #Use relative path ".." instead of WebArea to avoid problems when copying stuff to a remote server!
1823  os.symlink("%s/%s"%("..",file),"%s/DirectoryBrowsing/%s" % (WebArea,file))
1824 
1825 #######################
1826 #
1827 # Upload stage to remote location
1828 def syncToRemoteLoc(stage,drive,path,port):
1829  stage = addtrailingslash(stage)
1830  cmd = "rsync -avz"
1831  # We must, MUST, do os.path.normpath otherwise rsync will dump the files in the directory
1832  # we specify on the remote server, rather than creating the CMS_VERSION directory
1833  #--rsh=\"ssh -l relval\"
1834  args = "--port=%s %s %s:%s" % (port,os.path.normpath(stage),drive,path)
1835  retval = -1
1836  if _dryrun:
1837  print cmd + " --dry-run " + args
1838  retval = os.system(cmd + " --dry-run " + args )
1839  else:
1840  print cmd+" "+args
1841  retval = os.system(cmd + " " + args)
1842  return retval
1843 
1844 ################
1845 #
1846 # Delete tmp dir if we used it
1848  if os.path.exists(TMP_DIR) and IS_TMP:
1849  os.system("rm -Rf " + TMP_DIR)
1850 
1851 #####################
1852 #
1853 # Some functions used for copying
1854 
1855 def getRelativeDir(parent,child,keepTop=True):
1856  def _walkpath(path):
1857  dirs = []
1858  while True:
1859  head , tail = os.path.split(path)
1860  if tail == "":
1861  break
1862  dirs.append(tail)
1863  path = head
1864  for i in range(len(dirs)-1,-1,-1):
1865  adir = dirs[i]
1866  yield adir
1867  return
1868  pwalk = _walkpath(parent)
1869  n = 0
1870  try:
1871  while True:
1872  pwalk.next()
1873  n += 1
1874  except StopIteration:
1875  pass
1876 
1877  if keepTop:
1878  n = n - 1
1879 
1880  cwalk = _walkpath(child)
1881  try:
1882  #prewalk
1883  for x in range(n):
1884  cwalk.next()
1885  except StopIteration:
1886  print "ERROR: Unable to determine relative dir"
1887  raise ReldirExcept
1888 
1889  relpath = ""
1890  try:
1891  while True:
1892  relpath=os.path.join(relpath,cwalk.next())
1893  except StopIteration:
1894  pass
1895  return relpath
1896 
1897 def docopy(src,dest):
1898  try:
1899  copy2(src,dest)
1900  except OSError, detail:
1901  print "WARNING: Could not copy %s to %s because %s" % (src,dest,detail)
1902  except IOError, detail:
1903  print "WARNING: Could not copy %s to %s because %s" % (src,dest,detail)
1904  else:
1905  if _verbose:
1906  print "Copied %s to %s" % (src,dest)
1907 
1908 def copytree4(src,dest,keepTop=True):
1909  def _getNewLocation(source,child,dst,keepTop=keepTop):
1910  place = getRelativeDir(source,child,keepTop=keepTop)
1911  return os.path.join(dst,place)
1912  def _copyFilter(source,dst,curdir,fsnodes,filter,dirs=False):
1913  for node in fsnodes:
1914  dontFilter = True
1915  filterExist = not len(filter) == 0
1916  if filterExist:
1917  dontFilter = not reduce(lambda x,y: x or y,map(lambda x: fnmatch.fnmatch(node,x),filter))
1918  if dontFilter:
1919  node = os.path.join(curdir,node) # convert to absolute path
1920  try:
1921  newnode = _getNewLocation(source,node,dst)
1922  if dirs:
1923  os.mkdir(newnode)
1924  else:
1925  copy2(node,newnode)
1926  except IOError, detail:
1927  print "WARNING: Could not copy %s to %s because %s" % (node,newnode,detail)
1928  except OSError, detail:
1929  print "WARNING: Could not copy %s to %s because %s" % (src,dest,detail)
1930  except ReldirExcept:
1931  print "WARNING: Could not determine new location for source %s into destination %s" % (source,dst)
1932  else:
1933  if len(filter) > 0:
1934  try:
1935  match = fnmatch.fnmatch(node,filter[0])
1936  assert not match
1937  except AssertionError, detail:
1938  print node, filter[0], match
1939  raise RuntimeError
1940  if _verbose:
1941  if "root" in node:
1942  print "Filter %s Copied %s to %s" % (dontFilter,node,newnode)
1943  print "fnmatch %s" % fnmatch.fnmatch(node,cpFileFilter[0])
1944 
1945  gen = os.walk(src)
1946  try:
1947  newloc = _getNewLocation(src,src,dest)
1948 
1949  os.mkdir(newloc)
1950  try:
1951  while True:
1952  step = gen.next()
1953  curdir = step[0]
1954  dirs = step[1]
1955  files = step[2]
1956 
1957  _copyFilter(src,dest,curdir,dirs,cpDirFilter,dirs=True)
1958  _copyFilter(src,dest,curdir,files,cpFileFilter)
1959 
1960  except StopIteration:
1961  pass
1962  except IOError, detail:
1963  print "WARNING: Could not copy %s to %s because %s" % (src,dest,detail)
1964  except OSError, detail:
1965  print "WARNING: Could not copy %s to %s because %s" % (src,dest,detail)
1966  except ReldirExcept:
1967  print "WARNING: Could not determine the new location for source %s into destination %s" % (src,dest)
1968 
1969 def syscp(srcs,dest):
1970  if type(srcs) == type(""):
1971  if os.path.exists(srcs):
1972  if os.path.isdir(srcs):
1973  copytree4(srcs,dest)
1974  else:
1975  docopy(srcs,dest)
1976  else:
1977  print "ERROR: file to be copied %s does not exist" % foo
1978  else:
1979  for src in srcs:
1980  if os.path.exists(src):
1981  if os.path.isdir(src):
1982  #copy tree
1983  copytree4(src,dest)
1984  else:
1985  docopy(src,dest)
1986  else:
1987  print "ERROR: file to be copied %s does not exist" % foo
1988 
1990  print "%s\n" % PROG_NAME
1991 
1992 if __name__ == "__main__":
1993  main()
def getRelativeDir
Some functions used for copying.
def getDirnameDirs
Grab dirs that end in strings defined in DirName.
def createCandlHTML
Create HTML pages for candles.
def scanReportArea
Scan report area for required things.
def delTmpDir
Delete tmp dir if we used it.
return((rh^lh)&mask)
def syncToRemoteLoc
Upload stage to remote location.
def getStageRepDirs
Determine locations of staging and report dirs.
def createWebReports
Create web report index and create HTML file for each candle.
def get_environ
Get require environment variables.
list object
Definition: dbtoconf.py:77
def optionparse
Option parser.
Definition: main.py:1