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
@@ -17,6 +17,7 @@ def main():
17
17
  remove = False
18
18
  site = ""
19
19
  mute = False
20
+ userName = ""
20
21
 
21
22
  Script.registerSwitch("r", "AllowRead", " Allow only reading from the storage element")
22
23
  Script.registerSwitch("w", "AllowWrite", " Allow only writing to the storage element")
@@ -25,6 +26,7 @@ def main():
25
26
  Script.registerSwitch("a", "All", " Allow all access to the storage element")
26
27
  Script.registerSwitch("m", "Mute", " Do not send email")
27
28
  Script.registerSwitch("S:", "Site=", " Allow all SEs associated to site")
29
+ Script.registerSwitch("t:", "tokenOwner=", " Optional Name of the token owner")
28
30
  # Registering arguments will automatically add their description to the help menu
29
31
  Script.registerArgument(["seGroupList: list of SEs or comma-separated SEs"])
30
32
 
@@ -48,6 +50,8 @@ def main():
48
50
  mute = True
49
51
  if switch[0].lower() in ("s", "site"):
50
52
  site = switch[1]
53
+ if switch[0] in ("t", "tokenOwner"):
54
+ userName = switch[1]
51
55
 
52
56
  # imports
53
57
  from DIRAC import gLogger
@@ -69,15 +73,16 @@ def main():
69
73
  ses = resolveSEGroup(ses)
70
74
  diracAdmin = DiracAdmin()
71
75
 
72
- res = getProxyInfo()
73
- if not res["OK"]:
74
- gLogger.error("Failed to get proxy information", res["Message"])
75
- DIRAC.exit(2)
76
-
77
- userName = res["Value"].get("username")
78
76
  if not userName:
79
- gLogger.error("Failed to get username for proxy")
80
- DIRAC.exit(2)
77
+ res = getProxyInfo()
78
+ if not res["OK"]:
79
+ gLogger.error("Failed to get proxy information", res["Message"])
80
+ DIRAC.exit(2)
81
+
82
+ userName = res["Value"].get("username")
83
+ if not userName:
84
+ gLogger.error("Failed to get username for proxy")
85
+ DIRAC.exit(2)
81
86
 
82
87
  if site:
83
88
  res = getSites()
@@ -18,6 +18,7 @@ def main():
18
18
  remove = True
19
19
  sites = []
20
20
  mute = False
21
+ userName = ""
21
22
 
22
23
  Script.registerSwitch("r", "BanRead", " Ban only reading from the storage element")
23
24
  Script.registerSwitch("w", "BanWrite", " Ban writing to the storage element")
@@ -28,6 +29,7 @@ def main():
28
29
  Script.registerSwitch(
29
30
  "S:", "Site=", " Ban all SEs associate to site (note that if writing is allowed, check is always allowed)"
30
31
  )
32
+ Script.registerSwitch("t:", "tokenOwner=", " Optional Name of the token owner")
31
33
  # Registering arguments will automatically add their description to the help menu
32
34
  Script.registerArgument(["seGroupList: list of SEs or comma-separated SEs"])
33
35
 
@@ -56,6 +58,8 @@ def main():
56
58
  mute = True
57
59
  if switch[0].lower() in ("s", "site"):
58
60
  sites = switch[1].split(",")
61
+ if switch[0] in ("t", "tokenOwner"):
62
+ userName = switch[1]
59
63
 
60
64
  # from DIRAC.ConfigurationSystem.Client.CSAPI import CSAPI
61
65
  from DIRAC import gLogger
@@ -68,15 +72,16 @@ def main():
68
72
  ses = resolveSEGroup(ses)
69
73
  diracAdmin = DiracAdmin()
70
74
 
71
- res = getProxyInfo()
72
- if not res["OK"]:
73
- gLogger.error("Failed to get proxy information", res["Message"])
74
- DIRAC.exit(2)
75
-
76
- userName = res["Value"].get("username")
77
75
  if not userName:
78
- gLogger.error("Failed to get username for proxy")
79
- DIRAC.exit(2)
76
+ res = getProxyInfo()
77
+ if not res["OK"]:
78
+ gLogger.error("Failed to get proxy information", res["Message"])
79
+ DIRAC.exit(2)
80
+
81
+ userName = res["Value"].get("username")
82
+ if not userName:
83
+ gLogger.error("Failed to get username for proxy")
84
+ DIRAC.exit(2)
80
85
 
