CMS 3D CMS Logo

validateAlignments.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 #test execute: export CMSSW_BASE=/tmp/CMSSW && ./validateAlignments.py -c defaultCRAFTValidation.ini,test.ini -n -N test
3 from __future__ import print_function
4 from future.utils import lmap
5 import subprocess
6 import json
7 import yaml
8 import os
9 import argparse
10 import pprint
11 import sys
12 import shutil
13 import Alignment.OfflineValidation.TkAlAllInOneTool.findAndChange as fnc
14 
15 import Alignment.OfflineValidation.TkAlAllInOneTool.GCP as GCP
16 import Alignment.OfflineValidation.TkAlAllInOneTool.DMR as DMR
17 import Alignment.OfflineValidation.TkAlAllInOneTool.Zmumu as Zmumu
18 import Alignment.OfflineValidation.TkAlAllInOneTool.PV as PV
19 import Alignment.OfflineValidation.TkAlAllInOneTool.SplitV as SplitV
20 import Alignment.OfflineValidation.TkAlAllInOneTool.JetHT as JetHT
21 import Alignment.OfflineValidation.TkAlAllInOneTool.DiMuonV as DiMuonV
22 import Alignment.OfflineValidation.TkAlAllInOneTool.MTS as MTS
23 import Alignment.OfflineValidation.TkAlAllInOneTool.PixBary as PixBary
24 
25 
26 def parser():
27 
28  """ Parse user input """
29 
30  parser = argparse.ArgumentParser(description = "AllInOneTool for validation of the tracker alignment", formatter_class=argparse.RawTextHelpFormatter)
31  parser.add_argument("config", metavar='config', type=str, action="store", help="Global AllInOneTool config (json/yaml format)")
32  parser.add_argument("-d", "--dry", action = "store_true", help ="Set up everything, but don't run anything")
33  parser.add_argument("-v", "--verbose", action = "store_true", help ="Enable standard output stream")
34  parser.add_argument("-e", "--example", action = "store_true", help ="Print example of config in JSON format")
35  parser.add_argument("-f", "--force", action = "store_true", help ="Force creation of enviroment, possible overwritten old configuration")
36  parser.add_argument("-j", "--job-flavour", action = "store", default = "workday", choices = ["espresso", "microcentury", "longlunch", "workday", "tomorrow", "testmatch", "nextweek"], help ="Job flavours for HTCondor at CERN, default is 'workday'")
37 
38  return parser.parse_args()
39 
40 
42 
43  """Check if GRID proxy has been initialized."""
44 
45  try:
46  with open(os.devnull, "w") as dump:
47  subprocess.check_call(["voms-proxy-info", "--exists"],
48  stdout = dump, stderr = dump)
49  except subprocess.CalledProcessError:
50  return False
51  return True
52 
53 
54 def forward_proxy(rundir):
55 
56  """Forward proxy to location visible from the batch system.
57  Arguments:
58  - `rundir`: directory for storing the forwarded proxy
59  Return:
60  - Full path to the forwarded proxy
61  """
62 
63  if not check_proxy():
64  print("Please create proxy via 'voms-proxy-init -voms cms'.")
65  sys.exit(1)
66 
67 
68  proxyName = "{}/.user_proxy".format(rundir)
69  localProxy = subprocess.check_output(["voms-proxy-info", "--path"]).strip()
70  shutil.copyfile(localProxy, proxyName)
71 
72 
73  return proxyName
74 
75 
76 
77 def updateConfigurationFile(configurationFile, updateInstructions):
78 
79  """Update a template configuration file with custom configuration
80  Arguments:
81  - configurationFile: File name for the configuration file that will be updated
82  - updateInstructions: A dictionary defining the updated configuration with keys "overwrite", "remove", "add" and "addBefore" each containing a list with the instructions on what should be replaced, removed or added.
83  """
84 
85  # Read the original configuration file
86  with open(configurationFile,"r") as inputFile: fileContent = inputFile.readlines()
87 
88  # Perform all overwrite operations to the configuration file. First string where the substring before the first space matches with the replacing string is overwritten. If a character "|" is included in the instruction, the subtring before that is used to search for the overwritten line instead. If no such string is found, add the instruction to the end of the file.
89  if "overwrite" in updateInstructions:
90 
91  for instruction in updateInstructions["overwrite"]:
92 
93  decodeInstruction = instruction.split("|")
94  if(len(decodeInstruction) > 1):
95  lineToReplace = decodeInstruction[0]
96  newInstruction = instruction[instruction.index("|")+1:]
97  else:
98  lineToReplace = instruction.split()[0]
99  newInstruction = instruction
100 
101  lineOverwritten = False
102  for iLine in range(0,len(fileContent)):
103  if fileContent[iLine].startswith(lineToReplace):
104  fileContent[iLine] = newInstruction
105  if not fileContent[iLine].endswith("\n"):
106  fileContent[iLine] = fileContent[iLine] + "\n"
107  lineOverwritten = True
108  break
109 
110  # If did not find a line to overwrite, add the instruction to the end of the file
111  if not lineOverwritten:
112  fileContent.append(newInstruction)
113  if not fileContent[-1].endswith("\n"):
114  fileContent[-1] = fileContent[-1] + "\n"
115 
116  # Perform all remove operations to the configuration file. First string that starst with the instruction will be removed from the configuration file.
117  if "remove" in updateInstructions:
118  for instruction in updateInstructions["remove"]:
119  for iLine in range(0,len(fileContent)):
120  if fileContent[iLine].startswith(instruction):
121  fileContent.pop(iLine)
122  break
123 
124  # Perform all add operations to the configuration file. The instruction is added to the matching CRAB configuration section. If one is not found, it is added to the end of the file.
125  if "add" in updateInstructions:
126  for instruction in updateInstructions["add"]:
127  categories = instruction.split(".")
128  if len(categories) > 2:
129  category = categories[1]
130  else:
131  category = "nonExistent"
132  previousCategory = ""
133  lineFound = False
135  # First try to add the line to a correct section in CRAB configuration
136  for iLine in range(0,len(fileContent)):
137  if fileContent[iLine] == "\n" and previousCategory == category:
138  fileContent.insert(iLine, instruction)
139  if not fileContent[iLine].endswith("\n"):
140  fileContent[iLine] = fileContent[iLine] + "\n"
141  lineFound = True
142  break
143  elif fileContent[iLine] == "\n":
144  previousCategory = ""
145  else:
146  newCategories = fileContent[iLine].split(".")
147  if len(newCategories) > 2:
148  previousCategory = newCategories[1]
149  else:
150  previousCategory = ""
151 
152  # If the correct section is not found, add the new line to the end of the file
153  if not lineFound:
154  fileContent.append(instruction)
155  if not fileContent[-1].endswith("\n"):
156  fileContent[-1] = fileContent[-1] + "\n"
157 
158  # Perform all addBefore operations to the configuration file. This adds an instruction to the configuration file just before a line that starts with a string defined before the '|' character. If one is not found, the line is added to the end of the file.
159  if "addBefore" in updateInstructions:
160  for instruction in updateInstructions["addBefore"]:
161  lineBefore = instruction.split("|")[0]
162  newInstruction = instruction[instruction.index("|")+1:]
163  lineFound = False
164  for iLine in range(0,len(fileContent)):
165  if fileContent[iLine].startswith(lineBefore):
166  fileContent.insert(iLine,newInstruction)
167  if not fileContent[iLine].endswith("\n"):
168  fileContent[iLine] = fileContent[iLine] + "\n"
169  lineFound = True
170  break
171 
172 
173  # If the searched line is not found, add the new line to the end of the file
174  if not lineFound:
175  fileContent.append(newInstruction)
176  if not fileContent[-1].endswith("\n"):
177  fileContent[-1] = fileContent[-1] + "\n"
178 
179  # Write the updates to the configuration file
180  with open(configurationFile,"w") as outputFile:
181  outputFile.writelines(fileContent)
182 
183 
184 
185 def main():
187 
188 
189  if not check_proxy():
190  print("Grid proxy is required in most use cases of the tool.")
191  print("Please create a proxy via 'voms-proxy-init -voms cms'.")
192  sys.exit(1)
193 
194 
195  args = parser()
197 
198  if args.example:
199  with open("{}/src/Alignment/OfflineValidation/bin/example.yaml".format(os.environ["CMSSW_BASE"]), "r") as exampleFile: config = yaml.load(exampleFile, Loader=yaml.Loader)
200  pprint.pprint(config, width=30)
201  sys.exit(0)
203 
204  with open(args.config, "r") as configFile: if args.verbose:
205  print("Read AllInOne config: '{}'".format(args.config))
206 
207  if args.config.split(".")[-1] == "json":
208  config = json.load(configFile)
209 
210  elif args.config.split(".")[-1] == "yaml":
211  config = yaml.load(configFile, Loader=yaml.Loader)
212 
213  else:
214  raise Exception("Unknown config extension '{}'. Please use json/yaml format!".format(args.config.split(".")[-1]))
215 
216 
218  for path in fnc.find_and_change(list(), config):
219  if args.verbose and ("." in str(path) or "/" in str(path)):
220  print("Digesting path: "+str(path))
221 
222 
223  if os.path.isdir(config["name"]) and not args.force:
224  raise Exception("Validation directory '{}' already exists! Please choose another name for your directory.".format(config["name"]))
225 
226  validationDir = os.path.abspath(config["name"])
227  exeDir = "{}/executables".format(validationDir)
228  cmsconfigDir = "{}/cmsConfigs".format(validationDir)
230  subprocess.call(["mkdir", "-p", validationDir] + ((["-v"] if args.verbose else [])))
231  subprocess.call(["mkdir", "-p", exeDir] + (["-v"] if args.verbose else []))
232  subprocess.call(["mkdir", "-p", cmsconfigDir] + (["-v"] if args.verbose else []))
233 
234 
235  subprocess.call(["cp", "-f", args.config, validationDir] + (["-v"] if args.verbose else []))
236 
237 
238  crabTemplateFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/crabTemplate.py")
239  condorTemplateFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/condorTemplate.submit")
240  executableTempleteFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/executableTemplate.sh")
241 
243 
244  jobs = []
245 
246 
247  for validation in config["validations"]:
248  if validation == "GCP":
249  jobs.extend(GCP.GCP(config, validationDir))
250 
251  elif validation == "DMR":
252  jobs.extend(DMR.DMR(config, validationDir))
253 
254  elif validation == "Zmumu":
255  jobs.extend(Zmumu.Zmumu(config, validationDir))
256 
257  elif validation == "PV":
258  jobs.extend(PV.PV(config, validationDir))
259 
260  elif validation == "SplitV":
261  jobs.extend(SplitV.SplitV(config, validationDir))
262 
263  elif validation == "JetHT":
264  jobs.extend(JetHT.JetHT(config, validationDir))
265  elif validation == "DiMuonV":
266  jobs.extend(DiMuonV.DiMuonV(config, validationDir))
267  elif validation == "MTS":
268  jobs.extend(MTS.MTS(config, validationDir))
269  elif validation == "PixBary":
270  jobs.extend(PixBary.PixBary(config, validationDir, args.verbose))
271  else:
272  raise Exception("Unknown validation method: {}".format(validation))
273 
274 
275  subprocess.call(["mkdir", "-p", "{}/DAG/".format(validationDir)] + (["-v"] if args.verbose else []))
276 
277  with open("{}/DAG/dagFile".format(validationDir), "w") as dag:
278  for job in jobs:
279 
280  subprocess.call(["mkdir", "-p", job["dir"]] + (["-v"] if args.verbose else []))
281  subprocess.call(["mkdir", "-p", job["config"]["output"]] + (["-v"] if args.verbose else []))
282  subprocess.call(["mkdir", "-p", "{}/condor".format(job["dir"])] + (["-v"] if args.verbose else []))
283  subprocess.call(["ln", "-fs", job["config"]["output"], "{}/output".format(job["dir"])] + (["-v"] if args.verbose else []))
284 
285 
286  crabConfigurationFile = "{}/crabConfiguration.py".format(job["dir"])
287  subprocess.call(["cp", crabTemplateFile, crabConfigurationFile] + (["-v"] if args.verbose else []))
288  condorSubmitFile = "{}/condor.sub".format(job["dir"])
289  subprocess.call(["cp", condorTemplateFile, condorSubmitFile] + (["-v"] if args.verbose else []))
290  executableFile = "{}/run.sh".format(job["dir"])
291  subprocess.call(["cp", executableTempleteFile, executableFile] + (["-v"] if args.verbose else []))
292 
293 
294  if args.verbose:
295  print("Forwarding grid proxy to directory {}".format(job["dir"]))
296  myProxy = forward_proxy(job["dir"])
297 
298 
299  subprocess.call("cp -f $(which {}) {}".format(job["exe"], exeDir) + (" -v" if args.verbose else ""), shell = True)
300  subprocess.call(["ln", "-fs", "{}/{}".format(exeDir, job["exe"]), job["dir"]] + (["-v"] if args.verbose else []))
301  if "cms-config" in job:
302  cmsConfig = job["cms-config"].split("/")[-1]
303 
304  subprocess.call(["cp", "-f", job["cms-config"], "{}/{}".format(cmsconfigDir, cmsConfig)] + (["-v"] if args.verbose else []))
305  subprocess.call(["ln", "-fs", "{}/{}".format(cmsconfigDir, cmsConfig), "{}/validation_cfg.py".format(job["dir"])] + (["-v"] if args.verbose else []))
306 
307 
308  with open("{}/validation.json".format(job["dir"]), "w") as jsonFile:
309  if args.verbose:
310  print("Write local json config: '{}'".format("{}/validation.json".format(job["dir"])))
311 
312  json.dump(job["config"], jsonFile, indent=4)
313 
314 
315  executableCustomization = {"overwrite": [], "addBefore": []}
316 
317  executableCustomization["overwrite"].append("export X509|export X509_USER_PROXY={}".format(myProxy)) # Define the proxy location
318  executableCustomization["overwrite"].append("cd workDir|cd {}".format(job["dir"])) # Define the work directory for this job
319 
320  # Option the give free arguments to the executable
321  if "exeArguments" in job:
322  executableCustomization["overwrite"].append("./cmsRun|./{} {}".format(job["exe"], job["exeArguments"])) # Define the correct executable for this job
323  else: # Default arguments
324  executableCustomization["overwrite"].append("./cmsRun|./{} {}validation.json".format(job["exe"], "validation_cfg.py config=" if "cms-config" in job else "")) # Define the correct executable for this job
325 
326  # Option to include the condor job number given as a command line argument
327  if "nCondorJobs" in job:
328  executableCustomization["addBefore"].append("./{}|JOBNUMBER=${{1:--1}}".format(job["exe"]))
329 
330  # Do the manual configuration on top of the executable file
331  updateConfigurationFile(executableFile, executableCustomization)
332 
333  # Give the correct access rights for the executable
334  subprocess.call(["chmod", "a+rx", executableFile] + (["-v"] if args.verbose else []))
335 
336 
337  condorSubmitCustomization = {"overwrite": [], "addBefore": []}
338 
339 
340  condorSubmitCustomization["addBefore"].append('+JobFlavour|+environment = "CMSSW_BASE={}"'.format(fnc.digest_path("$CMSSW_BASE")))
341 
342  # Take given flavour for the job, except if overwritten in job config
343  condorSubmitCustomization["overwrite"].append('+JobFlavour = "{}"'.format(args.job_flavour if not 'flavour' in job else job['flavour']))
344 
345  # If condor job array is sent, add job ID information to submit file
346  if "nCondorJobs" in job:
347  condorSubmitCustomization["addBefore"].append("output|arguments = $(ProcID)")
348  condorSubmitCustomization["overwrite"].append("output = condor/condor$(ProcID).out")
349  condorSubmitCustomization["overwrite"].append("error = condor/condor$(ProcID).err")
350  condorSubmitCustomization["overwrite"].append("log = condor/condor$(ProcID).log")
351  condorSubmitCustomization["overwrite"].append("queue {}".format(job["nCondorJobs"]))
352 
353  # Do the customization for the condor submit file
354  updateConfigurationFile(condorSubmitFile, condorSubmitCustomization)
355 
356 
357  dag.write("JOB {} condor.sub DIR {}\n".format(job["name"], job["dir"]))
358 
359  if job["dependencies"]:
360  dag.write("\n")
361  dag.write("PARENT {} CHILD {}".format(" ".join(job["dependencies"]), job["name"]))
362 
363  dag.write("\n\n")
364 
365 
366  if "crabCustomConfiguration" in job["config"]:
367  updateConfigurationFile(crabConfigurationFile, job["config"]["crabCustomConfiguration"])
368 
369 
370  if args.verbose:
371  print("DAGman config has been written: '{}'".format("{}/DAG/dagFile".format(validationDir)))
372 
373 
374  if args.dry:
375  print("Enviroment is set up. If you want to submit everything, call 'condor_submit_dag {}/DAG/dagFile'".format(validationDir))
376 
377  else:
378  subprocess.call(["condor_submit_dag", "{}/DAG/dagFile".format(validationDir)])
379 
380 
381 if __name__ == "__main__":
382 
383  main()
384 
def PixBary(config, validationDir, verbose=False)
Definition: PixBary.py:6
def DiMuonV(config, validationDir)
Definition: DiMuonV.py:4
def SplitV(config, validationDir)
Definition: SplitV.py:4
def Zmumu(config, validationDir)
Definition: Zmumu.py:4
def updateConfigurationFile(configurationFile, updateInstructions)
def MTS(config, validationDir)
Definition: MTS.py:4
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def DMR(config, validationDir)
Definition: DMR.py:4
static std::string join(char **cmd)
Definition: RemoteFile.cc:21
def PV(config, validationDir)
Definition: PV.py:5
Definition: main.py:1
def GCP(config, validationDir)
Definition: GCP.py:4
if(threadIdxLocalY==0 &&threadIdxLocalX==0)
def JetHT(config, validationDir)
Definition: JetHT.py:30