DIRAC 9.0.0a42__py3-none-any.whl → 9.0.7__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 (236) hide show
  1. DIRAC/AccountingSystem/Client/AccountingCLI.py +0 -140
  2. DIRAC/AccountingSystem/Client/DataStoreClient.py +0 -13
  3. DIRAC/AccountingSystem/Client/Types/BaseAccountingType.py +0 -7
  4. DIRAC/AccountingSystem/ConfigTemplate.cfg +0 -5
  5. DIRAC/AccountingSystem/Service/DataStoreHandler.py +0 -72
  6. DIRAC/ConfigurationSystem/Client/Helpers/CSGlobals.py +0 -9
  7. DIRAC/ConfigurationSystem/Client/Helpers/Registry.py +38 -26
  8. DIRAC/ConfigurationSystem/Client/Helpers/Resources.py +11 -43
  9. DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py +0 -16
  10. DIRAC/ConfigurationSystem/Client/LocalConfiguration.py +14 -8
  11. DIRAC/ConfigurationSystem/Client/PathFinder.py +47 -8
  12. DIRAC/ConfigurationSystem/Client/SyncPlugins/CERNLDAPSyncPlugin.py +4 -1
  13. DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py +32 -19
  14. DIRAC/ConfigurationSystem/Client/test/Test_PathFinder.py +41 -1
  15. DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
  16. DIRAC/Core/Base/API.py +4 -7
  17. DIRAC/Core/Base/SQLAlchemyDB.py +1 -0
  18. DIRAC/Core/DISET/ServiceReactor.py +11 -3
  19. DIRAC/Core/DISET/private/BaseClient.py +1 -2
  20. DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
  21. DIRAC/Core/DISET/private/Transports/SSL/M2Utils.py +3 -1
  22. DIRAC/Core/LCG/GOCDBClient.py +5 -7
  23. DIRAC/Core/Security/DiracX.py +31 -17
  24. DIRAC/Core/Security/IAMService.py +5 -10
  25. DIRAC/Core/Security/Locations.py +27 -18
  26. DIRAC/Core/Security/ProxyInfo.py +9 -5
  27. DIRAC/Core/Security/VOMSService.py +2 -4
  28. DIRAC/Core/Security/m2crypto/X509Certificate.py +4 -6
  29. DIRAC/Core/Security/m2crypto/asn1_utils.py +17 -5
  30. DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
  31. DIRAC/Core/Tornado/Client/ClientSelector.py +4 -1
  32. DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
  33. DIRAC/Core/Utilities/CGroups2.py +328 -0
  34. DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
  35. DIRAC/Core/Utilities/DErrno.py +5 -309
  36. DIRAC/Core/Utilities/Extensions.py +10 -1
  37. DIRAC/Core/Utilities/File.py +1 -1
  38. DIRAC/Core/Utilities/Graphs/GraphData.py +1 -1
  39. DIRAC/Core/Utilities/Graphs/GraphUtilities.py +6 -1
  40. DIRAC/Core/Utilities/JDL.py +1 -195
  41. DIRAC/Core/Utilities/List.py +1 -124
  42. DIRAC/Core/Utilities/MySQL.py +103 -99
  43. DIRAC/Core/Utilities/Os.py +32 -1
  44. DIRAC/Core/Utilities/Platform.py +2 -107
  45. DIRAC/Core/Utilities/Proxy.py +0 -4
  46. DIRAC/Core/Utilities/ReturnValues.py +7 -252
  47. DIRAC/Core/Utilities/StateMachine.py +12 -178
  48. DIRAC/Core/Utilities/Subprocess.py +35 -14
  49. DIRAC/Core/Utilities/TimeUtilities.py +10 -253
  50. DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
  51. DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
  52. DIRAC/Core/scripts/dirac_agent.py +1 -1
  53. DIRAC/Core/scripts/dirac_apptainer_exec.py +72 -46
  54. DIRAC/Core/scripts/dirac_configure.py +1 -3
  55. DIRAC/Core/scripts/dirac_install_db.py +24 -6
  56. DIRAC/Core/scripts/dirac_platform.py +1 -92
  57. DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
  58. DIRAC/DataManagementSystem/Agent/RequestOperations/RemoveFile.py +7 -6
  59. DIRAC/DataManagementSystem/Client/FTS3Job.py +71 -34
  60. DIRAC/DataManagementSystem/DB/FTS3DB.py +7 -3
  61. DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
  62. DIRAC/DataManagementSystem/DB/FileCatalogDB.sql +9 -9
  63. DIRAC/DataManagementSystem/DB/FileCatalogWithFkAndPsDB.sql +9 -9
  64. DIRAC/DataManagementSystem/Utilities/DMSHelpers.py +6 -2
  65. DIRAC/DataManagementSystem/scripts/dirac_admin_allow_se.py +13 -8
  66. DIRAC/DataManagementSystem/scripts/dirac_admin_ban_se.py +13 -8
  67. DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
  68. DIRAC/DataManagementSystem/scripts/dirac_dms_protocol_matrix.py +0 -1
  69. DIRAC/FrameworkSystem/Client/BundleDeliveryClient.py +2 -7
  70. DIRAC/FrameworkSystem/Client/ComponentInstaller.py +9 -4
  71. DIRAC/FrameworkSystem/Client/ProxyManagerClient.py +5 -2
  72. DIRAC/FrameworkSystem/Client/SystemAdministratorClientCLI.py +11 -6
  73. DIRAC/FrameworkSystem/ConfigTemplate.cfg +2 -0
  74. DIRAC/FrameworkSystem/DB/AuthDB.py +3 -3
  75. DIRAC/FrameworkSystem/DB/InstalledComponentsDB.py +4 -4
  76. DIRAC/FrameworkSystem/DB/ProxyDB.py +11 -3
  77. DIRAC/FrameworkSystem/DB/TokenDB.py +1 -1
  78. DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py +8 -6
  79. DIRAC/FrameworkSystem/Utilities/MonitoringUtilities.py +2 -19
  80. DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
  81. DIRAC/FrameworkSystem/Utilities/diracx.py +36 -14
  82. DIRAC/FrameworkSystem/private/authorization/AuthServer.py +2 -2
  83. DIRAC/FrameworkSystem/scripts/dirac_admin_update_pilot.py +18 -11
  84. DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
  85. DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +7 -8
  86. DIRAC/Interfaces/API/Dirac.py +27 -15
  87. DIRAC/Interfaces/API/DiracAdmin.py +45 -17
  88. DIRAC/Interfaces/API/Job.py +9 -13
  89. DIRAC/Interfaces/scripts/dirac_admin_allow_site.py +12 -18
  90. DIRAC/Interfaces/scripts/dirac_admin_ban_site.py +12 -10
  91. DIRAC/Interfaces/scripts/dirac_admin_get_site_mask.py +4 -13
  92. DIRAC/Interfaces/scripts/dirac_admin_reset_job.py +3 -6
  93. DIRAC/Interfaces/scripts/dirac_wms_job_parameters.py +0 -1
  94. DIRAC/MonitoringSystem/Client/Types/WMSHistory.py +4 -0
  95. DIRAC/MonitoringSystem/Client/WebAppClient.py +26 -0
  96. DIRAC/MonitoringSystem/ConfigTemplate.cfg +9 -0
  97. DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -25
  98. DIRAC/MonitoringSystem/Service/MonitoringHandler.py +0 -33
  99. DIRAC/MonitoringSystem/Service/WebAppHandler.py +599 -0
  100. DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
  101. DIRAC/ProductionSystem/DB/ProductionDB.sql +4 -4
  102. DIRAC/ProductionSystem/scripts/dirac_prod_get.py +2 -2
  103. DIRAC/ProductionSystem/scripts/dirac_prod_get_all.py +2 -2
  104. DIRAC/ProductionSystem/scripts/dirac_prod_get_trans.py +2 -3
  105. DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
  106. DIRAC/RequestManagementSystem/Agent/RequestOperations/ForwardDISET.py +2 -14
  107. DIRAC/RequestManagementSystem/Client/ReqClient.py +66 -13
  108. DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
  109. DIRAC/RequestManagementSystem/DB/RequestDB.py +10 -5
  110. DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
  111. DIRAC/RequestManagementSystem/private/RequestValidator.py +40 -46
  112. DIRAC/ResourceStatusSystem/Client/SiteStatus.py +4 -2
  113. DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
  114. DIRAC/ResourceStatusSystem/DB/ResourceManagementDB.py +8 -8
  115. DIRAC/ResourceStatusSystem/DB/ResourceStatusDB.py +2 -2
  116. DIRAC/ResourceStatusSystem/Utilities/CSHelpers.py +2 -31
  117. DIRAC/ResourceStatusSystem/scripts/dirac_rss_set_status.py +30 -12
  118. DIRAC/Resources/Catalog/RucioFileCatalogClient.py +195 -1
  119. DIRAC/Resources/Catalog/test/Test_RucioFileCatalogClient.py +181 -0
  120. DIRAC/Resources/Computing/AREXComputingElement.py +25 -8
  121. DIRAC/Resources/Computing/BatchSystems/Condor.py +126 -108
  122. DIRAC/Resources/Computing/BatchSystems/SLURM.py +5 -1
  123. DIRAC/Resources/Computing/BatchSystems/test/Test_SLURM.py +46 -0
  124. DIRAC/Resources/Computing/ComputingElement.py +1 -1
  125. DIRAC/Resources/Computing/HTCondorCEComputingElement.py +44 -44
  126. DIRAC/Resources/Computing/InProcessComputingElement.py +4 -2
  127. DIRAC/Resources/Computing/LocalComputingElement.py +1 -18
  128. DIRAC/Resources/Computing/SSHBatchComputingElement.py +1 -17
  129. DIRAC/Resources/Computing/SSHComputingElement.py +1 -18
  130. DIRAC/Resources/Computing/SingularityComputingElement.py +19 -5
  131. DIRAC/Resources/Computing/test/Test_HTCondorCEComputingElement.py +67 -49
  132. DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
  133. DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
  134. DIRAC/Resources/IdProvider/IdProviderFactory.py +11 -3
  135. DIRAC/Resources/MessageQueue/StompMQConnector.py +1 -1
  136. DIRAC/Resources/Storage/GFAL2_StorageBase.py +24 -15
  137. DIRAC/Resources/Storage/OccupancyPlugins/WLCGAccountingHTTPJson.py +1 -3
  138. DIRAC/Resources/Storage/StorageBase.py +4 -2
  139. DIRAC/Resources/Storage/StorageElement.py +6 -7
  140. DIRAC/StorageManagementSystem/DB/StorageManagementDB.sql +2 -2
  141. DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +10 -16
  142. DIRAC/TransformationSystem/Agent/TransformationAgent.py +22 -1
  143. DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +16 -16
  144. DIRAC/TransformationSystem/Client/TaskManager.py +2 -4
  145. DIRAC/TransformationSystem/Client/Transformation.py +6 -7
  146. DIRAC/TransformationSystem/Client/TransformationClient.py +21 -11
  147. DIRAC/TransformationSystem/Client/Utilities.py +9 -0
  148. DIRAC/TransformationSystem/DB/TransformationDB.py +11 -14
  149. DIRAC/TransformationSystem/DB/TransformationDB.sql +9 -9
  150. DIRAC/TransformationSystem/Service/TransformationManagerHandler.py +0 -333
  151. DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
  152. DIRAC/TransformationSystem/Utilities/TransformationInfo.py +7 -5
  153. DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
  154. DIRAC/TransformationSystem/test/Test_TransformationInfo.py +22 -15
  155. DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
  156. DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
  157. DIRAC/WorkloadManagementSystem/Agent/JobAgent.py +38 -26
  158. DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +12 -8
  159. DIRAC/WorkloadManagementSystem/Agent/PilotSyncAgent.py +4 -3
  160. DIRAC/WorkloadManagementSystem/Agent/PushJobAgent.py +13 -13
  161. DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +18 -14
  162. DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +18 -51
  163. DIRAC/WorkloadManagementSystem/Agent/StatesAccountingAgent.py +41 -1
  164. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +45 -4
  165. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobCleaningAgent.py +7 -9
  166. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
  167. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +9 -2
  168. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +4 -5
  169. DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +9 -9
  170. DIRAC/WorkloadManagementSystem/Client/InputDataResolution.py +6 -6
  171. DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py +10 -11
  172. DIRAC/WorkloadManagementSystem/Client/JobReport.py +1 -1
  173. DIRAC/WorkloadManagementSystem/Client/JobState/CachedJobState.py +3 -0
  174. DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
  175. DIRAC/WorkloadManagementSystem/Client/JobState/JobState.py +6 -0
  176. DIRAC/WorkloadManagementSystem/Client/JobStateUpdateClient.py +3 -0
  177. DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -152
  178. DIRAC/WorkloadManagementSystem/Client/PoolXMLSlice.py +12 -19
  179. DIRAC/WorkloadManagementSystem/Client/SandboxStoreClient.py +25 -38
  180. DIRAC/WorkloadManagementSystem/Client/WMSClient.py +2 -3
  181. DIRAC/WorkloadManagementSystem/Client/test/Test_Client_DownloadInputData.py +29 -0
  182. DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +4 -8
  183. DIRAC/WorkloadManagementSystem/DB/JobDB.py +89 -132
  184. DIRAC/WorkloadManagementSystem/DB/JobDB.sql +8 -8
  185. DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
  186. DIRAC/WorkloadManagementSystem/DB/JobLoggingDB.py +19 -6
  187. DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +9 -9
  188. DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py +16 -5
  189. DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.sql +3 -3
  190. DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +44 -82
  191. DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
  192. DIRAC/WorkloadManagementSystem/DB/tests/Test_JobDB.py +1 -1
  193. DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
  194. DIRAC/WorkloadManagementSystem/Executor/JobSanity.py +5 -4
  195. DIRAC/WorkloadManagementSystem/Executor/JobScheduling.py +4 -0
  196. DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py +75 -33
  197. DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +22 -11
  198. DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapperTemplate.py +9 -10
  199. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +60 -10
  200. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
  201. DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +33 -154
  202. DIRAC/WorkloadManagementSystem/Service/JobMonitoringHandler.py +5 -323
  203. DIRAC/WorkloadManagementSystem/Service/JobStateUpdateHandler.py +0 -16
  204. DIRAC/WorkloadManagementSystem/Service/PilotManagerHandler.py +6 -103
  205. DIRAC/WorkloadManagementSystem/Service/SandboxStoreHandler.py +7 -53
  206. DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +16 -79
  207. DIRAC/WorkloadManagementSystem/Service/WMSUtilities.py +4 -18
  208. DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -209
  209. DIRAC/WorkloadManagementSystem/Utilities/JobParameters.py +65 -3
  210. DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +2 -64
  211. DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
  212. DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +73 -7
  213. DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py +41 -11
  214. DIRAC/WorkloadManagementSystem/Utilities/RemoteRunner.py +16 -0
  215. DIRAC/WorkloadManagementSystem/Utilities/Utils.py +36 -1
  216. DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +15 -0
  217. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -15
  218. DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
  219. DIRAC/WorkloadManagementSystem/Utilities/test/Test_PilotWrapper.py +16 -0
  220. DIRAC/WorkloadManagementSystem/scripts/dirac_jobexec.py +7 -2
  221. DIRAC/WorkloadManagementSystem/scripts/dirac_wms_pilot_job_info.py +1 -1
  222. DIRAC/__init__.py +62 -60
  223. DIRAC/tests/Utilities/testJobDefinitions.py +22 -28
  224. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/METADATA +8 -5
  225. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/RECORD +229 -228
  226. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/WHEEL +1 -1
  227. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/entry_points.txt +0 -3
  228. DIRAC/Core/Utilities/test/Test_List.py +0 -150
  229. DIRAC/Core/Utilities/test/Test_Time.py +0 -88
  230. DIRAC/Resources/Computing/PilotBundle.py +0 -70
  231. DIRAC/TransformationSystem/scripts/dirac_transformation_archive.py +0 -30
  232. DIRAC/TransformationSystem/scripts/dirac_transformation_clean.py +0 -30
  233. DIRAC/TransformationSystem/scripts/dirac_transformation_remove_output.py +0 -30
  234. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobManager.py +0 -58
  235. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info/licenses}/LICENSE +0 -0
  236. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/top_level.txt +0 -0
