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