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 
215  for path in fnc.find_and_change(list(), config):
216  if args.verbose and ("." in str(path) or "/" in str(path)):
217  print("Digesting path: "+str(path))
218 
219 
220  if os.path.isdir(config["name"]) and not args.force:
221  raise Exception("Validation directory '{}' already exists! Please choose another name for your directory.".format(config["name"]))
222 
223  validationDir = os.path.abspath(config["name"])
224  exeDir = "{}/executables".format(validationDir)
225  cmsconfigDir = "{}/cmsConfigs".format(validationDir)
227  subprocess.call(["mkdir", "-p", validationDir] + ((["-v"] if args.verbose else [])))
228  subprocess.call(["mkdir", "-p", exeDir] + (["-v"] if args.verbose else []))
229  subprocess.call(["mkdir", "-p", cmsconfigDir] + (["-v"] if args.verbose else []))
230 
231 
232  subprocess.call(["cp", "-f", args.config, validationDir] + (["-v"] if args.verbose else []))
233 
234 
235  crabTemplateFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/crabTemplate.py")
236  condorTemplateFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/condorTemplate.submit")
237  executableTempleteFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/executableTemplate.sh")
238 
240 
241  jobs = []
242 
243 
244  for validation in config["validations"]:
245  if validation == "GCP":
246  jobs.extend(GCP.GCP(config, validationDir))
247 
248  elif validation == "DMR":
249  jobs.extend(DMR.DMR(config, validationDir))
250 
251  elif validation == "Zmumu":
252  jobs.extend(Zmumu.Zmumu(config, validationDir))
253 
254  elif validation == "PV":
255  jobs.extend(PV.PV(config, validationDir))
256 
257  elif validation == "SplitV":
258  jobs.extend(SplitV.SplitV(config, validationDir))
259 
260  elif validation == "JetHT":
261  jobs.extend(JetHT.JetHT(config, validationDir))
262 
263  else:
264  raise Exception("Unknown validation method: {}".format(validation))
265 
266 
267  subprocess.call(["mkdir", "-p", "{}/DAG/".format(validationDir)] + (["-v"] if args.verbose else []))
268 
269  with open("{}/DAG/dagFile".format(validationDir), "w") as dag:
270  for job in jobs:
271 
272  subprocess.call(["mkdir", "-p", job["dir"]] + (["-v"] if args.verbose else []))
273  subprocess.call(["mkdir", "-p", job["config"]["output"]] + (["-v"] if args.verbose else []))
274  subprocess.call(["mkdir", "-p", "{}/condor".format(job["dir"])] + (["-v"] if args.verbose else []))
275  subprocess.call(["ln", "-fs", job["config"]["output"], "{}/output".format(job["dir"])] + (["-v"] if args.verbose else []))
276 
277 
278  crabConfigurationFile = "{}/crabConfiguration.py".format(job["dir"])
279  subprocess.call(["cp", crabTemplateFile, crabConfigurationFile] + (["-v"] if args.verbose else []))
280  condorSubmitFile = "{}/condor.sub".format(job["dir"])
281  subprocess.call(["cp", condorTemplateFile, condorSubmitFile] + (["-v"] if args.verbose else []))
282  executableFile = "{}/run.sh".format(job["dir"])
283  subprocess.call(["cp", executableTempleteFile, executableFile] + (["-v"] if args.verbose else []))
284 
285 
286  if args.verbose:
287  print("Forwarding grid proxy to directory {}".format(job["dir"]))
288  myProxy = forward_proxy(job["dir"])
289 
290 
291  subprocess.call("cp -f $(which {}) {}".format(job["exe"], exeDir) + (" -v" if args.verbose else ""), shell = True)
292  subprocess.call(["ln", "-fs", "{}/{}".format(exeDir, job["exe"]), job["dir"]] + (["-v"] if args.verbose else []))
293  if "cms-config" in job:
294  cmsConfig = job["cms-config"].split("/")[-1]
295 
296  subprocess.call(["cp", "-f", job["cms-config"], "{}/{}".format(cmsconfigDir, cmsConfig)] + (["-v"] if args.verbose else []))
297  subprocess.call(["ln", "-fs", "{}/{}".format(cmsconfigDir, cmsConfig), "{}/validation_cfg.py".format(job["dir"])] + (["-v"] if args.verbose else []))
298 
299 
300  with open("{}/validation.json".format(job["dir"]), "w") as jsonFile:
301  if args.verbose:
302  print("Write local json config: '{}'".format("{}/validation.json".format(job["dir"])))
303 
304  json.dump(job["config"], jsonFile, indent=4)
305 
306 
307  executableCustomization = {"overwrite": [], "addBefore": []}
308 
309  executableCustomization["overwrite"].append("export X509|export X509_USER_PROXY={}".format(myProxy)) # Define the proxy location
310  executableCustomization["overwrite"].append("cd workDir|cd {}".format(job["dir"])) # Define the work directory for this job
311 
312  # Option the give free arguments to the executable
313  if "exeArguments" in job:
314  executableCustomization["overwrite"].append("./cmsRun|./{} {}".format(job["exe"], job["exeArguments"])) # Define the correct executable for this job
315  else: # Default arguments
316  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
317 
318  # Option to include the condor job number given as a command line argument
319  if "nCondorJobs" in job:
320  executableCustomization["addBefore"].append("./{}|JOBNUMBER=${{1:--1}}".format(job["exe"]))
321 
322  # Do the manual configuration on top of the executable file
323  updateConfigurationFile(executableFile, executableCustomization)
324 
325  # Give the correct access rights for the executable
326  subprocess.call(["chmod", "a+rx", executableFile] + (["-v"] if args.verbose else []))
327 
328 
329  condorSubmitCustomization = {"overwrite": [], "addBefore": []}
330 
331  # Take given flavour for the job, except if overwritten in job config
332  condorSubmitCustomization["overwrite"].append('+JobFlavour = "{}"'.format(args.job_flavour if not 'flavour' in job else job['flavour']))
333 
334  # If condor job array is sent, add job ID information to submit file
335  if "nCondorJobs" in job:
336  condorSubmitCustomization["addBefore"].append("output|arguments = $(ProcID)")
337  condorSubmitCustomization["overwrite"].append("output = condor/condor$(ProcID).out")
338  condorSubmitCustomization["overwrite"].append("error = condor/condor$(ProcID).err")
339  condorSubmitCustomization["overwrite"].append("log = condor/condor$(ProcID).log")
340  condorSubmitCustomization["overwrite"].append("queue {}".format(job["nCondorJobs"]))
341 
342  # Do the customization for the condor submit file
343  updateConfigurationFile(condorSubmitFile, condorSubmitCustomization)
344 
345 
346  dag.write("JOB {} condor.sub DIR {}\n".format(job["name"], job["dir"]))
347 
348  if job["dependencies"]:
349  dag.write("\n")
350  dag.write("PARENT {} CHILD {}".format(" ".join(job["dependencies"]), job["name"]))
351 
352  dag.write("\n\n")
353 
354 
355  if "crabCustomConfiguration" in job["config"]:
356  updateConfigurationFile(crabConfigurationFile, job["config"]["crabCustomConfiguration"])
357 
358 
359  if args.verbose:
360  print("DAGman config has been written: '{}'".format("{}/DAG/dagFile".format(validationDir)))
361 
362 
363  if args.dry:
364  print("Enviroment is set up. If you want to submit everything, call 'condor_submit_dag {}/DAG/dagFile'".format(validationDir))
365 
366  else:
367  subprocess.call(["condor_submit_dag", "{}/DAG/dagFile".format(validationDir)])
368 
369 
370 if __name__ == "__main__":
371 
372  main()
373 
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