@@ -22,7 +22,6 @@ from DIRAC.Core.DISET.RequestHandler import RequestHandler
22
22
  from DIRAC.Core.Security import Properties
23
23
  from DIRAC.Core.Utilities.File import getGlobbedTotalSize, mkDir
24
24
  from DIRAC.Core.Utilities.ObjectLoader import ObjectLoader
25
- from DIRAC.DataManagementSystem.Service.StorageElementHandler import getDiskSpace
26
25
  from DIRAC.FrameworkSystem.Utilities.diracx import TheImpersonator
27
26
 
28
27
 
@@ -120,7 +119,7 @@ class SandboxStoreHandlerMixin:
120
119
  format=extension,
121
120
  )
122
121
 
123
- with TheImpersonator(credDict) as client:
122
+ with TheImpersonator(credDict, source="SandboxStore") as client:
124
123
  res = client.jobs.initiate_sandbox_upload(sandbox_info)
125
124
 
126
125
  if res.url:
@@ -139,7 +138,7 @@ class SandboxStoreHandlerMixin:
139
138
  gLogger.debug("Sandbox already exists in storage backend", res.pfn)
140
139
 
141
140
  assignTo = {key: [(res.pfn, assignTo[key])] for key in assignTo}
142
- result = self.export_assignSandboxesToEntities(assignTo)
141
+ result = self.sandboxDB.assignSandboxesToEntities(assignTo, credDict["username"], credDict["group"])
143
142
  if not result["OK"]:
