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
@@ -7,7 +7,6 @@ import stat
7
7
  from urllib.parse import urlparse
8
8
 
9
9
  from DIRAC import S_ERROR, S_OK
10
- from DIRAC.Resources.Computing.PilotBundle import bundleProxy, writeScript
11
10
  from DIRAC.Resources.Computing.SSHComputingElement import SSHComputingElement
12
11
  from DIRAC.WorkloadManagementSystem.Client import PilotStatus
13
12
 
@@ -80,18 +79,6 @@ class SSHBatchComputingElement(SSHComputingElement):
80
79
  if not os.access(executableFile, 5):
81
80
  os.chmod(executableFile, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
82
81
 
83
- # if no proxy is supplied, the executable can be submitted directly
84
- # otherwise a wrapper script is needed to get the proxy to the execution node
85
- # The wrapper script makes debugging more complicated and thus it is
86
- # recommended to transfer a proxy inside the executable if possible.
87
- if proxy:
88
- self.log.verbose("Setting up proxy for payload")
89
- wrapperContent = bundleProxy(executableFile, proxy)
90
- name = writeScript(wrapperContent, os.getcwd())
91
- submitFile = name
92
- else: # no proxy
93
- submitFile = executableFile
94
-
95
82
  # Submit jobs now
96
83
  restJobs = numberOfJobs
97
84
  submittedJobs = []
@@ -100,7 +87,7 @@ class SSHBatchComputingElement(SSHComputingElement):
100
87
  if slots not in rankHosts:
101
88
  continue
102
89
  for host in rankHosts[slots]:
103
- result = self._submitJobToHost(submitFile, min(slots, restJobs), host)
90
+ result = self._submitJobToHost(executableFile, min(slots, restJobs), host)
104
91
  if not result["OK"]:
105
92
  continue
106
93
 
@@ -114,9 +101,6 @@ class SSHBatchComputingElement(SSHComputingElement):
114
101
  if restJobs <= 0:
115
102
  break
116
103
 
117
- if proxy:
118
- os.remove(submitFile)
119
-
120
104
  result = S_OK(submittedJobs)
121
105
  result["PilotStampDict"] = stampDict
122
106
  return result
@@ -78,7 +78,6 @@ from DIRAC import S_ERROR, S_OK, gLogger
78
78
  from DIRAC.Core.Utilities.List import breakListIntoChunks, uniqueElements
79
79
  from DIRAC.Resources.Computing.BatchSystems.executeBatch import executeBatchContent
80
80
  from DIRAC.Resources.Computing.ComputingElement import ComputingElement
81
- from DIRAC.Resources.Computing.PilotBundle import bundleProxy, writeScript
82
81
 
83
82
 
84
83
  class SSH:
@@ -532,23 +531,7 @@ class SSHComputingElement(ComputingElement):
532
531
  if not os.access(executableFile, 5):
533
532
  os.chmod(executableFile, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
534
533
 
535
- # if no proxy is supplied, the executable can be submitted directly
536
- # otherwise a wrapper script is needed to get the proxy to the execution node
537
- # The wrapper script makes debugging more complicated and thus it is
538
- # recommended to transfer a proxy inside the executable if possible.
539
- if proxy:
540
- self.log.verbose("Setting up proxy for payload")
541
- wrapperContent = bundleProxy(executableFile, proxy)
542
- name = writeScript(wrapperContent, os.getcwd())
543
- submitFile = name
544
- else: # no proxy
545
- submitFile = executableFile
546
-
547
- result = self._submitJobToHost(submitFile, numberOfJobs)
548
- if proxy:
549
- os.remove(submitFile)
550
-
551
- return result
534
+ return self._submitJobToHost(executableFile, numberOfJobs)
552
535
 
553
536
  def _submitJobToHost(self, executableFile, numberOfJobs, host=None):
554
537
  """Submit prepared executable to the given host"""
@@ -22,7 +22,7 @@ from pathlib import Path
22
22
  import DIRAC
23
23
  from DIRAC import S_ERROR, S_OK, gConfig, gLogger
24
24
  from DIRAC.ConfigurationSystem.Client.Helpers import Operations
25
- from DIRAC.Core.Utilities.Subprocess import systemCall
25
+ from DIRAC.Core.Utilities.CGroups2 import CG2Manager
26
26
  from DIRAC.Core.Utilities.ThreadScheduler import gThreadScheduler
27
27
  from DIRAC.Resources.Computing.ComputingElement import ComputingElement
28
28
  from DIRAC.Resources.Storage.StorageElement import StorageElement
@@ -381,8 +381,18 @@ class SingularityComputingElement(ComputingElement):
381
381
  for seName in localSEs:
382
382
  try:
383
383
  # Find the base path if a File protocol is defined
384
- mountedPath = StorageElement(seName).getStorageParameters(protocol="file")["Value"]["Path"]
385
- bindPaths.append(f"{mountedPath}:{mountedPath}:ro")
384
+ se = StorageElement(seName)
385
+ if not se.valid:
386
+ self.log.warn(f"Storage Element {seName} not valid")
387
+ continue
388
+
389
+ mountedPath = se.getStorageParameters(protocol="file")["Value"]["Path"]
390
+ mountOptions = se.getStorageParameters(protocol="file")["Value"].get("MountOptions", "ro")
391
+ if mountOptions not in ("rw", "ro"):
392
+ self.log.warn(f"Unknown mount mode: {mountOptions}")
393
+ continue
394
+
395
+ bindPaths.append(f"{mountedPath}:{mountedPath}:{mountOptions}")
386
396
  except KeyError:
387
397
  pass
388
398
 
@@ -405,7 +415,9 @@ class SingularityComputingElement(ComputingElement):
405
415
 
406
416
  self.log.debug(f"Execute singularity command: {cmd}")
407
417
  self.log.debug(f"Execute singularity env: {self.__getEnv()}")
408
- result = systemCall(0, cmd, callbackFunction=self.sendOutput, env=self.__getEnv())
418
+ result = CG2Manager().systemCall(
419
+ 0, cmd, callbackFunction=self.sendOutput, env=self.__getEnv(), ceParameters=self.ceParameters
420
+ )
409
421
 
410
422
  self.__runningJobs -= 1
411
423
 
@@ -417,7 +429,9 @@ class SingularityComputingElement(ComputingElement):
417
429
  self.log.error(f"Singularity env was: {self.__getEnv()}")
418
430
  debugCmd = [outerCmd[0], "--debug"] + outerCmd[1:] + ["echo", "All okay"]
419
431
  self.log.error("Running with debug output to facilitate debugging", debugCmd)
420
- result = systemCall(0, debugCmd, callbackFunction=self.sendOutput, env=self.__getEnv())
432
+ result = CG2Manager().systemCall(
433
+ 0, debugCmd, callbackFunction=self.sendOutput, env=self.__getEnv(), ceParameters=self.ceParameters
434
+ )
421
435
  if proxy and renewTask:
422
436
  gThreadScheduler.removeTask(renewTask)
423
437
  self.__deleteWorkArea(baseDir)
@@ -2,6 +2,7 @@
2
2
  """
3
3
  tests for HTCondorCEComputingElement module
4
4
  """
5
+ import json
5
6
  import uuid
6
7
 
7
8
  import pytest
@@ -12,18 +13,30 @@ from DIRAC.Resources.Computing.BatchSystems import Condor
12
13
 
13
14
  MODNAME = "DIRAC.Resources.Computing.HTCondorCEComputingElement"
14
15
 
15
- STATUS_LINES = """
16
- 123.2 5 4 0 undefined
17
- 123.1 3 undefined undefined undefined
18
- """.strip().split(
19
- "\n"
20
- )
21
-
22
- HISTORY_LINES = """
23
- 123.0 4 undefined undefined undefined
24
- """.strip().split(
25
- "\n"
26
- )
16
+ STATUS_QUEUE = [
17
+ {
18
+ "ClusterId": 123,
19
+ "ProcId": 2,
20
+ "JobStatus": 5,
21
+ "HoldReasonCode": 4,
22
+ "HoldReasonSubCode": 0,
23
+ "HoldReason": "The credentials for the job are invalid",
24
+ },
25
+ {
26
+ "ClusterId": 123,
27
+ "ProcId": 1,
28
+ "JobStatus": 3,
29
+ },
30
+ ]
31
+
32
+
33
+ STATUS_HISTORY = [
34
+ {
35
+ "ClusterId": 123,
36
+ "ProcId": 0,
37
+ "JobStatus": 4,
38
+ }
39
+ ]
27
40
 
28
41
 
29
42
  @pytest.fixture
@@ -32,42 +45,47 @@ def setUp():
32
45
 
33
46
 
34
47
  def test_parseCondorStatus():
35
- statusLines = f"""
36
- 104098.1 1 undefined undefined undefined
37
- 104098.2 2 undefined undefined undefined
38
- 104098.3 3 undefined undefined undefined
39
- 104098.4 4 undefined undefined undefined
40
- 104098.5 5 16 57 Input data are being spooled
41
- 104098.6 5 3 {Condor.HOLD_REASON_SUBCODE} Policy
42
- 104098.7 5 1 0 undefined
43
-
44
- foo bar
45
- 104096.1 3 16 test test
46
- 104096.2 3 test
47
- 104096.3 5 undefined undefined undefined
48
- 104096.4 7
49
- """.strip().split(
50
- "\n"
51
- )
52
- # force there to be an empty line
48
+ statusOutput = {"ClusterId": 104098, "ProcId": 1, "JobStatus": 1}
49
+ assert HTCE.getCondorStatus(statusOutput) == ("Waiting", "")
53
50
 
54
- expectedResults = {
55
- "104098.1": "Waiting",
56
- "104098.2": "Running",
57
- "104098.3": "Aborted",
58
- "104098.4": "Done",
59
- "104098.5": "Waiting",
60
- "104098.6": "Failed",
61
- "104098.7": "Aborted",
62
- "foo": "Unknown",
63
- "104096.1": "Aborted",
64
- "104096.2": "Aborted",
65
- "104096.3": "Aborted",
66
- "104096.4": "Unknown",
67
- }
51
+ statusOutput = {"ClusterId": 104098, "ProcId": 2, "JobStatus": 2}
52
+ assert HTCE.getCondorStatus(statusOutput) == ("Running", "")
53
+
54
+ statusOutput = {"ClusterId": 104098, "ProcId": 3, "JobStatus": 3}
55
+ assert HTCE.getCondorStatus(statusOutput) == ("Aborted", "")
56
+
57
+ statusOutput = {"ClusterId": 104098, "ProcId": 4, "JobStatus": 4}
58
+ assert HTCE.getCondorStatus(statusOutput) == ("Done", "")
68
59
 
69
- for jobID, expected in expectedResults.items():
70
- assert HTCE.parseCondorStatus(statusLines, jobID)[0] == expected
60
+ statusOutput = {
61
+ "ClusterId": 104098,
62
+ "ProcId": 5,
63
+ "JobStatus": 5,
64
+ "HoldReasonCode": 16,
65
+ "HoldReasonSubCode": 57,
66
+ "HoldReason": "Input data are being spooled",
67
+ }
68
+ assert HTCE.getCondorStatus(statusOutput) == ("Waiting", "Input data are being spooled")
69
+
70
+ statusOutput = {
71
+ "ClusterId": 104098,
72
+ "ProcId": 6,
73
+ "JobStatus": 5,
74
+ "HoldReasonCode": 3,
75
+ "HoldReasonSubCode": HTCE.HOLD_REASON_SUBCODE,
76
+ "HoldReason": "Policy",
77
+ }
78
+ assert HTCE.getCondorStatus(statusOutput) == ("Failed", "Policy")
79
+
80
+ statusOutput = {
81
+ "ClusterId": 104098,
82
+ "ProcId": 7,
83
+ "JobStatus": 5,
84
+ "HoldReasonCode": 1,
85
+ "HoldReasonSubCode": 0,
86
+ "HoldReason": "Aborted by user",
87
+ }
88
+ assert HTCE.getCondorStatus(statusOutput) == ("Aborted", "Aborted by user")
71
89
 
72
90
 
73
91
  def test_getJobStatus(mocker):
@@ -75,8 +93,8 @@ def test_getJobStatus(mocker):
75
93
  mocker.patch(
76
94
  MODNAME + ".systemCall",
77
95
  side_effect=[
78
- S_OK((0, "\n".join(STATUS_LINES), "")),
79
- S_OK((0, "\n".join(HISTORY_LINES), "")),
96
+ S_OK((0, json.dumps(STATUS_QUEUE), "")),
97
+ S_OK((0, json.dumps(STATUS_HISTORY), "")),
80
98
  S_OK((0, "", "")),
81
99
  S_OK((0, "", "")),
82
100
  ],
@@ -110,7 +128,7 @@ def test_getJobStatus(mocker):
110
128
  def test_getJobStatusBatchSystem(mocker):
111
129
  """Test Condor Batch System plugin getJobStatus"""
112
130
  patchPopen = mocker.patch("DIRAC.Resources.Computing.BatchSystems.Condor.subprocess.Popen")
113
- patchPopen.return_value.communicate.side_effect = [("\n".join(STATUS_LINES), ""), ("\n".join(HISTORY_LINES), "")]
131
+ patchPopen.return_value.communicate.side_effect = [(json.dumps(STATUS_QUEUE), ""), (json.dumps(STATUS_HISTORY), "")]
114
132
  patchPopen.return_value.returncode = 0
115
133
 
116
134
  ret = Condor.Condor().getJobStatus(JobIDList=["123.0", "123.1", "123.2", "333.3"])
@@ -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
 
@@ -26,3 +26,16 @@ class CheckInIdProvider(OAuth2IdProvider):
26
26
 
27
27
  idPScope = f"eduperson_entitlement?value=urn:mace:egi.eu:group:{vo}:role={groupElements[1]}#aai.egi.eu"
28
28
  return scope_to_list(idPScope)
29
+
30
+ def fetchToken(self, **kwargs):
31
+ """Fetch token
32
+
33
+ :param kwargs:
34
+ :return: dict
35
+ """
36
+
37
+ if "audience" in kwargs:
38
+ kwargs["resource"] = kwargs["audience"]
39
+ kwargs.pop("audience")
40
+
41
+ return super().fetchToken(**kwargs)
@@ -1,6 +1,7 @@
1
- """ The Identity Provider Factory instantiates IdProvider objects
2
- according to their configuration
1
+ """The Identity Provider Factory instantiates IdProvider objects
2
+ according to their configuration
3
3
  """
4
+
4
5
  import jwt
5
6
 
6
7
  from DIRAC import S_OK, S_ERROR, gLogger, gConfig
@@ -40,11 +41,12 @@ class IdProviderFactory:
40
41
  return result
41
42
  return self.getIdProvider(result["Value"])
42
43
 
43
- def getIdProvider(self, name, **kwargs):
44
+ def getIdProvider(self, name, client_name_prefix="", **kwargs):
44
45
  """This method returns a IdProvider instance corresponding to the supplied
45
46
  name.
46
47
 
47
48
  :param str name: the name of the Identity Provider client
49
+ :param str client_name_prefix: name of the client of the IdP
48
50
 
49
51
  :return: S_OK(IdProvider)/S_ERROR()
50
52
  """
@@ -68,8 +70,14 @@ class IdProviderFactory:
68
70
  if not result["OK"]:
69
71
  self.log.error("Failed to read configuration", f"{name}: {result['Message']}")
70
72
  return result
73
+
71
74
  pDict = result["Value"]
72
75
 
76
+ if client_name_prefix:
77
+ client_name_prefix = client_name_prefix + "_"
78
+ pDict["client_id"] = pDict[f"{client_name_prefix}client_id"]
79
+ pDict["client_secret"] = pDict[f"{client_name_prefix}client_secret"]
80
+
73
81
  pDict.update(kwargs)
74
82
  pDict["ProviderName"] = name
75
83
 
@@ -172,7 +172,7 @@ class StompMQConnector(MQConnector):
172
172
  try:
173
173
  try:
174
174
  self.connection.send(body=json.dumps(message), destination=destination)
175
- except stomp.exception.StompException:
175
+ except (stomp.exception.StompException, ConnectionError):
176
176
  self.connect()
177
177
  self.connection.send(body=json.dumps(message), destination=destination)
178
178
  except Exception as e:
@@ -142,9 +142,11 @@ class GFAL2_StorageBase(StorageBase):
142
142
  # Workaround for https://github.com/xrootd/xrootd/issues/2396
143
143
  # xrootd internaly sets nproc, so we save the limit and reset it
144
144
  # just after creating the context
145
- saved_limits = getrlimit(RLIMIT_NPROC)
145
+ saved_limit = getrlimit(RLIMIT_NPROC)
146
146
  self.ctx = gfal2.creat_context()
147
- setrlimit(RLIMIT_NPROC, saved_limits)
147
+ new_limit = getrlimit(RLIMIT_NPROC)
148
+ if saved_limit != new_limit:
149
+ setrlimit(RLIMIT_NPROC, saved_limit)
148
150
 
149
151
  # by default turn off BDII checks
150
152
  self.ctx.set_opt_boolean("BDII", "ENABLE", False)
@@ -163,6 +165,10 @@ class GFAL2_StorageBase(StorageBase):
163
165
  os.environ.get("DIRAC_GFAL_GRIDFTP_ENABLE_IPV6", "true").lower() not in ["false", "no"],
164
166
  )
165
167
 
168
+ # Disable retrieving the bearer token for every operations.
169
+ # It is only useful for TPC
170
+ self.ctx.set_opt_boolean("HTTP PLUGIN", "RETRIEVE_BEARER_TOKEN", False)
171
+
166
172
  # spaceToken used for copying from and to the storage element
167
173
  self.spaceToken = parameters.get("SpaceToken", "")
168
174
  # stageTimeout, default timeout to try and stage/pin a file
@@ -303,20 +309,23 @@ class GFAL2_StorageBase(StorageBase):
303
309
  failed = {}
304
310
  successful = {}
305
311
 
306
- for dest_url, src_file in urls.items():
307
- if not src_file:
308
- errStr = "GFAL2_StorageBase.putFile: Source file not set. Argument must be a dictionary \
309
- (or a list of a dictionary) {url : local path}"
310
- self.log.debug(errStr)
311
- failed[dest_url] = errStr
312
- continue
312
+ # In principle we only need the bearer token when doing TPC, however it's a
313
+ # bit cumbersome to test, so we always re-enable it when uploading
314
+ with setGfalSetting(self.ctx, "HTTP PLUGIN", "RETRIEVE_BEARER_TOKEN", True):
315
+ for dest_url, src_file in urls.items():
316
+ if not src_file:
317
+ errStr = "GFAL2_StorageBase.putFile: Source file not set. Argument must be a dictionary \
318
+ (or a list of a dictionary) {url : local path}"
319
+ self.log.debug(errStr)
320
+ failed[dest_url] = errStr
321
+ continue
313
322
 
314
- try:
315
- successful[dest_url] = self._putSingleFile(src_file, dest_url, sourceSize)
316
- except (gfal2.GError, ValueError, RuntimeError) as e:
317
- detailMsg = f"Failed to copy {src_file} to {dest_url}: {repr(e)}"
318
- self.log.debug("Exception while copying", detailMsg)
319
- failed[dest_url] = detailMsg
323
+ try:
324
+ successful[dest_url] = self._putSingleFile(src_file, dest_url, sourceSize)
325
+ except (gfal2.GError, ValueError, RuntimeError) as e:
326
+ detailMsg = f"Failed to copy {src_file} to {dest_url}: {repr(e)}"
327
+ self.log.debug("Exception while copying", detailMsg)
328
+ failed[dest_url] = detailMsg
320
329
 
321
330
  return {"Failed": failed, "Successful": successful}
322
331
 
@@ -41,9 +41,7 @@ class WLCGAccountingHTTPJson(WLCGAccountingJson):
41
41
  """
42
42
  try:
43
43
  with open(filePath, "w") as fd:
44
- caPath = getCAsLocation()
45
- userProxy = getProxyLocation()
46
- res = requests.get(occupancyLFN, cert=userProxy, verify=caPath, timeout=30)
44
+ res = requests.get(occupancyLFN, cert=getProxyLocation(), verify=getCAsLocation(), timeout=30)
47
45
  res.raise_for_status()
48
46
  fd.write(res.text)
49
47
  except Exception as e:
@@ -1,4 +1,4 @@
1
- """ Base Storage Class provides the base interface for all storage plug-ins
1
+ """Base Storage Class provides the base interface for all storage plug-ins
2
2
 
3
3
  exists()
4
4
 
@@ -33,6 +33,7 @@ These are the methods for getting information about the Storage:
33
33
  getOccupancy()
34
34
 
35
35
  """
36
+
36
37
  import errno
37
38
  import json
38
39
  import os
@@ -113,7 +114,8 @@ class StorageBase:
113
114
 
114
115
  def getParameters(self):
115
116
  """Get the parameters with which the storage was instantiated"""
116
- parameterDict = dict(self.protocolParameters)
117
+ parameterDict = dict(self._allProtocolParameters)
118
+ parameterDict.update(self.protocolParameters)
117
119
  parameterDict["StorageName"] = self.name
118
120
  parameterDict["PluginName"] = self.pluginName
119
121
  parameterDict["URLBase"] = self.getURLBase().get("Value", "")
@@ -65,14 +65,13 @@ class StorageElementCache:
65
65
  # Because the gfal2 context caches the proxy location,
66
66
  # we also use the proxy location as a key.
67
67
  # In practice, there should almost always be one, except for the REA
68
- # If we see its memory consumtpion exploding, this might be a place to look
69
- proxyLoc = getProxyLocation()
68
+ # If we see its memory consumption exploding, this might be a place to look
70
69
 
71
70
  # ensure protocolSections is hashable! (tuple)
72
71
  if isinstance(protocolSections, list):
73
72
  protocolSections = tuple(protocolSections)
74
73
 
75
- argTuple = (tId, name, protocolSections, vo, proxyLoc)
74
+ argTuple = (tId, name, protocolSections, vo, getProxyLocation())
76
75
  seObj = self.seCache.get(argTuple)
77
76
 
78
77
  if not seObj:
@@ -439,10 +438,6 @@ class StorageElementItem:
439
438
 
440
439
  kwargs["occupancyLFN"] = occupancyLFN
441
440
 
442
- filteredPlugins = self.__filterPlugins("getOccupancy")
443
- if not filteredPlugins:
444
- return S_ERROR(errno.EPROTONOSUPPORT, "No storage plugins to query the occupancy")
445
-
446
441
  # Call occupancy plugin if requested
447
442
  occupancyPlugin = self.options.get("OccupancyPlugin")
448
443
  if occupancyPlugin:
@@ -460,6 +455,10 @@ class StorageElementItem:
460
455
  except Exception as e:
461
456
  return S_ERROR(f"Occupancy plugin failed: {str(e)}")
462
457
 
458
+ filteredPlugins = self.__filterPlugins("getOccupancy")
459
+ if not filteredPlugins:
460
+ return S_ERROR(errno.EPROTONOSUPPORT, "No storage plugins to query the occupancy")
461
+
463
462
  # Try all of the storages one by one
464
463
  for storage in filteredPlugins:
465
464
  # The result of the plugin is always in B
@@ -23,7 +23,7 @@ CREATE TABLE Tasks(
23
23
  `CompleteTime` DATETIME,
24
24
  `CallBackMethod` VARCHAR(255),
25
25
  `SourceTaskID` VARCHAR(32),
26
- PRIMARY KEY(`TaskID`,`Status`),
26
+ PRIMARY KEY(`TaskID`),
27
27
  INDEX(`TaskID`,`Status`)
28
28
  )ENGINE=INNODB;
29
29
 
@@ -41,7 +41,7 @@ CREATE TABLE CacheReplicas(
41
41
  `LastUpdate` DATETIME,
42
42
  `Reason` VARCHAR(255),
43
43
  `Links` INTEGER DEFAULT 0,
44
- PRIMARY KEY (`ReplicaID`,`LFN`,`SE`),
44
+ PRIMARY KEY (`ReplicaID`),
45
45
  INDEX(`ReplicaID`,`Status`,`SE`)
46
46
  )ENGINE=INNODB;
47
47
 
@@ -7,23 +7,21 @@
7
7
  In case you want to further extend it you are required to follow the note on the
8
8
  initialize method and on the _getClients method.
9
9
  """
10
- import time
11
- import datetime
12
10
  import concurrent.futures
11
+ import datetime
12
+ import time
13
13
 
14
- from DIRAC import S_OK
15
-
14
+ from DIRAC import S_OK, gConfig
15
+ from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
16
16
  from DIRAC.Core.Base.AgentModule import AgentModule
17
17
  from DIRAC.Core.Security.ProxyInfo import getProxyInfo
18
- from DIRAC.Core.Utilities.List import breakListIntoChunks
19
18
  from DIRAC.Core.Utilities.Dictionaries import breakDictionaryIntoChunks
20
- from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
19
+ from DIRAC.Core.Utilities.List import breakListIntoChunks
20
+ from DIRAC.TransformationSystem.Agent.TransformationAgentsUtilities import TransformationAgentsUtilities
21
21
  from DIRAC.TransformationSystem.Client.FileReport import FileReport
22
- from DIRAC.TransformationSystem.Client.WorkflowTasks import WorkflowTasks
23
22
  from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
24
- from DIRAC.TransformationSystem.Agent.TransformationAgentsUtilities import TransformationAgentsUtilities
23
+ from DIRAC.TransformationSystem.Client.WorkflowTasks import WorkflowTasks
25
24
  from DIRAC.WorkloadManagementSystem.Client import JobStatus
26
- from DIRAC.WorkloadManagementSystem.Client.JobManagerClient import JobManagerClient
27
25
 
28
26
  AGENT_NAME = "Transformation/TaskManagerAgentBase"
29
27
 
@@ -40,7 +38,6 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
40
38
  TransformationAgentsUtilities.__init__(self)
41
39
 
42
40
  self.transClient = None
43
- self.jobManagerClient = None
44
41
  self.transType = []
45
42
 
46
43
  self.tasksPerLoop = 50
@@ -69,7 +66,6 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
69
66
 
70
67
  # Default clients
71
68
  self.transClient = TransformationClient()
72
- self.jobManagerClient = JobManagerClient()
73
69
 
74
70
  # Bulk submission flag
75
71
  self.bulkSubmissionFlag = self.am_getOption("BulkSubmission", self.bulkSubmissionFlag)
@@ -193,11 +189,9 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
193
189
  else:
194
190
  # Get the transformations which should be submitted
195
191
  self.tasksPerLoop = self.am_getOption("TasksPerLoop", self.tasksPerLoop)
196
- res = self.jobManagerClient.getMaxParametricJobs()
197
- if not res["OK"]:
198
- self.log.warn("Could not get the maxParametricJobs from JobManager", res["Message"])
199
- else:
200
- self.maxParametricJobs = res["Value"]
192
+ self.maxParametricJobs = gConfig.getValue(
193
+ "/Systems/WorkloadManagement/Services/JobManager/MaxParametricJobs", self.maxParametricJobs
194
+ )
201
195
 
202
196
  self._addOperationForTransformations(
203
197
  self.operationsOnTransformationDict,
@@ -1,4 +1,4 @@
1
- """ TransformationAgent processes transformations found in the transformation database.
1
+ """TransformationAgent processes transformations found in the transformation database.
2
2
 
3
3
  The following options can be set for the TransformationAgent.
4
4
 
@@ -8,6 +8,7 @@ The following options can be set for the TransformationAgent.
8
8
  :dedent: 2
9
9
  :caption: TransformationAgent options
10
10
  """
11
+
11
12
  from importlib import import_module
12
13
 
13
14
  import time
@@ -15,6 +16,7 @@ import os
15
16
  import datetime
16
17
  import pickle
17
18
  import concurrent.futures
19
+ from pathlib import Path
18
20
 
19
21
  from DIRAC import S_OK, S_ERROR
20
22
  from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
@@ -127,6 +129,9 @@ class TransformationAgent(AgentModule, TransformationAgentsUtilities):
127
129
  if not res["OK"]:
128
130
  self._logError("Failed to obtain transformations:", res["Message"])
129
131
  return S_OK()
132
+
133
+ active_trans_ids = [t["TransformationID"] for t in res["Value"]]
134
+ self.cleanOldTransformationCache(active_trans_ids)
130
135
  # Process the transformations
131
136
  count = 0
132
137
  future_to_transID = {}
@@ -164,6 +169,22 @@ class TransformationAgent(AgentModule, TransformationAgentsUtilities):
164
169
 
165
170
  return S_OK()
166
171
 
172
+ def cleanOldTransformationCache(self, active_trans_ids: list[int]):
173
+ cache_filenames = {Path(self.__cacheFile(tid)) for tid in active_trans_ids}
174
+ existing_caches = set(Path(self.workDirectory).glob("*.pkl"))
175
+ useless_cache_files = existing_caches - cache_filenames
176
+
177
+ if useless_cache_files:
178
+ self._logInfo(f"Found potentially {len(useless_cache_files)} useless cache files")
179
+
180
+ # Since idle transformations aren't in active_trans_ids, let's filter it more
181
+ # and take only files that haven't been touched for 2 month
182
+ last_update_threshold = (datetime.datetime.utcnow() - datetime.timedelta(days=60)).timestamp()
183
+
184
+ for cache_file in useless_cache_files:
185
+ if Path(cache_file).stat().st_mtime < last_update_threshold:
186
+ cache_file.unlink()
187
+
167
188
  def getTransformations(self):
168
189
  """Obtain the transformations to be executed - this is executed at the start of every loop (it's really the
169
190
  only real thing in the execute()