flwr-nightly 1.13.0.dev20241106__py3-none-any.whl → 1.13.0.dev20241117__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.

Files changed (58) hide show
  1. flwr/cli/app.py +2 -0
  2. flwr/cli/build.py +37 -0
  3. flwr/cli/install.py +5 -3
  4. flwr/cli/ls.py +228 -0
  5. flwr/cli/run/run.py +16 -5
  6. flwr/client/app.py +68 -19
  7. flwr/client/clientapp/app.py +51 -35
  8. flwr/client/grpc_rere_client/connection.py +2 -12
  9. flwr/client/nodestate/__init__.py +25 -0
  10. flwr/client/nodestate/in_memory_nodestate.py +38 -0
  11. flwr/client/nodestate/nodestate.py +30 -0
  12. flwr/client/nodestate/nodestate_factory.py +37 -0
  13. flwr/client/rest_client/connection.py +4 -14
  14. flwr/client/supernode/app.py +57 -53
  15. flwr/common/args.py +148 -0
  16. flwr/common/config.py +10 -0
  17. flwr/common/constant.py +21 -7
  18. flwr/common/date.py +18 -0
  19. flwr/common/logger.py +6 -2
  20. flwr/common/object_ref.py +47 -16
  21. flwr/common/serde.py +10 -0
  22. flwr/common/typing.py +32 -11
  23. flwr/proto/exec_pb2.py +23 -17
  24. flwr/proto/exec_pb2.pyi +50 -20
  25. flwr/proto/exec_pb2_grpc.py +34 -0
  26. flwr/proto/exec_pb2_grpc.pyi +13 -0
  27. flwr/proto/run_pb2.py +32 -27
  28. flwr/proto/run_pb2.pyi +44 -1
  29. flwr/proto/simulationio_pb2.py +2 -2
  30. flwr/proto/simulationio_pb2_grpc.py +34 -0
  31. flwr/proto/simulationio_pb2_grpc.pyi +13 -0
  32. flwr/server/app.py +83 -87
  33. flwr/server/driver/driver.py +1 -1
  34. flwr/server/driver/grpc_driver.py +6 -20
  35. flwr/server/driver/inmemory_driver.py +1 -3
  36. flwr/server/run_serverapp.py +8 -238
  37. flwr/server/serverapp/app.py +44 -89
  38. flwr/server/strategy/aggregate.py +4 -4
  39. flwr/server/superlink/fleet/rest_rere/rest_api.py +10 -9
  40. flwr/server/superlink/linkstate/in_memory_linkstate.py +76 -62
  41. flwr/server/superlink/linkstate/linkstate.py +24 -9
  42. flwr/server/superlink/linkstate/sqlite_linkstate.py +87 -128
  43. flwr/server/superlink/linkstate/utils.py +191 -32
  44. flwr/server/superlink/simulation/simulationio_servicer.py +22 -1
  45. flwr/simulation/__init__.py +3 -1
  46. flwr/simulation/app.py +245 -352
  47. flwr/simulation/legacy_app.py +402 -0
  48. flwr/simulation/run_simulation.py +8 -19
  49. flwr/simulation/simulationio_connection.py +2 -2
  50. flwr/superexec/deployment.py +13 -7
  51. flwr/superexec/exec_servicer.py +32 -3
  52. flwr/superexec/executor.py +4 -3
  53. flwr/superexec/simulation.py +52 -145
  54. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241117.dist-info}/METADATA +10 -7
  55. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241117.dist-info}/RECORD +58 -51
  56. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241117.dist-info}/entry_points.txt +1 -0
  57. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241117.dist-info}/LICENSE +0 -0
  58. {flwr_nightly-1.13.0.dev20241106.dist-info → flwr_nightly-1.13.0.dev20241117.dist-info}/WHEEL +0 -0
flwr/proto/run_pb2.pyi CHANGED
@@ -5,6 +5,7 @@ isort:skip_file
5
5
  import builtins
6
6
  import flwr.proto.fab_pb2
7
7
  import flwr.proto.node_pb2
