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