DIRAC 9.0.0a54__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 (166) 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 +34 -32
  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 +9 -2
  14. DIRAC/ConfigurationSystem/Client/test/Test_PathFinder.py +41 -1
  15. DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
  16. DIRAC/Core/DISET/ServiceReactor.py +11 -3
  17. DIRAC/Core/DISET/private/BaseClient.py +1 -2
  18. DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
  19. DIRAC/Core/Security/DiracX.py +12 -7
  20. DIRAC/Core/Security/IAMService.py +4 -3
  21. DIRAC/Core/Security/ProxyInfo.py +9 -5
  22. DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
  23. DIRAC/Core/Tornado/Client/ClientSelector.py +4 -1
  24. DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
  25. DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
  26. DIRAC/Core/Utilities/DErrno.py +5 -309
  27. DIRAC/Core/Utilities/Extensions.py +10 -1
  28. DIRAC/Core/Utilities/Graphs/GraphData.py +1 -1
  29. DIRAC/Core/Utilities/JDL.py +1 -195
  30. DIRAC/Core/Utilities/List.py +1 -124
  31. DIRAC/Core/Utilities/MySQL.py +101 -97
  32. DIRAC/Core/Utilities/Os.py +32 -1
  33. DIRAC/Core/Utilities/Platform.py +2 -107
  34. DIRAC/Core/Utilities/ReturnValues.py +7 -252
  35. DIRAC/Core/Utilities/StateMachine.py +12 -178
  36. DIRAC/Core/Utilities/TimeUtilities.py +10 -253
  37. DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
  38. DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
  39. DIRAC/Core/scripts/dirac_agent.py +1 -1
  40. DIRAC/Core/scripts/dirac_apptainer_exec.py +16 -7
  41. DIRAC/Core/scripts/dirac_platform.py +1 -92
  42. DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
  43. DIRAC/DataManagementSystem/Agent/RequestOperations/RemoveFile.py +7 -6
  44. DIRAC/DataManagementSystem/Client/FTS3Job.py +71 -34
  45. DIRAC/DataManagementSystem/DB/FTS3DB.py +3 -0
  46. DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
  47. DIRAC/DataManagementSystem/Utilities/DMSHelpers.py +6 -2
  48. DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
  49. DIRAC/DataManagementSystem/scripts/dirac_dms_protocol_matrix.py +0 -1
  50. DIRAC/FrameworkSystem/Client/ComponentInstaller.py +4 -2
  51. DIRAC/FrameworkSystem/DB/ProxyDB.py +9 -5
  52. DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
  53. DIRAC/FrameworkSystem/Utilities/diracx.py +2 -74
  54. DIRAC/FrameworkSystem/private/authorization/AuthServer.py +2 -2
  55. DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
  56. DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +1 -1
  57. DIRAC/Interfaces/API/Dirac.py +27 -13
  58. DIRAC/Interfaces/API/DiracAdmin.py +42 -7
  59. DIRAC/Interfaces/API/Job.py +1 -0
  60. DIRAC/Interfaces/scripts/dirac_admin_allow_site.py +7 -1
  61. DIRAC/Interfaces/scripts/dirac_admin_ban_site.py +7 -1
  62. DIRAC/Interfaces/scripts/dirac_wms_job_parameters.py +0 -1
  63. DIRAC/MonitoringSystem/Client/Types/WMSHistory.py +4 -0
  64. DIRAC/MonitoringSystem/Client/WebAppClient.py +26 -0
  65. DIRAC/MonitoringSystem/ConfigTemplate.cfg +9 -0
  66. DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -25
  67. DIRAC/MonitoringSystem/Service/MonitoringHandler.py +0 -33
  68. DIRAC/MonitoringSystem/Service/WebAppHandler.py +599 -0
  69. DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
  70. DIRAC/ProductionSystem/scripts/dirac_prod_get_trans.py +2 -3
  71. DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
  72. DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
  73. DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
  74. DIRAC/ResourceStatusSystem/Client/SiteStatus.py +4 -2
  75. DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
  76. DIRAC/ResourceStatusSystem/Utilities/CSHelpers.py +2 -31
  77. DIRAC/ResourceStatusSystem/scripts/dirac_rss_set_status.py +18 -4
  78. DIRAC/Resources/Catalog/RucioFileCatalogClient.py +1 -1
  79. DIRAC/Resources/Computing/AREXComputingElement.py +19 -3
  80. DIRAC/Resources/Computing/BatchSystems/Condor.py +126 -108
  81. DIRAC/Resources/Computing/BatchSystems/SLURM.py +5 -1
  82. DIRAC/Resources/Computing/BatchSystems/test/Test_SLURM.py +46 -0
  83. DIRAC/Resources/Computing/HTCondorCEComputingElement.py +37 -43
  84. DIRAC/Resources/Computing/SingularityComputingElement.py +6 -1
  85. DIRAC/Resources/Computing/test/Test_HTCondorCEComputingElement.py +67 -49
  86. DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
  87. DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
  88. DIRAC/Resources/IdProvider/IdProviderFactory.py +11 -3
  89. DIRAC/Resources/Storage/StorageBase.py +4 -2
  90. DIRAC/Resources/Storage/StorageElement.py +4 -4
  91. DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +10 -16
  92. DIRAC/TransformationSystem/Agent/TransformationAgent.py +22 -1
  93. DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +15 -15
  94. DIRAC/TransformationSystem/Client/Transformation.py +2 -1
  95. DIRAC/TransformationSystem/Client/TransformationClient.py +0 -7
  96. DIRAC/TransformationSystem/Client/Utilities.py +9 -0
  97. DIRAC/TransformationSystem/Service/TransformationManagerHandler.py +0 -336
  98. DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
  99. DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
  100. DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
  101. DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
  102. DIRAC/WorkloadManagementSystem/Agent/JobAgent.py +1 -5
  103. DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +11 -7
  104. DIRAC/WorkloadManagementSystem/Agent/PilotSyncAgent.py +4 -3
  105. DIRAC/WorkloadManagementSystem/Agent/PushJobAgent.py +13 -13
  106. DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +10 -13
  107. DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +18 -51
  108. DIRAC/WorkloadManagementSystem/Agent/StatesAccountingAgent.py +41 -1
  109. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +2 -0
  110. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobCleaningAgent.py +7 -9
  111. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
  112. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +8 -2
  113. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +4 -5
  114. DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +7 -5
  115. DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py +10 -11
  116. DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
  117. DIRAC/WorkloadManagementSystem/Client/JobStateUpdateClient.py +3 -0
  118. DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -152
  119. DIRAC/WorkloadManagementSystem/Client/SandboxStoreClient.py +25 -38
  120. DIRAC/WorkloadManagementSystem/Client/WMSClient.py +2 -3
  121. DIRAC/WorkloadManagementSystem/Client/test/Test_Client_DownloadInputData.py +29 -0
  122. DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +4 -8
  123. DIRAC/WorkloadManagementSystem/DB/JobDB.py +40 -69
  124. DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
  125. DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +9 -9
  126. DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py +3 -2
  127. DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +28 -39
  128. DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
  129. DIRAC/WorkloadManagementSystem/DB/tests/Test_JobDB.py +1 -1
  130. DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
  131. DIRAC/WorkloadManagementSystem/Executor/JobSanity.py +3 -3
  132. DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py +2 -14
  133. DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +14 -9
  134. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +36 -10
  135. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
  136. DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +33 -154
  137. DIRAC/WorkloadManagementSystem/Service/JobMonitoringHandler.py +5 -323
  138. DIRAC/WorkloadManagementSystem/Service/JobStateUpdateHandler.py +0 -16
  139. DIRAC/WorkloadManagementSystem/Service/PilotManagerHandler.py +6 -102
  140. DIRAC/WorkloadManagementSystem/Service/SandboxStoreHandler.py +5 -51
  141. DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +16 -79
  142. DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -199
  143. DIRAC/WorkloadManagementSystem/Utilities/JobParameters.py +65 -3
  144. DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +2 -64
  145. DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
  146. DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +73 -7
  147. DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py +2 -0
  148. DIRAC/WorkloadManagementSystem/Utilities/RemoteRunner.py +16 -0
  149. DIRAC/WorkloadManagementSystem/Utilities/Utils.py +36 -1
  150. DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +15 -0
  151. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -5
  152. DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
  153. DIRAC/WorkloadManagementSystem/Utilities/test/Test_PilotWrapper.py +16 -0
  154. DIRAC/__init__.py +55 -54
  155. {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/METADATA +6 -4
  156. {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/RECORD +160 -160
  157. {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/WHEEL +1 -1
  158. {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/entry_points.txt +0 -3
  159. DIRAC/Core/Utilities/test/Test_List.py +0 -150
  160. DIRAC/Core/Utilities/test/Test_Time.py +0 -88
  161. DIRAC/TransformationSystem/scripts/dirac_transformation_archive.py +0 -30
  162. DIRAC/TransformationSystem/scripts/dirac_transformation_clean.py +0 -30
  163. DIRAC/TransformationSystem/scripts/dirac_transformation_remove_output.py +0 -30
  164. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobManager.py +0 -58
  165. {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/licenses/LICENSE +0 -0
  166. {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,599 @@
1
+ """
2
+ The WebAppHandler module provides a class to handle web requests from the DIRAC WebApp.
3
+ It is not indented to be used in diracx
4
+ """
5
+ from DIRAC import S_ERROR, S_OK
6
+ from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
7
+ from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getSites
8
+ from DIRAC.Core.DISET.RequestHandler import RequestHandler
9
+ from DIRAC.Core.Utilities.ObjectLoader import ObjectLoader
10
+ from DIRAC.RequestManagementSystem.Client.Operation import Operation
11
+ from DIRAC.RequestManagementSystem.Client.Request import Request
12
+ from DIRAC.TransformationSystem.Client import TransformationFilesStatus
13
+ from DIRAC.WorkloadManagementSystem.Client import JobStatus
14
+ from DIRAC.WorkloadManagementSystem.Service.JobPolicy import RIGHT_GET_INFO, JobPolicy
15
+
16
+ TASKS_STATE_NAMES = ["TotalCreated", "Created"] + sorted(
17
+ set(JobStatus.JOB_STATES) | set(Request.ALL_STATES) | set(Operation.ALL_STATES)
18
+ )
19
+ FILES_STATE_NAMES = ["PercentProcessed", "Total"] + TransformationFilesStatus.TRANSFORMATION_FILES_STATES
20
+
21
+
22
+ class WebAppHandler(RequestHandler):
23
+ @classmethod
24
+ def initializeHandler(cls, serviceInfoDict):
25
+ """Initialization of DB objects"""
26
+
27
+ try:
28
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.PilotAgentsDB", "PilotAgentsDB")
29
+ if not result["OK"]:
30
+ return result
31
+ try:
32
+ cls.pilotAgentsDB = result["Value"](parentLogger=cls.log)
33
+ except RuntimeError:
34
+ cls.log.warn("Could not connect to PilotAgentsDB")
35
+
36
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.JobDB", "JobDB")
37
+ if not result["OK"]:
38
+ return result
39
+ try:
40
+ cls.jobDB = result["Value"](parentLogger=cls.log)
41
+ except RuntimeError:
42
+ cls.log.warn("Could not connect to JobDB")
43
+
44
+ result = ObjectLoader().loadObject("TransformationSystem.DB.TransformationDB", "TransformationDB")
45
+ if not result["OK"]:
46
+ return result
47
+ try:
48
+ cls.transformationDB = result["Value"](parentLogger=cls.log)
49
+ except RuntimeError:
50
+ cls.log.warn("Could not connect to TransformationDB")
51
+
52
+ except RuntimeError as excp:
53
+ cls.log.exception()
54
+ return S_ERROR(f"Can't connect to DB: {excp}")
55
+
56
+ return S_OK()
57
+
58
+ ##############################################################################
59
+ # PilotAgents
60
+ ##############################################################################
61
+
62
+ types_getPilotMonitorWeb = [dict, list, int, int]
63
+
64
+ @classmethod
65
+ def export_getPilotMonitorWeb(cls, selectDict, sortList, startItem, maxItems):
66
+ """Get the summary of the pilot information for a given page in the
67
+ pilot monitor in a generic format
68
+ """
69
+
70
+ return cls.pilotAgentsDB.getPilotMonitorWeb(selectDict, sortList, startItem, maxItems)
71
+
72
+ types_getPilotMonitorSelectors = []
73
+
74
+ @classmethod
75
+ def export_getPilotMonitorSelectors(cls):
76
+ """Get all the distinct selector values for the Pilot Monitor web portal page"""
77
+
78
+ return cls.pilotAgentsDB.getPilotMonitorSelectors()
79
+
80
+ types_getPilotSummaryWeb = [dict, list, int, int]
81
+
82
+ @classmethod
83
+ def export_getPilotSummaryWeb(cls, selectDict, sortList, startItem, maxItems):
84
+ """Get the summary of the pilot information for a given page in the
85
+ pilot monitor in a generic format
86
+ """
87
+
88
+ return cls.pilotAgentsDB.getPilotSummaryWeb(selectDict, sortList, startItem, maxItems)
89
+
90
+ types_getPilotStatistics = [str, dict]
91
+
92
+ @classmethod
93
+ def export_getPilotStatistics(cls, attribute, selectDict):
94
+ """Get pilot statistics distribution per attribute value with a given selection"""
95
+
96
+ startDate = selectDict.get("FromDate", None)
97
+ if startDate:
98
+ del selectDict["FromDate"]
99
+
100
+ if startDate is None:
101
+ startDate = selectDict.get("LastUpdate", None)
102
+ if startDate:
103
+ del selectDict["LastUpdate"]
104
+ endDate = selectDict.get("ToDate", None)
105
+ if endDate:
106
+ del selectDict["ToDate"]
107
+
108
+ result = cls.pilotAgentsDB.getCounters(
109
+ "PilotAgents", [attribute], selectDict, newer=startDate, older=endDate, timeStamp="LastUpdateTime"
110
+ )
111
+ statistics = {}
112
+ if result["OK"]:
113
+ for status, count in result["Value"]:
114
+ statistics[status[attribute]] = count
115
+
116
+ return S_OK(statistics)
117
+
118
+ types_getPilotsCounters = [str, list, dict]
119
+
120
+ # This was PilotManagerHandler.getCounters
121
+ @classmethod
122
+ def export_getPilotsCounters(cls, table, keys, condDict, newer=None, timeStamp="SubmissionTime"):
123
+ """Set the pilot agent status"""
124
+
125
+ return cls.pilotAgentsDB.getCounters(table, keys, condDict, newer=newer, timeStamp=timeStamp)
126
+
127
+ ##############################################################################
128
+ # Jobs
129
+ ##############################################################################
130
+
131
+ types_getJobPageSummaryWeb = [dict, list, int, int]
132
+
133
+ def export_getJobPageSummaryWeb(self, selectDict, sortList, startItem, maxItems, selectJobs=True):
134
+ """Get the summary of the job information for a given page in the
135
+ job monitor in a generic format
136
+ """
137
+
138
+ resultDict = {}
139
+
140
+ startDate, endDate, selectDict = self.parseSelectors(selectDict)
141
+
142
+ # initialize jobPolicy
143
+ credDict = self.getRemoteCredentials()
144
+ owner = credDict["username"]
145
+ ownerGroup = credDict["group"]
146
+ operations = Operations(group=ownerGroup)
147
+ globalJobsInfo = operations.getValue("/Services/JobMonitoring/GlobalJobsInfo", True)
148
+ jobPolicy = JobPolicy(owner, ownerGroup, globalJobsInfo)
149
+ jobPolicy.jobDB = self.jobDB
150
+ result = jobPolicy.getControlledUsers(RIGHT_GET_INFO)
151
+ if not result["OK"]:
152
+ return result
153
+ if not result["Value"]:
154
+ return S_ERROR(f"User and group combination has no job rights ({owner!r}, {ownerGroup!r})")
155
+ if result["Value"] != "ALL":
156
+ selectDict[("Owner", "OwnerGroup")] = result["Value"]
157
+
158
+ # Sorting instructions. Only one for the moment.
159
+ if sortList:
160
+ orderAttribute = sortList[0][0] + ":" + sortList[0][1]
161
+ else:
162
+ orderAttribute = None
163
+
164
+ result = self.jobDB.getCounters(
165
+ "Jobs", ["Status"], selectDict, newer=startDate, older=endDate, timeStamp="LastUpdateTime"
166
+ )
167
+ if not result["OK"]:
168
+ return result
169
+
170
+ statusDict = {}
171
+ nJobs = 0
172
+ for stDict, count in result["Value"]:
173
+ nJobs += count
174
+ statusDict[stDict["Status"]] = count
175
+
176
+ resultDict["TotalRecords"] = nJobs
177
+ if nJobs == 0:
178
+ return S_OK(resultDict)
179
+
180
+ resultDict["Extras"] = statusDict
181
+
182
+ if selectJobs:
183
+ iniJob = startItem
184
+ if iniJob >= nJobs:
185
+ return S_ERROR("Item number out of range")
186
+
187
+ result = self.jobDB.selectJobs(
188
+ selectDict, orderAttribute=orderAttribute, newer=startDate, older=endDate, limit=(maxItems, iniJob)
189
+ )
190
+ if not result["OK"]:
191
+ return result
192
+
193
+ summaryJobList = result["Value"]
194
+ if not globalJobsInfo:
195
+ validJobs, _invalidJobs, _nonauthJobs, _ownJobs = jobPolicy.evaluateJobRights(
196
+ summaryJobList, RIGHT_GET_INFO
197
+ )
198
+ summaryJobList = validJobs
199
+
200
+ result = self.jobDB.getJobsAttributes(summaryJobList)
201
+ if not result["OK"]:
202
+ return result
203
+
204
+ summaryDict = result["Value"]
205
+ # If no jobs can be selected after the properties check
206
+ if not summaryDict:
207
+ return S_OK(resultDict)
208
+
209
+ # Evaluate last sign of life time
210
+ for jobDict in summaryDict.values():
211
+ if not jobDict.get("HeartBeatTime") or jobDict["HeartBeatTime"] == "None":
212
+ jobDict["LastSignOfLife"] = jobDict["LastUpdateTime"]
213
+ else:
214
+ jobDict["LastSignOfLife"] = jobDict["HeartBeatTime"]
215
+
216
+ # prepare the standard structure now
217
+ # This should be faster than making a list of values()
218
+ for jobDict in summaryDict.values():
219
+ paramNames = list(jobDict)
220
+ break
221
+ records = [list(jobDict.values()) for jobDict in summaryDict.values()]
222
+
223
+ resultDict["ParameterNames"] = paramNames
224
+ resultDict["Records"] = records
225
+
226
+ return S_OK(resultDict)
227
+
228
+ types_getJobStats = [str, dict]
229
+
230
+ @classmethod
231
+ def export_getJobStats(cls, attribute, selectDict):
232
+ """Get job statistics distribution per attribute value with a given selection"""
233
+ startDate, endDate, selectDict = cls.parseSelectors(selectDict)
234
+ result = cls.jobDB.getCounters(
235
+ "Jobs", [attribute], selectDict, newer=startDate, older=endDate, timeStamp="LastUpdateTime"
236
+ )
237
+ if not result["OK"]:
238
+ return result
239
+ resultDict = {}
240
+ for cDict, count in result["Value"]:
241
+ resultDict[cDict[attribute]] = count
242
+
243
+ return S_OK(resultDict)
244
+
245
+ @classmethod
246
+ def parseSelectors(cls, selectDict=None):
247
+ """Parse selectors before DB query
248
+
249
+ :param dict selectDict: selectors
250
+
251
+ :return: str, str, dict -- start/end date, selectors
252
+ """
253
+ selectDict = selectDict or {}
254
+
255
+ # Get time period
256
+ startDate = selectDict.get("FromDate", None)
257
+ if startDate:
258
+ del selectDict["FromDate"]
259
+ # For backward compatibility
260
+ if startDate is None:
261
+ startDate = selectDict.get("LastUpdate", None)
262
+ if startDate:
263
+ del selectDict["LastUpdate"]
264
+ endDate = selectDict.get("ToDate", None)
265
+ if endDate:
266
+ del selectDict["ToDate"]
267
+
268
+ # Provide JobID bound to a specific PilotJobReference
269
+ # There is no reason to have both PilotJobReference and JobID in selectDict
270
+ # If that occurs, use the JobID instead of the PilotJobReference
271
+ pilotJobRefs = selectDict.get("PilotJobReference")
272
+ if pilotJobRefs:
273
+ del selectDict["PilotJobReference"]
274
+ if not selectDict.get("JobID"):
275
+ for pilotJobRef in [pilotJobRefs] if isinstance(pilotJobRefs, str) else pilotJobRefs:
276
+ res = cls.pilotAgentsDB.getPilotInfo(pilotJobRef)
277
+ if res["OK"] and "Jobs" in res["Value"][pilotJobRef]:
278
+ selectDict["JobID"] = selectDict.get("JobID", [])
279
+ selectDict["JobID"].extend(res["Value"][pilotJobRef]["Jobs"])
280
+
281
+ return startDate, endDate, selectDict
282
+
283
+ types_getJobsCounters = [list]
284
+
285
+ # This was JobManagerHanlder.getCounters
286
+ @classmethod
287
+ def export_getJobsCounters(cls, attrList, attrDict=None, cutDate=""):
288
+ """
289
+ Retrieve list of distinct attributes values from attrList
290
+ with attrDict as condition.
291
+ For each set of distinct values, count number of occurences.
292
+ Return a list. Each item is a list with 2 items, the list of distinct
293
+ attribute values and the counter
294
+ """
295
+
296
+ _, _, attrDict = cls.parseSelectors(attrDict)
297
+ return cls.jobDB.getCounters("Jobs", attrList, attrDict, newer=str(cutDate), timeStamp="LastUpdateTime")
298
+
299
+ types_getSiteSummaryWeb = [dict, list, int, int]
300
+
301
+ @classmethod
302
+ def export_getSiteSummaryWeb(cls, selectDict, sortList, startItem, maxItems):
303
+ """Get the summary of the jobs running on sites in a generic format
304
+
305
+ :param dict selectDict: selectors
306
+ :param list sortList: sorting list
307
+ :param int startItem: start item number
308
+ :param int maxItems: maximum of items
309
+
310
+ :return: S_OK(dict)/S_ERROR()
311
+ """
312
+ return cls.jobDB.getSiteSummaryWeb(selectDict, sortList, startItem, maxItems)
313
+
314
+ types_getSiteSummarySelectors = []
315
+
316
+ @classmethod
317
+ def export_getSiteSummarySelectors(cls):
318
+ """Get all the distinct selector values for the site summary web portal page
319
+
320
+ :return: S_OK(dict)/S_ERROR()
321
+ """
322
+ resultDict = {}
323
+ statusList = ["Good", "Fair", "Poor", "Bad", "Idle"]
324
+ resultDict["Status"] = statusList
325
+ maskStatus = ["Active", "Banned", "NoMask", "Reduced"]
326
+ resultDict["MaskStatus"] = maskStatus
327
+
328
+ res = getSites()
329
+ if not res["OK"]:
330
+ return res
331
+ siteList = res["Value"]
332
+
333
+ countryList = []
334
+ for site in siteList:
335
+ if site.find(".") != -1:
336
+ country = site.split(".")[2].lower()
337
+ if country not in countryList:
338
+ countryList.append(country)
339
+ countryList.sort()
340
+ resultDict["Country"] = countryList
341
+ siteList.sort()
342
+ resultDict["Site"] = siteList
343
+
344
+ return S_OK(resultDict)
345
+
346
+ types_getApplicationStates = []
347
+
348
+ @classmethod
349
+ def export_getApplicationStates(cls, condDict=None, older=None, newer=None):
350
+ """Return Distinct Values of ApplicationStatus job Attribute in WMS"""
351
+ return cls.jobDB.getDistinctJobAttributes("ApplicationStatus", condDict, older, newer)
352
+
353
+ types_getJobTypes = []
354
+
355
+ @classmethod
356
+ def export_getJobTypes(cls, condDict=None, older=None, newer=None):
357
+ """Return Distinct Values of JobType job Attribute in WMS"""
358
+ return cls.jobDB.getDistinctJobAttributes("JobType", condDict, older, newer)
359
+
360
+ types_getOwners = []
361
+
362
+ @classmethod
363
+ def export_getOwners(cls, condDict=None, older=None, newer=None):
364
+ """
365
+ Return Distinct Values of Owner job Attribute in WMS
366
+ """
367
+ return cls.jobDB.getDistinctJobAttributes("Owner", condDict, older, newer)
368
+
369
+ types_getOwnerGroup = []
370
+
371
+ @classmethod
372
+ def export_getOwnerGroup(cls):
373
+ """
374
+ Return Distinct Values of OwnerGroup from the JobDB
375
+ """
376
+ return cls.jobDB.getDistinctJobAttributes("OwnerGroup")
377
+
378
+ types_getJobGroups = []
379
+
380
+ @classmethod
381
+ def export_getJobGroups(cls, condDict=None, older=None, cutDate=None):
382
+ """
383
+ Return Distinct Values of ProductionId job Attribute in WMS
384
+ """
385
+ return cls.jobDB.getDistinctJobAttributes("JobGroup", condDict, older, newer=cutDate)
386
+
387
+ types_getSites = []
388
+
389
+ @classmethod
390
+ def export_getSites(cls, condDict=None, older=None, newer=None):
391
+ """
392
+ Return Distinct Values of Site job Attribute in WMS
393
+ """
394
+ return cls.jobDB.getDistinctJobAttributes("Site", condDict, older, newer)
395
+
396
+ types_getStates = []
397
+
398
+ @classmethod
399
+ def export_getStates(cls, condDict=None, older=None, newer=None):
400
+ """
401
+ Return Distinct Values of Status job Attribute in WMS
402
+ """
403
+ return cls.jobDB.getDistinctJobAttributes("Status", condDict, older, newer)
404
+
405
+ types_getMinorStates = []
406
+
407
+ @classmethod
408
+ def export_getMinorStates(cls, condDict=None, older=None, newer=None):
409
+ """
410
+ Return Distinct Values of Minor Status job Attribute in WMS
411
+ """
412
+ return cls.jobDB.getDistinctJobAttributes("MinorStatus", condDict, older, newer)
413
+
414
+ ##############################################################################
415
+ # Transformations
416
+ ##############################################################################
417
+
418
+ types_getDistinctAttributeValues = [str, dict]
419
+
420
+ @classmethod
421
+ def export_getDistinctAttributeValues(cls, attribute, selectDict):
422
+ res = cls.transformationDB.getTableDistinctAttributeValues("Transformations", [attribute], selectDict)
423
+ if not res["OK"]:
424
+ return res
425
+ return S_OK(res["Value"][attribute])
426
+
427
+ types_getTransformationFilesSummaryWeb = [dict, list, int, int]
428
+
429
+ @classmethod
430
+ def export_getTransformationFilesSummaryWeb(cls, selectDict, sortList, startItem, maxItems):
431
+ fromDate = selectDict.get("FromDate", None)
432
+ if fromDate:
433
+ del selectDict["FromDate"]
434
+ toDate = selectDict.get("ToDate", None)
435
+ if toDate:
436
+ del selectDict["ToDate"]
437
+ # Sorting instructions. Only one for the moment.
438
+ if sortList:
439
+ orderAttribute = sortList[0][0] + ":" + sortList[0][1]
440
+ else:
441
+ orderAttribute = None
442
+ # Get the columns that match the selection
443
+ res = cls.transformationDB.getTransformationFiles(
444
+ condDict=selectDict, older=toDate, newer=fromDate, timeStamp="LastUpdate", orderAttribute=orderAttribute
445
+ )
446
+ if not res["OK"]:
447
+ return res
448
+ allRows = res["Value"]
449
+
450
+ # Prepare the standard structure now within the resultDict dictionary
451
+ resultDict = {}
452
+ resultDict["TotalRecords"] = len(allRows)
453
+
454
+ if not allRows:
455
+ return S_OK(resultDict)
456
+
457
+ # Get the rows which are within the selected window
458
+ ini = startItem
459
+ last = ini + maxItems
460
+ if ini >= resultDict["TotalRecords"]:
461
+ return S_ERROR("Item number out of range")
462
+ if last > resultDict["TotalRecords"]:
463
+ last = resultDict["TotalRecords"]
464
+
465
+ selectedRows = allRows[ini:last]
466
+ resultDict["Records"] = []
467
+ for row in selectedRows:
468
+ resultDict["Records"].append(list(row.values()))
469
+
470
+ # Create the ParameterNames entry
471
+ resultDict["ParameterNames"] = list(selectedRows[0].keys())
472
+
473
+ # Generate the status dictionary
474
+ statusDict = {}
475
+ for row in selectedRows:
476
+ status = row["Status"]
477
+ statusDict[status] = statusDict.setdefault(status, 0) + 1
478
+ resultDict["Extras"] = statusDict
479
+
480
+ # Obtain the distinct values of the selection parameters
481
+ res = cls.transformationDB.getTableDistinctAttributeValues(
482
+ "TransformationFiles",
483
+ ["TransformationID", "Status", "UsedSE", "TargetSE"],
484
+ selectDict,
485
+ older=toDate,
486
+ newer=fromDate,
487
+ )
488
+ if not res["OK"]:
489
+ return res
490
+ resultDict["Selections"] = res["Value"]
491
+
492
+ return S_OK(resultDict)
493
+
494
+ types_getTransformationSummaryWeb = [dict, list, int, int]
495
+
496
+ @classmethod
497
+ def export_getTransformationSummaryWeb(cls, selectDict, sortList, startItem, maxItems):
498
+ """Get the summary of the transformation information for a given page in the generic format"""
499
+
500
+ # Obtain the timing information from the selectDict
501
+ last_update = selectDict.get("CreationDate", None)
502
+ if last_update:
503
+ del selectDict["CreationDate"]
504
+ fromDate = selectDict.get("FromDate", None)
505
+ if fromDate:
506
+ del selectDict["FromDate"]
507
+ if not fromDate:
508
+ fromDate = last_update
509
+ toDate = selectDict.get("ToDate", None)
510
+ if toDate:
511
+ del selectDict["ToDate"]
512
+ # Sorting instructions. Only one for the moment.
513
+ if sortList:
514
+ orderAttribute = []
515
+ for i in sortList:
516
+ orderAttribute += [i[0] + ":" + i[1]]
517
+ else:
518
+ orderAttribute = None
519
+
520
+ # Get the transformations that match the selection
521
+ res = cls.transformationDB.getTransformations(
522
+ condDict=selectDict, older=toDate, newer=fromDate, orderAttribute=orderAttribute
523
+ )
524
+ if not res["OK"]:
525
+ return res
526
+
527
+ ops = Operations()
528
+ # Prepare the standard structure now within the resultDict dictionary
529
+ resultDict = {}
530
+ trList = res["Records"]
531
+ # Create the total records entry
532
+ nTrans = len(trList)
533
+ resultDict["TotalRecords"] = nTrans
534
+ # Create the ParameterNames entry
535
+ # As this list is a reference to the list in the DB, we cannot extend it, therefore copy it
536
+ resultDict["ParameterNames"] = list(res["ParameterNames"])
537
+ # Add the job states to the ParameterNames entry
538
+ taskStateNames = TASKS_STATE_NAMES + ops.getValue("Transformations/AdditionalTaskStates", [])
539
+ resultDict["ParameterNames"] += ["Jobs_" + x for x in taskStateNames]
540
+ # Add the file states to the ParameterNames entry
541
+ fileStateNames = FILES_STATE_NAMES + ops.getValue("Transformations/AdditionalFileStates", [])
542
+ resultDict["ParameterNames"] += ["Files_" + x for x in fileStateNames]
543
+
544
+ # Get the transformations which are within the selected window
545
+ if nTrans == 0:
546
+ return S_OK(resultDict)
547
+ ini = startItem
548
+ last = ini + maxItems
549
+ if ini >= nTrans:
550
+ return S_ERROR("Item number out of range")
551
+ if last > nTrans:
552
+ last = nTrans
553
+ transList = trList[ini:last]
554
+
555
+ statusDict = {}
556
+ extendableTranfs = ops.getValue("Transformations/ExtendableTransfTypes", ["Simulation", "MCsimulation"])
557
+ givenUpFileStatus = ops.getValue("Transformations/GivenUpFileStatus", ["MissingInFC"])
558
+ problematicStatuses = ops.getValue("Transformations/ProblematicStatuses", ["Problematic"])
559
+ # Add specific information for each selected transformation
560
+ for trans in transList:
561
+ transDict = dict(zip(resultDict["ParameterNames"], trans))
562
+
563
+ # Update the status counters
564
+ status = transDict["Status"]
565
+ statusDict[status] = statusDict.setdefault(status, 0) + 1
566
+
567
+ # Get the statistics on the number of jobs for the transformation
568
+ transID = transDict["TransformationID"]
569
+ res = cls.transformationDB.getTransformationTaskStats(transID)
570
+ taskDict = {}
571
+ if res["OK"] and res["Value"]:
572
+ taskDict = res["Value"]
573
+ for state in taskStateNames:
574
+ trans.append(taskDict.get(state, 0))
575
+
576
+ # Get the statistics for the number of files for the transformation
577
+ fileDict = {}
578
+ transType = transDict["Type"]
579
+ if transType.lower() in extendableTranfs:
580
+ fileDict["PercentProcessed"] = "-"
581
+ else:
582
+ res = cls.transformationDB.getTransformationStats(transID)
583
+ if res["OK"]:
584
+ fileDict = res["Value"]
585
+ total = fileDict["Total"]
586
+ for stat in givenUpFileStatus:
587
+ total -= fileDict.get(stat, 0)
588
+ processed = fileDict.get(TransformationFilesStatus.PROCESSED, 0)
589
+ fileDict["PercentProcessed"] = f"{int(processed * 1000.0 / total) / 10.0:.1f}" if total else 0.0
590
+ problematic = 0
591
+ for stat in problematicStatuses:
592
+ problematic += fileDict.get(stat, 0)
593
+ fileDict["Problematic"] = problematic
594
+ for state in fileStateNames:
595
+ trans.append(fileDict.get(state, 0))
596
+
597
+ resultDict["Records"] = transList
598
+ resultDict["Extras"] = statusDict
599
+ return S_OK(resultDict)
@@ -5,7 +5,6 @@ import hashlib
5
5
  import re
6
6
 
7
7
  from DIRAC import S_OK, S_ERROR, gConfig
8
- from DIRAC.ConfigurationSystem.Client.Helpers import CSGlobals
9
8
  from DIRAC.ConfigurationSystem.Client.PathFinder import getServiceSection
10
9
  from DIRAC.MonitoringSystem.private.Plotters.BasePlotter import BasePlotter as myBasePlotter
11
10
  from DIRAC.Core.Utilities.ObjectLoader import loadObjects
@@ -56,7 +55,6 @@ class MainReporter:
56
55
  :param str setup: DIRAC setup
57
56
  """
58
57
  self.__db = db
59
- self.__setup = CSGlobals.getSetup().lower()
60
58
  self.__csSection = getServiceSection("Monitoring/Monitoring")
61
59
  self.__plotterList = PlottersList()
62
60
 
@@ -75,7 +73,6 @@ class MainReporter:
75
73
  requestToHash[key] = epoch - epoch % granularity
76
74
  md5Hash = hashlib.md5()
77
75
  md5Hash.update(repr(requestToHash).encode())
78
- md5Hash.update(self.__setup.encode())
79
76
  return md5Hash.hexdigest()
80
77
 
81
78
  def generate(self, reportRequest):
@@ -16,14 +16,13 @@ def main():
16
16
  Script.registerArgument("prodID: Production ID")
17
17
  _, args = Script.parseCommandLine()
18
18
 
19
+ from DIRAC.MonitoringSystem.Client.WebAppClient import WebAppClient
19
20
  from DIRAC.ProductionSystem.Client.ProductionClient import ProductionClient
20
- from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
21
21
 
22
22
  # get arguments
23
23
  prodID = args[0]
24
24
 
25
25
  prodClient = ProductionClient()
26
- transClient = TransformationClient()
27
26
 
28
27
  res = prodClient.getProductionTransformations(prodID)
29
28
  transIDs = []
@@ -70,7 +69,7 @@ def main():
70
69
  ]
71
70
  resList = []
72
71
 
73
- res = transClient.getTransformationSummaryWeb({"TransformationID": transIDs}, [], 0, len(transIDs))
72
+ res = WebAppClient().getTransformationSummaryWeb({"TransformationID": transIDs}, [], 0, len(transIDs))
74
73
 
75
74
  if not res["OK"]:
76
75
  DIRAC.gLogger.error(res["Message"])
@@ -48,13 +48,13 @@ from DIRAC.RequestManagementSystem.private.RequestTask import RequestTask
48
48
  # # agent name
49
49
  AGENT_NAME = "RequestManagement/RequestExecutingAgent"
50
50
  # # requests/cycle
51
- REQUESTSPERCYCLE = 100
51
+ REQUESTSPERCYCLE = 300
52
52
  # # minimal nb of subprocess running
53
- MINPROCESS = 20
53
+ MINPROCESS = 50
54
54
  # # maximal nb of subprocess executed same time
55
- MAXPROCESS = 20
55
+ MAXPROCESS = 50
56
56
  # # ProcessPool queue size
57
- QUEUESIZE = 20
57
+ QUEUESIZE = 100
58
58
  # # file timeout
59
59
  FILETIMEOUT = 300
60
60
  # # operation timeout
@@ -62,7 +62,9 @@ OPERATIONTIMEOUT = 300
62
62
  # # ProcessPool finalization timeout
63
63
  POOLTIMEOUT = 900
64
64
  # # ProcessPool sleep time
65
- POOLSLEEP = 5
65
+ POOLSLEEP = 1
66
+ # # Fetch multiple requests at once from the DB. Otherwise, one by one
67
+ BULKREQUEST = 300
66
68
 
67
69
 
68
70
  class AgentConfigError(Exception):
@@ -108,7 +110,7 @@ class RequestExecutingAgent(AgentModule):
108
110
  self.__poolSleep = POOLSLEEP
109
111
  self.__requestClient = None
110
112
  # Size of the bulk if use of getRequests. If 0, use getRequest
111
- self.__bulkRequest = 0
113
+ self.__bulkRequest = BULKREQUEST
112
114
  self.__rmsMonitoring = False
113
115
 
114
116
  def processPool(self):