8
+ import flwr.proto.recordset_pb2
8
9
  import flwr.proto.transport_pb2
9
10
  import google.protobuf.descriptor
10
11
  import google.protobuf.internal.containers
@@ -36,12 +37,23 @@ class Run(google.protobuf.message.Message):
36
37
  FAB_VERSION_FIELD_NUMBER: builtins.int
37
38
  OVERRIDE_CONFIG_FIELD_NUMBER: builtins.int
38
39
  FAB_HASH_FIELD_NUMBER: builtins.int
40
+ PENDING_AT_FIELD_NUMBER: builtins.int
41
+ STARTING_AT_FIELD_NUMBER: builtins.int
42
+ RUNNING_AT_FIELD_NUMBER: builtins.int
43
+ FINISHED_AT_FIELD_NUMBER: builtins.int
44
+ STATUS_FIELD_NUMBER: builtins.int
39
45
  run_id: builtins.int
40
46
  fab_id: typing.Text
41
47
  fab_version: typing.Text
42
48
  @property
43
49
  def override_config(self) -> google.protobuf.internal.containers.MessageMap[typing.Text, flwr.proto.transport_pb2.Scalar]: ...
44
50
  fab_hash: typing.Text
51
+ pending_at: typing.Text
52
+ starting_at: typing.Text
53
+ running_at: typing.Text
54
+ finished_at: typing.Text
55
+ @property
56
+ def status(self) -> global___RunStatus: ...
45
57
  def __init__(self,
46
58
  *,
47
59
  run_id: builtins.int = ...,
@@ -49,8 +61,14 @@ class Run(google.protobuf.message.Message):
49
61
  fab_version: typing.Text = ...,
50
62
  override_config: typing.Optional[typing.Mapping[typing.Text, flwr.proto.transport_pb2.Scalar]] = ...,
51
63
  fab_hash: typing.Text = ...,
64
+ pending_at: typing.Text = ...,
65
+ starting_at: typing.Text = ...,
66
+ running_at: typing.Text = ...,
67
+ finished_at: typing.Text = ...,
68
+ status: typing.Optional[global___RunStatus] = ...,
52
69
  ) -> None: ...
53
- def ClearField(self, field_name: typing_extensions.Literal["fab_hash",b"fab_hash","fab_id",b"fab_id","fab_version",b"fab_version","override_config",b"override_config","run_id",b"run_id"]) -> None: ...
70
+ def HasField(self, field_name: typing_extensions.Literal["status",b"status"]) -> builtins.bool: ...
71
+ def ClearField(self, field_name: typing_extensions.Literal["fab_hash",b"fab_hash","fab_id",b"fab_id","fab_version",b"fab_version","finished_at",b"finished_at","override_config",b"override_config","pending_at",b"pending_at","run_id",b"run_id","running_at",b"running_at","starting_at",b"starting_at","status",b"status"]) -> None: ...
54
72
  global___Run = Run
55
73
 
56
74
  class RunStatus(google.protobuf.message.Message):
@@ -223,3 +241,28 @@ class GetRunStatusResponse(google.protobuf.message.Message):
223
241
  ) -> None: ...
224
242
  def ClearField(self, field_name: typing_extensions.Literal["run_status_dict",b"run_status_dict"]) -> None: ...
225
243
  global___GetRunStatusResponse = GetRunStatusResponse
244
+
245
+ class GetFederationOptionsRequest(google.protobuf.message.Message):
246
+ """Get Federation Options associated with run"""
247
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
248
+ RUN_ID_FIELD_NUMBER: builtins.int
249
+ run_id: builtins.int
250
+ def __init__(self,
251
+ *,
252
+ run_id: builtins.int = ...,
253
+ ) -> None: ...
254
+ def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
255
+ global___GetFederationOptionsRequest = GetFederationOptionsRequest
256
+
257
+ class GetFederationOptionsResponse(google.protobuf.message.Message):
258
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
259
+ FEDERATION_OPTIONS_FIELD_NUMBER: builtins.int
260
+ @property
261
+ def federation_options(self) -> flwr.proto.recordset_pb2.ConfigsRecord: ...
262
+ def __init__(self,
263
+ *,
264
+ federation_options: typing.Optional[flwr.proto.recordset_pb2.ConfigsRecord] = ...,
265
+ ) -> None: ...
266
+ def HasField(self, field_name: typing_extensions.Literal["federation_options",b"federation_options"]) -> builtins.bool: ...
267
+ def ClearField(self, field_name: typing_extensions.Literal["federation_options",b"federation_options"]) -> None: ...
268
+ global___GetFederationOptionsResponse = GetFederationOptionsResponse
@@ -18,7 +18,7 @@ from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
18
18
  from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
