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.
- DIRAC/AccountingSystem/Client/AccountingCLI.py +0 -140
- DIRAC/AccountingSystem/Client/DataStoreClient.py +0 -13
- DIRAC/AccountingSystem/Client/Types/BaseAccountingType.py +0 -7
- DIRAC/AccountingSystem/ConfigTemplate.cfg +0 -5
- DIRAC/AccountingSystem/Service/DataStoreHandler.py +0 -72
- DIRAC/ConfigurationSystem/Client/Helpers/CSGlobals.py +0 -9
- DIRAC/ConfigurationSystem/Client/Helpers/Registry.py +38 -26
- DIRAC/ConfigurationSystem/Client/Helpers/Resources.py +11 -43
- DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py +0 -16
- DIRAC/ConfigurationSystem/Client/LocalConfiguration.py +14 -8
- DIRAC/ConfigurationSystem/Client/PathFinder.py +47 -8
- DIRAC/ConfigurationSystem/Client/SyncPlugins/CERNLDAPSyncPlugin.py +4 -1
- DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py +32 -19
- DIRAC/ConfigurationSystem/Client/test/Test_PathFinder.py +41 -1
- DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
- DIRAC/Core/Base/API.py +4 -7
- DIRAC/Core/Base/SQLAlchemyDB.py +1 -0
- DIRAC/Core/DISET/ServiceReactor.py +11 -3
- DIRAC/Core/DISET/private/BaseClient.py +1 -2
- DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
- DIRAC/Core/DISET/private/Transports/SSL/M2Utils.py +3 -1
- DIRAC/Core/LCG/GOCDBClient.py +5 -7
- DIRAC/Core/Security/DiracX.py +31 -17
- DIRAC/Core/Security/IAMService.py +5 -10
- DIRAC/Core/Security/Locations.py +27 -18
- DIRAC/Core/Security/ProxyInfo.py +9 -5
- DIRAC/Core/Security/VOMSService.py +2 -4
- DIRAC/Core/Security/m2crypto/X509Certificate.py +4 -6
- DIRAC/Core/Security/m2crypto/asn1_utils.py +17 -5
- DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
- DIRAC/Core/Tornado/Client/ClientSelector.py +4 -1
- DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
- DIRAC/Core/Utilities/CGroups2.py +328 -0
- DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
- DIRAC/Core/Utilities/DErrno.py +5 -309
- DIRAC/Core/Utilities/Extensions.py +10 -1
- DIRAC/Core/Utilities/File.py +1 -1
- DIRAC/Core/Utilities/Graphs/GraphData.py +1 -1
- DIRAC/Core/Utilities/Graphs/GraphUtilities.py +6 -1
- DIRAC/Core/Utilities/JDL.py +1 -195
- DIRAC/Core/Utilities/List.py +1 -124
- DIRAC/Core/Utilities/MySQL.py +103 -99
- DIRAC/Core/Utilities/Os.py +32 -1
- DIRAC/Core/Utilities/Platform.py +2 -107
- DIRAC/Core/Utilities/Proxy.py +0 -4
- DIRAC/Core/Utilities/ReturnValues.py +7 -252
- DIRAC/Core/Utilities/StateMachine.py +12 -178
- DIRAC/Core/Utilities/Subprocess.py +35 -14
- DIRAC/Core/Utilities/TimeUtilities.py +10 -253
- DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
- DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
- DIRAC/Core/scripts/dirac_agent.py +1 -1
- DIRAC/Core/scripts/dirac_apptainer_exec.py +72 -46
- DIRAC/Core/scripts/dirac_configure.py +1 -3
- DIRAC/Core/scripts/dirac_install_db.py +24 -6
- DIRAC/Core/scripts/dirac_platform.py +1 -92
- DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
- DIRAC/DataManagementSystem/Agent/RequestOperations/RemoveFile.py +7 -6
- DIRAC/DataManagementSystem/Client/FTS3Job.py +71 -34
- DIRAC/DataManagementSystem/DB/FTS3DB.py +7 -3
- DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
- DIRAC/DataManagementSystem/DB/FileCatalogDB.sql +9 -9
- DIRAC/DataManagementSystem/DB/FileCatalogWithFkAndPsDB.sql +9 -9
- DIRAC/DataManagementSystem/Utilities/DMSHelpers.py +6 -2
- DIRAC/DataManagementSystem/scripts/dirac_admin_allow_se.py +13 -8
- DIRAC/DataManagementSystem/scripts/dirac_admin_ban_se.py +13 -8
- DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
- DIRAC/DataManagementSystem/scripts/dirac_dms_protocol_matrix.py +0 -1
- DIRAC/FrameworkSystem/Client/BundleDeliveryClient.py +2 -7
- DIRAC/FrameworkSystem/Client/ComponentInstaller.py +9 -4
- DIRAC/FrameworkSystem/Client/ProxyManagerClient.py +5 -2
- DIRAC/FrameworkSystem/Client/SystemAdministratorClientCLI.py +11 -6
- DIRAC/FrameworkSystem/ConfigTemplate.cfg +2 -0
- DIRAC/FrameworkSystem/DB/AuthDB.py +3 -3
- DIRAC/FrameworkSystem/DB/InstalledComponentsDB.py +4 -4
- DIRAC/FrameworkSystem/DB/ProxyDB.py +11 -3
- DIRAC/FrameworkSystem/DB/TokenDB.py +1 -1
- DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py +8 -6
- DIRAC/FrameworkSystem/Utilities/MonitoringUtilities.py +2 -19
- DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
- DIRAC/FrameworkSystem/Utilities/diracx.py +36 -14
- DIRAC/FrameworkSystem/private/authorization/AuthServer.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_admin_update_pilot.py +18 -11
- DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +7 -8
- DIRAC/Interfaces/API/Dirac.py +27 -15
- DIRAC/Interfaces/API/DiracAdmin.py +45 -17
- DIRAC/Interfaces/API/Job.py +9 -13
- DIRAC/Interfaces/scripts/dirac_admin_allow_site.py +12 -18
- DIRAC/Interfaces/scripts/dirac_admin_ban_site.py +12 -10
- DIRAC/Interfaces/scripts/dirac_admin_get_site_mask.py +4 -13
- DIRAC/Interfaces/scripts/dirac_admin_reset_job.py +3 -6
- DIRAC/Interfaces/scripts/dirac_wms_job_parameters.py +0 -1
- DIRAC/MonitoringSystem/Client/Types/WMSHistory.py +4 -0
- DIRAC/MonitoringSystem/Client/WebAppClient.py +26 -0
- DIRAC/MonitoringSystem/ConfigTemplate.cfg +9 -0
- DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -25
- DIRAC/MonitoringSystem/Service/MonitoringHandler.py +0 -33
- DIRAC/MonitoringSystem/Service/WebAppHandler.py +599 -0
- DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
- DIRAC/ProductionSystem/DB/ProductionDB.sql +4 -4
- DIRAC/ProductionSystem/scripts/dirac_prod_get.py +2 -2
- DIRAC/ProductionSystem/scripts/dirac_prod_get_all.py +2 -2
- DIRAC/ProductionSystem/scripts/dirac_prod_get_trans.py +2 -3
- DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
- DIRAC/RequestManagementSystem/Agent/RequestOperations/ForwardDISET.py +2 -14
- DIRAC/RequestManagementSystem/Client/ReqClient.py +66 -13
- DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
- DIRAC/RequestManagementSystem/DB/RequestDB.py +10 -5
- DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
- DIRAC/RequestManagementSystem/private/RequestValidator.py +40 -46
- DIRAC/ResourceStatusSystem/Client/SiteStatus.py +4 -2
- DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
- DIRAC/ResourceStatusSystem/DB/ResourceManagementDB.py +8 -8
- DIRAC/ResourceStatusSystem/DB/ResourceStatusDB.py +2 -2
- DIRAC/ResourceStatusSystem/Utilities/CSHelpers.py +2 -31
- DIRAC/ResourceStatusSystem/scripts/dirac_rss_set_status.py +30 -12
- DIRAC/Resources/Catalog/RucioFileCatalogClient.py +195 -1
- DIRAC/Resources/Catalog/test/Test_RucioFileCatalogClient.py +181 -0
- DIRAC/Resources/Computing/AREXComputingElement.py +25 -8
- DIRAC/Resources/Computing/BatchSystems/Condor.py +126 -108
- DIRAC/Resources/Computing/BatchSystems/SLURM.py +5 -1
- DIRAC/Resources/Computing/BatchSystems/test/Test_SLURM.py +46 -0
- DIRAC/Resources/Computing/ComputingElement.py +1 -1
- DIRAC/Resources/Computing/HTCondorCEComputingElement.py +44 -44
- DIRAC/Resources/Computing/InProcessComputingElement.py +4 -2
- DIRAC/Resources/Computing/LocalComputingElement.py +1 -18
- DIRAC/Resources/Computing/SSHBatchComputingElement.py +1 -17
- DIRAC/Resources/Computing/SSHComputingElement.py +1 -18
- DIRAC/Resources/Computing/SingularityComputingElement.py +19 -5
- DIRAC/Resources/Computing/test/Test_HTCondorCEComputingElement.py +67 -49
- DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
- DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
- DIRAC/Resources/IdProvider/IdProviderFactory.py +11 -3
- DIRAC/Resources/MessageQueue/StompMQConnector.py +1 -1
- DIRAC/Resources/Storage/GFAL2_StorageBase.py +24 -15
- DIRAC/Resources/Storage/OccupancyPlugins/WLCGAccountingHTTPJson.py +1 -3
- DIRAC/Resources/Storage/StorageBase.py +4 -2
- DIRAC/Resources/Storage/StorageElement.py +6 -7
- DIRAC/StorageManagementSystem/DB/StorageManagementDB.sql +2 -2
- DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +10 -16
- DIRAC/TransformationSystem/Agent/TransformationAgent.py +22 -1
- DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +16 -16
- DIRAC/TransformationSystem/Client/TaskManager.py +2 -4
- DIRAC/TransformationSystem/Client/Transformation.py +6 -7
- DIRAC/TransformationSystem/Client/TransformationClient.py +21 -11
- DIRAC/TransformationSystem/Client/Utilities.py +9 -0
- DIRAC/TransformationSystem/DB/TransformationDB.py +11 -14
- DIRAC/TransformationSystem/DB/TransformationDB.sql +9 -9
- DIRAC/TransformationSystem/Service/TransformationManagerHandler.py +0 -333
- DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
- DIRAC/TransformationSystem/Utilities/TransformationInfo.py +7 -5
- DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
- DIRAC/TransformationSystem/test/Test_TransformationInfo.py +22 -15
- DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
- DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
- DIRAC/WorkloadManagementSystem/Agent/JobAgent.py +38 -26
- DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +12 -8
- DIRAC/WorkloadManagementSystem/Agent/PilotSyncAgent.py +4 -3
- DIRAC/WorkloadManagementSystem/Agent/PushJobAgent.py +13 -13
- DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +18 -14
- DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +18 -51
- DIRAC/WorkloadManagementSystem/Agent/StatesAccountingAgent.py +41 -1
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +45 -4
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobCleaningAgent.py +7 -9
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +9 -2
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +4 -5
- DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +9 -9
- DIRAC/WorkloadManagementSystem/Client/InputDataResolution.py +6 -6
- DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py +10 -11
- DIRAC/WorkloadManagementSystem/Client/JobReport.py +1 -1
- DIRAC/WorkloadManagementSystem/Client/JobState/CachedJobState.py +3 -0
- DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
- DIRAC/WorkloadManagementSystem/Client/JobState/JobState.py +6 -0
- DIRAC/WorkloadManagementSystem/Client/JobStateUpdateClient.py +3 -0
- DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -152
- DIRAC/WorkloadManagementSystem/Client/PoolXMLSlice.py +12 -19
- DIRAC/WorkloadManagementSystem/Client/SandboxStoreClient.py +25 -38
- DIRAC/WorkloadManagementSystem/Client/WMSClient.py +2 -3
- DIRAC/WorkloadManagementSystem/Client/test/Test_Client_DownloadInputData.py +29 -0
- DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +4 -8
- DIRAC/WorkloadManagementSystem/DB/JobDB.py +89 -132
- DIRAC/WorkloadManagementSystem/DB/JobDB.sql +8 -8
- DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
- DIRAC/WorkloadManagementSystem/DB/JobLoggingDB.py +19 -6
- DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +9 -9
- DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py +16 -5
- DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.sql +3 -3
- DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +44 -82
- DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
- DIRAC/WorkloadManagementSystem/DB/tests/Test_JobDB.py +1 -1
- DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
- DIRAC/WorkloadManagementSystem/Executor/JobSanity.py +5 -4
- DIRAC/WorkloadManagementSystem/Executor/JobScheduling.py +4 -0
- DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py +75 -33
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +22 -11
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapperTemplate.py +9 -10
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +60 -10
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
- DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +33 -154
- DIRAC/WorkloadManagementSystem/Service/JobMonitoringHandler.py +5 -323
- DIRAC/WorkloadManagementSystem/Service/JobStateUpdateHandler.py +0 -16
- DIRAC/WorkloadManagementSystem/Service/PilotManagerHandler.py +6 -103
- DIRAC/WorkloadManagementSystem/Service/SandboxStoreHandler.py +7 -53
- DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +16 -79
- DIRAC/WorkloadManagementSystem/Service/WMSUtilities.py +4 -18
- DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -209
- DIRAC/WorkloadManagementSystem/Utilities/JobParameters.py +65 -3
- DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +2 -64
- DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
- DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +73 -7
- DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py +41 -11
- DIRAC/WorkloadManagementSystem/Utilities/RemoteRunner.py +16 -0
- DIRAC/WorkloadManagementSystem/Utilities/Utils.py +36 -1
- DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +15 -0
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -15
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_PilotWrapper.py +16 -0
- DIRAC/WorkloadManagementSystem/scripts/dirac_jobexec.py +7 -2
- DIRAC/WorkloadManagementSystem/scripts/dirac_wms_pilot_job_info.py +1 -1
- DIRAC/__init__.py +62 -60
- DIRAC/tests/Utilities/testJobDefinitions.py +22 -28
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/METADATA +8 -5
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/RECORD +229 -228
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/WHEEL +1 -1
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/entry_points.txt +0 -3
- DIRAC/Core/Utilities/test/Test_List.py +0 -150
- DIRAC/Core/Utilities/test/Test_Time.py +0 -88
- DIRAC/Resources/Computing/PilotBundle.py +0 -70
- DIRAC/TransformationSystem/scripts/dirac_transformation_archive.py +0 -30
- DIRAC/TransformationSystem/scripts/dirac_transformation_clean.py +0 -30
- DIRAC/TransformationSystem/scripts/dirac_transformation_remove_output.py +0 -30
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobManager.py +0 -58
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info/licenses}/LICENSE +0 -0
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/top_level.txt +0 -0
|
@@ -7,7 +7,6 @@ import stat
|
|
|
7
7
|
from urllib.parse import urlparse
|
|
8
8
|
|
|
9
9
|
from DIRAC import S_ERROR, S_OK
|
|
10
|
-
from DIRAC.Resources.Computing.PilotBundle import bundleProxy, writeScript
|
|
11
10
|
from DIRAC.Resources.Computing.SSHComputingElement import SSHComputingElement
|
|
12
11
|
from DIRAC.WorkloadManagementSystem.Client import PilotStatus
|
|
13
12
|
|
|
@@ -80,18 +79,6 @@ class SSHBatchComputingElement(SSHComputingElement):
|
|
|
80
79
|
if not os.access(executableFile, 5):
|
|
81
80
|
os.chmod(executableFile, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
|
82
81
|
|
|
83
|
-
# if no proxy is supplied, the executable can be submitted directly
|
|
84
|
-
# otherwise a wrapper script is needed to get the proxy to the execution node
|
|
85
|
-
# The wrapper script makes debugging more complicated and thus it is
|
|
86
|
-
# recommended to transfer a proxy inside the executable if possible.
|
|
87
|
-
if proxy:
|
|
88
|
-
self.log.verbose("Setting up proxy for payload")
|
|
89
|
-
wrapperContent = bundleProxy(executableFile, proxy)
|
|
90
|
-
name = writeScript(wrapperContent, os.getcwd())
|
|
91
|
-
submitFile = name
|
|
92
|
-
else: # no proxy
|
|
93
|
-
submitFile = executableFile
|
|
94
|
-
|
|
95
82
|
# Submit jobs now
|
|
96
83
|
restJobs = numberOfJobs
|
|
97
84
|
submittedJobs = []
|
|
@@ -100,7 +87,7 @@ class SSHBatchComputingElement(SSHComputingElement):
|
|
|
100
87
|
if slots not in rankHosts:
|
|
101
88
|
continue
|
|
102
89
|
for host in rankHosts[slots]:
|
|
103
|
-
result = self._submitJobToHost(
|
|
90
|
+
result = self._submitJobToHost(executableFile, min(slots, restJobs), host)
|
|
104
91
|
if not result["OK"]:
|
|
105
92
|
continue
|
|
106
93
|
|
|
@@ -114,9 +101,6 @@ class SSHBatchComputingElement(SSHComputingElement):
|
|
|
114
101
|
if restJobs <= 0:
|
|
115
102
|
break
|
|
116
103
|
|
|
117
|
-
if proxy:
|
|
118
|
-
os.remove(submitFile)
|
|
119
|
-
|
|
120
104
|
result = S_OK(submittedJobs)
|
|
121
105
|
result["PilotStampDict"] = stampDict
|
|
122
106
|
return result
|
|
@@ -78,7 +78,6 @@ from DIRAC import S_ERROR, S_OK, gLogger
|
|
|
78
78
|
from DIRAC.Core.Utilities.List import breakListIntoChunks, uniqueElements
|
|
79
79
|
from DIRAC.Resources.Computing.BatchSystems.executeBatch import executeBatchContent
|
|
80
80
|
from DIRAC.Resources.Computing.ComputingElement import ComputingElement
|
|
81
|
-
from DIRAC.Resources.Computing.PilotBundle import bundleProxy, writeScript
|
|
82
81
|
|
|
83
82
|
|
|
84
83
|
class SSH:
|
|
@@ -532,23 +531,7 @@ class SSHComputingElement(ComputingElement):
|
|
|
532
531
|
if not os.access(executableFile, 5):
|
|
533
532
|
os.chmod(executableFile, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
|
534
533
|
|
|
535
|
-
|
|
536
|
-
# otherwise a wrapper script is needed to get the proxy to the execution node
|
|
537
|
-
# The wrapper script makes debugging more complicated and thus it is
|
|
538
|
-
# recommended to transfer a proxy inside the executable if possible.
|
|
539
|
-
if proxy:
|
|
540
|
-
self.log.verbose("Setting up proxy for payload")
|
|
541
|
-
wrapperContent = bundleProxy(executableFile, proxy)
|
|
542
|
-
name = writeScript(wrapperContent, os.getcwd())
|
|
543
|
-
submitFile = name
|
|
544
|
-
else: # no proxy
|
|
545
|
-
submitFile = executableFile
|
|
546
|
-
|
|
547
|
-
result = self._submitJobToHost(submitFile, numberOfJobs)
|
|
548
|
-
if proxy:
|
|
549
|
-
os.remove(submitFile)
|
|
550
|
-
|
|
551
|
-
return result
|
|
534
|
+
return self._submitJobToHost(executableFile, numberOfJobs)
|
|
552
535
|
|
|
553
536
|
def _submitJobToHost(self, executableFile, numberOfJobs, host=None):
|
|
554
537
|
"""Submit prepared executable to the given host"""
|
|
@@ -22,7 +22,7 @@ from pathlib import Path
|
|
|
22
22
|
import DIRAC
|
|
23
23
|
from DIRAC import S_ERROR, S_OK, gConfig, gLogger
|
|
24
24
|
from DIRAC.ConfigurationSystem.Client.Helpers import Operations
|
|
25
|
-
from DIRAC.Core.Utilities.
|
|
25
|
+
from DIRAC.Core.Utilities.CGroups2 import CG2Manager
|
|
26
26
|
from DIRAC.Core.Utilities.ThreadScheduler import gThreadScheduler
|
|
27
27
|
from DIRAC.Resources.Computing.ComputingElement import ComputingElement
|
|
28
28
|
from DIRAC.Resources.Storage.StorageElement import StorageElement
|
|
@@ -381,8 +381,18 @@ class SingularityComputingElement(ComputingElement):
|
|
|
381
381
|
for seName in localSEs:
|
|
382
382
|
try:
|
|
383
383
|
# Find the base path if a File protocol is defined
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
se = StorageElement(seName)
|
|
385
|
+
if not se.valid:
|
|
386
|
+
self.log.warn(f"Storage Element {seName} not valid")
|
|
387
|
+
continue
|
|
388
|
+
|
|
389
|
+
mountedPath = se.getStorageParameters(protocol="file")["Value"]["Path"]
|
|
390
|
+
mountOptions = se.getStorageParameters(protocol="file")["Value"].get("MountOptions", "ro")
|
|
391
|
+
if mountOptions not in ("rw", "ro"):
|
|
392
|
+
self.log.warn(f"Unknown mount mode: {mountOptions}")
|
|
393
|
+
continue
|
|
394
|
+
|
|
395
|
+
bindPaths.append(f"{mountedPath}:{mountedPath}:{mountOptions}")
|
|
386
396
|
except KeyError:
|
|
387
397
|
pass
|
|
388
398
|
|
|
@@ -405,7 +415,9 @@ class SingularityComputingElement(ComputingElement):
|
|
|
405
415
|
|
|
406
416
|
self.log.debug(f"Execute singularity command: {cmd}")
|
|
407
417
|
self.log.debug(f"Execute singularity env: {self.__getEnv()}")
|
|
408
|
-
result =
|
|
418
|
+
result = CG2Manager().systemCall(
|
|
419
|
+
0, cmd, callbackFunction=self.sendOutput, env=self.__getEnv(), ceParameters=self.ceParameters
|
|
420
|
+
)
|
|
409
421
|
|
|
410
422
|
self.__runningJobs -= 1
|
|
411
423
|
|
|
@@ -417,7 +429,9 @@ class SingularityComputingElement(ComputingElement):
|
|
|
417
429
|
self.log.error(f"Singularity env was: {self.__getEnv()}")
|
|
418
430
|
debugCmd = [outerCmd[0], "--debug"] + outerCmd[1:] + ["echo", "All okay"]
|
|
419
431
|
self.log.error("Running with debug output to facilitate debugging", debugCmd)
|
|
420
|
-
result =
|
|
432
|
+
result = CG2Manager().systemCall(
|
|
433
|
+
0, debugCmd, callbackFunction=self.sendOutput, env=self.__getEnv(), ceParameters=self.ceParameters
|
|
434
|
+
)
|
|
421
435
|
if proxy and renewTask:
|
|
422
436
|
gThreadScheduler.removeTask(renewTask)
|
|
423
437
|
self.__deleteWorkArea(baseDir)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
tests for HTCondorCEComputingElement module
|
|
4
4
|
"""
|
|
5
|
+
import json
|
|
5
6
|
import uuid
|
|
6
7
|
|
|
7
8
|
import pytest
|
|
@@ -12,18 +13,30 @@ from DIRAC.Resources.Computing.BatchSystems import Condor
|
|
|
12
13
|
|
|
13
14
|
MODNAME = "DIRAC.Resources.Computing.HTCondorCEComputingElement"
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
123
|
|
18
|
-
""
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
STATUS_QUEUE = [
|
|
17
|
+
{
|
|
18
|
+
"ClusterId": 123,
|
|
19
|
+
"ProcId": 2,
|
|
20
|
+
"JobStatus": 5,
|
|
21
|
+
"HoldReasonCode": 4,
|
|
22
|
+
"HoldReasonSubCode": 0,
|
|
23
|
+
"HoldReason": "The credentials for the job are invalid",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"ClusterId": 123,
|
|
27
|
+
"ProcId": 1,
|
|
28
|
+
"JobStatus": 3,
|
|
29
|
+
},
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
STATUS_HISTORY = [
|
|
34
|
+
{
|
|
35
|
+
"ClusterId": 123,
|
|
36
|
+
"ProcId": 0,
|
|
37
|
+
"JobStatus": 4,
|
|
38
|
+
}
|
|
39
|
+
]
|
|
27
40
|
|
|
28
41
|
|
|
29
42
|
@pytest.fixture
|
|
@@ -32,42 +45,47 @@ def setUp():
|
|
|
32
45
|
|
|
33
46
|
|
|
34
47
|
def test_parseCondorStatus():
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
104098.2 2 undefined undefined undefined
|
|
38
|
-
104098.3 3 undefined undefined undefined
|
|
39
|
-
104098.4 4 undefined undefined undefined
|
|
40
|
-
104098.5 5 16 57 Input data are being spooled
|
|
41
|
-
104098.6 5 3 {Condor.HOLD_REASON_SUBCODE} Policy
|
|
42
|
-
104098.7 5 1 0 undefined
|
|
43
|
-
|
|
44
|
-
foo bar
|
|
45
|
-
104096.1 3 16 test test
|
|
46
|
-
104096.2 3 test
|
|
47
|
-
104096.3 5 undefined undefined undefined
|
|
48
|
-
104096.4 7
|
|
49
|
-
""".strip().split(
|
|
50
|
-
"\n"
|
|
51
|
-
)
|
|
52
|
-
# force there to be an empty line
|
|
48
|
+
statusOutput = {"ClusterId": 104098, "ProcId": 1, "JobStatus": 1}
|
|
49
|
+
assert HTCE.getCondorStatus(statusOutput) == ("Waiting", "")
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"foo": "Unknown",
|
|
63
|
-
"104096.1": "Aborted",
|
|
64
|
-
"104096.2": "Aborted",
|
|
65
|
-
"104096.3": "Aborted",
|
|
66
|
-
"104096.4": "Unknown",
|
|
67
|
-
}
|
|
51
|
+
statusOutput = {"ClusterId": 104098, "ProcId": 2, "JobStatus": 2}
|
|
52
|
+
assert HTCE.getCondorStatus(statusOutput) == ("Running", "")
|
|
53
|
+
|
|
54
|
+
statusOutput = {"ClusterId": 104098, "ProcId": 3, "JobStatus": 3}
|
|
55
|
+
assert HTCE.getCondorStatus(statusOutput) == ("Aborted", "")
|
|
56
|
+
|
|
57
|
+
statusOutput = {"ClusterId": 104098, "ProcId": 4, "JobStatus": 4}
|
|
58
|
+
assert HTCE.getCondorStatus(statusOutput) == ("Done", "")
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
statusOutput = {
|
|
61
|
+
"ClusterId": 104098,
|
|
62
|
+
"ProcId": 5,
|
|
63
|
+
"JobStatus": 5,
|
|
64
|
+
"HoldReasonCode": 16,
|
|
65
|
+
"HoldReasonSubCode": 57,
|
|
66
|
+
"HoldReason": "Input data are being spooled",
|
|
67
|
+
}
|
|
68
|
+
assert HTCE.getCondorStatus(statusOutput) == ("Waiting", "Input data are being spooled")
|
|
69
|
+
|
|
70
|
+
statusOutput = {
|
|
71
|
+
"ClusterId": 104098,
|
|
72
|
+
"ProcId": 6,
|
|
73
|
+
"JobStatus": 5,
|
|
74
|
+
"HoldReasonCode": 3,
|
|
75
|
+
"HoldReasonSubCode": HTCE.HOLD_REASON_SUBCODE,
|
|
76
|
+
"HoldReason": "Policy",
|
|
77
|
+
}
|
|
78
|
+
assert HTCE.getCondorStatus(statusOutput) == ("Failed", "Policy")
|
|
79
|
+
|
|
80
|
+
statusOutput = {
|
|
81
|
+
"ClusterId": 104098,
|
|
82
|
+
"ProcId": 7,
|
|
83
|
+
"JobStatus": 5,
|
|
84
|
+
"HoldReasonCode": 1,
|
|
85
|
+
"HoldReasonSubCode": 0,
|
|
86
|
+
"HoldReason": "Aborted by user",
|
|
87
|
+
}
|
|
88
|
+
assert HTCE.getCondorStatus(statusOutput) == ("Aborted", "Aborted by user")
|
|
71
89
|
|
|
72
90
|
|
|
73
91
|
def test_getJobStatus(mocker):
|
|
@@ -75,8 +93,8 @@ def test_getJobStatus(mocker):
|
|
|
75
93
|
mocker.patch(
|
|
76
94
|
MODNAME + ".systemCall",
|
|
77
95
|
side_effect=[
|
|
78
|
-
S_OK((0,
|
|
79
|
-
S_OK((0,
|
|
96
|
+
S_OK((0, json.dumps(STATUS_QUEUE), "")),
|
|
97
|
+
S_OK((0, json.dumps(STATUS_HISTORY), "")),
|
|
80
98
|
S_OK((0, "", "")),
|
|
81
99
|
S_OK((0, "", "")),
|
|
82
100
|
],
|
|
@@ -110,7 +128,7 @@ def test_getJobStatus(mocker):
|
|
|
110
128
|
def test_getJobStatusBatchSystem(mocker):
|
|
111
129
|
"""Test Condor Batch System plugin getJobStatus"""
|
|
112
130
|
patchPopen = mocker.patch("DIRAC.Resources.Computing.BatchSystems.Condor.subprocess.Popen")
|
|
113
|
-
patchPopen.return_value.communicate.side_effect = [(
|
|
131
|
+
patchPopen.return_value.communicate.side_effect = [(json.dumps(STATUS_QUEUE), ""), (json.dumps(STATUS_HISTORY), "")]
|
|
114
132
|
patchPopen.return_value.returncode = 0
|
|
115
133
|
|
|
116
134
|
ret = Condor.Condor().getJobStatus(JobIDList=["123.0", "123.1", "123.2", "333.3"])
|
|
@@ -25,7 +25,7 @@ while True:
|
|
|
25
25
|
if os.path.isfile(stopFile):
|
|
26
26
|
os.remove(stopFile)
|
|
27
27
|
break
|
|
28
|
-
if (time.time() - start) >
|
|
28
|
+
if (time.time() - start) > 5:
|
|
29
29
|
break
|
|
30
30
|
print("End job", jobNumber, time.time())
|
|
31
31
|
"""
|
|
@@ -262,6 +262,7 @@ def test_executeJob_wholeNode8(createAndDelete):
|
|
|
262
262
|
assert "Not enough processors" in submissionResult["Message"]
|
|
263
263
|
|
|
264
264
|
|
|
265
|
+
@pytest.mark.slow
|
|
265
266
|
def test_executeJob_submitAndStop(createAndDelete):
|
|
266
267
|
time.sleep(0.5)
|
|
267
268
|
|
|
@@ -26,3 +26,16 @@ class CheckInIdProvider(OAuth2IdProvider):
|
|
|
26
26
|
|
|
27
27
|
idPScope = f"eduperson_entitlement?value=urn:mace:egi.eu:group:{vo}:role={groupElements[1]}#aai.egi.eu"
|
|
28
28
|
return scope_to_list(idPScope)
|
|
29
|
+
|
|
30
|
+
def fetchToken(self, **kwargs):
|
|
31
|
+
"""Fetch token
|
|
32
|
+
|
|
33
|
+
:param kwargs:
|
|
34
|
+
:return: dict
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if "audience" in kwargs:
|
|
38
|
+
kwargs["resource"] = kwargs["audience"]
|
|
39
|
+
kwargs.pop("audience")
|
|
40
|
+
|
|
41
|
+
return super().fetchToken(**kwargs)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
1
|
+
"""The Identity Provider Factory instantiates IdProvider objects
|
|
2
|
+
according to their configuration
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
import jwt
|
|
5
6
|
|
|
6
7
|
from DIRAC import S_OK, S_ERROR, gLogger, gConfig
|
|
@@ -40,11 +41,12 @@ class IdProviderFactory:
|
|
|
40
41
|
return result
|
|
41
42
|
return self.getIdProvider(result["Value"])
|
|
42
43
|
|
|
43
|
-
def getIdProvider(self, name, **kwargs):
|
|
44
|
+
def getIdProvider(self, name, client_name_prefix="", **kwargs):
|
|
44
45
|
"""This method returns a IdProvider instance corresponding to the supplied
|
|
45
46
|
name.
|
|
46
47
|
|
|
47
48
|
:param str name: the name of the Identity Provider client
|
|
49
|
+
:param str client_name_prefix: name of the client of the IdP
|
|
48
50
|
|
|
49
51
|
:return: S_OK(IdProvider)/S_ERROR()
|
|
50
52
|
"""
|
|
@@ -68,8 +70,14 @@ class IdProviderFactory:
|
|
|
68
70
|
if not result["OK"]:
|
|
69
71
|
self.log.error("Failed to read configuration", f"{name}: {result['Message']}")
|
|
70
72
|
return result
|
|
73
|
+
|
|
71
74
|
pDict = result["Value"]
|
|
72
75
|
|
|
76
|
+
if client_name_prefix:
|
|
77
|
+
client_name_prefix = client_name_prefix + "_"
|
|
78
|
+
pDict["client_id"] = pDict[f"{client_name_prefix}client_id"]
|
|
79
|
+
pDict["client_secret"] = pDict[f"{client_name_prefix}client_secret"]
|
|
80
|
+
|
|
73
81
|
pDict.update(kwargs)
|
|
74
82
|
pDict["ProviderName"] = name
|
|
75
83
|
|
|
@@ -172,7 +172,7 @@ class StompMQConnector(MQConnector):
|
|
|
172
172
|
try:
|
|
173
173
|
try:
|
|
174
174
|
self.connection.send(body=json.dumps(message), destination=destination)
|
|
175
|
-
except stomp.exception.StompException:
|
|
175
|
+
except (stomp.exception.StompException, ConnectionError):
|
|
176
176
|
self.connect()
|
|
177
177
|
self.connection.send(body=json.dumps(message), destination=destination)
|
|
178
178
|
except Exception as e:
|
|
@@ -142,9 +142,11 @@ class GFAL2_StorageBase(StorageBase):
|
|
|
142
142
|
# Workaround for https://github.com/xrootd/xrootd/issues/2396
|
|
143
143
|
# xrootd internaly sets nproc, so we save the limit and reset it
|
|
144
144
|
# just after creating the context
|
|
145
|
-
|
|
145
|
+
saved_limit = getrlimit(RLIMIT_NPROC)
|
|
146
146
|
self.ctx = gfal2.creat_context()
|
|
147
|
-
|
|
147
|
+
new_limit = getrlimit(RLIMIT_NPROC)
|
|
148
|
+
if saved_limit != new_limit:
|
|
149
|
+
setrlimit(RLIMIT_NPROC, saved_limit)
|
|
148
150
|
|
|
149
151
|
# by default turn off BDII checks
|
|
150
152
|
self.ctx.set_opt_boolean("BDII", "ENABLE", False)
|
|
@@ -163,6 +165,10 @@ class GFAL2_StorageBase(StorageBase):
|
|
|
163
165
|
os.environ.get("DIRAC_GFAL_GRIDFTP_ENABLE_IPV6", "true").lower() not in ["false", "no"],
|
|
164
166
|
)
|
|
165
167
|
|
|
168
|
+
# Disable retrieving the bearer token for every operations.
|
|
169
|
+
# It is only useful for TPC
|
|
170
|
+
self.ctx.set_opt_boolean("HTTP PLUGIN", "RETRIEVE_BEARER_TOKEN", False)
|
|
171
|
+
|
|
166
172
|
# spaceToken used for copying from and to the storage element
|
|
167
173
|
self.spaceToken = parameters.get("SpaceToken", "")
|
|
168
174
|
# stageTimeout, default timeout to try and stage/pin a file
|
|
@@ -303,20 +309,23 @@ class GFAL2_StorageBase(StorageBase):
|
|
|
303
309
|
failed = {}
|
|
304
310
|
successful = {}
|
|
305
311
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
312
|
+
# In principle we only need the bearer token when doing TPC, however it's a
|
|
313
|
+
# bit cumbersome to test, so we always re-enable it when uploading
|
|
314
|
+
with setGfalSetting(self.ctx, "HTTP PLUGIN", "RETRIEVE_BEARER_TOKEN", True):
|
|
315
|
+
for dest_url, src_file in urls.items():
|
|
316
|
+
if not src_file:
|
|
317
|
+
errStr = "GFAL2_StorageBase.putFile: Source file not set. Argument must be a dictionary \
|
|
318
|
+
(or a list of a dictionary) {url : local path}"
|
|
319
|
+
self.log.debug(errStr)
|
|
320
|
+
failed[dest_url] = errStr
|
|
321
|
+
continue
|
|
313
322
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
323
|
+
try:
|
|
324
|
+
successful[dest_url] = self._putSingleFile(src_file, dest_url, sourceSize)
|
|
325
|
+
except (gfal2.GError, ValueError, RuntimeError) as e:
|
|
326
|
+
detailMsg = f"Failed to copy {src_file} to {dest_url}: {repr(e)}"
|
|
327
|
+
self.log.debug("Exception while copying", detailMsg)
|
|
328
|
+
failed[dest_url] = detailMsg
|
|
320
329
|
|
|
321
330
|
return {"Failed": failed, "Successful": successful}
|
|
322
331
|
|
|
@@ -41,9 +41,7 @@ class WLCGAccountingHTTPJson(WLCGAccountingJson):
|
|
|
41
41
|
"""
|
|
42
42
|
try:
|
|
43
43
|
with open(filePath, "w") as fd:
|
|
44
|
-
|
|
45
|
-
userProxy = getProxyLocation()
|
|
46
|
-
res = requests.get(occupancyLFN, cert=userProxy, verify=caPath, timeout=30)
|
|
44
|
+
res = requests.get(occupancyLFN, cert=getProxyLocation(), verify=getCAsLocation(), timeout=30)
|
|
47
45
|
res.raise_for_status()
|
|
48
46
|
fd.write(res.text)
|
|
49
47
|
except Exception as e:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Base Storage Class provides the base interface for all storage plug-ins
|
|
2
2
|
|
|
3
3
|
exists()
|
|
4
4
|
|
|
@@ -33,6 +33,7 @@ These are the methods for getting information about the Storage:
|
|
|
33
33
|
getOccupancy()
|
|
34
34
|
|
|
35
35
|
"""
|
|
36
|
+
|
|
36
37
|
import errno
|
|
37
38
|
import json
|
|
38
39
|
import os
|
|
@@ -113,7 +114,8 @@ class StorageBase:
|
|
|
113
114
|
|
|
114
115
|
def getParameters(self):
|
|
115
116
|
"""Get the parameters with which the storage was instantiated"""
|
|
116
|
-
parameterDict = dict(self.
|
|
117
|
+
parameterDict = dict(self._allProtocolParameters)
|
|
118
|
+
parameterDict.update(self.protocolParameters)
|
|
117
119
|
parameterDict["StorageName"] = self.name
|
|
118
120
|
parameterDict["PluginName"] = self.pluginName
|
|
119
121
|
parameterDict["URLBase"] = self.getURLBase().get("Value", "")
|
|
@@ -65,14 +65,13 @@ class StorageElementCache:
|
|
|
65
65
|
# Because the gfal2 context caches the proxy location,
|
|
66
66
|
# we also use the proxy location as a key.
|
|
67
67
|
# In practice, there should almost always be one, except for the REA
|
|
68
|
-
# If we see its memory
|
|
69
|
-
proxyLoc = getProxyLocation()
|
|
68
|
+
# If we see its memory consumption exploding, this might be a place to look
|
|
70
69
|
|
|
71
70
|
# ensure protocolSections is hashable! (tuple)
|
|
72
71
|
if isinstance(protocolSections, list):
|
|
73
72
|
protocolSections = tuple(protocolSections)
|
|
74
73
|
|
|
75
|
-
argTuple = (tId, name, protocolSections, vo,
|
|
74
|
+
argTuple = (tId, name, protocolSections, vo, getProxyLocation())
|
|
76
75
|
seObj = self.seCache.get(argTuple)
|
|
77
76
|
|
|
78
77
|
if not seObj:
|
|
@@ -439,10 +438,6 @@ class StorageElementItem:
|
|
|
439
438
|
|
|
440
439
|
kwargs["occupancyLFN"] = occupancyLFN
|
|
441
440
|
|
|
442
|
-
filteredPlugins = self.__filterPlugins("getOccupancy")
|
|
443
|
-
if not filteredPlugins:
|
|
444
|
-
return S_ERROR(errno.EPROTONOSUPPORT, "No storage plugins to query the occupancy")
|
|
445
|
-
|
|
446
441
|
# Call occupancy plugin if requested
|
|
447
442
|
occupancyPlugin = self.options.get("OccupancyPlugin")
|
|
448
443
|
if occupancyPlugin:
|
|
@@ -460,6 +455,10 @@ class StorageElementItem:
|
|
|
460
455
|
except Exception as e:
|
|
461
456
|
return S_ERROR(f"Occupancy plugin failed: {str(e)}")
|
|
462
457
|
|
|
458
|
+
filteredPlugins = self.__filterPlugins("getOccupancy")
|
|
459
|
+
if not filteredPlugins:
|
|
460
|
+
return S_ERROR(errno.EPROTONOSUPPORT, "No storage plugins to query the occupancy")
|
|
461
|
+
|
|
463
462
|
# Try all of the storages one by one
|
|
464
463
|
for storage in filteredPlugins:
|
|
465
464
|
# The result of the plugin is always in B
|
|
@@ -23,7 +23,7 @@ CREATE TABLE Tasks(
|
|
|
23
23
|
`CompleteTime` DATETIME,
|
|
24
24
|
`CallBackMethod` VARCHAR(255),
|
|
25
25
|
`SourceTaskID` VARCHAR(32),
|
|
26
|
-
PRIMARY KEY(`TaskID
|
|
26
|
+
PRIMARY KEY(`TaskID`),
|
|
27
27
|
INDEX(`TaskID`,`Status`)
|
|
28
28
|
)ENGINE=INNODB;
|
|
29
29
|
|
|
@@ -41,7 +41,7 @@ CREATE TABLE CacheReplicas(
|
|
|
41
41
|
`LastUpdate` DATETIME,
|
|
42
42
|
`Reason` VARCHAR(255),
|
|
43
43
|
`Links` INTEGER DEFAULT 0,
|
|
44
|
-
PRIMARY KEY (`ReplicaID
|
|
44
|
+
PRIMARY KEY (`ReplicaID`),
|
|
45
45
|
INDEX(`ReplicaID`,`Status`,`SE`)
|
|
46
46
|
)ENGINE=INNODB;
|
|
47
47
|
|
|
@@ -7,23 +7,21 @@
|
|
|
7
7
|
In case you want to further extend it you are required to follow the note on the
|
|
8
8
|
initialize method and on the _getClients method.
|
|
9
9
|
"""
|
|
10
|
-
import time
|
|
11
|
-
import datetime
|
|
12
10
|
import concurrent.futures
|
|
11
|
+
import datetime
|
|
12
|
+
import time
|
|
13
13
|
|
|
14
|
-
from DIRAC import S_OK
|
|
15
|
-
|
|
14
|
+
from DIRAC import S_OK, gConfig
|
|
15
|
+
from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
|
|
16
16
|
from DIRAC.Core.Base.AgentModule import AgentModule
|
|
17
17
|
from DIRAC.Core.Security.ProxyInfo import getProxyInfo
|
|
18
|
-
from DIRAC.Core.Utilities.List import breakListIntoChunks
|
|
19
18
|
from DIRAC.Core.Utilities.Dictionaries import breakDictionaryIntoChunks
|
|
20
|
-
from DIRAC.
|
|
19
|
+
from DIRAC.Core.Utilities.List import breakListIntoChunks
|
|
20
|
+
from DIRAC.TransformationSystem.Agent.TransformationAgentsUtilities import TransformationAgentsUtilities
|
|
21
21
|
from DIRAC.TransformationSystem.Client.FileReport import FileReport
|
|
22
|
-
from DIRAC.TransformationSystem.Client.WorkflowTasks import WorkflowTasks
|
|
23
22
|
from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
|
|
24
|
-
from DIRAC.TransformationSystem.
|
|
23
|
+
from DIRAC.TransformationSystem.Client.WorkflowTasks import WorkflowTasks
|
|
25
24
|
from DIRAC.WorkloadManagementSystem.Client import JobStatus
|
|
26
|
-
from DIRAC.WorkloadManagementSystem.Client.JobManagerClient import JobManagerClient
|
|
27
25
|
|
|
28
26
|
AGENT_NAME = "Transformation/TaskManagerAgentBase"
|
|
29
27
|
|
|
@@ -40,7 +38,6 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
|
|
|
40
38
|
TransformationAgentsUtilities.__init__(self)
|
|
41
39
|
|
|
42
40
|
self.transClient = None
|
|
43
|
-
self.jobManagerClient = None
|
|
44
41
|
self.transType = []
|
|
45
42
|
|
|
46
43
|
self.tasksPerLoop = 50
|
|
@@ -69,7 +66,6 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
|
|
|
69
66
|
|
|
70
67
|
# Default clients
|
|
71
68
|
self.transClient = TransformationClient()
|
|
72
|
-
self.jobManagerClient = JobManagerClient()
|
|
73
69
|
|
|
74
70
|
# Bulk submission flag
|
|
75
71
|
self.bulkSubmissionFlag = self.am_getOption("BulkSubmission", self.bulkSubmissionFlag)
|
|
@@ -193,11 +189,9 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
|
|
|
193
189
|
else:
|
|
194
190
|
# Get the transformations which should be submitted
|
|
195
191
|
self.tasksPerLoop = self.am_getOption("TasksPerLoop", self.tasksPerLoop)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
else:
|
|
200
|
-
self.maxParametricJobs = res["Value"]
|
|
192
|
+
self.maxParametricJobs = gConfig.getValue(
|
|
193
|
+
"/Systems/WorkloadManagement/Services/JobManager/MaxParametricJobs", self.maxParametricJobs
|
|
194
|
+
)
|
|
201
195
|
|
|
202
196
|
self._addOperationForTransformations(
|
|
203
197
|
self.operationsOnTransformationDict,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""TransformationAgent processes transformations found in the transformation database.
|
|
2
2
|
|
|
3
3
|
The following options can be set for the TransformationAgent.
|
|
4
4
|
|
|
@@ -8,6 +8,7 @@ The following options can be set for the TransformationAgent.
|
|
|
8
8
|
:dedent: 2
|
|
9
9
|
:caption: TransformationAgent options
|
|
10
10
|
"""
|
|
11
|
+
|
|
11
12
|
from importlib import import_module
|
|
12
13
|
|
|
13
14
|
import time
|
|
@@ -15,6 +16,7 @@ import os
|
|
|
15
16
|
import datetime
|
|
16
17
|
import pickle
|
|
17
18
|
import concurrent.futures
|
|
19
|
+
from pathlib import Path
|
|
18
20
|
|
|
19
21
|
from DIRAC import S_OK, S_ERROR
|
|
20
22
|
from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
|
|
@@ -127,6 +129,9 @@ class TransformationAgent(AgentModule, TransformationAgentsUtilities):
|
|
|
127
129
|
if not res["OK"]:
|
|
128
130
|
self._logError("Failed to obtain transformations:", res["Message"])
|
|
129
131
|
return S_OK()
|
|
132
|
+
|
|
133
|
+
active_trans_ids = [t["TransformationID"] for t in res["Value"]]
|
|
134
|
+
self.cleanOldTransformationCache(active_trans_ids)
|
|
130
135
|
# Process the transformations
|
|
131
136
|
count = 0
|
|
132
137
|
future_to_transID = {}
|
|
@@ -164,6 +169,22 @@ class TransformationAgent(AgentModule, TransformationAgentsUtilities):
|
|
|
164
169
|
|
|
165
170
|
return S_OK()
|
|
166
171
|
|
|
172
|
+
def cleanOldTransformationCache(self, active_trans_ids: list[int]):
|
|
173
|
+
cache_filenames = {Path(self.__cacheFile(tid)) for tid in active_trans_ids}
|
|
174
|
+
existing_caches = set(Path(self.workDirectory).glob("*.pkl"))
|
|
175
|
+
useless_cache_files = existing_caches - cache_filenames
|
|
176
|
+
|
|
177
|
+
if useless_cache_files:
|
|
178
|
+
self._logInfo(f"Found potentially {len(useless_cache_files)} useless cache files")
|
|
179
|
+
|
|
180
|
+
# Since idle transformations aren't in active_trans_ids, let's filter it more
|
|
181
|
+
# and take only files that haven't been touched for 2 month
|
|
182
|
+
last_update_threshold = (datetime.datetime.utcnow() - datetime.timedelta(days=60)).timestamp()
|
|
183
|
+
|
|
184
|
+
for cache_file in useless_cache_files:
|
|
185
|
+
if Path(cache_file).stat().st_mtime < last_update_threshold:
|
|
186
|
+
cache_file.unlink()
|
|
187
|
+
|
|
167
188
|
def getTransformations(self):
|
|
168
189
|
"""Obtain the transformations to be executed - this is executed at the start of every loop (it's really the
|
|
169
190
|
only real thing in the execute()
|