144
143
  return result
145
144
  return S_OK(res.pfn)
@@ -153,7 +152,7 @@ class SandboxStoreHandlerMixin:
153
152
  fileHelper.markAsTransferred()
154
153
  sbURL = f"SB:{self.__localSEName}|{sbPath}"
155
154
  assignTo = {key: [(sbURL, assignTo[key])] for key in assignTo}
156
- result = self.export_assignSandboxesToEntities(assignTo)
155
+ result = self.sandboxDB.assignSandboxesToEntities(assignTo, credDict["username"], credDict["group"])
157
156
  if not result["OK"]:
158
157
  return result
159
158
  return S_OK(sbURL)
@@ -210,7 +209,9 @@ class SandboxStoreHandlerMixin:
210
209
 
211
210
  sbURL = f"SB:{self.__localSEName}|{sbPath}"
212
211
  assignTo = {key: [(sbURL, assignTo[key])] for key in assignTo}
213
- if not (result := self.export_assignSandboxesToEntities(assignTo))["OK"]:
212
+ if not (result := self.sandboxDB.assignSandboxesToEntities(assignTo, credDict["username"], credDict["group"]))[
213
+ "OK"
214
+ ]:
214
215
  return result
215
216
  return S_OK(sbURL)
216
217
 
@@ -328,33 +329,6 @@ class SandboxStoreHandlerMixin:
328
329
 
