flwr-nightly 1.23.0.dev20251028__py3-none-any.whl → 1.23.0.dev20251030__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/client/grpc_rere_client/grpc_adapter.py +32 -0
- flwr/common/constant.py +3 -1
- flwr/proto/fleet_pb2.py +31 -15
- flwr/proto/fleet_pb2.pyi +80 -0
- flwr/proto/fleet_pb2_grpc.py +136 -0
- flwr/proto/fleet_pb2_grpc.pyi +52 -0
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +17 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +122 -5
- flwr/server/superlink/fleet/message_handler/message_handler.py +72 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +64 -19
- flwr/server/superlink/fleet/vce/vce_api.py +3 -3
- flwr/server/superlink/linkstate/in_memory_linkstate.py +43 -2
- flwr/server/superlink/linkstate/linkstate.py +39 -0
- flwr/server/superlink/linkstate/sqlite_linkstate.py +57 -2
- {flwr_nightly-1.23.0.dev20251028.dist-info → flwr_nightly-1.23.0.dev20251030.dist-info}/METADATA +1 -1
- {flwr_nightly-1.23.0.dev20251028.dist-info → flwr_nightly-1.23.0.dev20251030.dist-info}/RECORD +18 -18
- {flwr_nightly-1.23.0.dev20251028.dist-info → flwr_nightly-1.23.0.dev20251030.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.23.0.dev20251028.dist-info → flwr_nightly-1.23.0.dev20251030.dist-info}/entry_points.txt +0 -0
|
@@ -34,14 +34,22 @@ from flwr.common.constant import (
|
|
|
34
34
|
from flwr.common.version import package_name, package_version
|
|
35
35
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
36
36
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
37
|
+
ActivateNodeRequest,
|
|
38
|
+
ActivateNodeResponse,
|
|
37
39
|
CreateNodeRequest,
|
|
38
40
|
CreateNodeResponse,
|
|
41
|
+
DeactivateNodeRequest,
|
|
42
|
+
DeactivateNodeResponse,
|
|
39
43
|
DeleteNodeRequest,
|
|
40
44
|
DeleteNodeResponse,
|
|
41
45
|
PullMessagesRequest,
|
|
42
46
|
PullMessagesResponse,
|
|
43
47
|
PushMessagesRequest,
|
|
44
48
|
PushMessagesResponse,
|
|
49
|
+
RegisterNodeFleetRequest,
|
|
50
|
+
RegisterNodeFleetResponse,
|
|
51
|
+
UnregisterNodeFleetRequest,
|
|
52
|
+
UnregisterNodeFleetResponse,
|
|
45
53
|
)
|
|
46
54
|
from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
|
|
47
55
|
from flwr.proto.grpcadapter_pb2_grpc import GrpcAdapterStub
|
|
@@ -130,6 +138,30 @@ class GrpcAdapter:
|
|
|
130
138
|
"""."""
|
|
131
139
|
return self._send_and_receive(request, DeleteNodeResponse, **kwargs)
|
|
132
140
|
|
|
141
|
+
def RegisterNode( # pylint: disable=C0103
|
|
142
|
+
self, request: RegisterNodeFleetRequest, **kwargs: Any
|
|
143
|
+
) -> RegisterNodeFleetResponse:
|
|
144
|
+
"""."""
|
|
145
|
+
return self._send_and_receive(request, RegisterNodeFleetResponse, **kwargs)
|
|
146
|
+
|
|
147
|
+
def ActivateNode( # pylint: disable=C0103
|
|
148
|
+
self, request: ActivateNodeRequest, **kwargs: Any
|
|
149
|
+
) -> ActivateNodeResponse:
|
|
150
|
+
"""."""
|
|
151
|
+
return self._send_and_receive(request, ActivateNodeResponse, **kwargs)
|
|
152
|
+
|
|
153
|
+
def DeactivateNode( # pylint: disable=C0103
|
|
154
|
+
self, request: DeactivateNodeRequest, **kwargs: Any
|
|
155
|
+
) -> DeactivateNodeResponse:
|
|
156
|
+
"""."""
|
|
157
|
+
return self._send_and_receive(request, DeactivateNodeResponse, **kwargs)
|
|
158
|
+
|
|
159
|
+
def UnregisterNode( # pylint: disable=C0103
|
|
160
|
+
self, request: UnregisterNodeFleetRequest, **kwargs: Any
|
|
161
|
+
) -> UnregisterNodeFleetResponse:
|
|
162
|
+
"""."""
|
|
163
|
+
return self._send_and_receive(request, UnregisterNodeFleetResponse, **kwargs)
|
|
164
|
+
|
|
133
165
|
def SendNodeHeartbeat( # pylint: disable=C0103
|
|
134
166
|
self, request: SendNodeHeartbeatRequest, **kwargs: Any
|
|
135
167
|
) -> SendNodeHeartbeatResponse:
|
flwr/common/constant.py
CHANGED
|
@@ -62,7 +62,9 @@ HEARTBEAT_DEFAULT_INTERVAL = 30
|
|
|
62
62
|
HEARTBEAT_CALL_TIMEOUT = 5
|
|
63
63
|
HEARTBEAT_BASE_MULTIPLIER = 0.8
|
|
64
64
|
HEARTBEAT_RANDOM_RANGE = (-0.1, 0.1)
|
|
65
|
-
|
|
65
|
+
HEARTBEAT_MIN_INTERVAL = 10
|
|
66
|
+
HEARTBEAT_MAX_INTERVAL = 1800 # 30 minutes
|
|
67
|
+
HEARTBEAT_INTERVAL_INF = 1e300 # Large value, disabling heartbeats
|
|
66
68
|
HEARTBEAT_PATIENCE = 2
|
|
67
69
|
RUN_FAILURE_DETAILS_NO_HEARTBEAT = "No heartbeat received from the run."
|
|
68
70
|
|
flwr/proto/fleet_pb2.py
CHANGED
|
@@ -19,7 +19,7 @@ from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
|
|
|
19
19
|
from flwr.proto import message_pb2 as flwr_dot_proto_dot_message__pb2
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x18\x66lwr/proto/message.proto\"C\n\x11\x43reateNodeRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\x1a\n\x12heartbeat_interval\x18\x02 \x01(\x01\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\"J\n\x13PullMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x13\n\x0bmessage_ids\x18\x02 \x03(\t\"\xa2\x01\n\x14PullMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\x97\x01\n\x13PushMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\xc9\x01\n\x14PushMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12>\n\x07results\x18\x02 \x03(\x0b\x32-.flwr.proto.PushMessagesResponse.ResultsEntry\x12\x17\n\x0fobjects_to_push\x18\x03 \x03(\t\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\
|
|
22
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x18\x66lwr/proto/message.proto\"C\n\x11\x43reateNodeRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\x1a\n\x12heartbeat_interval\x18\x02 \x01(\x01\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\".\n\x18RegisterNodeFleetRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\"\x1b\n\x19RegisterNodeFleetResponse\"E\n\x13\x41\x63tivateNodeRequest\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\x12\x1a\n\x12heartbeat_interval\x18\x02 \x01(\x01\"\'\n\x14\x41\x63tivateNodeResponse\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\"(\n\x15\x44\x65\x61\x63tivateNodeRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\"\x18\n\x16\x44\x65\x61\x63tivateNodeResponse\"-\n\x1aUnregisterNodeFleetRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x04\"\x1d\n\x1bUnregisterNodeFleetResponse\"J\n\x13PullMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x13\n\x0bmessage_ids\x18\x02 \x03(\t\"\xa2\x01\n\x14PullMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\x97\x01\n\x13PushMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\xc9\x01\n\x14PushMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12>\n\x07results\x18\x02 \x03(\x0b\x32-.flwr.proto.PushMessagesResponse.ResultsEntry\x12\x17\n\x0fobjects_to_push\x18\x03 \x03(\t\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\xbe\t\n\x05\x46leet\x12M\n\nCreateNode\x12\x1d.flwr.proto.CreateNodeRequest\x1a\x1e.flwr.proto.CreateNodeResponse\"\x00\x12M\n\nDeleteNode\x12\x1d.flwr.proto.DeleteNodeRequest\x1a\x1e.flwr.proto.DeleteNodeResponse\"\x00\x12]\n\x0cRegisterNode\x12$.flwr.proto.RegisterNodeFleetRequest\x1a%.flwr.proto.RegisterNodeFleetResponse\"\x00\x12S\n\x0c\x41\x63tivateNode\x12\x1f.flwr.proto.ActivateNodeRequest\x1a .flwr.proto.ActivateNodeResponse\"\x00\x12Y\n\x0e\x44\x65\x61\x63tivateNode\x12!.flwr.proto.DeactivateNodeRequest\x1a\".flwr.proto.DeactivateNodeResponse\"\x00\x12\x63\n\x0eUnregisterNode\x12&.flwr.proto.UnregisterNodeFleetRequest\x1a\'.flwr.proto.UnregisterNodeFleetResponse\"\x00\x12\x62\n\x11SendNodeHeartbeat\x12$.flwr.proto.SendNodeHeartbeatRequest\x1a%.flwr.proto.SendNodeHeartbeatResponse\"\x00\x12S\n\x0cPullMessages\x12\x1f.flwr.proto.PullMessagesRequest\x1a .flwr.proto.PullMessagesResponse\"\x00\x12S\n\x0cPushMessages\x12\x1f.flwr.proto.PushMessagesRequest\x1a .flwr.proto.PushMessagesResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x12M\n\nPushObject\x12\x1d.flwr.proto.PushObjectRequest\x1a\x1e.flwr.proto.PushObjectResponse\"\x00\x12M\n\nPullObject\x12\x1d.flwr.proto.PullObjectRequest\x1a\x1e.flwr.proto.PullObjectResponse\"\x00\x12q\n\x16\x43onfirmMessageReceived\x12).flwr.proto.ConfirmMessageReceivedRequest\x1a*.flwr.proto.ConfirmMessageReceivedResponse\"\x00\x62\x06proto3')
|
|
23
23
|
|
|
24
24
|
_globals = globals()
|
|
25
25
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -36,18 +36,34 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
|
36
36
|
_globals['_DELETENODEREQUEST']._serialized_end=333
|
|
37
37
|
_globals['_DELETENODERESPONSE']._serialized_start=335
|
|
38
38
|
_globals['_DELETENODERESPONSE']._serialized_end=355
|
|
39
|
-
_globals['
|
|
40
|
-
_globals['
|
|
41
|
-
_globals['
|
|
42
|
-
_globals['
|
|
43
|
-
_globals['
|
|
44
|
-
_globals['
|
|
45
|
-
_globals['
|
|
46
|
-
_globals['
|
|
47
|
-
_globals['
|
|
48
|
-
_globals['
|
|
49
|
-
_globals['
|
|
50
|
-
_globals['
|
|
51
|
-
_globals['
|
|
52
|
-
_globals['
|
|
39
|
+
_globals['_REGISTERNODEFLEETREQUEST']._serialized_start=357
|
|
40
|
+
_globals['_REGISTERNODEFLEETREQUEST']._serialized_end=403
|
|
41
|
+
_globals['_REGISTERNODEFLEETRESPONSE']._serialized_start=405
|
|
42
|
+
_globals['_REGISTERNODEFLEETRESPONSE']._serialized_end=432
|
|
43
|
+
_globals['_ACTIVATENODEREQUEST']._serialized_start=434
|
|
44
|
+
_globals['_ACTIVATENODEREQUEST']._serialized_end=503
|
|
45
|
+
_globals['_ACTIVATENODERESPONSE']._serialized_start=505
|
|
46
|
+
_globals['_ACTIVATENODERESPONSE']._serialized_end=544
|
|
47
|
+
_globals['_DEACTIVATENODEREQUEST']._serialized_start=546
|
|
48
|
+
_globals['_DEACTIVATENODEREQUEST']._serialized_end=586
|
|
49
|
+
_globals['_DEACTIVATENODERESPONSE']._serialized_start=588
|
|
50
|
+
_globals['_DEACTIVATENODERESPONSE']._serialized_end=612
|
|
51
|
+
_globals['_UNREGISTERNODEFLEETREQUEST']._serialized_start=614
|
|
52
|
+
_globals['_UNREGISTERNODEFLEETREQUEST']._serialized_end=659
|
|
53
|
+
_globals['_UNREGISTERNODEFLEETRESPONSE']._serialized_start=661
|
|
54
|
+
_globals['_UNREGISTERNODEFLEETRESPONSE']._serialized_end=690
|
|
55
|
+
_globals['_PULLMESSAGESREQUEST']._serialized_start=692
|
|
56
|
+
_globals['_PULLMESSAGESREQUEST']._serialized_end=766
|
|
57
|
+
_globals['_PULLMESSAGESRESPONSE']._serialized_start=769
|
|
58
|
+
_globals['_PULLMESSAGESRESPONSE']._serialized_end=931
|
|
59
|
+
_globals['_PUSHMESSAGESREQUEST']._serialized_start=934
|
|
60
|
+
_globals['_PUSHMESSAGESREQUEST']._serialized_end=1085
|
|
61
|
+
_globals['_PUSHMESSAGESRESPONSE']._serialized_start=1088
|
|
62
|
+
_globals['_PUSHMESSAGESRESPONSE']._serialized_end=1289
|
|
63
|
+
_globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_start=1243
|
|
64
|
+
_globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_end=1289
|
|
65
|
+
_globals['_RECONNECT']._serialized_start=1291
|
|
66
|
+
_globals['_RECONNECT']._serialized_end=1321
|
|
67
|
+
_globals['_FLEET']._serialized_start=1324
|
|
68
|
+
_globals['_FLEET']._serialized_end=2538
|
|
53
69
|
# @@protoc_insertion_point(module_scope)
|
flwr/proto/fleet_pb2.pyi
CHANGED
|
@@ -61,6 +61,86 @@ class DeleteNodeResponse(google.protobuf.message.Message):
|
|
|
61
61
|
) -> None: ...
|
|
62
62
|
global___DeleteNodeResponse = DeleteNodeResponse
|
|
63
63
|
|
|
64
|
+
class RegisterNodeFleetRequest(google.protobuf.message.Message):
|
|
65
|
+
"""RegisterNode messages (add prefix to avoid name clash)"""
|
|
66
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
67
|
+
PUBLIC_KEY_FIELD_NUMBER: builtins.int
|
|
68
|
+
public_key: builtins.bytes
|
|
69
|
+
def __init__(self,
|
|
70
|
+
*,
|
|
71
|
+
public_key: builtins.bytes = ...,
|
|
72
|
+
) -> None: ...
|
|
73
|
+
def ClearField(self, field_name: typing_extensions.Literal["public_key",b"public_key"]) -> None: ...
|
|
74
|
+
global___RegisterNodeFleetRequest = RegisterNodeFleetRequest
|
|
75
|
+
|
|
76
|
+
class RegisterNodeFleetResponse(google.protobuf.message.Message):
|
|
77
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
78
|
+
def __init__(self,
|
|
79
|
+
) -> None: ...
|
|
80
|
+
global___RegisterNodeFleetResponse = RegisterNodeFleetResponse
|
|
81
|
+
|
|
82
|
+
class ActivateNodeRequest(google.protobuf.message.Message):
|
|
83
|
+
"""ActivateNode messages"""
|
|
84
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
85
|
+
PUBLIC_KEY_FIELD_NUMBER: builtins.int
|
|
86
|
+
HEARTBEAT_INTERVAL_FIELD_NUMBER: builtins.int
|
|
87
|
+
public_key: builtins.bytes
|
|
88
|
+
heartbeat_interval: builtins.float
|
|
89
|
+
def __init__(self,
|
|
90
|
+
*,
|
|
91
|
+
public_key: builtins.bytes = ...,
|
|
92
|
+
heartbeat_interval: builtins.float = ...,
|
|
93
|
+
) -> None: ...
|
|
94
|
+
def ClearField(self, field_name: typing_extensions.Literal["heartbeat_interval",b"heartbeat_interval","public_key",b"public_key"]) -> None: ...
|
|
95
|
+
global___ActivateNodeRequest = ActivateNodeRequest
|
|
96
|
+
|
|
97
|
+
class ActivateNodeResponse(google.protobuf.message.Message):
|
|
98
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
99
|
+
NODE_ID_FIELD_NUMBER: builtins.int
|
|
100
|
+
node_id: builtins.int
|
|
101
|
+
def __init__(self,
|
|
102
|
+
*,
|
|
103
|
+
node_id: builtins.int = ...,
|
|
104
|
+
) -> None: ...
|
|
105
|
+
def ClearField(self, field_name: typing_extensions.Literal["node_id",b"node_id"]) -> None: ...
|
|
106
|
+
global___ActivateNodeResponse = ActivateNodeResponse
|
|
107
|
+
|
|
108
|
+
class DeactivateNodeRequest(google.protobuf.message.Message):
|
|
109
|
+
"""DeactivateNode messages"""
|
|
110
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
111
|
+
NODE_ID_FIELD_NUMBER: builtins.int
|
|
112
|
+
node_id: builtins.int
|
|
113
|
+
def __init__(self,
|
|
114
|
+
*,
|
|
115
|
+
node_id: builtins.int = ...,
|
|
116
|
+
) -> None: ...
|
|
117
|
+
def ClearField(self, field_name: typing_extensions.Literal["node_id",b"node_id"]) -> None: ...
|
|
118
|
+
global___DeactivateNodeRequest = DeactivateNodeRequest
|
|
119
|
+
|
|
120
|
+
class DeactivateNodeResponse(google.protobuf.message.Message):
|
|
121
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
122
|
+
def __init__(self,
|
|
123
|
+
) -> None: ...
|
|
124
|
+
global___DeactivateNodeResponse = DeactivateNodeResponse
|
|
125
|
+
|
|
126
|
+
class UnregisterNodeFleetRequest(google.protobuf.message.Message):
|
|
127
|
+
"""UnregisterNode messages (add prefix to avoid name clash)"""
|
|
128
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
129
|
+
NODE_ID_FIELD_NUMBER: builtins.int
|
|
130
|
+
node_id: builtins.int
|
|
131
|
+
def __init__(self,
|
|
132
|
+
*,
|
|
133
|
+
node_id: builtins.int = ...,
|
|
134
|
+
) -> None: ...
|
|
135
|
+
def ClearField(self, field_name: typing_extensions.Literal["node_id",b"node_id"]) -> None: ...
|
|
136
|
+
global___UnregisterNodeFleetRequest = UnregisterNodeFleetRequest
|
|
137
|
+
|
|
138
|
+
class UnregisterNodeFleetResponse(google.protobuf.message.Message):
|
|
139
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
140
|
+
def __init__(self,
|
|
141
|
+
) -> None: ...
|
|
142
|
+
global___UnregisterNodeFleetResponse = UnregisterNodeFleetResponse
|
|
143
|
+
|
|
64
144
|
class PullMessagesRequest(google.protobuf.message.Message):
|
|
65
145
|
"""PullMessages messages"""
|
|
66
146
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
flwr/proto/fleet_pb2_grpc.py
CHANGED
|
@@ -28,6 +28,26 @@ class FleetStub(object):
|
|
|
28
28
|
request_serializer=flwr_dot_proto_dot_fleet__pb2.DeleteNodeRequest.SerializeToString,
|
|
29
29
|
response_deserializer=flwr_dot_proto_dot_fleet__pb2.DeleteNodeResponse.FromString,
|
|
30
30
|
)
|
|
31
|
+
self.RegisterNode = channel.unary_unary(
|
|
32
|
+
'/flwr.proto.Fleet/RegisterNode',
|
|
33
|
+
request_serializer=flwr_dot_proto_dot_fleet__pb2.RegisterNodeFleetRequest.SerializeToString,
|
|
34
|
+
response_deserializer=flwr_dot_proto_dot_fleet__pb2.RegisterNodeFleetResponse.FromString,
|
|
35
|
+
)
|
|
36
|
+
self.ActivateNode = channel.unary_unary(
|
|
37
|
+
'/flwr.proto.Fleet/ActivateNode',
|
|
38
|
+
request_serializer=flwr_dot_proto_dot_fleet__pb2.ActivateNodeRequest.SerializeToString,
|
|
39
|
+
response_deserializer=flwr_dot_proto_dot_fleet__pb2.ActivateNodeResponse.FromString,
|
|
40
|
+
)
|
|
41
|
+
self.DeactivateNode = channel.unary_unary(
|
|
42
|
+
'/flwr.proto.Fleet/DeactivateNode',
|
|
43
|
+
request_serializer=flwr_dot_proto_dot_fleet__pb2.DeactivateNodeRequest.SerializeToString,
|
|
44
|
+
response_deserializer=flwr_dot_proto_dot_fleet__pb2.DeactivateNodeResponse.FromString,
|
|
45
|
+
)
|
|
46
|
+
self.UnregisterNode = channel.unary_unary(
|
|
47
|
+
'/flwr.proto.Fleet/UnregisterNode',
|
|
48
|
+
request_serializer=flwr_dot_proto_dot_fleet__pb2.UnregisterNodeFleetRequest.SerializeToString,
|
|
49
|
+
response_deserializer=flwr_dot_proto_dot_fleet__pb2.UnregisterNodeFleetResponse.FromString,
|
|
50
|
+
)
|
|
31
51
|
self.SendNodeHeartbeat = channel.unary_unary(
|
|
32
52
|
'/flwr.proto.Fleet/SendNodeHeartbeat',
|
|
33
53
|
request_serializer=flwr_dot_proto_dot_heartbeat__pb2.SendNodeHeartbeatRequest.SerializeToString,
|
|
@@ -85,6 +105,34 @@ class FleetServicer(object):
|
|
|
85
105
|
context.set_details('Method not implemented!')
|
|
86
106
|
raise NotImplementedError('Method not implemented!')
|
|
87
107
|
|
|
108
|
+
def RegisterNode(self, request, context):
|
|
109
|
+
"""Register Node
|
|
110
|
+
"""
|
|
111
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
112
|
+
context.set_details('Method not implemented!')
|
|
113
|
+
raise NotImplementedError('Method not implemented!')
|
|
114
|
+
|
|
115
|
+
def ActivateNode(self, request, context):
|
|
116
|
+
"""Activate Node
|
|
117
|
+
"""
|
|
118
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
119
|
+
context.set_details('Method not implemented!')
|
|
120
|
+
raise NotImplementedError('Method not implemented!')
|
|
121
|
+
|
|
122
|
+
def DeactivateNode(self, request, context):
|
|
123
|
+
"""Deactivate Node
|
|
124
|
+
"""
|
|
125
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
126
|
+
context.set_details('Method not implemented!')
|
|
127
|
+
raise NotImplementedError('Method not implemented!')
|
|
128
|
+
|
|
129
|
+
def UnregisterNode(self, request, context):
|
|
130
|
+
"""Unregister Node
|
|
131
|
+
"""
|
|
132
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
133
|
+
context.set_details('Method not implemented!')
|
|
134
|
+
raise NotImplementedError('Method not implemented!')
|
|
135
|
+
|
|
88
136
|
def SendNodeHeartbeat(self, request, context):
|
|
89
137
|
"""Missing associated documentation comment in .proto file."""
|
|
90
138
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
@@ -156,6 +204,26 @@ def add_FleetServicer_to_server(servicer, server):
|
|
|
156
204
|
request_deserializer=flwr_dot_proto_dot_fleet__pb2.DeleteNodeRequest.FromString,
|
|
157
205
|
response_serializer=flwr_dot_proto_dot_fleet__pb2.DeleteNodeResponse.SerializeToString,
|
|
158
206
|
),
|
|
207
|
+
'RegisterNode': grpc.unary_unary_rpc_method_handler(
|
|
208
|
+
servicer.RegisterNode,
|
|
209
|
+
request_deserializer=flwr_dot_proto_dot_fleet__pb2.RegisterNodeFleetRequest.FromString,
|
|
210
|
+
response_serializer=flwr_dot_proto_dot_fleet__pb2.RegisterNodeFleetResponse.SerializeToString,
|
|
211
|
+
),
|
|
212
|
+
'ActivateNode': grpc.unary_unary_rpc_method_handler(
|
|
213
|
+
servicer.ActivateNode,
|
|
214
|
+
request_deserializer=flwr_dot_proto_dot_fleet__pb2.ActivateNodeRequest.FromString,
|
|
215
|
+
response_serializer=flwr_dot_proto_dot_fleet__pb2.ActivateNodeResponse.SerializeToString,
|
|
216
|
+
),
|
|
217
|
+
'DeactivateNode': grpc.unary_unary_rpc_method_handler(
|
|
218
|
+
servicer.DeactivateNode,
|
|
219
|
+
request_deserializer=flwr_dot_proto_dot_fleet__pb2.DeactivateNodeRequest.FromString,
|
|
220
|
+
response_serializer=flwr_dot_proto_dot_fleet__pb2.DeactivateNodeResponse.SerializeToString,
|
|
221
|
+
),
|
|
222
|
+
'UnregisterNode': grpc.unary_unary_rpc_method_handler(
|
|
223
|
+
servicer.UnregisterNode,
|
|
224
|
+
request_deserializer=flwr_dot_proto_dot_fleet__pb2.UnregisterNodeFleetRequest.FromString,
|
|
225
|
+
response_serializer=flwr_dot_proto_dot_fleet__pb2.UnregisterNodeFleetResponse.SerializeToString,
|
|
226
|
+
),
|
|
159
227
|
'SendNodeHeartbeat': grpc.unary_unary_rpc_method_handler(
|
|
160
228
|
servicer.SendNodeHeartbeat,
|
|
161
229
|
request_deserializer=flwr_dot_proto_dot_heartbeat__pb2.SendNodeHeartbeatRequest.FromString,
|
|
@@ -240,6 +308,74 @@ class Fleet(object):
|
|
|
240
308
|
options, channel_credentials,
|
|
241
309
|
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
242
310
|
|
|
311
|
+
@staticmethod
|
|
312
|
+
def RegisterNode(request,
|
|
313
|
+
target,
|
|
314
|
+
options=(),
|
|
315
|
+
channel_credentials=None,
|
|
316
|
+
call_credentials=None,
|
|
317
|
+
insecure=False,
|
|
318
|
+
compression=None,
|
|
319
|
+
wait_for_ready=None,
|
|
320
|
+
timeout=None,
|
|
321
|
+
metadata=None):
|
|
322
|
+
return grpc.experimental.unary_unary(request, target, '/flwr.proto.Fleet/RegisterNode',
|
|
323
|
+
flwr_dot_proto_dot_fleet__pb2.RegisterNodeFleetRequest.SerializeToString,
|
|
324
|
+
flwr_dot_proto_dot_fleet__pb2.RegisterNodeFleetResponse.FromString,
|
|
325
|
+
options, channel_credentials,
|
|
326
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
327
|
+
|
|
328
|
+
@staticmethod
|
|
329
|
+
def ActivateNode(request,
|
|
330
|
+
target,
|
|
331
|
+
options=(),
|
|
332
|
+
channel_credentials=None,
|
|
333
|
+
call_credentials=None,
|
|
334
|
+
insecure=False,
|
|
335
|
+
compression=None,
|
|
336
|
+
wait_for_ready=None,
|
|
337
|
+
timeout=None,
|
|
338
|
+
metadata=None):
|
|
339
|
+
return grpc.experimental.unary_unary(request, target, '/flwr.proto.Fleet/ActivateNode',
|
|
340
|
+
flwr_dot_proto_dot_fleet__pb2.ActivateNodeRequest.SerializeToString,
|
|
341
|
+
flwr_dot_proto_dot_fleet__pb2.ActivateNodeResponse.FromString,
|
|
342
|
+
options, channel_credentials,
|
|
343
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
344
|
+
|
|
345
|
+
@staticmethod
|
|
346
|
+
def DeactivateNode(request,
|
|
347
|
+
target,
|
|
348
|
+
options=(),
|
|
349
|
+
channel_credentials=None,
|
|
350
|
+
call_credentials=None,
|
|
351
|
+
insecure=False,
|
|
352
|
+
compression=None,
|
|
353
|
+
wait_for_ready=None,
|
|
354
|
+
timeout=None,
|
|
355
|
+
metadata=None):
|
|
356
|
+
return grpc.experimental.unary_unary(request, target, '/flwr.proto.Fleet/DeactivateNode',
|
|
357
|
+
flwr_dot_proto_dot_fleet__pb2.DeactivateNodeRequest.SerializeToString,
|
|
358
|
+
flwr_dot_proto_dot_fleet__pb2.DeactivateNodeResponse.FromString,
|
|
359
|
+
options, channel_credentials,
|
|
360
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
361
|
+
|
|
362
|
+
@staticmethod
|
|
363
|
+
def UnregisterNode(request,
|
|
364
|
+
target,
|
|
365
|
+
options=(),
|
|
366
|
+
channel_credentials=None,
|
|
367
|
+
call_credentials=None,
|
|
368
|
+
insecure=False,
|
|
369
|
+
compression=None,
|
|
370
|
+
wait_for_ready=None,
|
|
371
|
+
timeout=None,
|
|
372
|
+
metadata=None):
|
|
373
|
+
return grpc.experimental.unary_unary(request, target, '/flwr.proto.Fleet/UnregisterNode',
|
|
374
|
+
flwr_dot_proto_dot_fleet__pb2.UnregisterNodeFleetRequest.SerializeToString,
|
|
375
|
+
flwr_dot_proto_dot_fleet__pb2.UnregisterNodeFleetResponse.FromString,
|
|
376
|
+
options, channel_credentials,
|
|
377
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
378
|
+
|
|
243
379
|
@staticmethod
|
|
244
380
|
def SendNodeHeartbeat(request,
|
|
245
381
|
target,
|
flwr/proto/fleet_pb2_grpc.pyi
CHANGED
|
@@ -20,6 +20,26 @@ class FleetStub:
|
|
|
20
20
|
flwr.proto.fleet_pb2.DeleteNodeRequest,
|
|
21
21
|
flwr.proto.fleet_pb2.DeleteNodeResponse]
|
|
22
22
|
|
|
23
|
+
RegisterNode: grpc.UnaryUnaryMultiCallable[
|
|
24
|
+
flwr.proto.fleet_pb2.RegisterNodeFleetRequest,
|
|
25
|
+
flwr.proto.fleet_pb2.RegisterNodeFleetResponse]
|
|
26
|
+
"""Register Node"""
|
|
27
|
+
|
|
28
|
+
ActivateNode: grpc.UnaryUnaryMultiCallable[
|
|
29
|
+
flwr.proto.fleet_pb2.ActivateNodeRequest,
|
|
30
|
+
flwr.proto.fleet_pb2.ActivateNodeResponse]
|
|
31
|
+
"""Activate Node"""
|
|
32
|
+
|
|
33
|
+
DeactivateNode: grpc.UnaryUnaryMultiCallable[
|
|
34
|
+
flwr.proto.fleet_pb2.DeactivateNodeRequest,
|
|
35
|
+
flwr.proto.fleet_pb2.DeactivateNodeResponse]
|
|
36
|
+
"""Deactivate Node"""
|
|
37
|
+
|
|
38
|
+
UnregisterNode: grpc.UnaryUnaryMultiCallable[
|
|
39
|
+
flwr.proto.fleet_pb2.UnregisterNodeFleetRequest,
|
|
40
|
+
flwr.proto.fleet_pb2.UnregisterNodeFleetResponse]
|
|
41
|
+
"""Unregister Node"""
|
|
42
|
+
|
|
23
43
|
SendNodeHeartbeat: grpc.UnaryUnaryMultiCallable[
|
|
24
44
|
flwr.proto.heartbeat_pb2.SendNodeHeartbeatRequest,
|
|
25
45
|
flwr.proto.heartbeat_pb2.SendNodeHeartbeatResponse]
|
|
@@ -78,6 +98,38 @@ class FleetServicer(metaclass=abc.ABCMeta):
|
|
|
78
98
|
context: grpc.ServicerContext,
|
|
79
99
|
) -> flwr.proto.fleet_pb2.DeleteNodeResponse: ...
|
|
80
100
|
|
|
101
|
+
@abc.abstractmethod
|
|
102
|
+
def RegisterNode(self,
|
|
103
|
+
request: flwr.proto.fleet_pb2.RegisterNodeFleetRequest,
|
|
104
|
+
context: grpc.ServicerContext,
|
|
105
|
+
) -> flwr.proto.fleet_pb2.RegisterNodeFleetResponse:
|
|
106
|
+
"""Register Node"""
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
@abc.abstractmethod
|
|
110
|
+
def ActivateNode(self,
|
|
111
|
+
request: flwr.proto.fleet_pb2.ActivateNodeRequest,
|
|
112
|
+
context: grpc.ServicerContext,
|
|
113
|
+
) -> flwr.proto.fleet_pb2.ActivateNodeResponse:
|
|
114
|
+
"""Activate Node"""
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
@abc.abstractmethod
|
|
118
|
+
def DeactivateNode(self,
|
|
119
|
+
request: flwr.proto.fleet_pb2.DeactivateNodeRequest,
|
|
120
|
+
context: grpc.ServicerContext,
|
|
121
|
+
) -> flwr.proto.fleet_pb2.DeactivateNodeResponse:
|
|
122
|
+
"""Deactivate Node"""
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
@abc.abstractmethod
|
|
126
|
+
def UnregisterNode(self,
|
|
127
|
+
request: flwr.proto.fleet_pb2.UnregisterNodeFleetRequest,
|
|
128
|
+
context: grpc.ServicerContext,
|
|
129
|
+
) -> flwr.proto.fleet_pb2.UnregisterNodeFleetResponse:
|
|
130
|
+
"""Unregister Node"""
|
|
131
|
+
pass
|
|
132
|
+
|
|
81
133
|
@abc.abstractmethod
|
|
82
134
|
def SendNodeHeartbeat(self,
|
|
83
135
|
request: flwr.proto.heartbeat_pb2.SendNodeHeartbeatRequest,
|
|
@@ -33,10 +33,14 @@ from flwr.common.version import package_name, package_version
|
|
|
33
33
|
from flwr.proto import grpcadapter_pb2_grpc # pylint: disable=E0611
|
|
34
34
|
from flwr.proto.fab_pb2 import GetFabRequest # pylint: disable=E0611
|
|
35
35
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
36
|
+
ActivateNodeRequest,
|
|
36
37
|
CreateNodeRequest,
|
|
38
|
+
DeactivateNodeRequest,
|
|
37
39
|
DeleteNodeRequest,
|
|
38
40
|
PullMessagesRequest,
|
|
39
41
|
PushMessagesRequest,
|
|
42
|
+
RegisterNodeFleetRequest,
|
|
43
|
+
UnregisterNodeFleetRequest,
|
|
40
44
|
)
|
|
41
45
|
from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
|
|
42
46
|
from flwr.proto.heartbeat_pb2 import SendNodeHeartbeatRequest # pylint: disable=E0611
|
|
@@ -77,7 +81,7 @@ def _handle(
|
|
|
77
81
|
class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer, FleetServicer):
|
|
78
82
|
"""Fleet API via GrpcAdapter servicer."""
|
|
79
83
|
|
|
80
|
-
def SendReceive( # pylint: disable=too-many-return-statements
|
|
84
|
+
def SendReceive( # pylint: disable=too-many-return-statements, too-many-branches
|
|
81
85
|
self, request: MessageContainer, context: grpc.ServicerContext
|
|
82
86
|
) -> MessageContainer:
|
|
83
87
|
"""."""
|
|
@@ -86,6 +90,18 @@ class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer, FleetService
|
|
|
86
90
|
return _handle(request, context, CreateNodeRequest, self.CreateNode)
|
|
87
91
|
if request.grpc_message_name == DeleteNodeRequest.__qualname__:
|
|
88
92
|
return _handle(request, context, DeleteNodeRequest, self.DeleteNode)
|
|
93
|
+
if request.grpc_message_name == RegisterNodeFleetRequest.__qualname__:
|
|
94
|
+
return _handle(
|
|
95
|
+
request, context, RegisterNodeFleetRequest, self.RegisterNode
|
|
96
|
+
)
|
|
97
|
+
if request.grpc_message_name == ActivateNodeRequest.__qualname__:
|
|
98
|
+
return _handle(request, context, ActivateNodeRequest, self.ActivateNode)
|
|
99
|
+
if request.grpc_message_name == DeactivateNodeRequest.__qualname__:
|
|
100
|
+
return _handle(request, context, DeactivateNodeRequest, self.DeactivateNode)
|
|
101
|
+
if request.grpc_message_name == UnregisterNodeFleetRequest.__qualname__:
|
|
102
|
+
return _handle(
|
|
103
|
+
request, context, UnregisterNodeFleetRequest, self.UnregisterNode
|
|
104
|
+
)
|
|
89
105
|
if request.grpc_message_name == SendNodeHeartbeatRequest.__qualname__:
|
|
90
106
|
return _handle(
|
|
91
107
|
request, context, SendNodeHeartbeatRequest, self.SendNodeHeartbeat
|
|
@@ -21,21 +21,32 @@ from logging import DEBUG, ERROR, INFO
|
|
|
21
21
|
import grpc
|
|
22
22
|
from google.protobuf.json_format import MessageToDict
|
|
23
23
|
|
|
24
|
-
from flwr.common.constant import
|
|
24
|
+
from flwr.common.constant import (
|
|
25
|
+
PUBLIC_KEY_ALREADY_IN_USE_MESSAGE,
|
|
26
|
+
SUPERNODE_NOT_CREATED_FROM_CLI_MESSAGE,
|
|
27
|
+
)
|
|
25
28
|
from flwr.common.inflatable import UnexpectedObjectContentError
|
|
26
29
|
from flwr.common.logger import log
|
|
27
30
|
from flwr.common.typing import InvalidRunStatusException
|
|
28
31
|
from flwr.proto import fleet_pb2_grpc # pylint: disable=E0611
|
|
29
32
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
30
33
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
34
|
+
ActivateNodeRequest,
|
|
35
|
+
ActivateNodeResponse,
|
|
31
36
|
CreateNodeRequest,
|
|
32
37
|
CreateNodeResponse,
|
|
38
|
+
DeactivateNodeRequest,
|
|
39
|
+
DeactivateNodeResponse,
|
|
33
40
|
DeleteNodeRequest,
|
|
34
41
|
DeleteNodeResponse,
|
|
35
42
|
PullMessagesRequest,
|
|
36
43
|
PullMessagesResponse,
|
|
37
44
|
PushMessagesRequest,
|
|
38
45
|
PushMessagesResponse,
|
|
46
|
+
RegisterNodeFleetRequest,
|
|
47
|
+
RegisterNodeFleetResponse,
|
|
48
|
+
UnregisterNodeFleetRequest,
|
|
49
|
+
UnregisterNodeFleetResponse,
|
|
39
50
|
)
|
|
40
51
|
from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
|
|
41
52
|
SendNodeHeartbeatRequest,
|
|
@@ -140,6 +151,104 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
|
140
151
|
log(DEBUG, "[Fleet.CreateNode] Response: %s", MessageToDict(response))
|
|
141
152
|
return response
|
|
142
153
|
|
|
154
|
+
def RegisterNode(
|
|
155
|
+
self, request: RegisterNodeFleetRequest, context: grpc.ServicerContext
|
|
156
|
+
) -> RegisterNodeFleetResponse:
|
|
157
|
+
"""Register a node."""
|
|
158
|
+
# Prevent registration when SuperNode authentication is enabled
|
|
159
|
+
if self.enable_supernode_auth:
|
|
160
|
+
log(ERROR, "SuperNode registration is disabled through Fleet API.")
|
|
161
|
+
context.abort(
|
|
162
|
+
grpc.StatusCode.FAILED_PRECONDITION,
|
|
163
|
+
"SuperNode authentication is enabled. "
|
|
164
|
+
"All SuperNodes must be registered via the CLI.",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
return message_handler.register_node(
|
|
169
|
+
request=request,
|
|
170
|
+
state=self.state_factory.state(),
|
|
171
|
+
)
|
|
172
|
+
except ValueError:
|
|
173
|
+
# Public key already in use
|
|
174
|
+
log(ERROR, PUBLIC_KEY_ALREADY_IN_USE_MESSAGE)
|
|
175
|
+
context.abort(
|
|
176
|
+
grpc.StatusCode.FAILED_PRECONDITION, PUBLIC_KEY_ALREADY_IN_USE_MESSAGE
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
raise RuntimeError # Make mypy happy
|
|
180
|
+
|
|
181
|
+
def ActivateNode(
|
|
182
|
+
self, request: ActivateNodeRequest, context: grpc.ServicerContext
|
|
183
|
+
) -> ActivateNodeResponse:
|
|
184
|
+
"""Activate a node."""
|
|
185
|
+
try:
|
|
186
|
+
response = message_handler.activate_node(
|
|
187
|
+
request=request,
|
|
188
|
+
state=self.state_factory.state(),
|
|
189
|
+
)
|
|
190
|
+
log(INFO, "[Fleet.ActivateNode] Activated node_id=%s", response.node_id)
|
|
191
|
+
return response
|
|
192
|
+
except message_handler.InvalidHeartbeatIntervalError:
|
|
193
|
+
# Heartbeat interval is invalid
|
|
194
|
+
log(ERROR, "[Fleet.ActivateNode] Invalid heartbeat interval")
|
|
195
|
+
context.abort(
|
|
196
|
+
grpc.StatusCode.INVALID_ARGUMENT, "Invalid heartbeat interval"
|
|
197
|
+
)
|
|
198
|
+
except ValueError as e:
|
|
199
|
+
log(ERROR, "[Fleet.ActivateNode] Activation failed: %s", str(e))
|
|
200
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, str(e))
|
|
201
|
+
|
|
202
|
+
raise RuntimeError # Make mypy happy
|
|
203
|
+
|
|
204
|
+
def DeactivateNode(
|
|
205
|
+
self, request: DeactivateNodeRequest, context: grpc.ServicerContext
|
|
206
|
+
) -> DeactivateNodeResponse:
|
|
207
|
+
"""Deactivate a node."""
|
|
208
|
+
try:
|
|
209
|
+
response = message_handler.deactivate_node(
|
|
210
|
+
request=request,
|
|
211
|
+
state=self.state_factory.state(),
|
|
212
|
+
)
|
|
213
|
+
log(INFO, "[Fleet.DeactivateNode] Deactivated node_id=%s", request.node_id)
|
|
214
|
+
return response
|
|
215
|
+
except ValueError as e:
|
|
216
|
+
log(ERROR, "[Fleet.DeactivateNode] Deactivation failed: %s", str(e))
|
|
217
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, str(e))
|
|
218
|
+
|
|
219
|
+
raise RuntimeError # Make mypy happy
|
|
220
|
+
|
|
221
|
+
def UnregisterNode(
|
|
222
|
+
self, request: UnregisterNodeFleetRequest, context: grpc.ServicerContext
|
|
223
|
+
) -> UnregisterNodeFleetResponse:
|
|
224
|
+
"""Unregister a node."""
|
|
225
|
+
# Prevent unregistration when SuperNode authentication is enabled
|
|
226
|
+
if self.enable_supernode_auth:
|
|
227
|
+
log(ERROR, "SuperNode unregistration is disabled through Fleet API.")
|
|
228
|
+
context.abort(
|
|
229
|
+
grpc.StatusCode.FAILED_PRECONDITION,
|
|
230
|
+
"SuperNode authentication is enabled. "
|
|
231
|
+
"All SuperNodes must be unregistered via the CLI.",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
response = message_handler.unregister_node(
|
|
236
|
+
request=request,
|
|
237
|
+
state=self.state_factory.state(),
|
|
238
|
+
)
|
|
239
|
+
log(
|
|
240
|
+
DEBUG, "[Fleet.UnregisterNode] Unregistered node_id=%s", request.node_id
|
|
241
|
+
)
|
|
242
|
+
return response
|
|
243
|
+
except ValueError as e:
|
|
244
|
+
log(
|
|
245
|
+
ERROR,
|
|
246
|
+
"[Fleet.UnregisterNode] Unregistration failed: %s",
|
|
247
|
+
str(e),
|
|
248
|
+
)
|
|
249
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, str(e))
|
|
250
|
+
raise RuntimeError from None # Make mypy happy
|
|
251
|
+
|
|
143
252
|
def DeleteNode(
|
|
144
253
|
self, request: DeleteNodeRequest, context: grpc.ServicerContext
|
|
145
254
|
) -> DeleteNodeResponse:
|
|
@@ -168,10 +277,18 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
|
168
277
|
) -> SendNodeHeartbeatResponse:
|
|
169
278
|
"""."""
|
|
170
279
|
log(DEBUG, "[Fleet.SendNodeHeartbeat] Request: %s", MessageToDict(request))
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
280
|
+
try:
|
|
281
|
+
return message_handler.send_node_heartbeat(
|
|
282
|
+
request=request,
|
|
283
|
+
state=self.state_factory.state(),
|
|
284
|
+
)
|
|
285
|
+
except message_handler.InvalidHeartbeatIntervalError:
|
|
286
|
+
# Heartbeat interval is invalid
|
|
287
|
+
log(ERROR, "[Fleet.SendNodeHeartbeat] Invalid heartbeat interval")
|
|
288
|
+
context.abort(
|
|
289
|
+
grpc.StatusCode.INVALID_ARGUMENT, "Invalid heartbeat interval"
|
|
290
|
+
)
|
|
291
|
+
raise RuntimeError # Make mypy happy
|
|
175
292
|
|
|
176
293
|
def PullMessages(
|
|
177
294
|
self, request: PullMessagesRequest, context: grpc.ServicerContext
|
|
@@ -18,7 +18,12 @@ from logging import ERROR
|
|
|
18
18
|
from typing import Optional
|
|
19
19
|
|
|
20
20
|
from flwr.common import Message, log
|
|
21
|
-
from flwr.common.constant import
|
|
21
|
+
from flwr.common.constant import (
|
|
22
|
+
HEARTBEAT_MAX_INTERVAL,
|
|
23
|
+
HEARTBEAT_MIN_INTERVAL,
|
|
24
|
+
NOOP_FLWR_AID,
|
|
25
|
+
Status,
|
|
26
|
+
)
|
|
22
27
|
from flwr.common.inflatable import UnexpectedObjectContentError
|
|
23
28
|
from flwr.common.serde import (
|
|
24
29
|
fab_to_proto,
|
|
@@ -29,8 +34,12 @@ from flwr.common.serde import (
|
|
|
29
34
|
from flwr.common.typing import Fab, InvalidRunStatusException
|
|
30
35
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
31
36
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
37
|
+
ActivateNodeRequest,
|
|
38
|
+
ActivateNodeResponse,
|
|
32
39
|
CreateNodeRequest,
|
|
33
40
|
CreateNodeResponse,
|
|
41
|
+
DeactivateNodeRequest,
|
|
42
|
+
DeactivateNodeResponse,
|
|
34
43
|
DeleteNodeRequest,
|
|
35
44
|
DeleteNodeResponse,
|
|
36
45
|
PullMessagesRequest,
|
|
@@ -38,6 +47,10 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
|
38
47
|
PushMessagesRequest,
|
|
39
48
|
PushMessagesResponse,
|
|
40
49
|
Reconnect,
|
|
50
|
+
RegisterNodeFleetRequest,
|
|
51
|
+
RegisterNodeFleetResponse,
|
|
52
|
+
UnregisterNodeFleetRequest,
|
|
53
|
+
UnregisterNodeFleetResponse,
|
|
41
54
|
)
|
|
42
55
|
from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
|
|
43
56
|
SendNodeHeartbeatRequest,
|
|
@@ -64,6 +77,10 @@ from flwr.supercore.object_store import NoObjectInStoreError, ObjectStore
|
|
|
64
77
|
from flwr.supercore.object_store.utils import store_mapping_and_register_objects
|
|
65
78
|
|
|
66
79
|
|
|
80
|
+
class InvalidHeartbeatIntervalError(Exception):
|
|
81
|
+
"""Invalid heartbeat interval exception."""
|
|
82
|
+
|
|
83
|
+
|
|
67
84
|
def create_node(
|
|
68
85
|
request: CreateNodeRequest, # pylint: disable=unused-argument
|
|
69
86
|
state: LinkState,
|
|
@@ -87,11 +104,56 @@ def delete_node(request: DeleteNodeRequest, state: LinkState) -> DeleteNodeRespo
|
|
|
87
104
|
return DeleteNodeResponse()
|
|
88
105
|
|
|
89
106
|
|
|
107
|
+
def register_node(
|
|
108
|
+
request: RegisterNodeFleetRequest,
|
|
109
|
+
state: LinkState,
|
|
110
|
+
) -> RegisterNodeFleetResponse:
|
|
111
|
+
"""Register a node (Fleet API only)."""
|
|
112
|
+
state.create_node(NOOP_FLWR_AID, request.public_key, 0)
|
|
113
|
+
return RegisterNodeFleetResponse()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def activate_node(
|
|
117
|
+
request: ActivateNodeRequest,
|
|
118
|
+
state: LinkState,
|
|
119
|
+
) -> ActivateNodeResponse:
|
|
120
|
+
"""Activate a node."""
|
|
121
|
+
node_id = state.get_node_id_by_public_key(request.public_key)
|
|
122
|
+
if node_id is None:
|
|
123
|
+
raise ValueError("No SuperNode found with the given public key.")
|
|
124
|
+
_validate_heartbeat_interval(request.heartbeat_interval)
|
|
125
|
+
if not state.activate_node(node_id, request.heartbeat_interval):
|
|
126
|
+
raise ValueError(f"SuperNode with node ID {node_id} could not be activated.")
|
|
127
|
+
return ActivateNodeResponse(node_id=node_id)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def deactivate_node(
|
|
131
|
+
request: DeactivateNodeRequest,
|
|
132
|
+
state: LinkState,
|
|
133
|
+
) -> DeactivateNodeResponse:
|
|
134
|
+
"""Deactivate a node."""
|
|
135
|
+
if not state.deactivate_node(request.node_id):
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f"SuperNode with node ID {request.node_id} could not be deactivated."
|
|
138
|
+
)
|
|
139
|
+
return DeactivateNodeResponse()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def unregister_node(
|
|
143
|
+
request: UnregisterNodeFleetRequest,
|
|
144
|
+
state: LinkState,
|
|
145
|
+
) -> UnregisterNodeFleetResponse:
|
|
146
|
+
"""Unregister a node (Fleet API only)."""
|
|
147
|
+
state.delete_node(NOOP_FLWR_AID, request.node_id)
|
|
148
|
+
return UnregisterNodeFleetResponse()
|
|
149
|
+
|
|
150
|
+
|
|
90
151
|
def send_node_heartbeat(
|
|
91
152
|
request: SendNodeHeartbeatRequest, # pylint: disable=unused-argument
|
|
92
153
|
state: LinkState, # pylint: disable=unused-argument
|
|
93
154
|
) -> SendNodeHeartbeatResponse:
|
|
94
155
|
"""."""
|
|
156
|
+
_validate_heartbeat_interval(request.heartbeat_interval)
|
|
95
157
|
res = state.acknowledge_node_heartbeat(
|
|
96
158
|
request.node.node_id, request.heartbeat_interval
|
|
97
159
|
)
|
|
@@ -286,3 +348,12 @@ def confirm_message_received(
|
|
|
286
348
|
store.delete(request.message_object_id)
|
|
287
349
|
|
|
288
350
|
return ConfirmMessageReceivedResponse()
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _validate_heartbeat_interval(interval: float) -> None:
|
|
354
|
+
"""Raise if heartbeat interval is out of bounds."""
|
|
355
|
+
if not HEARTBEAT_MIN_INTERVAL <= interval <= HEARTBEAT_MAX_INTERVAL:
|
|
356
|
+
raise InvalidHeartbeatIntervalError(
|
|
357
|
+
f"Heartbeat interval {interval} is out of bounds "
|
|
358
|
+
f"[{HEARTBEAT_MIN_INTERVAL}, {HEARTBEAT_MAX_INTERVAL}]."
|
|
359
|
+
)
|
|
@@ -25,14 +25,22 @@ from google.protobuf.message import Message as GrpcMessage
|
|
|
25
25
|
from flwr.common.exit import ExitCode, flwr_exit
|
|
26
26
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
27
27
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
28
|
+
ActivateNodeRequest,
|
|
29
|
+
ActivateNodeResponse,
|
|
28
30
|
CreateNodeRequest,
|
|
29
31
|
CreateNodeResponse,
|
|
32
|
+
DeactivateNodeRequest,
|
|
33
|
+
DeactivateNodeResponse,
|
|
30
34
|
DeleteNodeRequest,
|
|
31
35
|
DeleteNodeResponse,
|
|
32
36
|
PullMessagesRequest,
|
|
33
37
|
PullMessagesResponse,
|
|
34
38
|
PushMessagesRequest,
|
|
35
39
|
PushMessagesResponse,
|
|
40
|
+
RegisterNodeFleetRequest,
|
|
41
|
+
RegisterNodeFleetResponse,
|
|
42
|
+
UnregisterNodeFleetRequest,
|
|
43
|
+
UnregisterNodeFleetResponse,
|
|
36
44
|
)
|
|
37
45
|
from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
|
|
38
46
|
SendNodeHeartbeatRequest,
|
|
@@ -69,6 +77,8 @@ GrpcResponse = TypeVar("GrpcResponse", bound=GrpcMessage)
|
|
|
69
77
|
GrpcAsyncFunction = Callable[[GrpcRequest], Awaitable[GrpcResponse]]
|
|
70
78
|
RestEndPoint = Callable[[Request], Awaitable[Response]]
|
|
71
79
|
|
|
80
|
+
routes = []
|
|
81
|
+
|
|
72
82
|
|
|
73
83
|
def rest_request_response(
|
|
74
84
|
grpc_request_type: type[GrpcRequest],
|
|
@@ -76,6 +86,7 @@ def rest_request_response(
|
|
|
76
86
|
"""Convert an async gRPC-based function into a RESTful HTTP endpoint."""
|
|
77
87
|
|
|
78
88
|
def decorator(func: GrpcAsyncFunction[GrpcRequest, GrpcResponse]) -> RestEndPoint:
|
|
89
|
+
|
|
79
90
|
async def wrapper(request: Request) -> Response:
|
|
80
91
|
_check_headers(request.headers)
|
|
81
92
|
|
|
@@ -91,6 +102,9 @@ def rest_request_response(
|
|
|
91
102
|
headers={"Content-Type": "application/protobuf"},
|
|
92
103
|
)
|
|
93
104
|
|
|
105
|
+
# Register route
|
|
106
|
+
path = f"/api/v0/fleet/{func.__name__.replace('_', '-')}"
|
|
107
|
+
routes.append(Route(path, wrapper, methods=["POST"]))
|
|
94
108
|
return wrapper
|
|
95
109
|
|
|
96
110
|
return decorator
|
|
@@ -116,8 +130,56 @@ async def delete_node(request: DeleteNodeRequest) -> DeleteNodeResponse:
|
|
|
116
130
|
return message_handler.delete_node(request=request, state=state)
|
|
117
131
|
|
|
118
132
|
|
|
133
|
+
@rest_request_response(RegisterNodeFleetRequest)
|
|
134
|
+
async def register_node(
|
|
135
|
+
request: RegisterNodeFleetRequest,
|
|
136
|
+
) -> RegisterNodeFleetResponse:
|
|
137
|
+
"""Register a node (Fleet API only)."""
|
|
138
|
+
# Get state from app
|
|
139
|
+
state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
|
|
140
|
+
|
|
141
|
+
# Handle message
|
|
142
|
+
return message_handler.register_node(request=request, state=state)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@rest_request_response(ActivateNodeRequest)
|
|
146
|
+
async def activate_node(
|
|
147
|
+
request: ActivateNodeRequest,
|
|
148
|
+
) -> ActivateNodeResponse:
|
|
149
|
+
"""Activate a node."""
|
|
150
|
+
# Get state from app
|
|
151
|
+
state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
|
|
152
|
+
|
|
153
|
+
# Handle message
|
|
154
|
+
return message_handler.activate_node(request=request, state=state)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@rest_request_response(DeactivateNodeRequest)
|
|
158
|
+
async def deactivate_node(
|
|
159
|
+
request: DeactivateNodeRequest,
|
|
160
|
+
) -> DeactivateNodeResponse:
|
|
161
|
+
"""Deactivate a node."""
|
|
162
|
+
# Get state from app
|
|
163
|
+
state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
|
|
164
|
+
|
|
165
|
+
# Handle message
|
|
166
|
+
return message_handler.deactivate_node(request=request, state=state)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@rest_request_response(UnregisterNodeFleetRequest)
|
|
170
|
+
async def unregister_node(
|
|
171
|
+
request: UnregisterNodeFleetRequest,
|
|
172
|
+
) -> UnregisterNodeFleetResponse:
|
|
173
|
+
"""Unregister a node (Fleet API only)."""
|
|
174
|
+
# Get state from app
|
|
175
|
+
state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
|
|
176
|
+
|
|
177
|
+
# Handle message
|
|
178
|
+
return message_handler.unregister_node(request=request, state=state)
|
|
179
|
+
|
|
180
|
+
|
|
119
181
|
@rest_request_response(PullMessagesRequest)
|
|
120
|
-
async def
|
|
182
|
+
async def pull_messages(request: PullMessagesRequest) -> PullMessagesResponse:
|
|
121
183
|
"""Pull PullMessages."""
|
|
122
184
|
# Get state from app
|
|
123
185
|
state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
|
|
@@ -128,7 +190,7 @@ async def pull_message(request: PullMessagesRequest) -> PullMessagesResponse:
|
|
|
128
190
|
|
|
129
191
|
|
|
130
192
|
@rest_request_response(PushMessagesRequest)
|
|
131
|
-
async def
|
|
193
|
+
async def push_messages(request: PushMessagesRequest) -> PushMessagesResponse:
|
|
132
194
|
"""Pull PushMessages."""
|
|
133
195
|
# Get state from app
|
|
134
196
|
state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
|
|
@@ -212,23 +274,6 @@ async def confirm_message_received(
|
|
|
212
274
|
)
|
|
213
275
|
|
|
214
276
|
|
|
215
|
-
routes = [
|
|
216
|
-
Route("/api/v0/fleet/create-node", create_node, methods=["POST"]),
|
|
217
|
-
Route("/api/v0/fleet/delete-node", delete_node, methods=["POST"]),
|
|
218
|
-
Route("/api/v0/fleet/pull-messages", pull_message, methods=["POST"]),
|
|
219
|
-
Route("/api/v0/fleet/push-messages", push_message, methods=["POST"]),
|
|
220
|
-
Route("/api/v0/fleet/pull-object", pull_object, methods=["POST"]),
|
|
221
|
-
Route("/api/v0/fleet/push-object", push_object, methods=["POST"]),
|
|
222
|
-
Route("/api/v0/fleet/send-node-heartbeat", send_node_heartbeat, methods=["POST"]),
|
|
223
|
-
Route("/api/v0/fleet/get-run", get_run, methods=["POST"]),
|
|
224
|
-
Route("/api/v0/fleet/get-fab", get_fab, methods=["POST"]),
|
|
225
|
-
Route(
|
|
226
|
-
"/api/v0/fleet/confirm-message-received",
|
|
227
|
-
confirm_message_received,
|
|
228
|
-
methods=["POST"],
|
|
229
|
-
),
|
|
230
|
-
]
|
|
231
|
-
|
|
232
277
|
app: Starlette = Starlette(
|
|
233
278
|
debug=False,
|
|
234
279
|
routes=routes,
|
|
@@ -33,7 +33,7 @@ from flwr.clientapp.client_app import ClientApp, ClientAppException, LoadClientA
|
|
|
33
33
|
from flwr.clientapp.utils import get_load_client_app_fn
|
|
34
34
|
from flwr.common import Message
|
|
35
35
|
from flwr.common.constant import (
|
|
36
|
-
|
|
36
|
+
HEARTBEAT_INTERVAL_INF,
|
|
37
37
|
NOOP_FLWR_AID,
|
|
38
38
|
NUM_PARTITIONS_KEY,
|
|
39
39
|
PARTITION_ID_KEY,
|
|
@@ -62,10 +62,10 @@ def _register_nodes(
|
|
|
62
62
|
# use random bytes as public key
|
|
63
63
|
NOOP_FLWR_AID,
|
|
64
64
|
secrets.token_bytes(32),
|
|
65
|
-
heartbeat_interval=
|
|
65
|
+
heartbeat_interval=HEARTBEAT_INTERVAL_INF,
|
|
66
66
|
)
|
|
67
67
|
state.acknowledge_node_heartbeat(
|
|
68
|
-
node_id=node_id, heartbeat_interval=
|
|
68
|
+
node_id=node_id, heartbeat_interval=HEARTBEAT_INTERVAL_INF
|
|
69
69
|
)
|
|
70
70
|
nodes_mapping[node_id] = i
|
|
71
71
|
log(DEBUG, "Registered %i nodes", len(nodes_mapping))
|
|
@@ -28,7 +28,7 @@ from typing import Optional
|
|
|
28
28
|
from flwr.common import Context, Message, log, now
|
|
29
29
|
from flwr.common.constant import (
|
|
30
30
|
FLWR_APP_TOKEN_LENGTH,
|
|
31
|
-
|
|
31
|
+
HEARTBEAT_INTERVAL_INF,
|
|
32
32
|
HEARTBEAT_PATIENCE,
|
|
33
33
|
MESSAGE_TTL_TOLERANCE,
|
|
34
34
|
NODE_ID_NUM_BYTES,
|
|
@@ -386,6 +386,47 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
|
386
386
|
# Set online_until to current timestamp on deletion, if it is in the future
|
|
387
387
|
node.online_until = min(node.online_until, current.timestamp())
|
|
388
388
|
|
|
389
|
+
def activate_node(self, node_id: int, heartbeat_interval: float) -> bool:
|
|
390
|
+
"""Activate the node with the specified `node_id`."""
|
|
391
|
+
with self.lock:
|
|
392
|
+
self._check_and_tag_offline_nodes(node_ids=[node_id])
|
|
393
|
+
|
|
394
|
+
# Check if the node exists
|
|
395
|
+
if not (node := self.nodes.get(node_id)):
|
|
396
|
+
return False
|
|
397
|
+
|
|
398
|
+
# Only activate if the node is currently registered or offline
|
|
399
|
+
current_dt = now()
|
|
400
|
+
if node.status in (NodeStatus.REGISTERED, NodeStatus.OFFLINE):
|
|
401
|
+
node.status = NodeStatus.ONLINE
|
|
402
|
+
node.last_activated_at = current_dt.isoformat()
|
|
403
|
+
node.online_until = (
|
|
404
|
+
current_dt.timestamp() + HEARTBEAT_PATIENCE * heartbeat_interval
|
|
405
|
+
)
|
|
406
|
+
node.heartbeat_interval = heartbeat_interval
|
|
407
|
+
return True
|
|
408
|
+
return False
|
|
409
|
+
|
|
410
|
+
def deactivate_node(self, node_id: int) -> bool:
|
|
411
|
+
"""Deactivate the node with the specified `node_id`."""
|
|
412
|
+
with self.lock:
|
|
413
|
+
self._check_and_tag_offline_nodes(node_ids=[node_id])
|
|
414
|
+
|
|
415
|
+
# Check if the node exists
|
|
416
|
+
if not (node := self.nodes.get(node_id)):
|
|
417
|
+
return False
|
|
418
|
+
|
|
419
|
+
# Only deactivate if the node is currently online
|
|
420
|
+
current_dt = now()
|
|
421
|
+
if node.status == NodeStatus.ONLINE:
|
|
422
|
+
node.status = NodeStatus.OFFLINE
|
|
423
|
+
node.last_deactivated_at = current_dt.isoformat()
|
|
424
|
+
|
|
425
|
+
# Set online_until to current timestamp
|
|
426
|
+
node.online_until = current_dt.timestamp()
|
|
427
|
+
return True
|
|
428
|
+
return False
|
|
429
|
+
|
|
389
430
|
def get_nodes(self, run_id: int) -> set[int]:
|
|
390
431
|
"""Return all available nodes.
|
|
391
432
|
|
|
@@ -600,7 +641,7 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
|
600
641
|
current = now()
|
|
601
642
|
run_record = self.run_ids[run_id]
|
|
602
643
|
if new_status.status in (Status.STARTING, Status.RUNNING):
|
|
603
|
-
run_record.heartbeat_interval =
|
|
644
|
+
run_record.heartbeat_interval = HEARTBEAT_INTERVAL_INF
|
|
604
645
|
run_record.active_until = (
|
|
605
646
|
current.timestamp() + run_record.heartbeat_interval
|
|
606
647
|
)
|
|
@@ -139,6 +139,45 @@ class LinkState(CoreState): # pylint: disable=R0904
|
|
|
139
139
|
def delete_node(self, owner_aid: str, node_id: int) -> None:
|
|
140
140
|
"""Remove `node_id` from the link state."""
|
|
141
141
|
|
|
142
|
+
@abc.abstractmethod
|
|
143
|
+
def activate_node(self, node_id: int, heartbeat_interval: float) -> bool:
|
|
144
|
+
"""Activate the node with the specified `node_id`.
|
|
145
|
+
|
|
146
|
+
Transitions the node status to "online". The transition will fail
|
|
147
|
+
if the current status is not "registered" or "offline".
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
node_id : int
|
|
152
|
+
The identifier of the node to activate.
|
|
153
|
+
heartbeat_interval : float
|
|
154
|
+
The interval (in seconds) from the current timestamp within which
|
|
155
|
+
the next heartbeat from this node is expected to be received.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
bool
|
|
160
|
+
True if the status transition was successful, False otherwise.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
@abc.abstractmethod
|
|
164
|
+
def deactivate_node(self, node_id: int) -> bool:
|
|
165
|
+
"""Deactivate the node with the specified `node_id`.
|
|
166
|
+
|
|
167
|
+
Transitions the node status to "offline". The transition will fail
|
|
168
|
+
if the current status is not "online".
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
node_id : int
|
|
173
|
+
The identifier of the node to deactivate.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
bool
|
|
178
|
+
True if the status transition was successful, False otherwise.
|
|
179
|
+
"""
|
|
180
|
+
|
|
142
181
|
@abc.abstractmethod
|
|
143
182
|
def get_nodes(self, run_id: int) -> set[int]:
|
|
144
183
|
"""Retrieve all currently stored node IDs as a set.
|
|
@@ -27,7 +27,7 @@ from typing import Any, Optional, Union, cast
|
|
|
27
27
|
from flwr.common import Context, Message, Metadata, log, now
|
|
28
28
|
from flwr.common.constant import (
|
|
29
29
|
FLWR_APP_TOKEN_LENGTH,
|
|
30
|
-
|
|
30
|
+
HEARTBEAT_INTERVAL_INF,
|
|
31
31
|
HEARTBEAT_PATIENCE,
|
|
32
32
|
MESSAGE_TTL_TOLERANCE,
|
|
33
33
|
NODE_ID_NUM_BYTES,
|
|
@@ -618,6 +618,61 @@ class SqliteLinkState(LinkState, SqliteMixin): # pylint: disable=R0904
|
|
|
618
618
|
"deletion attempt."
|
|
619
619
|
)
|
|
620
620
|
|
|
621
|
+
def activate_node(self, node_id: int, heartbeat_interval: float) -> bool:
|
|
622
|
+
"""Activate the node with the specified `node_id`."""
|
|
623
|
+
with self.conn:
|
|
624
|
+
self._check_and_tag_offline_nodes([node_id])
|
|
625
|
+
|
|
626
|
+
# Only activate if the node is currently registered or offline
|
|
627
|
+
current_dt = now()
|
|
628
|
+
query = """
|
|
629
|
+
UPDATE node
|
|
630
|
+
SET status = ?,
|
|
631
|
+
last_activated_at = ?,
|
|
632
|
+
online_until = ?,
|
|
633
|
+
heartbeat_interval = ?
|
|
634
|
+
WHERE node_id = ? AND status in (?, ?)
|
|
635
|
+
RETURNING node_id
|
|
636
|
+
"""
|
|
637
|
+
params = (
|
|
638
|
+
NodeStatus.ONLINE,
|
|
639
|
+
current_dt.isoformat(),
|
|
640
|
+
current_dt.timestamp() + HEARTBEAT_PATIENCE * heartbeat_interval,
|
|
641
|
+
heartbeat_interval,
|
|
642
|
+
uint64_to_int64(node_id),
|
|
643
|
+
NodeStatus.REGISTERED,
|
|
644
|
+
NodeStatus.OFFLINE,
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
row = self.conn.execute(query, params).fetchone()
|
|
648
|
+
return row is not None
|
|
649
|
+
|
|
650
|
+
def deactivate_node(self, node_id: int) -> bool:
|
|
651
|
+
"""Deactivate the node with the specified `node_id`."""
|
|
652
|
+
with self.conn:
|
|
653
|
+
self._check_and_tag_offline_nodes([node_id])
|
|
654
|
+
|
|
655
|
+
# Only deactivate if the node is currently online
|
|
656
|
+
current_dt = now()
|
|
657
|
+
query = """
|
|
658
|
+
UPDATE node
|
|
659
|
+
SET status = ?,
|
|
660
|
+
last_deactivated_at = ?,
|
|
661
|
+
online_until = ?
|
|
662
|
+
WHERE node_id = ? AND status = ?
|
|
663
|
+
RETURNING node_id
|
|
664
|
+
"""
|
|
665
|
+
params = (
|
|
666
|
+
NodeStatus.OFFLINE,
|
|
667
|
+
current_dt.isoformat(),
|
|
668
|
+
current_dt.timestamp(),
|
|
669
|
+
uint64_to_int64(node_id),
|
|
670
|
+
NodeStatus.ONLINE,
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
row = self.conn.execute(query, params).fetchone()
|
|
674
|
+
return row is not None
|
|
675
|
+
|
|
621
676
|
def get_nodes(self, run_id: int) -> set[int]:
|
|
622
677
|
"""Retrieve all currently stored node IDs as a set.
|
|
623
678
|
|
|
@@ -927,7 +982,7 @@ class SqliteLinkState(LinkState, SqliteMixin): # pylint: disable=R0904
|
|
|
927
982
|
# when switching to starting or running
|
|
928
983
|
current = now()
|
|
929
984
|
if new_status.status in (Status.STARTING, Status.RUNNING):
|
|
930
|
-
heartbeat_interval =
|
|
985
|
+
heartbeat_interval = HEARTBEAT_INTERVAL_INF
|
|
931
986
|
active_until = current.timestamp() + heartbeat_interval
|
|
932
987
|
else:
|
|
933
988
|
heartbeat_interval = 0
|
{flwr_nightly-1.23.0.dev20251028.dist-info → flwr_nightly-1.23.0.dev20251030.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.23.0.
|
|
3
|
+
Version: 1.23.0.dev20251030
|
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
|
{flwr_nightly-1.23.0.dev20251028.dist-info → flwr_nightly-1.23.0.dev20251030.dist-info}/RECORD
RENAMED
|
@@ -98,7 +98,7 @@ flwr/client/grpc_adapter_client/__init__.py,sha256=RQWP5mFPROLHKgombiRvPXVWSoVrQ
|
|
|
98
98
|
flwr/client/grpc_adapter_client/connection.py,sha256=JGv02EjSOAG1E5BRUD4lwXc1LLiYJ0OhInvp31qx1cU,4404
|
|
99
99
|
flwr/client/grpc_rere_client/__init__.py,sha256=i7iS0Lt8B7q0E2L72e4F_YrKm6ClRKnd71PNA6PW2O0,752
|
|
100
100
|
flwr/client/grpc_rere_client/connection.py,sha256=3_CI7wx5ZrkGrVCXjDKiUGX9aJP9Zm9yl4iqSBAmH3U,13100
|
|
101
|
-
flwr/client/grpc_rere_client/grpc_adapter.py,sha256=
|
|
101
|
+
flwr/client/grpc_rere_client/grpc_adapter.py,sha256=YwsZOvFgCd1JdiHnl1Cc18vKOiGwWSSJ_9-ZXwlGZvs,7900
|
|
102
102
|
flwr/client/grpc_rere_client/node_auth_client_interceptor.py,sha256=EdTyb5ThFrpkeyXy9_nTKH7E9GnfAdXjcNZ_-uxt1AA,2410
|
|
103
103
|
flwr/client/message_handler/__init__.py,sha256=0lyljDVqre3WljiZbPcwCCf8GiIaSVI_yo_ylEyPwSE,719
|
|
104
104
|
flwr/client/message_handler/message_handler.py,sha256=X9SXX6et97Lw9_DGD93HKsEBGNjXClcFgc_5aLK0oiU,6541
|
|
@@ -126,7 +126,7 @@ flwr/common/__init__.py,sha256=5GCLVk399Az_rTJHNticRlL0Sl_oPw_j5_LuFKfX7-M,4171
|
|
|
126
126
|
flwr/common/address.py,sha256=9JucdTwlc-jpeJkRKeUboZoacUtErwSVtnDR9kAtLqE,4119
|
|
127
127
|
flwr/common/args.py,sha256=Nq2u4yePbkSY0CWFamn0hZY6Rms8G1xYDeDGIcLIITE,5849
|
|
128
128
|
flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
|
|
129
|
-
flwr/common/constant.py,sha256=
|
|
129
|
+
flwr/common/constant.py,sha256=du408_dylfRx6H-YXzpc_2BvpI1bnFUcz0RiQryM_n8,10254
|
|
130
130
|
flwr/common/context.py,sha256=Be8obQR_OvEDy1OmshuUKxGRQ7Qx89mf5F4xlhkR10s,2407
|
|
131
131
|
flwr/common/date.py,sha256=1ZT2cRSpC2DJqprOVTLXYCR_O2_OZR0zXO_brJ3LqWc,1554
|
|
132
132
|
flwr/common/differential_privacy.py,sha256=FdlpdpPl_H_2HJa8CQM1iCUGBBQ5Dc8CzxmHERM-EoE,6148
|
|
@@ -203,10 +203,10 @@ flwr/proto/fab_pb2.py,sha256=AQAUJmTt31Kq-lJQid3CJpQPrply2-OA_aSHokpVF84,2070
|
|
|
203
203
|
flwr/proto/fab_pb2.pyi,sha256=GOxeDi-UYzQVwIEzRk9YkzkriowmNdibZu3jMMtBPFo,3273
|
|
204
204
|
flwr/proto/fab_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
205
205
|
flwr/proto/fab_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
|
|
206
|
-
flwr/proto/fleet_pb2.py,sha256=
|
|
207
|
-
flwr/proto/fleet_pb2.pyi,sha256
|
|
208
|
-
flwr/proto/fleet_pb2_grpc.py,sha256=
|
|
209
|
-
flwr/proto/fleet_pb2_grpc.pyi,sha256=
|
|
206
|
+
flwr/proto/fleet_pb2.py,sha256=FyQJ-FCsYJLwQZeYrs_p6Y_OgA9jPoxGHyIvWeukXxU,7171
|
|
207
|
+
flwr/proto/fleet_pb2.pyi,sha256=-cKglysTm0FPkAqdD2cKzfxFhb1rP_z5aOD5RSPrFxU,11209
|
|
208
|
+
flwr/proto/fleet_pb2_grpc.py,sha256=ss_fFtTJuVSzYLxWsW5Fn6XfUBnJIKV7BTBX5vyhPMY,24083
|
|
209
|
+
flwr/proto/fleet_pb2_grpc.pyi,sha256=WJIV7EmoePxY6CaYF1cj3oZ2ss6E1pt7Zs5C6ehz69c,6461
|
|
210
210
|
flwr/proto/grpcadapter_pb2.py,sha256=PJ8DtfeV29g_y4Z3aNZlSZocLqSxeLmTsYCdOZDYCiE,1843
|
|
211
211
|
flwr/proto/grpcadapter_pb2.pyi,sha256=AR77gDsF6f8zqSIQp3877DUd7S8lP95lFak5Ir_WPkw,1716
|
|
212
212
|
flwr/proto/grpcadapter_pb2_grpc.py,sha256=rRNuNES5nBugUZWfeA8oAy8dMHgzqU_PF1srTseo3b8,2634
|
|
@@ -298,29 +298,29 @@ flwr/server/strategy/strategy.py,sha256=n4r52i5gK4KGToZvcJUeWuEif1tuI0HZUT3YJPTC
|
|
|
298
298
|
flwr/server/superlink/__init__.py,sha256=GNSuJ4-N6Z8wun2iZNlXqENt5beUyzC0Gi_tN396bbM,707
|
|
299
299
|
flwr/server/superlink/fleet/__init__.py,sha256=Uiwr33yfW_eL-pEfj80c_JUhIKRkCPsN1JSs2v4aglU,711
|
|
300
300
|
flwr/server/superlink/fleet/grpc_adapter/__init__.py,sha256=fUu1V63YrzjxAOZnBJx99WjuD4Mro7dJIFH-1V4NLV8,742
|
|
301
|
-
flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py,sha256=
|
|
301
|
+
flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py,sha256=zFWNbVnT-HMHJiZO154ciaT2GeA8TcFpwEJuNI793eU,5879
|
|
302
302
|
flwr/server/superlink/fleet/grpc_bidi/__init__.py,sha256=dOM49q1b9MrtUr5jldjEnQ38NhcUyYs-zC3gsJb1TtI,735
|
|
303
303
|
flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py,sha256=UKEp-3YBaTvNt7vKZW7KLgK5xsAiO7jxU-omG7CaO_s,6021
|
|
304
304
|
flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py,sha256=KouR9PUcrPmMtoLooF4O9SRAwIvfiroo8mPmqUc2EZc,6485
|
|
305
305
|
flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py,sha256=iSf0mbBAlig7G6subQwBSVjcUCgSihONKdZ1RmQPTOk,4887
|
|
306
306
|
flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=OsS-6GgCIzMMZDVu5Y-OKjynHVUrpdc_5OrtuB-IbU0,5174
|
|
307
307
|
flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=ahDJJ1e-lDxBpeBMgPk7YZt2wB38_QltcpOC0gLbpFs,758
|
|
308
|
-
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=
|
|
308
|
+
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=j81X6yVdhWZpdAOKEr3DQpCrd4ETnmdniQ12IMThymU,16226
|
|
309
309
|
flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py,sha256=UWeFQNBW2pGBRVN36HodHcv7bKTgMmdToh96Lhqip1M,5411
|
|
310
310
|
flwr/server/superlink/fleet/message_handler/__init__.py,sha256=fHsRV0KvJ8HtgSA4_YBsEzuhJLjO8p6xx4aCY2oE1p4,731
|
|
311
|
-
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=
|
|
311
|
+
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=_ZTKTloYgC3axwJYnKUZ9v6Va6psH_PEux-UPVtArzg,11021
|
|
312
312
|
flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=Lzc93nA7tDqoy-zRUaPG316oqFiZX1HUCL5ELaXY_xw,735
|
|
313
|
-
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=
|
|
313
|
+
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=Xcm_o41j0iHCBraLglZc2dY47FSiY8egmcznWUsrLYY,10383
|
|
314
314
|
flwr/server/superlink/fleet/vce/__init__.py,sha256=XOKbAWOzlCqEOQ3M2cBYkH7HKA7PxlbCJMunt-ty-DY,784
|
|
315
315
|
flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=PPH89Yqd1XKm-sRJN6R0WQlKT_b4v54Kzl2yzHAFzM8,1437
|
|
316
316
|
flwr/server/superlink/fleet/vce/backend/backend.py,sha256=cSrHZ5SjCCvy4vI0pgsyjtx3cDMuMQve8KcKkK-dWWo,2196
|
|
317
317
|
flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=cBZYTmfiAsb1HmVUmOQXYLU-UJmJTFWkj1wW4RYRDuc,7218
|
|
318
|
-
flwr/server/superlink/fleet/vce/vce_api.py,sha256=
|
|
318
|
+
flwr/server/superlink/fleet/vce/vce_api.py,sha256=7dJdxRjXttXfZSflZHd-TwX9278ZzrdJxmmWbRiEH_I,13389
|
|
319
319
|
flwr/server/superlink/linkstate/__init__.py,sha256=OtsgvDTnZLU3k0sUbkHbqoVwW6ql2FDmb6uT6DbNkZo,1064
|
|
320
|
-
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=
|
|
321
|
-
flwr/server/superlink/linkstate/linkstate.py,sha256=
|
|
320
|
+
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=l3n1HJfIOYYSAuSeD-HHDjnL490HxH5NTSj962OSDAk,31829
|
|
321
|
+
flwr/server/superlink/linkstate/linkstate.py,sha256=DabTgFFissJhT_MCPiluz5eFrS3c412q8FnRqQrAidY,15945
|
|
322
322
|
flwr/server/superlink/linkstate/linkstate_factory.py,sha256=KVBpc8UxVrJCQ7IkOjVZVWb-kqWx08AGVi7qXzZS190,2126
|
|
323
|
-
flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=
|
|
323
|
+
flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=Herm-aUM9Wmpg9IV7c25UdXGmPqL8sWdVOOCrWxha_k,47528
|
|
324
324
|
flwr/server/superlink/linkstate/utils.py,sha256=ZtyqSo4HzGrHXW3Wn_4irYMpIiq4onNI2XCIVOOJmJM,13971
|
|
325
325
|
flwr/server/superlink/serverappio/__init__.py,sha256=Fy4zJuoccZe5mZSEIpOmQvU6YeXFBa1M4eZuXXmJcn8,717
|
|
326
326
|
flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=-I7kBbr4w4ZVYwBZoAIle-xHKthFnZrsVfxa6WR8uxA,2310
|
|
@@ -433,7 +433,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
|
|
|
433
433
|
flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
|
|
434
434
|
flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=ZvKosLV7GN1_fOF-tOmhqFQysYQywCysRc-m23DVIWA,10265
|
|
435
435
|
flwr/supernode/start_client_internal.py,sha256=wKqh9-_rQYi7JFKpYBLRiUeq9YJUSyUd9b-SnVnuvwI,21567
|
|
436
|
-
flwr_nightly-1.23.0.
|
|
437
|
-
flwr_nightly-1.23.0.
|
|
438
|
-
flwr_nightly-1.23.0.
|
|
439
|
-
flwr_nightly-1.23.0.
|
|
436
|
+
flwr_nightly-1.23.0.dev20251030.dist-info/METADATA,sha256=yDbmaIH946MJMvXaMPddPZREsfS76qhbPVOMP8JA2b4,14559
|
|
437
|
+
flwr_nightly-1.23.0.dev20251030.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
438
|
+
flwr_nightly-1.23.0.dev20251030.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
|
|
439
|
+
flwr_nightly-1.23.0.dev20251030.dist-info/RECORD,,
|
{flwr_nightly-1.23.0.dev20251028.dist-info → flwr_nightly-1.23.0.dev20251030.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|