19
19
 
20
20
 
21
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x66lwr/proto/simulationio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"\x1d\n\x1bPullSimulationInputsRequest\"\x80\x01\n\x1cPullSimulationInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"T\n\x1cPushSimulationOutputsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\"\x1f\n\x1dPushSimulationOutputsResponse2\x92\x03\n\x0cSimulationIo\x12k\n\x14PullSimulationInputs\x12\'.flwr.proto.PullSimulationInputsRequest\x1a(.flwr.proto.PullSimulationInputsResponse\"\x00\x12n\n\x15PushSimulationOutputs\x12(.flwr.proto.PushSimulationOutputsRequest\x1a).flwr.proto.PushSimulationOutputsResponse\"\x00\x12\\\n\x0fUpdateRunStatus\x12\".flwr.proto.UpdateRunStatusRequest\x1a#.flwr.proto.UpdateRunStatusResponse\"\x00\x12G\n\x08PushLogs\x12\x1b.flwr.proto.PushLogsRequest\x1a\x1c.flwr.proto.PushLogsResponse\"\x00\x62\x06proto3')
21
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x66lwr/proto/simulationio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"\x1d\n\x1bPullSimulationInputsRequest\"\x80\x01\n\x1cPullSimulationInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"T\n\x1cPushSimulationOutputsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\"\x1f\n\x1dPushSimulationOutputsResponse2\xff\x03\n\x0cSimulationIo\x12k\n\x14PullSimulationInputs\x12\'.flwr.proto.PullSimulationInputsRequest\x1a(.flwr.proto.PullSimulationInputsResponse\"\x00\x12n\n\x15PushSimulationOutputs\x12(.flwr.proto.PushSimulationOutputsRequest\x1a).flwr.proto.PushSimulationOutputsResponse\"\x00\x12\\\n\x0fUpdateRunStatus\x12\".flwr.proto.UpdateRunStatusRequest\x1a#.flwr.proto.UpdateRunStatusResponse\"\x00\x12G\n\x08PushLogs\x12\x1b.flwr.proto.PushLogsRequest\x1a\x1c.flwr.proto.PushLogsResponse\"\x00\x12k\n\x14GetFederationOptions\x12\'.flwr.proto.GetFederationOptionsRequest\x1a(.flwr.proto.GetFederationOptionsResponse\"\x00\x62\x06proto3')
22
22
 
23
23
  _globals = globals()
24
24
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -34,5 +34,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
34
34
  _globals['_PUSHSIMULATIONOUTPUTSRESPONSE']._serialized_start=385
35
35
  _globals['_PUSHSIMULATIONOUTPUTSRESPONSE']._serialized_end=416
36
36
  _globals['_SIMULATIONIO']._serialized_start=419
37
- _globals['_SIMULATIONIO']._serialized_end=821
37
+ _globals['_SIMULATIONIO']._serialized_end=930
38
38
  # @@protoc_insertion_point(module_scope)
@@ -36,6 +36,11 @@ class SimulationIoStub(object):
36
36
  request_serializer=flwr_dot_proto_dot_log__pb2.PushLogsRequest.SerializeToString,
37
37
  response_deserializer=flwr_dot_proto_dot_log__pb2.PushLogsResponse.FromString,
38
38
  )
