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.
Files changed (47) hide show
  1. DIRAC/ConfigurationSystem/Client/LocalConfiguration.py +11 -8
  2. DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py +1 -1
  3. DIRAC/Core/Security/IAMService.py +4 -3
  4. DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
  5. DIRAC/Core/Utilities/DErrno.py +5 -309
  6. DIRAC/Core/Utilities/JDL.py +1 -195
  7. DIRAC/Core/Utilities/List.py +1 -127
  8. DIRAC/Core/Utilities/ReturnValues.py +7 -252
  9. DIRAC/Core/Utilities/StateMachine.py +12 -178
  10. DIRAC/Core/Utilities/TimeUtilities.py +10 -253
  11. DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
  12. DIRAC/Core/scripts/dirac_agent.py +1 -1
  13. DIRAC/DataManagementSystem/DB/FTS3DB.py +3 -0
  14. DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
  15. DIRAC/Resources/Catalog/RucioFileCatalogClient.py +1 -1
  16. DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
  17. DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +1 -1
  18. DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
  19. DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +1 -1
  20. DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +1 -1
  21. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +2 -0
  22. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
  23. DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
  24. DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -93
  25. DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
  26. DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
  27. DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
  28. DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +4 -2
  29. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +21 -5
  30. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
  31. DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +1 -1
  32. DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -199
  33. DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +1 -63
  34. DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
  35. DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +0 -123
  36. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -5
  37. DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
  38. DIRAC/__init__.py +55 -54
  39. {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/METADATA +2 -1
  40. {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/RECORD +44 -45
  41. DIRAC/Core/Utilities/test/Test_List.py +0 -150
  42. DIRAC/Core/Utilities/test/Test_Time.py +0 -88
  43. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobAdministration.py +0 -28
  44. {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/WHEEL +0 -0
  45. {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/entry_points.txt +0 -0
  46. {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/licenses/LICENSE +0 -0
  47. {dirac-9.0.0a64.dist-info → dirac-9.0.0a67.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
- """ This is the guy that parses and interprets the local configuration options.
2
- """
1
+ """This is the guy that parses and interprets the local configuration options."""
2
+
3
3
  import re
4
4
  import os
5
5
  import sys
@@ -286,16 +286,18 @@ class LocalConfiguration:
286
286
  gLogger.exception()
287
287
  return S_ERROR(str(e))
288
288
 
289
- def initialize(self, *, returnErrors=False):
289
+ def initialize(self, *, returnErrors=False, requireSuccessfulSync=False):
290
290
  """Entrypoint used by :py:class:`DIRAC.initialize`
291
291
 
292
+ :param requireSuccessfulSync: fails if syncing with the remote did not work
293
+
292
294
  TODO: This is currently a hack that returns a list of errors for so it
293
295
  can be used by ``__addUserDataToConfiguration``. This entire module
294
296
  should be refactored and simplified with ``Script.parseCommandLine``.
295
297
  """
296
298
  errorsList = self.__loadCFGFiles()
297
299
  if gConfigurationData.getServers():
298
- retVal = self.syncRemoteConfiguration()
300
+ retVal = self.syncRemoteConfiguration(strict=requireSuccessfulSync)
299
301
  if not retVal["OK"]:
300
302
  return retVal
301
303
  else:
@@ -321,7 +323,7 @@ class LocalConfiguration:
321
323
  gLogger.showHeaders(True)
322
324
  gLogger.enableLogsFromExternalLibs()
323
325
 
324
- def loadUserData(self):
326
+ def loadUserData(self, requireSuccessfulSync=False):
325
327
  """
326
328
  This is the magic method that reads the command line and processes it
327
329
  It is used by the Script Base class and the dirac-service and dirac-agent scripts
@@ -329,6 +331,7 @@ class LocalConfiguration:
329
331
  - any additional switches to be processed
330
332
  - mandatory and default configuration configuration options must be defined.
331
333
 
334
+ :param requireSuccessfulSync: if True, will fail if the sync with remote server failed
332
335
  """
333
336
  if self.initialized:
334
337
  return S_OK()
@@ -336,7 +339,7 @@ class LocalConfiguration:
336
339
  try:
337
340
  if not self.isParsed:
338
341
  self.__parseCommandLine() # Parse command line
339
- retVal = self.__addUserDataToConfiguration()
342
+ retVal = self.__addUserDataToConfiguration(requireSuccessfulSync=requireSuccessfulSync)
340
343
 
341
344
  for optionTuple in self.optionalEntryList:
342
345
  optionPath = self.__getAbsolutePath(optionTuple[0])
@@ -496,8 +499,8 @@ class LocalConfiguration:
496
499
 
497
500
  return errorsList
498
501
 
499
- def __addUserDataToConfiguration(self):
500
- retVal = self.initialize(returnErrors=True)
502
+ def __addUserDataToConfiguration(self, requireSuccessfulSync=False):
503
+ retVal = self.initialize(returnErrors=True, requireSuccessfulSync=requireSuccessfulSync)
501
504
  if not retVal["OK"]:
502
505
  return retVal
503
506
  errorsList = retVal["Value"]
@@ -594,7 +594,7 @@ class VOMS2CSSynchronizer:
594
594
 
595
595
  # Try to fill in the DiracX section
596
596
  if self.useIAM:
597
- iam_subs = self.iamSrv.getUsersSub()
597
+ iam_subs = self.iamSrv.getUsersSub(self.vo)
598
598
  diracx_vo_config = {"DiracX": {"CsSync": {"VOs": {self.vo: {"UserSubjects": iam_subs}}}}}
599
599
  iam_sub_cfg = CFG()
600
600
  iam_sub_cfg.loadFromDict(diracx_vo_config)
@@ -144,7 +144,7 @@ class IAMService:
144
144
  result = S_OK({"Users": users, "Errors": errors})
145
145
  return result
146
146
 
147
- def getUsersSub(self) -> dict[str, str]:
147
+ def getUsersSub(self, vo=None) -> dict[str, str]:
148
148
  """
149
149
  Return the mapping based on IAM sub:
150
150
  {nickname : sub}
@@ -152,6 +152,7 @@ class IAMService:
152
152
  iam_users_raw = self._getIamUserDump()
153
153
  diracx_user_section = {}
154
154
  for user_info in iam_users_raw:
155
+ userGroups = [grp["display"] for grp in user_info.get("groups", [])]
155
156
  # The nickname is available in the list of attributes
156
157
  # (if configured so)
157
158
  # in the form {'name': 'nickname', 'value': 'chaen'}
@@ -165,8 +166,8 @@ class IAMService:
165
166
  except (KeyError, IndexError):
166
167
  nickname = user_info["userName"]
167
168
  sub = user_info["id"]
168
-
169
- diracx_user_section[nickname] = sub
169
+ if not vo or vo in userGroups:
170
+ diracx_user_section[nickname] = sub
170
171
  # reorder it
171
172
  diracx_user_section = dict(sorted(diracx_user_section.items()))
172
173
 
@@ -2,294 +2,8 @@
2
2
  Condor ClassAd library.
3
3
  """
4
4
 
5
+ # Import from DIRACCommon for backward compatibility
6
+ from DIRACCommon.Core.Utilities.ClassAd.ClassAdLight import ClassAd
5
7
 
6
- class ClassAd:
7
- def __init__(self, jdl):
8
- """ClassAd constructor from a JDL string"""
9
- self.contents = {}
10
- result = self.__analyse_jdl(jdl)
11
- if result:
12
- self.contents = result
13
-
14
- def __analyse_jdl(self, jdl, index=0):
15
- """Analyse one [] jdl enclosure"""
16
-
17
- jdl = jdl.strip()
18
-
19
- # Strip all the blanks first
20
- # temp = jdl.replace(' ','').replace('\n','')
21
- temp = jdl
22
-
23
- result = {}
24
-
25
- if temp[0] != "[" or temp[-1] != "]":
26
- print("Invalid JDL: it should start with [ and end with ]")
27
- return result
28
-
29
- # Parse the jdl string now
30
- body = temp[1:-1]
31
- index = 0
32
- namemode = 1
33
- valuemode = 0
34
- while index < len(body):
35
- if namemode:
36
- ind = body.find("=", index)
37
- if ind != -1:
38
- name = body[index:ind]
39
- index = ind + 1
40
- valuemode = 1
41
- namemode = 0
42
- else:
43
- break
44
- elif valuemode:
45
- ind1 = body.find("[", index)
46
- ind2 = body.find(";", index)
47
- if ind1 != -1 and ind1 < ind2:
48
- value, newind = self.__find_subjdl(body, ind1)
49
- elif ind1 == -1 and ind2 == -1:
50
- value = body[index:]
51
- newind = len(body)
52
- else:
53
- if index == ind2:
54
- return {}
55
- else:
56
- value = body[index:ind2]
57
- newind = ind2 + 1
58
-
59
- result[name.strip()] = value.strip().replace("\n", "")
60
- index = newind
61
- valuemode = 0
62
- namemode = 1
63
-
64
- return result
65
-
66
- def __find_subjdl(self, body, index):
67
- """Find a full [] enclosure starting from index"""
68
- result = ""
69
- if body[index] != "[":
70
- return (result, 0)
71
-
72
- depth = 0
73
- ind = index
74
- while depth < 10:
75
- ind1 = body.find("]", ind + 1)
76
- ind2 = body.find("[", ind + 1)
77
- if ind2 != -1 and ind2 < ind1:
78
- depth += 1
79
- ind = ind2
80
- else:
81
- if depth > 0:
82
- depth -= 1
83
- ind = ind1
84
- else:
85
- result = body[index : ind1 + 1]
86
- if body[ind1 + 1] == ";":
87
- return (result, ind1 + 2)
88
- return result, 0
89
-
90
- return result, 0
91
-
92
- def insertAttributeInt(self, name, attribute):
93
- """Insert a named integer attribute"""
94
-
95
- self.contents[name] = str(attribute)
96
-
97
- def insertAttributeBool(self, name, attribute):
98
- """Insert a named boolean attribute"""
99
-
100
- if attribute:
101
- self.contents[name] = "true"
102
- else:
103
- self.contents[name] = "false"
104
-
105
- def insertAttributeString(self, name, attribute):
106
- """Insert a named string attribute"""
107
-
108
- self.contents[name] = '"' + str(attribute) + '"'
109
-
110
- def insertAttributeVectorString(self, name, attributelist):
111
- """Insert a named string list attribute"""
112
-
113
- tmp = ['"' + x + '"' for x in attributelist]
114
- tmpstr = ",".join(tmp)
115
- self.contents[name] = "{" + tmpstr + "}"
116
-
117
- def insertAttributeVectorInt(self, name, attributelist):
118
- """Insert a named string list attribute"""
119
-
120
- tmp = [str(x) for x in attributelist]
121
- tmpstr = ",".join(tmp)
122
- self.contents[name] = "{" + tmpstr + "}"
123
-
124
- def insertAttributeVectorStringList(self, name, attributelist):
125
- """Insert a named list of string lists"""
126
-
127
- listOfLists = []
128
- for stringList in attributelist:
129
- # tmp = map ( lambda x : '"' + x + '"', stringList )
130
- tmpstr = ",".join(stringList)
131
- listOfLists.append("{" + tmpstr + "}")
132
- self.contents[name] = "{" + ",".join(listOfLists) + "}"
133
-
134
- def lookupAttribute(self, name):
135
- """Check the presence of the given attribute"""
136
-
137
- return name in self.contents
138
-
139
- def set_expression(self, name, attribute):
140
- """Insert a named expression attribute"""
141
-
142
- self.contents[name] = str(attribute)
143
-
144
- def get_expression(self, name):
145
- """Get expression corresponding to a named attribute"""
146
-
147
- if name in self.contents:
148
- if isinstance(self.contents[name], int):
149
- return str(self.contents[name])
150
- return self.contents[name]
151
- return ""
152
-
153
- def isAttributeList(self, name):
154
- """Check if the given attribute is of the List type"""
155
- attribute = self.get_expression(name).strip()
156
- return attribute.startswith("{")
157
-
158
- def getListFromExpression(self, name):
159
- """Get a list of strings from a given expression"""
160
-
161
- tempString = self.get_expression(name).strip()
162
- listMode = False
163
- if tempString.startswith("{"):
164
- tempString = tempString[1:-1]
165
- listMode = True
166
-
167
- tempString = tempString.replace(" ", "").replace("\n", "")
168
- if tempString.find("{") < 0:
169
- if not listMode:
170
- tempString = tempString.replace('"', "")
171
- if not tempString:
172
- return []
173
- return tempString.split(",")
174
-
175
- resultList = []
176
- while tempString:
177
- if tempString.find("{") == 0:
178
- end = tempString.find("}")
179
- resultList.append(tempString[: end + 1])
180
- tempString = tempString[end + 1 :]
181
- if tempString.startswith(","):
182
- tempString = tempString[1:]
183
- elif tempString.find('"') == 0:
184
- end = tempString[1:].find('"')
185
- resultList.append(tempString[1 : end + 1])
186
- tempString = tempString[end + 2 :]
187
- if tempString.startswith(","):
188
- tempString = tempString[1:]
189
- else:
190
- end = tempString.find(",")
191
- if end < 0:
192
- resultList.append(tempString.replace('"', "").replace(" ", ""))
193
- break
194
- else:
195
- resultList.append(tempString[:end].replace('"', "").replace(" ", ""))
196
- tempString = tempString[end + 1 :]
197
-
198
- return resultList
199
-
200
- def getDictionaryFromSubJDL(self, name):
201
- """Get a dictionary of the JDL attributes from a subsection"""
202
-
203
- tempList = self.get_expression(name)[1:-1]
204
- resDict = {}
205
- for item in tempList.split(";"):
206
- if len(item.split("=")) == 2:
207
- resDict[item.split("=")[0].strip()] = item.split("=")[1].strip().replace('"', "")
208
- else:
209
- return {}
210
-
211
- return resDict
212
-
213
- def deleteAttribute(self, name):
214
- """Delete a named attribute"""
215
-
216
- if name in self.contents:
217
- del self.contents[name]
218
- return 1
219
- return 0
220
-
221
- def isOK(self):
222
- """Check the JDL validity - to be defined"""
223
-
224
- if self.contents:
225
- return 1
226
- return 0
227
-
228
- def asJDL(self):
229
- """Convert the JDL description into a string"""
230
-
231
- result = []
232
- for name, value in sorted(self.contents.items()):
233
- if value[0:1] == "{":
234
- result += [4 * " " + name + " = \n"]
235
- result += [8 * " " + "{\n"]
236
- strings = value[1:-1].split(",")
237
- for st in strings:
238
- result += [12 * " " + st.strip() + ",\n"]
239
- result[-1] = result[-1][:-2]
240
- result += ["\n" + 8 * " " + "};\n"]
241
- elif value[0:1] == "[":
242
- tempad = ClassAd(value)
243
- tempjdl = tempad.asJDL() + ";"
244
- lines = tempjdl.split("\n")
245
- result += [4 * " " + name + " = \n"]
246
- for line in lines:
247
- result += [8 * " " + line + "\n"]
248
-
249
- else:
250
- result += [4 * " " + name + " = " + str(value) + ";\n"]
251
- if result:
252
- result[-1] = result[-1][:-1]
253
- return "[ \n" + "".join(result) + "\n]"
254
-
255
- def getAttributeString(self, name):
256
- """Get String type attribute value"""
257
- value = ""
258
- if self.lookupAttribute(name):
259
- value = self.get_expression(name).replace('"', "")
260
- return value
261
-
262
- def getAttributeInt(self, name):
263
- """Get Integer type attribute value"""
264
- value = None
265
- if self.lookupAttribute(name):
266
- try:
267
- value = int(self.get_expression(name).replace('"', ""))
268
- except Exception:
269
- value = None
270
- return value
271
-
272
- def getAttributeBool(self, name):
273
- """Get Boolean type attribute value"""
274
- if not self.lookupAttribute(name):
275
- return False
276
-
277
- value = self.get_expression(name).replace('"', "")
278
- return value.lower() == "true"
279
-
280
- def getAttributeFloat(self, name):
281
- """Get Float type attribute value"""
282
- value = None
283
- if self.lookupAttribute(name):
284
- try:
285
- value = float(self.get_expression(name).replace('"', ""))
286
- except Exception:
287
- value = None
288
- return value
289
-
290
- def getAttributes(self) -> list[str]:
291
- """Get the list of all the attribute names
292
-
293
- :return: list of names as strings
294
- """
295
- return list(self.contents)
8
+ # Re-export for backward compatibility
9
+ __all__ = ["ClassAd"]