]> git.proxmox.com Git - mirror_ovs.git/blame - ovsdb/raft-private.c
Eliminate "whitelist" and "blacklist" terms.
[mirror_ovs.git] / ovsdb / raft-private.c
CommitLineData
1b1d2e6d
BP
1/*
2 * Copyright (c) 2017, 2018 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
18
19#include "raft-private.h"
20
21#include "openvswitch/dynamic-string.h"
22#include "ovsdb-error.h"
23#include "ovsdb-parser.h"
24#include "socket-util.h"
25#include "sset.h"
26\f
27/* Addresses of Raft servers. */
28
29struct ovsdb_error * OVS_WARN_UNUSED_RESULT
30raft_address_validate(const char *address)
31{
32 if (!strncmp(address, "unix:", 5)) {
33 return NULL;
34 } else if (!strncmp(address, "ssl:", 4) || !strncmp(address, "tcp:", 4)) {
35 struct sockaddr_storage ss;
f31b8ae7 36 if (!inet_parse_active(address + 4, -1, &ss, true)) {
1b1d2e6d
BP
37 return ovsdb_error(NULL, "%s: syntax error in address", address);
38 }
39 return NULL;
40 } else {
41 return ovsdb_error(NULL, "%s: expected \"tcp\" or \"ssl\" address",
42 address);
43 }
44}
45
46static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
47raft_address_validate_json(const struct json *address)
48{
49 if (address->type != JSON_STRING) {
50 return ovsdb_syntax_error(address, NULL,
51 "server address is not string");
52 }
53 return raft_address_validate(json_string(address));
54}
55
56/* Constructs and returns a "nickname" for a Raft server based on its 'address'
57 * and server ID 'sid'. The nickname is just a short name for the server to
58 * use in log messages, to make them more readable.
59 *
60 * The caller must eventually free the returned string. */
61char *
62raft_address_to_nickname(const char *address, const struct uuid *sid)
63{
64 if (!strncmp(address, "unix:", 5)) {
65 const char *p = address + 5;
66
67 const char *slash = strrchr(p, '/');
68 if (slash) {
69 p = slash + 1;
70 }
71
72 int len = strcspn(p, ".");
73 if (len) {
74 return xmemdup0(p, len);
75 }
76 }
77
78 return xasprintf(SID_FMT, SID_ARGS(sid));
79}
80\f
81/* Sets of Raft server addresses. */
82
83struct ovsdb_error * OVS_WARN_UNUSED_RESULT
84raft_addresses_from_json(const struct json *json, struct sset *addresses)
85{
86 sset_init(addresses);
87
88 const struct json_array *array = json_array(json);
89 if (!array->n) {
90 return ovsdb_syntax_error(json, NULL,
91 "at least one remote address is required");
92 }
93 for (size_t i = 0; i < array->n; i++) {
94 const struct json *address = array->elems[i];
95 struct ovsdb_error *error = raft_address_validate_json(address);
96 if (error) {
97 sset_destroy(addresses);
98 sset_init(addresses);
99 return error;
100 }
101 sset_add(addresses, json_string(address));
102 }
103 return NULL;
104}
105
106struct json *
107raft_addresses_to_json(const struct sset *sset)
108{
109 struct json *array;
110 const char *s;
111
112 array = json_array_create_empty();
113 SSET_FOR_EACH (s, sset) {
114 json_array_add(array, json_string_create(s));
115 }
116 return array;
117}
118\f
119/* raft_server. */
120
121const char *
122raft_server_phase_to_string(enum raft_server_phase phase)
123{
124 switch (phase) {
125 case RAFT_PHASE_STABLE: return "stable";
126 case RAFT_PHASE_CATCHUP: return "adding: catchup";
127 case RAFT_PHASE_CAUGHT_UP: return "adding: caught up";
128 case RAFT_PHASE_COMMITTING: return "adding: committing";
129 case RAFT_PHASE_REMOVE: return "removing";
130 default: return "<error>";
131 }
132}
133
134void
135raft_server_destroy(struct raft_server *s)
136{
137 if (s) {
138 free(s->address);
139 free(s->nickname);
8c2c503b 140 free(s->last_install_snapshot_request);
1b1d2e6d
BP
141 free(s);
142 }
143}
144
145void
146raft_servers_destroy(struct hmap *servers)
147{
148 struct raft_server *s, *next;
149 HMAP_FOR_EACH_SAFE (s, next, hmap_node, servers) {
150 hmap_remove(servers, &s->hmap_node);
151 raft_server_destroy(s);
152 }
153 hmap_destroy(servers);
154}
155
156struct raft_server *
157raft_server_add(struct hmap *servers, const struct uuid *sid,
158 const char *address)
159{
160 struct raft_server *s = xzalloc(sizeof *s);
161 s->sid = *sid;
162 s->address = xstrdup(address);
163 s->nickname = raft_address_to_nickname(address, sid);
164 s->phase = RAFT_PHASE_STABLE;
165 hmap_insert(servers, &s->hmap_node, uuid_hash(sid));
166 return s;
167}
168
169
170struct raft_server *
171raft_server_find(const struct hmap *servers, const struct uuid *sid)
172{
173 struct raft_server *s;
174 HMAP_FOR_EACH_IN_BUCKET (s, hmap_node, uuid_hash(sid), servers) {
175 if (uuid_equals(sid, &s->sid)) {
176 return s;
177 }
178 }
179 return NULL;
180}
181
182const char *
183raft_servers_get_nickname__(const struct hmap *servers, const struct uuid *sid)
184{
185 const struct raft_server *s = raft_server_find(servers, sid);
186 return s ? s->nickname : NULL;
187}
188
189const char *
190raft_servers_get_nickname(const struct hmap *servers,
191 const struct uuid *sid,
192 char buf[SID_LEN + 1], size_t bufsize)
193{
194 const char *s = raft_servers_get_nickname__(servers, sid);
195 if (s) {
196 return s;
197 }
198 snprintf(buf, bufsize, SID_FMT, SID_ARGS(sid));
199 return buf;
200}
201
202static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
203raft_servers_from_json__(const struct json *json, struct hmap *servers)
204{
205 if (!json || json->type != JSON_OBJECT) {
206 return ovsdb_syntax_error(json, NULL, "servers must be JSON object");
207 } else if (shash_is_empty(json_object(json))) {
208 return ovsdb_syntax_error(json, NULL, "must have at least one server");
209 }
210
211 /* Parse new servers. */
212 struct shash_node *node;
213 SHASH_FOR_EACH (node, json_object(json)) {
214 /* Parse server UUID. */
215 struct uuid sid;
216 if (!uuid_from_string(&sid, node->name)) {
217 return ovsdb_syntax_error(json, NULL, "%s is not a UUID",
218 node->name);
219 }
220
221 const struct json *address = node->data;
222 struct ovsdb_error *error = raft_address_validate_json(address);
223 if (error) {
224 return error;
225 }
226
227 raft_server_add(servers, &sid, json_string(address));
228 }
229
230 return NULL;
231}
232
233struct ovsdb_error * OVS_WARN_UNUSED_RESULT
234raft_servers_from_json(const struct json *json, struct hmap *servers)
235{
236 hmap_init(servers);
237 struct ovsdb_error *error = raft_servers_from_json__(json, servers);
238 if (error) {
239 raft_servers_destroy(servers);
240 }
241 return error;
242}
243
244struct ovsdb_error * OVS_WARN_UNUSED_RESULT
245raft_servers_validate_json(const struct json *json)
246{
247 struct hmap servers = HMAP_INITIALIZER(&servers);
248 struct ovsdb_error *error = raft_servers_from_json__(json, &servers);
249 raft_servers_destroy(&servers);
250 return error;
251}
252
253struct json *
254raft_servers_to_json(const struct hmap *servers)
255{
256 struct json *json = json_object_create();
257 struct raft_server *s;
258 HMAP_FOR_EACH (s, hmap_node, servers) {
259 char sid_s[UUID_LEN + 1];
260 sprintf(sid_s, UUID_FMT, UUID_ARGS(&s->sid));
261 json_object_put_string(json, sid_s, s->address);
262 }
263 return json;
264}
265
266void
267raft_servers_format(const struct hmap *servers, struct ds *ds)
268{
269 int i = 0;
270 const struct raft_server *s;
271 HMAP_FOR_EACH (s, hmap_node, servers) {
272 if (i++) {
273 ds_put_cstr(ds, ", ");
274 }
275 ds_put_format(ds, SID_FMT"(%s)", SID_ARGS(&s->sid), s->address);
276 }
277}
278\f
279/* Raft log entries. */
280
281void
282raft_entry_clone(struct raft_entry *dst, const struct raft_entry *src)
283{
284 dst->term = src->term;
285 dst->data = json_nullable_clone(src->data);
286 dst->eid = src->eid;
287 dst->servers = json_nullable_clone(src->servers);
a76ba825 288 dst->election_timer = src->election_timer;
1b1d2e6d
BP
289}
290
291void
292raft_entry_uninit(struct raft_entry *e)
293{
294 if (e) {
295 json_destroy(e->data);
296 json_destroy(e->servers);
297 }
298}
299
300struct json *
301raft_entry_to_json(const struct raft_entry *e)
302{
303 struct json *json = json_object_create();
304 raft_put_uint64(json, "term", e->term);
305 if (e->data) {
306 json_object_put(json, "data", json_clone(e->data));
307 json_object_put_format(json, "eid", UUID_FMT, UUID_ARGS(&e->eid));
308 }
309 if (e->servers) {
310 json_object_put(json, "servers", json_clone(e->servers));
311 }
8e354614
HZ
312 if (e->election_timer) {
313 raft_put_uint64(json, "election_timer", e->election_timer);
314 }
315
1b1d2e6d
BP
316 return json;
317}
318
319struct ovsdb_error * OVS_WARN_UNUSED_RESULT
320raft_entry_from_json(struct json *json, struct raft_entry *e)
321{
322 memset(e, 0, sizeof *e);
323
324 struct ovsdb_parser p;
325 ovsdb_parser_init(&p, json, "raft log entry");
326 e->term = raft_parse_required_uint64(&p, "term");
327 e->data = json_nullable_clone(
328 ovsdb_parser_member(&p, "data", OP_OBJECT | OP_ARRAY | OP_OPTIONAL));
329 e->eid = e->data ? raft_parse_required_uuid(&p, "eid") : UUID_ZERO;
330 e->servers = json_nullable_clone(
331 ovsdb_parser_member(&p, "servers", OP_OBJECT | OP_OPTIONAL));
332 if (e->servers) {
333 ovsdb_parser_put_error(&p, raft_servers_validate_json(e->servers));
334 }
8e354614 335 e->election_timer = raft_parse_optional_uint64(&p, "election_timer");
1b1d2e6d
BP
336
337 struct ovsdb_error *error = ovsdb_parser_finish(&p);
338 if (error) {
339 raft_entry_uninit(e);
340 }
341 return error;
342}
343
344bool
345raft_entry_equals(const struct raft_entry *a, const struct raft_entry *b)
346{
347 return (a->term == b->term
348 && json_equal(a->data, b->data)
349 && uuid_equals(&a->eid, &b->eid)
350 && json_equal(a->servers, b->servers));
351}
352\f
353void
354raft_header_uninit(struct raft_header *h)
355{
356 if (!h) {
357 return;
358 }
359
360 free(h->name);
361 free(h->local_address);
362 sset_destroy(&h->remote_addresses);
363 raft_entry_uninit(&h->snap);
364}
365
366static void
367raft_header_from_json__(struct raft_header *h, struct ovsdb_parser *p)
368{
369 /* Parse always-required fields. */
370 h->sid = raft_parse_required_uuid(p, "server_id");
371 h->name = nullable_xstrdup(raft_parse_required_string(p, "name"));
372 h->local_address = nullable_xstrdup(
373 raft_parse_required_string(p, "local_address"));
374
375 /* Parse "remote_addresses", if present.
376 *
377 * If this is present, then this database file is for the special case of a
378 * server that was created with "ovsdb-tool join-cluster" and has not yet
379 * joined its cluster, */
380 const struct json *remote_addresses
381 = ovsdb_parser_member(p, "remote_addresses", OP_ARRAY | OP_OPTIONAL);
382 h->joining = remote_addresses != NULL;
383 if (h->joining) {
384 struct ovsdb_error *error = raft_addresses_from_json(
385 remote_addresses, &h->remote_addresses);
386 if (error) {
387 ovsdb_parser_put_error(p, error);
388 } else if (sset_find_and_delete(&h->remote_addresses, h->local_address)
389 && sset_is_empty(&h->remote_addresses)) {
390 ovsdb_parser_raise_error(p, "at least one remote address (other "
391 "than the local address) is required");
392 }
393 } else {
394 /* The set of servers is mandatory. */
395 h->snap.servers = json_nullable_clone(
396 ovsdb_parser_member(p, "prev_servers", OP_OBJECT));
397 if (h->snap.servers) {
398 ovsdb_parser_put_error(p, raft_servers_validate_json(
399 h->snap.servers));
400 }
401
402 /* Term, index, and snapshot are optional, but if any of them is
403 * present, all of them must be. */
404 h->snap_index = raft_parse_optional_uint64(p, "prev_index");
405 if (h->snap_index) {
406 h->snap.data = json_nullable_clone(
407 ovsdb_parser_member(p, "prev_data", OP_ANY));
408 h->snap.eid = raft_parse_required_uuid(p, "prev_eid");
409 h->snap.term = raft_parse_required_uint64(p, "prev_term");
a76ba825
HZ
410 h->snap.election_timer = raft_parse_optional_uint64(
411 p, "prev_election_timer");
1b1d2e6d
BP
412 }
413 }
414
415 /* Parse cluster ID. If we're joining a cluster, this is optional,
416 * otherwise it is mandatory. */
417 raft_parse_uuid(p, "cluster_id", h->joining, &h->cid);
418}
419
420struct ovsdb_error * OVS_WARN_UNUSED_RESULT
421raft_header_from_json(struct raft_header *h, const struct json *json)
422{
423 struct ovsdb_parser p;
424 ovsdb_parser_init(&p, json, "raft header");
425 memset(h, 0, sizeof *h);
426 sset_init(&h->remote_addresses);
427 raft_header_from_json__(h, &p);
428 struct ovsdb_error *error = ovsdb_parser_finish(&p);
429 if (error) {
430 raft_header_uninit(h);
431 }
432 return error;
433}
434
435struct json *
436raft_header_to_json(const struct raft_header *h)
437{
438 struct json *json = json_object_create();
439
440 json_object_put_format(json, "server_id", UUID_FMT, UUID_ARGS(&h->sid));
441 if (!uuid_is_zero(&h->cid)) {
442 json_object_put_format(json, "cluster_id",
443 UUID_FMT, UUID_ARGS(&h->cid));
444 }
445 json_object_put_string(json, "local_address", h->local_address);
446 json_object_put_string(json, "name", h->name);
447
448 if (!sset_is_empty(&h->remote_addresses)) {
449 json_object_put(json, "remote_addresses",
450 raft_addresses_to_json(&h->remote_addresses));
451 }
452
453 if (h->snap.servers) {
454 json_object_put(json, "prev_servers", json_clone(h->snap.servers));
455 }
456 if (h->snap_index) {
457 raft_put_uint64(json, "prev_index", h->snap_index);
458 raft_put_uint64(json, "prev_term", h->snap.term);
459 if (h->snap.data) {
460 json_object_put(json, "prev_data", json_clone(h->snap.data));
461 }
462 json_object_put_format(json, "prev_eid",
463 UUID_FMT, UUID_ARGS(&h->snap.eid));
a76ba825
HZ
464 if (h->snap.election_timer) {
465 raft_put_uint64(json, "prev_election_timer",
466 h->snap.election_timer);
467 }
1b1d2e6d
BP
468 }
469
470 return json;
471}
472\f
473void
474raft_record_uninit(struct raft_record *r)
475{
476 if (!r) {
477 return;
478 }
479
480 free(r->comment);
481
482 switch (r->type) {
483 case RAFT_REC_ENTRY:
484 json_destroy(r->entry.data);
485 json_destroy(r->entry.servers);
486 break;
487
488 case RAFT_REC_NOTE:
489 free(r->note);
490 break;
491
492 case RAFT_REC_TERM:
493 case RAFT_REC_VOTE:
494 case RAFT_REC_COMMIT_INDEX:
495 case RAFT_REC_LEADER:
496 break;
497 }
498}
499
500static void
501raft_record_from_json__(struct raft_record *r, struct ovsdb_parser *p)
502{
503 r->comment = nullable_xstrdup(raft_parse_optional_string(p, "comment"));
504
505 /* Parse "note". */
506 const char *note = raft_parse_optional_string(p, "note");
507 if (note) {
508 r->type = RAFT_REC_NOTE;
509 r->term = 0;
510 r->note = xstrdup(note);
511 return;
512 }
513
514 /* Parse "commit_index". */
515 r->commit_index = raft_parse_optional_uint64(p, "commit_index");
516 if (r->commit_index) {
517 r->type = RAFT_REC_COMMIT_INDEX;
518 r->term = 0;
519 return;
520 }
521
522 /* All remaining types of log records include "term", plus at most one of:
523 *
524 * - "index" plus zero or more of "data", "eid", and "servers". "data"
525 * and "eid" must be both present or both absent.
526 *
527 * - "vote".
528 *
529 * - "leader".
530 */
531
532 /* Parse "term".
533 *
534 * A Raft leader can replicate entries from previous terms to the other
535 * servers in the cluster, retaining the original terms on those entries
536 * (see section 3.6.2 "Committing entries from previous terms" for more
537 * information), so it's OK for the term in a log record to precede the
538 * current term. */
539 r->term = raft_parse_required_uint64(p, "term");
540
541 /* Parse "leader". */
542 if (raft_parse_optional_uuid(p, "leader", &r->sid)) {
543 r->type = RAFT_REC_LEADER;
544 if (uuid_is_zero(&r->sid)) {
545 ovsdb_parser_raise_error(p, "record says leader is all-zeros SID");
546 }
547 return;
548 }
549
550 /* Parse "vote". */
551 if (raft_parse_optional_uuid(p, "vote", &r->sid)) {
552 r->type = RAFT_REC_VOTE;
553 if (uuid_is_zero(&r->sid)) {
554 ovsdb_parser_raise_error(p, "record votes for all-zeros SID");
555 }
556 return;
557 }
558
559 /* If "index" is present parse the rest of the entry, otherwise it's just a
560 * term update. */
561 r->entry.index = raft_parse_optional_uint64(p, "index");
562 if (!r->entry.index) {
563 r->type = RAFT_REC_TERM;
564 } else {
565 r->type = RAFT_REC_ENTRY;
566 r->entry.servers = json_nullable_clone(
567 ovsdb_parser_member(p, "servers", OP_OBJECT | OP_OPTIONAL));
568 if (r->entry.servers) {
569 ovsdb_parser_put_error(
570 p, raft_servers_validate_json(r->entry.servers));
571 }
8e354614
HZ
572 r->entry.election_timer = raft_parse_optional_uint64(
573 p, "election_timer");
1b1d2e6d
BP
574 r->entry.data = json_nullable_clone(
575 ovsdb_parser_member(p, "data",
576 OP_OBJECT | OP_ARRAY | OP_OPTIONAL));
577 r->entry.eid = (r->entry.data
578 ? raft_parse_required_uuid(p, "eid")
579 : UUID_ZERO);
580 }
581}
582
583struct ovsdb_error * OVS_WARN_UNUSED_RESULT
584raft_record_from_json(struct raft_record *r, const struct json *json)
585{
586 struct ovsdb_parser p;
587 ovsdb_parser_init(&p, json, "raft log record");
588 raft_record_from_json__(r, &p);
589 struct ovsdb_error *error = ovsdb_parser_finish(&p);
590 if (error) {
591 raft_record_uninit(r);
592 }
593 return error;
594}
595
596struct json *
597raft_record_to_json(const struct raft_record *r)
598{
599 struct json *json = json_object_create();
600
601 if (r->comment && *r->comment) {
602 json_object_put_string(json, "comment", r->comment);
603 }
604
605 switch (r->type) {
606 case RAFT_REC_ENTRY:
607 raft_put_uint64(json, "term", r->term);
608 raft_put_uint64(json, "index", r->entry.index);
609 if (r->entry.data) {
610 json_object_put(json, "data", json_clone(r->entry.data));
611 }
612 if (r->entry.servers) {
613 json_object_put(json, "servers", json_clone(r->entry.servers));
614 }
8e354614
HZ
615 if (r->entry.election_timer) {
616 raft_put_uint64(json, "election_timer", r->entry.election_timer);
617 }
1b1d2e6d
BP
618 if (!uuid_is_zero(&r->entry.eid)) {
619 json_object_put_format(json, "eid",
620 UUID_FMT, UUID_ARGS(&r->entry.eid));
621 }
622 break;
623
624 case RAFT_REC_TERM:
625 raft_put_uint64(json, "term", r->term);
626 break;
627
628 case RAFT_REC_VOTE:
629 raft_put_uint64(json, "term", r->term);
630 json_object_put_format(json, "vote", UUID_FMT, UUID_ARGS(&r->sid));
631 break;
632
633 case RAFT_REC_NOTE:
634 json_object_put(json, "note", json_string_create(r->note));
635 break;
636
637 case RAFT_REC_COMMIT_INDEX:
638 raft_put_uint64(json, "commit_index", r->commit_index);
639 break;
640
641 case RAFT_REC_LEADER:
642 raft_put_uint64(json, "term", r->term);
643 json_object_put_format(json, "leader", UUID_FMT, UUID_ARGS(&r->sid));
644 break;
645
646 default:
647 OVS_NOT_REACHED();
648 }
649 return json;
650}
651\f
652/* Puts 'integer' into JSON 'object' with the given 'name'.
653 *
654 * The OVS JSON implementation only supports integers in the range
655 * INT64_MIN...INT64_MAX, which causes trouble for values from INT64_MAX+1 to
656 * UINT64_MAX. We map those into the negative range. */
657void
658raft_put_uint64(struct json *object, const char *name, uint64_t integer)
659{
660 json_object_put(object, name, json_integer_create(integer));
661}
662
663/* Parses an integer from parser 'p' with the given 'name'.
664 *
665 * The OVS JSON implementation only supports integers in the range
666 * INT64_MIN...INT64_MAX, which causes trouble for values from INT64_MAX+1 to
667 * UINT64_MAX. We map the negative range back into positive numbers. */
668static uint64_t
669raft_parse_uint64__(struct ovsdb_parser *p, const char *name, bool optional)
670{
671 enum ovsdb_parser_types types = OP_INTEGER | (optional ? OP_OPTIONAL : 0);
672 const struct json *json = ovsdb_parser_member(p, name, types);
673 return json ? json_integer(json) : 0;
674}
675
676uint64_t
677raft_parse_optional_uint64(struct ovsdb_parser *p, const char *name)
678{
679 return raft_parse_uint64__(p, name, true);
680}
681
682uint64_t
683raft_parse_required_uint64(struct ovsdb_parser *p, const char *name)
684{
685 return raft_parse_uint64__(p, name, false);
686}
687
688static int
689raft_parse_boolean__(struct ovsdb_parser *p, const char *name, bool optional)
690{
691 enum ovsdb_parser_types types = OP_BOOLEAN | (optional ? OP_OPTIONAL : 0);
692 const struct json *json = ovsdb_parser_member(p, name, types);
693 return json ? json_boolean(json) : -1;
694}
695
696bool
697raft_parse_required_boolean(struct ovsdb_parser *p, const char *name)
698{
699 return raft_parse_boolean__(p, name, false);
700}
701
702/* Returns true or false if present, -1 if absent. */
703int
704raft_parse_optional_boolean(struct ovsdb_parser *p, const char *name)
705{
706 return raft_parse_boolean__(p, name, true);
707}
708
709static const char *
710raft_parse_string__(struct ovsdb_parser *p, const char *name, bool optional)
711{
712 enum ovsdb_parser_types types = OP_STRING | (optional ? OP_OPTIONAL : 0);
713 const struct json *json = ovsdb_parser_member(p, name, types);
714 return json ? json_string(json) : NULL;
715}
716
717const char *
718raft_parse_required_string(struct ovsdb_parser *p, const char *name)
719{
720 return raft_parse_string__(p, name, false);
721}
722
723const char *
724raft_parse_optional_string(struct ovsdb_parser *p, const char *name)
725{
726 return raft_parse_string__(p, name, true);
727}
728
729bool
730raft_parse_uuid(struct ovsdb_parser *p, const char *name, bool optional,
731 struct uuid *uuid)
732{
733 const char *s = raft_parse_string__(p, name, optional);
734 if (s) {
735 if (uuid_from_string(uuid, s)) {
736 return true;
737 }
738 ovsdb_parser_raise_error(p, "%s is not a valid UUID", name);
739 }
740 *uuid = UUID_ZERO;
741 return false;
742}
743
744struct uuid
745raft_parse_required_uuid(struct ovsdb_parser *p, const char *name)
746{
747 struct uuid uuid;
748 raft_parse_uuid(p, name, false, &uuid);
749 return uuid;
750}
751
752bool
753raft_parse_optional_uuid(struct ovsdb_parser *p, const char *name,
754 struct uuid *uuid)
755{
756 return raft_parse_uuid(p, name, true, uuid);
757}
758