39
+ self.GetFederationOptions = channel.unary_unary(
40
+ '/flwr.proto.SimulationIo/GetFederationOptions',
41
+ request_serializer=flwr_dot_proto_dot_run__pb2.GetFederationOptionsRequest.SerializeToString,
42
+ response_deserializer=flwr_dot_proto_dot_run__pb2.GetFederationOptionsResponse.FromString,
43
+ )
39
44
 
40
45
 
41
46
  class SimulationIoServicer(object):
@@ -69,6 +74,13 @@ class SimulationIoServicer(object):
69
74
  context.set_details('Method not implemented!')
70
75
  raise NotImplementedError('Method not implemented!')
71
76
 
77
+ def GetFederationOptions(self, request, context):
78
+ """Get Federation Options
79
+ """
80
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
81
+ context.set_details('Method not implemented!')
82
+ raise NotImplementedError('Method not implemented!')
83
+
72
84
 
73
85
  def add_SimulationIoServicer_to_server(servicer, server):
74
86
  rpc_method_handlers = {
@@ -92,6 +104,11 @@ def add_SimulationIoServicer_to_server(servicer, server):
92
104
  request_deserializer=flwr_dot_proto_dot_log__pb2.PushLogsRequest.FromString,
93
105
  response_serializer=flwr_dot_proto_dot_log__pb2.PushLogsResponse.SerializeToString,
94
106
  ),
107
+ 'GetFederationOptions': grpc.unary_unary_rpc_method_handler(
108
+ servicer.GetFederationOptions,
109
+ request_deserializer=flwr_dot_proto_dot_run__pb2.GetFederationOptionsRequest.FromString,
110
+ response_serializer=flwr_dot_proto_dot_run__pb2.GetFederationOptionsResponse.SerializeToString,
111
+ ),
95
112
  }
96
113
  generic_handler = grpc.method_handlers_generic_handler(
97
114
  'flwr.proto.SimulationIo', rpc_method_handlers)
@@ -169,3 +186,20 @@ class SimulationIo(object):
169
186
  flwr_dot_proto_dot_log__pb2.PushLogsResponse.FromString,
170
187
  options, channel_credentials,
171
188
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
189
+
190
+ @staticmethod
191
+ def GetFederationOptions(request,
192
+ target,
193
+ options=(),
194
+ channel_credentials=None,
195
+ call_credentials=None,
196
+ insecure=False,
197
+ compression=None,
198
+ wait_for_ready=None,
199
+ timeout=None,
200
+ metadata=None):
201
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/GetFederationOptions',
202
+ flwr_dot_proto_dot_run__pb2.GetFederationOptionsRequest.SerializeToString,
203
+ flwr_dot_proto_dot_run__pb2.GetFederationOptionsResponse.FromString,
204
+ options, channel_credentials,
205
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@@ -30,6 +30,11 @@ class SimulationIoStub:
30
30
  flwr.proto.log_pb2.PushLogsResponse]
31
31
  """Push ServerApp logs"""
32
32
 
33
+ GetFederationOptions: grpc.UnaryUnaryMultiCallable[
34
+ flwr.proto.run_pb2.GetFederationOptionsRequest,
35
+ flwr.proto.run_pb2.GetFederationOptionsResponse]
36
+ """Get Federation Options"""
37
+
33
38
 
34
39
  class SimulationIoServicer(metaclass=abc.ABCMeta):
35
40
  @abc.abstractmethod
@@ -64,5 +69,13 @@ class SimulationIoServicer(metaclass=abc.ABCMeta):
64
69
  """Push ServerApp logs"""
65
70
  pass
66
71
 
72
+ @abc.abstractmethod
73
+ def GetFederationOptions(self,
74
+ request: flwr.proto.run_pb2.GetFederationOptionsRequest,
75
+ context: grpc.ServicerContext,
76
+ ) -> flwr.proto.run_pb2.GetFederationOptionsResponse:
77
+ """Get Federation Options"""
78
+ pass
79
+
67
80
 
68
81
  def add_SimulationIoServicer_to_server(servicer: SimulationIoServicer, server: grpc.Server) -> None: ...
