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