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,260 +1,17 @@
1
- """
2
- DIRAC TimeUtilities module
3
- Support for basic Date and Time operations
4
- based on system datetime module.
5
-
6
- It provides common interface to UTC timestamps,
7
- converter to string types and back.
8
-
9
- Useful timedelta constant are also provided to
10
- define time intervals.
11
-
12
- Notice: datetime.timedelta objects allow multiplication and division by interger
13
- but not by float. Thus:
14
-
15
- - DIRAC.TimeUtilities.second * 1.5 is not allowed
16
- - DIRAC.TimeUtilities.second * 3 / 2 is allowed
1
+ """Backward compatibility wrapper - moved to DIRACCommon
17
2
 
18
- An timeInterval class provides a method to check
19
- if a give datetime is in the defined interval.
3
+ This module has been moved to DIRACCommon.Core.Utilities.TimeUtilities to avoid
4
+ circular dependencies and allow DiracX to use these utilities without
5
+ triggering DIRAC's global state initialization.
20
6
 
7
+ All exports are maintained for backward compatibility.
21
8
  """
22
- import datetime
23
- import sys
24
- import time
25
-
26
- from DIRAC import gLogger
27
-
28
- # Some useful constants for time operations
29
- microsecond = datetime.timedelta(microseconds=1)
30
- second = datetime.timedelta(seconds=1)
31
- minute = datetime.timedelta(minutes=1)
32
- hour = datetime.timedelta(hours=1)
33
- day = datetime.timedelta(days=1)
34
- week = datetime.timedelta(days=7)
35
-
36
-
37
- def timeThis(method):
38
- """Function to be used as a decorator for timing other functions/methods"""
39
-
40
- def timed(*args, **kw):
41
- """What actually times"""
42
- ts = time.time()
43
- result = method(*args, **kw)
44
- if sys.stdout.isatty():
45
- return result
46
- te = time.time()
47
-
48
- pre = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC ")
49
-
50
- try:
51
- pre += args[0].log.getName() + "/" + args[0].log.getSubName() + " TIME: " + args[0].transString
52
- except AttributeError:
53
- try:
54
- pre += args[0].log.getName() + " TIME: " + args[0].transString
55
- except AttributeError:
56
- try:
57
- pre += args[0].log.getName() + "/" + args[0].log.getSubName() + " TIME: "
58
- except AttributeError:
59
- pre += "TIME: "
60
- except IndexError:
61
- pre += "TIME: "
62
-
63
- argsLen = ""
64
- if args:
65
- try:
66
- if isinstance(args[1], (list, dict)):
67
- argsLen = f"arguments len: {len(args[1])}"
68
- except IndexError:
69
- if kw:
70
- try:
71
- if isinstance(list(list(kw.items())[0])[1], (list, dict)):
72
- argsLen = f"arguments len: {len(list(list(kw.items())[0])[1])}"
73
- except IndexError:
74
- argsLen = ""
75
-
76
- gLogger.info(f"{pre} Exec time ===> function {method.__name__!r} {argsLen} -> {te - ts:2.2f} sec")
77
- return result
78
-
79
- return timed
80
-
81
-
82
- def toEpoch(dateTimeObject=None):
83
- """
84
- Get seconds since epoch. Accepts datetime or date objects
85
- """
86
- return toEpochMilliSeconds(dateTimeObject) // 1000
9
+ from functools import partial
87
10
 
11
+ # Re-export everything from DIRACCommon for backward compatibility
12
+ from DIRACCommon.Core.Utilities.TimeUtilities import * # noqa: F401, F403
88
13
 
89
- def toEpochMilliSeconds(dateTimeObject=None):
90
- """
91
- Get milliseconds since epoch
92
- """
93
- if dateTimeObject is None:
94
- dateTimeObject = datetime.datetime.utcnow()
95
- if dateTimeObject.resolution == datetime.timedelta(days=1):
96
- # Add time information corresponding to midnight UTC if it's a datetime.date
97
- dateTimeObject = datetime.datetime.combine(
98
- dateTimeObject, datetime.time.min.replace(tzinfo=datetime.timezone.utc)
99
- )
100
- posixTime = dateTimeObject.replace(tzinfo=datetime.timezone.utc).timestamp()
101
- return int(posixTime * 1000)
102
-
103
-
104
- def fromEpoch(epoch):
105
- """
106
- Get datetime object from epoch
107
- """
108
- # Check if the timestamp is in milliseconds
109
- if epoch > 10**17: # nanoseconds
110
- epoch /= 1000**3
111
- elif epoch > 10**14: # microseconds
112
- epoch /= 1000**2
113
- elif epoch > 10**11: # milliseconds
114
- epoch /= 1000
115
- return datetime.datetime.utcfromtimestamp(epoch)
116
-
117
-
118
- def toString(myDate=None):
119
- """
120
- Convert to String
121
- if argument type is neither _dateTimeType, _dateType, nor _timeType
122
- the current dateTime converted to String is returned instead
123
-
124
- Notice: datetime.timedelta are converted to strings using the format:
125
- [day] days [hour]:[min]:[sec]:[microsec]
126
- where hour, min, sec, microsec are always positive integers,
127
- and day carries the sign.
128
- To keep internal consistency we are using:
129
- [hour]:[min]:[sec]:[microsec]
130
- where min, sec, microsec are always positive integers and hour carries the sign.
131
- """
132
- if isinstance(myDate, datetime.date):
133
- return str(myDate)
134
-
135
- elif isinstance(myDate, datetime.time):
136
- return "%02d:%02d:%02d.%06d" % (
137
- myDate.days * 24 + myDate.seconds / 3600,
138
- myDate.seconds % 3600 / 60,
139
- myDate.seconds % 60,
140
- myDate.microseconds,
141
- )
142
- else:
143
- return toString(datetime.datetime.utcnow())
144
-
145
-
146
- def fromString(myDate=None):
147
- """
148
- Convert date/time/datetime String back to appropriated objects
149
-
150
- The format of the string it is assume to be that returned by toString method.
151
- See notice on toString method
152
- On Error, return None
153
-
154
- :param myDate: the date string to be converted
155
- :type myDate: str or datetime.datetime
156
- """
157
- if isinstance(myDate, datetime.datetime):
158
- return myDate
159
- if isinstance(myDate, str):
160
- if myDate.find(" ") > 0:
161
- dateTimeTuple = myDate.split(" ")
162
- dateTuple = dateTimeTuple[0].split("-")
163
- try:
164
- return datetime.datetime(year=dateTuple[0], month=dateTuple[1], day=dateTuple[2]) + fromString(
165
- dateTimeTuple[1]
166
- )
167
- # return datetime.datetime.utcnow().combine( fromString( dateTimeTuple[0] ),
168
- # fromString( dateTimeTuple[1] ) )
169
- except Exception:
170
- try:
171
- return datetime.datetime(
172
- year=int(dateTuple[0]), month=int(dateTuple[1]), day=int(dateTuple[2])
173
- ) + fromString(dateTimeTuple[1])
174
- except ValueError:
175
- return None
176
- # return datetime.datetime.utcnow().combine( fromString( dateTimeTuple[0] ),
177
- # fromString( dateTimeTuple[1] ) )
178
- elif myDate.find(":") > 0:
179
- timeTuple = myDate.replace(".", ":").split(":")
180
- try:
181
- if len(timeTuple) == 4:
182
- return datetime.timedelta(
183
- hours=int(timeTuple[0]),
184
- minutes=int(timeTuple[1]),
185
- seconds=int(timeTuple[2]),
186
- microseconds=int(timeTuple[3]),
187
- )
188
- elif len(timeTuple) == 3:
189
- try:
190
- return datetime.timedelta(
191
- hours=int(timeTuple[0]),
192
- minutes=int(timeTuple[1]),
193
- seconds=int(timeTuple[2]),
194
- microseconds=0,
195
- )
196
- except ValueError:
197
- return None
198
- else:
199
- return None
200
- except Exception:
201
- return None
202
- elif myDate.find("-") > 0:
203
- dateTuple = myDate.split("-")
204
- try:
205
- return datetime.date(int(dateTuple[0]), int(dateTuple[1]), int(dateTuple[2]))
206
- except Exception:
207
- return None
208
-
209
- return None
210
-
211
-
212
- class timeInterval:
213
- """
214
- Simple class to define a timeInterval object able to check if a given
215
- dateTime is inside
216
- """
217
-
218
- def __init__(self, initialDateTime, intervalTimeDelta):
219
- """
220
- Initialization method, it requires the initial dateTime and the
221
- timedelta that define the limits.
222
- The upper limit is not included thus it is [begin,end)
223
- If not properly initialized an error flag is set, and subsequent calls
224
- to any method will return None
225
- """
226
- if not isinstance(initialDateTime, datetime.datetime) or not isinstance(intervalTimeDelta, datetime.timedelta):
227
- self.__error = True
228
- return None
229
- self.__error = False
230
- if intervalTimeDelta.days < 0:
231
- self.__startDateTime = initialDateTime + intervalTimeDelta
232
- self.__endDateTime = initialDateTime
233
- else:
234
- self.__startDateTime = initialDateTime
235
- self.__endDateTime = initialDateTime + intervalTimeDelta
236
-
237
- def includes(self, myDateTime):
238
- """ """
239
- if self.__error:
240
- return None
241
- if not isinstance(myDateTime, datetime.datetime):
242
- return None
243
- if myDateTime < self.__startDateTime:
244
- return False
245
- if myDateTime >= self.__endDateTime:
246
- return False
247
- return True
248
-
249
-
250
- def queryTime(f):
251
- """Decorator to measure the function call time"""
14
+ from DIRAC import gLogger
252
15
 
253
- def measureQueryTime(*args, **kwargs):
254
- start = time.time()
255
- result = f(*args, **kwargs)
256
- if result["OK"] and "QueryTime" not in result:
257
- result["QueryTime"] = time.time() - start
258
- return result
259
16
 
260
- return measureQueryTime
17
+ timeThis = partial(timeThis, logger_info=gLogger.info)
@@ -19,9 +19,6 @@ def jdl_monkey_business(monkeypatch):
19
19
  monkeypatch.setattr("DIRAC.Core.Base.API.getSites", lambda: S_OK(["LCG.IN2P3.fr"]))
20
20
  monkeypatch.setattr("DIRAC.WorkloadManagementSystem.Utilities.JobModel.getSites", lambda: S_OK(["LCG.IN2P3.fr"]))
21
21
  monkeypatch.setattr("DIRAC.Interfaces.API.Job.getDIRACPlatforms", lambda: S_OK("x86_64-slc6-gcc49-opt"))
22
- monkeypatch.setattr(
23
- "DIRAC.WorkloadManagementSystem.Utilities.JobModel.getDIRACPlatforms", lambda: S_OK("x86_64-slc6-gcc49-opt")
24
- )
25
22
  yield
26
23
 
27
24
 
@@ -25,7 +25,7 @@ def main():
25
25
  localCfg.setConfigurationForAgent(agentName)
26
26
  localCfg.addDefaultEntry("/DIRAC/Security/UseServerCertificate", "yes")
27
27
  localCfg.addDefaultEntry("LogColor", True)
28
- resultDict = localCfg.loadUserData()
28
+ resultDict = localCfg.loadUserData(requireSuccessfulSync=True)
29
29
  if not resultDict["OK"]:
30
30
  gLogger.error("There were errors when loading configuration", resultDict["Message"])
31
31
  sys.exit(1)
@@ -16,6 +16,7 @@ from sqlalchemy import (
16
16
  Enum,
17
17
  Float,
18
18
  ForeignKey,
19
+ Index,
19
20
  Integer,
20
21
  MetaData,
21
22
  SmallInteger,
@@ -85,6 +86,7 @@ fts3JobTable = Table(
85
86
  Column("error", String(2048)),
86
87
  Column("status", Enum(*FTS3Job.ALL_STATES), server_default=FTS3Job.INIT_STATE, index=True),
87
88
  Column("assignment", String(255), server_default=None),
89
+ Index("idx_jobs_lastupdate_assignment", "lastUpdate", "assignment"),
88
90
  mysql_engine="InnoDB",
89
91
  )
90
92
 
@@ -110,6 +112,7 @@ fts3OperationTable = Table(
110
112
  Column("error", String(1024)),
111
113
  Column("type", String(255)),
112
114
  Column("assignment", String(255), server_default=None),
115
+ Index("idx_operations_lastupdate_assignment", "lastUpdate", "assignment"),
113
116
  mysql_engine="InnoDB",
114
117
  )
115
118
 
@@ -6,6 +6,7 @@ integration tests (Test_ReqDB.py)
6
6
  # pylint: disable=invalid-name,wrong-import-position
7
7
  import time
8
8
 
9
+ import pytest
9
10
 
10
11
  from DIRAC.RequestManagementSystem.Client.Request import Request
11
12
  from DIRAC.RequestManagementSystem.Client.Operation import Operation
@@ -42,6 +43,7 @@ def test_stress(reqDB):
42
43
  assert delete["OK"], delete
43
44
 
44
45
 
46
+ @pytest.mark.slow
45
47
  def test_stressBulk(reqDB):
46
48
  """stress test bulk"""
47
49
 
@@ -151,7 +151,7 @@ class RucioFileCatalogClient(FileCatalogClientBase):
151
151
  self.authHost = options.get("AuthHost", None)
152
152
  self.caCertPath = Locations.getCAsLocation()
153
153
  try:
154
- sLog.info(f"Logging in with a proxy located at: {self.proxyPath}")
154
+ sLog.debug(f"Logging in with a proxy located at: {self.proxyPath}")
155
155
  sLog.debug("account: ", self.username)
156
156
  sLog.debug("rucio host: ", self.rucioHost)
157
157
  sLog.debug("auth host: ", self.authHost)
@@ -25,7 +25,7 @@ while True:
25
25
  if os.path.isfile(stopFile):
26
26
  os.remove(stopFile)
27
27
  break
28
- if (time.time() - start) > 30:
28
+ if (time.time() - start) > 5:
29
29
  break
30
30
  print("End job", jobNumber, time.time())
31
31
  """