flwr/server/app.py CHANGED
@@ -22,7 +22,6 @@ import sys
22
22
  import threading
23
23
  from collections.abc import Sequence
24
24
  from logging import DEBUG, INFO, WARN
25
- from os.path import isfile
26
25
  from pathlib import Path
27
26
  from time import sleep
28
27
  from typing import Optional
@@ -37,23 +36,26 @@ from cryptography.hazmat.primitives.serialization import (
37
36
 
38
37
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
39
38
  from flwr.common.address import parse_address
39
+ from flwr.common.args import try_obtain_server_certificates
40
40
  from flwr.common.config import get_flwr_dir, parse_config_args
41
41
  from flwr.common.constant import (
42
- EXEC_API_DEFAULT_ADDRESS,
42
+ CLIENT_OCTET,
43
+ EXEC_API_DEFAULT_SERVER_ADDRESS,
43
44
  FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
44
45
  FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
45
46
  FLEET_API_REST_DEFAULT_ADDRESS,
46
47
  ISOLATION_MODE_PROCESS,
47
48
  ISOLATION_MODE_SUBPROCESS,
48
49
  MISSING_EXTRA_REST,
49
- SERVERAPPIO_API_DEFAULT_ADDRESS,
50
- SIMULATIONIO_API_DEFAULT_ADDRESS,
50
+ SERVER_OCTET,
51
+ SERVERAPPIO_API_DEFAULT_SERVER_ADDRESS,
52
+ SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS,
51
53
  TRANSPORT_TYPE_GRPC_ADAPTER,
52
54
  TRANSPORT_TYPE_GRPC_RERE,
53
55
  TRANSPORT_TYPE_REST,
54
56
  )
55
57
  from flwr.common.exit_handlers import register_exit_handlers
56
- from flwr.common.logger import log
58
+ from flwr.common.logger import log, warn_deprecated_feature
57
59
  from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
58
60
  private_key_to_bytes,
59
61
  public_key_to_bytes,
@@ -99,6 +101,11 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
99
101
  ) -> History:
100
102
  """Start a Flower server using the gRPC transport layer.
101
103
 
104
+ Warning
105
+ -------
106
+ This function is deprecated since 1.13.0. Use the :code:`flower-superlink` command
107
+ instead to start a SuperLink.
108
+
102
109
  Parameters
103
110
  ----------
104
111
  server_address : Optional[str]
@@ -156,6 +163,17 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
156
163
  >>> )
157
164
  >>> )
158
165
  """
166
+ msg = (
167
+ "flwr.server.start_server() is deprecated."
168
+ "\n\tInstead, use the `flower-superlink` CLI command to start a SuperLink "
169
+ "as shown below:"
170
+ "\n\n\t\t$ flower-superlink --insecure"
171
+ "\n\n\tTo view usage and all available options, run:"
172
+ "\n\n\t\t$ flower-superlink --help"
173
+ "\n\n\tUsing `start_server()` is deprecated."
174
+ )
175
+ warn_deprecated_feature(name=msg)
176
+
159
177
  event(EventType.START_SERVER_ENTER)
160
178
 
161
179
  # Parse IP address
@@ -215,13 +233,19 @@ def run_superlink() -> None:
215
233
 
216
234
  event(EventType.RUN_SUPERLINK_ENTER)
217
235
 
236
+ # Warn unused options
237
+ if args.flwr_dir is not None:
238
+ log(
239
+ WARN, "The `--flwr-dir` option is currently not in use and will be ignored."
240
+ )
241
+
218
242
  # Parse IP addresses
219
243
  serverappio_address, _, _ = _format_address(args.serverappio_api_address)
220
244
  exec_address, _, _ = _format_address(args.exec_api_address)
221
245
  simulationio_address, _, _ = _format_address(args.simulationio_api_address)
222
246
 
223
247
  # Obtain certificates
224
- certificates = _try_obtain_certificates(args)
248
+ certificates = try_obtain_server_certificates(args, args.fleet_api_type)
225
249
 
226
250
  # Initialize StateFactory
