]> git.proxmox.com Git - mirror_ovs.git/blob - ovsdb/raft-private.c
dist-docs: Include manpages generated from rST.
[mirror_ovs.git] / ovsdb / raft-private.c
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
29 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
30 raft_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;
36 if (!inet_parse_active(address + 4, -1, &ss, true)) {
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
46 static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
47 raft_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. */
61 char *
62 raft_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
83 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
84 raft_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
106 struct json *
107 raft_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
121 const char *
122 raft_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
134 void
135 raft_server_destroy(struct raft_server *s)
136 {
137 if (s) {
138 free(s->address);
139 free(s->nickname);
140 free(s);
141 }
142 }
143
144 void
145 raft_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
155 struct raft_server *
156 raft_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
169 struct raft_server *
170 raft_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
181 const char *
182 raft_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
188 const char *
189 raft_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
201 static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
202 raft_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
232 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
233 raft_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
243 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
244 raft_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
252 struct json *
253 raft_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
265 void
266 raft_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
280 void
281 raft_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);
287 dst->election_timer = src->election_timer;
288 }
289
290 void
291 raft_entry_uninit(struct raft_entry *e)
292 {
293 if (e) {
294 json_destroy(e->data);
295 json_destroy(e->servers);
296 }
297 }
298
299 struct json *
300 raft_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 }
311 if (e->election_timer) {
312 raft_put_uint64(json, "election_timer", e->election_timer);
313 }
314
315 return json;
316 }
317
318 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
319 raft_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 }
334 e->election_timer = raft_parse_optional_uint64(&p, "election_timer");
335
336 struct ovsdb_error *error = ovsdb_parser_finish(&p);
337 if (error) {
338 raft_entry_uninit(e);
339 }
340 return error;
341 }
342
343 bool
344 raft_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
352 void
353 raft_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
365 static void
366 raft_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");
409 h->snap.election_timer = raft_parse_optional_uint64(
410 p, "prev_election_timer");
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
419 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
420 raft_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
434 struct json *
435 raft_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));
463 if (h->snap.election_timer) {
464 raft_put_uint64(json, "prev_election_timer",
465 h->snap.election_timer);
466 }
467 }
468
469 return json;
470 }
471 \f
472 void
473 raft_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
499 static void
500 raft_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 }
571 r->entry.election_timer = raft_parse_optional_uint64(
572 p, "election_timer");
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
582 struct ovsdb_error * OVS_WARN_UNUSED_RESULT
583 raft_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
595 struct json *
596 raft_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 }
614 if (r->entry.election_timer) {
615 raft_put_uint64(json, "election_timer", r->entry.election_timer);
616 }
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. */
656 void
657 raft_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. */
667 static uint64_t
668 raft_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
675 uint64_t
676 raft_parse_optional_uint64(struct ovsdb_parser *p, const char *name)
677 {
678 return raft_parse_uint64__(p, name, true);
679 }
680
681 uint64_t
682 raft_parse_required_uint64(struct ovsdb_parser *p, const char *name)
683 {
684 return raft_parse_uint64__(p, name, false);
685 }
686
687 static int
688 raft_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
695 bool
696 raft_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. */
702 int
703 raft_parse_optional_boolean(struct ovsdb_parser *p, const char *name)
704 {
705 return raft_parse_boolean__(p, name, true);
706 }
707
708 static const char *
709 raft_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
716 const char *
717 raft_parse_required_string(struct ovsdb_parser *p, const char *name)
718 {
719 return raft_parse_string__(p, name, false);
720 }
721
722 const char *
723 raft_parse_optional_string(struct ovsdb_parser *p, const char *name)
724 {
725 return raft_parse_string__(p, name, true);
726 }
727
728 bool
729 raft_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
743 struct uuid
744 raft_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
751 bool
752 raft_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