@@ -262,6 +262,7 @@ def test_executeJob_wholeNode8(createAndDelete):
262
262
  assert "Not enough processors" in submissionResult["Message"]
263
263
 
264
264
 
265
+ @pytest.mark.slow
265
266
  def test_executeJob_submitAndStop(createAndDelete):
266
267
  time.sleep(0.5)
267
268
 
@@ -37,7 +37,7 @@ from DIRAC.WorkloadManagementSystem.Service.JobPolicy import (
37
37
  RIGHT_DELETE,
38
38
  RIGHT_KILL,
39
39
  )
40
- from DIRAC.WorkloadManagementSystem.Utilities.jobAdministration import kill_delete_jobs
40
+ from DIRAC.WorkloadManagementSystem.DB.StatusUtils import kill_delete_jobs
41
41
 
42
42
  # # agent's name
43
43
  AGENT_NAME = "Transformation/TransformationCleaningAgent"
@@ -5,6 +5,8 @@ import os
5
5
  import copy
6
6
  import shutil
7
7
 
8
+ import pytest
9
+
8
10
  from unittest.mock import MagicMock as Mock
9
11
 
10
12
  from DIRAC import gLogger
@@ -742,6 +744,7 @@ class FailoverRequestSuccess(ModulesTestCase):
742
744
  class ScriptSuccess(ModulesTestCase):
