DIRAC 9.0.0a64__py3-none-any.whl → 9.0.0a67__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- DIRAC/ConfigurationSystem/Client/LocalConfiguration.py +11 -8
- DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py +1 -1
- DIRAC/Core/Security/IAMService.py +4 -3
- DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
- DIRAC/Core/Utilities/DErrno.py +5 -309
- DIRAC/Core/Utilities/JDL.py +1 -195
- DIRAC/Core/Utilities/List.py +1 -127
- DIRAC/Core/Utilities/ReturnValues.py +7 -252
- DIRAC/Core/Utilities/StateMachine.py +12 -178
- DIRAC/Core/Utilities/TimeUtilities.py +10 -253
- DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
- DIRAC/Core/scripts/dirac_agent.py +1 -1
- DIRAC/DataManagementSystem/DB/FTS3DB.py +3 -0
- DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
- DIRAC/Resources/Catalog/RucioFileCatalogClient.py +1 -1
- DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
- DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +1 -1
- DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
- DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +1 -1
- DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +1 -1
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +2 -0
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
- DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
- DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -93
- DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
- DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
- DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +4 -2
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +21 -5
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
- DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +1 -1
- DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -199
- DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +1 -63
- DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
- DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +0 -123
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -5
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
- DIRAC/__init__.py +55 -54
- {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/METADATA +2 -1
- {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/RECORD +44 -45
- DIRAC/Core/Utilities/test/Test_List.py +0 -150
- DIRAC/Core/Utilities/test/Test_Time.py +0 -88
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobAdministration.py +0 -28
- {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/WHEEL +0 -0
- {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/entry_points.txt +0 -0
- {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/licenses/LICENSE +0 -0
- {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/top_level.txt +0 -0
|
@@ -4,176 +4,12 @@
|
|
|
4
4
|
getParameterVectorLength() - to get the total size of the bunch of parametric jobs
|
|
5
5
|
generateParametricJobs() - to get a list of expanded descriptions of all the jobs
|
|
6
6
|
"""
|
|
7
|
-
import re
|
|
8
7
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
|
|
8
|
+
# Import from DIRACCommon for backward compatibility
|
|
9
|
+
from DIRACCommon.WorkloadManagementSystem.Utilities.ParametricJob import (
|
|
10
|
+
getParameterVectorLength,
|
|
11
|
+
generateParametricJobs,
|
|
12
|
+
)
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if parList:
|
|
16
|
-
if nPar != len(parList):
|
|
17
|
-
return []
|
|
18
|
-
else:
|
|
19
|
-
parameterList = list(parList)
|
|
20
|
-
else:
|
|
21
|
-
# The first parameter must have the same type as the other ones even if not defined explicitly
|
|
22
|
-
parameterList = [parStart * type(parFactor)(1) + type(parStep)(0)]
|
|
23
|
-
for np in range(1, nPar):
|
|
24
|
-
parameterList.append(parameterList[np - 1] * parFactor + parStep)
|
|
25
|
-
|
|
26
|
-
return parameterList
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def getParameterVectorLength(jobClassAd):
|
|
30
|
-
"""Get the length of parameter vector in the parametric job description
|
|
31
|
-
|
|
32
|
-
:param jobClassAd: ClassAd job description object
|
|
33
|
-
:return: result structure with the Value: int number of parameter values, None if not a parametric job
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
nParValues = None
|
|
37
|
-
attributes = jobClassAd.getAttributes()
|
|
38
|
-
for attribute in attributes:
|
|
39
|
-
if attribute.startswith("Parameters"):
|
|
40
|
-
if jobClassAd.isAttributeList(attribute):
|
|
41
|
-
parameterList = jobClassAd.getListFromExpression(attribute)
|
|
42
|
-
nThisParValues = len(parameterList)
|
|
43
|
-
else:
|
|
44
|
-
nThisParValues = jobClassAd.getAttributeInt(attribute)
|
|
45
|
-
if nParValues is not None and nParValues != nThisParValues:
|
|
46
|
-
return S_ERROR(
|
|
47
|
-
EWMSJDL,
|
|
48
|
-
"Different length of parameter vectors: for %s, %s != %d" % (attribute, nParValues, nThisParValues),
|
|
49
|
-
)
|
|
50
|
-
nParValues = nThisParValues
|
|
51
|
-
if nParValues is not None and nParValues <= 0:
|
|
52
|
-
return S_ERROR(EWMSJDL, "Illegal number of job parameters %d" % (nParValues))
|
|
53
|
-
return S_OK(nParValues)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def __updateAttribute(classAd, attribute, parName, parValue):
|
|
57
|
-
# If there is something to do:
|
|
58
|
-
pattern = r"%%\(%s\)s" % parName
|
|
59
|
-
if parName == "0":
|
|
60
|
-
pattern = "%s"
|
|
61
|
-
expr = classAd.get_expression(attribute)
|
|
62
|
-
if not re.search(pattern, expr):
|
|
63
|
-
return False
|
|
64
|
-
|
|
65
|
-
pattern = "%%(%s)s" % parName
|
|
66
|
-
if parName == "0":
|
|
67
|
-
pattern = "%s"
|
|
68
|
-
|
|
69
|
-
parValue = parValue.strip()
|
|
70
|
-
if classAd.isAttributeList(attribute):
|
|
71
|
-
parValue = parValue.strip()
|
|
72
|
-
if parValue.startswith("{"):
|
|
73
|
-
parValue = parValue.lstrip("{").rstrip("}").strip()
|
|
74
|
-
|
|
75
|
-
expr = classAd.get_expression(attribute)
|
|
76
|
-
newexpr = expr.replace(pattern, str(parValue))
|
|
77
|
-
classAd.set_expression(attribute, newexpr)
|
|
78
|
-
return True
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def generateParametricJobs(jobClassAd):
|
|
82
|
-
"""Generate a series of ClassAd job descriptions expanding
|
|
83
|
-
job parameters
|
|
84
|
-
|
|
85
|
-
:param jobClassAd: ClassAd job description object
|
|
86
|
-
:return: list of ClassAd job description objects
|
|
87
|
-
"""
|
|
88
|
-
if not jobClassAd.lookupAttribute("Parameters"):
|
|
89
|
-
return S_OK([jobClassAd.asJDL()])
|
|
90
|
-
|
|
91
|
-
result = getParameterVectorLength(jobClassAd)
|
|
92
|
-
if not result["OK"]:
|
|
93
|
-
return result
|
|
94
|
-
nParValues = result["Value"]
|
|
95
|
-
if nParValues is None:
|
|
96
|
-
return S_ERROR(EWMSJDL, "Can not determine the number of job parameters")
|
|
97
|
-
|
|
98
|
-
parameterDict = {}
|
|
99
|
-
attributes = jobClassAd.getAttributes()
|
|
100
|
-
for attribute in attributes:
|
|
101
|
-
for key in ["Parameters", "ParameterStart", "ParameterStep", "ParameterFactor"]:
|
|
102
|
-
if attribute.startswith(key):
|
|
103
|
-
seqID = "0" if "." not in attribute else attribute.split(".")[1]
|
|
104
|
-
parameterDict.setdefault(seqID, {})
|
|
105
|
-
if key == "Parameters":
|
|
106
|
-
if jobClassAd.isAttributeList(attribute):
|
|
107
|
-
parList = jobClassAd.getListFromExpression(attribute)
|
|
108
|
-
if len(parList) != nParValues:
|
|
109
|
-
return S_ERROR(EWMSJDL, "Inconsistent parametric job description")
|
|
110
|
-
parameterDict[seqID]["ParameterList"] = parList
|
|
111
|
-
else:
|
|
112
|
-
if attribute != "Parameters":
|
|
113
|
-
return S_ERROR(EWMSJDL, "Inconsistent parametric job description")
|
|
114
|
-
nPar = jobClassAd.getAttributeInt(attribute)
|
|
115
|
-
if nPar is None:
|
|
116
|
-
value = jobClassAd.get_expression(attribute)
|
|
117
|
-
return S_ERROR(EWMSJDL, f"Inconsistent parametric job description: {attribute}={value}")
|
|
118
|
-
parameterDict[seqID]["Parameters"] = nPar
|
|
119
|
-
else:
|
|
120
|
-
value = jobClassAd.getAttributeInt(attribute)
|
|
121
|
-
if value is None:
|
|
122
|
-
value = jobClassAd.getAttributeFloat(attribute)
|
|
123
|
-
if value is None:
|
|
124
|
-
value = jobClassAd.get_expression(attribute)
|
|
125
|
-
return S_ERROR(f"Illegal value for {attribute} JDL field: {value}")
|
|
126
|
-
parameterDict[seqID][key] = value
|
|
127
|
-
|
|
128
|
-
if "0" in parameterDict and not parameterDict.get("0"):
|
|
129
|
-
parameterDict.pop("0")
|
|
130
|
-
|
|
131
|
-
parameterLists = {}
|
|
132
|
-
for seqID in parameterDict:
|
|
133
|
-
parList = __getParameterSequence(
|
|
134
|
-
nParValues,
|
|
135
|
-
parList=parameterDict[seqID].get("ParameterList", []),
|
|
136
|
-
parStart=parameterDict[seqID].get("ParameterStart", 1),
|
|
137
|
-
parStep=parameterDict[seqID].get("ParameterStep", 0),
|
|
138
|
-
parFactor=parameterDict[seqID].get("ParameterFactor", 1),
|
|
139
|
-
)
|
|
140
|
-
if not parList:
|
|
141
|
-
return S_ERROR(EWMSJDL, "Inconsistent parametric job description")
|
|
142
|
-
|
|
143
|
-
parameterLists[seqID] = parList
|
|
144
|
-
|
|
145
|
-
jobDescList = []
|
|
146
|
-
jobDesc = jobClassAd.asJDL()
|
|
147
|
-
# Width of the sequential parameter number
|
|
148
|
-
zLength = len(str(nParValues - 1))
|
|
149
|
-
for n in range(nParValues):
|
|
150
|
-
newJobDesc = jobDesc
|
|
151
|
-
newJobDesc = newJobDesc.replace("%n", str(n).zfill(zLength))
|
|
152
|
-
newClassAd = ClassAd(newJobDesc)
|
|
153
|
-
for seqID in parameterLists:
|
|
154
|
-
parameter = parameterLists[seqID][n]
|
|
155
|
-
for attribute in newClassAd.getAttributes():
|
|
156
|
-
__updateAttribute(newClassAd, attribute, seqID, str(parameter))
|
|
157
|
-
|
|
158
|
-
for seqID in parameterLists:
|
|
159
|
-
for attribute in ["Parameters", "ParameterStart", "ParameterStep", "ParameterFactor"]:
|
|
160
|
-
if seqID == "0":
|
|
161
|
-
newClassAd.deleteAttribute(attribute)
|
|
162
|
-
else:
|
|
163
|
-
newClassAd.deleteAttribute(f"{attribute}.{seqID}")
|
|
164
|
-
|
|
165
|
-
parameter = parameterLists[seqID][n]
|
|
166
|
-
if seqID == "0":
|
|
167
|
-
attribute = "Parameter"
|
|
168
|
-
else:
|
|
169
|
-
attribute = f"Parameter.{seqID}"
|
|
170
|
-
if isinstance(parameter, str) and parameter.startswith("{"):
|
|
171
|
-
newClassAd.insertAttributeInt(attribute, str(parameter))
|
|
172
|
-
else:
|
|
173
|
-
newClassAd.insertAttributeString(attribute, str(parameter))
|
|
174
|
-
|
|
175
|
-
newClassAd.insertAttributeInt("ParameterNumber", n)
|
|
176
|
-
newJDL = newClassAd.asJDL()
|
|
177
|
-
jobDescList.append(newJDL)
|
|
178
|
-
|
|
179
|
-
return S_OK(jobDescList)
|
|
14
|
+
# Re-export for backward compatibility
|
|
15
|
+
__all__ = ["getParameterVectorLength", "generateParametricJobs"]
|
|
@@ -1,127 +1,4 @@
|
|
|
1
|
-
from DIRAC import S_ERROR, S_OK, gLogger
|
|
2
|
-
from DIRAC.StorageManagementSystem.DB.StorageManagementDB import StorageManagementDB
|
|
3
1
|
from DIRAC.WorkloadManagementSystem.Client import JobStatus
|
|
4
|
-
from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
|
|
5
|
-
from DIRAC.WorkloadManagementSystem.DB.PilotAgentsDB import PilotAgentsDB
|
|
6
|
-
from DIRAC.WorkloadManagementSystem.DB.TaskQueueDB import TaskQueueDB
|
|
7
|
-
from DIRAC.WorkloadManagementSystem.Service.JobPolicy import RIGHT_DELETE, RIGHT_KILL
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _deleteJob(jobID, force=False):
|
|
11
|
-
"""Set the job status to "Deleted"
|
|
12
|
-
and remove the pilot that ran and its logging info if the pilot is finished.
|
|
13
|
-
|
|
14
|
-
:param int jobID: job ID
|
|
15
|
-
:return: S_OK()/S_ERROR()
|
|
16
|
-
"""
|
|
17
|
-
if not (result := JobDB().setJobStatus(jobID, JobStatus.DELETED, "Checking accounting", force=force))["OK"]:
|
|
18
|
-
gLogger.warn("Failed to set job Deleted status", result["Message"])
|
|
19
|
-
return result
|
|
20
|
-
|
|
21
|
-
if not (result := TaskQueueDB().deleteJob(jobID))["OK"]:
|
|
22
|
-
gLogger.warn("Failed to delete job from the TaskQueue")
|
|
23
|
-
|
|
24
|
-
# if it was the last job for the pilot
|
|
25
|
-
result = PilotAgentsDB().getPilotsForJobID(jobID)
|
|
26
|
-
if not result["OK"]:
|
|
27
|
-
gLogger.error("Failed to get Pilots for JobID", result["Message"])
|
|
28
|
-
return result
|
|
29
|
-
for pilot in result["Value"]:
|
|
30
|
-
res = PilotAgentsDB().getJobsForPilot(pilot)
|
|
31
|
-
if not res["OK"]:
|
|
32
|
-
gLogger.error("Failed to get jobs for pilot", res["Message"])
|
|
33
|
-
return res
|
|
34
|
-
if not res["Value"]: # if list of jobs for pilot is empty, delete pilot
|
|
35
|
-
result = PilotAgentsDB().getPilotInfo(pilotID=pilot)
|
|
36
|
-
if not result["OK"]:
|
|
37
|
-
gLogger.error("Failed to get pilot info", result["Message"])
|
|
38
|
-
return result
|
|
39
|
-
ret = PilotAgentsDB().deletePilot(result["Value"]["PilotJobReference"])
|
|
40
|
-
if not ret["OK"]:
|
|
41
|
-
gLogger.error("Failed to delete pilot from PilotAgentsDB", ret["Message"])
|
|
42
|
-
return ret
|
|
43
|
-
|
|
44
|
-
return S_OK()
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _killJob(jobID, sendKillCommand=True, force=False):
|
|
48
|
-
"""Kill one job
|
|
49
|
-
|
|
50
|
-
:param int jobID: job ID
|
|
51
|
-
:param bool sendKillCommand: send kill command
|
|
52
|
-
|
|
53
|
-
:return: S_OK()/S_ERROR()
|
|
54
|
-
"""
|
|
55
|
-
if sendKillCommand:
|
|
56
|
-
if not (result := JobDB().setJobCommand(jobID, "Kill"))["OK"]:
|
|
57
|
-
gLogger.warn("Failed to set job Kill command", result["Message"])
|
|
58
|
-
return result
|
|
59
|
-
|
|
60
|
-
gLogger.info("Job marked for termination", jobID)
|
|
61
|
-
if not (result := JobDB().setJobStatus(jobID, JobStatus.KILLED, "Marked for termination", force=force))["OK"]:
|
|
62
|
-
gLogger.warn("Failed to set job Killed status", result["Message"])
|
|
63
|
-
if not (result := TaskQueueDB().deleteJob(jobID))["OK"]:
|
|
64
|
-
gLogger.warn("Failed to delete job from the TaskQueue", result["Message"])
|
|
65
|
-
|
|
66
|
-
return S_OK()
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def kill_delete_jobs(right, validJobList, nonauthJobList=[], force=False):
|
|
70
|
-
"""Kill (== set the status to "KILLED") or delete (== set the status to "DELETED") jobs as necessary
|
|
71
|
-
|
|
72
|
-
:param str right: RIGHT_KILL or RIGHT_DELETE
|
|
73
|
-
|
|
74
|
-
:return: S_OK()/S_ERROR()
|
|
75
|
-
"""
|
|
76
|
-
badIDs = []
|
|
77
|
-
|
|
78
|
-
killJobList = []
|
|
79
|
-
deleteJobList = []
|
|
80
|
-
if validJobList:
|
|
81
|
-
result = JobDB().getJobsAttributes(killJobList, ["Status"])
|
|
82
|
-
if not result["OK"]:
|
|
83
|
-
return result
|
|
84
|
-
jobStates = result["Value"]
|
|
85
|
-
|
|
86
|
-
# Get the jobs allowed to transition to the Killed state
|
|
87
|
-
killJobList.extend(_filterJobStateTransition(jobStates, JobStatus.KILLED))
|
|
88
|
-
|
|
89
|
-
if right == RIGHT_DELETE:
|
|
90
|
-
# Get the jobs allowed to transition to the Deleted state
|
|
91
|
-
deleteJobList.extend(_filterJobStateTransition(jobStates, JobStatus.DELETED))
|
|
92
|
-
|
|
93
|
-
for jobID in killJobList:
|
|
94
|
-
result = _killJob(jobID, force=force)
|
|
95
|
-
if not result["OK"]:
|
|
96
|
-
badIDs.append(jobID)
|
|
97
|
-
|
|
98
|
-
for jobID in deleteJobList:
|
|
99
|
-
result = _deleteJob(jobID, force=force)
|
|
100
|
-
if not result["OK"]:
|
|
101
|
-
badIDs.append(jobID)
|
|
102
|
-
|
|
103
|
-
# Look for jobs that are in the Staging state to send kill signal to the stager
|
|
104
|
-
stagingJobList = [jobID for jobID, sDict in jobStates.items() if sDict["Status"] == JobStatus.STAGING]
|
|
105
|
-
|
|
106
|
-
if stagingJobList:
|
|
107
|
-
stagerDB = StorageManagementDB()
|
|
108
|
-
gLogger.info("Going to send killing signal to stager as well!")
|
|
109
|
-
result = stagerDB.killTasksBySourceTaskID(stagingJobList)
|
|
110
|
-
if not result["OK"]:
|
|
111
|
-
gLogger.warn("Failed to kill some Stager tasks", result["Message"])
|
|
112
|
-
|
|
113
|
-
if nonauthJobList or badIDs:
|
|
114
|
-
result = S_ERROR("Some jobs failed deletion")
|
|
115
|
-
if nonauthJobList:
|
|
116
|
-
gLogger.warn("Non-authorized JobIDs won't be deleted", str(nonauthJobList))
|
|
117
|
-
result["NonauthorizedJobIDs"] = nonauthJobList
|
|
118
|
-
if badIDs:
|
|
119
|
-
gLogger.warn("JobIDs failed to be deleted", str(badIDs))
|
|
120
|
-
result["FailedJobIDs"] = badIDs
|
|
121
|
-
return result
|
|
122
|
-
|
|
123
|
-
jobsList = killJobList if right == RIGHT_KILL else deleteJobList
|
|
124
|
-
return S_OK(jobsList)
|
|
125
2
|
|
|
126
3
|
|
|
127
4
|
def _filterJobStateTransition(jobStates, candidateState):
|
|
@@ -175,11 +175,7 @@ def test_logLevelValidator_invalid():
|
|
|
175
175
|
|
|
176
176
|
def test_platformValidator_valid():
|
|
177
177
|
"""Test the platform validator with valid input."""
|
|
178
|
-
|
|
179
|
-
"DIRAC.WorkloadManagementSystem.Utilities.JobModel.getDIRACPlatforms",
|
|
180
|
-
return_value=S_OK(["x86_64-slc6-gcc62-opt"]),
|
|
181
|
-
):
|
|
182
|
-
job = BaseJobDescriptionModel(executable=EXECUTABLE, platform="x86_64-slc6-gcc62-opt")
|
|
178
|
+
job = BaseJobDescriptionModel(executable=EXECUTABLE, platform="x86_64-slc6-gcc62-opt")
|
|
183
179
|
assert job.platform == "x86_64-slc6-gcc62-opt"
|
|
184
180
|
|
|
185
181
|
|
|
@@ -1,138 +1,55 @@
|
|
|
1
|
-
""" This is a test of the parametric job generation tools
|
|
2
|
-
"""
|
|
1
|
+
""" This is a test of the parametric job generation tools"""
|
|
3
2
|
# pylint: disable= missing-docstring
|
|
4
3
|
|
|
5
4
|
import pytest
|
|
6
5
|
|
|
6
|
+
# Test imports from DIRAC to verify backward compatibility
|
|
7
7
|
from DIRAC.WorkloadManagementSystem.Utilities.ParametricJob import generateParametricJobs, getParameterVectorLength
|
|
8
8
|
from DIRAC.Core.Utilities.ClassAd.ClassAdLight import ClassAd
|
|
9
9
|
|
|
10
|
-
TEST_JDL_NO_PARAMETERS = """
|
|
11
|
-
[
|
|
12
|
-
Executable = "my_executable";
|
|
13
|
-
Arguments = "%s";
|
|
14
|
-
JobName = "Test_%n";
|
|
15
|
-
]
|
|
16
|
-
"""
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Executable = "my_executable";
|
|
21
|
-
Arguments = "%s";
|
|
22
|
-
JobName = "Test_%n";
|
|
23
|
-
Parameters = { "a", "b", "c" }
|
|
24
|
-
]
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
TEST_JDL_SIMPLE_BUNCH = """
|
|
28
|
-
[
|
|
29
|
-
Executable = "my_executable";
|
|
30
|
-
Arguments = "%s";
|
|
31
|
-
JobName = "Test_%n";
|
|
32
|
-
Parameters = 3;
|
|
33
|
-
ParameterStart = 5;
|
|
34
|
-
]
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
TEST_JDL_SIMPLE_PROGRESSION = """
|
|
38
|
-
[
|
|
39
|
-
Executable = "my_executable";
|
|
40
|
-
Arguments = "%s";
|
|
41
|
-
JobName = "Test_%n";
|
|
42
|
-
Parameters = 3;
|
|
43
|
-
ParameterStart = 1;
|
|
44
|
-
ParameterStep = 1;
|
|
45
|
-
ParameterFactor = 2;
|
|
46
|
-
]
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
TEST_JDL_MULTI = """
|
|
50
|
-
[
|
|
51
|
-
Executable = "my_executable";
|
|
52
|
-
Arguments = "%(A)s %(B)s";
|
|
53
|
-
JobName = "Test_%n";
|
|
54
|
-
Parameters = 3;
|
|
55
|
-
ParameterStart.A = 1;
|
|
56
|
-
ParameterStep.A = 1;
|
|
57
|
-
ParameterFactor.A = 2;
|
|
58
|
-
Parameters.B = { "a","b","c" };
|
|
59
|
-
]
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
TEST_JDL_MULTI_BAD = """
|
|
63
|
-
[
|
|
64
|
-
Executable = "my_executable";
|
|
65
|
-
Arguments = "%(A)s %(B)s";
|
|
66
|
-
JobName = "Test_%n";
|
|
67
|
-
Parameters = 3;
|
|
68
|
-
ParameterStart.A = 1;
|
|
69
|
-
ParameterStep.A = 1;
|
|
70
|
-
ParameterFactor.A = 2;
|
|
71
|
-
Parameters.B = { "a","b","c","d" };
|
|
72
|
-
]
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@pytest.mark.parametrize(
|
|
77
|
-
"jdl, expectedArguments",
|
|
78
|
-
[
|
|
79
|
-
(TEST_JDL_SIMPLE, ["a", "b", "c"]),
|
|
80
|
-
(TEST_JDL_SIMPLE_BUNCH, ["5", "5", "5"]),
|
|
81
|
-
(TEST_JDL_SIMPLE_PROGRESSION, ["1", "3", "7"]),
|
|
82
|
-
(TEST_JDL_MULTI, ["1 a", "3 b", "7 c"]),
|
|
83
|
-
(TEST_JDL_NO_PARAMETERS, []),
|
|
84
|
-
],
|
|
85
|
-
)
|
|
86
|
-
def test_getParameterVectorLength_successful(jdl: str, expectedArguments: list[str]):
|
|
11
|
+
def test_backward_compatibility_import():
|
|
12
|
+
"""Test that imports from DIRAC still work (backward compatibility)"""
|
|
87
13
|
# Arrange
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
# Act
|
|
91
|
-
result = getParameterVectorLength(jobDescription)
|
|
92
|
-
|
|
93
|
-
# Assert
|
|
94
|
-
assert result["OK"], result["Message"]
|
|
95
|
-
if expectedArguments:
|
|
96
|
-
assert result["Value"] == len(expectedArguments)
|
|
97
|
-
else:
|
|
98
|
-
assert result["Value"] == None
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@pytest.mark.parametrize("jdl", [TEST_JDL_MULTI_BAD])
|
|
102
|
-
def test_getParameterVectorLength_unsuccessful(jdl: str):
|
|
103
|
-
# Arrange
|
|
104
|
-
jobDescription = ClassAd(jdl)
|
|
105
|
-
|
|
106
|
-
# Act
|
|
107
|
-
result = getParameterVectorLength(jobDescription)
|
|
108
|
-
|
|
109
|
-
# Assert
|
|
110
|
-
assert not result["OK"], result["Value"]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@pytest.mark.parametrize(
|
|
114
|
-
"jdl, expectedArguments",
|
|
14
|
+
jdl = """
|
|
115
15
|
[
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
]
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
assert
|
|
131
|
-
assert
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
16
|
+
Executable = "my_executable";
|
|
17
|
+
Arguments = "%s";
|
|
18
|
+
JobName = "Test_%n";
|
|
19
|
+
Parameters = { "a", "b", "c" }
|
|
20
|
+
]
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Act - Test that we can import and use the functions from DIRAC
|
|
24
|
+
jobDescription = ClassAd(jdl)
|
|
25
|
+
vector_result = getParameterVectorLength(jobDescription)
|
|
26
|
+
generate_result = generateParametricJobs(jobDescription)
|
|
27
|
+
|
|
28
|
+
# Assert - Verify functions work correctly
|
|
29
|
+
assert vector_result["OK"]
|
|
30
|
+
assert vector_result["Value"] == 3
|
|
31
|
+
assert generate_result["OK"]
|
|
32
|
+
assert len(generate_result["Value"]) == 3
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Import and run the comprehensive tests from DIRACCommon to avoid duplication
|
|
36
|
+
# This ensures the DIRAC re-exports work with the full test suite
|
|
37
|
+
try:
|
|
38
|
+
from DIRACCommon.tests.WorkloadManagementSystem.Utilities.test_ParametricJob import (
|
|
39
|
+
test_getParameterVectorLength_successful,
|
|
40
|
+
test_getParameterVectorLength_unsuccessful,
|
|
41
|
+
test_generateParametricJobs,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Re-export the DIRACCommon tests so they run as part of DIRAC test suite
|
|
45
|
+
# This validates that the backward compatibility imports work correctly
|
|
46
|
+
__all__ = [
|
|
47
|
+
"test_backward_compatibility_import",
|
|
48
|
+
"test_getParameterVectorLength_successful",
|
|
49
|
+
"test_getParameterVectorLength_unsuccessful",
|
|
50
|
+
"test_generateParametricJobs",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
except ImportError:
|
|
54
|
+
# If DIRACCommon tests can't be imported, just run the backward compatibility test
|
|
55
|
+
__all__ = ["test_backward_compatibility_import"]
|
DIRAC/__init__.py
CHANGED
|
@@ -1,59 +1,60 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
2
|
+
DIRAC - Distributed Infrastructure with Remote Agent Control
|
|
3
|
+
|
|
4
|
+
The distributed data production and analysis system of LHCb and other VOs.
|
|
5
|
+
|
|
6
|
+
DIRAC is a software framework for distributed computing which
|
|
7
|
+
allows to integrate various computing resources in a single
|
|
8
|
+
system. At the same time it integrates all kinds of computing
|
|
9
|
+
activities like Monte Carlo simulations, data processing, or
|
|
10
|
+
final user analysis.
|
|
11
|
+
|
|
12
|
+
It is build as number of cooperating systems:
|
|
13
|
+
- Accounting
|
|
14
|
+
- Configuration
|
|
15
|
+
- Core
|
|
16
|
+
- Base
|
|
17
|
+
- Security
|
|
18
|
+
- Utilities
|
|
19
|
+
- Workflow
|
|
20
|
+
- Framework
|
|
21
|
+
- RequestManagement
|
|
22
|
+
- Resources
|
|
23
|
+
- Transformation
|
|
24
|
+
|
|
25
|
+
Which are used by other system providing functionality to
|
|
26
|
+
the end user:
|
|
27
|
+
- DataManagement
|
|
28
|
+
- Interfaces
|
|
29
|
+
- ResourceStatus
|
|
30
|
+
- StorageManagement
|
|
31
|
+
- WorkloadManagement
|
|
32
|
+
|
|
33
|
+
It defines the following data members:
|
|
34
|
+
- version: DIRAC version string
|
|
35
|
+
|
|
36
|
+
- errorMail: mail address for important errors
|
|
37
|
+
- alarmMail: mail address for important alarms
|
|
38
|
+
|
|
39
|
+
It loads Modules from :
|
|
40
|
+
- DIRAC.Core.Utililies
|
|
41
|
+
|
|
42
|
+
It loads:
|
|
43
|
+
- S_OK: OK return structure
|
|
44
|
+
- S_ERROR: ERROR return structure
|
|
45
|
+
- gLogger: global Logger object
|
|
46
|
+
- gConfig: global Config object
|
|
47
|
+
|
|
48
|
+
It defines the following functions:
|
|
49
|
+
- abort: aborts execution
|
|
50
|
+
- exit: finish execution using callbacks
|
|
51
|
+
- siteName: returns DIRAC name for current site
|
|
52
|
+
|
|
53
|
+
- getPlatform(): DIRAC platform string for current host
|
|
54
|
+
- getPlatformTuple(): DIRAC platform tuple for current host
|
|
55
55
|
|
|
56
56
|
"""
|
|
57
|
+
|
|
57
58
|
import importlib.metadata
|
|
58
59
|
import os
|
|
59
60
|
import re
|
|
@@ -237,7 +238,7 @@ def initialize(
|
|
|
237
238
|
log_level = getattr(LogLevel, gLogger.getLevel())
|
|
238
239
|
gLogger.setLevel(LogLevel.ALWAYS)
|
|
239
240
|
try:
|
|
240
|
-
returnValueOrRaise(localCfg.initialize())
|
|
241
|
+
returnValueOrRaise(localCfg.initialize(requireSuccessfulSync=require_auth))
|
|
241
242
|
finally:
|
|
242
243
|
# Restore the pre-existing log level
|
|
243
244
|
gLogger.setLevel(log_level)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DIRAC
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.0a67
|
|
4
4
|
Summary: DIRAC is an interware, meaning a software framework for distributed computing.
|
|
5
5
|
Home-page: https://github.com/DIRACGrid/DIRAC/
|
|
6
6
|
License: GPL-3.0-only
|
|
@@ -19,6 +19,7 @@ Requires-Dist: cachetools
|
|
|
19
19
|
Requires-Dist: certifi
|
|
20
20
|
Requires-Dist: cwltool
|
|
21
21
|
Requires-Dist: diraccfg
|
|
22
|
+
Requires-Dist: DIRACCommon==v9.0.0a67
|
|
22
23
|
Requires-Dist: diracx-client>=v0.0.1a18
|
|
23
24
|
Requires-Dist: diracx-core>=v0.0.1a18
|
|
24
25
|
Requires-Dist: db12
|