blob: f6900dfc91e007003e8fb912479fa37878764e6c [file] [log] [blame]
Serge Bazanski6bd41592021-08-23 13:18:37 +02001syntax = "proto3";
2package metropolis.proto.api;
3option go_package = "source.monogon.dev/metropolis/proto/api";
4
Mateusz Zalega944cb532022-06-20 16:54:17 +02005import "google/protobuf/duration.proto";
6
Tim Windelschmidt9f21f532024-05-07 15:14:20 +02007import "osbase/logtree/proto/logtree.proto";
Serge Bazanskibc671d02021-10-05 17:53:32 +02008import "metropolis/proto/common/common.proto";
Serge Bazanski9ffa1f92021-09-01 15:42:23 +02009import "metropolis/proto/ext/authorization.proto";
10
Serge Bazanski56114472021-10-11 14:47:54 +020011// Management service available to Cluster Managers, allowing operational work
12// to be performed on the cluster (eg. adding nodes, retrieving information
13// about a running cluster, etc.).
Serge Bazanski6bd41592021-08-23 13:18:37 +020014service Management {
15 // GetRegisterTicket retrieves the current RegisterTicket which is required
16 // for new nodes to register into the cluster. Presenting this ticket on
17 // registration does not automatically grant access to arbitrary node
18 // registration. Instead, it is used to guard the API surface of the
19 // Register RPC from potential denial of service attacks, and can be
20 // regenerated at any time in case it leaks.
Serge Bazanski9ffa1f92021-09-01 15:42:23 +020021 rpc GetRegisterTicket(GetRegisterTicketRequest) returns (GetRegisterTicketResponse) {
22 option (metropolis.proto.ext.authorization) = {
23 need: PERMISSION_GET_REGISTER_TICKET
24 };
25 }
Serge Bazanski56114472021-10-11 14:47:54 +020026
Serge Bazanskibc671d02021-10-05 17:53:32 +020027 // GetClusterInfo retrieves publicly available summary information about
28 // this cluster, notably data required for nodes to register into a cluster
29 // or join it (other than the Register Ticket, which is gated by an
30 // additional permission).
31 rpc GetClusterInfo(GetClusterInfoRequest) returns (GetClusterInfoResponse) {
32 option (metropolis.proto.ext.authorization) = {
33 need: PERMISSION_READ_CLUSTER_STATUS
34 };
35 }
Serge Bazanski56114472021-10-11 14:47:54 +020036
37 // GetNodes retrieves information about nodes in the cluster. Currently,
38 // it returns all available data about all nodes.
39 rpc GetNodes(GetNodesRequest) returns (stream Node) {
40 option (metropolis.proto.ext.authorization) = {
41 need: PERMISSION_READ_CLUSTER_STATUS
42 };
43 }
Serge Bazanski1612d4b2021-11-12 13:54:15 +010044
45 // ApproveNode progresses a node's registration process by changing its state
46 // in the cluster from NEW to STANDBY, if not yet STANDBY. This is required
47 // for the node to fully become part of the cluster (ie. have an UP state),
48 // and is required to be called by a manager manually.
49 //
50 // Managers can find out what nodes require approval by performing
51 // a GetNodes call and filtering for nodes in the NEW state. This call is
52 // idempotent and can be executed multiple times, and is a no-op if the node
53 // is already in the STANDBY or even UP states.
54 //
55 // In the future, approval process will be governed by cluster policy, but
56 // currently any node can be approved by a manager, and the manager is
57 // responsible for performing an out-of-band attestation of the node being/
58 // approved (eg. by verifying that the node that is being approved has the
59 // same public key as what the registering node displays in its startup
60 // logs).
61 rpc ApproveNode(ApproveNodeRequest) returns (ApproveNodeResponse) {
62 option (metropolis.proto.ext.authorization) = {
63 need: PERMISSION_APPROVE_NODE
64 };
65 }
Mateusz Zalegabb2edbe2022-06-08 11:57:09 +020066
67 // UpdateNodeRoles updates a single node's roles.
68 rpc UpdateNodeRoles(UpdateNodeRolesRequest) returns (UpdateNodeRolesResponse) {
69 option (metropolis.proto.ext.authorization) = {
70 need: PERMISSION_UPDATE_NODE_ROLES
71 };
72 }
Serge Bazanski8456ddf2023-10-30 18:56:59 +010073
74 // Decommissioning a node takes it from UP, through
75 //
76 // 1. DECOMMISSION_REQUESTED
77 // The node will detect this state on the cluster and begin a cleanup
78 // process which consists of removing either key material or zeroing
79 // out the data partition, depending on cluster policy. It will report
80 // to the cluster that it has begun the process, which will take it to
81 // the next state:
82 //
83 // 2. DECOMMISSIONING
84 // The node will continue cleanup. After cleanup is successful, it will
85 // report back to the cluster which will take it to DECOMMISSIONED. The
86 // node then reboots, and never comes back.
87 //
88 // 3. DECOMMISSIONED
89 // The node can be removed with a subsequent DeleteNode call.
90 //
91 // TODO(q3k): implement this, possibly iron out the state machine involved.
92 //
93 // The node cannot have any roles assigned to it when it is being
94 // decommissioned: none may be assigned when the decommissioning process is
95 // requested, and none may be added to it while it is decommissioning.
96 rpc DecommissionNode(DecommissionNodeRequest) returns (DecommissionNodeResponse) {
97 option (metropolis.proto.ext.authorization) = {
98 need: PERMISSION_DECOMMISSION_NODE
99 };
100 }
101
102 // Delete a node from the cluster. By default the node must be in the
103 // DECOMMISSIONED state and may not have any roles assigned. However, some
104 // safety bypasses are available for nodes which have become unavailable and
105 // thus cannot be decommissioned correctly - see the request documentation
106 // for more details.
107 rpc DeleteNode(DeleteNodeRequest) returns (DeleteNodeResponse) {
108 option (metropolis.proto.ext.authorization) = {
109 need: PERMISSION_DELETE_NODE
110 };
111 }
Serge Bazanski1f789542024-05-22 14:01:50 +0200112
113 // Add, update or remove labels from a given node. The given node must exist,
114 // but can be in any state.
115 rpc UpdateNodeLabels(UpdateNodeLabelsRequest) returns (UpdateNodeLabelsResponse) {
116 option (metropolis.proto.ext.authorization) = {
117 need: PERMISSION_UPDATE_NODE_LABELS
118 };
119 }
Serge Bazanski6bd41592021-08-23 13:18:37 +0200120}
121
122message GetRegisterTicketRequest {
123}
124
125message GetRegisterTicketResponse {
126 // Opaque bytes that comprise the RegisterTicket.
127 bytes ticket = 1;
Serge Bazanski2893e982021-09-09 13:06:16 +0200128}
Serge Bazanskibc671d02021-10-05 17:53:32 +0200129
130message GetClusterInfoRequest {
131}
132
133message GetClusterInfoResponse {
134 // cluster_directory contains information about individual nodes in the
135 // cluster that can be used to dial the cluster's services.
136 metropolis.proto.common.ClusterDirectory cluster_directory = 1;
Serge Bazanski2f58ac02021-10-05 11:47:20 +0200137
Serge Bazanskifbd38e22021-10-08 14:41:16 +0200138 // ca_certificate is the x509 DER encoded CA certificate of the cluster.
139 bytes ca_certificate = 2;
Serge Bazanski5df62ba2023-03-22 17:56:46 +0100140
141 metropolis.proto.common.ClusterConfiguration cluster_configuration = 3;
Serge Bazanskibc671d02021-10-05 17:53:32 +0200142}
Serge Bazanski56114472021-10-11 14:47:54 +0200143
144message GetNodesRequest {
Mateusz Zalega955e46e2022-05-27 18:00:50 +0200145 // filter is a CEL expression used to limit the count of GetNodes results.
146 // Each processed node protobuf message is exposed to the filter as
147 // "node" variable, while related state and health enum constants are
148 // anchored in the root namespace, eg. NODE_STATE_UP, or HEARTBEAT_TIMEOUT.
149 // A node is returned each time the expression is evaluated as true. If
150 // empty, all nodes are returned.
151 string filter = 1;
Serge Bazanski56114472021-10-11 14:47:54 +0200152}
153
154// Node in a Metropolis cluster, streamed by Management.GetNodes. For each node
155// in the cluster, this message will be emitted and will contain information
156// about that node.
157//
158// The fields contained are node fields that PERMISSION_READ_CLUSTER_STATUS
159// allows access to, ie. 'non-private' fields, ones that might be internal to
160// the cluster and possibly considered sensitive information about the
161// infrastructure, but whose knowledge does not allow to escalate privileges
162// within the cluster.
163message Node {
164 // Raw Ed25519 public key of this node, which can be used to generate
165 // the node's ID. This is always set.
166 bytes pubkey = 1;
Serge Bazanski30fd1542023-03-29 14:19:02 +0200167 // Node ID calculated from pubkey, ie. 'metropolis-123456'.
168 string id = 7;
Serge Bazanski56114472021-10-11 14:47:54 +0200169 // State of the node from the point of view of the cluster. This is
170 // always set.
171 metropolis.proto.common.NodeState state = 2;
172 // Last reported status by the Node, absent if a node hasn't yet reported
173 // its status.
174 metropolis.proto.common.NodeStatus status = 3;
175 // Roles assigned by the cluster. This is always set.
176 metropolis.proto.common.NodeRoles roles = 4;
Serge Bazanski1612d4b2021-11-12 13:54:15 +0100177
Mateusz Zalega32b19292022-05-17 13:26:55 +0200178 // Health describes node's health as seen from the cluster perspective.
179 enum Health {
180 INVALID = 0;
181 // UNKNOWN is used whenever there were no heartbeats received from a
182 // given node AND too little time has passed since last Curator leader
183 // election to know whether the node is actually timing out. UNKNOWN
184 // is also returned for nodes which NodeState does not equal
185 // NODE_STATE_UP.
186 UNKNOWN = 1;
187 // HEALTHY describes nodes that have sent a heartbeat recently.
188 HEALTHY = 2;
189 // HEARTBEAT_TIMEOUT describes nodes that have not sent a heartbeat in
190 // the interval specified by curator.HeartbeatTimeout.
191 HEARTBEAT_TIMEOUT = 3;
192 }
193 Health health = 5;
Mateusz Zalega2175ec92022-06-13 09:29:09 +0200194 // time_since_heartbeat is the duration since the last of the node's
195 // heartbeats was received, expressed in nanoseconds. It is only valid with
196 // the health status of either HEALTHY or HEARTBEAT_TIMEOUT.
Mateusz Zalega944cb532022-06-20 16:54:17 +0200197 google.protobuf.Duration time_since_heartbeat = 6;
Serge Bazanskie4a4ce12023-03-22 18:29:54 +0100198
199 // tpm_usage describes whether this node has a TPM 2.0 and whether it is
200 // being actively used as part of its membership in the Metropolis cluster.
201 //
202 // Currently, the TPM 2.0 is only used to seal the local part of the disk
203 // encryption key and the early join credentials of the node. Depending on
204 // future cluster configuration settings, this might also indicate that the
205 // node has actually passed high assurance hardware attestation against the
206 // cluster.
207 metropolis.proto.common.NodeTPMUsage tpm_usage = 8;
Serge Bazanski1f789542024-05-22 14:01:50 +0200208
209 // Labels attached to the node.
210 metropolis.proto.common.NodeLabels labels = 9;
Mateusz Zalega32b19292022-05-17 13:26:55 +0200211}
Serge Bazanski1612d4b2021-11-12 13:54:15 +0100212
213message ApproveNodeRequest {
214 // Raw public key of the node being approved, has to correspond to a node
215 // currently in the cluster.
216 bytes pubkey = 1;
217}
218
219message ApproveNodeResponse {
Mateusz Zalega32b19292022-05-17 13:26:55 +0200220}
Mateusz Zalegabb2edbe2022-06-08 11:57:09 +0200221
222// UpdateNodeRolesRequest updates roles of a single node matching pubkey. All
223// role fields are optional, and no change will result if they're either unset
224// or if their value matches existing state.
225message UpdateNodeRolesRequest {
Mateusz Zalega9c315f12022-08-11 16:31:22 +0200226 // node uniquely identifies the node subject to this request.
227 oneof node {
228 // pubkey is the Ed25519 public key of this node, which can be used to
229 // generate the node's ID.
230 bytes pubkey = 1;
231 // id is the human-readable identifier of the node, based on its public
232 // key.
233 string id = 4;
234 }
Mateusz Zalegabb2edbe2022-06-08 11:57:09 +0200235
Serge Bazanski15f7f632023-03-14 17:17:20 +0100236 // kubernetesController adjusts the appropriate role when set.
Mateusz Zalegabb2edbe2022-06-08 11:57:09 +0200237 optional bool kubernetesWorker = 2;
Serge Bazanski15f7f632023-03-14 17:17:20 +0100238 // kubernetesController adjusts the appropriate role when set. Nodes performing
239 // this role must also be consensus members.
240 optional bool kubernetesController = 5;
Mateusz Zalegabb2edbe2022-06-08 11:57:09 +0200241 optional bool consensusMember = 3;
242}
243
244message UpdateNodeRolesResponse {
245}
Serge Bazanskib40c0082023-03-29 14:28:04 +0200246
Serge Bazanski8456ddf2023-10-30 18:56:59 +0100247message DecommissionNodeRequest {
248 // node uniquely identifies the node subject to this request.
249 oneof node {
250 // pubkey is the Ed25519 public key of this node, which can be used to
251 // generate the node's ID.
252 bytes pubkey = 1;
253 // id is the human-readable identifier of the node, based on its public
254 // key.
255 string id = 4;
256 }
257}
258
259message DecommissionNodeResponse {
260}
261
262message DeleteNodeRequest {
263 // node uniquely identifies the node subject to this request.
264 oneof node {
265 // pubkey is the Ed25519 public key of this node, which can be used to
266 // generate the node's ID.
267 bytes pubkey = 1;
268 // id is the human-readable identifier of the node, based on its public
269 // key.
270 string id = 2;
271 }
272
273 message SafetyBypassHasRoles {
274 }
275 // If set, safety_bypass_has_roles allows the removal of nodes which still have
276 // roles assigned.
277 //
278 // Danger: removing nodes which still have roles assigned might leave the
279 // cluster in an inconsistent state. Unassigning roles from a nodes via
280 // UpdateNodeRoles ensures consistency.
281 //
282 // It's also advised to never use this option in automated workflows, as this
283 // prevents a runaway automation from removing nodes that are still used for
284 // actual work.
285 //
286 // Nodes which broke down or otherwise become unreachable shouldn't need to
287 // enable this option, as unassigning the role from a node does not require it
288 // to be healthy.
289 //
290 // A short summary of how to deal with possible inconsistencies after removing
291 // a node with roles still assigned:
292 //
293 // 1. KubernetesWorker: remove the node from the Kubernetes cluster via kubectl
294 // (kubectl delete node metropolis-xxx).
295 // 2. KubernetesController: no cleanup should be necessary.
296 // 3. ConsensusMember:
297 // a. the cluster still has quorum: remove the node from etcd.
298 // TODO(q3k): document this
299 // b. the cluster has no quorum: rebuild the cluster
300 SafetyBypassHasRoles safety_bypass_has_roles = 3;
301
302 message SafetyBypassNotDecommissioned {
303 }
304 // If set, safety_bypass_not_decommissioned will allow to remove nodes that
305 // haven't been yet decommissioned.
306 //
307 // Danger: removing nodes which haven't been decommissioned via
308 // DecommissionNode can leave nodes attempting to reconnect to the cluster,
309 // and does not fully clean up cryptographic material from the node.
310 //
311 // This option will need to be used when a node has broken down, as it's
312 // impossible to move a node from UP to DECOMMISSIONED if that node is
313 // unreachable.
314 //
315 // To clean up after using this option:
316 //
317 // 1. Make sure that the node does not boot back up. The cluster will prevent
318 // the node from rejoining the cluster, but the node will by itself
319 // continue to crash and reboot due to a rejection by the cluster.
320 // 2. Zero our the node's ESP to remove any leftover cryptographic requests.
321 // These secrets are safeguarded according to the cluster's
322 // StorageSecurityPolicy and NodeTPMUsage. Depending on the settings,
323 // cleaning up these secrets before letting other systems access the node
324 // might be critical to maintaining cluster security.
325 SafetyBypassNotDecommissioned safety_bypass_not_decommissioned = 4;
326}
327
328message DeleteNodeResponse {
329}
330
Lorenz Brun5a5c66b2024-08-22 16:11:44 +0200331message RebootRequest {
332 enum Type {
333 TYPE_INVALID = 0;
334 // FIRMWARE performs a firmware-assisted (EFI, PSCI, ...) reboot and
335 // signals the firmware to perform a thorough reset if possible. This
336 // maximizes chances to clear hardware-related issues. The exact
337 // implementation is up to firmware.
338 FIRMWARE = 1;
339 // KEXEC performs a KEXEC reboot without going through firmware at all.
340 // This is the fastest reboot option, but does not fully reset most
341 // hardware and has compatibility issues on certain hardware.
342 KEXEC = 2;
343 // POWER_OFF fully powers off the system. It can only be started again by
344 // a physical power button, Wake On LAN if set supported by the NIC or
345 // an out-of-band management controller if available.
346 POWER_OFF = 3;
347 }
348 Type type = 1;
349 enum NextBoot {
350 // START_NORMAL starts the system normally, respecting standard A/B slot
351 // booting rules. Any staged but not activated updates will be activated
352 // as with a normal reboot.
353 START_NORMAL = 0;
354 // START_ROLLBACK tries to boot into the currently inactive slot on reboot.
355 START_ROLLBACK = 1;
356 // START_FIRMWARE_UI tries to boot into the EFI firmware UI. Cannot be used
357 // together with KEXEC as firmare is not involved there.
358 START_FIRMWARE_UI = 2;
359 }
360 // NextBoot can be used to select the boot slot to reboot into. This works
361 // even for POWER_OFF, but there the next boot will need to be triggered
362 // externally. START_FIRMWARE_UI cannot be used together with KEXEC.
363 NextBoot next_boot = 2;
364}
365
366message RebootResponse {
367
368}
369
Serge Bazanskib40c0082023-03-29 14:28:04 +0200370// NodeManagement runs on every node of the cluster and providers management
371// and troubleshooting RPCs to operators. All requests must be authenticated.
372service NodeManagement {
Serge Bazanskida114862023-03-29 17:46:42 +0200373 // GetLogs Returns historical and/or streaming logs for a given DN with given
374 // filters from the system global LogTree.
375 //
Tim Windelschmidt9f21f532024-05-07 15:14:20 +0200376 // For more information about this API, see //osbase/logtree. But, in
Serge Bazanskida114862023-03-29 17:46:42 +0200377 // summary:
378 // - All logging is performed to a DN (distinguished name), which is a
379 // dot-delimited string like foo.bar.baz.
380 // - Log entries can be either raw (coming from unstructured logging from
381 // an external service, like a running process) or leveled (emitted by
382 // Metropolis code with a source line, timestamp, and severity).
383 // - The DNs form a tree of logging nodes - and when requesting logs, a
384 // given subtree of DNs can be requested, instead of just a given DN.
385 // - All supervised processes live at `root.<supervisor DN>`. For more
386 // example paths, see the console logs of a running Metropolis node, or
387 // request all logs (at DN "").
388 //
Serge Bazanskib91938f2023-03-29 14:31:22 +0200389 rpc Logs(GetLogsRequest) returns (stream GetLogsResponse) {
390 option (metropolis.proto.ext.authorization) = {
391 need: PERMISSION_READ_NODE_LOGS
392 };
393 }
Lorenz Brun35fcf032023-06-29 04:15:58 +0200394 // UpdateNode updates the node operating system to a new version.
395 //
396 // Metropolis uses a side-by-side (A/B) update process. This method installs
397 // the OS from the given bundle into the inactive slot, activates that slot
398 // and then (optionally) reboots to activate it.
399 rpc UpdateNode(UpdateNodeRequest) returns (UpdateNodeResponse) {
400 option (metropolis.proto.ext.authorization) = {
401 need: PERMISSION_UPDATE_NODE
402 };
403 }
Lorenz Brun5a5c66b2024-08-22 16:11:44 +0200404
405 // Reboot initiates a node reboot or power-off. It can also be used to roll
406 // back to the inactive slot.
407 rpc Reboot(RebootRequest) returns (RebootResponse) {
408 option (metropolis.proto.ext.authorization) = {
409 need: PERMISSION_NODE_POWER_MANAGEMENT
410 };
411 }
Serge Bazanskib91938f2023-03-29 14:31:22 +0200412}
413
Serge Bazanskib91938f2023-03-29 14:31:22 +0200414message GetLogsRequest {
415 // DN from which to request logs. All supervised runnables live at `root.`,
416 // the init code lives at `init.`.
417 string dn = 1;
418 // Filters to apply to returned data.
Serge Bazanskida114862023-03-29 17:46:42 +0200419 repeated metropolis.proto.common.LogFilter filters = 2;
Serge Bazanskib91938f2023-03-29 14:31:22 +0200420
421 enum BacklogMode {
422 BACKLOG_INVALID = 0;
423 // No historic data will be returned.
424 BACKLOG_DISABLE = 1;
425 // All available historic data will be returned.
426 BACKLOG_ALL = 2;
427 // At most backlog_count entries will be returned, if available.
428 BACKLOG_COUNT = 3;
429 }
430 BacklogMode backlog_mode = 3;
431 int64 backlog_count = 4;
432
433 enum StreamMode {
434 STREAM_INVALID = 0;
Serge Bazanskida114862023-03-29 17:46:42 +0200435 // No streaming entries, gRPC stream will be closed as soon as all backlog
436 // data is served.
Serge Bazanskib91938f2023-03-29 14:31:22 +0200437 STREAM_DISABLE = 1;
Serge Bazanskida114862023-03-29 17:46:42 +0200438 // Entries will be streamed as early as available right after all backlog
439 // data is served.
Serge Bazanskib91938f2023-03-29 14:31:22 +0200440 STREAM_UNBUFFERED = 2;
441 }
442 StreamMode stream_mode = 5;
443}
444
Serge Bazanskib91938f2023-03-29 14:31:22 +0200445message GetLogsResponse {
Serge Bazanskida114862023-03-29 17:46:42 +0200446 // Entries from the requested historical entries (via WithBackLog). They will
447 // all be served before the first stream_entries are served (if any).
Tim Windelschmidt9f21f532024-05-07 15:14:20 +0200448 repeated osbase.pkg.logtree.proto.LogEntry backlog_entries = 1;
Serge Bazanskida114862023-03-29 17:46:42 +0200449 // Entries streamed as they arrive. Currently no server-side buffering is
450 // enabled, instead every line is served as early as it arrives. However, this
451 // might change in the future, so this behaviour cannot be depended upon.
Tim Windelschmidt9f21f532024-05-07 15:14:20 +0200452 repeated osbase.pkg.logtree.proto.LogEntry stream_entries = 2;
Lorenz Brun35fcf032023-06-29 04:15:58 +0200453}
454
Lorenz Brund14be0e2023-07-31 16:46:14 +0200455enum ActivationMode {
456 ACTIVATION_INVALID = 0;
457 // The new bundle is not activated immediately. It gets activated on the next
458 // reboot/reset.
459 ACTIVATION_NONE = 1;
460 // The node is rebooted immediately to activate the new image.
461 ACTIVATION_REBOOT = 2;
462 // The node uses kexec to activate the new image immediately without fully
463 // rebooting.
464 ACTIVATION_KEXEC = 3;
465}
466
Lorenz Brun35fcf032023-06-29 04:15:58 +0200467message UpdateNodeRequest {
468 // An HTTPS URL to a Metropolis bundle containing the new OS to install.
469 string bundle_url = 1;
470
Lorenz Brund14be0e2023-07-31 16:46:14 +0200471 reserved 2;
472
473 // Specifies how the updated image should be activated.
474 ActivationMode activation_mode = 3;
Lorenz Brun35fcf032023-06-29 04:15:58 +0200475}
476
Serge Bazanski1f789542024-05-22 14:01:50 +0200477message UpdateNodeResponse {}
478
479message UpdateNodeLabelsRequest {
480 // node uniquely identifies the node subject to this request.
481 oneof node {
482 // pubkey is the Ed25519 public key of this node, which can be used to
483 // generate the node's ID.
484 bytes pubkey = 1;
485 // id is the human-readable identifier of the node, based on its public
486 // key.
487 string id = 2;
488 }
489
490 message Pair {
491 string key = 1;
492 string value = 2;
493 }
494 // Labels to be added (created or updated by key).
495 //
496 // The given pairs must have unique, valid keys and valid values.
497 repeated Pair upsert = 3;
498
499 // Labels to be removed (by key).
500 //
501 // The given keys do not have to exist on the node, but cannot intersect with
502 // keys given in the upsert list.
503 repeated string delete = 4;
504}
505
506message UpdateNodeLabelsResponse {
507}
508