227
251
  state_factory = LinkStateFactory(args.database)
@@ -359,18 +383,27 @@ def run_superlink() -> None:
359
383
  else:
360
384
  raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
361
385
 
362
- if args.isolation == ISOLATION_MODE_SUBPROCESS:
363
- # Scheduler thread
364
- scheduler_th = threading.Thread(
365
- target=_flwr_serverapp_scheduler,
366
- args=(
367
- state_factory,
368
- args.serverappio_api_address,
369
- args.ssl_ca_certfile,
370
- ),
371
- )
372
- scheduler_th.start()
373
- bckg_threads.append(scheduler_th)
386
+ if args.isolation == ISOLATION_MODE_SUBPROCESS:
387
+
388
+ _octet, _colon, _port = serverappio_address.rpartition(":")
389
+ io_address = (
390
+ f"{CLIENT_OCTET}:{_port}" if _octet == SERVER_OCTET else serverappio_address
391
+ )
392
+ address = simulationio_address if sim_exec else io_address
393
+ cmd = "flwr-simulation" if sim_exec else "flwr-serverapp"
394
+
395
+ # Scheduler thread
396
+ scheduler_th = threading.Thread(
397
+ target=_flwr_scheduler,
398
+ args=(
399
+ state_factory,
400
+ address,
401
+ args.ssl_ca_certfile,
402
+ cmd,
403
+ ),
404
+ )
405
+ scheduler_th.start()
406
+ bckg_threads.append(scheduler_th)
374
407
 
375
408
  # Graceful shutdown
