parsl 2024.1.22__py3-none-any.whl → 2024.2.5__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.
- parsl/app/errors.py +1 -5
- parsl/curvezmq.py +205 -0
- parsl/dataflow/dflow.py +1 -1
- parsl/executors/high_throughput/executor.py +78 -49
- parsl/executors/high_throughput/interchange.py +14 -7
- parsl/executors/high_throughput/process_worker_pool.py +44 -9
- parsl/executors/high_throughput/zmq_pipes.py +21 -15
- parsl/executors/taskvine/manager.py +60 -43
- parsl/executors/taskvine/manager_config.py +14 -0
- parsl/monitoring/monitoring.py +22 -4
- parsl/monitoring/remote.py +1 -1
- parsl/providers/errors.py +4 -6
- parsl/providers/slurm/slurm.py +7 -6
- parsl/tests/configs/ad_hoc_cluster_htex.py +1 -0
- parsl/tests/configs/azure_single_node.py +1 -0
- parsl/tests/configs/bluewaters.py +1 -0
- parsl/tests/configs/bridges.py +1 -0
- parsl/tests/configs/cc_in2p3.py +1 -0
- parsl/tests/configs/comet.py +1 -0
- parsl/tests/configs/cooley_htex.py +1 -0
- parsl/tests/configs/ec2_single_node.py +1 -0
- parsl/tests/configs/ec2_spot.py +1 -0
- parsl/tests/configs/frontera.py +1 -0
- parsl/tests/configs/htex_ad_hoc_cluster.py +1 -0
- parsl/tests/configs/htex_local.py +1 -0
- parsl/tests/configs/htex_local_alternate.py +1 -0
- parsl/tests/configs/htex_local_intask_staging.py +1 -0
- parsl/tests/configs/htex_local_rsync_staging.py +1 -0
- parsl/tests/configs/local_adhoc.py +1 -0
- parsl/tests/configs/midway.py +1 -0
- parsl/tests/configs/nscc_singapore.py +1 -0
- parsl/tests/configs/osg_htex.py +1 -0
- parsl/tests/configs/petrelkube.py +1 -0
- parsl/tests/configs/summit.py +1 -0
- parsl/tests/configs/swan_htex.py +1 -0
- parsl/tests/configs/theta.py +1 -0
- parsl/tests/conftest.py +12 -2
- parsl/tests/manual_tests/htex_local.py +1 -0
- parsl/tests/manual_tests/test_ad_hoc_htex.py +1 -0
- parsl/tests/manual_tests/test_fan_in_out_htex_remote.py +1 -0
- parsl/tests/manual_tests/test_memory_limits.py +1 -0
- parsl/tests/scaling_tests/htex_local.py +1 -0
- parsl/tests/sites/test_affinity.py +1 -0
- parsl/tests/sites/test_concurrent.py +2 -1
- parsl/tests/sites/test_dynamic_executor.py +1 -0
- parsl/tests/sites/test_worker_info.py +1 -0
- parsl/tests/test_bash_apps/test_stdout.py +6 -1
- parsl/tests/test_curvezmq.py +455 -0
- parsl/tests/test_data/test_file_apps.py +5 -5
- parsl/tests/test_data/test_file_staging.py +3 -3
- parsl/tests/test_docs/test_kwargs.py +3 -3
- parsl/tests/test_htex/test_cpu_affinity_explicit.py +52 -0
- parsl/tests/test_htex/test_htex.py +46 -0
- parsl/tests/test_htex/test_htex_zmq_binding.py +53 -13
- parsl/tests/test_python_apps/test_futures.py +5 -5
- parsl/tests/test_regression/test_97_parallelism_0.py +1 -0
- parsl/tests/test_scaling/test_block_error_handler.py +6 -5
- parsl/tests/test_scaling/test_regression_1621.py +1 -0
- parsl/tests/test_scaling/test_scale_down.py +1 -0
- parsl/version.py +1 -1
- {parsl-2024.1.22.data → parsl-2024.2.5.data}/scripts/process_worker_pool.py +44 -9
- {parsl-2024.1.22.dist-info → parsl-2024.2.5.dist-info}/METADATA +5 -6
- {parsl-2024.1.22.dist-info → parsl-2024.2.5.dist-info}/RECORD +69 -65
- {parsl-2024.1.22.data → parsl-2024.2.5.data}/scripts/exec_parsl_function.py +0 -0
- {parsl-2024.1.22.data → parsl-2024.2.5.data}/scripts/parsl_coprocess.py +0 -0
- {parsl-2024.1.22.dist-info → parsl-2024.2.5.dist-info}/LICENSE +0 -0
- {parsl-2024.1.22.dist-info → parsl-2024.2.5.dist-info}/WHEEL +0 -0
- {parsl-2024.1.22.dist-info → parsl-2024.2.5.dist-info}/entry_points.txt +0 -0
- {parsl-2024.1.22.dist-info → parsl-2024.2.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import parsl
|
4
|
+
import pytest
|
5
|
+
import random
|
6
|
+
from parsl.tests.configs.htex_local import fresh_config
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
|
11
|
+
@parsl.python_app
|
12
|
+
def my_affinity():
|
13
|
+
"""an app that returns the affinity of the unix process it is currently in.
|
14
|
+
"""
|
15
|
+
import os
|
16
|
+
return os.sched_getaffinity(0)
|
17
|
+
|
18
|
+
|
19
|
+
@pytest.mark.local
|
20
|
+
@pytest.mark.multiple_cores_required
|
21
|
+
def test_cpu_affinity_explicit():
|
22
|
+
available_cores = os.sched_getaffinity(0)
|
23
|
+
|
24
|
+
logger.debug(f"Got these cores: {available_cores}")
|
25
|
+
|
26
|
+
assert len(available_cores) >= 2, "This test requires multiple cores. Run with '-k not multiple_cores' to skip"
|
27
|
+
|
28
|
+
cores_as_list = list(available_cores)
|
29
|
+
|
30
|
+
single_core = random.choice(cores_as_list)
|
31
|
+
affinity = f"list:{single_core}"
|
32
|
+
|
33
|
+
logger.debug(f"Will test with affinity for one worker, one core: {affinity}")
|
34
|
+
|
35
|
+
config = fresh_config()
|
36
|
+
config.executors[0].cpu_affinity = affinity
|
37
|
+
config.executors[0].max_workers = 1
|
38
|
+
|
39
|
+
logger.debug(f"config: {config}")
|
40
|
+
# TODO: is there a `with` style for this, to properly deal with exceptions?
|
41
|
+
|
42
|
+
parsl.load(config)
|
43
|
+
try:
|
44
|
+
|
45
|
+
worker_affinity = my_affinity().result()
|
46
|
+
logger.debug(f"worker reported this affinity: {worker_affinity}")
|
47
|
+
assert len(worker_affinity) == 1
|
48
|
+
assert worker_affinity == set((single_core,))
|
49
|
+
|
50
|
+
finally:
|
51
|
+
parsl.dfk().cleanup()
|
52
|
+
parsl.clear()
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import pathlib
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
from parsl import curvezmq
|
6
|
+
from parsl import HighThroughputExecutor
|
7
|
+
|
8
|
+
|
9
|
+
@pytest.mark.local
|
10
|
+
@pytest.mark.parametrize("encrypted", (True, False))
|
11
|
+
@pytest.mark.parametrize("cert_dir_provided", (True, False))
|
12
|
+
def test_htex_start_encrypted(
|
13
|
+
encrypted: bool, cert_dir_provided: bool, tmpd_cwd: pathlib.Path
|
14
|
+
):
|
15
|
+
htex = HighThroughputExecutor(encrypted=encrypted)
|
16
|
+
htex.run_dir = str(tmpd_cwd)
|
17
|
+
if cert_dir_provided:
|
18
|
+
provided_base_dir = tmpd_cwd / "provided"
|
19
|
+
provided_base_dir.mkdir()
|
20
|
+
cert_dir = curvezmq.create_certificates(provided_base_dir)
|
21
|
+
htex.cert_dir = cert_dir
|
22
|
+
else:
|
23
|
+
cert_dir = curvezmq.create_certificates(htex.logdir)
|
24
|
+
|
25
|
+
if not encrypted and cert_dir_provided:
|
26
|
+
with pytest.raises(AttributeError) as pyt_e:
|
27
|
+
htex.start()
|
28
|
+
assert "change cert_dir to None" in str(pyt_e.value)
|
29
|
+
return
|
30
|
+
|
31
|
+
htex.start()
|
32
|
+
|
33
|
+
assert htex.encrypted is encrypted
|
34
|
+
if encrypted:
|
35
|
+
assert htex.cert_dir == cert_dir
|
36
|
+
assert htex.outgoing_q.zmq_context.cert_dir == cert_dir
|
37
|
+
assert htex.incoming_q.zmq_context.cert_dir == cert_dir
|
38
|
+
assert htex.command_client.zmq_context.cert_dir == cert_dir
|
39
|
+
assert isinstance(htex.outgoing_q.zmq_context, curvezmq.ClientContext)
|
40
|
+
assert isinstance(htex.incoming_q.zmq_context, curvezmq.ClientContext)
|
41
|
+
assert isinstance(htex.command_client.zmq_context, curvezmq.ClientContext)
|
42
|
+
else:
|
43
|
+
assert htex.cert_dir is None
|
44
|
+
assert htex.outgoing_q.zmq_context.cert_dir is None
|
45
|
+
assert htex.incoming_q.zmq_context.cert_dir is None
|
46
|
+
assert htex.command_client.zmq_context.cert_dir is None
|
@@ -1,42 +1,82 @@
|
|
1
|
-
import
|
1
|
+
import pathlib
|
2
|
+
from typing import Optional
|
3
|
+
from unittest import mock
|
2
4
|
|
3
5
|
import psutil
|
4
6
|
import pytest
|
5
7
|
import zmq
|
6
8
|
|
9
|
+
from parsl import curvezmq
|
7
10
|
from parsl.executors.high_throughput.interchange import Interchange
|
8
11
|
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
@pytest.fixture
|
14
|
+
def encrypted(request: pytest.FixtureRequest):
|
15
|
+
if hasattr(request, "param"):
|
16
|
+
return request.param
|
17
|
+
return True
|
18
|
+
|
19
|
+
|
20
|
+
@pytest.fixture
|
21
|
+
def cert_dir(encrypted: bool, tmpd_cwd: pathlib.Path):
|
22
|
+
if not encrypted:
|
23
|
+
return None
|
24
|
+
return curvezmq.create_certificates(tmpd_cwd)
|
25
|
+
|
26
|
+
|
27
|
+
@pytest.mark.local
|
28
|
+
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
29
|
+
@mock.patch.object(curvezmq.ServerContext, "socket", return_value=mock.MagicMock())
|
30
|
+
def test_interchange_curvezmq_sockets(
|
31
|
+
mock_socket: mock.MagicMock, cert_dir: Optional[str], encrypted: bool
|
32
|
+
):
|
33
|
+
address = "127.0.0.1"
|
34
|
+
ix = Interchange(interchange_address=address, cert_dir=cert_dir)
|
35
|
+
assert isinstance(ix.zmq_context, curvezmq.ServerContext)
|
36
|
+
assert ix.zmq_context.encrypted is encrypted
|
37
|
+
assert mock_socket.call_count == 5
|
38
|
+
|
39
|
+
|
40
|
+
@pytest.mark.local
|
41
|
+
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
42
|
+
def test_interchange_binding_no_address(cert_dir: Optional[str]):
|
43
|
+
ix = Interchange(cert_dir=cert_dir)
|
12
44
|
assert ix.interchange_address == "*"
|
13
45
|
|
14
46
|
|
15
|
-
|
47
|
+
@pytest.mark.local
|
48
|
+
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
49
|
+
def test_interchange_binding_with_address(cert_dir: Optional[str]):
|
16
50
|
# Using loopback address
|
17
51
|
address = "127.0.0.1"
|
18
|
-
ix = Interchange(interchange_address=address)
|
52
|
+
ix = Interchange(interchange_address=address, cert_dir=cert_dir)
|
19
53
|
assert ix.interchange_address == address
|
20
54
|
|
21
55
|
|
22
|
-
|
56
|
+
@pytest.mark.local
|
57
|
+
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
58
|
+
def test_interchange_binding_with_non_ipv4_address(cert_dir: Optional[str]):
|
23
59
|
# Confirm that a ipv4 address is required
|
24
60
|
address = "localhost"
|
25
61
|
with pytest.raises(zmq.error.ZMQError):
|
26
|
-
Interchange(interchange_address=address)
|
62
|
+
Interchange(interchange_address=address, cert_dir=cert_dir)
|
27
63
|
|
28
64
|
|
29
|
-
|
30
|
-
|
65
|
+
@pytest.mark.local
|
66
|
+
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
67
|
+
def test_interchange_binding_bad_address(cert_dir: Optional[str]):
|
68
|
+
"""Confirm that we raise a ZMQError when a bad address is supplied"""
|
31
69
|
address = "550.0.0.0"
|
32
70
|
with pytest.raises(zmq.error.ZMQError):
|
33
|
-
Interchange(interchange_address=address)
|
71
|
+
Interchange(interchange_address=address, cert_dir=cert_dir)
|
34
72
|
|
35
73
|
|
36
|
-
|
37
|
-
|
74
|
+
@pytest.mark.local
|
75
|
+
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
76
|
+
def test_limited_interface_binding(cert_dir: Optional[str]):
|
77
|
+
"""When address is specified the worker_port would be bound to it rather than to 0.0.0.0"""
|
38
78
|
address = "127.0.0.1"
|
39
|
-
ix = Interchange(interchange_address=address)
|
79
|
+
ix = Interchange(interchange_address=address, cert_dir=cert_dir)
|
40
80
|
ix.worker_result_port
|
41
81
|
proc = psutil.Process()
|
42
82
|
conns = proc.connections(kind="tcp")
|
@@ -42,10 +42,10 @@ def test_fut_case_1():
|
|
42
42
|
|
43
43
|
|
44
44
|
@pytest.mark.staging_required
|
45
|
-
def test_fut_case_2(
|
45
|
+
def test_fut_case_2(tmpd_cwd):
|
46
46
|
"""Testing the behavior of DataFutures where there are no dependencies
|
47
47
|
"""
|
48
|
-
output_f =
|
48
|
+
output_f = tmpd_cwd / 'test_fut_case_2.txt'
|
49
49
|
app_fu = delay_incr(1, delay=0.01, outputs=[File(str(output_f))])
|
50
50
|
data_fu = app_fu.outputs[0]
|
51
51
|
|
@@ -69,13 +69,13 @@ def test_fut_case_3():
|
|
69
69
|
|
70
70
|
|
71
71
|
@pytest.mark.staging_required
|
72
|
-
def test_fut_case_4(
|
72
|
+
def test_fut_case_4(tmpd_cwd):
|
73
73
|
"""Testing the behavior of DataFutures where there are dependencies
|
74
74
|
|
75
75
|
The first call has a delay, and the second call depends on the first
|
76
76
|
"""
|
77
|
-
output_f1 =
|
78
|
-
output_f2 =
|
77
|
+
output_f1 = tmpd_cwd / 'test_fut_case_4_f1.txt'
|
78
|
+
output_f2 = tmpd_cwd / 'test_fut_case_4_f2.txt'
|
79
79
|
app_1 = delay_incr(1, delay=0.01, outputs=[File(str(output_f1))])
|
80
80
|
app_2 = delay_incr(app_1, delay=0.01, outputs=[File(str(output_f2))])
|
81
81
|
data_2 = app_2.outputs[0]
|
@@ -11,7 +11,7 @@ from functools import partial
|
|
11
11
|
@pytest.mark.local
|
12
12
|
def test_block_error_handler_false():
|
13
13
|
mock = Mock()
|
14
|
-
htex = HighThroughputExecutor(block_error_handler=False)
|
14
|
+
htex = HighThroughputExecutor(block_error_handler=False, encrypted=True)
|
15
15
|
assert htex.block_error_handler is noop_error_handler
|
16
16
|
htex.set_bad_state_and_fail_all = mock
|
17
17
|
|
@@ -27,7 +27,7 @@ def test_block_error_handler_false():
|
|
27
27
|
@pytest.mark.local
|
28
28
|
def test_block_error_handler_mock():
|
29
29
|
handler_mock = Mock()
|
30
|
-
htex = HighThroughputExecutor(block_error_handler=handler_mock)
|
30
|
+
htex = HighThroughputExecutor(block_error_handler=handler_mock, encrypted=True)
|
31
31
|
assert htex.block_error_handler is handler_mock
|
32
32
|
|
33
33
|
bad_jobs = {'1': JobStatus(JobState.FAILED),
|
@@ -43,6 +43,7 @@ def test_block_error_handler_mock():
|
|
43
43
|
@pytest.mark.local
|
44
44
|
def test_simple_error_handler():
|
45
45
|
htex = HighThroughputExecutor(block_error_handler=simple_error_handler,
|
46
|
+
encrypted=True,
|
46
47
|
provider=LocalProvider(init_blocks=3))
|
47
48
|
|
48
49
|
assert htex.block_error_handler is simple_error_handler
|
@@ -76,7 +77,7 @@ def test_simple_error_handler():
|
|
76
77
|
|
77
78
|
@pytest.mark.local
|
78
79
|
def test_windowed_error_handler():
|
79
|
-
htex = HighThroughputExecutor(block_error_handler=windowed_error_handler)
|
80
|
+
htex = HighThroughputExecutor(block_error_handler=windowed_error_handler, encrypted=True)
|
80
81
|
assert htex.block_error_handler is windowed_error_handler
|
81
82
|
|
82
83
|
bad_state_mock = Mock()
|
@@ -110,7 +111,7 @@ def test_windowed_error_handler():
|
|
110
111
|
|
111
112
|
@pytest.mark.local
|
112
113
|
def test_windowed_error_handler_sorting():
|
113
|
-
htex = HighThroughputExecutor(block_error_handler=windowed_error_handler)
|
114
|
+
htex = HighThroughputExecutor(block_error_handler=windowed_error_handler, encrypted=True)
|
114
115
|
assert htex.block_error_handler is windowed_error_handler
|
115
116
|
|
116
117
|
bad_state_mock = Mock()
|
@@ -136,7 +137,7 @@ def test_windowed_error_handler_sorting():
|
|
136
137
|
@pytest.mark.local
|
137
138
|
def test_windowed_error_handler_with_threshold():
|
138
139
|
error_handler = partial(windowed_error_handler, threshold=2)
|
139
|
-
htex = HighThroughputExecutor(block_error_handler=error_handler)
|
140
|
+
htex = HighThroughputExecutor(block_error_handler=error_handler, encrypted=True)
|
140
141
|
assert htex.block_error_handler is error_handler
|
141
142
|
|
142
143
|
bad_state_mock = Mock()
|
parsl/version.py
CHANGED
@@ -20,8 +20,8 @@ import multiprocessing
|
|
20
20
|
from multiprocessing.managers import DictProxy
|
21
21
|
from multiprocessing.sharedctypes import Synchronized
|
22
22
|
|
23
|
+
from parsl import curvezmq
|
23
24
|
from parsl.process_loggers import wrap_with_logs
|
24
|
-
|
25
25
|
from parsl.version import VERSION as PARSL_VERSION
|
26
26
|
from parsl.app.errors import RemoteExceptionWrapper
|
27
27
|
from parsl.executors.high_throughput.errors import WorkerLost
|
@@ -63,7 +63,8 @@ class Manager:
|
|
63
63
|
heartbeat_period,
|
64
64
|
poll_period,
|
65
65
|
cpu_affinity,
|
66
|
-
available_accelerators: Sequence[str]
|
66
|
+
available_accelerators: Sequence[str],
|
67
|
+
cert_dir: Optional[str]):
|
67
68
|
"""
|
68
69
|
Parameters
|
69
70
|
----------
|
@@ -118,6 +119,8 @@ class Manager:
|
|
118
119
|
available_accelerators: list of str
|
119
120
|
List of accelerators available to the workers.
|
120
121
|
|
122
|
+
cert_dir : str | None
|
123
|
+
Path to the certificate directory.
|
121
124
|
"""
|
122
125
|
|
123
126
|
logger.info("Manager started")
|
@@ -137,15 +140,16 @@ class Manager:
|
|
137
140
|
print("Failed to find a viable address to connect to interchange. Exiting")
|
138
141
|
exit(5)
|
139
142
|
|
140
|
-
self.
|
141
|
-
self.
|
143
|
+
self.cert_dir = cert_dir
|
144
|
+
self.zmq_context = curvezmq.ClientContext(self.cert_dir)
|
145
|
+
self.task_incoming = self.zmq_context.socket(zmq.DEALER)
|
142
146
|
self.task_incoming.setsockopt(zmq.IDENTITY, uid.encode('utf-8'))
|
143
147
|
# Linger is set to 0, so that the manager can exit even when there might be
|
144
148
|
# messages in the pipe
|
145
149
|
self.task_incoming.setsockopt(zmq.LINGER, 0)
|
146
150
|
self.task_incoming.connect(task_q_url)
|
147
151
|
|
148
|
-
self.result_outgoing = self.
|
152
|
+
self.result_outgoing = self.zmq_context.socket(zmq.DEALER)
|
149
153
|
self.result_outgoing.setsockopt(zmq.IDENTITY, uid.encode('utf-8'))
|
150
154
|
self.result_outgoing.setsockopt(zmq.LINGER, 0)
|
151
155
|
self.result_outgoing.connect(result_q_url)
|
@@ -468,7 +472,7 @@ class Manager:
|
|
468
472
|
|
469
473
|
self.task_incoming.close()
|
470
474
|
self.result_outgoing.close()
|
471
|
-
self.
|
475
|
+
self.zmq_context.term()
|
472
476
|
delta = time.time() - start
|
473
477
|
logger.info("process_worker_pool ran for {} seconds".format(delta))
|
474
478
|
return
|
@@ -578,7 +582,7 @@ def worker(
|
|
578
582
|
# If desired, set process affinity
|
579
583
|
if cpu_affinity != "none":
|
580
584
|
# Count the number of cores per worker
|
581
|
-
avail_cores = sorted(os.sched_getaffinity(0)) # Get the available
|
585
|
+
avail_cores = sorted(os.sched_getaffinity(0)) # Get the available threads
|
582
586
|
cores_per_worker = len(avail_cores) // pool_size
|
583
587
|
assert cores_per_worker > 0, "Affinity does not work if there are more workers than cores"
|
584
588
|
|
@@ -590,6 +594,23 @@ def worker(
|
|
590
594
|
my_cores = avail_cores[cores_per_worker * cpu_worker_id:cores_per_worker * (cpu_worker_id + 1)]
|
591
595
|
elif cpu_affinity == "alternating":
|
592
596
|
my_cores = avail_cores[worker_id::pool_size]
|
597
|
+
elif cpu_affinity[0:4] == "list":
|
598
|
+
thread_ranks = cpu_affinity.split(":")[1:]
|
599
|
+
if len(thread_ranks) != pool_size:
|
600
|
+
raise ValueError("Affinity list {} has wrong number of thread ranks".format(cpu_affinity))
|
601
|
+
threads = thread_ranks[worker_id]
|
602
|
+
thread_list = threads.split(",")
|
603
|
+
my_cores = []
|
604
|
+
for tl in thread_list:
|
605
|
+
thread_range = tl.split("-")
|
606
|
+
if len(thread_range) == 1:
|
607
|
+
my_cores.append(int(thread_range[0]))
|
608
|
+
elif len(thread_range) == 2:
|
609
|
+
start_thread = int(thread_range[0])
|
610
|
+
end_thread = int(thread_range[1]) + 1
|
611
|
+
my_cores += list(range(start_thread, end_thread))
|
612
|
+
else:
|
613
|
+
raise ValueError("Affinity list formatting is not expected {}".format(cpu_affinity))
|
593
614
|
else:
|
594
615
|
raise ValueError("Affinity strategy {} is not supported".format(cpu_affinity))
|
595
616
|
|
@@ -703,6 +724,8 @@ if __name__ == "__main__":
|
|
703
724
|
help="Enable logging at DEBUG level")
|
704
725
|
parser.add_argument("-a", "--addresses", default='',
|
705
726
|
help="Comma separated list of addresses at which the interchange could be reached")
|
727
|
+
parser.add_argument("--cert_dir", required=True,
|
728
|
+
help="Path to certificate directory.")
|
706
729
|
parser.add_argument("-l", "--logdir", default="process_worker_pool_logs",
|
707
730
|
help="Process worker pool log directory")
|
708
731
|
parser.add_argument("-u", "--uid", default=str(uuid.uuid4()).split('-')[-1],
|
@@ -729,7 +752,17 @@ if __name__ == "__main__":
|
|
729
752
|
help="Poll period used in milliseconds")
|
730
753
|
parser.add_argument("-r", "--result_port", required=True,
|
731
754
|
help="REQUIRED: Result port for posting results to the interchange")
|
732
|
-
|
755
|
+
|
756
|
+
def strategyorlist(s: str):
|
757
|
+
allowed_strategies = ["none", "block", "alternating", "block-reverse"]
|
758
|
+
if s in allowed_strategies:
|
759
|
+
return s
|
760
|
+
elif s[0:4] == "list":
|
761
|
+
return s
|
762
|
+
else:
|
763
|
+
raise argparse.ArgumentTypeError("cpu-affinity must be one of {} or a list format".format(allowed_strategies))
|
764
|
+
|
765
|
+
parser.add_argument("--cpu-affinity", type=strategyorlist,
|
733
766
|
required=True,
|
734
767
|
help="Whether/how workers should control CPU affinity.")
|
735
768
|
parser.add_argument("--available-accelerators", type=str, nargs="*",
|
@@ -746,6 +779,7 @@ if __name__ == "__main__":
|
|
746
779
|
|
747
780
|
logger.info("Python version: {}".format(sys.version))
|
748
781
|
logger.info("Debug logging: {}".format(args.debug))
|
782
|
+
logger.info("Certificates dir: {}".format(args.cert_dir))
|
749
783
|
logger.info("Log dir: {}".format(args.logdir))
|
750
784
|
logger.info("Manager ID: {}".format(args.uid))
|
751
785
|
logger.info("Block ID: {}".format(args.block_id))
|
@@ -777,7 +811,8 @@ if __name__ == "__main__":
|
|
777
811
|
heartbeat_period=int(args.hb_period),
|
778
812
|
poll_period=int(args.poll),
|
779
813
|
cpu_affinity=args.cpu_affinity,
|
780
|
-
available_accelerators=args.available_accelerators
|
814
|
+
available_accelerators=args.available_accelerators,
|
815
|
+
cert_dir=None if args.cert_dir == "None" else args.cert_dir)
|
781
816
|
manager.start()
|
782
817
|
|
783
818
|
except Exception:
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: parsl
|
3
|
-
Version: 2024.
|
3
|
+
Version: 2024.2.5
|
4
4
|
Summary: Simple data dependent workflows in Python
|
5
5
|
Home-page: https://github.com/Parsl/parsl
|
6
|
-
Download-URL: https://github.com/Parsl/parsl/archive/2024.
|
6
|
+
Download-URL: https://github.com/Parsl/parsl/archive/2024.02.05.tar.gz
|
7
7
|
Author: The Parsl Team
|
8
8
|
Author-email: parsl@googlegroups.com
|
9
9
|
License: Apache 2.0
|
@@ -19,9 +19,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Requires-Python: >=3.8.0
|
20
20
|
License-File: LICENSE
|
21
21
|
Requires-Dist: pyzmq >=17.1.2
|
22
|
-
Requires-Dist: typeguard
|
22
|
+
Requires-Dist: typeguard !=3.*,<5,>=2.10
|
23
23
|
Requires-Dist: typing-extensions <5,>=4.6
|
24
|
-
Requires-Dist: six
|
25
24
|
Requires-Dist: globus-sdk
|
26
25
|
Requires-Dist: dill
|
27
26
|
Requires-Dist: tblib
|
@@ -35,7 +34,7 @@ Requires-Dist: pydot ; extra == 'all'
|
|
35
34
|
Requires-Dist: networkx <2.6,>=2.5 ; extra == 'all'
|
36
35
|
Requires-Dist: Flask >=1.0.2 ; extra == 'all'
|
37
36
|
Requires-Dist: flask-sqlalchemy ; extra == 'all'
|
38
|
-
Requires-Dist: pandas <
|
37
|
+
Requires-Dist: pandas <2.2 ; extra == 'all'
|
39
38
|
Requires-Dist: plotly ; extra == 'all'
|
40
39
|
Requires-Dist: python-daemon ; extra == 'all'
|
41
40
|
Requires-Dist: boto3 ; extra == 'all'
|
@@ -90,7 +89,7 @@ Requires-Dist: pydot ; extra == 'visualization'
|
|
90
89
|
Requires-Dist: networkx <2.6,>=2.5 ; extra == 'visualization'
|
91
90
|
Requires-Dist: Flask >=1.0.2 ; extra == 'visualization'
|
92
91
|
Requires-Dist: flask-sqlalchemy ; extra == 'visualization'
|
93
|
-
Requires-Dist: pandas <
|
92
|
+
Requires-Dist: pandas <2.2 ; extra == 'visualization'
|
94
93
|
Requires-Dist: plotly ; extra == 'visualization'
|
95
94
|
Requires-Dist: python-daemon ; extra == 'visualization'
|
96
95
|
Provides-Extra: workqueue
|