329
330
  return result
330
331
 
331
- ##################
332
- # Assigning sbs to jobs
333
-
334
- types_assignSandboxesToEntities = [dict]
335
-
336
- def export_assignSandboxesToEntities(self, enDict, ownerName="", ownerGroup=""):
337
- """
338
- Assign sandboxes to jobs.
339
- Expects a dict of { entityId : [ ( SB, SBType ), ... ] }
340
- """
341
- credDict = self.getRemoteCredentials()
342
- return self.sandboxDB.assignSandboxesToEntities(
343
- enDict, credDict["username"], credDict["group"], ownerName, ownerGroup
344
- )
345
-
346
- ##################
347
- # Unassign sbs to jobs
348
-
349
- types_unassignEntities = [(list, tuple)]
350
-
351
- def export_unassignEntities(self, entitiesList):
352
- """
353
- Unassign a list of jobs
354
- """
355
- credDict = self.getRemoteCredentials()
356
- return self.sandboxDB.unassignEntities(entitiesList, credDict["username"], credDict["group"])
357
-
358
332
  ##################
359
333
  # Getting assigned sandboxes
360
334
 
@@ -376,26 +350,6 @@ class SandboxStoreHandlerMixin:
376
350
  sbDict[SBType].append(f"SB:{SEName}|{SEPFN}")
377
351
  return S_OK(sbDict)
378
352
 
379
- ##################
380
- # Disk space left management
381
-
382
- types_getFreeDiskSpace = []
383
-
384
- def export_getFreeDiskSpace(self):
385
- """Get the free disk space of the storage element
386
- If no size is specified, terabytes will be used by default.
387
- """
388
-
389
- return getDiskSpace(self.getCSOption("BasePath", "/opt/dirac/storage/sandboxes"))
390
-
391
- types_getTotalDiskSpace = []
392
-
393
- def export_getTotalDiskSpace(self):
394
- """Get the total disk space of the storage element
395
- If no size is specified, terabytes will be used by default.
396
- """
397
- return getDiskSpace(self.getCSOption("BasePath", "/opt/dirac/storage/sandboxes"), total=True)
398
-
399
353
  ##################
400
354
  # Download sandboxes
401
355
 
@@ -415,7 +369,7 @@ class SandboxStoreHandlerMixin:
415
369
  # If the PFN starts with S3, we know it has been uploaded to the
416
370
  # S3 sandbox store, so download it from there before sending it
417
371
  if filePath.startswith("/S3"):
418
- with TheImpersonator(credDict) as client:
372
+ with TheImpersonator(credDict, source="SandboxStore") as client:
419
373
  res = client.jobs.get_sandbox_file(pfn=filePath)
420
374
  r = requests.get(res.url)
421
375
  r.raise_for_status()
@@ -3,7 +3,6 @@ This is a DIRAC WMS administrator interface.
3
3
  """
4
4
  from DIRAC import S_ERROR, S_OK
5
5
  from DIRAC.ConfigurationSystem.Client.Helpers import Registry
6
- from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getSites
7
6
  from DIRAC.Core.DISET.RequestHandler import RequestHandler
8
7
  from DIRAC.Core.Utilities.Decorators import deprecated
9
8
  from DIRAC.Core.Utilities.ObjectLoader import ObjectLoader
@@ -151,86 +150,24 @@ class WMSAdministratorHandlerMixin:
151
150
  :param str jobID: job ID
152
151
  :return: S_OK(dict)/S_ERROR()
153
152
  """
154
- pilotReference = ""
155
- # Get the pilot grid reference first from the job parameters
156
-
157
- credDict = self.getRemoteCredentials()
158
- vo = credDict.get("VO", Registry.getVOForGroup(credDict["group"]))
159
- res = self.elasticJobParametersDB.getJobParameters(int(jobID), vo=vo, paramList=["Pilot_Reference"])
160
- if not res["OK"]:
161
- return res
162
- if res["Value"].get(int(jobID)):
163
- pilotReference = res["Value"][int(jobID)]["Pilot_Reference"]
164
-
165
- if not pilotReference:
166
- res = self.jobDB.getJobParameter(int(jobID), "Pilot_Reference")
167
- if not res["OK"]:
168
- return res
169
- pilotReference = res["Value"]
170
-
171
- if not pilotReference:
172
- # Failed to get the pilot reference, try to look in the attic parameters
173
- res = self.jobDB.getAtticJobParameters(int(jobID), ["Pilot_Reference"])
174
- if res["OK"]:
175
- c = -1
176
- # Get the pilot reference for the last rescheduling cycle
177
- for cycle in res["Value"]:
178
- if cycle > c:
179
- pilotReference = res["Value"][cycle]["Pilot_Reference"]
180
- c = cycle
181
-
182
- if pilotReference:
183
- return self.pilotManager.getPilotOutput(pilotReference)
184
- return S_ERROR("No pilot job reference found")
153
+ result = self.pilotManager.getPilots(jobID)
185
154
 
