flwr 1.13.1__py3-none-any.whl → 1.15.0__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.
- flwr/cli/app.py +5 -0
- flwr/cli/auth_plugin/__init__.py +31 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +150 -0
- flwr/cli/build.py +1 -0
- flwr/cli/cli_user_auth_interceptor.py +90 -0
- flwr/cli/config_utils.py +43 -149
- flwr/cli/constant.py +27 -0
- flwr/cli/example.py +1 -0
- flwr/cli/install.py +2 -1
- flwr/cli/log.py +34 -37
- flwr/cli/login/__init__.py +22 -0
- flwr/cli/login/login.py +116 -0
- flwr/cli/ls.py +214 -106
- flwr/cli/new/__init__.py +1 -0
- flwr/cli/new/new.py +2 -1
- flwr/cli/new/templates/app/.gitignore.tpl +3 -0
- flwr/cli/new/templates/app/README.md.tpl +3 -2
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +3 -4
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
- flwr/cli/run/__init__.py +1 -0
- flwr/cli/run/run.py +103 -43
- flwr/cli/stop.py +139 -0
- flwr/cli/utils.py +186 -8
- flwr/client/app.py +49 -50
- flwr/client/client.py +1 -32
- flwr/client/clientapp/app.py +23 -26
- flwr/client/clientapp/utils.py +2 -1
- flwr/client/grpc_adapter_client/connection.py +1 -1
- flwr/client/grpc_client/connection.py +2 -13
- flwr/client/grpc_rere_client/client_interceptor.py +19 -119
- flwr/client/grpc_rere_client/connection.py +59 -43
- flwr/client/grpc_rere_client/grpc_adapter.py +12 -12
- flwr/client/message_handler/message_handler.py +1 -2
- flwr/client/message_handler/task_handler.py +0 -17
- flwr/client/mod/comms_mods.py +1 -0
- flwr/client/mod/localdp_mod.py +1 -1
- flwr/client/nodestate/__init__.py +1 -0
- flwr/client/nodestate/nodestate.py +1 -0
- flwr/client/nodestate/nodestate_factory.py +1 -0
- flwr/client/numpy_client.py +0 -44
- flwr/client/rest_client/connection.py +37 -29
- flwr/client/supernode/app.py +20 -74
- flwr/common/address.py +1 -0
- flwr/common/args.py +26 -47
- flwr/common/auth_plugin/__init__.py +24 -0
- flwr/common/auth_plugin/auth_plugin.py +122 -0
- flwr/common/config.py +169 -17
- flwr/common/constant.py +38 -9
- flwr/common/differential_privacy.py +2 -1
- flwr/common/exit/__init__.py +24 -0
- flwr/common/exit/exit.py +99 -0
- flwr/common/exit/exit_code.py +93 -0
- flwr/common/exit_handlers.py +24 -10
- flwr/common/grpc.py +167 -4
- flwr/common/logger.py +66 -7
- flwr/common/message.py +1 -0
- flwr/common/object_ref.py +57 -54
- flwr/common/pyproject.py +1 -0
- flwr/common/record/__init__.py +1 -0
- flwr/common/record/parametersrecord.py +1 -0
- flwr/common/record/recordset.py +1 -1
- flwr/common/retry_invoker.py +77 -0
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +45 -0
- flwr/common/secure_aggregation/secaggplus_utils.py +2 -2
- flwr/common/serde.py +6 -4
- flwr/common/telemetry.py +15 -4
- flwr/common/typing.py +32 -0
- flwr/common/version.py +1 -0
- flwr/proto/clientappio_pb2.py +1 -1
- flwr/proto/error_pb2.py +1 -1
- flwr/proto/exec_pb2.py +27 -15
- flwr/proto/exec_pb2.pyi +80 -2
- flwr/proto/exec_pb2_grpc.py +102 -0
- flwr/proto/exec_pb2_grpc.pyi +39 -0
- flwr/proto/fab_pb2.py +5 -5
- flwr/proto/fab_pb2.pyi +4 -1
- flwr/proto/fleet_pb2.py +31 -31
- flwr/proto/fleet_pb2.pyi +23 -23
- flwr/proto/fleet_pb2_grpc.py +30 -30
- flwr/proto/fleet_pb2_grpc.pyi +20 -20
- flwr/proto/grpcadapter_pb2.py +1 -1
- flwr/proto/log_pb2.py +1 -1
- flwr/proto/message_pb2.py +1 -1
- flwr/proto/node_pb2.py +3 -3
- flwr/proto/node_pb2.pyi +1 -4
- flwr/proto/recordset_pb2.py +1 -1
- flwr/proto/run_pb2.py +1 -1
- flwr/proto/serverappio_pb2.py +24 -25
- flwr/proto/serverappio_pb2.pyi +32 -32
- flwr/proto/serverappio_pb2_grpc.py +62 -28
- flwr/proto/serverappio_pb2_grpc.pyi +29 -16
- flwr/proto/simulationio_pb2.py +3 -3
- flwr/proto/simulationio_pb2_grpc.py +34 -0
- flwr/proto/simulationio_pb2_grpc.pyi +13 -0
- flwr/proto/task_pb2.py +1 -1
- flwr/proto/transport_pb2.py +1 -1
- flwr/server/app.py +152 -112
- flwr/server/compat/app_utils.py +7 -2
- flwr/server/compat/driver_client_proxy.py +1 -2
- flwr/server/driver/grpc_driver.py +38 -85
- flwr/server/driver/inmemory_driver.py +7 -2
- flwr/server/run_serverapp.py +8 -9
- flwr/server/serverapp/app.py +37 -13
- flwr/server/strategy/dpfedavg_fixed.py +1 -0
- flwr/server/superlink/driver/serverappio_grpc.py +2 -1
- flwr/server/superlink/driver/serverappio_servicer.py +148 -63
- flwr/server/superlink/ffs/disk_ffs.py +1 -0
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -87
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -0
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -165
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +56 -35
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +99 -169
- flwr/server/superlink/fleet/message_handler/message_handler.py +69 -29
- flwr/server/superlink/fleet/rest_rere/rest_api.py +20 -19
- flwr/server/superlink/fleet/vce/__init__.py +1 -0
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -0
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -0
- flwr/server/superlink/fleet/vce/vce_api.py +2 -2
- flwr/server/superlink/linkstate/in_memory_linkstate.py +60 -99
- flwr/server/superlink/linkstate/linkstate.py +30 -36
- flwr/server/superlink/linkstate/sqlite_linkstate.py +105 -188
- flwr/server/superlink/linkstate/utils.py +18 -8
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +33 -0
- flwr/server/superlink/utils.py +65 -0
- flwr/server/utils/validator.py +9 -34
- flwr/simulation/app.py +20 -10
- flwr/simulation/legacy_app.py +4 -2
- flwr/simulation/ray_transport/ray_actor.py +1 -0
- flwr/simulation/ray_transport/utils.py +1 -0
- flwr/simulation/run_simulation.py +36 -22
- flwr/simulation/simulationio_connection.py +5 -1
- flwr/superexec/app.py +1 -0
- flwr/superexec/deployment.py +1 -0
- flwr/superexec/exec_grpc.py +20 -2
- flwr/superexec/exec_servicer.py +97 -2
- flwr/superexec/exec_user_auth_interceptor.py +101 -0
- flwr/superexec/executor.py +1 -0
- {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/METADATA +14 -13
- {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/RECORD +150 -144
- flwr/proto/common_pb2.py +0 -36
- flwr/proto/common_pb2.pyi +0 -121
- flwr/proto/common_pb2_grpc.py +0 -4
- flwr/proto/common_pb2_grpc.pyi +0 -4
- flwr/proto/control_pb2.py +0 -27
- flwr/proto/control_pb2.pyi +0 -7
- flwr/proto/control_pb2_grpc.py +0 -135
- flwr/proto/control_pb2_grpc.pyi +0 -53
- {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/LICENSE +0 -0
- {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/WHEEL +0 -0
- {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/entry_points.txt +0 -0
|
@@ -14,38 +14,42 @@
|
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""Flower gRPC Driver."""
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
import time
|
|
18
19
|
import warnings
|
|
19
20
|
from collections.abc import Iterable
|
|
20
|
-
from logging import DEBUG,
|
|
21
|
-
from typing import
|
|
21
|
+
from logging import DEBUG, WARNING
|
|
22
|
+
from typing import Optional, cast
|
|
22
23
|
|
|
23
24
|
import grpc
|
|
24
25
|
|
|
25
26
|
from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
|
|
26
|
-
from flwr.common.constant import
|
|
27
|
-
|
|
27
|
+
from flwr.common.constant import (
|
|
28
|
+
SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
|
|
29
|
+
SUPERLINK_NODE_ID,
|
|
30
|
+
)
|
|
31
|
+
from flwr.common.grpc import create_channel, on_channel_state_change
|
|
28
32
|
from flwr.common.logger import log
|
|
29
|
-
from flwr.common.retry_invoker import
|
|
30
|
-
from flwr.common.serde import
|
|
33
|
+
from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
|
|
34
|
+
from flwr.common.serde import message_from_proto, message_to_proto, run_from_proto
|
|
31
35
|
from flwr.common.typing import Run
|
|
36
|
+
from flwr.proto.message_pb2 import Message as ProtoMessage # pylint: disable=E0611
|
|
32
37
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
33
38
|
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
|
34
39
|
from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
|
|
35
40
|
GetNodesRequest,
|
|
36
41
|
GetNodesResponse,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
PullResMessagesRequest,
|
|
43
|
+
PullResMessagesResponse,
|
|
44
|
+
PushInsMessagesRequest,
|
|
45
|
+
PushInsMessagesResponse,
|
|
41
46
|
)
|
|
42
47
|
from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub # pylint: disable=E0611
|
|
43
|
-
from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
|
|
44
48
|
|
|
45
49
|
from .driver import Driver
|
|
46
50
|
|
|
47
51
|
ERROR_MESSAGE_DRIVER_NOT_CONNECTED = """
|
|
48
|
-
[
|
|
52
|
+
[flwr-serverapp] Error: Not connected.
|
|
49
53
|
|
|
50
54
|
Call `connect()` on the `GrpcDriverStub` instance before calling any of the other
|
|
51
55
|
`GrpcDriverStub` methods.
|
|
@@ -75,7 +79,7 @@ class GrpcDriver(Driver):
|
|
|
75
79
|
self._run: Optional[Run] = None
|
|
76
80
|
self._grpc_stub: Optional[ServerAppIoStub] = None
|
|
77
81
|
self._channel: Optional[grpc.Channel] = None
|
|
78
|
-
self.node = Node(node_id=
|
|
82
|
+
self.node = Node(node_id=SUPERLINK_NODE_ID)
|
|
79
83
|
self._retry_invoker = _make_simple_grpc_retry_invoker()
|
|
80
84
|
|
|
81
85
|
@property
|
|
@@ -96,9 +100,10 @@ class GrpcDriver(Driver):
|
|
|
96
100
|
insecure=(self._cert is None),
|
|
97
101
|
root_certificates=self._cert,
|
|
98
102
|
)
|
|
103
|
+
self._channel.subscribe(on_channel_state_change)
|
|
99
104
|
self._grpc_stub = ServerAppIoStub(self._channel)
|
|
100
105
|
_wrap_stub(self._grpc_stub, self._retry_invoker)
|
|
101
|
-
log(DEBUG, "[
|
|
106
|
+
log(DEBUG, "[flwr-serverapp] Connected to %s", self._addr)
|
|
102
107
|
|
|
103
108
|
def _disconnect(self) -> None:
|
|
104
109
|
"""Disconnect from the ServerAppIo API."""
|
|
@@ -109,7 +114,7 @@ class GrpcDriver(Driver):
|
|
|
109
114
|
self._channel = None
|
|
110
115
|
self._grpc_stub = None
|
|
111
116
|
channel.close()
|
|
112
|
-
log(DEBUG, "[
|
|
117
|
+
log(DEBUG, "[flwr-serverapp] Disconnected")
|
|
113
118
|
|
|
114
119
|
def set_run(self, run_id: int) -> None:
|
|
115
120
|
"""Set the run."""
|
|
@@ -192,20 +197,22 @@ class GrpcDriver(Driver):
|
|
|
192
197
|
This method takes an iterable of messages and sends each message
|
|
193
198
|
to the node specified in `dst_node_id`.
|
|
194
199
|
"""
|
|
195
|
-
# Construct
|
|
196
|
-
|
|
200
|
+
# Construct Messages
|
|
201
|
+
message_proto_list: list[ProtoMessage] = []
|
|
197
202
|
for msg in messages:
|
|
198
203
|
# Check message
|
|
199
204
|
self._check_message(msg)
|
|
200
|
-
# Convert
|
|
201
|
-
|
|
205
|
+
# Convert to proto
|
|
206
|
+
msg_proto = message_to_proto(msg)
|
|
202
207
|
# Add to list
|
|
203
|
-
|
|
208
|
+
message_proto_list.append(msg_proto)
|
|
204
209
|
# Call GrpcDriverStub method
|
|
205
|
-
res:
|
|
206
|
-
|
|
210
|
+
res: PushInsMessagesResponse = self._stub.PushMessages(
|
|
211
|
+
PushInsMessagesRequest(
|
|
212
|
+
messages_list=message_proto_list, run_id=cast(Run, self._run).run_id
|
|
213
|
+
)
|
|
207
214
|
)
|
|
208
|
-
return list(res.
|
|
215
|
+
return list(res.message_ids)
|
|
209
216
|
|
|
210
217
|
def pull_messages(self, message_ids: Iterable[str]) -> Iterable[Message]:
|
|
211
218
|
"""Pull messages based on message IDs.
|
|
@@ -213,12 +220,15 @@ class GrpcDriver(Driver):
|
|
|
213
220
|
This method is used to collect messages from the SuperLink that correspond to a
|
|
214
221
|
set of given message IDs.
|
|
215
222
|
"""
|
|
216
|
-
# Pull
|
|
217
|
-
res:
|
|
218
|
-
|
|
223
|
+
# Pull Messages
|
|
224
|
+
res: PullResMessagesResponse = self._stub.PullMessages(
|
|
225
|
+
PullResMessagesRequest(
|
|
226
|
+
message_ids=message_ids,
|
|
227
|
+
run_id=cast(Run, self._run).run_id,
|
|
228
|
+
)
|
|
219
229
|
)
|
|
220
|
-
# Convert
|
|
221
|
-
msgs = [
|
|
230
|
+
# Convert Message from Protobuf representation
|
|
231
|
+
msgs = [message_from_proto(msg_proto) for msg_proto in res.messages_list]
|
|
222
232
|
return msgs
|
|
223
233
|
|
|
224
234
|
def send_and_receive(
|
|
@@ -258,60 +268,3 @@ class GrpcDriver(Driver):
|
|
|
258
268
|
return
|
|
259
269
|
# Disconnect
|
|
260
270
|
self._disconnect()
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
264
|
-
"""Create a simple gRPC retry invoker."""
|
|
265
|
-
|
|
266
|
-
def _on_sucess(retry_state: RetryState) -> None:
|
|
267
|
-
if retry_state.tries > 1:
|
|
268
|
-
log(
|
|
269
|
-
INFO,
|
|
270
|
-
"Connection successful after %.2f seconds and %s tries.",
|
|
271
|
-
retry_state.elapsed_time,
|
|
272
|
-
retry_state.tries,
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
def _on_backoff(retry_state: RetryState) -> None:
|
|
276
|
-
if retry_state.tries == 1:
|
|
277
|
-
log(WARN, "Connection attempt failed, retrying...")
|
|
278
|
-
else:
|
|
279
|
-
log(
|
|
280
|
-
WARN,
|
|
281
|
-
"Connection attempt failed, retrying in %.2f seconds",
|
|
282
|
-
retry_state.actual_wait,
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
def _on_giveup(retry_state: RetryState) -> None:
|
|
286
|
-
if retry_state.tries > 1:
|
|
287
|
-
log(
|
|
288
|
-
WARN,
|
|
289
|
-
"Giving up reconnection after %.2f seconds and %s tries.",
|
|
290
|
-
retry_state.elapsed_time,
|
|
291
|
-
retry_state.tries,
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
return RetryInvoker(
|
|
295
|
-
wait_gen_factory=lambda: exponential(max_delay=MAX_RETRY_DELAY),
|
|
296
|
-
recoverable_exceptions=grpc.RpcError,
|
|
297
|
-
max_tries=None,
|
|
298
|
-
max_time=None,
|
|
299
|
-
on_success=_on_sucess,
|
|
300
|
-
on_backoff=_on_backoff,
|
|
301
|
-
on_giveup=_on_giveup,
|
|
302
|
-
should_giveup=lambda e: e.code() != grpc.StatusCode.UNAVAILABLE, # type: ignore
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def _wrap_stub(stub: ServerAppIoStub, retry_invoker: RetryInvoker) -> None:
|
|
307
|
-
"""Wrap the gRPC stub with a retry invoker."""
|
|
308
|
-
|
|
309
|
-
def make_lambda(original_method: Any) -> Any:
|
|
310
|
-
return lambda *args, **kwargs: retry_invoker.invoke(
|
|
311
|
-
original_method, *args, **kwargs
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
for method_name in vars(stub):
|
|
315
|
-
method = getattr(stub, method_name)
|
|
316
|
-
if callable(method):
|
|
317
|
-
setattr(stub, method_name, make_lambda(method))
|
|
@@ -22,6 +22,7 @@ from typing import Optional, cast
|
|
|
22
22
|
from uuid import UUID
|
|
23
23
|
|
|
24
24
|
from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
|
|
25
|
+
from flwr.common.constant import SUPERLINK_NODE_ID
|
|
25
26
|
from flwr.common.serde import message_from_taskres, message_to_taskins
|
|
26
27
|
from flwr.common.typing import Run
|
|
27
28
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
@@ -49,7 +50,7 @@ class InMemoryDriver(Driver):
|
|
|
49
50
|
self._run: Optional[Run] = None
|
|
50
51
|
self.state = state_factory.state()
|
|
51
52
|
self.pull_interval = pull_interval
|
|
52
|
-
self.node = Node(node_id=
|
|
53
|
+
self.node = Node(node_id=SUPERLINK_NODE_ID)
|
|
53
54
|
|
|
54
55
|
def _check_message(self, message: Message) -> None:
|
|
55
56
|
# Check if the message is valid
|
|
@@ -142,7 +143,11 @@ class InMemoryDriver(Driver):
|
|
|
142
143
|
# Pull TaskRes
|
|
143
144
|
task_res_list = self.state.get_task_res(task_ids=msg_ids)
|
|
144
145
|
# Delete tasks in state
|
|
145
|
-
|
|
146
|
+
# Delete the TaskIns/TaskRes pairs if TaskRes is found
|
|
147
|
+
task_ins_ids_to_delete = {
|
|
148
|
+
UUID(task_res.task.ancestry[0]) for task_res in task_res_list
|
|
149
|
+
}
|
|
150
|
+
self.state.delete_tasks(task_ins_ids=task_ins_ids_to_delete)
|
|
146
151
|
# Convert TaskRes to Message
|
|
147
152
|
msgs = [message_from_taskres(taskres) for taskres in task_res_list]
|
|
148
153
|
return msgs
|
flwr/server/run_serverapp.py
CHANGED
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"""Run ServerApp."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
import sys
|
|
19
18
|
from logging import DEBUG, ERROR
|
|
20
19
|
from typing import Optional
|
|
21
20
|
|
|
22
|
-
from flwr.common import Context
|
|
23
|
-
from flwr.common.
|
|
21
|
+
from flwr.common import Context, EventType, event
|
|
22
|
+
from flwr.common.exit_handlers import register_exit_handlers
|
|
23
|
+
from flwr.common.logger import log
|
|
24
24
|
from flwr.common.object_ref import load_app
|
|
25
25
|
|
|
26
26
|
from .driver import Driver
|
|
@@ -66,12 +66,11 @@ def run(
|
|
|
66
66
|
return context
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
# pylint: disable-next=too-many-branches,too-many-statements,too-many-locals
|
|
70
69
|
def run_server_app() -> None:
|
|
71
70
|
"""Run Flower server app."""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
event(EventType.RUN_SERVER_APP_ENTER)
|
|
72
|
+
log(
|
|
73
|
+
ERROR,
|
|
74
|
+
"The command `flower-server-app` has been replaced by `flwr run`.",
|
|
75
75
|
)
|
|
76
|
-
|
|
77
|
-
sys.exit()
|
|
76
|
+
register_exit_handlers(event_type=EventType.RUN_SERVER_APP_LEAVE)
|
flwr/server/serverapp/app.py
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""Flower ServerApp process."""
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
import argparse
|
|
18
|
-
import sys
|
|
19
19
|
from logging import DEBUG, ERROR, INFO
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from queue import Queue
|
|
@@ -24,6 +24,7 @@ from typing import Optional
|
|
|
24
24
|
|
|
25
25
|
from flwr.cli.config_utils import get_fab_metadata
|
|
26
26
|
from flwr.cli.install import install_from_fab
|
|
27
|
+
from flwr.cli.utils import get_sha256_hash
|
|
27
28
|
from flwr.common.args import add_args_flwr_app_common
|
|
28
29
|
from flwr.common.config import (
|
|
29
30
|
get_flwr_dir,
|
|
@@ -36,6 +37,7 @@ from flwr.common.constant import (
|
|
|
36
37
|
Status,
|
|
37
38
|
SubStatus,
|
|
38
39
|
)
|
|
40
|
+
from flwr.common.exit import ExitCode, flwr_exit
|
|
39
41
|
from flwr.common.logger import (
|
|
40
42
|
log,
|
|
41
43
|
mirror_output_to_queue,
|
|
@@ -50,7 +52,8 @@ from flwr.common.serde import (
|
|
|
50
52
|
run_from_proto,
|
|
51
53
|
run_status_to_proto,
|
|
52
54
|
)
|
|
53
|
-
from flwr.common.
|
|
55
|
+
from flwr.common.telemetry import EventType, event
|
|
56
|
+
from flwr.common.typing import RunNotRunningException, RunStatus
|
|
54
57
|
from flwr.proto.run_pb2 import UpdateRunStatusRequest # pylint: disable=E0611
|
|
55
58
|
from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
|
|
56
59
|
PullServerAppInputsRequest,
|
|
@@ -69,19 +72,18 @@ def flwr_serverapp() -> None:
|
|
|
69
72
|
|
|
70
73
|
args = _parse_args_run_flwr_serverapp().parse_args()
|
|
71
74
|
|
|
72
|
-
log(INFO, "
|
|
75
|
+
log(INFO, "Start `flwr-serverapp` process")
|
|
73
76
|
|
|
74
77
|
if not args.insecure:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"`flwr-serverapp` does not support TLS yet.
|
|
78
|
-
"Please use the '--insecure' flag.",
|
|
78
|
+
flwr_exit(
|
|
79
|
+
ExitCode.COMMON_TLS_NOT_SUPPORTED,
|
|
80
|
+
"`flwr-serverapp` does not support TLS yet.",
|
|
79
81
|
)
|
|
80
|
-
sys.exit(1)
|
|
81
82
|
|
|
82
83
|
log(
|
|
83
84
|
DEBUG,
|
|
84
|
-
"
|
|
85
|
+
"`flwr-serverapp` will attempt to connect to SuperLink's "
|
|
86
|
+
"ServerAppIo API at %s",
|
|
85
87
|
args.serverappio_api_address,
|
|
86
88
|
)
|
|
87
89
|
run_serverapp(
|
|
@@ -96,7 +98,7 @@ def flwr_serverapp() -> None:
|
|
|
96
98
|
restore_output()
|
|
97
99
|
|
|
98
100
|
|
|
99
|
-
def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
101
|
+
def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
|
|
100
102
|
serverappio_api_address: str,
|
|
101
103
|
log_queue: Queue[Optional[str]],
|
|
102
104
|
run_once: bool,
|
|
@@ -112,12 +114,15 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
|
112
114
|
# Resolve directory where FABs are installed
|
|
113
115
|
flwr_dir_ = get_flwr_dir(flwr_dir)
|
|
114
116
|
log_uploader = None
|
|
115
|
-
|
|
117
|
+
success = True
|
|
118
|
+
hash_run_id = None
|
|
119
|
+
run_status = None
|
|
116
120
|
while True:
|
|
117
121
|
|
|
118
122
|
try:
|
|
119
123
|
# Pull ServerAppInputs from LinkState
|
|
120
124
|
req = PullServerAppInputsRequest()
|
|
125
|
+
log(DEBUG, "[flwr-serverapp] Pull ServerAppInputs")
|
|
121
126
|
res: PullServerAppInputsResponse = driver._stub.PullServerAppInputs(req)
|
|
122
127
|
if not res.HasField("run"):
|
|
123
128
|
sleep(3)
|
|
@@ -128,6 +133,8 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
|
128
133
|
run = run_from_proto(res.run)
|
|
129
134
|
fab = fab_from_proto(res.fab)
|
|
130
135
|
|
|
136
|
+
hash_run_id = get_sha256_hash(run.run_id)
|
|
137
|
+
|
|
131
138
|
driver.set_run(run.run_id)
|
|
132
139
|
|
|
133
140
|
# Start log uploader for this run
|
|
@@ -138,7 +145,7 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
|
138
145
|
stub=driver._stub,
|
|
139
146
|
)
|
|
140
147
|
|
|
141
|
-
log(DEBUG, "
|
|
148
|
+
log(DEBUG, "[flwr-serverapp] Start FAB installation.")
|
|
142
149
|
install_from_fab(fab.content, flwr_dir=flwr_dir_, skip_prompt=True)
|
|
143
150
|
|
|
144
151
|
fab_id, fab_version = get_fab_metadata(fab.content)
|
|
@@ -159,7 +166,7 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
|
159
166
|
|
|
160
167
|
log(
|
|
161
168
|
DEBUG,
|
|
162
|
-
"
|
|
169
|
+
"[flwr-serverapp] Will load ServerApp `%s` in %s",
|
|
163
170
|
server_app_attr,
|
|
164
171
|
app_path,
|
|
165
172
|
)
|
|
@@ -170,6 +177,11 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
|
170
177
|
UpdateRunStatusRequest(run_id=run.run_id, run_status=run_status_proto)
|
|
171
178
|
)
|
|
172
179
|
|
|
180
|
+
event(
|
|
181
|
+
EventType.FLWR_SERVERAPP_RUN_ENTER,
|
|
182
|
+
event_details={"run-id-hash": hash_run_id},
|
|
183
|
+
)
|
|
184
|
+
|
|
173
185
|
# Load and run the ServerApp with the Driver
|
|
174
186
|
updated_context = run_(
|
|
175
187
|
driver=driver,
|
|
@@ -180,17 +192,25 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
|
180
192
|
|
|
181
193
|
# Send resulting context
|
|
182
194
|
context_proto = context_to_proto(updated_context)
|
|
195
|
+
log(DEBUG, "[flwr-serverapp] Will push ServerAppOutputs")
|
|
183
196
|
out_req = PushServerAppOutputsRequest(
|
|
184
197
|
run_id=run.run_id, context=context_proto
|
|
185
198
|
)
|
|
186
199
|
_ = driver._stub.PushServerAppOutputs(out_req)
|
|
187
200
|
|
|
188
201
|
run_status = RunStatus(Status.FINISHED, SubStatus.COMPLETED, "")
|
|
202
|
+
except RunNotRunningException:
|
|
203
|
+
log(INFO, "")
|
|
204
|
+
log(INFO, "Run ID %s stopped.", run.run_id)
|
|
205
|
+
log(INFO, "")
|
|
206
|
+
run_status = None
|
|
207
|
+
success = False
|
|
189
208
|
|
|
190
209
|
except Exception as ex: # pylint: disable=broad-exception-caught
|
|
191
210
|
exc_entity = "ServerApp"
|
|
192
211
|
log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)
|
|
193
212
|
run_status = RunStatus(Status.FINISHED, SubStatus.FAILED, str(ex))
|
|
213
|
+
success = False
|
|
194
214
|
|
|
195
215
|
finally:
|
|
196
216
|
# Stop log uploader for this run and upload final logs
|
|
@@ -206,6 +226,10 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
|
|
|
206
226
|
run_id=run.run_id, run_status=run_status_proto
|
|
207
227
|
)
|
|
208
228
|
)
|
|
229
|
+
event(
|
|
230
|
+
EventType.FLWR_SERVERAPP_RUN_LEAVE,
|
|
231
|
+
event_details={"run-id-hash": hash_run_id, "success": success},
|
|
232
|
+
)
|
|
209
233
|
|
|
210
234
|
# Stop the loop if `flwr-serverapp` is expected to process a single run
|
|
211
235
|
if run_once:
|
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""ServerAppIo gRPC API."""
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
from logging import INFO
|
|
18
19
|
from typing import Optional
|
|
19
20
|
|
|
20
21
|
import grpc
|
|
21
22
|
|
|
22
23
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
24
|
+
from flwr.common.grpc import generic_create_grpc_server
|
|
23
25
|
from flwr.common.logger import log
|
|
24
26
|
from flwr.proto.serverappio_pb2_grpc import ( # pylint: disable=E0611
|
|
25
27
|
add_ServerAppIoServicer_to_server,
|
|
@@ -27,7 +29,6 @@ from flwr.proto.serverappio_pb2_grpc import ( # pylint: disable=E0611
|
|
|
27
29
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
|
28
30
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
|
29
31
|
|
|
30
|
-
from ..fleet.grpc_bidi.grpc_server import generic_create_grpc_server
|
|
31
32
|
from .serverappio_servicer import ServerAppIoServicer
|
|
32
33
|
|
|
33
34
|
|