DIRAC 9.0.0a54__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 +34 -32
- 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 +9 -2
- DIRAC/ConfigurationSystem/Client/test/Test_PathFinder.py +41 -1
- DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
- 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/Security/DiracX.py +12 -7
- DIRAC/Core/Security/IAMService.py +4 -3
- DIRAC/Core/Security/ProxyInfo.py +9 -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/ClassAd/ClassAdLight.py +4 -290
- DIRAC/Core/Utilities/DErrno.py +5 -309
- DIRAC/Core/Utilities/Extensions.py +10 -1
- DIRAC/Core/Utilities/Graphs/GraphData.py +1 -1
- DIRAC/Core/Utilities/JDL.py +1 -195
- DIRAC/Core/Utilities/List.py +1 -124
- DIRAC/Core/Utilities/MySQL.py +101 -97
- DIRAC/Core/Utilities/Os.py +32 -1
- DIRAC/Core/Utilities/Platform.py +2 -107
- DIRAC/Core/Utilities/ReturnValues.py +7 -252
- DIRAC/Core/Utilities/StateMachine.py +12 -178
- 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 +16 -7
- 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 +3 -0
- DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
- DIRAC/DataManagementSystem/Utilities/DMSHelpers.py +6 -2
- DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
- DIRAC/DataManagementSystem/scripts/dirac_dms_protocol_matrix.py +0 -1
- DIRAC/FrameworkSystem/Client/ComponentInstaller.py +4 -2
- DIRAC/FrameworkSystem/DB/ProxyDB.py +9 -5
- DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
- DIRAC/FrameworkSystem/Utilities/diracx.py +2 -74
- DIRAC/FrameworkSystem/private/authorization/AuthServer.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +1 -1
- DIRAC/Interfaces/API/Dirac.py +27 -13
- DIRAC/Interfaces/API/DiracAdmin.py +42 -7
- DIRAC/Interfaces/API/Job.py +1 -0
- DIRAC/Interfaces/scripts/dirac_admin_allow_site.py +7 -1
- DIRAC/Interfaces/scripts/dirac_admin_ban_site.py +7 -1
- 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/scripts/dirac_prod_get_trans.py +2 -3
- DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
- DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
- DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
- DIRAC/ResourceStatusSystem/Client/SiteStatus.py +4 -2
- DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
- DIRAC/ResourceStatusSystem/Utilities/CSHelpers.py +2 -31
- DIRAC/ResourceStatusSystem/scripts/dirac_rss_set_status.py +18 -4
- DIRAC/Resources/Catalog/RucioFileCatalogClient.py +1 -1
- DIRAC/Resources/Computing/AREXComputingElement.py +19 -3
- 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/HTCondorCEComputingElement.py +37 -43
- DIRAC/Resources/Computing/SingularityComputingElement.py +6 -1
- 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/Storage/StorageBase.py +4 -2
- DIRAC/Resources/Storage/StorageElement.py +4 -4
- DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +10 -16
- DIRAC/TransformationSystem/Agent/TransformationAgent.py +22 -1
- DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +15 -15
- DIRAC/TransformationSystem/Client/Transformation.py +2 -1
- DIRAC/TransformationSystem/Client/TransformationClient.py +0 -7
- DIRAC/TransformationSystem/Client/Utilities.py +9 -0
- DIRAC/TransformationSystem/Service/TransformationManagerHandler.py +0 -336
- 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/Workflow/Modules/test/Test_Modules.py +5 -0
- DIRAC/WorkloadManagementSystem/Agent/JobAgent.py +1 -5
- DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +11 -7
- DIRAC/WorkloadManagementSystem/Agent/PilotSyncAgent.py +4 -3
- DIRAC/WorkloadManagementSystem/Agent/PushJobAgent.py +13 -13
- DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +10 -13
- DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +18 -51
- DIRAC/WorkloadManagementSystem/Agent/StatesAccountingAgent.py +41 -1
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +2 -0
- 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 +8 -2
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +4 -5
- DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +7 -5
- DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py +10 -11
- DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
- DIRAC/WorkloadManagementSystem/Client/JobStateUpdateClient.py +3 -0
- DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -152
- 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 +40 -69
- DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
- DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +9 -9
- DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py +3 -2
- DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +28 -39
- 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 +3 -3
- DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py +2 -14
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +14 -9
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +36 -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 -102
- DIRAC/WorkloadManagementSystem/Service/SandboxStoreHandler.py +5 -51
- DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +16 -79
- DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -199
- 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 +2 -0
- 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 -5
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_PilotWrapper.py +16 -0
- DIRAC/__init__.py +55 -54
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/METADATA +6 -4
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/RECORD +160 -160
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/WHEEL +1 -1
- {dirac-9.0.0a54.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/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.0a54.dist-info → dirac-9.0.7.dist-info}/licenses/LICENSE +0 -0
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/top_level.txt +0 -0
|
@@ -50,6 +50,7 @@ When using a local condor_schedd look at the HTCondor documentation for enabling
|
|
|
50
50
|
|
|
51
51
|
import datetime
|
|
52
52
|
import errno
|
|
53
|
+
import json
|
|
53
54
|
import os
|
|
54
55
|
import subprocess
|
|
55
56
|
import tempfile
|
|
@@ -63,10 +64,14 @@ from DIRAC.Core.Utilities.File import mkDir
|
|
|
63
64
|
from DIRAC.Core.Utilities.List import breakListIntoChunks
|
|
64
65
|
from DIRAC.Core.Utilities.Subprocess import systemCall
|
|
65
66
|
from DIRAC.FrameworkSystem.private.authorization.utils.Tokens import writeToTokenFile
|
|
66
|
-
from DIRAC.Resources.Computing.BatchSystems.Condor import
|
|
67
|
+
from DIRAC.Resources.Computing.BatchSystems.Condor import (
|
|
68
|
+
HOLD_REASON_SUBCODE,
|
|
69
|
+
STATE_ATTRIBUTES,
|
|
70
|
+
getCondorStatus,
|
|
71
|
+
subTemplate,
|
|
72
|
+
)
|
|
67
73
|
from DIRAC.Resources.Computing.ComputingElement import ComputingElement
|
|
68
74
|
from DIRAC.WorkloadManagementSystem.Client import PilotStatus
|
|
69
|
-
from DIRAC.WorkloadManagementSystem.Client.PilotManagerClient import PilotManagerClient
|
|
70
75
|
|
|
71
76
|
MANDATORY_PARAMETERS = ["Queue"]
|
|
72
77
|
DEFAULT_WORKINGDIRECTORY = "/opt/dirac/pro/runit/WorkloadManagement/SiteDirectorHT"
|
|
@@ -386,33 +391,10 @@ class HTCondorCEComputingElement(ComputingElement):
|
|
|
386
391
|
|
|
387
392
|
#############################################################################
|
|
388
393
|
def getCEStatus(self):
|
|
389
|
-
"""Method to return information on running and pending jobs.
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
"""
|
|
394
|
-
result = S_OK()
|
|
395
|
-
result["SubmittedJobs"] = 0
|
|
396
|
-
result["RunningJobs"] = 0
|
|
397
|
-
result["WaitingJobs"] = 0
|
|
398
|
-
|
|
399
|
-
# getWaitingPilots
|
|
400
|
-
condDict = {"DestinationSite": self.ceName, "Status": PilotStatus.PILOT_WAITING_STATES}
|
|
401
|
-
res = PilotManagerClient().countPilots(condDict)
|
|
402
|
-
if res["OK"]:
|
|
403
|
-
result["WaitingJobs"] = int(res["Value"])
|
|
404
|
-
else:
|
|
405
|
-
self.log.warn(f"Failure getting pilot count for {self.ceName}: {res['Message']} ")
|
|
406
|
-
|
|
407
|
-
# getRunningPilots
|
|
408
|
-
condDict = {"DestinationSite": self.ceName, "Status": PilotStatus.RUNNING}
|
|
409
|
-
res = PilotManagerClient().countPilots(condDict)
|
|
410
|
-
if res["OK"]:
|
|
411
|
-
result["RunningJobs"] = int(res["Value"])
|
|
412
|
-
else:
|
|
413
|
-
self.log.warn(f"Failure getting pilot count for {self.ceName}: {res['Message']} ")
|
|
414
|
-
|
|
415
|
-
return result
|
|
394
|
+
"""Method to return information on running and pending jobs."""
|
|
395
|
+
return S_ERROR(
|
|
396
|
+
"getCEStatus() not supported for HTCondorCEComputingElement: HTCondor does not expose this information"
|
|
397
|
+
)
|
|
416
398
|
|
|
417
399
|
def getJobStatus(self, jobIDList):
|
|
418
400
|
"""Get the status information for the given list of jobs"""
|
|
@@ -424,45 +406,57 @@ class HTCondorCEComputingElement(ComputingElement):
|
|
|
424
406
|
if isinstance(jobIDList, str):
|
|
425
407
|
jobIDList = [jobIDList]
|
|
426
408
|
|
|
409
|
+
self.tokenFile = None
|
|
427
410
|
resultDict = {}
|
|
428
411
|
condorIDs = {}
|
|
429
412
|
# Get all condorIDs so we can just call condor_q and condor_history once
|
|
430
413
|
for jobReference in jobIDList:
|
|
431
414
|
jobReference = jobReference.split(":::")[0]
|
|
432
|
-
condorIDs[jobReference] =
|
|
415
|
+
condorIDs[self._jobReferenceToCondorID(jobReference)] = jobReference
|
|
433
416
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
qList = []
|
|
437
|
-
for _condorIDs in breakListIntoChunks(condorIDs.values(), 100):
|
|
438
|
-
# This will return a list of 1245.75 3 undefined undefined undefined
|
|
417
|
+
jobsMetadata = []
|
|
418
|
+
for _condorIDs in breakListIntoChunks(condorIDs.keys(), 100):
|
|
439
419
|
cmd = ["condor_q"]
|
|
440
420
|
cmd.extend(self.remoteScheddOptions.strip().split(" "))
|
|
441
421
|
cmd.extend(_condorIDs)
|
|
442
|
-
cmd.extend(["-
|
|
422
|
+
cmd.extend(["-attributes", STATE_ATTRIBUTES])
|
|
423
|
+
cmd.extend(["-json"])
|
|
443
424
|
result = self._executeCondorCommand(cmd, keepTokenFile=True)
|
|
444
425
|
if not result["OK"]:
|
|
445
426
|
return result
|
|
446
427
|
|
|
447
|
-
|
|
428
|
+
if result["Value"]:
|
|
429
|
+
jobsMetadata.extend(json.loads(result["Value"]))
|
|
448
430
|
|
|
449
431
|
condorHistCall = ["condor_history"]
|
|
450
432
|
condorHistCall.extend(self.remoteScheddOptions.strip().split(" "))
|
|
451
433
|
condorHistCall.extend(_condorIDs)
|
|
452
|
-
condorHistCall.extend(["-
|
|
434
|
+
condorHistCall.extend(["-attributes", STATE_ATTRIBUTES])
|
|
435
|
+
condorHistCall.extend(["-json"])
|
|
453
436
|
result = self._executeCondorCommand(cmd, keepTokenFile=True)
|
|
454
437
|
if not result["OK"]:
|
|
455
438
|
return result
|
|
456
439
|
|
|
457
|
-
|
|
440
|
+
if result["Value"]:
|
|
441
|
+
jobsMetadata.extend(json.loads(result["Value"]))
|
|
458
442
|
|
|
459
|
-
|
|
460
|
-
|
|
443
|
+
foundJobIDs = set()
|
|
444
|
+
for jobDict in jobsMetadata:
|
|
445
|
+
jobStatus, reason = getCondorStatus(jobDict)
|
|
446
|
+
condorId = f"{jobDict['ClusterId']}.{jobDict['ProcId']}"
|
|
447
|
+
jobReference = condorIDs.get(condorId)
|
|
461
448
|
|
|
462
449
|
if jobStatus == PilotStatus.ABORTED:
|
|
463
|
-
self.log.verbose("Job", f"{
|
|
450
|
+
self.log.verbose("Job", f"{jobReference} held: {reason}")
|
|
451
|
+
|
|
452
|
+
resultDict[jobReference] = jobStatus
|
|
453
|
+
foundJobIDs.add(jobReference)
|
|
464
454
|
|
|
465
|
-
|
|
455
|
+
# Check if we have any jobs that were not found in the condor_q or condor_history
|
|
456
|
+
for jobReference in condorIDs.values():
|
|
457
|
+
if jobReference not in foundJobIDs:
|
|
458
|
+
self.log.verbose("Job", f"{jobReference} not found in condor_q or condor_history")
|
|
459
|
+
resultDict[jobReference] = PilotStatus.UNKNOWN
|
|
466
460
|
|
|
467
461
|
self.tokenFile = None
|
|
468
462
|
|
|
@@ -387,7 +387,12 @@ class SingularityComputingElement(ComputingElement):
|
|
|
387
387
|
continue
|
|
388
388
|
|
|
389
389
|
mountedPath = se.getStorageParameters(protocol="file")["Value"]["Path"]
|
|
390
|
-
|
|
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}")
|
|
391
396
|
except KeyError:
|
|
392
397
|
pass
|
|
393
398
|
|
|
@@ -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
|
|
|
@@ -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", "")
|
|
@@ -438,10 +438,6 @@ class StorageElementItem:
|
|
|
438
438
|
|
|
439
439
|
kwargs["occupancyLFN"] = occupancyLFN
|
|
440
440
|
|
|
441
|
-
filteredPlugins = self.__filterPlugins("getOccupancy")
|
|
442
|
-
if not filteredPlugins:
|
|
443
|
-
return S_ERROR(errno.EPROTONOSUPPORT, "No storage plugins to query the occupancy")
|
|
444
|
-
|
|
445
441
|
# Call occupancy plugin if requested
|
|
446
442
|
occupancyPlugin = self.options.get("OccupancyPlugin")
|
|
447
443
|
if occupancyPlugin:
|
|
@@ -459,6 +455,10 @@ class StorageElementItem:
|
|
|
459
455
|
except Exception as e:
|
|
460
456
|
return S_ERROR(f"Occupancy plugin failed: {str(e)}")
|
|
461
457
|
|
|
458
|
+
filteredPlugins = self.__filterPlugins("getOccupancy")
|
|
459
|
+
if not filteredPlugins:
|
|
460
|
+
return S_ERROR(errno.EPROTONOSUPPORT, "No storage plugins to query the occupancy")
|
|
461
|
+
|
|
462
462
|
# Try all of the storages one by one
|
|
463
463
|
for storage in filteredPlugins:
|
|
464
464
|
# The result of the plugin is always in B
|
|
@@ -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()
|
|
@@ -16,14 +16,12 @@ from datetime import datetime, timedelta
|
|
|
16
16
|
|
|
17
17
|
# # from DIRAC
|
|
18
18
|
from DIRAC import S_ERROR, S_OK
|
|
19
|
-
from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData
|
|
20
19
|
from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
|
|
21
20
|
from DIRAC.Core.Base.AgentModule import AgentModule
|
|
22
21
|
from DIRAC.Core.Utilities.DErrno import cmpError
|
|
23
22
|
from DIRAC.Core.Utilities.List import breakListIntoChunks
|
|
24
23
|
from DIRAC.Core.Utilities.Proxy import executeWithUserProxy
|
|
25
24
|
from DIRAC.Core.Utilities.ReturnValues import returnSingleResult
|
|
26
|
-
from DIRAC.DataManagementSystem.Client.DataManager import DataManager
|
|
27
25
|
from DIRAC.RequestManagementSystem.Client.File import File
|
|
28
26
|
from DIRAC.RequestManagementSystem.Client.Operation import Operation
|
|
29
27
|
from DIRAC.RequestManagementSystem.Client.ReqClient import ReqClient
|
|
@@ -34,8 +32,12 @@ from DIRAC.Resources.Catalog.FileCatalogClient import FileCatalogClient
|
|
|
34
32
|
from DIRAC.Resources.Storage.StorageElement import StorageElement
|
|
35
33
|
from DIRAC.TransformationSystem.Client import TransformationStatus
|
|
36
34
|
from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
|
|
37
|
-
from DIRAC.WorkloadManagementSystem.
|
|
38
|
-
from DIRAC.WorkloadManagementSystem.
|
|
35
|
+
from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
|
|
36
|
+
from DIRAC.WorkloadManagementSystem.Service.JobPolicy import (
|
|
37
|
+
RIGHT_DELETE,
|
|
38
|
+
RIGHT_KILL,
|
|
39
|
+
)
|
|
40
|
+
from DIRAC.WorkloadManagementSystem.DB.StatusUtils import kill_delete_jobs
|
|
39
41
|
|
|
40
42
|
# # agent's name
|
|
41
43
|
AGENT_NAME = "Transformation/TransformationCleaningAgent"
|
|
@@ -59,12 +61,12 @@ class TransformationCleaningAgent(AgentModule):
|
|
|
59
61
|
|
|
60
62
|
# # transformation client
|
|
61
63
|
self.transClient = None
|
|
62
|
-
# # wms client
|
|
63
|
-
self.wmsClient = None
|
|
64
64
|
# # request client
|
|
65
65
|
self.reqClient = None
|
|
66
66
|
# # file catalog client
|
|
67
67
|
self.metadataClient = None
|
|
68
|
+
# # JobDB
|
|
69
|
+
self.jobDB = None
|
|
68
70
|
|
|
69
71
|
# # transformations types
|
|
70
72
|
self.transformationTypes = None
|
|
@@ -119,14 +121,12 @@ class TransformationCleaningAgent(AgentModule):
|
|
|
119
121
|
|
|
120
122
|
# # transformation client
|
|
121
123
|
self.transClient = TransformationClient()
|
|
122
|
-
# # wms client
|
|
123
|
-
self.wmsClient = WMSClient()
|
|
124
124
|
# # request client
|
|
125
125
|
self.reqClient = ReqClient()
|
|
126
126
|
# # file catalog client
|
|
127
127
|
self.metadataClient = FileCatalogClient()
|
|
128
|
-
# # job
|
|
129
|
-
self.
|
|
128
|
+
# # job DB
|
|
129
|
+
self.jobDB = JobDB()
|
|
130
130
|
|
|
131
131
|
return S_OK()
|
|
132
132
|
|
|
@@ -224,7 +224,7 @@ class TransformationCleaningAgent(AgentModule):
|
|
|
224
224
|
So, we should just clean from time to time.
|
|
225
225
|
What I added here is done only when the agent finalize, and it's quite light-ish operation anyway.
|
|
226
226
|
"""
|
|
227
|
-
res = self.
|
|
227
|
+
res = self.jobDB.getDistinctJobAttributes("JobGroup", None, datetime.utcnow() - timedelta(days=365))
|
|
228
228
|
if not res["OK"]:
|
|
229
229
|
self.log.error("Failed to get job groups", res["Message"])
|
|
230
230
|
return res
|
|
@@ -268,7 +268,7 @@ class TransformationCleaningAgent(AgentModule):
|
|
|
268
268
|
|
|
269
269
|
# Remove JobIDs that were unknown to the TransformationSystem
|
|
270
270
|
jobGroupsToCheck = [str(transDict["TransformationID"]).zfill(8) for transDict in toClean + toArchive]
|
|
271
|
-
res = self.
|
|
271
|
+
res = self.jobDB.selectJobs({"JobGroup": jobGroupsToCheck})
|
|
272
272
|
if not res["OK"]:
|
|
273
273
|
return res
|
|
274
274
|
jobIDsToRemove = [int(jobID) for jobID in res["Value"]]
|
|
@@ -610,8 +610,8 @@ class TransformationCleaningAgent(AgentModule):
|
|
|
610
610
|
# Prevent 0 job IDs
|
|
611
611
|
jobIDs = [int(j) for j in transJobIDs if int(j)]
|
|
612
612
|
allRemove = True
|
|
613
|
-
for jobList in breakListIntoChunks(jobIDs,
|
|
614
|
-
res =
|
|
613
|
+
for jobList in breakListIntoChunks(jobIDs, 1000):
|
|
614
|
+
res = kill_delete_jobs(RIGHT_KILL, jobList, force=True)
|
|
615
615
|
if res["OK"]:
|
|
616
616
|
self.log.info(f"Successfully killed {len(jobList)} jobs from WMS")
|
|
617
617
|
elif ("InvalidJobIDs" in res) and ("NonauthorizedJobIDs" not in res) and ("FailedJobIDs" not in res):
|
|
@@ -623,7 +623,7 @@ class TransformationCleaningAgent(AgentModule):
|
|
|
623
623
|
self.log.error("Failed to kill jobs", f"(n={len(res['FailedJobIDs'])})")
|
|
624
624
|
allRemove = False
|
|
625
625
|
|
|
626
|
-
res =
|
|
626
|
+
res = kill_delete_jobs(RIGHT_DELETE, jobList, force=True)
|
|
627
627
|
if res["OK"]:
|
|
628
628
|
self.log.info("Successfully deleted jobs from WMS", f"(n={len(jobList)})")
|
|
629
629
|
elif ("InvalidJobIDs" in res) and ("NonauthorizedJobIDs" not in res) and ("FailedJobIDs" not in res):
|
|
@@ -9,6 +9,7 @@ from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
|
|
|
9
9
|
from DIRAC.Core.Base.API import API
|
|
10
10
|
from DIRAC.Core.Utilities.JEncode import encode
|
|
11
11
|
from DIRAC.Core.Utilities.PromptUser import promptUser
|
|
12
|
+
from DIRAC.MonitoringSystem.Client.WebAppClient import WebAppClient
|
|
12
13
|
from DIRAC.RequestManagementSystem.Client.Operation import Operation
|
|
13
14
|
from DIRAC.TransformationSystem.Client.BodyPlugin.BaseBody import BaseBody
|
|
14
15
|
from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
|
|
@@ -498,7 +499,7 @@ class Transformation(API):
|
|
|
498
499
|
]
|
|
499
500
|
dictList = []
|
|
500
501
|
|
|
501
|
-
result =
|
|
502
|
+
result = WebAppClient().getTransformationSummaryWeb(condDict, orderby, start, maxitems)
|
|
502
503
|
if not result["OK"]:
|
|
503
504
|
self._prettyPrint(result)
|
|
504
505
|
return result
|