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.
- DIRAC/AccountingSystem/Client/Types/Network.py +8 -8
- DIRAC/AccountingSystem/Client/Types/PilotSubmission.py +3 -3
- DIRAC/ConfigurationSystem/Client/CSAPI.py +11 -1
- DIRAC/ConfigurationSystem/Client/Helpers/CSGlobals.py +0 -9
- DIRAC/ConfigurationSystem/Client/Helpers/Registry.py +3 -29
- DIRAC/ConfigurationSystem/Client/SyncPlugins/CERNLDAPSyncPlugin.py +4 -1
- DIRAC/ConfigurationSystem/ConfigTemplate.cfg +3 -0
- DIRAC/ConfigurationSystem/private/Modificator.py +11 -3
- DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
- DIRAC/Core/DISET/ServiceReactor.py +11 -3
- DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
- DIRAC/Core/Security/DiracX.py +11 -6
- DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
- DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
- DIRAC/Core/Utilities/ElasticSearchDB.py +1 -2
- DIRAC/Core/Utilities/Subprocess.py +66 -57
- DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
- DIRAC/Core/Utilities/test/Test_Subprocess.py +58 -8
- DIRAC/Core/scripts/dirac_apptainer_exec.py +8 -8
- DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
- DIRAC/DataManagementSystem/Client/DataManager.py +6 -7
- DIRAC/DataManagementSystem/Client/FTS3Job.py +125 -34
- DIRAC/DataManagementSystem/Client/test/Test_FTS3Objects.py +1 -0
- DIRAC/DataManagementSystem/Client/test/Test_scitag.py +69 -0
- DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
- DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
- DIRAC/FrameworkSystem/DB/InstalledComponentsDB.py +3 -2
- DIRAC/FrameworkSystem/DB/ProxyDB.py +9 -5
- DIRAC/FrameworkSystem/Utilities/MonitoringUtilities.py +1 -0
- DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
- DIRAC/FrameworkSystem/Utilities/diracx.py +41 -10
- DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +1 -1
- DIRAC/FrameworkSystem/scripts/dirac_uninstall_component.py +1 -0
- DIRAC/Interfaces/API/Dirac.py +3 -6
- DIRAC/Interfaces/Utilities/DConfigCache.py +2 -0
- DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -5
- DIRAC/MonitoringSystem/Service/WebAppHandler.py +25 -6
- DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
- DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
- DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
- DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
- DIRAC/Resources/Computing/AREXComputingElement.py +18 -2
- DIRAC/Resources/Computing/BatchSystems/Condor.py +0 -3
- DIRAC/Resources/Computing/BatchSystems/executeBatch.py +15 -7
- DIRAC/Resources/Computing/LocalComputingElement.py +0 -2
- DIRAC/Resources/Computing/SSHComputingElement.py +61 -38
- DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
- DIRAC/Resources/IdProvider/IdProviderFactory.py +13 -3
- DIRAC/Resources/IdProvider/tests/Test_IdProviderFactory.py +7 -0
- DIRAC/Resources/Storage/FileStorage.py +121 -2
- DIRAC/TransformationSystem/Agent/InputDataAgent.py +4 -1
- DIRAC/TransformationSystem/Agent/MCExtensionAgent.py +5 -2
- DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +3 -4
- DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +44 -9
- DIRAC/TransformationSystem/Agent/ValidateOutputDataAgent.py +4 -2
- DIRAC/TransformationSystem/Client/TransformationClient.py +9 -1
- DIRAC/TransformationSystem/Client/Utilities.py +6 -3
- DIRAC/TransformationSystem/DB/TransformationDB.py +105 -43
- DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
- DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
- DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
- DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +8 -11
- DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +39 -7
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +8 -2
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +24 -4
- DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +4 -3
- DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +3 -3
- DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +8 -8
- DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +1 -1
- DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +48 -21
- DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +19 -4
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +3 -4
- DIRAC/WorkloadManagementSystem/JobWrapper/Watchdog.py +16 -45
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +18 -9
- DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +25 -2
- DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +18 -31
- DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +4 -1
- {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/METADATA +6 -5
- {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/RECORD +84 -82
- {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/WHEEL +0 -0
- {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/entry_points.txt +0 -0
- {dirac-9.0.0a69.dist-info → dirac-9.0.0a70.dist-info}/licenses/LICENSE +0 -0
- {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(
|
|
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
|
-
"""
|
|
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(
|
|
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(
|
|
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
|
-
# #
|
|
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
|
-
# #
|
|
129
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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=
|
|
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,
|
|
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
|
-
@
|
|
406
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
315
|
-
|
|
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
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 -
|
|
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
|
|
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.
|
|
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
|
-
"""
|
|
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)
|