flwr-nightly 1.13.0.dev20241021__py3-none-any.whl → 1.13.0.dev20241111__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.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/cli/build.py +2 -2
- flwr/cli/config_utils.py +97 -0
- flwr/cli/log.py +63 -97
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -0
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/run.py +34 -88
- flwr/client/app.py +23 -20
- flwr/client/clientapp/app.py +22 -18
- flwr/client/nodestate/__init__.py +25 -0
- flwr/client/nodestate/in_memory_nodestate.py +38 -0
- flwr/client/nodestate/nodestate.py +30 -0
- flwr/client/nodestate/nodestate_factory.py +37 -0
- flwr/client/{node_state.py → run_info_store.py} +4 -3
- flwr/client/supernode/app.py +6 -8
- flwr/common/args.py +83 -0
- flwr/common/config.py +10 -0
- flwr/common/constant.py +39 -5
- flwr/common/context.py +9 -4
- flwr/common/date.py +3 -3
- flwr/common/logger.py +108 -1
- flwr/common/object_ref.py +47 -16
- flwr/common/serde.py +24 -0
- flwr/common/telemetry.py +0 -6
- flwr/common/typing.py +10 -1
- flwr/proto/exec_pb2.py +14 -17
- flwr/proto/exec_pb2.pyi +14 -22
- flwr/proto/log_pb2.py +29 -0
- flwr/proto/log_pb2.pyi +39 -0
- flwr/proto/log_pb2_grpc.py +4 -0
- flwr/proto/log_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +8 -8
- flwr/proto/message_pb2.pyi +4 -1
- flwr/proto/run_pb2.py +32 -27
- flwr/proto/run_pb2.pyi +26 -0
- flwr/proto/serverappio_pb2.py +52 -0
- flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +54 -0
- flwr/proto/serverappio_pb2_grpc.py +376 -0
- flwr/proto/serverappio_pb2_grpc.pyi +147 -0
- flwr/proto/simulationio_pb2.py +38 -0
- flwr/proto/simulationio_pb2.pyi +65 -0
- flwr/proto/simulationio_pb2_grpc.py +205 -0
- flwr/proto/simulationio_pb2_grpc.pyi +81 -0
- flwr/server/app.py +272 -105
- flwr/server/driver/driver.py +15 -1
- flwr/server/driver/grpc_driver.py +25 -36
- flwr/server/driver/inmemory_driver.py +6 -16
- flwr/server/run_serverapp.py +29 -23
- flwr/server/{superlink/state → serverapp}/__init__.py +3 -9
- flwr/server/serverapp/app.py +214 -0
- flwr/server/strategy/aggregate.py +4 -4
- flwr/server/strategy/fedadam.py +11 -1
- flwr/server/superlink/driver/__init__.py +1 -1
- flwr/server/superlink/driver/{driver_grpc.py → serverappio_grpc.py} +19 -16
- flwr/server/superlink/driver/{driver_servicer.py → serverappio_servicer.py} +125 -39
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +4 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -2
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +4 -2
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -2
- flwr/server/superlink/fleet/message_handler/message_handler.py +7 -7
- flwr/server/superlink/fleet/rest_rere/rest_api.py +7 -7
- flwr/server/superlink/fleet/vce/vce_api.py +23 -23
- flwr/server/superlink/linkstate/__init__.py +28 -0
- flwr/server/superlink/{state/in_memory_state.py → linkstate/in_memory_linkstate.py} +184 -36
- flwr/server/superlink/{state/state.py → linkstate/linkstate.py} +149 -19
- flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +9 -9
- flwr/server/superlink/{state/sqlite_state.py → linkstate/sqlite_linkstate.py} +306 -65
- flwr/server/superlink/{state → linkstate}/utils.py +81 -30
- flwr/server/superlink/simulation/__init__.py +15 -0
- flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
- flwr/server/superlink/simulation/simulationio_servicer.py +153 -0
- flwr/simulation/__init__.py +5 -1
- flwr/simulation/app.py +273 -345
- flwr/simulation/legacy_app.py +382 -0
- flwr/simulation/ray_transport/ray_client_proxy.py +2 -2
- flwr/simulation/run_simulation.py +57 -131
- flwr/simulation/simulationio_connection.py +86 -0
- flwr/superexec/app.py +6 -134
- flwr/superexec/deployment.py +61 -66
- flwr/superexec/exec_grpc.py +15 -8
- flwr/superexec/exec_servicer.py +36 -65
- flwr/superexec/executor.py +26 -7
- flwr/superexec/simulation.py +54 -107
- {flwr_nightly-1.13.0.dev20241021.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/METADATA +5 -4
- {flwr_nightly-1.13.0.dev20241021.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/RECORD +88 -69
- {flwr_nightly-1.13.0.dev20241021.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/entry_points.txt +2 -0
- flwr/client/node_state_tests.py +0 -66
- flwr/proto/driver_pb2.py +0 -42
- flwr/proto/driver_pb2_grpc.py +0 -239
- flwr/proto/driver_pb2_grpc.pyi +0 -94
- {flwr_nightly-1.13.0.dev20241021.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.13.0.dev20241021.dist-info → flwr_nightly-1.13.0.dev20241111.dist-info}/WHEEL +0 -0
flwr/server/app.py
CHANGED
|
@@ -17,12 +17,14 @@
|
|
|
17
17
|
import argparse
|
|
18
18
|
import csv
|
|
19
19
|
import importlib.util
|
|
20
|
+
import subprocess
|
|
20
21
|
import sys
|
|
21
22
|
import threading
|
|
22
23
|
from collections.abc import Sequence
|
|
23
|
-
from logging import INFO, WARN
|
|
24
|
+
from logging import DEBUG, INFO, WARN
|
|
24
25
|
from os.path import isfile
|
|
25
26
|
from pathlib import Path
|
|
27
|
+
from time import sleep
|
|
26
28
|
from typing import Optional
|
|
27
29
|
|
|
28
30
|
import grpc
|
|
@@ -35,13 +37,17 @@ from cryptography.hazmat.primitives.serialization import (
|
|
|
35
37
|
|
|
36
38
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
|
|
37
39
|
from flwr.common.address import parse_address
|
|
38
|
-
from flwr.common.config import get_flwr_dir
|
|
40
|
+
from flwr.common.config import get_flwr_dir, parse_config_args
|
|
39
41
|
from flwr.common.constant import (
|
|
40
|
-
|
|
42
|
+
EXEC_API_DEFAULT_ADDRESS,
|
|
41
43
|
FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
|
|
42
44
|
FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
|
|
43
45
|
FLEET_API_REST_DEFAULT_ADDRESS,
|
|
46
|
+
ISOLATION_MODE_PROCESS,
|
|
47
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
44
48
|
MISSING_EXTRA_REST,
|
|
49
|
+
SERVERAPPIO_API_DEFAULT_ADDRESS,
|
|
50
|
+
SIMULATIONIO_API_DEFAULT_ADDRESS,
|
|
45
51
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
46
52
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
47
53
|
TRANSPORT_TYPE_REST,
|
|
@@ -56,13 +62,16 @@ from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
|
|
|
56
62
|
add_FleetServicer_to_server,
|
|
57
63
|
)
|
|
58
64
|
from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
|
|
65
|
+
from flwr.superexec.app import load_executor
|
|
66
|
+
from flwr.superexec.exec_grpc import run_exec_api_grpc
|
|
67
|
+
from flwr.superexec.simulation import SimulationEngine
|
|
59
68
|
|
|
60
69
|
from .client_manager import ClientManager
|
|
61
70
|
from .history import History
|
|
62
71
|
from .server import Server, init_defaults, run_fl
|
|
63
72
|
from .server_config import ServerConfig
|
|
64
73
|
from .strategy import Strategy
|
|
65
|
-
from .superlink.driver.
|
|
74
|
+
from .superlink.driver.serverappio_grpc import run_serverappio_api_grpc
|
|
66
75
|
from .superlink.ffs.ffs_factory import FfsFactory
|
|
67
76
|
from .superlink.fleet.grpc_adapter.grpc_adapter_servicer import GrpcAdapterServicer
|
|
68
77
|
from .superlink.fleet.grpc_bidi.grpc_server import (
|
|
@@ -71,7 +80,8 @@ from .superlink.fleet.grpc_bidi.grpc_server import (
|
|
|
71
80
|
)
|
|
72
81
|
from .superlink.fleet.grpc_rere.fleet_servicer import FleetServicer
|
|
73
82
|
from .superlink.fleet.grpc_rere.server_interceptor import AuthenticateServerInterceptor
|
|
74
|
-
from .superlink.
|
|
83
|
+
from .superlink.linkstate import LinkStateFactory
|
|
84
|
+
from .superlink.simulation.simulationio_grpc import run_simulationio_api_grpc
|
|
75
85
|
|
|
76
86
|
DATABASE = ":flwr-in-memory-state:"
|
|
77
87
|
BASE_DIR = get_flwr_dir() / "superlink" / "ffs"
|
|
@@ -198,125 +208,180 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
198
208
|
|
|
199
209
|
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
|
|
200
210
|
def run_superlink() -> None:
|
|
201
|
-
"""Run Flower SuperLink (
|
|
211
|
+
"""Run Flower SuperLink (ServerAppIo API and Fleet API)."""
|
|
202
212
|
args = _parse_args_run_superlink().parse_args()
|
|
203
213
|
|
|
204
214
|
log(INFO, "Starting Flower SuperLink")
|
|
205
215
|
|
|
206
216
|
event(EventType.RUN_SUPERLINK_ENTER)
|
|
207
217
|
|
|
208
|
-
#
|
|
209
|
-
|
|
218
|
+
# Warn unused options
|
|
219
|
+
if args.flwr_dir is not None:
|
|
220
|
+
log(
|
|
221
|
+
WARN, "The `--flwr-dir` option is currently not in use and will be ignored."
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Parse IP addresses
|
|
225
|
+
serverappio_address, _, _ = _format_address(args.serverappio_api_address)
|
|
226
|
+
exec_address, _, _ = _format_address(args.exec_api_address)
|
|
227
|
+
simulationio_address, _, _ = _format_address(args.simulationio_api_address)
|
|
210
228
|
|
|
211
229
|
# Obtain certificates
|
|
212
230
|
certificates = _try_obtain_certificates(args)
|
|
213
231
|
|
|
214
232
|
# Initialize StateFactory
|
|
215
|
-
state_factory =
|
|
233
|
+
state_factory = LinkStateFactory(args.database)
|
|
216
234
|
|
|
217
235
|
# Initialize FfsFactory
|
|
218
236
|
ffs_factory = FfsFactory(args.storage_dir)
|
|
219
237
|
|
|
220
|
-
# Start
|
|
221
|
-
|
|
222
|
-
|
|
238
|
+
# Start Exec API
|
|
239
|
+
executor = load_executor(args)
|
|
240
|
+
exec_server: grpc.Server = run_exec_api_grpc(
|
|
241
|
+
address=exec_address,
|
|
223
242
|
state_factory=state_factory,
|
|
224
243
|
ffs_factory=ffs_factory,
|
|
244
|
+
executor=executor,
|
|
225
245
|
certificates=certificates,
|
|
246
|
+
config=parse_config_args(
|
|
247
|
+
[args.executor_config] if args.executor_config else args.executor_config
|
|
248
|
+
),
|
|
226
249
|
)
|
|
250
|
+
grpc_servers = [exec_server]
|
|
227
251
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if args.fleet_api_type in [
|
|
232
|
-
TRANSPORT_TYPE_GRPC_RERE,
|
|
233
|
-
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
234
|
-
]:
|
|
235
|
-
args.fleet_api_address = FLEET_API_GRPC_RERE_DEFAULT_ADDRESS
|
|
236
|
-
elif args.fleet_api_type == TRANSPORT_TYPE_REST:
|
|
237
|
-
args.fleet_api_address = FLEET_API_REST_DEFAULT_ADDRESS
|
|
238
|
-
|
|
239
|
-
fleet_address, host, port = _format_address(args.fleet_api_address)
|
|
252
|
+
# Determine Exec plugin
|
|
253
|
+
# If simulation is used, don't start ServerAppIo and Fleet APIs
|
|
254
|
+
sim_exec = isinstance(executor, SimulationEngine)
|
|
240
255
|
|
|
241
|
-
|
|
242
|
-
if num_workers != 1:
|
|
243
|
-
log(
|
|
244
|
-
WARN,
|
|
245
|
-
"The Fleet API currently supports only 1 worker. "
|
|
246
|
-
"You have specified %d workers. "
|
|
247
|
-
"Support for multiple workers will be added in future releases. "
|
|
248
|
-
"Proceeding with a single worker.",
|
|
249
|
-
args.fleet_api_num_workers,
|
|
250
|
-
)
|
|
251
|
-
num_workers = 1
|
|
252
|
-
|
|
253
|
-
# Start Fleet API
|
|
254
|
-
if args.fleet_api_type == TRANSPORT_TYPE_REST:
|
|
255
|
-
if (
|
|
256
|
-
importlib.util.find_spec("requests")
|
|
257
|
-
and importlib.util.find_spec("starlette")
|
|
258
|
-
and importlib.util.find_spec("uvicorn")
|
|
259
|
-
) is None:
|
|
260
|
-
sys.exit(MISSING_EXTRA_REST)
|
|
261
|
-
|
|
262
|
-
_, ssl_certfile, ssl_keyfile = (
|
|
263
|
-
certificates if certificates is not None else (None, None, None)
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
fleet_thread = threading.Thread(
|
|
267
|
-
target=_run_fleet_api_rest,
|
|
268
|
-
args=(
|
|
269
|
-
host,
|
|
270
|
-
port,
|
|
271
|
-
ssl_keyfile,
|
|
272
|
-
ssl_certfile,
|
|
273
|
-
state_factory,
|
|
274
|
-
ffs_factory,
|
|
275
|
-
num_workers,
|
|
276
|
-
),
|
|
277
|
-
)
|
|
278
|
-
fleet_thread.start()
|
|
279
|
-
bckg_threads.append(fleet_thread)
|
|
280
|
-
elif args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE:
|
|
281
|
-
maybe_keys = _try_setup_node_authentication(args, certificates)
|
|
282
|
-
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None
|
|
283
|
-
if maybe_keys is not None:
|
|
284
|
-
(
|
|
285
|
-
node_public_keys,
|
|
286
|
-
server_private_key,
|
|
287
|
-
server_public_key,
|
|
288
|
-
) = maybe_keys
|
|
289
|
-
state = state_factory.state()
|
|
290
|
-
state.store_node_public_keys(node_public_keys)
|
|
291
|
-
state.store_server_private_public_key(
|
|
292
|
-
private_key_to_bytes(server_private_key),
|
|
293
|
-
public_key_to_bytes(server_public_key),
|
|
294
|
-
)
|
|
295
|
-
log(
|
|
296
|
-
INFO,
|
|
297
|
-
"Node authentication enabled with %d known public keys",
|
|
298
|
-
len(node_public_keys),
|
|
299
|
-
)
|
|
300
|
-
interceptors = [AuthenticateServerInterceptor(state)]
|
|
256
|
+
bckg_threads = []
|
|
301
257
|
|
|
302
|
-
|
|
303
|
-
|
|
258
|
+
if sim_exec:
|
|
259
|
+
simulationio_server: grpc.Server = run_simulationio_api_grpc(
|
|
260
|
+
address=simulationio_address,
|
|
304
261
|
state_factory=state_factory,
|
|
305
262
|
ffs_factory=ffs_factory,
|
|
306
263
|
certificates=certificates,
|
|
307
|
-
interceptors=interceptors,
|
|
308
264
|
)
|
|
309
|
-
grpc_servers.append(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
265
|
+
grpc_servers.append(simulationio_server)
|
|
266
|
+
|
|
267
|
+
else:
|
|
268
|
+
# Start ServerAppIo API
|
|
269
|
+
serverappio_server: grpc.Server = run_serverappio_api_grpc(
|
|
270
|
+
address=serverappio_address,
|
|
313
271
|
state_factory=state_factory,
|
|
314
272
|
ffs_factory=ffs_factory,
|
|
315
273
|
certificates=certificates,
|
|
316
274
|
)
|
|
317
|
-
grpc_servers.append(
|
|
318
|
-
|
|
319
|
-
|
|
275
|
+
grpc_servers.append(serverappio_server)
|
|
276
|
+
|
|
277
|
+
# Start Fleet API
|
|
278
|
+
if not args.fleet_api_address:
|
|
279
|
+
if args.fleet_api_type in [
|
|
280
|
+
TRANSPORT_TYPE_GRPC_RERE,
|
|
281
|
+
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
282
|
+
]:
|
|
283
|
+
args.fleet_api_address = FLEET_API_GRPC_RERE_DEFAULT_ADDRESS
|
|
284
|
+
elif args.fleet_api_type == TRANSPORT_TYPE_REST:
|
|
285
|
+
args.fleet_api_address = FLEET_API_REST_DEFAULT_ADDRESS
|
|
286
|
+
|
|
287
|
+
fleet_address, host, port = _format_address(args.fleet_api_address)
|
|
288
|
+
|
|
289
|
+
num_workers = args.fleet_api_num_workers
|
|
290
|
+
if num_workers != 1:
|
|
291
|
+
log(
|
|
292
|
+
WARN,
|
|
293
|
+
"The Fleet API currently supports only 1 worker. "
|
|
294
|
+
"You have specified %d workers. "
|
|
295
|
+
"Support for multiple workers will be added in future releases. "
|
|
296
|
+
"Proceeding with a single worker.",
|
|
297
|
+
args.fleet_api_num_workers,
|
|
298
|
+
)
|
|
299
|
+
num_workers = 1
|
|
300
|
+
|
|
301
|
+
if args.fleet_api_type == TRANSPORT_TYPE_REST:
|
|
302
|
+
if (
|
|
303
|
+
importlib.util.find_spec("requests")
|
|
304
|
+
and importlib.util.find_spec("starlette")
|
|
305
|
+
and importlib.util.find_spec("uvicorn")
|
|
306
|
+
) is None:
|
|
307
|
+
sys.exit(MISSING_EXTRA_REST)
|
|
308
|
+
|
|
309
|
+
_, ssl_certfile, ssl_keyfile = (
|
|
310
|
+
certificates if certificates is not None else (None, None, None)
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
fleet_thread = threading.Thread(
|
|
314
|
+
target=_run_fleet_api_rest,
|
|
315
|
+
args=(
|
|
316
|
+
host,
|
|
317
|
+
port,
|
|
318
|
+
ssl_keyfile,
|
|
319
|
+
ssl_certfile,
|
|
320
|
+
state_factory,
|
|
321
|
+
ffs_factory,
|
|
322
|
+
num_workers,
|
|
323
|
+
),
|
|
324
|
+
)
|
|
325
|
+
fleet_thread.start()
|
|
326
|
+
bckg_threads.append(fleet_thread)
|
|
327
|
+
elif args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE:
|
|
328
|
+
maybe_keys = _try_setup_node_authentication(args, certificates)
|
|
329
|
+
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None
|
|
330
|
+
if maybe_keys is not None:
|
|
331
|
+
(
|
|
332
|
+
node_public_keys,
|
|
333
|
+
server_private_key,
|
|
334
|
+
server_public_key,
|
|
335
|
+
) = maybe_keys
|
|
336
|
+
state = state_factory.state()
|
|
337
|
+
state.store_node_public_keys(node_public_keys)
|
|
338
|
+
state.store_server_private_public_key(
|
|
339
|
+
private_key_to_bytes(server_private_key),
|
|
340
|
+
public_key_to_bytes(server_public_key),
|
|
341
|
+
)
|
|
342
|
+
log(
|
|
343
|
+
INFO,
|
|
344
|
+
"Node authentication enabled with %d known public keys",
|
|
345
|
+
len(node_public_keys),
|
|
346
|
+
)
|
|
347
|
+
interceptors = [AuthenticateServerInterceptor(state)]
|
|
348
|
+
|
|
349
|
+
fleet_server = _run_fleet_api_grpc_rere(
|
|
350
|
+
address=fleet_address,
|
|
351
|
+
state_factory=state_factory,
|
|
352
|
+
ffs_factory=ffs_factory,
|
|
353
|
+
certificates=certificates,
|
|
354
|
+
interceptors=interceptors,
|
|
355
|
+
)
|
|
356
|
+
grpc_servers.append(fleet_server)
|
|
357
|
+
elif args.fleet_api_type == TRANSPORT_TYPE_GRPC_ADAPTER:
|
|
358
|
+
fleet_server = _run_fleet_api_grpc_adapter(
|
|
359
|
+
address=fleet_address,
|
|
360
|
+
state_factory=state_factory,
|
|
361
|
+
ffs_factory=ffs_factory,
|
|
362
|
+
certificates=certificates,
|
|
363
|
+
)
|
|
364
|
+
grpc_servers.append(fleet_server)
|
|
365
|
+
else:
|
|
366
|
+
raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
|
|
367
|
+
|
|
368
|
+
if args.isolation == ISOLATION_MODE_SUBPROCESS:
|
|
369
|
+
|
|
370
|
+
address = simulationio_address if sim_exec else serverappio_address
|
|
371
|
+
cmd = "flwr-simulation" if sim_exec else "flwr-serverapp"
|
|
372
|
+
|
|
373
|
+
# Scheduler thread
|
|
374
|
+
scheduler_th = threading.Thread(
|
|
375
|
+
target=_flwr_scheduler,
|
|
376
|
+
args=(
|
|
377
|
+
state_factory,
|
|
378
|
+
address,
|
|
379
|
+
args.ssl_ca_certfile,
|
|
380
|
+
cmd,
|
|
381
|
+
),
|
|
382
|
+
)
|
|
383
|
+
scheduler_th.start()
|
|
384
|
+
bckg_threads.append(scheduler_th)
|
|
320
385
|
|
|
321
386
|
# Graceful shutdown
|
|
322
387
|
register_exit_handlers(
|
|
@@ -331,7 +396,49 @@ def run_superlink() -> None:
|
|
|
331
396
|
for thread in bckg_threads:
|
|
332
397
|
if not thread.is_alive():
|
|
333
398
|
sys.exit(1)
|
|
334
|
-
|
|
399
|
+
exec_server.wait_for_termination(timeout=1)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _flwr_scheduler(
|
|
403
|
+
state_factory: LinkStateFactory,
|
|
404
|
+
io_api_address: str,
|
|
405
|
+
ssl_ca_certfile: Optional[str],
|
|
406
|
+
cmd: str,
|
|
407
|
+
) -> None:
|
|
408
|
+
log(DEBUG, "Started %s scheduler thread.", cmd)
|
|
409
|
+
|
|
410
|
+
state = state_factory.state()
|
|
411
|
+
|
|
412
|
+
# Periodically check for a pending run in the LinkState
|
|
413
|
+
while True:
|
|
414
|
+
sleep(3)
|
|
415
|
+
pending_run_id = state.get_pending_run_id()
|
|
416
|
+
|
|
417
|
+
if pending_run_id:
|
|
418
|
+
|
|
419
|
+
log(
|
|
420
|
+
INFO,
|
|
421
|
+
"Launching %s subprocess. Connects to SuperLink on %s",
|
|
422
|
+
cmd,
|
|
423
|
+
io_api_address,
|
|
424
|
+
)
|
|
425
|
+
# Start subprocess
|
|
426
|
+
command = [
|
|
427
|
+
cmd,
|
|
428
|
+
"--run-once",
|
|
429
|
+
"--superlink",
|
|
430
|
+
io_api_address,
|
|
431
|
+
]
|
|
432
|
+
if ssl_ca_certfile:
|
|
433
|
+
command.append("--root-certificates")
|
|
434
|
+
command.append(ssl_ca_certfile)
|
|
435
|
+
else:
|
|
436
|
+
command.append("--insecure")
|
|
437
|
+
|
|
438
|
+
subprocess.Popen( # pylint: disable=consider-using-with
|
|
439
|
+
command,
|
|
440
|
+
text=True,
|
|
441
|
+
)
|
|
335
442
|
|
|
336
443
|
|
|
337
444
|
def _format_address(address: str) -> tuple[str, str, int]:
|
|
@@ -489,7 +596,7 @@ def _try_obtain_certificates(
|
|
|
489
596
|
|
|
490
597
|
def _run_fleet_api_grpc_rere(
|
|
491
598
|
address: str,
|
|
492
|
-
state_factory:
|
|
599
|
+
state_factory: LinkStateFactory,
|
|
493
600
|
ffs_factory: FfsFactory,
|
|
494
601
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
495
602
|
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
|
|
@@ -517,7 +624,7 @@ def _run_fleet_api_grpc_rere(
|
|
|
517
624
|
|
|
518
625
|
def _run_fleet_api_grpc_adapter(
|
|
519
626
|
address: str,
|
|
520
|
-
state_factory:
|
|
627
|
+
state_factory: LinkStateFactory,
|
|
521
628
|
ffs_factory: FfsFactory,
|
|
522
629
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
523
630
|
) -> grpc.Server:
|
|
@@ -548,11 +655,11 @@ def _run_fleet_api_rest(
|
|
|
548
655
|
port: int,
|
|
549
656
|
ssl_keyfile: Optional[str],
|
|
550
657
|
ssl_certfile: Optional[str],
|
|
551
|
-
state_factory:
|
|
658
|
+
state_factory: LinkStateFactory,
|
|
552
659
|
ffs_factory: FfsFactory,
|
|
553
660
|
num_workers: int,
|
|
554
661
|
) -> None:
|
|
555
|
-
"""Run
|
|
662
|
+
"""Run ServerAppIo API (REST-based)."""
|
|
556
663
|
try:
|
|
557
664
|
import uvicorn
|
|
558
665
|
|
|
@@ -579,14 +686,16 @@ def _run_fleet_api_rest(
|
|
|
579
686
|
|
|
580
687
|
|
|
581
688
|
def _parse_args_run_superlink() -> argparse.ArgumentParser:
|
|
582
|
-
"""Parse command line arguments for both
|
|
689
|
+
"""Parse command line arguments for both ServerAppIo API and Fleet API."""
|
|
583
690
|
parser = argparse.ArgumentParser(
|
|
584
691
|
description="Start a Flower SuperLink",
|
|
585
692
|
)
|
|
586
693
|
|
|
587
694
|
_add_args_common(parser=parser)
|
|
588
|
-
|
|
695
|
+
_add_args_serverappio_api(parser=parser)
|
|
589
696
|
_add_args_fleet_api(parser=parser)
|
|
697
|
+
_add_args_exec_api(parser=parser)
|
|
698
|
+
_add_args_simulationio_api(parser=parser)
|
|
590
699
|
|
|
591
700
|
return parser
|
|
592
701
|
|
|
@@ -599,6 +708,17 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
599
708
|
"paths are provided. By default, the server runs with HTTPS enabled. "
|
|
600
709
|
"Use this flag only if you understand the risks.",
|
|
601
710
|
)
|
|
711
|
+
parser.add_argument(
|
|
712
|
+
"--flwr-dir",
|
|
713
|
+
default=None,
|
|
714
|
+
help="""The path containing installed Flower Apps.
|
|
715
|
+
The default directory is:
|
|
716
|
+
|
|
717
|
+
- `$FLWR_HOME/` if `$FLWR_HOME` is defined
|
|
718
|
+
- `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
|
|
719
|
+
- `$HOME/.flwr/` in all other cases
|
|
720
|
+
""",
|
|
721
|
+
)
|
|
602
722
|
parser.add_argument(
|
|
603
723
|
"--ssl-certfile",
|
|
604
724
|
help="Fleet API server SSL certificate file (as a path str) "
|
|
@@ -618,6 +738,19 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
618
738
|
"to create a secure connection.",
|
|
619
739
|
type=str,
|
|
620
740
|
)
|
|
741
|
+
parser.add_argument(
|
|
742
|
+
"--isolation",
|
|
743
|
+
default=ISOLATION_MODE_SUBPROCESS,
|
|
744
|
+
required=False,
|
|
745
|
+
choices=[
|
|
746
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
747
|
+
ISOLATION_MODE_PROCESS,
|
|
748
|
+
],
|
|
749
|
+
help="Isolation mode when running a `ServerApp` (`subprocess` by default, "
|
|
750
|
+
"possible values: `subprocess`, `process`). Use `subprocess` to configure "
|
|
751
|
+
"SuperLink to run a `ServerApp` in a subprocess. Use `process` to indicate "
|
|
752
|
+
"that a separate independent process gets created outside of SuperLink.",
|
|
753
|
+
)
|
|
621
754
|
parser.add_argument(
|
|
622
755
|
"--database",
|
|
623
756
|
help="A string representing the path to the database "
|
|
@@ -650,11 +783,11 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
650
783
|
)
|
|
651
784
|
|
|
652
785
|
|
|
653
|
-
def
|
|
786
|
+
def _add_args_serverappio_api(parser: argparse.ArgumentParser) -> None:
|
|
654
787
|
parser.add_argument(
|
|
655
|
-
"--
|
|
656
|
-
help="
|
|
657
|
-
default=
|
|
788
|
+
"--serverappio-api-address",
|
|
789
|
+
help="ServerAppIo API (gRPC) server address (IPv4, IPv6, or a domain name).",
|
|
790
|
+
default=SERVERAPPIO_API_DEFAULT_ADDRESS,
|
|
658
791
|
)
|
|
659
792
|
|
|
660
793
|
|
|
@@ -681,3 +814,37 @@ def _add_args_fleet_api(parser: argparse.ArgumentParser) -> None:
|
|
|
681
814
|
type=int,
|
|
682
815
|
help="Set the number of concurrent workers for the Fleet API server.",
|
|
683
816
|
)
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def _add_args_exec_api(parser: argparse.ArgumentParser) -> None:
|
|
820
|
+
"""Add command line arguments for Exec API."""
|
|
821
|
+
parser.add_argument(
|
|
822
|
+
"--exec-api-address",
|
|
823
|
+
help="Exec API server address (IPv4, IPv6, or a domain name)",
|
|
824
|
+
default=EXEC_API_DEFAULT_ADDRESS,
|
|
825
|
+
)
|
|
826
|
+
parser.add_argument(
|
|
827
|
+
"--executor",
|
|
828
|
+
help="For example: `deployment:exec` or `project.package.module:wrapper.exec`. "
|
|
829
|
+
"The default is `flwr.superexec.deployment:executor`",
|
|
830
|
+
default="flwr.superexec.deployment:executor",
|
|
831
|
+
)
|
|
832
|
+
parser.add_argument(
|
|
833
|
+
"--executor-dir",
|
|
834
|
+
help="The directory for the executor.",
|
|
835
|
+
default=".",
|
|
836
|
+
)
|
|
837
|
+
parser.add_argument(
|
|
838
|
+
"--executor-config",
|
|
839
|
+
help="Key-value pairs for the executor config, separated by spaces. "
|
|
840
|
+
"For example:\n\n`--executor-config 'verbose=true "
|
|
841
|
+
'root-certificates="certificates/superlink-ca.crt"\'`',
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
def _add_args_simulationio_api(parser: argparse.ArgumentParser) -> None:
|
|
846
|
+
parser.add_argument(
|
|
847
|
+
"--simulationio-api-address",
|
|
848
|
+
help="SimulationIo API (gRPC) server address (IPv4, IPv6, or a domain name).",
|
|
849
|
+
default=SIMULATIONIO_API_DEFAULT_ADDRESS,
|
|
850
|
+
)
|
flwr/server/driver/driver.py
CHANGED
|
@@ -24,7 +24,21 @@ from flwr.common.typing import Run
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class Driver(ABC):
|
|
27
|
-
"""Abstract base Driver class for the
|
|
27
|
+
"""Abstract base Driver class for the ServerAppIo API."""
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def set_run(self, run_id: int) -> None:
|
|
31
|
+
"""Request a run to the SuperLink with a given `run_id`.
|
|
32
|
+
|
|
33
|
+
If a Run with the specified `run_id` exists, a local Run
|
|
34
|
+
object will be created. It enables further functionality
|
|
35
|
+
in the driver, such as sending `Messages`.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
run_id : int
|
|
40
|
+
The `run_id` of the Run this Driver object operates in.
|
|
41
|
+
"""
|
|
28
42
|
|
|
29
43
|
@property
|
|
30
44
|
@abstractmethod
|