DIRAC 9.0.8__py3-none-any.whl → 9.0.9__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/Core/Utilities/Subprocess.py +66 -57
- DIRAC/Core/Utilities/test/Test_Subprocess.py +58 -8
- DIRAC/WorkloadManagementSystem/JobWrapper/Watchdog.py +16 -45
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +3 -4
- DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +1 -1
- {dirac-9.0.8.dist-info → dirac-9.0.9.dist-info}/METADATA +2 -2
- {dirac-9.0.8.dist-info → dirac-9.0.9.dist-info}/RECORD +11 -11
- {dirac-9.0.8.dist-info → dirac-9.0.9.dist-info}/WHEEL +0 -0
- {dirac-9.0.8.dist-info → dirac-9.0.9.dist-info}/entry_points.txt +0 -0
- {dirac-9.0.8.dist-info → dirac-9.0.9.dist-info}/licenses/LICENSE +0 -0
- {dirac-9.0.8.dist-info → dirac-9.0.9.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
DIRAC Wrapper to execute python and system commands with a wrapper, that might
|
|
3
3
|
set a timeout.
|
|
4
|
-
3
|
|
4
|
+
3 functions are provided:
|
|
5
5
|
|
|
6
6
|
- shellCall( iTimeOut, cmdSeq, callbackFunction = None, env = None ):
|
|
7
7
|
it uses subprocess.Popen class with "shell = True".
|
|
@@ -26,6 +26,7 @@ set a timeout.
|
|
|
26
26
|
should be used to wrap third party python functions
|
|
27
27
|
|
|
28
28
|
"""
|
|
29
|
+
|
|
29
30
|
import os
|
|
30
31
|
import selectors
|
|
31
32
|
import signal
|
|
@@ -161,7 +162,6 @@ class Subprocess:
|
|
|
161
162
|
|
|
162
163
|
self.child = None
|
|
163
164
|
self.childPID = 0
|
|
164
|
-
self.childKilled = False
|
|
165
165
|
self.callback = None
|
|
166
166
|
self.bufferList = []
|
|
167
167
|
self.cmdSeq = []
|
|
@@ -193,7 +193,7 @@ class Subprocess:
|
|
|
193
193
|
f"First and last data in buffer: \n{dataString[:100]} \n....\n {dataString[-100:]} ",
|
|
194
194
|
)
|
|
195
195
|
retDict = S_ERROR(
|
|
196
|
-
"Reached maximum allowed length (%d bytes)
|
|
196
|
+
"Reached maximum allowed length (%d bytes) for called function return value" % self.bufferLimit
|
|
197
197
|
)
|
|
198
198
|
retDict["Value"] = dataString
|
|
199
199
|
return retDict
|
|
@@ -241,60 +241,71 @@ class Subprocess:
|
|
|
241
241
|
events = sel.select(timeout=timeout or self.timeout or None)
|
|
242
242
|
return [key.fileobj for key, event in events if event & selectors.EVENT_READ]
|
|
243
243
|
|
|
244
|
-
def
|
|
245
|
-
"""
|
|
246
|
-
|
|
247
|
-
:param int pid: process id
|
|
248
|
-
:param int sig: signal to send, default 9 (SIGKILL)
|
|
249
|
-
"""
|
|
244
|
+
def __terminateProcess(self, process):
|
|
245
|
+
"""Tries to terminate a process with SIGTERM. Returns a (gone, alive) tuple"""
|
|
246
|
+
self.log.verbose(f"Sending SIGTERM signal to PID {process.pid}")
|
|
250
247
|
try:
|
|
251
|
-
|
|
252
|
-
except
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
248
|
+
process.terminate()
|
|
249
|
+
except psutil.NoSuchProcess:
|
|
250
|
+
return ([], [])
|
|
251
|
+
return psutil.wait_procs([process], timeout=60)
|
|
252
|
+
|
|
253
|
+
def __poll(self, process):
|
|
254
|
+
"""Non-blocking check of whether process `pid` is still alive.
|
|
255
|
+
Returns:
|
|
256
|
+
- (0, 0) if process is still running (like os.waitpid(pid, os.WNOHANG))
|
|
257
|
+
- (pid, exitcode) if process has terminated
|
|
258
|
+
- None if process info cannot be retrieved
|
|
259
|
+
"""
|
|
259
260
|
try:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
exitcode = process.wait(timeout=0)
|
|
262
|
+
return (process.pid, exitcode) # exited
|
|
263
|
+
except psutil.TimeoutExpired:
|
|
264
|
+
return (0, 0) # still running
|
|
265
|
+
except psutil.NoSuchProcess:
|
|
264
266
|
return None
|
|
265
267
|
|
|
266
268
|
def killChild(self, recursive=True):
|
|
267
|
-
"""
|
|
268
|
-
|
|
269
|
-
:param boolean recursive: flag to kill all descendants
|
|
269
|
+
"""Kills a process tree (including children) with signal SIGTERM. If that fails, escalate to SIGKILL
|
|
270
|
+
returns (gone, alive) tuple.
|
|
270
271
|
"""
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
272
|
+
|
|
273
|
+
self.log.info(f"Killing childPID {self.childPID}")
|
|
274
|
+
|
|
275
|
+
gone, alive = [], []
|
|
276
|
+
try:
|
|
277
|
+
child_process = psutil.Process(self.childPID)
|
|
278
|
+
except psutil.NoSuchProcess:
|
|
279
|
+
self.log.warn(f"Child PID {self.childPID} no longer exists")
|
|
280
|
+
return (gone, alive)
|
|
281
|
+
|
|
282
|
+
if recursive:
|
|
283
|
+
# grandchildren
|
|
284
|
+
children = child_process.children(recursive=True)
|
|
285
|
+
self.log.info(f"Sending kill signal to {len(children)} children PIDs")
|
|
286
|
+
for p in children:
|
|
285
287
|
try:
|
|
286
288
|
p.terminate()
|
|
287
289
|
except psutil.NoSuchProcess:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
continue
|
|
291
|
+
g, a = psutil.wait_procs(children, timeout=60)
|
|
292
|
+
gone.extend(g)
|
|
293
|
+
alive.extend(a)
|
|
294
|
+
|
|
295
|
+
# now killing the child_process
|
|
296
|
+
g, a = self.__terminateProcess(child_process)
|
|
297
|
+
gone.extend(g)
|
|
298
|
+
alive.extend(a)
|
|
299
|
+
|
|
300
|
+
# if there's something still alive, use SIGKILL
|
|
301
|
+
if alive:
|
|
291
302
|
for p in alive:
|
|
292
303
|
try:
|
|
293
304
|
p.kill()
|
|
294
305
|
except psutil.NoSuchProcess:
|
|
295
306
|
pass
|
|
296
307
|
|
|
297
|
-
|
|
308
|
+
return psutil.wait_procs(alive, timeout=60)
|
|
298
309
|
|
|
299
310
|
def pythonCall(self, function, *stArgs, **stKeyArgs):
|
|
300
311
|
"""call python function :function: with :stArgs: and :stKeyArgs:"""
|
|
@@ -309,8 +320,6 @@ class Subprocess:
|
|
|
309
320
|
if pid == 0:
|
|
310
321
|
os.close(readFD)
|
|
311
322
|
self.__executePythonFunction(function, writeFD, *stArgs, **stKeyArgs)
|
|
312
|
-
# FIXME: the close it is done at __executePythonFunction, do we need it here?
|
|
313
|
-
os.close(writeFD)
|
|
314
323
|
else:
|
|
315
324
|
os.close(writeFD)
|
|
316
325
|
readSeq = self.__selectFD([readFD])
|
|
@@ -319,14 +328,13 @@ class Subprocess:
|
|
|
319
328
|
try:
|
|
320
329
|
if len(readSeq) == 0:
|
|
321
330
|
self.log.debug("Timeout limit reached for pythonCall", function.__name__)
|
|
322
|
-
self.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
retries -= 1
|
|
331
|
+
gone, alive = self.__terminateProcess(psutil.Process(pid))
|
|
332
|
+
if alive:
|
|
333
|
+
for p in alive:
|
|
334
|
+
try:
|
|
335
|
+
p.kill()
|
|
336
|
+
except psutil.NoSuchProcess:
|
|
337
|
+
continue
|
|
330
338
|
|
|
331
339
|
return S_ERROR('%d seconds timeout for "%s" call' % (self.timeout, function.__name__))
|
|
332
340
|
elif readSeq[0] == readFD:
|
|
@@ -400,7 +408,7 @@ class Subprocess:
|
|
|
400
408
|
if len(dataString) + baseLength > self.bufferLimit:
|
|
401
409
|
self.log.error("Maximum output buffer length reached")
|
|
402
410
|
retDict = S_ERROR(
|
|
403
|
-
"Reached maximum allowed length (%d bytes) for called
|
|
411
|
+
"Reached maximum allowed length (%d bytes) for called function return value" % self.bufferLimit
|
|
404
412
|
)
|
|
405
413
|
retDict["Value"] = dataString
|
|
406
414
|
return retDict
|
|
@@ -446,6 +454,7 @@ class Subprocess:
|
|
|
446
454
|
start_new_session=start_new_session,
|
|
447
455
|
)
|
|
448
456
|
self.childPID = self.child.pid
|
|
457
|
+
child_process = psutil.Process(self.childPID)
|
|
449
458
|
except OSError as v:
|
|
450
459
|
retDict = S_ERROR(repr(v))
|
|
451
460
|
retDict["Value"] = (-1, "", str(v))
|
|
@@ -464,9 +473,9 @@ class Subprocess:
|
|
|
464
473
|
self.bufferList = [["", 0], ["", 0]]
|
|
465
474
|
initialTime = time.time()
|
|
466
475
|
|
|
467
|
-
exitStatus = self.__poll(
|
|
476
|
+
exitStatus = self.__poll(child_process)
|
|
468
477
|
|
|
469
|
-
while (0, 0) == exitStatus
|
|
478
|
+
while (0, 0) == exitStatus: # This means that the process is still alive
|
|
470
479
|
retDict = self.__readFromCommand()
|
|
471
480
|
if not retDict["OK"]:
|
|
472
481
|
return retDict
|
|
@@ -478,14 +487,14 @@ class Subprocess:
|
|
|
478
487
|
1, "Timeout (%d seconds) for '%s' call" % (self.timeout, cmdSeq)
|
|
479
488
|
)
|
|
480
489
|
time.sleep(0.01)
|
|
481
|
-
exitStatus = self.__poll(
|
|
490
|
+
exitStatus = self.__poll(child_process)
|
|
482
491
|
|
|
483
492
|
self.__readFromCommand()
|
|
484
493
|
|
|
485
494
|
if exitStatus:
|
|
486
495
|
exitStatus = exitStatus[1]
|
|
487
496
|
|
|
488
|
-
if exitStatus >= 256:
|
|
497
|
+
if exitStatus and exitStatus >= 256:
|
|
489
498
|
exitStatus = int(exitStatus / 256)
|
|
490
499
|
return S_OK((exitStatus, self.bufferList[0][0], self.bufferList[1][0]))
|
|
491
500
|
finally:
|
|
@@ -3,23 +3,25 @@
|
|
|
3
3
|
# Date: 2012/12/11 18:04:25
|
|
4
4
|
########################################################################
|
|
5
5
|
|
|
6
|
-
"""
|
|
7
|
-
|
|
6
|
+
""":mod: SubprocessTests
|
|
7
|
+
=======================
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
.. module: SubprocessTests
|
|
10
|
+
:synopsis: unittest for Subprocess module
|
|
11
|
+
.. moduleauthor:: Krzysztof.Ciba@NOSPAMgmail.com
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
unittest for Subprocess module
|
|
14
14
|
"""
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
import platform
|
|
17
|
+
import time
|
|
17
18
|
from os.path import dirname, join
|
|
18
19
|
from subprocess import Popen
|
|
19
20
|
|
|
21
|
+
import psutil
|
|
20
22
|
import pytest
|
|
21
23
|
|
|
22
|
-
from DIRAC.Core.Utilities.Subprocess import
|
|
24
|
+
from DIRAC.Core.Utilities.Subprocess import Subprocess, getChildrenPIDs, pythonCall, shellCall, systemCall
|
|
23
25
|
|
|
24
26
|
# Mark this entire module as slow
|
|
25
27
|
pytestmark = pytest.mark.slow
|
|
@@ -72,3 +74,51 @@ def test_decodingCommandOutput():
|
|
|
72
74
|
retVal = sp.systemCall(r"""python -c 'import os; os.fdopen(2, "wb").write(b"\xdf")'""", shell=True)
|
|
73
75
|
assert retVal["OK"]
|
|
74
76
|
assert retVal["Value"] == (0, "", "\ufffd")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@pytest.fixture
|
|
80
|
+
def subprocess_instance():
|
|
81
|
+
"""Provides a Subprocess instance for testing."""
|
|
82
|
+
subp = Subprocess()
|
|
83
|
+
return subp
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.fixture
|
|
87
|
+
def dummy_child():
|
|
88
|
+
"""Spawn a dummy process tree: parent -> child."""
|
|
89
|
+
# Start a shell that sleeps, with a subprocess child
|
|
90
|
+
parent = Popen(["bash", "-c", "sleep 10 & wait"])
|
|
91
|
+
time.sleep(0.2) # give it a moment to start
|
|
92
|
+
yield parent
|
|
93
|
+
# Ensure cleanup
|
|
94
|
+
try:
|
|
95
|
+
parent.terminate()
|
|
96
|
+
parent.wait(timeout=1)
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_kill_child_process_tree(subprocess_instance, dummy_child):
|
|
102
|
+
"""Test that killChild kills both parent and its children."""
|
|
103
|
+
subprocess_instance.childPID = dummy_child.pid
|
|
104
|
+
parent_proc = psutil.Process(subprocess_instance.childPID)
|
|
105
|
+
|
|
106
|
+
# Sanity check: parent should exist
|
|
107
|
+
assert parent_proc.is_running()
|
|
108
|
+
|
|
109
|
+
# It should have at least one sleeping child
|
|
110
|
+
children = parent_proc.children(recursive=True)
|
|
111
|
+
assert children, "Expected dummy process to have at least one child"
|
|
112
|
+
|
|
113
|
+
# Kill the tree
|
|
114
|
+
gone, alive = subprocess_instance.killChild(recursive=True)
|
|
115
|
+
|
|
116
|
+
# Verify the parent and children are terminated
|
|
117
|
+
for p in gone:
|
|
118
|
+
assert not p.is_running(), f"Process {p.pid} still alive"
|
|
119
|
+
for p in alive:
|
|
120
|
+
assert not p.is_running(), f"Process {p.pid} still alive"
|
|
121
|
+
|
|
122
|
+
# Verify parent is gone
|
|
123
|
+
with pytest.raises(psutil.NoSuchProcess):
|
|
124
|
+
psutil.Process(subprocess_instance.childPID)
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
"""The Watchdog class is used by the Job Wrapper to resolve and monitor
|
|
2
|
+
the system resource consumption. The Watchdog can determine if
|
|
3
|
+
a running job is stalled and indicate this to the Job Wrapper.
|
|
4
|
+
Furthermore, the Watchdog will identify when the Job CPU limit has been
|
|
5
|
+
exceeded and fail jobs meaningfully.
|
|
6
|
+
|
|
7
|
+
Information is returned to the WMS via the heart-beat mechanism. This
|
|
8
|
+
also interprets control signals from the WMS e.g. to kill a running
|
|
9
|
+
job.
|
|
10
|
+
|
|
11
|
+
- Still to implement:
|
|
12
|
+
- CPU normalization for correct comparison with job limit
|
|
13
13
|
"""
|
|
14
|
+
|
|
14
15
|
import datetime
|
|
15
16
|
import errno
|
|
16
17
|
import getpass
|
|
17
18
|
import math
|
|
18
19
|
import os
|
|
19
|
-
import signal
|
|
20
20
|
import socket
|
|
21
21
|
import time
|
|
22
22
|
from pathlib import Path
|
|
@@ -32,28 +32,6 @@ from DIRAC.WorkloadManagementSystem.Client import JobMinorStatus
|
|
|
32
32
|
from DIRAC.WorkloadManagementSystem.Client.JobStateUpdateClient import JobStateUpdateClient
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def kill_proc_tree(pid, sig=signal.SIGTERM, includeParent=True):
|
|
36
|
-
"""Kill a process tree (including grandchildren) with signal
|
|
37
|
-
"sig" and return a (gone, still_alive) tuple.
|
|
38
|
-
called as soon as a child terminates.
|
|
39
|
-
|
|
40
|
-
Taken from https://psutil.readthedocs.io/en/latest/index.html#kill-process-tree
|
|
41
|
-
"""
|
|
42
|
-
assert pid != os.getpid(), "won't kill myself"
|
|
43
|
-
parent = psutil.Process(pid)
|
|
44
|
-
children = parent.children(recursive=True)
|
|
45
|
-
if includeParent:
|
|
46
|
-
children.append(parent)
|
|
47
|
-
for p in children:
|
|
48
|
-
try:
|
|
49
|
-
p.send_signal(sig)
|
|
50
|
-
except psutil.NoSuchProcess:
|
|
51
|
-
pass
|
|
52
|
-
_gone, alive = psutil.wait_procs(children, timeout=10)
|
|
53
|
-
for p in alive:
|
|
54
|
-
p.kill()
|
|
55
|
-
|
|
56
|
-
|
|
57
35
|
class Watchdog:
|
|
58
36
|
#############################################################################
|
|
59
37
|
def __init__(self, pid, exeThread, spObject, jobCPUTime, memoryLimit=0, processors=1, jobArgs={}):
|
|
@@ -212,7 +190,7 @@ class Watchdog:
|
|
|
212
190
|
if self.littleTimeLeftCount == 0 and self.__timeLeft() == -1:
|
|
213
191
|
self.checkError = JobMinorStatus.JOB_EXCEEDED_CPU
|
|
214
192
|
self.log.error(self.checkError, self.timeLeft)
|
|
215
|
-
self.
|
|
193
|
+
self.spObject.killChild()
|
|
216
194
|
return S_OK()
|
|
217
195
|
|
|
218
196
|
self.littleTimeLeftCount -= 1
|
|
@@ -321,7 +299,7 @@ class Watchdog:
|
|
|
321
299
|
|
|
322
300
|
self.log.info("=================END=================")
|
|
323
301
|
|
|
324
|
-
self.
|
|
302
|
+
self.spObject.killChild()
|
|
325
303
|
return S_OK()
|
|
326
304
|
|
|
327
305
|
recentStdOut = "None"
|
|
@@ -408,7 +386,7 @@ class Watchdog:
|
|
|
408
386
|
if "Kill" in signalDict:
|
|
409
387
|
self.log.info("Received Kill signal, stopping job via control signal")
|
|
410
388
|
self.checkError = JobMinorStatus.RECEIVED_KILL_SIGNAL
|
|
411
|
-
self.
|
|
389
|
+
self.spObject.killChild()
|
|
412
390
|
else:
|
|
413
391
|
self.log.info("The following control signal was sent but not understood by the watchdog:")
|
|
414
392
|
self.log.info(signalDict)
|
|
@@ -862,13 +840,6 @@ class Watchdog:
|
|
|
862
840
|
|
|
863
841
|
return result
|
|
864
842
|
|
|
865
|
-
#############################################################################
|
|
866
|
-
def __killRunningThread(self):
|
|
867
|
-
"""Will kill the running thread process and any child processes."""
|
|
868
|
-
self.log.info("Sending kill signal to application PID", self.spObject.getChildPID())
|
|
869
|
-
self.spObject.killChild()
|
|
870
|
-
return S_OK("Thread killed")
|
|
871
|
-
|
|
872
843
|
#############################################################################
|
|
873
844
|
def __sendSignOfLife(self, jobID, heartBeatDict, staticParamDict):
|
|
874
845
|
"""Sends sign of life 'heartbeat' signal and triggers control signal
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
1
|
+
"""Test class for JobWrapper"""
|
|
2
|
+
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
5
5
|
import tempfile
|
|
@@ -314,8 +314,7 @@ def test_processKilledSubprocess(mocker):
|
|
|
314
314
|
result = jw.process("sleep 20", {})
|
|
315
315
|
|
|
316
316
|
assert result["OK"]
|
|
317
|
-
assert result["Value"]["payloadStatus"]
|
|
318
|
-
assert not result["Value"]["payloadOutput"]
|
|
317
|
+
assert result["Value"]["payloadStatus"] is None
|
|
319
318
|
assert not result["Value"]["payloadExecutorError"]
|
|
320
319
|
assert result["Value"]["watchdogError"] == "Job is stalled!" # Error message from the watchdog
|
|
321
320
|
|
|
@@ -218,7 +218,7 @@ class PilotCStoJSONSynchronizer:
|
|
|
218
218
|
|
|
219
219
|
preferredURLPatterns = gConfigurationData.extractOptionFromCFG("/DIRAC/PreferredURLPatterns")
|
|
220
220
|
if preferredURLPatterns:
|
|
221
|
-
pilotDict["PreferredURLPatterns"] = preferredURLPatterns
|
|
221
|
+
pilotDict["PreferredURLPatterns"] = preferredURLPatterns.replace(" ", "").split(",")
|
|
222
222
|
|
|
223
223
|
self.log.debug("Got pilotDict", str(pilotDict))
|
|
224
224
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DIRAC
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.9
|
|
4
4
|
Summary: DIRAC is an interware, meaning a software framework for distributed computing.
|
|
5
5
|
Home-page: https://github.com/DIRACGrid/DIRAC/
|
|
6
6
|
License: GPL-3.0-only
|
|
@@ -19,7 +19,7 @@ Requires-Dist: cachetools
|
|
|
19
19
|
Requires-Dist: certifi
|
|
20
20
|
Requires-Dist: cwltool
|
|
21
21
|
Requires-Dist: diraccfg
|
|
22
|
-
Requires-Dist: DIRACCommon==v9.0.
|
|
22
|
+
Requires-Dist: DIRACCommon==v9.0.9
|
|
23
23
|
Requires-Dist: diracx-client>=v0.0.1
|
|
24
24
|
Requires-Dist: diracx-core>=v0.0.1
|
|
25
25
|
Requires-Dist: diracx-cli>=v0.0.1
|
|
@@ -259,7 +259,7 @@ DIRAC/Core/Utilities/ReturnValues.py,sha256=EHtnn1I3Lvq6RO9tTVZMlf19a8QGX1B6Qa7l
|
|
|
259
259
|
DIRAC/Core/Utilities/Shifter.py,sha256=EkJGPR_zbL0SS3E2_cq1-MjkTH91ZaFJlh-tQ2PxDGk,2571
|
|
260
260
|
DIRAC/Core/Utilities/SiteSEMapping.py,sha256=Uynx_evXQPmfbMKH-TfqzLq4e_7APVrP6gC20cJEBYs,5683
|
|
261
261
|
DIRAC/Core/Utilities/StateMachine.py,sha256=e1rc0X6J8Cf_W9HlLnLgroLkdrksUQUxdkH_qSoZVoA,769
|
|
262
|
-
DIRAC/Core/Utilities/Subprocess.py,sha256=
|
|
262
|
+
DIRAC/Core/Utilities/Subprocess.py,sha256=_lNp7ncg6BYC2TLvkcqACelZSsb0_5oyM2Jq-T5YbFc,23315
|
|
263
263
|
DIRAC/Core/Utilities/ThreadPool.py,sha256=tETtgUdW1dlq_7xp_nQUTxNC87jAkrNnwvuDpGS3ffE,11484
|
|
264
264
|
DIRAC/Core/Utilities/ThreadSafe.py,sha256=EAxEMqEuYlzDvAaupW3fi2MY-3kGZJ0zbK9i7JhEmt4,1117
|
|
265
265
|
DIRAC/Core/Utilities/ThreadScheduler.py,sha256=IzyS59NF0ZhQwGYLpZ-yMX8_6kXFSGL9FQapylnoPLE,6212
|
|
@@ -304,7 +304,7 @@ DIRAC/Core/Utilities/test/Test_Pfn.py,sha256=XWTXejQf_TnicaPbQ4ZyDsl1KXDpq3IMNFl
|
|
|
304
304
|
DIRAC/Core/Utilities/test/Test_ProcessPool.py,sha256=NnfHNkhTOgADk-P9ds_ADuvcSNlK_XdoibRkk13r_NE,10890
|
|
305
305
|
DIRAC/Core/Utilities/test/Test_Profiler.py,sha256=8QRRXm-PwJpjLGwAOJxnrpnnMbmVzXhJ1ZcQ9gDBX5c,4215
|
|
306
306
|
DIRAC/Core/Utilities/test/Test_ReturnValues.py,sha256=w6Jz-Vblgu8RDXPzVi6BZjuFXcSmW-Zb0mcBgW1RWOw,1926
|
|
307
|
-
DIRAC/Core/Utilities/test/Test_Subprocess.py,sha256=
|
|
307
|
+
DIRAC/Core/Utilities/test/Test_Subprocess.py,sha256=f9THTKnVdGcNwpZ0PtfCQz0UAtNmF06126-F0mUAxWQ,3605
|
|
308
308
|
DIRAC/Core/Utilities/test/Test_entrypoints.py,sha256=z_3f7m59v3W6ZsqgeCFbJoBnMY-cKR_blKbPqcV7528,413
|
|
309
309
|
DIRAC/Core/Utilities/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
310
310
|
DIRAC/Core/Workflow/Module.py,sha256=XF_iDyJ1wIwUesHLfDGz9BywHgEuOwqzC2f7fY3E7r4,12631
|
|
@@ -1209,9 +1209,9 @@ DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py,sha256=s_YHg_PsTGVHBLrS1
|
|
|
1209
1209
|
DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapperOfflineTemplate.py,sha256=wem5VDN9XiC7szAzdsbgHUxpIOQB2Hj36DIVMoV9px8,2490
|
|
1210
1210
|
DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapperTemplate.py,sha256=4QgcFPMLRaTagP9e_Vvsla8pFH8HdewklHfS-gyS4-g,3313
|
|
1211
1211
|
DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapperUtilities.py,sha256=5w_4PMnaHhuexChADDvt1L9Ih1PstdUuYWObnlv9Dto,10072
|
|
1212
|
-
DIRAC/WorkloadManagementSystem/JobWrapper/Watchdog.py,sha256=
|
|
1212
|
+
DIRAC/WorkloadManagementSystem/JobWrapper/Watchdog.py,sha256=UWVDC_tWzlE9i5PsLycrRda4j3hWjaK1RnQVOvydg6U,38857
|
|
1213
1213
|
DIRAC/WorkloadManagementSystem/JobWrapper/__init__.py,sha256=e9Oa_ddNLweR3Lp_HOMK6WqqCWWj2SLPxF5UH4F19ic,61
|
|
1214
|
-
DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py,sha256=
|
|
1214
|
+
DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py,sha256=AhevZTkZfFQV9XN5UTWFmoMe9GvwN_Caorto1o_YoU8,39119
|
|
1215
1215
|
DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py,sha256=dC_SvC5Rlchlj2NvBfN7FH1ioXgC8bf9U8BQnEL5GYg,21982
|
|
1216
1216
|
DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_Watchdog.py,sha256=a-QJ1E1ZcWObhOVgxZYD_nYjseCWsbjT0KxjZDNWyAQ,882
|
|
1217
1217
|
DIRAC/WorkloadManagementSystem/JobWrapper/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -1242,7 +1242,7 @@ DIRAC/WorkloadManagementSystem/Utilities/JobModel.py,sha256=jN9sFbzMZo9tab6Kp7Oe
|
|
|
1242
1242
|
DIRAC/WorkloadManagementSystem/Utilities/JobParameters.py,sha256=JW3AAEtBJn1gIO_rm2Ft5qqjfLteIo3HpQtGNZBfhxE,8365
|
|
1243
1243
|
DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py,sha256=WtGJzC7fHvydANh8JH6e1Kk_jebrCMPr2c5cw3ufjm8,7826
|
|
1244
1244
|
DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py,sha256=FNUsGhvsFVrtmA7r8G-sd4QTMeBkqG1sdtwiBUKQyd0,605
|
|
1245
|
-
DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py,sha256=
|
|
1245
|
+
DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py,sha256=ZQk2tD40986awO9pae1zmdPEWlnMJt4m61Z_RU3LWl8,12476
|
|
1246
1246
|
DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py,sha256=VcvQTpeyTbVYqSsPQDyAt37N2CaEAnIuvbR6yk4kYk8,15465
|
|
1247
1247
|
DIRAC/WorkloadManagementSystem/Utilities/QueueUtilities.py,sha256=J5-n_lvWbW_TRjrlqp8hx1SHEaXDW2Dxp3R1hBBrWnE,12082
|
|
1248
1248
|
DIRAC/WorkloadManagementSystem/Utilities/RemoteRunner.py,sha256=7FcEtlYSJMzdbLIFBUKD-j_wqRHya-ISqk8w-JRy3kw,12159
|
|
@@ -1296,9 +1296,9 @@ DIRAC/tests/Workflow/Integration/exe-script.py,sha256=B_slYdTocEzqfQLRhwuPiLyYUn
|
|
|
1296
1296
|
DIRAC/tests/Workflow/Integration/helloWorld.py,sha256=tBgEHH3ZF7ZiTS57gtmm3DW-Qxgm_57HWHpM-Y8XSws,205
|
|
1297
1297
|
DIRAC/tests/Workflow/Regression/helloWorld.py,sha256=69eCgFuVSYo-mK3Dj2dw1c6g86sF5FksKCf8V2aGVoM,509
|
|
1298
1298
|
DIRAC/tests/Workflow/Regression/helloWorld.xml,sha256=xwydIcFTAHIX-YPfQfyxuQ7hzvIO3IhR3UAF7ORgkGg,5310
|
|
1299
|
-
dirac-9.0.
|
|
1300
|
-
dirac-9.0.
|
|
1301
|
-
dirac-9.0.
|
|
1302
|
-
dirac-9.0.
|
|
1303
|
-
dirac-9.0.
|
|
1304
|
-
dirac-9.0.
|
|
1299
|
+
dirac-9.0.9.dist-info/licenses/LICENSE,sha256=uyr4oV6jmjUeepXZPPjkJRwa5q5MrI7jqJz5sVXNblQ,32452
|
|
1300
|
+
dirac-9.0.9.dist-info/METADATA,sha256=UjEl56_6P3co7mVc4nFt9FsB51VuvNFA-MkK1kHsQGk,10016
|
|
1301
|
+
dirac-9.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
1302
|
+
dirac-9.0.9.dist-info/entry_points.txt,sha256=hupzIL8aVmjK3nn7RLKdhcaiPmLOiD3Kulh3CSDHKmw,16492
|
|
1303
|
+
dirac-9.0.9.dist-info/top_level.txt,sha256=RISrnN9kb_mPqmVu8_o4jF-DSX8-h6AcgfkO9cgfkHA,6
|
|
1304
|
+
dirac-9.0.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|