186
- ##############################################################################
187
- types_getSiteSummaryWeb = [dict, list, int, int]
188
-
189
- @classmethod
190
- def export_getSiteSummaryWeb(cls, selectDict, sortList, startItem, maxItems):
191
- """Get the summary of the jobs running on sites in a generic format
192
-
193
- :param dict selectDict: selectors
194
- :param list sortList: sorting list
195
- :param int startItem: start item number
196
- :param int maxItems: maximum of items
197
-
198
- :return: S_OK(dict)/S_ERROR()
199
- """
200
- return cls.jobDB.getSiteSummaryWeb(selectDict, sortList, startItem, maxItems)
201
-
202
- ##############################################################################
203
- types_getSiteSummarySelectors = []
204
-
205
- @classmethod
206
- def export_getSiteSummarySelectors(cls):
207
- """Get all the distinct selector values for the site summary web portal page
155
+ if not result["OK"]:
156
+ return result
157
+ pilotJobReferences = result["Value"].keys()
208
158
 
209
- :return: S_OK(dict)/S_ERROR()
210
- """
211
- resultDict = {}
212
- statusList = ["Good", "Fair", "Poor", "Bad", "Idle"]
213
- resultDict["Status"] = statusList
214
- maskStatus = ["Active", "Banned", "NoMask", "Reduced"]
215
- resultDict["MaskStatus"] = maskStatus
216
-
217
- res = getSites()
218
- if not res["OK"]:
219
- return res
220
- siteList = res["Value"]
221
-
222
- countryList = []
223
- for site in siteList:
224
- if site.find(".") != -1:
225
- country = site.split(".")[2].lower()
226
- if country not in countryList:
227
- countryList.append(country)
228
- countryList.sort()
229
- resultDict["Country"] = countryList
230
- siteList.sort()
231
- resultDict["Site"] = siteList
232
-
233
- return S_OK(resultDict)
159
+ outputs = {"StdOut": "", "StdErr": ""}
160
+ for pilotRef in pilotJobReferences:
161
+ result = self.pilotManager.getPilotOutput(pilotRef)
162
+ if not result["OK"]:
163
+ stdout = f"Could not retrieve output: {result['Message']}"
164
+ error = f"Could not retrieve error: {result['Message']}"
165
+ else:
166
+ stdout, error = result["Value"]["StdOut"], result["Value"]["StdErr"]
167
+ outputs["StdOut"] += f"# PilotJobReference: {pilotRef}\n\n{stdout}\n"
168
+ outputs["StdErr"] += f"# PilotJobReference: {pilotRef}\n\n{error}\n"
169
+
170
+ return S_OK(outputs)
234
171
 
235
172
 
236
173
  class WMSAdministratorHandler(WMSAdministratorHandlerMixin, RequestHandler):
@@ -121,24 +121,10 @@ def killPilotsInQueues(pilotRefDict):
121
121
  return result
122
122
  ce = result["Value"]
123
123
 
124
- opsH = Operations(vo=vo)
125
-
126
- pilotDN = opsH.getValue("Pilot/GenericPilotDN")
127
- if not pilotDN:
128
- owner = opsH.getValue("Pilot/GenericPilotUser")
129
- res = getDNForUsername(owner)
130
- if not res["OK"]:
131
- return S_ERROR(f"Cannot get the generic pilot DN: {res['Message']}")
132
- pilotDN = res["Value"][0]
133
-
134
- pilotGroup = opsH.getValue("Pilot/GenericPilotGroup")
135
- group = getGroupOption(pilotGroup, "VOMSRole")
136
- ret = gProxyManager.getPilotProxyFromVOMSGroup(pilotDN, group)
137
- if not ret["OK"]:
138
- gLogger.error("Could not get proxy:", f"User '{pilotDN}' Group '{group}' : {ret['Message']}")
139
- return S_ERROR("Failed to get the pilot's owner proxy")
140
- proxy = ret["Value"]
141
- ce.setProxy(proxy)
124
+ pilotDict["VO"] = vo
125
+ result = setPilotCredentials(ce, pilotDict)
126
+ if not result["OK"]:
127
+ return result
142
128
 
143
129
  pilotList = pilotDict["PilotList"]
144
130
  result = ce.killJob(pilotList)
@@ -1,219 +1,38 @@
1
- """ This module contains the JobModel class, which is used to validate the job description """
1
+ from __future__ import annotations
2
2
 
3
- # pylint: disable=no-self-argument, no-self-use, invalid-name, missing-function-docstring
4
-
5
- from collections.abc import Iterable
6
- from typing import Any, Annotated, TypeAlias, Self
7
-
8
- from pydantic import BaseModel, BeforeValidator, model_validator, field_validator, ConfigDict
3
+ from typing import ClassVar
4
+ from pydantic import PrivateAttr
5
+ from DIRACCommon.WorkloadManagementSystem.Utilities.JobModel import * # noqa: F401, F403
9
6
 
10
7
  from DIRAC import gLogger
