DIRAC 9.0.0a69__py3-none-any.whl → 9.0.0a70__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 (84) hide show
  1. DIRAC/AccountingSystem/Client/Types/Network.py +8 -8
  2. DIRAC/AccountingSystem/Client/Types/PilotSubmission.py +3 -3
  3. DIRAC/ConfigurationSystem/Client/CSAPI.py +11 -1
  4. DIRAC/ConfigurationSystem/Client/Helpers/CSGlobals.py +0 -9
  5. DIRAC/ConfigurationSystem/Client/Helpers/Registry.py +3 -29
  6. DIRAC/ConfigurationSystem/Client/SyncPlugins/CERNLDAPSyncPlugin.py +4 -1
  7. DIRAC/ConfigurationSystem/ConfigTemplate.cfg +3 -0
  8. DIRAC/ConfigurationSystem/private/Modificator.py +11 -3
  9. DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
  10. DIRAC/Core/DISET/ServiceReactor.py +11 -3
  11. DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
  12. DIRAC/Core/Security/DiracX.py +11 -6
  13. DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
  14. DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
  15. DIRAC/Core/Utilities/ElasticSearchDB.py +1 -2
  16. DIRAC/Core/Utilities/Subprocess.py +66 -57
  17. DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
  18. DIRAC/Core/Utilities/test/Test_Subprocess.py +58 -8
  19. DIRAC/Core/scripts/dirac_apptainer_exec.py +8 -8
  20. DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
  21. DIRAC/DataManagementSystem/Client/DataManager.py +6 -7
  22. DIRAC/DataManagementSystem/Client/FTS3Job.py +125 -34
  23. DIRAC/DataManagementSystem/Client/test/Test_FTS3Objects.py +1 -0
  24. DIRAC/DataManagementSystem/Client/test/Test_scitag.py +69 -0
  25. DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
  26. DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
  27. DIRAC/FrameworkSystem/DB/InstalledComponentsDB.py +3 -2
  28. DIRAC/FrameworkSystem/DB/ProxyDB.py +9 -5
  29. DIRAC/FrameworkSystem/Utilities/MonitoringUtilities.py +1 -0
  30. DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
  31. DIRAC/FrameworkSystem/Utilities/diracx.py +41 -10
  32. DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
  33. DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +1 -1
  34. DIRAC/FrameworkSystem/scripts/dirac_uninstall_component.py +1 -0
  35. DIRAC/Interfaces/API/Dirac.py +3 -6
  36. DIRAC/Interfaces/Utilities/DConfigCache.py +2 -0
  37. DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -5
  38. DIRAC/MonitoringSystem/Service/WebAppHandler.py +25 -6
  39. DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
  40. DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
  41. DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
  42. DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
  43. DIRAC/Resources/Computing/AREXComputingElement.py +18 -2
  44. DIRAC/Resources/Computing/BatchSystems/Condor.py +0 -3
  45. DIRAC/Resources/Computing/BatchSystems/executeBatch.py +15 -7
  46. DIRAC/Resources/Computing/LocalComputingElement.py +0 -2
  47. DIRAC/Resources/Computing/SSHComputingElement.py +61 -38
  48. DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
  49. DIRAC/Resources/IdProvider/IdProviderFactory.py +13 -3
  50. DIRAC/Resources/IdProvider/tests/Test_IdProviderFactory.py +7 -0
  51. DIRAC/Resources/Storage/FileStorage.py +121 -2
  52. DIRAC/TransformationSystem/Agent/InputDataAgent.py +4 -1
  53. DIRAC/TransformationSystem/Agent/MCExtensionAgent.py +5 -2
  54. DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +3 -4
  55. DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +44 -9
  56. DIRAC/TransformationSystem/Agent/ValidateOutputDataAgent.py +4 -2
  57. DIRAC/TransformationSystem/Client/TransformationClient.py +9 -1
  58. DIRAC/TransformationSystem/Client/Utilities.py +6 -3
  59. DIRAC/TransformationSystem/DB/TransformationDB.py +105 -43
  60. DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
  61. DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
  62. DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
  63. DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +8 -11
  64. DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +39 -7
  65. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +8 -2
  66. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +24 -4
  67. DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +4 -3
  68. DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +3 -3
  69. DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +8 -8
  70. DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +1 -1
  71. DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +48 -21
  72. DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +19 -4
  73. DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +3 -4
  74. DIRAC/WorkloadManagementSystem/JobWrapper/Watchdog.py +16 -45
  75. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +18 -9
  76. DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +25 -2
  77. DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +18 -31
  78. DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +4 -1
  79. {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/METADATA +6 -5
  80. {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/RECORD +84 -82
  81. {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/WHEEL +0 -0
  82. {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/entry_points.txt +0 -0
  83. {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/licenses/LICENSE +0 -0
  84. {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/top_level.txt +0 -0
@@ -74,7 +74,10 @@ class InputDataAgent(AgentModule):
74
74
  """Main execution method"""
75
75
 
76
76
  # Get all the transformations
77
- result = self.transClient.getTransformations({"Status": "Active", "Type": self.transformationTypes})
77
+ result = self.transClient.getTransformations(
78
+ {"Status": "Active", "Type": self.transformationTypes},
79
+ columns=["TransformationID", "AuthorDN", "AuthorGroup"],
80
+ )
78
81
  if not result["OK"]:
79
82
  self.log.error("InputDataAgent.execute: Failed to get transformations.", result["Message"])
80
83
  return S_OK()
@@ -1,4 +1,4 @@
1
- """ Agent to extend the number of tasks given the Transformation definition
1
+ """Agent to extend the number of tasks given the Transformation definition
2
2
 
3
3
  The following options can be set for the MCExtensionAgent.
4
4
 
@@ -8,6 +8,7 @@ The following options can be set for the MCExtensionAgent.
8
8
  :dedent: 2
9
9
  :caption: MCExtensionAgent options
10
10
  """
11
+
11
12
  from DIRAC import S_OK, gLogger
12
13
  from DIRAC.Core.Base.AgentModule import AgentModule
13
14
  from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
@@ -54,7 +55,9 @@ class MCExtensionAgent(AgentModule):
54
55
  return S_OK("Disabled via CS flag")
55
56
 
56
57
  # Obtain the transformations in Cleaning status and remove any mention of the jobs/files
57
- res = self.transClient.getTransformations({"Status": "Active", "Type": self.transformationTypes})
58
+ res = self.transClient.getTransformations(
59
+ {"Status": "Active", "Type": self.transformationTypes}, columns=["TransformationID", "MaxNumberOfTasks"]
60
+ )
58
61
  if res["OK"]:
59
62
  for transDict in res["Value"]:
60
63
  transID = transDict["TransformationID"]
@@ -22,7 +22,6 @@ from DIRAC.TransformationSystem.Client.FileReport import FileReport
22
22
  from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
23
23
  from DIRAC.TransformationSystem.Client.WorkflowTasks import WorkflowTasks
24
24
  from DIRAC.WorkloadManagementSystem.Client import JobStatus
25
- from DIRAC.WorkloadManagementSystem.Client.JobManagerClient import JobManagerClient
26
25
 
27
26
  AGENT_NAME = "Transformation/TaskManagerAgentBase"
28
27
 
@@ -39,7 +38,6 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
39
38
  TransformationAgentsUtilities.__init__(self)
40
39
 
41
40
  self.transClient = None
42
- self.jobManagerClient = None
43
41
  self.transType = []
44
42
 
45
43
  self.tasksPerLoop = 50
@@ -68,7 +66,6 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
68
66
 
69
67
  # Default clients
70
68
  self.transClient = TransformationClient()
71
- self.jobManagerClient = JobManagerClient()
72
69
 
73
70
  # Bulk submission flag
74
71
  self.bulkSubmissionFlag = self.am_getOption("BulkSubmission", self.bulkSubmissionFlag)
@@ -234,7 +231,9 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
234
231
  selectCond["Type"] = transType
235
232
  if agentType:
236
233
  selectCond["AgentType"] = agentType
237
- res = self.transClient.getTransformations(condDict=selectCond)
234
+ res = self.transClient.getTransformations(
235
+ condDict=selectCond, columns=["TransformationID", "Body", "Author", "AuthorGroup"]
236
+ )
238
237
  if not res["OK"]:
239
238
  self.log.error("Failed to get transformations:", res["Message"])
240
239
  elif not res["Value"]:
@@ -20,6 +20,7 @@ from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
20
20
  from DIRAC.Core.Base.AgentModule import AgentModule
21
21
  from DIRAC.Core.Utilities.DErrno import cmpError
22
22
  from DIRAC.Core.Utilities.List import breakListIntoChunks
23
+ from DIRAC.Core.Utilities.ObjectLoader import ObjectLoader
23
24
  from DIRAC.Core.Utilities.Proxy import executeWithUserProxy
24
25
  from DIRAC.Core.Utilities.ReturnValues import returnSingleResult
25
26
  from DIRAC.RequestManagementSystem.Client.File import File
@@ -32,7 +33,6 @@ from DIRAC.Resources.Catalog.FileCatalogClient import FileCatalogClient
32
33
  from DIRAC.Resources.Storage.StorageElement import StorageElement
33
34
  from DIRAC.TransformationSystem.Client import TransformationStatus
34
35
  from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
35
- from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
36
36
  from DIRAC.WorkloadManagementSystem.Service.JobPolicy import (
37
37
  RIGHT_DELETE,
38
38
  RIGHT_KILL,
@@ -65,8 +65,11 @@ class TransformationCleaningAgent(AgentModule):
65
65
  self.reqClient = None
66
66
  # # file catalog client
67
67
  self.metadataClient = None
68
- # # JobDB
68
+ # # databases
69
69
  self.jobDB = None
70
+ self.pilotAgentsDB = None
71
+ self.taskQueueDB = None
72
+ self.storageManagementDB = None
70
73
 
71
74
  # # transformations types
72
75
  self.transformationTypes = None
@@ -125,8 +128,26 @@ class TransformationCleaningAgent(AgentModule):
125
128
  self.reqClient = ReqClient()
126
129
  # # file catalog client
127
130
  self.metadataClient = FileCatalogClient()
128
- # # job DB
129
- self.jobDB = JobDB()
131
+ # # databases
132
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.JobDB", "JobDB")
133
+ if not result["OK"]:
134
+ return result
135
+ self.jobDB = result["Value"]()
136
+
137
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.PilotAgentsDB", "PilotAgentsDB")
138
+ if not result["OK"]:
139
+ return result
140
+ self.pilotAgentsDB = result["Value"]()
141
+
142
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.TaskQueueDB", "TaskQueueDB")
143
+ if not result["OK"]:
144
+ return result
145
+ self.taskQueueDB = result["Value"]()
146
+
147
+ result = ObjectLoader().loadObject("StorageManagementSystem.DB.StorageManagementDB", "StorageManagementDB")
148
+ if not result["OK"]:
149
+ return result
150
+ self.storageManagementDB = result["Value"]()
130
151
 
131
152
  return S_OK()
132
153
 
@@ -144,7 +165,8 @@ class TransformationCleaningAgent(AgentModule):
144
165
 
145
166
  # Obtain the transformations in Cleaning status and remove any mention of the jobs/files
146
167
  res = self.transClient.getTransformations(
147
- {"Status": TransformationStatus.CLEANING, "Type": self.transformationTypes}
168
+ {"Status": TransformationStatus.CLEANING, "Type": self.transformationTypes},
169
+ columns=["TransformationID", "Author", "AuthorGroup", "Type"],
148
170
  )
149
171
  if res["OK"]:
150
172
  for transDict in res["Value"]:
@@ -161,7 +183,10 @@ class TransformationCleaningAgent(AgentModule):
161
183
  self.log.error("Failed to get transformations", res["Message"])
162
184
 
163
185
  # Obtain the transformations in RemovingFiles status and removes the output files
164
- res = self.transClient.getTransformations({"Status": "RemovingFiles", "Type": self.transformationTypes})
186
+ res = self.transClient.getTransformations(
187
+ {"Status": "RemovingFiles", "Type": self.transformationTypes},
188
+ columns=["TransformationID", "Author", "AuthorGroup"],
189
+ )
165
190
  if res["OK"]:
166
191
  for transDict in res["Value"]:
167
192
  if self.shifterProxy:
@@ -183,6 +208,7 @@ class TransformationCleaningAgent(AgentModule):
183
208
  {"Status": TransformationStatus.COMPLETED, "Type": self.transformationTypes},
184
209
  older=olderThanTime,
185
210
  timeStamp="LastUpdate",
211
+ columns=["TransformationID", "Author", "AuthorGroup"],
186
212
  )
187
213
  if res["OK"]:
188
214
  for transDict in res["Value"]:
@@ -230,7 +256,10 @@ class TransformationCleaningAgent(AgentModule):
230
256
  return res
231
257
  transformationIDs = res["Value"]
232
258
  if transformationIDs:
233
- res = self.transClient.getTransformations({"TransformationID": transformationIDs})
259
+ res = self.transClient.getTransformations(
260
+ {"TransformationID": transformationIDs},
261
+ columns=["TransformationID", "Status", "Author", "AuthorGroup", "Type"],
262
+ )
234
263
  if not res["OK"]:
235
264
  self.log.error("Failed to get transformations", res["Message"])
236
265
  return res
@@ -607,11 +636,17 @@ class TransformationCleaningAgent(AgentModule):
607
636
  :param self: self reference
608
637
  :param list trasnJobIDs: job IDs
609
638
  """
639
+ db_kwargs = dict(
640
+ jobdb=self.jobDB,
641
+ taskqueuedb=self.taskQueueDB,
642
+ pilotagentsdb=self.pilotAgentsDB,
643
+ storagemanagementdb=self.storageManagementDB,
644
+ )
610
645
  # Prevent 0 job IDs
611
646
  jobIDs = [int(j) for j in transJobIDs if int(j)]
612
647
  allRemove = True
613
648
  for jobList in breakListIntoChunks(jobIDs, 1000):
614
- res = kill_delete_jobs(RIGHT_KILL, jobList, force=True)
649
+ res = kill_delete_jobs(RIGHT_KILL, jobList, force=True, **db_kwargs)
615
650
  if res["OK"]:
616
651
  self.log.info(f"Successfully killed {len(jobList)} jobs from WMS")
617
652
  elif ("InvalidJobIDs" in res) and ("NonauthorizedJobIDs" not in res) and ("FailedJobIDs" not in res):
@@ -623,7 +658,7 @@ class TransformationCleaningAgent(AgentModule):
623
658
  self.log.error("Failed to kill jobs", f"(n={len(res['FailedJobIDs'])})")
624
659
  allRemove = False
625
660
 
626
- res = kill_delete_jobs(RIGHT_DELETE, jobList, force=True)
661
+ res = kill_delete_jobs(RIGHT_DELETE, jobList, force=True, **db_kwargs)
627
662
  if res["OK"]:
628
663
  self.log.info("Successfully deleted jobs from WMS", f"(n={len(jobList)})")
629
664
  elif ("InvalidJobIDs" in res) and ("NonauthorizedJobIDs" not in res) and ("FailedJobIDs" not in res):
@@ -76,7 +76,9 @@ class ValidateOutputDataAgent(AgentModule):
76
76
  self.updateWaitingIntegrity()
77
77
  gLogger.info("-" * 40)
78
78
 
79
- res = self.transClient.getTransformations({"Status": "ValidatingOutput", "Type": self.transformationTypes})
79
+ res = self.transClient.getTransformations(
80
+ {"Status": "ValidatingOutput", "Type": self.transformationTypes}, columns=["TransformationID"]
81
+ )
80
82
  if not res["OK"]:
81
83
  gLogger.error("Failed to get ValidatingOutput transformations", res["Message"])
82
84
  return res
@@ -98,7 +100,7 @@ class ValidateOutputDataAgent(AgentModule):
98
100
  def updateWaitingIntegrity(self):
99
101
  """Get 'WaitingIntegrity' transformations, update to 'ValidatedOutput'"""
100
102
  gLogger.info("Looking for transformations in the WaitingIntegrity status to update")
101
- res = self.transClient.getTransformations({"Status": "WaitingIntegrity"})
103
+ res = self.transClient.getTransformations({"Status": "WaitingIntegrity"}, columns=["TransformationID"])
102
104
  if not res["OK"]:
103
105
  gLogger.error("Failed to get WaitingIntegrity transformations", res["Message"])
104
106
  return res
@@ -114,13 +114,21 @@ class TransformationClient(Client):
114
114
  newer=None,
115
115
  timeStamp=None,
116
116
  orderAttribute=None,
117
- limit=100,
117
+ limit=None,
118
118
  extraParams=False,
119
119
  columns=None,
120
120
  ):
121
121
  """gets all the transformations in the system, incrementally. "limit" here is just used to determine the offset."""
122
122
  rpcClient = self._getRPC()
123
123
 
124
+ # If the body is requested (or is served by default)
125
+ # we take smaller chunk, not to take too much memory
126
+ # on the server
127
+ if columns and "Body" not in columns:
128
+ limit = 100_000
129
+ else:
130
+ limit = 1_000
131
+
124
132
  transformations = []
125
133
  if condDict is None:
126
134
  condDict = {}
@@ -9,7 +9,7 @@ Utilities for Transformation system
9
9
  import ast
10
10
  import random
11
11
 
12
- from cachetools import LRUCache, cached
12
+ from cachetools import LRUCache, cachedmethod
13
13
  from cachetools.keys import hashkey
14
14
  from DIRAC import S_OK, S_ERROR, gLogger
15
15
 
@@ -24,6 +24,9 @@ from DIRAC.Resources.Catalog.FileCatalog import FileCatalog
24
24
  from DIRAC.Resources.Storage.StorageElement import StorageElement
25
25
  from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
26
26
 
27
+ # Module-level cache for isSameSEInList method (shared across PluginUtilities instances)
28
+ _is_same_se_in_list_cache = LRUCache(maxsize=1024)
29
+
27
30
 
28
31
  class PluginUtilities:
29
32
  """
@@ -402,8 +405,8 @@ class PluginUtilities:
402
405
 
403
406
  return StorageElement(se1).isSameSE(StorageElement(se2))
404
407
 
405
- @cached(
406
- LRUCache(maxsize=1024),
408
+ @cachedmethod(
409
+ lambda self: _is_same_se_in_list_cache,
407
410
  key=lambda _, a, b: hashkey(a, *sorted(b)),
408
411
  )
409
412
  def isSameSEInList(self, se1, seList):
@@ -6,6 +6,9 @@ This class is typically used as a base class for more specific data processing
6
6
  databases
7
7
  """
8
8
 
9
+ # Disable it because pylint does not understand decorator (convertToReturnValue)
10
+
11
+ # pylint: disable=invalid-sequence-index
9
12
  import re
10
13
  import time
11
14
  import threading
@@ -15,6 +18,7 @@ from errno import ENOENT
15
18
  from DIRAC import gLogger, S_OK, S_ERROR
16
19
  from DIRAC.Core.Base.DB import DB
17
20
  from DIRAC.Core.Utilities.DErrno import cmpError
21
+ from DIRAC.Core.Utilities.ReturnValues import convertToReturnValue, returnValueOrRaise
18
22
  from DIRAC.Resources.Catalog.FileCatalog import FileCatalog
19
23
  from DIRAC.Core.Security.ProxyInfo import getProxyInfo
20
24
  from DIRAC.Core.Utilities.List import stringListToString, intListToString, breakListIntoChunks
@@ -25,6 +29,7 @@ from DIRAC.DataManagementSystem.Client.MetaQuery import MetaQuery
25
29
 
26
30
  MAX_ERROR_COUNT = 10
27
31
 
32
+ TMP_TABLE_JOIN_LIMIT = 100
28
33
  #############################################################################
29
34
 
30
35
 
@@ -270,6 +275,7 @@ class TransformationDB(DB):
270
275
  self.__updateTransformationLogging(transID, message, author, connection=connection)
271
276
  return S_OK(transID)
272
277
 
278
+ @convertToReturnValue
273
279
  def getTransformations(
274
280
  self,
275
281
  condDict=None,
@@ -289,32 +295,54 @@ class TransformationDB(DB):
289
295
  columns = self.TRANSPARAMS
290
296
  else:
291
297
  columns = [c for c in columns if c in self.TRANSPARAMS]
292
- req = "SELECT {} FROM Transformations {}".format(
293
- intListToString(columns),
294
- self.buildCondition(condDict, older, newer, timeStamp, orderAttribute, limit, offset=offset),
295
- )
296
- res = self._query(req, conn=connection)
297
- if not res["OK"]:
298
- return res
299
- if condDict is None:
300
- condDict = {}
301
- webList = []
298
+
299
+ join_query = ""
300
+
301
+ try:
302
+ # If we request multiple TransformationIDs, and they are more than TMP_TABLE_JOIN_LIMIT,
303
+ # we create a temporary table to speed up the query
304
+ if (
305
+ "TransformationID" in condDict
306
+ and isinstance(condDict["TransformationID"], list)
307
+ and len(condDict["TransformationID"]) > TMP_TABLE_JOIN_LIMIT
308
+ ):
309
+ # Create temporary table for TransformationIDs
310
+ transIDs = condDict.pop("TransformationID")
311
+ sqlCmd = "CREATE TEMPORARY TABLE to_query_TransformationIDs (TransID INTEGER NOT NULL, PRIMARY KEY (TransID)) ENGINE=MEMORY;"
312
+ returnValueOrRaise(self._update(sqlCmd, conn=connection))
313
+ join_query = " JOIN to_query_TransformationIDs t ON TransformationID = t.TransID"
314
+
315
+ # Insert TransformationIDs into temporary table
316
+ sqlCmd = "INSERT INTO to_query_TransformationIDs (TransID) VALUES ( %s )"
317
+ returnValueOrRaise(self._updatemany(sqlCmd, [(transID,) for transID in transIDs], conn=connection))
318
+
319
+ req = "SELECT {} FROM Transformations {} {}".format(
320
+ intListToString(columns),
321
+ join_query,
322
+ self.buildCondition(condDict, older, newer, timeStamp, orderAttribute, limit, offset=offset),
323
+ )
324
+ matching_transformations = returnValueOrRaise(self._query(req, conn=connection))
325
+
326
+ finally:
327
+ # Clean up temporary table
328
+ if join_query:
329
+ sqlCmd = "DROP TEMPORARY TABLE to_query_TransformationIDs"
330
+ self._update(sqlCmd, conn=connection)
331
+
332
+ # TODO: optimize by getting all the extra params at once
302
333
  resultList = []
303
- for row in res["Value"]:
334
+ for row in matching_transformations:
304
335
  # Prepare the structure for the web
305
- rList = [str(item) if not isinstance(item, int) else item for item in row]
306
336
  transDict = dict(zip(columns, row))
307
- webList.append(rList)
308
337
  if extraParams:
309
- res = self.__getAdditionalParameters(transDict["TransformationID"], connection=connection)
310
- if not res["OK"]:
311
- return res
312
- transDict.update(res["Value"])
338
+ trans_extra_param = returnValueOrRaise(
339
+ self.__getAdditionalParameters(transDict["TransformationID"], connection=connection)
340
+ )
341
+
342
+ transDict.update(trans_extra_param)
313
343
  resultList.append(transDict)
314
- result = S_OK(resultList)
315
- result["Records"] = webList
316
- result["ParameterNames"] = columns
317
- return result
344
+
345
+ return resultList
318
346
 
319
347
  def getTransformation(self, transName, extraParams=False, connection=False):
320
348
  """Get Transformation definition and parameters of Transformation identified by TransformationID"""
@@ -710,21 +738,38 @@ class TransformationDB(DB):
710
738
  countDict["Total"] = sum(countDict.values())
711
739
  return S_OK(countDict)
712
740
 
741
+ @convertToReturnValue
713
742
  def __addFilesToTransformation(self, transID, fileIDs, connection=False):
714
- req = "SELECT FileID from TransformationFiles"
715
- req = req + " WHERE TransformationID = %d AND FileID IN (%s);" % (transID, intListToString(fileIDs))
716
- res = self._query(req, conn=connection)
717
- if not res["OK"]:
718
- return res
719
- for tupleIn in res["Value"]:
720
- fileIDs.remove(tupleIn[0])
721
- if not fileIDs:
722
- return S_OK([])
723
- values = [(transID, fileID) for fileID in fileIDs]
724
- req = "INSERT INTO TransformationFiles (TransformationID,FileID,LastUpdate,InsertedTime) VALUES (%s, %s, UTC_TIMESTAMP(), UTC_TIMESTAMP())"
725
- if not (res := self._updatemany(req, values, conn=connection))["OK"]:
726
- return res
727
- return S_OK(fileIDs)
743
+ # Create temporary table for FileIDs
744
+ sqlCmd = "CREATE TEMPORARY TABLE to_query_FileIDs (FileID INT(11) UNSIGNED NOT NULL, PRIMARY KEY (FileID)) ENGINE=MEMORY;"
745
+ returnValueOrRaise(self._update(sqlCmd, conn=connection))
746
+
747
+ try:
748
+ # Insert FileIDs into temporary table
749
+ sqlCmd = "INSERT INTO to_query_FileIDs (FileID) VALUES ( %s )"
750
+ returnValueOrRaise(self._updatemany(sqlCmd, [(fileID,) for fileID in fileIDs], conn=connection))
751
+
752
+ # Query existing files using JOIN
753
+ req = (
754
+ "SELECT tf.FileID FROM TransformationFiles tf JOIN to_query_FileIDs t ON tf.FileID = t.FileID WHERE tf.TransformationID = %d;"
755
+ % transID
756
+ )
757
+ res = returnValueOrRaise(self._query(req, conn=connection))
758
+
759
+ # Remove already existing fileIDs using set difference for efficiency
760
+ existingFileIDs = {tupleIn[0] for tupleIn in res}
761
+ fileIDs = list(set(fileIDs) - existingFileIDs)
762
+ if not fileIDs:
763
+ return []
764
+
765
+ values = [(transID, fileID) for fileID in fileIDs]
766
+ req = "INSERT INTO TransformationFiles (TransformationID,FileID,LastUpdate,InsertedTime) VALUES (%s, %s, UTC_TIMESTAMP(), UTC_TIMESTAMP())"
767
+ returnValueOrRaise(self._updatemany(req, values, conn=connection))
768
+ return fileIDs
769
+ finally:
770
+ # Clean up temporary table
771
+ sqlCmd = "DROP TEMPORARY TABLE to_query_FileIDs"
772
+ returnValueOrRaise(self._update(sqlCmd, conn=connection))
728
773
 
729
774
  def __insertExistingTransformationFiles(self, transID, fileTuplesList, connection=False):
730
775
  """Inserting already transformation files in TransformationFiles table (e.g. for deriving transformations)"""
@@ -1271,18 +1316,35 @@ class TransformationDB(DB):
1271
1316
  # These methods manipulate the DataFiles table
1272
1317
  #
1273
1318
 
1319
+ @convertToReturnValue
1274
1320
  def __getFileIDsForLfns(self, lfns, connection=False):
1275
1321
  """Get file IDs for the given list of lfns
1276
1322
  warning: if the file is not present, we'll see no errors
1277
1323
  """
1278
- req = f"SELECT LFN,FileID FROM DataFiles WHERE LFN in ({stringListToString(lfns)});"
1279
- res = self._query(req, conn=connection)
1280
- if not res["OK"]:
1281
- return res
1282
- lfns = dict(res["Value"])
1283
- # Reverse dictionary
1284
- fids = {fileID: lfn for lfn, fileID in lfns.items()}
1285
- return S_OK((fids, lfns))
1324
+
1325
+ if not lfns:
1326
+ return ({}, {})
1327
+ # Create temporary table for LFNs
1328
+ sqlCmd = "CREATE TEMPORARY TABLE to_query_LFNs (LFN VARCHAR(255) NOT NULL, PRIMARY KEY (LFN)) ENGINE=MEMORY;"
1329
+ returnValueOrRaise(self._update(sqlCmd, conn=connection))
1330
+
1331
+ try:
1332
+ # Insert LFNs into temporary table
1333
+ sqlCmd = "INSERT INTO to_query_LFNs (LFN) VALUES ( %s )"
1334
+ returnValueOrRaise(self._updatemany(sqlCmd, [(lfn,) for lfn in lfns], conn=connection))
1335
+
1336
+ # Query using JOIN with temporary table
1337
+ req = "SELECT df.LFN, df.FileID FROM DataFiles df JOIN to_query_LFNs t ON df.LFN = t.LFN;"
1338
+ res = returnValueOrRaise(self._query(req, conn=connection))
1339
+
1340
+ lfns = dict(res)
1341
+ # Reverse dictionary
1342
+ fids = {fileID: lfn for lfn, fileID in lfns.items()}
1343
+ return (fids, lfns)
1344
+ finally:
1345
+ # Clean up temporary table
1346
+ sqlCmd = "DROP TEMPORARY TABLE to_query_LFNs"
1347
+ self._update(sqlCmd, conn=connection)
1286
1348
 
1287
1349
  def __getLfnsForFileIDs(self, fileIDs, connection=False):
1288
1350
  """Get lfns for the given list of fileIDs"""
@@ -1,10 +1,10 @@
1
1
  """
2
2
  Command Line Parameters for creating the Replication transformations Script
3
3
  """
4
- from DIRAC import S_OK, S_ERROR, gLogger
4
+ from DIRAC import S_ERROR, S_OK, gLogger
5
+ from DIRAC.ConfigurationSystem.Client.Helpers.Registry import getVOForGroup
5
6
  from DIRAC.Core.Security.Properties import SecurityProperty
6
7
  from DIRAC.Core.Security.ProxyInfo import getProxyInfo
7
- from DIRAC.ConfigurationSystem.Client.Helpers.Registry import getVOMSVOForGroup
8
8
 
9
9
 
10
10
  class Params:
@@ -144,7 +144,7 @@ class Params:
144
144
  return False
145
145
  proxyValues = proxyInfo.get("Value", {})
146
146
  group = proxyValues.get("group", "")
147
- vomsvo = getVOMSVOForGroup(group)
147
+ vomsvo = getVOForGroup(group)
148
148
  if not vomsvo:
149
149
  self.errorMessages.append("ERROR: ProxyGroup not associated to VOMS VO, get a different proxy")
150
150
  return False
@@ -16,7 +16,7 @@ from urllib.request import urlopen
16
16
  from DIRAC.Interfaces.API.Dirac import Dirac
17
17
  from DIRAC.Core.Utilities.File import mkDir
18
18
  from DIRAC.Core.Base.Script import Script
19
- from DIRAC.ConfigurationSystem.Client.Helpers.CSGlobals import getVO, getSetup
19
+ from DIRAC.ConfigurationSystem.Client.Helpers.CSGlobals import getVO
20
20
  from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData
21
21
 
22
22
 
@@ -79,14 +79,12 @@ def __configurePilot(basepath, vo):
79
79
  This method was created specifically for LHCb pilots, more info
80
80
  about othe VOs is needed to make it more general.
81
81
  """
82
- currentSetup = getSetup()
83
82
  masterCS = gConfigurationData.getMasterServer()
84
83
 
85
84
  os.system(
86
85
  "python "
87
86
  + basepath
88
- + "dirac-pilot.py -S %s -l %s -C %s -N ce.debug.ch -Q default -n DIRAC.JobDebugger.ch -dd"
89
- % (currentSetup, vo, masterCS)
87
+ + f"dirac-pilot.py -l {vo} -C {masterCS} -N ce.debug.ch -Q default -n DIRAC.JobDebugger.ch -dd"
90
88
  )
91
89
 
92
90
  diracdir = os.path.expanduser("~") + os.path.sep
@@ -1,14 +1,13 @@
1
1
  """Test the dirac-transformation-replication script and helper"""
2
2
  import unittest
3
+ from unittest.mock import MagicMock as Mock
4
+ from unittest.mock import patch
3
5
 
4
- from unittest.mock import MagicMock as Mock, patch
5
-
6
- from DIRAC import S_OK, S_ERROR
7
-
8
- from DIRAC.TransformationSystem.Utilities.ReplicationTransformation import createDataTransformation
6
+ from DIRAC import S_ERROR, S_OK
9
7
  from DIRAC.TransformationSystem.Utilities.ReplicationCLIParameters import Params
8
+ from DIRAC.TransformationSystem.Utilities.ReplicationTransformation import createDataTransformation
10
9
 
11
- GET_VOMS = "DIRAC.TransformationSystem.Utilities.ReplicationCLIParameters.getVOMSVOForGroup"
10
+ GET_VOMS = "DIRAC.TransformationSystem.Utilities.ReplicationCLIParameters.getVOForGroup"
12
11
  GET_PROXY = "DIRAC.TransformationSystem.Utilities.ReplicationCLIParameters.getProxyInfo"
13
12
 
14
13
 
@@ -1,4 +1,4 @@
1
- """ The Site Director is an agent performing pilot job submission to particular sites/Computing Elements.
1
+ """The Site Director is an agent performing pilot job submission to particular sites/Computing Elements.
2
2
 
3
3
  .. literalinclude:: ../ConfigTemplate.cfg
4
4
  :start-after: ##BEGIN SiteDirector
@@ -7,6 +7,7 @@
7
7
  :caption: SiteDirector options
8
8
 
9
9
  """
10
+
10
11
  import datetime
11
12
  import os
12
13
  from collections import defaultdict
@@ -147,10 +148,10 @@ class SiteDirector(AgentModule):
147
148
  self.sendSubmissionAccounting = True
148
149
 
149
150
  # Get the site description dictionary
150
- siteNames = self.am_getOption("Site", [])
151
- ceTypes = self.am_getOption("CETypes", [])
152
- ces = self.am_getOption("CEs", [])
153
- tags = self.am_getOption("Tags", [])
151
+ siteNames = self.am_getOption("Site", []) or None
152
+ ceTypes = self.am_getOption("CETypes", []) or None
153
+ ces = self.am_getOption("CEs", []) or None
154
+ tags = self.am_getOption("Tags", []) or None
154
155
 
155
156
  # Display options used
156
157
  self.log.always("VO:", self.vo)
@@ -229,12 +230,8 @@ class SiteDirector(AgentModule):
229
230
  site = self.queueDict[queueName]["Site"]
230
231
  ce = self.queueDict[queueName]["CEName"]
231
232
 
232
- # Check the status of the Site
233
- if site in siteMaskList:
234
- continue
235
-
236
- # Check the status of the CE (only for RSS=Active)
237
- if ce not in ceMaskList:
233
+ # Check the status of the Site and CE
234
+ if site in siteMaskList and ce in ceMaskList:
238
235
  continue
239
236
 
240
237
  self.log.warn("Queue not considered because not usable:", queueName)