376
409
  register_exit_handlers(
@@ -388,12 +421,13 @@ def run_superlink() -> None:
388
421
  exec_server.wait_for_termination(timeout=1)
389
422
 
390
423
 
391
- def _flwr_serverapp_scheduler(
424
+ def _flwr_scheduler(
392
425
  state_factory: LinkStateFactory,
393
- serverappio_api_address: str,
426
+ io_api_address: str,
394
427
  ssl_ca_certfile: Optional[str],
428
+ cmd: str,
395
429
  ) -> None:
396
- log(DEBUG, "Started flwr-serverapp scheduler thread.")
430
+ log(DEBUG, "Started %s scheduler thread.", cmd)
397
431
 
398
432
  state = state_factory.state()
399
433
 
@@ -406,14 +440,16 @@ def _flwr_serverapp_scheduler(
406
440
 
407
441
  log(
408
442
  INFO,
409
- "Launching `flwr-serverapp` subprocess. Connects to SuperLink on %s",
410
- serverappio_api_address,
443
+ "Launching %s subprocess. Connects to SuperLink on %s",
444
+ cmd,
445
+ io_api_address,
411
446
  )
412
- # Start ServerApp subprocess
447
+ # Start subprocess
413
448
  command = [
414
- "flwr-serverapp",
415
- "--superlink",
416
- serverappio_api_address,
449
+ cmd,
450
+ "--run-once",
451
+ "--serverappio-api-address",
452
+ io_api_address,
417
453
  ]
418
454
  if ssl_ca_certfile:
419
455
  command.append("--root-certificates")
@@ -526,60 +562,6 @@ def _try_setup_node_authentication(
526
562
  )
527
563
 
528
564
 
529
- def _try_obtain_certificates(
530
- args: argparse.Namespace,
531
- ) -> Optional[tuple[bytes, bytes, bytes]]:
532
- # Obtain certificates
533
- if args.insecure:
534
- log(WARN, "Option `--insecure` was set. Starting insecure HTTP server.")
535
- return None
536
- # Check if certificates are provided
537
- if args.fleet_api_type in [TRANSPORT_TYPE_GRPC_RERE, TRANSPORT_TYPE_GRPC_ADAPTER]:
538
- if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
539
- if not isfile(args.ssl_ca_certfile):
540
- sys.exit("Path argument `--ssl-ca-certfile` does not point to a file.")
541
- if not isfile(args.ssl_certfile):
542
- sys.exit("Path argument `--ssl-certfile` does not point to a file.")
543
- if not isfile(args.ssl_keyfile):
544
- sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
545
- certificates = (
546
- Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
547
- Path(args.ssl_certfile).read_bytes(), # server certificate
548
- Path(args.ssl_keyfile).read_bytes(), # server private key
549
- )
550
- return certificates
551
- if args.ssl_certfile or args.ssl_keyfile or args.ssl_ca_certfile:
552
- sys.exit(
553
- "You need to provide valid file paths to `--ssl-certfile`, "
554
- "`--ssl-keyfile`, and `—-ssl-ca-certfile` to create a secure "
555
- "connection in Fleet API server (gRPC-rere)."
556
- )
557
- if args.fleet_api_type == TRANSPORT_TYPE_REST:
558
- if args.ssl_certfile and args.ssl_keyfile:
559
- if not isfile(args.ssl_certfile):
560
- sys.exit("Path argument `--ssl-certfile` does not point to a file.")
561
- if not isfile(args.ssl_keyfile):
562
- sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
563
- certificates = (
564
- b"",
565
- Path(args.ssl_certfile).read_bytes(), # server certificate
566
- Path(args.ssl_keyfile).read_bytes(), # server private key
567
- )
568
- return certificates
569
- if args.ssl_certfile or args.ssl_keyfile:
570
- sys.exit(
571
- "You need to provide valid file paths to `--ssl-certfile` "
572
- "and `--ssl-keyfile` to create a secure connection "
573
- "in Fleet API server (REST, experimental)."
574
- )
575
- sys.exit(
576
- "Certificates are required unless running in insecure mode. "
577
- "Please provide certificate paths to `--ssl-certfile`, "
578
- "`--ssl-keyfile`, and `—-ssl-ca-certfile` or run the server "
579
- "in insecure mode using '--insecure' if you understand the risks."
580
- )
581
-
582
-
583
565
  def _run_fleet_api_grpc_rere(
584
566
  address: str,
585
567
  state_factory: LinkStateFactory,
@@ -694,6 +676,17 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
694
676
  "paths are provided. By default, the server runs with HTTPS enabled. "
695
677
  "Use this flag only if you understand the risks.",
696
678
  )
679
+ parser.add_argument(
680
+ "--flwr-dir",
681
+ default=None,
682
+ help="""The path containing installed Flower Apps.
683
+ The default directory is:
684
+
685
+ - `$FLWR_HOME/` if `$FLWR_HOME` is defined
686
+ - `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
687
+ - `$HOME/.flwr/` in all other cases
688
+ """,
689
+ )
697
690
  parser.add_argument(
698
691
  "--ssl-certfile",
699
692
  help="Fleet API server SSL certificate file (as a path str) "
@@ -761,8 +754,9 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
761
754
  def _add_args_serverappio_api(parser: argparse.ArgumentParser) -> None:
762
755
  parser.add_argument(
763
756
  "--serverappio-api-address",
764
- help="ServerAppIo API (gRPC) server address (IPv4, IPv6, or a domain name).",
765
- default=SERVERAPPIO_API_DEFAULT_ADDRESS,
757
+ default=SERVERAPPIO_API_DEFAULT_SERVER_ADDRESS,
758
+ help="ServerAppIo API (gRPC) server address (IPv4, IPv6, or a domain name). "
759
+ f"By default, it is set to {SERVERAPPIO_API_DEFAULT_SERVER_ADDRESS}.",
766
760
  )
767
761
 
768
762
 
@@ -795,8 +789,9 @@ def _add_args_exec_api(parser: argparse.ArgumentParser) -> None:
795
789
  """Add command line arguments for Exec API."""
796
790
  parser.add_argument(
797
791
  "--exec-api-address",
798
- help="Exec API server address (IPv4, IPv6, or a domain name)",
799
- default=EXEC_API_DEFAULT_ADDRESS,
792
+ help="Exec API server address (IPv4, IPv6, or a domain name) "
793
+ f"By default, it is set to {EXEC_API_DEFAULT_SERVER_ADDRESS}.",
794
+ default=EXEC_API_DEFAULT_SERVER_ADDRESS,
800
795
  )
801
796
  parser.add_argument(
802
797
  "--executor",
@@ -820,6 +815,7 @@ def _add_args_exec_api(parser: argparse.ArgumentParser) -> None:
820
815
  def _add_args_simulationio_api(parser: argparse.ArgumentParser) -> None:
821
816
  parser.add_argument(
822
817
  "--simulationio-api-address",
823
- help="SimulationIo API (gRPC) server address (IPv4, IPv6, or a domain name).",
824
- default=SIMULATIONIO_API_DEFAULT_ADDRESS,
818
+ default=SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS,
819
+ help="SimulationIo API (gRPC) server address (IPv4, IPv6, or a domain name)."
820
+ f"By default, it is set to {SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS}.",
825
821
  )
@@ -27,7 +27,7 @@ class Driver(ABC):
27
27
  """Abstract base Driver class for the ServerAppIo API."""
28
28
 
29
29
  @abstractmethod
30
- def init_run(self, run_id: int) -> None:
30
+ def set_run(self, run_id: int) -> None:
31
31
  """Request a run to the SuperLink with a given `run_id`.
32
32
 
33
33
  If a Run with the specified `run_id` exists, a local Run
@@ -23,14 +23,10 @@ from typing import Optional, cast
23
23
  import grpc
24
24
 
25
25
  from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
26
- from flwr.common.constant import SERVERAPPIO_API_DEFAULT_ADDRESS
26
+ from flwr.common.constant import SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS
27
27
  from flwr.common.grpc import create_channel
28
28
  from flwr.common.logger import log
29
- from flwr.common.serde import (
30
- message_from_taskres,
31
- message_to_taskins,
32
- user_config_from_proto,
33
- )
29
+ from flwr.common.serde import message_from_taskres, message_to_taskins, run_from_proto
34
30
  from flwr.common.typing import Run
35
31
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
36
32
  from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
@@ -70,7 +66,7 @@ class GrpcDriver(Driver):
70
66
 
71
67
  def __init__( # pylint: disable=too-many-arguments
72
68
  self,
73
- serverappio_service_address: str = SERVERAPPIO_API_DEFAULT_ADDRESS,
69
+ serverappio_service_address: str = SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
74
70
  root_certificates: Optional[bytes] = None,
75
71
  ) -> None:
76
72
  self._addr = serverappio_service_address
@@ -112,24 +108,14 @@ class GrpcDriver(Driver):
112
108
  channel.close()
113
109
  log(DEBUG, "[Driver] Disconnected")
114
110
 
115
- def init_run(self, run_id: int) -> None:
116
- """Initialize the run."""
117
- # Check if is initialized
118
- if self._run is not None:
119
- return
120
-
111
+ def set_run(self, run_id: int) -> None:
112
+ """Set the run."""
121
113
  # Get the run info
122
114
  req = GetRunRequest(run_id=run_id)
123
115
  res: GetRunResponse = self._stub.GetRun(req)
124
116
  if not res.HasField("run"):
125
117
  raise RuntimeError(f"Cannot find the run with ID: {run_id}")
126
- self._run = Run(
127
- run_id=res.run.run_id,
128
- fab_id=res.run.fab_id,
129
- fab_version=res.run.fab_version,
130
- fab_hash=res.run.fab_hash,
131
- override_config=user_config_from_proto(res.run.override_config),
132
- )
118
+ self._run = run_from_proto(res.run)
133
119
 
134
120
  @property
135
121
  def run(self) -> Run:
@@ -62,10 +62,8 @@ class InMemoryDriver(Driver):
62
62
  ):
63
63
  raise ValueError(f"Invalid message: {message}")
64
64
 
65
- def init_run(self, run_id: int) -> None:
65
+ def set_run(self, run_id: int) -> None:
66
66
  """Initialize the run."""
67
- if self._run is not None:
68
- return
69
67
  run = self.state.get_run(run_id)
70
68
  if run is None:
71
69
  raise RuntimeError(f"Cannot find the run with ID: {run_id}")