11
- from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
12
- from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatforms, getSites
13
-
14
-
15
- # HACK: Convert appropriate iterables into sets
16
- def default_set_validator(value):
17
- if value is None:
18
- return set()
19
- elif not isinstance(value, Iterable):
20
- return value
21
- elif isinstance(value, (str, bytes, bytearray)):
22
- return value
23
- else:
24
- return set(value)
25
-
26
-
27
- CoercibleSetStr: TypeAlias = Annotated[set[str], BeforeValidator(default_set_validator)]
28
-
29
-
30
- class BaseJobDescriptionModel(BaseModel):
31
- """Base model for the job description (not parametric)"""
32
-
33
- model_config = ConfigDict(validate_assignment=True)
34
-
35
- arguments: str = ""
36
- bannedSites: CoercibleSetStr = set()
37
- # TODO: This should use a field factory
38
- cpuTime: int = Operations().getValue("JobDescription/DefaultCPUTime", 86400)
39
- executable: str
40
- executionEnvironment: dict = None
41
- gridCE: str = ""
42
- inputSandbox: CoercibleSetStr = set()
43
- inputData: CoercibleSetStr = set()
44
- inputDataPolicy: str = ""
45
- jobConfigArgs: str = ""
46
- jobGroup: str = ""
47
- jobType: str = "User"
48
- jobName: str = "Name"
49
- # TODO: This should be an StrEnum
50
- logLevel: str = "INFO"
51
- # TODO: This can't be None with this type hint
52
- maxNumberOfProcessors: int = None
53
- minNumberOfProcessors: int = 1
54
- outputData: CoercibleSetStr = set()
55
- outputPath: str = ""
56
- outputSandbox: CoercibleSetStr = set()
57
- outputSE: str = ""
58
- platform: str = ""
59
- # TODO: This should use a field factory
60
- priority: int = Operations().getValue("JobDescription/DefaultPriority", 1)
61
- sites: CoercibleSetStr = set()
62
- stderr: str = "std.err"
63
- stdout: str = "std.out"
64
- tags: CoercibleSetStr = set()
65
- extraFields: dict[str, Any] = {}
66
-
67
- @field_validator("cpuTime")
68
- def checkCPUTimeBounds(cls, v):
69
- minCPUTime = Operations().getValue("JobDescription/MinCPUTime", 100)
70
- maxCPUTime = Operations().getValue("JobDescription/MaxCPUTime", 500000)
71
- if not minCPUTime <= v <= maxCPUTime:
72
- raise ValueError(f"cpuTime out of bounds (must be between {minCPUTime} and {maxCPUTime})")
73
- return v
74
-
75
- @field_validator("executable")
76
- def checkExecutableIsNotAnEmptyString(cls, v: str):
77
- if not v:
78
- raise ValueError("executable must not be an empty string")
79
- return v
80
-
81
- @field_validator("jobType")
82
- def checkJobTypeIsAllowed(cls, v: str):
83
- jobTypes = Operations().getValue("JobDescription/AllowedJobTypes", ["User", "Test", "Hospital"])
84
- transformationTypes = Operations().getValue("Transformations/DataProcessing", [])
85
- allowedTypes = jobTypes + transformationTypes
86
- if v not in allowedTypes:
87
- raise ValueError(f"jobType '{v}' is not allowed for this kind of user (must be in {allowedTypes})")
88
- return v
89
-
90
- @field_validator("inputData")
91
- def checkInputDataDoesntContainDoubleSlashes(cls, v):
92
- if v:
93
- for lfn in v:
94
- if lfn.find("//") > -1:
95
- raise ValueError("Input data contains //")
96
- return v
97
-
98
- @field_validator("inputData")
99
- def addLFNPrefixIfStringStartsWithASlash(cls, v: set[str]):
100
- if v:
101
- v = {lfn.strip() for lfn in v if lfn.strip()}
102
- v = {f"LFN:{lfn}" if lfn.startswith("/") else lfn for lfn in v}
103
-
104
- for lfn in v:
105
- if not lfn.startswith("LFN:/"):
106
- raise ValueError("Input data files must start with LFN:/")
107
- return v
108
-
109
- @model_validator(mode="after")
110
- def checkNumberOfInputDataFiles(self) -> Self:
111
- if self.inputData:
112
- maxInputDataFiles = Operations().getValue("JobDescription/MaxInputData", 500)
113
- if self.jobType == "User" and len(self.inputData) >= maxInputDataFiles:
114
- raise ValueError(f"inputData contains too many files (must contain at most {maxInputDataFiles})")
115
- return self
116
-
117
- @field_validator("inputSandbox")
118
- def checkLFNSandboxesAreWellFormated(cls, v: set[str]):
119
- for inputSandbox in v:
120
- if inputSandbox.startswith("LFN:") and not inputSandbox.startswith("LFN:/"):
121
- raise ValueError("LFN files must start by LFN:/")
122
- return v
123
-
124
- @field_validator("logLevel")
125
- def checkLogLevelIsValid(cls, v: str):
126
- v = v.upper()
127
- possibleLogLevels = gLogger.getAllPossibleLevels()
128
- if v not in possibleLogLevels:
129
- raise ValueError(f"Log level {v} not in {possibleLogLevels}")
130
- return v
131
-
132
- @field_validator("minNumberOfProcessors")
133
- def checkMinNumberOfProcessorsBounds(cls, v):
134
- minNumberOfProcessors = Operations().getValue("JobDescription/MinNumberOfProcessors", 1)
135
- maxNumberOfProcessors = Operations().getValue("JobDescription/MaxNumberOfProcessors", 1024)
136
- if not minNumberOfProcessors <= v <= maxNumberOfProcessors:
137
- raise ValueError(
138
- f"minNumberOfProcessors out of bounds (must be between {minNumberOfProcessors} and {maxNumberOfProcessors})"
139
- )
140
- return v
141
-
142
- @field_validator("maxNumberOfProcessors")
143
- def checkMaxNumberOfProcessorsBounds(cls, v):
144
- minNumberOfProcessors = Operations().getValue("JobDescription/MinNumberOfProcessors", 1)
145
- maxNumberOfProcessors = Operations().getValue("JobDescription/MaxNumberOfProcessors", 1024)
146
- if not minNumberOfProcessors <= v <= maxNumberOfProcessors:
147
- raise ValueError(
148
- f"minNumberOfProcessors out of bounds (must be between {minNumberOfProcessors} and {maxNumberOfProcessors})"
149
- )
150
- return v
151
-
152
- @model_validator(mode="after")
153
- def checkThatMaxNumberOfProcessorsIsGreaterThanMinNumberOfProcessors(self) -> Self:
154
- if self.maxNumberOfProcessors:
155
- if self.maxNumberOfProcessors < self.minNumberOfProcessors:
156
- raise ValueError("maxNumberOfProcessors must be greater than minNumberOfProcessors")
157
- return self
158
-
159
- @model_validator(mode="after")
160
- def addTagsDependingOnNumberOfProcessors(self) -> Self:
161
- if self.minNumberOfProcessors == self.maxNumberOfProcessors:
162
- self.tags.add(f"{self.minNumberOfProcessors}Processors")
163
- if self.minNumberOfProcessors > 1:
164
- self.tags.add("MultiProcessor")
165
- return self
166
-
167
- @field_validator("sites")
168
- def checkSites(cls, v: set[str]):
169
- if v:
170
- res = getSites()
171
- if not res["OK"]:
172
- raise ValueError(res["Message"])
173
- invalidSites = v - set(res["Value"]).union({"ANY"})
174
- if invalidSites:
175
- raise ValueError(f"Invalid sites: {' '.join(invalidSites)}")
176
- return v
8
+ from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getSites
177
9
 