743
745
  #################################################
744
746
 
747
+ @pytest.mark.slow
745
748
  def test_execute(self):
746
749
  self.script.jobType = "merge"
747
750
  self.script.stepInputData = ["foo", "bar"]
@@ -770,6 +773,7 @@ class ScriptSuccess(ModulesTestCase):
770
773
  class ScriptUnicode(ModulesTestCase):
771
774
  #################################################
772
775
 
776
+ @pytest.mark.slow
773
777
  def test_execute(self):
774
778
  self.script.jobType = "merge"
775
779
  self.script.stepInputData = ["foo", "bar"]
@@ -799,6 +803,7 @@ class ScriptUnicode(ModulesTestCase):
799
803
  class ScriptFailure(ModulesTestCase):
800
804
  #################################################
801
805
 
806
+ @pytest.mark.slow
802
807
  def test_execute(self):
803
808
  self.script.jobType = "merge"
804
809
  self.script.stepInputData = ["foo", "bar"]
@@ -39,7 +39,7 @@ from DIRAC.WorkloadManagementSystem.Client.WMSClient import WMSClient
39
39
  from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
40
40
  from DIRAC.WorkloadManagementSystem.DB.SandboxMetadataDB import SandboxMetadataDB
41
41
  from DIRAC.WorkloadManagementSystem.Service.JobPolicy import RIGHT_DELETE
