File indexing completed on 2024-04-06 11:57:16
0001
0002
0003 from __future__ import print_function
0004 from future.utils import lmap
0005 import subprocess
0006 import json
0007 import yaml
0008 import os
0009 import argparse
0010 import pprint
0011 import sys
0012 import shutil
0013 import Alignment.OfflineValidation.TkAlAllInOneTool.findAndChange as fnc
0014
0015 import Alignment.OfflineValidation.TkAlAllInOneTool.GCP as GCP
0016 import Alignment.OfflineValidation.TkAlAllInOneTool.DMR as DMR
0017 import Alignment.OfflineValidation.TkAlAllInOneTool.Zmumu as Zmumu
0018 import Alignment.OfflineValidation.TkAlAllInOneTool.PV as PV
0019 import Alignment.OfflineValidation.TkAlAllInOneTool.SplitV as SplitV
0020 import Alignment.OfflineValidation.TkAlAllInOneTool.JetHT as JetHT
0021 import Alignment.OfflineValidation.TkAlAllInOneTool.DiMuonV as DiMuonV
0022 import Alignment.OfflineValidation.TkAlAllInOneTool.MTS as MTS
0023
0024
0025 def parser():
0026
0027 """ Parse user input """
0028
0029 parser = argparse.ArgumentParser(description = "AllInOneTool for validation of the tracker alignment", formatter_class=argparse.RawTextHelpFormatter)
0030 parser.add_argument("config", metavar='config', type=str, action="store", help="Global AllInOneTool config (json/yaml format)")
0031 parser.add_argument("-d", "--dry", action = "store_true", help ="Set up everything, but don't run anything")
0032 parser.add_argument("-v", "--verbose", action = "store_true", help ="Enable standard output stream")
0033 parser.add_argument("-e", "--example", action = "store_true", help ="Print example of config in JSON format")
0034 parser.add_argument("-f", "--force", action = "store_true", help ="Force creation of enviroment, possible overwritten old configuration")
0035 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'")
0036
0037 return parser.parse_args()
0038
0039
0040 def check_proxy():
0041
0042 """Check if GRID proxy has been initialized."""
0043
0044 try:
0045 with open(os.devnull, "w") as dump:
0046 subprocess.check_call(["voms-proxy-info", "--exists"],
0047 stdout = dump, stderr = dump)
0048 except subprocess.CalledProcessError:
0049 return False
0050 return True
0051
0052
0053 def forward_proxy(rundir):
0054
0055 """Forward proxy to location visible from the batch system.
0056 Arguments:
0057 - `rundir`: directory for storing the forwarded proxy
0058 Return:
0059 - Full path to the forwarded proxy
0060 """
0061
0062 if not check_proxy():
0063 print("Please create proxy via 'voms-proxy-init -voms cms'.")
0064 sys.exit(1)
0065
0066
0067 proxyName = "{}/.user_proxy".format(rundir)
0068 localProxy = subprocess.check_output(["voms-proxy-info", "--path"]).strip()
0069 shutil.copyfile(localProxy, proxyName)
0070
0071
0072 return proxyName
0073
0074
0075
0076 def updateConfigurationFile(configurationFile, updateInstructions):
0077
0078 """Update a template configuration file with custom configuration
0079 Arguments:
0080 - configurationFile: File name for the configuration file that will be updated
0081 - 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.
0082 """
0083
0084
0085 with open(configurationFile,"r") as inputFile:
0086 fileContent = inputFile.readlines()
0087
0088
0089 if "overwrite" in updateInstructions:
0090
0091 for instruction in updateInstructions["overwrite"]:
0092
0093 decodeInstruction = instruction.split("|")
0094 if(len(decodeInstruction) > 1):
0095 lineToReplace = decodeInstruction[0]
0096 newInstruction = instruction[instruction.index("|")+1:]
0097 else:
0098 lineToReplace = instruction.split()[0]
0099 newInstruction = instruction
0100
0101 lineOverwritten = False
0102 for iLine in range(0,len(fileContent)):
0103 if fileContent[iLine].startswith(lineToReplace):
0104 fileContent[iLine] = newInstruction
0105 if not fileContent[iLine].endswith("\n"):
0106 fileContent[iLine] = fileContent[iLine] + "\n"
0107 lineOverwritten = True
0108 break
0109
0110
0111 if not lineOverwritten:
0112 fileContent.append(newInstruction)
0113 if not fileContent[-1].endswith("\n"):
0114 fileContent[-1] = fileContent[-1] + "\n"
0115
0116
0117 if "remove" in updateInstructions:
0118 for instruction in updateInstructions["remove"]:
0119 for iLine in range(0,len(fileContent)):
0120 if fileContent[iLine].startswith(instruction):
0121 fileContent.pop(iLine)
0122 break
0123
0124
0125 if "add" in updateInstructions:
0126 for instruction in updateInstructions["add"]:
0127 categories = instruction.split(".")
0128 if len(categories) > 2:
0129 category = categories[1]
0130 else:
0131 category = "nonExistent"
0132 previousCategory = ""
0133 lineFound = False
0134
0135
0136 for iLine in range(0,len(fileContent)):
0137 if fileContent[iLine] == "\n" and previousCategory == category:
0138 fileContent.insert(iLine, instruction)
0139 if not fileContent[iLine].endswith("\n"):
0140 fileContent[iLine] = fileContent[iLine] + "\n"
0141 lineFound = True
0142 break
0143 elif fileContent[iLine] == "\n":
0144 previousCategory = ""
0145 else:
0146 newCategories = fileContent[iLine].split(".")
0147 if len(newCategories) > 2:
0148 previousCategory = newCategories[1]
0149 else:
0150 previousCategory = ""
0151
0152
0153 if not lineFound:
0154 fileContent.append(instruction)
0155 if not fileContent[-1].endswith("\n"):
0156 fileContent[-1] = fileContent[-1] + "\n"
0157
0158
0159 if "addBefore" in updateInstructions:
0160 for instruction in updateInstructions["addBefore"]:
0161 lineBefore = instruction.split("|")[0]
0162 newInstruction = instruction[instruction.index("|")+1:]
0163 lineFound = False
0164 for iLine in range(0,len(fileContent)):
0165 if fileContent[iLine].startswith(lineBefore):
0166 fileContent.insert(iLine,newInstruction)
0167 if not fileContent[iLine].endswith("\n"):
0168 fileContent[iLine] = fileContent[iLine] + "\n"
0169 lineFound = True
0170 break
0171
0172
0173
0174 if not lineFound:
0175 fileContent.append(newInstruction)
0176 if not fileContent[-1].endswith("\n"):
0177 fileContent[-1] = fileContent[-1] + "\n"
0178
0179
0180 with open(configurationFile,"w") as outputFile:
0181 outputFile.writelines(fileContent)
0182
0183
0184
0185 def main():
0186
0187
0188
0189 if not check_proxy():
0190 print("Grid proxy is required in most use cases of the tool.")
0191 print("Please create a proxy via 'voms-proxy-init -voms cms'.")
0192 sys.exit(1)
0193
0194
0195 args = parser()
0196
0197
0198 if args.example:
0199 with open("{}/src/Alignment/OfflineValidation/bin/example.yaml".format(os.environ["CMSSW_BASE"]), "r") as exampleFile:
0200 config = yaml.load(exampleFile, Loader=yaml.Loader)
0201 pprint.pprint(config, width=30)
0202 sys.exit(0)
0203
0204
0205 with open(args.config, "r") as configFile:
0206 if args.verbose:
0207 print("Read AllInOne config: '{}'".format(args.config))
0208
0209 if args.config.split(".")[-1] == "json":
0210 config = json.load(configFile)
0211
0212 elif args.config.split(".")[-1] == "yaml":
0213 config = yaml.load(configFile, Loader=yaml.Loader)
0214
0215 else:
0216 raise Exception("Unknown config extension '{}'. Please use json/yaml format!".format(args.config.split(".")[-1]))
0217
0218
0219
0220 for path in fnc.find_and_change(list(), config):
0221 if args.verbose and ("." in str(path) or "/" in str(path)):
0222 print("Digesting path: "+str(path))
0223
0224
0225 if os.path.isdir(config["name"]) and not args.force:
0226 raise Exception("Validation directory '{}' already exists! Please choose another name for your directory.".format(config["name"]))
0227
0228 validationDir = os.path.abspath(config["name"])
0229 exeDir = "{}/executables".format(validationDir)
0230 cmsconfigDir = "{}/cmsConfigs".format(validationDir)
0231
0232 subprocess.call(["mkdir", "-p", validationDir] + ((["-v"] if args.verbose else [])))
0233 subprocess.call(["mkdir", "-p", exeDir] + (["-v"] if args.verbose else []))
0234 subprocess.call(["mkdir", "-p", cmsconfigDir] + (["-v"] if args.verbose else []))
0235
0236
0237 subprocess.call(["cp", "-f", args.config, validationDir] + (["-v"] if args.verbose else []))
0238
0239
0240 crabTemplateFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/crabTemplate.py")
0241 condorTemplateFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/condorTemplate.submit")
0242 executableTempleteFile = fnc.digest_path("$CMSSW_BASE/src/Alignment/OfflineValidation/python/TkAlAllInOneTool/templates/executableTemplate.sh")
0243
0244
0245
0246 jobs = []
0247
0248
0249 for validation in config["validations"]:
0250 if validation == "GCP":
0251 jobs.extend(GCP.GCP(config, validationDir))
0252
0253 elif validation == "DMR":
0254 jobs.extend(DMR.DMR(config, validationDir))
0255
0256 elif validation == "Zmumu":
0257 jobs.extend(Zmumu.Zmumu(config, validationDir))
0258
0259 elif validation == "PV":
0260 jobs.extend(PV.PV(config, validationDir))
0261
0262 elif validation == "SplitV":
0263 jobs.extend(SplitV.SplitV(config, validationDir))
0264
0265 elif validation == "JetHT":
0266 jobs.extend(JetHT.JetHT(config, validationDir))
0267 elif validation == "DiMuonV":
0268 jobs.extend(DiMuonV.DiMuonV(config, validationDir))
0269 elif validation == "MTS":
0270 jobs.extend(MTS.MTS(config, validationDir))
0271 else:
0272 raise Exception("Unknown validation method: {}".format(validation))
0273
0274
0275 subprocess.call(["mkdir", "-p", "{}/DAG/".format(validationDir)] + (["-v"] if args.verbose else []))
0276
0277 with open("{}/DAG/dagFile".format(validationDir), "w") as dag:
0278 for job in jobs:
0279
0280 subprocess.call(["mkdir", "-p", job["dir"]] + (["-v"] if args.verbose else []))
0281 subprocess.call(["mkdir", "-p", job["config"]["output"]] + (["-v"] if args.verbose else []))
0282 subprocess.call(["mkdir", "-p", "{}/condor".format(job["dir"])] + (["-v"] if args.verbose else []))
0283 subprocess.call(["ln", "-fs", job["config"]["output"], "{}/output".format(job["dir"])] + (["-v"] if args.verbose else []))
0284
0285
0286 crabConfigurationFile = "{}/crabConfiguration.py".format(job["dir"])
0287 subprocess.call(["cp", crabTemplateFile, crabConfigurationFile] + (["-v"] if args.verbose else []))
0288 condorSubmitFile = "{}/condor.sub".format(job["dir"])
0289 subprocess.call(["cp", condorTemplateFile, condorSubmitFile] + (["-v"] if args.verbose else []))
0290 executableFile = "{}/run.sh".format(job["dir"])
0291 subprocess.call(["cp", executableTempleteFile, executableFile] + (["-v"] if args.verbose else []))
0292
0293
0294 if args.verbose:
0295 print("Forwarding grid proxy to directory {}".format(job["dir"]))
0296 myProxy = forward_proxy(job["dir"])
0297
0298
0299 subprocess.call("cp -f $(which {}) {}".format(job["exe"], exeDir) + (" -v" if args.verbose else ""), shell = True)
0300 subprocess.call(["ln", "-fs", "{}/{}".format(exeDir, job["exe"]), job["dir"]] + (["-v"] if args.verbose else []))
0301 if "cms-config" in job:
0302 cmsConfig = job["cms-config"].split("/")[-1]
0303
0304 subprocess.call(["cp", "-f", job["cms-config"], "{}/{}".format(cmsconfigDir, cmsConfig)] + (["-v"] if args.verbose else []))
0305 subprocess.call(["ln", "-fs", "{}/{}".format(cmsconfigDir, cmsConfig), "{}/validation_cfg.py".format(job["dir"])] + (["-v"] if args.verbose else []))
0306
0307
0308 with open("{}/validation.json".format(job["dir"]), "w") as jsonFile:
0309 if args.verbose:
0310 print("Write local json config: '{}'".format("{}/validation.json".format(job["dir"])))
0311
0312 json.dump(job["config"], jsonFile, indent=4)
0313
0314
0315 executableCustomization = {"overwrite": [], "addBefore": []}
0316
0317 executableCustomization["overwrite"].append("export X509|export X509_USER_PROXY={}".format(myProxy))
0318 executableCustomization["overwrite"].append("cd workDir|cd {}".format(job["dir"]))
0319
0320
0321 if "exeArguments" in job:
0322 executableCustomization["overwrite"].append("./cmsRun|./{} {}".format(job["exe"], job["exeArguments"]))
0323 else:
0324 executableCustomization["overwrite"].append("./cmsRun|./{} {}validation.json".format(job["exe"], "validation_cfg.py config=" if "cms-config" in job else ""))
0325
0326
0327 if "nCondorJobs" in job:
0328 executableCustomization["addBefore"].append("./{}|JOBNUMBER=${{1:--1}}".format(job["exe"]))
0329
0330
0331 updateConfigurationFile(executableFile, executableCustomization)
0332
0333
0334 subprocess.call(["chmod", "a+rx", executableFile] + (["-v"] if args.verbose else []))
0335
0336
0337 condorSubmitCustomization = {"overwrite": [], "addBefore": []}
0338
0339
0340 condorSubmitCustomization["addBefore"].append('+JobFlavour|+environment = "CMSSW_BASE={}"'.format(fnc.digest_path("$CMSSW_BASE")))
0341
0342
0343 condorSubmitCustomization["overwrite"].append('+JobFlavour = "{}"'.format(args.job_flavour if not 'flavour' in job else job['flavour']))
0344
0345
0346 if "nCondorJobs" in job:
0347 condorSubmitCustomization["addBefore"].append("output|arguments = $(ProcID)")
0348 condorSubmitCustomization["overwrite"].append("output = condor/condor$(ProcID).out")
0349 condorSubmitCustomization["overwrite"].append("error = condor/condor$(ProcID).err")
0350 condorSubmitCustomization["overwrite"].append("log = condor/condor$(ProcID).log")
0351 condorSubmitCustomization["overwrite"].append("queue {}".format(job["nCondorJobs"]))
0352
0353
0354 updateConfigurationFile(condorSubmitFile, condorSubmitCustomization)
0355
0356
0357 dag.write("JOB {} condor.sub DIR {}\n".format(job["name"], job["dir"]))
0358
0359 if job["dependencies"]:
0360 dag.write("\n")
0361 dag.write("PARENT {} CHILD {}".format(" ".join(job["dependencies"]), job["name"]))
0362
0363 dag.write("\n\n")
0364
0365
0366 if "crabCustomConfiguration" in job["config"]:
0367 updateConfigurationFile(crabConfigurationFile, job["config"]["crabCustomConfiguration"])
0368
0369
0370 if args.verbose:
0371 print("DAGman config has been written: '{}'".format("{}/DAG/dagFile".format(validationDir)))
0372
0373
0374 if args.dry:
0375 print("Enviroment is set up. If you want to submit everything, call 'condor_submit_dag {}/DAG/dagFile'".format(validationDir))
0376
0377 else:
0378 subprocess.call(["condor_submit_dag", "{}/DAG/dagFile".format(validationDir)])
0379
0380
0381 if __name__ == "__main__":
0382
0383 main()