178
- @model_validator(mode="after")
179
- def checkThatSitesAndBannedSitesAreNotMutuallyExclusive(self) -> Self:
180
- if self.sites and self.bannedSites:
181
- while self.bannedSites:
182
- self.sites.discard(self.bannedSites.pop())
183
- if not self.sites:
184
- raise ValueError("sites and bannedSites are mutually exclusive")
185
- return self
186
10
 
187
- @field_validator("platform")
188
- def checkPlatform(cls, v: str):
189
- if v:
190
- res = getDIRACPlatforms()
191
- if not res["OK"]:
192
- raise ValueError(res["Message"])
193
- if v not in res["Value"]:
194
- raise ValueError("Invalid platform")
195
- return v
11
+ def _make_model_config(cls=None) -> BaseJobDescriptionModelConfg:
12
+ from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
196
13
 
197
- @field_validator("priority")
198
- def checkPriorityBounds(cls, v):
199
- minPriority = Operations().getValue("JobDescription/MinPriority", 0)
200
- maxPriority = Operations().getValue("JobDescription/MaxPriority", 10)
201
- if not minPriority <= v <= maxPriority:
202
- raise ValueError(f"priority out of bounds (must be between {minPriority} and {maxPriority})")
203
- return v
14
+ ops = Operations()
15
+ allowedJobTypes = ops.getValue("JobDescription/AllowedJobTypes", ["User", "Test", "Hospital"])
16
+ allowedJobTypes += ops.getValue("Transformations/DataProcessing", [])
17
+ return {
18
+ "cpuTime": ops.getValue("JobDescription/DefaultCPUTime", 86400),
19
+ "priority": ops.getValue("JobDescription/DefaultPriority", 1),
20
+ "minCPUTime": ops.getValue("JobDescription/MinCPUTime", 100),
21
+ "maxCPUTime": ops.getValue("JobDescription/MaxCPUTime", 500000),
22
+ "allowedJobTypes": allowedJobTypes,
23
+ "maxInputDataFiles": ops.getValue("JobDescription/MaxInputData", 500),
24
+ "minNumberOfProcessors": ops.getValue("JobDescription/MinNumberOfProcessors", 1),
25
+ "maxNumberOfProcessors": ops.getValue("JobDescription/MaxNumberOfProcessors", 1024),
26
+ "minPriority": ops.getValue("JobDescription/MinPriority", 0),
27
+ "maxPriority": ops.getValue("JobDescription/MaxPriority", 10),
28
+ "possibleLogLevels": gLogger.getAllPossibleLevels(),
29
+ "sites": getSites(),
30
+ }
204
31
 
205
32
 
206
- class JobDescriptionModel(BaseJobDescriptionModel):
207
- """Model for the job description (non parametric job with user credentials, i.e server side)"""
33
+ class BaseJobDescriptionModel(BaseJobDescriptionModel): # noqa: F405 pylint: disable=function-redefined
34
+ _config_builder: ClassVar = _make_model_config
208
35
 
209
- owner: str
210
- ownerGroup: str
211
- vo: str
212
36
 
213
- @model_validator(mode="after")
214
- def checkLFNMatchesREGEX(self) -> Self:
215
- if self.inputData:
216
- for lfn in self.inputData:
217
- if not lfn.startswith(f"LFN:/{self.vo}/"):
218
- raise ValueError(f"Input data not correctly specified (must start with LFN:/{self.vo}/)")
219
- return self
37
+ class JobDescriptionModel(JobDescriptionModel): # noqa: F405 pylint: disable=function-redefined
38
+ _config_builder: ClassVar = _make_model_config
@@ -1,8 +1,8 @@
1
- """ DIRAC Workload Management System utility module to get available memory and processors
2
- """
1
+ """DIRAC Workload Management System utility module to get available memory and processors"""
2
+
3
3
  import multiprocessing
4
4
 
5
- from DIRAC import gConfig, gLogger
5
+ from DIRAC import S_OK, gConfig, gLogger
6
6
  from DIRAC.Core.Utilities.List import fromChar
7
7
 
8
8
 
@@ -139,3 +139,65 @@ def getNumberOfGPUs(siteName=None, gridCE=None, queue=None):
139
139
  # 3) return 0
140
140
  gLogger.info("NumberOfGPUs could not be found in CS")
141
141
  return 0