81
86
  for site in sites:
82
87
  res = DMSHelpers().getSEsForSite(site)
@@ -41,6 +41,7 @@ class CreateMovingRequest:
41
41
  self.flags = [
42
42
  ("C", "CheckMigration", "Ensure the LFNs are migrated to tape before removing any replicas"),
43
43
  ("X", "Execute", "Put Requests, else dryrun"),
44
+ ("", "SourceOnly", "Only treat files that are already at the Source-SE"),
44
45
  ]
45
46
  self.registerSwitchesAndParseCommandLine()
46
47
  self.getLFNList()
@@ -208,6 +209,7 @@ class CreateMovingRequest:
208
209
 
209
210
  replicate = Operation()
210
211
  replicate.Type = "ReplicateAndRegister"
212
+ replicate.SourceSE = ",".join(self.switches.get("SourceSE", []))
211
213
  replicate.TargetSE = self.switches.get("TargetSE")
212
214
  self.addLFNs(replicate, lfnChunk, addPFN=True)
213
215
  request.addOperation(replicate)
@@ -177,7 +177,6 @@ def main():
177
177
  for src, dst in ((x, y) for x in fromSE for y in targetSE):
178
178
  if ftsTab:
179
179
  try:
180
- # breakpoint()
181
180
  fts3TpcProto = fts3Plugin.selectTPCProtocols(sourceSEName=ses[src].name, destSEName=ses[dst].name)
182
181
  res = ses[dst].generateTransferURLsBetweenSEs(lfn, ses[src], fts3TpcProto)
183
182
  except ValueError as e:
@@ -143,13 +143,9 @@ class BundleDeliveryClient(Client):
143
143
  if "X509_CERT_DIR" in os.environ:
144
144
  X509_CERT_DIR = os.environ["X509_CERT_DIR"]
145
145
  del os.environ["X509_CERT_DIR"]
146
- casLocation = Locations.getCAsLocation()
147
- if not casLocation:
148
- casLocation = Locations.getCAsDefaultLocation()
149
- result = self.syncDir("CAs", casLocation)
150
146
  if X509_CERT_DIR:
151
147
  os.environ["X509_CERT_DIR"] = X509_CERT_DIR
152
- return result
148
+ return self.syncDir("CAs", Locations.getCAsLocation())
153
149
 
154
150
  def syncCRLs(self):
155
151
  """Synchronize CRLs
@@ -160,10 +156,9 @@ class BundleDeliveryClient(Client):
160
156
  if "X509_CERT_DIR" in os.environ:
161
157
  X509_CERT_DIR = os.environ["X509_CERT_DIR"]
162
158
  del os.environ["X509_CERT_DIR"]
163
- result = self.syncDir("CRLs", Locations.getCAsLocation())
164
159
  if X509_CERT_DIR:
165
160
  os.environ["X509_CERT_DIR"] = X509_CERT_DIR
166
- return result
161
+ return self.syncDir("CRLs", Locations.getCAsLocation())
167
162
 
168
163
  def getCAs(self):
169
164
  """This method can be used to create the CAs. If the file can not be created,
@@ -60,7 +60,6 @@ from collections import defaultdict
60
60
 
61
61
  import importlib_metadata as metadata
62
62
  import importlib_resources
63
- import MySQLdb
64
63
  from diraccfg import CFG
65
64
  from prompt_toolkit import prompt
66
65
 
@@ -69,7 +68,6 @@ from DIRAC import gConfig, gLogger, rootPath
69
68
  from DIRAC.ConfigurationSystem.Client import PathFinder
70
69
  from DIRAC.ConfigurationSystem.Client.CSAPI import CSAPI