42
- from DIRAC.WorkloadManagementSystem.Utilities.jobAdministration import kill_delete_jobs
42
+ from DIRAC.WorkloadManagementSystem.DB.StatusUtils import kill_delete_jobs
43
43
  from DIRAC.WorkloadManagementSystem.Utilities.JobParameters import getJobParameters
44
44
 
45
45
 
@@ -23,7 +23,7 @@ from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
23
23
  from DIRAC.WorkloadManagementSystem.DB.JobLoggingDB import JobLoggingDB
24
24
  from DIRAC.WorkloadManagementSystem.DB.PilotAgentsDB import PilotAgentsDB
25
25
  from DIRAC.WorkloadManagementSystem.Service.JobPolicy import RIGHT_KILL
26
- from DIRAC.WorkloadManagementSystem.Utilities.jobAdministration import kill_delete_jobs
26
+ from DIRAC.WorkloadManagementSystem.DB.StatusUtils import kill_delete_jobs
27
27
  from DIRAC.WorkloadManagementSystem.Utilities.JobParameters import getJobParameters
28
28
  from DIRAC.WorkloadManagementSystem.Utilities.Utils import rescheduleJobs
29
29
 
@@ -260,6 +260,7 @@ def test__checkMatcherInfo(mocker, matcherInfo, matcherParams, expectedResult):
260
260
  #############################################################################
261
261
 
262
262
 
263
+ @pytest.mark.slow
263
264
  @pytest.mark.parametrize(
264
265
  "mockGCReply, mockPMReply, expected",
265
266
  [
@@ -308,6 +309,7 @@ def test__setupProxy(mocker, mockGCReply, mockPMReply, expected):
308
309
  assert result["Message"] == expected["Message"]
309
310
 
310
311
 
312
+ @pytest.mark.slow
311
313
  @pytest.mark.parametrize(
312
314
  "mockGCReply, mockPMReply, expected",
313
315
  [
@@ -178,6 +178,7 @@ def jobID():
178
178
  shutil.rmtree(jobID)
179
179
 
180
180
 
181
+ @pytest.mark.slow
181
182
  def test_submitJobWrapper(mocker, jobID):
182
183
  """Test JobAgent._submitJobWrapper()"""
183
184
  mocker.patch("DIRAC.WorkloadManagementSystem.Agent.JobAgent.AgentModule.__init__")