142
+
143
+
144
+ def getJobParameters(jobIDs: list[int], parName: str | None, vo: str = "") -> dict:
145
+ """Utility to get a job parameter for a list of jobIDs pertaining to a VO.
146
+ If the jobID is not in the JobParametersDB, it will be looked up in the JobDB.
147
+
148
+ Requires direct access to the JobParametersDB and JobDB.
149
+
150
+ :param jobIDs: list of jobIDs
151
+ :param parName: name of the parameter to be retrieved
152
+ :param vo: VO of the jobIDs
153
+ :return: dictionary with jobID as key and the parameter as value
154
+ :rtype: dict
155
+ """
156
+ from DIRAC.WorkloadManagementSystem.DB.JobParametersDB import JobParametersDB
157
+ from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
158
+
159
+ elasticJobParametersDB = JobParametersDB()
160
+ jobDB = JobDB()
161
+
162
+ if vo: # a user is connecting, with a proxy
163
+ res = elasticJobParametersDB.getJobParameters(jobIDs, vo, parName)
164
+ if not res["OK"]:
165
+ return res
166
+ parameters = res["Value"]
167
+ else: # a service is connecting, no proxy, e.g. StalledJobAgent
168
+ q = f"SELECT JobID, VO FROM Jobs WHERE JobID IN ({','.join([str(jobID) for jobID in jobIDs])})"
169
+ res = jobDB._query(q)
170
+ if not res["OK"]:
171
+ return res
172
+ if not res["Value"]:
173
+ return S_OK({})
174
+ # get the VO for each jobID
175
+ voDict = {}
176
+ for jobID, vo in res["Value"]:
177
+ if vo not in voDict:
178
+ voDict[vo] = []
179
+ voDict[vo].append(jobID)
180
+ # get the parameters for each VO
181
+ parameters = {}
182
+ for vo, jobIDs in voDict.items():
183
+ res = elasticJobParametersDB.getJobParameters(jobIDs, vo, parName)
184
+ if not res["OK"]:
185
+ return res
186
+ parameters.update(res["Value"])
187
+
188
+ # Need anyway to get also from JobDB, for those jobs with parameters registered in MySQL or in both backends
189
+ res = jobDB.getJobParameters(jobIDs, parName)
190
+ if not res["OK"]:
191
+ return res
192
+ parametersM = res["Value"]
193
+
194
+ # and now combine
195
+ final = dict(parametersM)
196
+ # if job in JobDB, update with parameters from ES if any
197
+ for jobID in final:
198
+ final[jobID].update(parameters.get(jobID, {}))
199
+ # if job in ES and not in JobDB, take ES
200
+ for jobID in parameters:
201
+ if jobID not in final:
202
+ final[jobID] = parameters[jobID]
203
+ return S_OK(final)
@@ -9,10 +9,11 @@ from DIRAC import S_ERROR, S_OK, gLogger
9
9
  from DIRAC.Core.Utilities import TimeUtilities
10
10
  from DIRAC.Core.Utilities.ObjectLoader import ObjectLoader
11
11
  from DIRAC.WorkloadManagementSystem.Client import JobStatus
12
+ from DIRACCommon.WorkloadManagementSystem.Utilities.JobStatusUtility import getStartAndEndTime, getNewStatus
12
13
 
13
14
  if TYPE_CHECKING:
14
- from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
15
15
  from DIRAC.WorkloadManagementSystem.DB.JobLoggingDB import JobLoggingDB
16
+ from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
16
17
 
17
18
 
18
19
  class JobStatusUtility:
@@ -180,66 +181,3 @@ class JobStatusUtility:
180
181
  return result
181
182
 
182
183
  return S_OK((attrNames, attrValues))
183
-
184
-
185
- def getStartAndEndTime(startTime, endTime, updateTimes, timeStamps, statusDict):
186
- newStat = ""
187
- firstUpdate = TimeUtilities.toEpoch(TimeUtilities.fromString(updateTimes[0]))
188
- for ts, st in timeStamps:
189
- if firstUpdate >= ts:
190
- newStat = st
191
- # Pick up start and end times from all updates
192
- for updTime in updateTimes:
193
- sDict = statusDict[updTime]
194
- newStat = sDict.get("Status", newStat)
195
-
196
- if not startTime and newStat == JobStatus.RUNNING:
197
- # Pick up the start date when the job starts running if not existing
198
- startTime = updTime
199
- elif not endTime and newStat in JobStatus.JOB_FINAL_STATES:
200
- # Pick up the end time when the job is in a final status
201
- endTime = updTime
202
-
203
- return startTime, endTime
204
-
205
-
206
- def getNewStatus(
207
- jobID: int,
208
- updateTimes: list[datetime],
209
- lastTime: datetime,
210
- statusDict: dict[datetime, Any],
211
- currentStatus,
212
- force: bool,
213
- log,
214
- ):
215
- status = ""
216
- minor = ""
217
- application = ""
218
- # Get the last status values looping on the most recent upupdateTimes in chronological order
219
- for updTime in [dt for dt in updateTimes if dt >= lastTime]:
220
- sDict = statusDict[updTime]
221
- log.debug(f"\tTime {updTime} - Statuses {str(sDict)}")
222
- status = sDict.get("Status", currentStatus)
223
- # evaluate the state machine if the status is changing
224
- if not force and status != currentStatus:
225
- res = JobStatus.JobsStateMachine(currentStatus).getNextState(status)
226
- if not res["OK"]:
227
- return res
228
- newStat = res["Value"]
229
- # If the JobsStateMachine does not accept the candidate, don't update
230
- if newStat != status:
231
- # keeping the same status
232
- log.error(
233
- f"Job Status Error: {jobID} can't move from {currentStatus} to {status}: using {newStat}",
234
- )
235
- status = newStat
236
- sDict["Status"] = newStat
237
- # Change the source to indicate this is not what was requested
238
- source = sDict.get("Source", "")
239
- sDict["Source"] = source + "(SM)"
240
- # at this stage status == newStat. Set currentStatus to this new status
241
- currentStatus = newStat
242
-
243
- minor = sDict.get("MinorStatus", minor)
244
- application = sDict.get("ApplicationStatus", application)
245
- return S_OK((status, minor, application))