]> git.proxmox.com Git - mirror_ovs.git/blame - ovsdb/raft-private.c
dist-docs: Include manpages generated from rST.
[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);
140 free(s);
141 }
142}
143
144void
145raft_servers_destroy(struct hmap *servers)
146{
147 struct raft_server *s, *next;
148 HMAP_FOR_EACH_SAFE (s, next, hmap_node, servers) {
149 hmap_remove(servers, &s->hmap_node);
150 raft_server_destroy(s);
151 }
152 hmap_destroy(servers);
153}
154
155struct raft_server *
156raft_server_add(struct hmap *servers, const struct uuid *sid,
157 const char *address)
158{
159 struct raft_server *s = xzalloc(sizeof *s);
160 s->sid = *sid;
161 s->address = xstrdup(address);
162 s->nickname = raft_address_to_nickname(address, sid);
163 s->phase = RAFT_PHASE_STABLE;
164 hmap_insert(servers, &s->hmap_node, uuid_hash(sid));
165 return s;
166}
167
168
169struct raft_server *
170raft_server_find(const struct hmap *servers, const struct uuid *sid)
171{
172 struct raft_server *s;
173 HMAP_FOR_EACH_IN_BUCKET (s, hmap_node, uuid_hash(sid), servers) {
174 if (uuid_equals(sid, &s->sid)) {
175 return s;
176 }
177 }
178 return NULL;
179}
180
181const char *
182raft_servers_get_nickname__(const struct hmap *servers, const struct uuid *sid)
183{
184 const struct raft_server *s = raft_server_find(servers, sid);
185 return s ? s->nickname : NULL;
186}
187
188const char *
189raft_servers_get_nickname(const struct hmap *servers,
190 const struct uuid *sid,
191 char buf[SID_LEN + 1], size_t bufsize)
192{
193 const char *s = raft_servers_get_nickname__(servers, sid);
194 if (s) {
195 return s;
196 }
197 snprintf(buf, bufsize, SID_FMT, SID_ARGS(sid));
198 return buf;
199}
200
201static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
202raft_servers_from_json__(const struct json *json, struct hmap *servers)
203{
204 if (!json || json->type != JSON_OBJECT) {
205 return ovsdb_syntax_error(json, NULL, "servers must be JSON object");
206 } else if (shash_is_empty(json_object(json))) {
207 return ovsdb_syntax_error(json, NULL, "must have at least one server");
208 }
209
210 /* Parse new servers. */
211 struct shash_node *node;
212 SHASH_FOR_EACH (node, json_object(json)) {
213 /* Parse server UUID. */
214 struct uuid sid;
215 if (!uuid_from_string(&sid, node->name)) {
216 return ovsdb_syntax_error(json, NULL, "%s is not a UUID",
217 node->name);
218 }
219
220 const struct json *address = node->data;
221 struct ovsdb_error *error = raft_address_validate_json(address);
222 if (error) {
223 return error;
224 }
225
226 raft_server_add(servers, &sid, json_string(address));
227 }
228
229 return NULL;
230}
231
232struct ovsdb_error * OVS_WARN_UNUSED_RESULT
233raft_servers_from_json(const struct json *json, struct hmap *servers)
234{
235 hmap_init(servers);
236 struct ovsdb_error *error = raft_servers_from_json__(json, servers);
237 if (error) {
238 raft_servers_destroy(servers);
239 }
240 return error;
241}
242
243struct ovsdb_error * OVS_WARN_UNUSED_RESULT
244raft_servers_validate_json(const struct json *json)
245{
246 struct hmap servers = HMAP_INITIALIZER(&servers);
247 struct ovsdb_error *error = raft_servers_from_json__(json, &servers);
248 raft_servers_destroy(&servers);
249 return error;
250}
251
252struct json *
253raft_servers_to_json(const struct hmap *servers)
254{
255 struct json *json = json_object_create();
256 struct raft_server *s;
257 HMAP_FOR_EACH (s, hmap_node, servers) {
258 char sid_s[UUID_LEN + 1];
259 sprintf(sid_s, UUID_FMT, UUID_ARGS(&s->sid));
260 json_object_put_string(json, sid_s, s->address);
261 }
262 return json;
263}
264
265void
266raft_servers_format(const struct hmap *servers, struct ds *ds)
267{
268 int i = 0;
269 const struct raft_server *s;
270 HMAP_FOR_EACH (s, hmap_node, servers) {
271 if (i++) {
272 ds_put_cstr(ds, ", ");
273 }
274 ds_put_format(ds, SID_FMT"(%s)", SID_ARGS(&s->sid), s->address);
275 }
276}
277\f
278/* Raft log entries. */
279
280void
281raft_entry_clone(struct raft_entry *dst, const struct raft_entry *src)
282{
283 dst->term = src->term;
284 dst->data = json_nullable_clone(src->data);
285 dst->eid = src->eid;
286 dst->servers = json_nullable_clone(src->servers);
a76ba825 287 dst->election_timer = src->election_timer;
1b1d2e6d
BP
288}
289
290void
291raft_entry_uninit(struct raft_entry *e)
292{
293 if (e) {
294 json_destroy(e->data);
295 json_destroy(e->servers);
296 }
297}
298
299struct json *
300raft_entry_to_json(const struct raft_entry *e)
301{
302 struct json *json = json_object_create();
303 raft_put_uint64(json, "term", e->term);
304 if (e->data) {
305 json_object_put(json, "data", json_clone(e->data));
306 json_object_put_format(json, "eid", UUID_FMT, UUID_ARGS(&e->eid));
307 }
308 if (e->servers) {
309 json_object_put(json, "servers", json_clone(e->servers));
310 }
8e354614
HZ
311 if (e->election_timer) {
312 raft_put_uint64(json, "election_timer", e->election_timer);
313 }
314
1b1d2e6d
BP
315 return json;
316}
317
318struct ovsdb_error * OVS_WARN_UNUSED_RESULT
319raft_entry_from_json(struct json *json, struct raft_entry *e)
320{
321 memset(e, 0, sizeof *e);
322
323 struct ovsdb_parser p;
324 ovsdb_parser_init(&p, json, "raft log entry");
325 e->term = raft_parse_required_uint64(&p, "term");
326 e->data = json_nullable_clone(
327 ovsdb_parser_member(&p, "data", OP_OBJECT | OP_ARRAY | OP_OPTIONAL));
328 e->eid = e->data ? raft_parse_required_uuid(&p, "eid") : UUID_ZERO;
329 e->servers = json_nullable_clone(
330 ovsdb_parser_member(&p, "servers", OP_OBJECT | OP_OPTIONAL));
331 if (e->servers) {
332 ovsdb_parser_put_error(&p, raft_servers_validate_json(e->servers));
333 }
8e354614 334 e->election_timer = raft_parse_optional_uint64(&p, "election_timer");
1b1d2e6d
BP
335
336 struct ovsdb_error *error = ovsdb_parser_finish(&p);
337 if (error) {
338 raft_entry_uninit(e);
339 }
340 return error;
341}
342
343bool
344raft_entry_equals(const struct raft_entry *a, const struct raft_entry *b)
345{
346 return (a->term == b->term
347 && json_equal(a->data, b->data)
348 && uuid_equals(&a->eid, &b->eid)
349 && json_equal(a->servers, b->servers));
350}
351\f
352void
353raft_header_uninit(struct raft_header *h)
354{
355 if (!h) {
356 return;
357 }
358
359 free(h->name);
360 free(h->local_address);
361 sset_destroy(&h->remote_addresses);
362 raft_entry_uninit(&h->snap);
363}
364
365static void
366raft_header_from_json__(struct raft_header *h, struct ovsdb_parser *p)
367{
368 /* Parse always-required fields. */
369 h->sid = raft_parse_required_uuid(p, "server_id");
370 h->name = nullable_xstrdup(raft_parse_required_string(p, "name"));
371 h->local_address = nullable_xstrdup(
372 raft_parse_required_string(p, "local_address"));
373
374 /* Parse "remote_addresses", if present.
375 *
376 * If this is present, then this database file is for the special case of a
377 * server that was created with "ovsdb-tool join-cluster" and has not yet
378 * joined its cluster, */
379 const struct json *remote_addresses
380 = ovsdb_parser_member(p, "remote_addresses", OP_ARRAY | OP_OPTIONAL);
381 h->joining = remote_addresses != NULL;
382 if (h->joining) {
383 struct ovsdb_error *error = raft_addresses_from_json(
384 remote_addresses, &h->remote_addresses);
385 if (error) {
386 ovsdb_parser_put_error(p, error);
387 } else if (sset_find_and_delete(&h->remote_addresses, h->local_address)
388 && sset_is_empty(&h->remote_addresses)) {
389 ovsdb_parser_raise_error(p, "at least one remote address (other "
390 "than the local address) is required");
391 }
392 } else {
393 /* The set of servers is mandatory. */
394 h->snap.servers = json_nullable_clone(
395 ovsdb_parser_member(p, "prev_servers", OP_OBJECT));
396 if (h->snap.servers) {
397 ovsdb_parser_put_error(p, raft_servers_validate_json(
398 h->snap.servers));
399 }
400
401 /* Term, index, and snapshot are optional, but if any of them is
402 * present, all of them must be. */
403 h->snap_index = raft_parse_optional_uint64(p, "prev_index");
404 if (h->snap_index) {
405 h->snap.data = json_nullable_clone(
406 ovsdb_parser_member(p, "prev_data", OP_ANY));
407 h->snap.eid = raft_parse_required_uuid(p, "prev_eid");
408 h->snap.term = raft_parse_required_uint64(p, "prev_term");
a76ba825
HZ
409 h->snap.election_timer = raft_parse_optional_uint64(
410 p, "prev_election_timer");
1b1d2e6d
BP
411 }
412 }
413
414 /* Parse cluster ID. If we're joining a cluster, this is optional,
415 * otherwise it is mandatory. */
416 raft_parse_uuid(p, "cluster_id", h->joining, &h->cid);
417}
418
419struct ovsdb_error * OVS_WARN_UNUSED_RESULT
420raft_header_from_json(struct raft_header *h, const struct json *json)
421{
422 struct ovsdb_parser p;
423 ovsdb_parser_init(&p, json, "raft header");
424 memset(h, 0, sizeof *h);
425 sset_init(&h->remote_addresses);
426 raft_header_from_json__(h, &p);
427 struct ovsdb_error *error = ovsdb_parser_finish(&p);
428 if (error) {
429 raft_header_uninit(h);
430 }
431 return error;
432}
433
434struct json *
435raft_header_to_json(const struct raft_header *h)
436{
437 struct json *json = json_object_create();
438
439 json_object_put_format(json, "server_id", UUID_FMT, UUID_ARGS(&h->sid));
440 if (!uuid_is_zero(&h->cid)) {
441 json_object_put_format(json, "cluster_id",
442 UUID_FMT, UUID_ARGS(&h->cid));
443 }
444 json_object_put_string(json, "local_address", h->local_address);
445 json_object_put_string(json, "name", h->name);
446
447 if (!sset_is_empty(&h->remote_addresses)) {
448 json_object_put(json, "remote_addresses",
449 raft_addresses_to_json(&h->remote_addresses));
450 }
451
452 if (h->snap.servers) {
453 json_object_put(json, "prev_servers", json_clone(h->snap.servers));
454 }
455 if (h->snap_index) {
456 raft_put_uint64(json, "prev_index", h->snap_index);
457 raft_put_uint64(json, "prev_term", h->snap.term);
458 if (h->snap.data) {
459 json_object_put(json, "prev_data", json_clone(h->snap.data));
460 }
461 json_object_put_format(json, "prev_eid",
462 UUID_FMT, UUID_ARGS(&h->snap.eid));
a76ba825
HZ
463 if (h->snap.election_timer) {
464 raft_put_uint64(json, "prev_election_timer",
465 h->snap.election_timer);
466 }
1b1d2e6d
BP
467 }
468
469 return json;
470}
471\f
472void
473raft_record_uninit(struct raft_record *r)
474{
475 if (!r) {
476 return;
477 }
478
479 free(r->comment);
480
481 switch (r->type) {
482 case RAFT_REC_ENTRY:
483 json_destroy(r->entry.data);
484 json_destroy(r->entry.servers);
485 break;
486
487 case RAFT_REC_NOTE:
488 free(r->note);
489 break;
490
491 case RAFT_REC_TERM:
492 case RAFT_REC_VOTE:
493 case RAFT_REC_COMMIT_INDEX:
494 case RAFT_REC_LEADER:
495 break;
496 }
497}
498
499static void
500raft_record_from_json__(struct raft_record *r, struct ovsdb_parser *p)
501{
502 r->comment = nullable_xstrdup(raft_parse_optional_string(p, "comment"));
503
504 /* Parse "note". */
505 const char *note = raft_parse_optional_string(p, "note");
506 if (note) {
507 r->type = RAFT_REC_NOTE;
508 r->term = 0;
509 r->note = xstrdup(note);
510 return;
511 }
512
513 /* Parse "commit_index". */
514 r->commit_index = raft_parse_optional_uint64(p, "commit_index");
515 if (r->commit_index) {
516 r->type = RAFT_REC_COMMIT_INDEX;
517 r->term = 0;
518 return;
519 }
520
521 /* All remaining types of log records include "term", plus at most one of:
522 *
523 * - "index" plus zero or more of "data", "eid", and "servers". "data"
524 * and "eid" must be both present or both absent.
525 *
526 * - "vote".
527 *
528 * - "leader".
529 */
530
531 /* Parse "term".
532 *
533 * A Raft leader can replicate entries from previous terms to the other
534 * servers in the cluster, retaining the original terms on those entries
535 * (see section 3.6.2 "Committing entries from previous terms" for more
536 * information), so it's OK for the term in a log record to precede the
537 * current term. */
538 r->term = raft_parse_required_uint64(p, "term");
539
540 /* Parse "leader". */
541 if (raft_parse_optional_uuid(p, "leader", &r->sid)) {
542 r->type = RAFT_REC_LEADER;
543 if (uuid_is_zero(&r->sid)) {
544 ovsdb_parser_raise_error(p, "record says leader is all-zeros SID");
545 }
546 return;
547 }
548
549 /* Parse "vote". */
550 if (raft_parse_optional_uuid(p, "vote", &r->sid)) {
551 r->type = RAFT_REC_VOTE;
552 if (uuid_is_zero(&r->sid)) {
553 ovsdb_parser_raise_error(p, "record votes for all-zeros SID");
554 }
555 return;
556 }
557
558 /* If "index" is present parse the rest of the entry, otherwise it's just a
559 * term update. */
560 r->entry.index = raft_parse_optional_uint64(p, "index");
561 if (!r->entry.index) {
562 r->type = RAFT_REC_TERM;
563 } else {
564 r->type = RAFT_REC_ENTRY;
565 r->entry.servers = json_nullable_clone(
566 ovsdb_parser_member(p, "servers", OP_OBJECT | OP_OPTIONAL));
567 if (r->entry.servers) {
568 ovsdb_parser_put_error(
569 p, raft_servers_validate_json(r->entry.servers));
570 }
8e354614
HZ
571 r->entry.election_timer = raft_parse_optional_uint64(
572 p, "election_timer");
1b1d2e6d
BP
573 r->entry.data = json_nullable_clone(
574 ovsdb_parser_member(p, "data",
575 OP_OBJECT | OP_ARRAY | OP_OPTIONAL));
576 r->entry.eid = (r->entry.data
577 ? raft_parse_required_uuid(p, "eid")
578 : UUID_ZERO);
579 }
580}
581
582struct ovsdb_error * OVS_WARN_UNUSED_RESULT
583raft_record_from_json(struct raft_record *r, const struct json *json)
584{
585 struct ovsdb_parser p;
586 ovsdb_parser_init(&p, json, "raft log record");
587 raft_record_from_json__(r, &p);
588 struct ovsdb_error *error = ovsdb_parser_finish(&p);
589 if (error) {
590 raft_record_uninit(r);
591 }
592 return error;
593}
594
595struct json *
596raft_record_to_json(const struct raft_record *r)
597{
598 struct json *json = json_object_create();
599
600 if (r->comment && *r->comment) {
601 json_object_put_string(json, "comment", r->comment);
602 }
603
604 switch (r->type) {
605 case RAFT_REC_ENTRY:
606 raft_put_uint64(json, "term", r->term);
607 raft_put_uint64(json, "index", r->entry.index);
608 if (r->entry.data) {
609 json_object_put(json, "data", json_clone(r->entry.data));
610 }
611 if (r->entry.servers) {
612 json_object_put(json, "servers", json_clone(r->entry.servers));
613 }
8e354614
HZ
614 if (r->entry.election_timer) {
615 raft_put_uint64(json, "election_timer", r->entry.election_timer);
616 }
1b1d2e6d
BP
617 if (!uuid_is_zero(&r->entry.eid)) {
618 json_object_put_format(json, "eid",
619 UUID_FMT, UUID_ARGS(&r->entry.eid));
620 }
621 break;
622
623 case RAFT_REC_TERM:
624 raft_put_uint64(json, "term", r->term);
625 break;
626
627 case RAFT_REC_VOTE:
628 raft_put_uint64(json, "term", r->term);
629 json_object_put_format(json, "vote", UUID_FMT, UUID_ARGS(&r->sid));
630 break;
631
632 case RAFT_REC_NOTE:
633 json_object_put(json, "note", json_string_create(r->note));
634 break;
635
636 case RAFT_REC_COMMIT_INDEX:
637 raft_put_uint64(json, "commit_index", r->commit_index);
638 break;
639
640 case RAFT_REC_LEADER:
641 raft_put_uint64(json, "term", r->term);
642 json_object_put_format(json, "leader", UUID_FMT, UUID_ARGS(&r->sid));
643 break;
644
645 default:
646 OVS_NOT_REACHED();
647 }
648 return json;
649}
650\f
651/* Puts 'integer' into JSON 'object' with the given 'name'.
652 *
653 * The OVS JSON implementation only supports integers in the range
654 * INT64_MIN...INT64_MAX, which causes trouble for values from INT64_MAX+1 to
655 * UINT64_MAX. We map those into the negative range. */
656void
657raft_put_uint64(struct json *object, const char *name, uint64_t integer)
658{
659 json_object_put(object, name, json_integer_create(integer));
660}
661
662/* Parses an integer from parser 'p' with the given 'name'.
663 *
664 * The OVS JSON implementation only supports integers in the range
665 * INT64_MIN...INT64_MAX, which causes trouble for values from INT64_MAX+1 to
666 * UINT64_MAX. We map the negative range back into positive numbers. */
667static uint64_t
668raft_parse_uint64__(struct ovsdb_parser *p, const char *name, bool optional)
669{
670 enum ovsdb_parser_types types = OP_INTEGER | (optional ? OP_OPTIONAL : 0);
671 const struct json *json = ovsdb_parser_member(p, name, types);
672 return json ? json_integer(json) : 0;
673}
674
675uint64_t
676raft_parse_optional_uint64(struct ovsdb_parser *p, const char *name)
677{
678 return raft_parse_uint64__(p, name, true);
679}
680
681uint64_t
682raft_parse_required_uint64(struct ovsdb_parser *p, const char *name)
683{
684 return raft_parse_uint64__(p, name, false);
685}
686
687static int
688raft_parse_boolean__(struct ovsdb_parser *p, const char *name, bool optional)
689{
690 enum ovsdb_parser_types types = OP_BOOLEAN | (optional ? OP_OPTIONAL : 0);
691 const struct json *json = ovsdb_parser_member(p, name, types);
692 return json ? json_boolean(json) : -1;
693}
694
695bool
696raft_parse_required_boolean(struct ovsdb_parser *p, const char *name)
697{
698 return raft_parse_boolean__(p, name, false);
699}
700
701/* Returns true or false if present, -1 if absent. */
702int
703raft_parse_optional_boolean(struct ovsdb_parser *p, const char *name)
704{
705 return raft_parse_boolean__(p, name, true);
706}
707
708static const char *
709raft_parse_string__(struct ovsdb_parser *p, const char *name, bool optional)
710{
711 enum ovsdb_parser_types types = OP_STRING | (optional ? OP_OPTIONAL : 0);
712 const struct json *json = ovsdb_parser_member(p, name, types);
713 return json ? json_string(json) : NULL;
714}
715
716const char *
717raft_parse_required_string(struct ovsdb_parser *p, const char *name)
718{
719 return raft_parse_string__(p, name, false);
720}
721
722const char *
723raft_parse_optional_string(struct ovsdb_parser *p, const char *name)
724{
725 return raft_parse_string__(p, name, true);
726}
727
728bool
729raft_parse_uuid(struct ovsdb_parser *p, const char *name, bool optional,
730 struct uuid *uuid)
731{
732 const char *s = raft_parse_string__(p, name, optional);
733 if (s) {
734 if (uuid_from_string(uuid, s)) {
735 return true;
736 }
737 ovsdb_parser_raise_error(p, "%s is not a valid UUID", name);
738 }
739 *uuid = UUID_ZERO;
740 return false;
741}
742
743struct uuid
744raft_parse_required_uuid(struct ovsdb_parser *p, const char *name)
745{
746 struct uuid uuid;
747 raft_parse_uuid(p, name, false, &uuid);
748 return uuid;
749}
750
751bool
752raft_parse_optional_uuid(struct ovsdb_parser *p, const char *name,
753 struct uuid *uuid)
754{
755 return raft_parse_uuid(p, name, true, uuid);
756}
757