71
70
  from DIRAC.ConfigurationSystem.Client.Helpers import (
72
- CSGlobals,
73
71
  cfgInstallPath,
74
72
  cfgInstallSection,
75
73
  cfgPath,
@@ -85,6 +83,7 @@ from DIRAC.Core.Security.Properties import (
85
83
  PRODUCTION_MANAGEMENT,
86
84
  PROXY_MANAGEMENT,
87
85
  SERVICE_ADMINISTRATOR,
86
+ SITE_MANAGER,
88
87
  TRUSTED_HOST,
89
88
  )
90
89
  from DIRAC.Core.Utilities.Extensions import (
@@ -96,7 +95,6 @@ from DIRAC.Core.Utilities.Extensions import (
96
95
  findServices,
97
96
  )
98
97
  from DIRAC.Core.Utilities.File import mkDir, mkLink
99
- from DIRAC.Core.Utilities.MySQL import MySQL
100
98
  from DIRAC.Core.Utilities.PrettyPrint import printTable
101
99
  from DIRAC.Core.Utilities.ReturnValues import S_ERROR, S_OK
102
100
  from DIRAC.Core.Utilities.Subprocess import systemCall
@@ -432,6 +430,8 @@ class ComponentInstaller:
432
430
  defaultHostProperties = [
433
431
  TRUSTED_HOST,
434
432
  CS_ADMINISTRATOR,
433
+ SERVICE_ADMINISTRATOR,
434
+ SITE_MANAGER,
435
435
  JOB_ADMINISTRATOR,
436
436
  FULL_DELEGATION,
437
437
  PROXY_MANAGEMENT,
@@ -2053,6 +2053,8 @@ class ComponentInstaller:
2053
2053
  """
2054
2054
  Install requested DB in MySQL server
2055
2055
  """
2056
+ import MySQLdb
2057
+
2056
2058
  dbName = MySQLdb.escape_string(dbName.encode()).decode()
2057
2059
  if not self.mysqlRootPwd:
2058
2060
  rootPwdPath = cfgInstallPath("Database", "RootPwd")
@@ -2106,7 +2108,8 @@ class ComponentInstaller:
2106
2108
 
2107
2109
  perms = (
2108
2110
  "SELECT,INSERT,LOCK TABLES,UPDATE,DELETE,CREATE,DROP,ALTER,REFERENCES,"
2109
- "CREATE VIEW,SHOW VIEW,INDEX,TRIGGER,ALTER ROUTINE,CREATE ROUTINE"
2111
+ "CREATE VIEW,SHOW VIEW,INDEX,TRIGGER,ALTER ROUTINE,CREATE ROUTINE,"
2112
+ "CREATE TEMPORARY TABLES"
2110
2113
  )
2111
2114
  cmd = f"GRANT {perms} ON `{dbName}`.* TO '{self.mysqlUser}'@'%'"
2112
2115
  result = self.execMySQL(cmd)
@@ -2199,6 +2202,8 @@ class ComponentInstaller:
2199
2202
  """
2200
2203
  Execute MySQL Command
2201
2204
  """
2205
+ from DIRAC.Core.Utilities.MySQL import MySQL
2206
+
2202
2207
  if not self.mysqlRootPwd:
2203
2208
  return S_ERROR("MySQL root password is not defined")
2204
2209
  if dbName not in self.db:
@@ -418,7 +418,7 @@ class ProxyManagerClient(metaclass=DIRACSingleton.DIRACSingleton):
418
418
  proxyToConnect=proxyToConnect,
419
419
  )
420
420
 
421
- def dumpProxyToFile(self, chain, destinationFile=None, requiredTimeLeft=600):
421
+ def dumpProxyToFile(self, chain, destinationFile=None, requiredTimeLeft=600, includeToken=True):
422
422
  """Dump a proxy to a file. It's cached so multiple calls won't generate extra files
423
423
 
424
424
  :param X509Chain chain: proxy as a chain
@@ -442,7 +442,10 @@ class ProxyManagerClient(metaclass=DIRACSingleton.DIRACSingleton):
442
442
  filename = retVal["Value"]
443
443
  if not (result := chain.getDIRACGroup())["OK"]:
444
444
  return result
445
- if not (result := addTokenToPEM(filename, result["Value"]))["OK"]: # pylint: disable=unsubscriptable-object
445
+ if (
446
+ includeToken
447
+ and not (result := addTokenToPEM(filename, result["Value"]))["OK"] # pylint: disable=unsubscriptable-object
448
+ ):
446
449
  return result
447
450
  self.__filesCache.add(cHash, chain.getRemainingSecs()["Value"], filename)
448
451
  return S_OK(filename)
@@ -11,7 +11,6 @@ import sys
11
11
  import time
12
12
 
13
13
  from DIRAC import gConfig, gLogger
14
- from DIRAC.ConfigurationSystem.Client.Helpers import CSGlobals
15
14
  from DIRAC.Core.Base.CLI import CLI, colorize
16
15
  from DIRAC.Core.Security.ProxyInfo import getProxyInfo
17
16
  from DIRAC.Core.Utilities import List
@@ -623,6 +622,11 @@ class SystemAdministratorClientCLI(CLI):
623
622
  install agent <system> <agent> [-m <ModuleName>] [-p <Option>=<Value>] [-p <Option>=<Value>] ...
624
623
  install executor <system> <executor> [-m <ModuleName>] [-p <Option>=<Value>] [-p <Option>=<Value>] ...
625
624
  """
625
+ result = getProxyInfo()
626
+ if not result["OK"]:
627
+ self._errMsg(result["Message"])
628
+ user = result["Value"]["username"]
629
+
626
630
  argss = args.split()
627
631
  hostSetup = extension = None
628
632
  if not argss:
@@ -673,7 +677,7 @@ class SystemAdministratorClientCLI(CLI):
673
677
 
674
678
  if database != "InstalledComponentsDB":
675
679
  result = MonitoringUtilities.monitorInstallation(
676
- "DB", system.replace("System", ""), database, cpu=cpu, hostname=hostname
680
+ "DB", system.replace("System", ""), database, cpu=cpu, hostname=hostname, user=user
677
681
  )
678
682
  if not result["OK"]:
679
683
  self._errMsg(result["Message"])
@@ -786,14 +790,14 @@ class SystemAdministratorClientCLI(CLI):
786
790
  return
787
791
 
788
792
  result = MonitoringUtilities.monitorInstallation(
789
- "DB", system, "InstalledComponentsDB", cpu=cpu, hostname=hostname
793
+ "DB", system, "InstalledComponentsDB", cpu=cpu, hostname=hostname, user=user
790
794
  )
791
795
  if not result["OK"]:
792
796
  self._errMsg(f"Error registering installation into database: {result['Message']}")
793
797
  return
794
798
 
795
799
  result = MonitoringUtilities.monitorInstallation(
796
- option, system, component, module, cpu=cpu, hostname=hostname
800
+ option, system, component, module, cpu=cpu, hostname=hostname, user=user
797
801
  )
798
802
  if not result["OK"]:
799
803
  self._errMsg(f"Error registering installation into database: {result['Message']}")
@@ -820,6 +824,7 @@ class SystemAdministratorClientCLI(CLI):
820
824
  result = getProxyInfo()
821
825
  if not result["OK"]:
822
826
  self._errMsg(result["Message"])
827
+ user = result["Value"]["username"]
823
828
 
824
829
  option = argss[0]
825
830
  if option == "db":
@@ -842,7 +847,7 @@ class SystemAdministratorClientCLI(CLI):
842
847
  self._errMsg(result["Message"])
843
848
  return
844
849
  system = result["Value"][component]["System"]
845
- result = MonitoringUtilities.monitorUninstallation(system, component, hostname=hostname, cpu=cpu)
850
+ result = MonitoringUtilities.monitorUninstallation(system, component, hostname=hostname, cpu=cpu, user=user)
846
851
  if not result["OK"]:
847
852
  self._errMsg(result["Message"])
848
853
  return
@@ -937,7 +942,7 @@ class SystemAdministratorClientCLI(CLI):
937
942
  else:
938
943
  cpu = result["Value"]["CPUModel"]
939
944
  hostname = self.host
940
- result = MonitoringUtilities.monitorUninstallation(system, component, hostname=hostname, cpu=cpu)
945
+ result = MonitoringUtilities.monitorUninstallation(system, component, hostname=hostname, cpu=cpu, user=user)
941
946
  if not result["OK"]:
942
947
  return result
943
948
 
@@ -167,6 +167,7 @@ Services
167
167
  componentExists = authenticated
168
168
  getComponents = authenticated
169
169
  hostExists = authenticated
170
+ installationExists = authenticated
170
171
  getHosts = authenticated
171
172
  installationExists = authenticated
172
173
  getInstallations = authenticated
@@ -184,6 +185,7 @@ Services
184
185
  componentExists = authenticated
185
186
  getComponents = authenticated
186
187
  hostExists = authenticated
188
+ installationExists = authenticated
187
189
  getHosts = authenticated
188
190
  installationExists = authenticated
189
191
  getInstallations = authenticated
@@ -22,7 +22,7 @@ Model = declarative_base()
22
22
 
23
23
  class RefreshToken(Model):
24
24
  __tablename__ = "RefreshToken"
25
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
25
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
26
26
  jti = Column(String(255), nullable=False, primary_key=True)
27
27
  issued_at = Column(Integer, nullable=False, default=0)
28
28
  access_token = Column(Text, nullable=False)
@@ -31,7 +31,7 @@ class RefreshToken(Model):
31
31
 
32
32
  class JWK(Model):
33
33
  __tablename__ = "JWK"
34
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
34
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
35
35
  kid = Column(String(255), unique=True, primary_key=True, nullable=False)
36
36
  key = Column(Text, nullable=False)
37
37
  expires_at = Column(Integer, nullable=False, default=0)
@@ -39,7 +39,7 @@ class JWK(Model):
39
39
 
40
40
  class AuthSession(Model):
41
41
  __tablename__ = "AuthSession"
42
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
42
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
43
43
  id = Column(String(255), unique=True, primary_key=True, nullable=False)
44
44
  uri = Column(String(255))
45
45
  state = Column(String(255))
@@ -27,7 +27,7 @@ class Component(componentsBase):
27
27
  """
28
28
 
29
29
  __tablename__ = "Components"
30
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
30
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
31
31
 
32
32
  componentID = Column("ComponentID", Integer, primary_key=True)
33
33
  system = Column("DIRACSystem", String(32), nullable=False)
@@ -87,7 +87,7 @@ class Host(componentsBase):
87
87
  """
88
88
 
89
89
  __tablename__ = "Hosts"
90
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
90
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
91
91
 
92
92
  hostID = Column("HostID", Integer, primary_key=True)
93
93
  hostName = Column("HostName", String(32), nullable=False)
@@ -138,7 +138,7 @@ class InstalledComponent(componentsBase):
138
138
  """
139
139
 
140
140
  __tablename__ = "InstalledComponents"
141
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
141
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
142
142
 
143
143
  componentID = Column("ComponentID", Integer, ForeignKey("Components.ComponentID"), primary_key=True)
144
144
  hostID = Column("HostID", Integer, ForeignKey("Hosts.HostID"), primary_key=True)
@@ -217,7 +217,7 @@ class HostLogging(componentsBase):
217
217
  """
218
218
 
219
219
  __tablename__ = "HostLogging"
220
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
220
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
221
221
 
222
222
  hostName = Column("HostName", String(32), nullable=False, primary_key=True)
223
223
  # status
@@ -9,6 +9,9 @@
9
9
  * ProxyDB_Log -- table with logs.
10
10
  """
11
11
  import textwrap
12
+ from threading import Lock
13
+
14
+ from cachetools import TTLCache, cachedmethod
12
15
 
13
16
  from DIRAC import S_ERROR, S_OK, gLogger
14
17
  from DIRAC.ConfigurationSystem.Client.Helpers import Registry
@@ -22,6 +25,10 @@ from DIRAC.Resources.ProxyProvider.ProxyProviderFactory import ProxyProviderFact
22
25
 
23
26
  DEFAULT_MAIL_FROM = "proxymanager@diracgrid.org"
24
27
 
28
+ # Module-level cache for getProxyStrength method (shared across ProxyDB instances)
29
+ _get_proxy_strength_cache = TTLCache(maxsize=1000, ttl=600)
30
+ _get_proxy_strength_lock = Lock()
31
+
25
32
 
26
33
  class ProxyDB(DB):
27
34
  NOTIFICATION_TIMES = [2592000, 1296000]
@@ -395,6 +402,7 @@ class ProxyDB(DB):
395
402
  return S_ERROR(", ".join(errMsgs))
396
403
  return result
397
404
 
405
+ @cachedmethod(lambda self: _get_proxy_strength_cache, lock=lambda self: _get_proxy_strength_lock)
398
406
  def getProxyStrength(self, userDN, userGroup=None, vomsAttr=None):
399
407
  """Load the proxy in cache corresponding to the criteria, and check its strength
400
408
 
@@ -597,13 +605,13 @@ class ProxyDB(DB):
597
605
  :return: S_OK(dict)/S_ERROR() -- dict contain attribute and VOMS VO
598
606
  """
599
607
  if requiredVOMSAttribute:
600
- return S_OK({"attribute": requiredVOMSAttribute, "VOMSVO": Registry.getVOMSVOForGroup(userGroup)})
608
+ return S_OK({"attribute": requiredVOMSAttribute, "VO": Registry.getVOForGroup(userGroup)})
601
609
 
602
610
  csVOMSMapping = Registry.getVOMSAttributeForGroup(userGroup)
603
611
  if not csVOMSMapping:
604
612
  return S_ERROR(f"No mapping defined for group {userGroup} in the CS")
605
613
 
606
- return S_OK({"attribute": csVOMSMapping, "VOMSVO": Registry.getVOMSVOForGroup(userGroup)})
614
+ return S_OK({"attribute": csVOMSMapping, "VO": Registry.getVOForGroup(userGroup)})
607
615
 
608
616
  def getVOMSProxy(self, userDN, userGroup, requiredLifeTime=None, requestedVOMSAttr=None):
609
617
  """Get proxy string from the Proxy Repository for use with userDN
@@ -620,7 +628,7 @@ class ProxyDB(DB):
620
628
  if not retVal["OK"]:
621
629
  return retVal
622
630
  vomsAttr = retVal["Value"]["attribute"]
623
- vomsVO = retVal["Value"]["VOMSVO"]
631
+ vomsVO = retVal["Value"]["VO"]
624
632
 
625
633
  # Look in the cache
626
634
  retVal = self.__getPemAndTimeLeft(userDN, userGroup, vomsAttr)
@@ -24,7 +24,7 @@ class Token(Model, OAuth2TokenMixin):
24
24
  """This class describes token fields"""
25
25
 
26
26
  __tablename__ = "Token"
27
- __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8"}
27
+ __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
28
28
  # access_token too large for varchar(255)
29
29
  # 767 bytes is the stated prefix limitation for InnoDB tables in MySQL version 5.6
30
30
  # https://stackoverflow.com/questions/1827063/mysql-error-key-specification-without-a-key-length
@@ -1,11 +1,12 @@
1
- """ ProxyManager is the implementation of the ProxyManagement service in the DISET framework
1
+ """ProxyManager is the implementation of the ProxyManagement service in the DISET framework
2
2
 
3
- .. literalinclude:: ../ConfigTemplate.cfg
4
- :start-after: ##BEGIN ProxyManager:
5
- :end-before: ##END
6
- :dedent: 2
7
- :caption: ProxyManager options
3
+ .. literalinclude:: ../ConfigTemplate.cfg
4
+ :start-after: ##BEGIN ProxyManager:
5
+ :end-before: ##END
6
+ :dedent: 2
7
+ :caption: ProxyManager options
8
8
  """
9
+
9
10
  from DIRAC import S_ERROR, S_OK, gLogger
10
11
  from DIRAC.ConfigurationSystem.Client.Helpers import Registry
11
12
  from DIRAC.Core.DISET.RequestHandler import RequestHandler, getServiceOption
@@ -325,6 +326,7 @@ class ProxyManagerHandlerMixin:
325
326
  credDict["group"],
326
327
  set(credDict.get("groupProperties", []) + credDict.get("properties", [])),
327
328
  expires_minutes=credDict["secondsLeft"] // 60 + 1,
329
+ source="ProxyManager",
328
330
  )
329
331
 
330
332
 
@@ -6,10 +6,9 @@ import socket
6
6
 
7
7
  from DIRAC import S_OK
8
8
  from DIRAC.FrameworkSystem.Client.ComponentMonitoringClient import ComponentMonitoringClient
9
- from DIRAC.Core.Security.ProxyInfo import getProxyInfo
10
9
 
11
10
 
12
- def monitorInstallation(componentType, system, component, module=None, cpu=None, hostname=None):
11
+ def monitorInstallation(componentType, system, component, module=None, cpu=None, hostname=None, user=None):
13
12
  """
14
13
  Register the installation of a component in the InstalledComponentsDB
15
14
  """
@@ -19,14 +18,6 @@ def monitorInstallation(componentType, system, component, module=None, cpu=None,
19
18
  module = component
20
19
 
21
20
  # Retrieve user installing the component
22
- user = None
23
- result = getProxyInfo()
24
- if result["OK"]:
25
- proxyInfo = result["Value"]
26
- if "username" in proxyInfo:
27
- user = proxyInfo["username"]
28
- else:
29
- return result
30
21
  if not user:
31
22
  user = "unknown"
32
23
 
@@ -61,21 +52,13 @@ def monitorInstallation(componentType, system, component, module=None, cpu=None,
61
52
  return result
62
53
 
63
54
 
64
- def monitorUninstallation(system, component, cpu=None, hostname=None):
55
+ def monitorUninstallation(system, component, cpu=None, hostname=None, user=None):
65
56
  """
66
57
  Register the uninstallation of a component in the InstalledComponentsDB
67
58
  """
68
59
  monitoringClient = ComponentMonitoringClient()
69
60
 
70
61
  # Retrieve user uninstalling the component
71
- user = None
72
- result = getProxyInfo()
73
- if result["OK"]:
74
- proxyInfo = result["Value"]
75
- if "username" in proxyInfo:
76
- user = proxyInfo["username"]
77
- else:
78
- return result
79
62
  if not user:
80
63
  user = "unknown"
81
64
 
@@ -10,11 +10,12 @@ DEFAULT_RT_EXPIRATION_TIME = 24 * 3600
10
10
  DEFAULT_AT_EXPIRATION_TIME = 1200
11
11
 
12
12
 
13
- def getIdProviderClient(userGroup: str, idProviderClientName: str = None):
13
+ def getIdProviderClient(userGroup: str, idProviderClientName: str = None, client_name_prefix: str = ""):
14
14
  """Get an IdProvider client
15
15
 
16
16
  :param userGroup: group name
17
17
  :param idProviderClientName: name of an identity provider in the DIRAC CS
18
+ :param client_name_prefix: prefix of the client in the CS options
18
19
  """
19
20
  # Get IdProvider credentials from CS
20
21
  if not idProviderClientName and userGroup:
@@ -23,7 +24,7 @@ def getIdProviderClient(userGroup: str, idProviderClientName: str = None):
23
24
  return S_ERROR(f"The {userGroup} group belongs to the VO that is not tied to any Identity Provider.")
24
25
 
25
26
  # Prepare the client instance of the appropriate IdP
26
- return IdProviderFactory().getIdProvider(idProviderClientName)
27
+ return IdProviderFactory().getIdProvider(idProviderClientName, client_name_prefix=client_name_prefix)
27
28
 
28
29
 
29
30
  def getCachedKey(
@@ -1,28 +1,40 @@
1
1
  import requests
2
2
 
3
- from cachetools import TTLCache, cached
3
+ from cachetools import TTLCache, LRUCache, cached
4
4
  from cachetools.keys import hashkey
5
5
  from pathlib import Path
6
6
  from tempfile import NamedTemporaryFile
7
7
  from typing import Any
8
+ from collections.abc import Generator
8
9
  from DIRAC import gConfig
9
10
  from DIRAC.ConfigurationSystem.Client.Helpers import Registry
10
-
11
+ from contextlib import contextmanager
11
12
 
12
13
  from diracx.core.preferences import DiracxPreferences
13
14
 
14
15
  from diracx.core.utils import write_credentials
15
16
 
16
17
  from diracx.core.models import TokenResponse
17
- from diracx.client import DiracClient
18
+
19
+ try:
20
+ from diracx.client.sync import SyncDiracClient
21
+ except ImportError:
22
+ # TODO: Remove this once diracx is tagged
23
+ from diracx.client import DiracClient as SyncDiracClient
18
24
 
19
25
  # How long tokens are kept
20
26
  DEFAULT_TOKEN_CACHE_TTL = 5 * 60
21
27
  DEFAULT_TOKEN_CACHE_SIZE = 1024
22
28
 
29
+ legacy_exchange_session = requests.Session()
30
+
31
+
32
+ def get_token(
33
+ username: str, group: str, dirac_properties: set[str], *, expires_minutes: int | None = None, source: str = ""
34
+ ):
35
+ """Do a legacy exchange to get a DiracX access_token+refresh_token
23
36
 
24
- def get_token(username: str, group: str, dirac_properties: set[str], *, expires_minutes: int | None = None):
25
- """Do a legacy exchange to get a DiracX access_token+refresh_token"""
37
+ The source parameter only purpose is to appear in the URL on diracx logs"""
26
38
  diracxUrl = gConfig.getValue("/DiracX/URL")
27
39
  if not diracxUrl:
28
40
  raise ValueError("Missing mandatory /DiracX/URL configuration")
@@ -33,12 +45,13 @@ def get_token(username: str, group: str, dirac_properties: set[str], *, expires_
33
45
  vo = Registry.getVOForGroup(group)
34
46
  scopes = [f"vo:{vo}", f"group:{group}"] + [f"property:{prop}" for prop in dirac_properties]
35
47
 
36
- r = requests.get(
48
+ r = legacy_exchange_session.get(
37
49
  f"{diracxUrl}/api/auth/legacy-exchange",
38
50
  params={
39
51
  "preferred_username": username,
40
52
  "scope": " ".join(scopes),
41
53
  "expires_minutes": expires_minutes,
54
+ "source": source,
42
55
  },
43
56
  headers={"Authorization": f"Bearer {apiKey}"},
44
57
  timeout=10,
@@ -51,21 +64,25 @@ def get_token(username: str, group: str, dirac_properties: set[str], *, expires_
51
64
 
52
65
  @cached(
53
66
  TTLCache(maxsize=DEFAULT_TOKEN_CACHE_SIZE, ttl=DEFAULT_TOKEN_CACHE_TTL),
54
- key=lambda a, b, c: hashkey(a, b, *sorted(c)),
67
+ key=lambda a, b, c, **_: hashkey(a, b, *sorted(c)),
55
68
  )
56
- def _get_token_file(username: str, group: str, dirac_properties: set[str]) -> Path:
69
+ def _get_token_file(username: str, group: str, dirac_properties: set[str], *, source: str = "") -> Path:
57
70
  """Write token to a temporary file and return the path to that file"""
58
- data = get_token(username, group, dirac_properties)
71
+ data = get_token(username, group, dirac_properties, source=source)
59
72
  token_location = Path(NamedTemporaryFile().name)
60
73
  write_credentials(TokenResponse(**data), location=token_location)
61
74
  return token_location
62
75
 
63
76
 
64
- def TheImpersonator(credDict: dict[str, Any]) -> DiracClient:
77
+ diracx_client_cache = LRUCache(maxsize=64)
78
+
79
+
80
+ @contextmanager
81
+ def TheImpersonator(credDict: dict[str, Any], *, source: str = "") -> Generator[SyncDiracClient, None, None]:
65
82
  """
66
83
  Client to be used by DIRAC server needing to impersonate
67
84
  a user for diracx.
68
- It queries a token, places it in a file, and returns the `DiracClient`
85
+ It queries a token, places it in a file, and returns the `SyncDiracClient`
69
86
  class
70
87
 
71
88
  Use as a context manager
@@ -78,7 +95,12 @@ def TheImpersonator(credDict: dict[str, Any]) -> DiracClient:
78
95
  credDict["username"],
79
96
  credDict["group"],
80
97
  set(credDict.get("groupProperties", []) + credDict.get("properties", [])),
98
+ source=source,
81
99
  )
82
- pref = DiracxPreferences(url=diracxUrl, credentials_path=token_location)
83
-
84
- return DiracClient(diracx_preferences=pref)
100
+ client = diracx_client_cache.get(token_location)
101
+ if client is None:
102
+ pref = DiracxPreferences(url=diracxUrl, credentials_path=token_location)
103
+ client = SyncDiracClient(diracx_preferences=pref)
104
+ client.__enter__()
105
+ diracx_client_cache[token_location] = client
106
+ yield client