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