]> git.proxmox.com Git - mirror_ovs.git/blob - ovsdb/ovsdb-tool.c
ovsdb-tool: fix memory leak while running "db-is-standalone" command
[mirror_ovs.git] / ovsdb / ovsdb-tool.c
1 /*
2 * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2016, 2017 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 #include <errno.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "column.h"
26 #include "command-line.h"
27 #include "compiler.h"
28 #include "dirs.h"
29 #include "openvswitch/dynamic-string.h"
30 #include "fatal-signal.h"
31 #include "file.h"
32 #include "hash.h"
33 #include "lockfile.h"
34 #include "log.h"
35 #include "openvswitch/hmap.h"
36 #include "openvswitch/json.h"
37 #include "ovsdb.h"
38 #include "ovsdb-data.h"
39 #include "ovsdb-error.h"
40 #include "ovsdb-parser.h"
41 #include "raft.h"
42 #include "raft-private.h"
43 #include "smap.h"
44 #include "socket-util.h"
45 #include "storage.h"
46 #include "table.h"
47 #include "timeval.h"
48 #include "transaction.h"
49 #include "util.h"
50 #include "openvswitch/vlog.h"
51
52 /* -m, --more: Verbosity level for "show-log" command output. */
53 static int show_log_verbosity;
54
55 /* --role: RBAC role to use for "transact" and "query" commands. */
56 static const char *rbac_role;
57
58 /* --cid: Cluster ID for "join-cluster" command. */
59 static struct uuid cid;
60
61 static const struct ovs_cmdl_command *get_all_commands(void);
62
63 OVS_NO_RETURN static void usage(void);
64 static void parse_options(int argc, char *argv[]);
65
66 static const char *default_db(void);
67 static const char *default_schema(void);
68
69 int
70 main(int argc, char *argv[])
71 {
72 struct ovs_cmdl_context ctx = { .argc = 0, };
73 set_program_name(argv[0]);
74 parse_options(argc, argv);
75 fatal_ignore_sigpipe();
76 fatal_signal_init();
77 ctx.argc = argc - optind;
78 ctx.argv = argv + optind;
79 ovs_cmdl_run_command(&ctx, get_all_commands());
80 return 0;
81 }
82
83 static void
84 parse_options(int argc, char *argv[])
85 {
86 enum {
87 OPT_RBAC_ROLE = UCHAR_MAX + 1,
88 OPT_CID
89 };
90 static const struct option long_options[] = {
91 {"more", no_argument, NULL, 'm'},
92 {"rbac-role", required_argument, NULL, OPT_RBAC_ROLE},
93 {"cid", required_argument, NULL, OPT_CID},
94 {"verbose", optional_argument, NULL, 'v'},
95 {"help", no_argument, NULL, 'h'},
96 {"option", no_argument, NULL, 'o'},
97 {"version", no_argument, NULL, 'V'},
98 {NULL, 0, NULL, 0},
99 };
100 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
101
102 for (;;) {
103 int c;
104
105 c = getopt_long(argc, argv, short_options, long_options, NULL);
106 if (c == -1) {
107 break;
108 }
109
110 switch (c) {
111 case 'm':
112 show_log_verbosity++;
113 break;
114
115 case OPT_RBAC_ROLE:
116 rbac_role = optarg;
117 break;
118
119 case OPT_CID:
120 if (!uuid_from_string(&cid, optarg) || uuid_is_zero(&cid)) {
121 ovs_fatal(0, "%s: not a valid UUID", optarg);
122 }
123 break;
124
125 case 'h':
126 usage();
127
128 case 'o':
129 ovs_cmdl_print_options(long_options);
130 exit(EXIT_SUCCESS);
131
132 case 'V':
133 ovs_print_version(0, 0);
134 exit(EXIT_SUCCESS);
135
136 case 'v':
137 vlog_set_verbosity(optarg);
138 break;
139
140 case '?':
141 exit(EXIT_FAILURE);
142
143 default:
144 abort();
145 }
146 }
147 free(short_options);
148 }
149
150 static void
151 usage(void)
152 {
153 printf("%s: Open vSwitch database management utility\n"
154 "usage: %s [OPTIONS] COMMAND [ARG...]\n"
155 " create [DB [SCHEMA]] create DB with the given SCHEMA\n"
156 " create-cluster DB CONTENTS LOCAL\n"
157 " create clustered DB with given CONTENTS and LOCAL address\n"
158 " [--cid=UUID] join-cluster DB NAME LOCAL REMOTE...\n"
159 " join clustered DB with given NAME and LOCAL and REMOTE addrs\n"
160 " compact [DB [DST]] compact DB in-place (or to DST)\n"
161 " convert [DB [SCHEMA [DST]]] convert DB to SCHEMA (to DST)\n"
162 " db-name [DB] report name of schema used by DB\n"
163 " db-version [DB] report version of schema used by DB\n"
164 " db-cksum [DB] report checksum of schema used by DB\n"
165 " db-cid DB report cluster ID of clustered DB\n"
166 " db-sid DB report server ID of clustered DB\n"
167 " db-local-address DB report local address of clustered DB\n"
168 " db-is-clustered DB test whether DB is clustered\n"
169 " db-is-standalone DB test whether DB is standalone\n"
170 " schema-name [SCHEMA] report SCHEMA's name\n"
171 " schema-version [SCHEMA] report SCHEMA's schema version\n"
172 " schema-cksum [SCHEMA] report SCHEMA's checksum\n"
173 " compare-versions A OP B compare OVSDB schema version numbers\n"
174 " query [DB] TRNS execute read-only transaction on DB\n"
175 " transact [DB] TRNS execute read/write transaction on DB\n"
176 " cluster-to-standalone DB DB Convert clustered DB to\n"
177 " standalone DB when cluster is down and cannot be\n"
178 " revived\n"
179 " [-m]... show-log [DB] print DB's log entries\n"
180 "The default DB is %s.\n"
181 "The default SCHEMA is %s.\n",
182 program_name, program_name, default_db(), default_schema());
183 vlog_usage();
184 printf("\
185 \nOther options:\n\
186 -m, --more increase show-log verbosity\n\
187 --rbac-role=ROLE RBAC role for transact and query commands\n\
188 -h, --help display this help message\n\
189 -V, --version display version information\n");
190 exit(EXIT_SUCCESS);
191 }
192
193 static const char *
194 default_db(void)
195 {
196 static char *db;
197 if (!db) {
198 db = xasprintf("%s/conf.db", ovs_dbdir());
199 }
200 return db;
201 }
202
203 static const char *
204 default_schema(void)
205 {
206 static char *schema;
207 if (!schema) {
208 schema = xasprintf("%s/vswitch.ovsschema", ovs_pkgdatadir());
209 }
210 return schema;
211 }
212 \f
213 static struct json *
214 parse_json(const char *s)
215 {
216 struct json *json = json_from_string(s);
217 if (json->type == JSON_STRING) {
218 ovs_fatal(0, "\"%s\": %s", s, json->string);
219 }
220 return json;
221 }
222
223 static void
224 print_and_free_json(struct json *json)
225 {
226 char *string = json_to_string(json, JSSF_SORT);
227 json_destroy(json);
228 puts(string);
229 free(string);
230 }
231
232 static void
233 check_ovsdb_error(struct ovsdb_error *error)
234 {
235 if (error) {
236 ovs_fatal(0, "%s", ovsdb_error_to_string(error));
237 }
238 }
239
240 /* Opens the standalone database 'filename' and returns its schema. */
241 static struct ovsdb_schema *
242 read_standalone_schema(const char *filename)
243 {
244 struct ovsdb_storage *storage = ovsdb_storage_open_standalone(filename,
245 false);
246 struct ovsdb_schema *schema = ovsdb_storage_read_schema(storage);
247 ovsdb_storage_close(storage);
248 return schema;
249 }
250 \f
251 static void
252 do_create(struct ovs_cmdl_context *ctx)
253 {
254 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
255 const char *schema_file_name = ctx->argc >= 3 ? ctx->argv[2] : default_schema();
256 struct ovsdb_schema *schema;
257 struct ovsdb_log *log;
258 struct json *json;
259
260 /* Read schema from file and convert to JSON. */
261 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
262 json = ovsdb_schema_to_json(schema);
263 ovsdb_schema_destroy(schema);
264
265 /* Create database file. */
266 check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC,
267 OVSDB_LOG_CREATE_EXCL, -1, &log));
268 check_ovsdb_error(ovsdb_log_write_and_free(log, json));
269 check_ovsdb_error(ovsdb_log_commit_block(log));
270 ovsdb_log_close(log);
271 }
272
273 static void
274 do_create_cluster(struct ovs_cmdl_context *ctx)
275 {
276 const char *db_file_name = ctx->argv[1];
277 const char *src_file_name = ctx->argv[2];
278 const char *local = ctx->argv[3];
279
280 struct ovsdb_schema *schema;
281 struct json *data;
282
283 struct ovsdb_error *error = ovsdb_schema_from_file(src_file_name, &schema);
284 if (!error) {
285 /* It's just a schema file. */
286 data = json_object_create();
287 } else {
288 /* Not a schema file. Try reading it as a standalone database. */
289 ovsdb_error_destroy(error);
290
291 struct ovsdb *ovsdb = ovsdb_file_read(src_file_name, false);
292 char *comment = xasprintf("created from %s", src_file_name);
293 data = ovsdb_to_txn_json(ovsdb, comment);
294 free(comment);
295 schema = ovsdb_schema_clone(ovsdb->schema);
296 ovsdb_destroy(ovsdb);
297 }
298
299 ovsdb_schema_persist_ephemeral_columns(schema, src_file_name);
300
301 struct json *schema_json = ovsdb_schema_to_json(schema);
302
303 /* Create database file. */
304 struct json *snapshot = json_array_create_2(schema_json, data);
305 check_ovsdb_error(raft_create_cluster(db_file_name, schema->name,
306 local, snapshot));
307 ovsdb_schema_destroy(schema);
308 json_destroy(snapshot);
309 }
310
311 static void
312 do_join_cluster(struct ovs_cmdl_context *ctx)
313 {
314 const char *db_file_name = ctx->argv[1];
315 const char *name = ctx->argv[2];
316 const char *local = ctx->argv[3];
317
318 /* Check for a plausible 'name'. */
319 if (!ovsdb_parser_is_id(name)) {
320 ovs_fatal(0, "%s: not a valid schema name (use \"schema-name\" "
321 "command to find the correct name)", name);
322 }
323
324 /* Create database file. */
325 struct sset remote_addrs = SSET_INITIALIZER(&remote_addrs);
326 for (size_t i = 4; i < ctx->argc; i++) {
327 sset_add(&remote_addrs, ctx->argv[i]);
328 }
329 check_ovsdb_error(raft_join_cluster(db_file_name, name, local,
330 &remote_addrs,
331 uuid_is_zero(&cid) ? NULL : &cid));
332 sset_destroy(&remote_addrs);
333 }
334
335 static struct ovsdb_error *
336 write_standalone_db(const char *file_name, const char *comment,
337 const struct ovsdb *db)
338 {
339 struct ovsdb_log *log;
340 struct ovsdb_error *error = ovsdb_log_open(file_name, OVSDB_MAGIC,
341 OVSDB_LOG_CREATE, false, &log);
342 if (error) {
343 return error;
344 }
345
346 error = ovsdb_log_write_and_free(log, ovsdb_schema_to_json(db->schema));
347 if (!error) {
348 error = ovsdb_log_write_and_free(log, ovsdb_to_txn_json(db, comment));
349 }
350 ovsdb_log_close(log);
351
352 if (error) {
353 remove(file_name);
354 }
355 return error;
356 }
357
358 /* Reads 'src_name' and writes it back, compacted, to 'dst_name', adding the
359 * specified 'comment'. If 'new_schema' is nonull, converts the databse to
360 * that schema.
361 *
362 * Standalone databases only. */
363 static void
364 compact_or_convert(const char *src_name_, const char *dst_name_,
365 struct ovsdb_schema *new_schema, const char *comment)
366 {
367 bool in_place = dst_name_ == NULL;
368
369 /* Dereference symlinks for source and destination names. In the in-place
370 * case this ensures that, if the source name is a symlink, we replace its
371 * target instead of replacing the symlink by a regular file. In the
372 * non-in-place, this has the same effect for the destination name. */
373 char *src_name = follow_symlinks(src_name_);
374 char *dst_name = (in_place
375 ? xasprintf("%s.tmp", src_name)
376 : follow_symlinks(dst_name_));
377
378 /* Lock the source, if we will be replacing it. */
379 struct lockfile *src_lock = NULL;
380 if (in_place) {
381 int retval = lockfile_lock(src_name, &src_lock);
382 if (retval) {
383 ovs_fatal(retval, "%s: failed to lock lockfile", src_name);
384 }
385 }
386
387 /* Get (temporary) destination and lock it. */
388 struct lockfile *dst_lock = NULL;
389 int retval = lockfile_lock(dst_name, &dst_lock);
390 if (retval) {
391 ovs_fatal(retval, "%s: failed to lock lockfile", dst_name);
392 }
393
394 /* Save a copy. */
395 struct ovsdb *ovsdb = (new_schema
396 ? ovsdb_file_read_as_schema(src_name, new_schema)
397 : ovsdb_file_read(src_name, false));
398 ovsdb_storage_close(ovsdb->storage);
399 ovsdb->storage = NULL;
400 check_ovsdb_error(write_standalone_db(dst_name, comment, ovsdb));
401 ovsdb_destroy(ovsdb);
402
403 /* Replace source. */
404 if (in_place) {
405 #ifdef _WIN32
406 unlink(src_name);
407 #endif
408 if (rename(dst_name, src_name)) {
409 ovs_fatal(errno, "failed to rename \"%s\" to \"%s\"",
410 dst_name, src_name);
411 }
412 fsync_parent_dir(dst_name);
413 lockfile_unlock(src_lock);
414 }
415
416 lockfile_unlock(dst_lock);
417
418 free(src_name);
419 free(dst_name);
420 }
421
422 static void
423 do_compact(struct ovs_cmdl_context *ctx)
424 {
425 const char *db = ctx->argc >= 2 ? ctx->argv[1] : default_db();
426 const char *target = ctx->argc >= 3 ? ctx->argv[2] : NULL;
427
428 compact_or_convert(db, target, NULL, "compacted by ovsdb-tool "VERSION);
429 }
430
431 static void
432 do_convert(struct ovs_cmdl_context *ctx)
433 {
434 const char *db = ctx->argc >= 2 ? ctx->argv[1] : default_db();
435 const char *schema = ctx->argc >= 3 ? ctx->argv[2] : default_schema();
436 const char *target = ctx->argc >= 4 ? ctx->argv[3] : NULL;
437 struct ovsdb_schema *new_schema;
438
439 check_ovsdb_error(ovsdb_schema_from_file(schema, &new_schema));
440 compact_or_convert(db, target, new_schema,
441 "converted by ovsdb-tool "VERSION);
442 }
443
444 static void
445 do_needs_conversion(struct ovs_cmdl_context *ctx)
446 {
447 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
448 const char *schema_file_name = ctx->argc >= 3 ? ctx->argv[2] : default_schema();
449 struct ovsdb_schema *schema1 = read_standalone_schema(db_file_name);
450 struct ovsdb_schema *schema2;
451
452 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema2));
453 puts(ovsdb_schema_equal(schema1, schema2) ? "no" : "yes");
454 ovsdb_schema_destroy(schema1);
455 ovsdb_schema_destroy(schema2);
456 }
457
458 static void
459 do_db_name(struct ovs_cmdl_context *ctx)
460 {
461 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
462
463 struct ovsdb_log *log;
464 check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC"|"RAFT_MAGIC,
465 OVSDB_LOG_READ_ONLY, -1, &log));
466 if (!strcmp(ovsdb_log_get_magic(log), OVSDB_MAGIC)) {
467 struct json *schema_json;
468 check_ovsdb_error(ovsdb_log_read(log, &schema_json));
469
470 struct ovsdb_schema *schema;
471 check_ovsdb_error(ovsdb_schema_from_json(schema_json, &schema));
472
473 puts(schema->name);
474
475 ovsdb_schema_destroy(schema);
476 json_destroy(schema_json);
477 } else if (!strcmp(ovsdb_log_get_magic(log), RAFT_MAGIC)) {
478 struct raft_metadata md;
479 check_ovsdb_error(raft_read_metadata(log, &md));
480 puts(md.name);
481 raft_metadata_destroy(&md);
482 } else {
483 OVS_NOT_REACHED();
484 }
485
486 ovsdb_log_close(log);
487 }
488
489 static void
490 do_db_version(struct ovs_cmdl_context *ctx)
491 {
492 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
493 struct ovsdb_schema *schema = read_standalone_schema(db_file_name);
494
495 puts(schema->version);
496 ovsdb_schema_destroy(schema);
497 }
498
499 static void
500 do_db_cksum(struct ovs_cmdl_context *ctx)
501 {
502 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
503 struct ovsdb_schema *schema = read_standalone_schema(db_file_name);
504 puts(schema->cksum);
505 ovsdb_schema_destroy(schema);
506 }
507
508 static struct raft_metadata
509 read_cluster_metadata(const char *filename)
510 {
511 struct ovsdb_log *log;
512 check_ovsdb_error(ovsdb_log_open(filename, OVSDB_MAGIC"|"RAFT_MAGIC,
513 OVSDB_LOG_READ_ONLY, -1, &log));
514 if (strcmp(ovsdb_log_get_magic(log), RAFT_MAGIC)) {
515 ovs_fatal(0, "%s: not a clustered database", filename);
516 }
517
518 struct raft_metadata md;
519 check_ovsdb_error(raft_read_metadata(log, &md));
520
521 ovsdb_log_close(log);
522
523 return md;
524 }
525
526 static void
527 do_db_cid(struct ovs_cmdl_context *ctx)
528 {
529 const char *db_file_name = ctx->argv[1];
530 struct raft_metadata md = read_cluster_metadata(db_file_name);
531 if (uuid_is_zero(&md.cid)) {
532 fprintf(stderr, "%s: cluster ID not yet known\n", db_file_name);
533 exit(2);
534 }
535 printf(UUID_FMT"\n", UUID_ARGS(&md.cid));
536 raft_metadata_destroy(&md);
537 }
538
539 static void
540 do_db_sid(struct ovs_cmdl_context *ctx)
541 {
542 const char *db_file_name = ctx->argv[1];
543 struct raft_metadata md = read_cluster_metadata(db_file_name);
544 printf(UUID_FMT"\n", UUID_ARGS(&md.sid));
545 raft_metadata_destroy(&md);
546 }
547
548 static void
549 do_db_local_address(struct ovs_cmdl_context *ctx)
550 {
551 const char *db_file_name = ctx->argv[1];
552 struct raft_metadata md = read_cluster_metadata(db_file_name);
553 puts(md.local);
554 raft_metadata_destroy(&md);
555 }
556
557 static void
558 do_db_has_magic(struct ovs_cmdl_context *ctx, const char *magic)
559 {
560 const char *filename = ctx->argv[1];
561 struct ovsdb_log *log;
562
563 check_ovsdb_error(ovsdb_log_open(filename, OVSDB_MAGIC"|"RAFT_MAGIC,
564 OVSDB_LOG_READ_ONLY, -1, &log));
565 int cmp = strcmp(ovsdb_log_get_magic(log), magic);
566 ovsdb_log_close(log);
567 if (cmp) {
568 exit(2);
569 }
570 }
571
572 static void
573 do_db_is_clustered(struct ovs_cmdl_context *ctx)
574 {
575 do_db_has_magic(ctx, RAFT_MAGIC);
576 }
577
578 static void
579 do_db_is_standalone(struct ovs_cmdl_context *ctx)
580 {
581 do_db_has_magic(ctx, OVSDB_MAGIC);
582 }
583
584 static void
585 do_schema_name(struct ovs_cmdl_context *ctx)
586 {
587 const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
588 struct ovsdb_schema *schema;
589
590 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
591 puts(schema->name);
592 ovsdb_schema_destroy(schema);
593 }
594
595 static void
596 do_schema_version(struct ovs_cmdl_context *ctx)
597 {
598 const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
599 struct ovsdb_schema *schema;
600
601 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
602 puts(schema->version);
603 ovsdb_schema_destroy(schema);
604 }
605
606 static void
607 do_schema_cksum(struct ovs_cmdl_context *ctx)
608 {
609 const char *schema_file_name
610 = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
611 struct ovsdb_schema *schema;
612
613 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
614 puts(schema->cksum);
615 ovsdb_schema_destroy(schema);
616 }
617
618 /* Standalone databases only. */
619 static void
620 transact(struct ovs_cmdl_context *ctx, bool rw)
621 {
622 const char *db_file_name = ctx->argc >= 3 ? ctx->argv[1] : default_db();
623 const char *transaction = ctx->argv[ctx->argc - 1];
624
625 struct ovsdb *ovsdb = ovsdb_file_read(db_file_name, rw);
626 struct json *request = parse_json(transaction);
627 struct json *result = ovsdb_execute(ovsdb, NULL, request, false,
628 rbac_role, NULL, 0, NULL);
629 json_destroy(request);
630
631 print_and_free_json(result);
632 ovsdb_destroy(ovsdb);
633 }
634
635 static void
636 do_query(struct ovs_cmdl_context *ctx)
637 {
638 transact(ctx, false);
639 }
640
641 static void
642 do_transact(struct ovs_cmdl_context *ctx)
643 {
644 transact(ctx, true);
645 }
646
647 static void
648 print_db_changes(struct shash *tables, struct smap *names,
649 const struct ovsdb_schema *schema)
650 {
651 struct shash_node *n1;
652
653 int i = 0;
654 SHASH_FOR_EACH (n1, tables) {
655 const char *table = n1->name;
656 struct ovsdb_table_schema *table_schema;
657 struct json *rows = n1->data;
658 struct shash_node *n2;
659
660 if (n1->name[0] == '_' || rows->type != JSON_OBJECT) {
661 continue;
662 }
663
664 if (i++ == 0) {
665 putchar('\n');
666 }
667
668 table_schema = schema ? shash_find_data(&schema->tables, table) : NULL;
669 SHASH_FOR_EACH (n2, json_object(rows)) {
670 const char *row_uuid = n2->name;
671 struct json *columns = n2->data;
672 struct shash_node *n3;
673
674 const char *old_name = smap_get(names, row_uuid);
675 char *new_name = NULL;
676 if (columns->type == JSON_OBJECT) {
677 struct json *new_name_json;
678
679 new_name_json = shash_find_data(json_object(columns), "name");
680 if (new_name_json) {
681 new_name = json_to_string(new_name_json, JSSF_SORT);
682 }
683 }
684
685 printf(" table %s", table);
686
687 if (!old_name) {
688 if (new_name) {
689 printf(" insert row %s (%.8s):\n", new_name, row_uuid);
690 } else {
691 printf(" insert row %.8s:\n", row_uuid);
692 }
693 } else {
694 printf(" row %s (%.8s):\n", old_name, row_uuid);
695 }
696
697 if (columns->type == JSON_OBJECT) {
698 if (show_log_verbosity > 1) {
699 SHASH_FOR_EACH (n3, json_object(columns)) {
700 const char *column = n3->name;
701 const struct ovsdb_column *column_schema;
702 struct json *value = n3->data;
703 char *value_string = NULL;
704
705 column_schema =
706 (table_schema
707 ? shash_find_data(&table_schema->columns, column)
708 : NULL);
709 if (column_schema) {
710 const struct ovsdb_type *type;
711 struct ovsdb_error *error;
712 struct ovsdb_datum datum;
713
714 type = &column_schema->type;
715 error = ovsdb_datum_from_json(&datum, type,
716 value, NULL);
717 if (!error) {
718 struct ds s;
719
720 ds_init(&s);
721 ovsdb_datum_to_string(&datum, type, &s);
722 value_string = ds_steal_cstr(&s);
723 } else {
724 ovsdb_error_destroy(error);
725 }
726 }
727 if (!value_string) {
728 value_string = json_to_string(value, JSSF_SORT);
729 }
730 printf(" %s=%s\n", column, value_string);
731 free(value_string);
732 }
733 }
734
735 if (new_name && (!old_name || strcmp(old_name, new_name))) {
736 smap_replace_nocopy(names, row_uuid, new_name);
737 new_name = NULL;
738 } else if (!old_name) {
739 smap_add_nocopy(names, xstrdup(row_uuid),
740 xmemdup0(row_uuid, 8));
741 }
742 } else if (columns->type == JSON_NULL) {
743 printf(" delete row\n");
744 smap_remove(names, row_uuid);
745 }
746
747 free(new_name);
748 }
749 }
750 }
751
752 static void
753 print_change_record(const struct json *json, const struct ovsdb_schema *schema,
754 struct smap *names)
755 {
756 if (!json || json->type != JSON_OBJECT) {
757 return;
758 }
759
760 struct json *date, *comment;
761
762 date = shash_find_data(json_object(json), "_date");
763 if (date && date->type == JSON_INTEGER) {
764 long long int t = json_integer(date);
765 char *s;
766
767 if (t < INT32_MAX) {
768 /* Older versions of ovsdb wrote timestamps in seconds. */
769 t *= 1000;
770 }
771
772 s = xastrftime_msec(" %Y-%m-%d %H:%M:%S.###", t, true);
773 fputs(s, stdout);
774 free(s);
775 }
776
777 comment = shash_find_data(json_object(json), "_comment");
778 if (comment && comment->type == JSON_STRING) {
779 printf(" \"%s\"", json_string(comment));
780 }
781
782 if (show_log_verbosity > 0) {
783 print_db_changes(json_object(json), names, schema);
784 }
785 }
786
787 static void
788 do_show_log_standalone(struct ovsdb_log *log)
789 {
790 struct smap names = SMAP_INITIALIZER(&names);
791 struct ovsdb_schema *schema = NULL;
792
793 for (unsigned int i = 0; ; i++) {
794 struct json *json;
795
796 check_ovsdb_error(ovsdb_log_read(log, &json));
797 if (!json) {
798 break;
799 }
800
801 printf("record %u:", i);
802 if (i == 0) {
803 check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
804 printf(" \"%s\" schema, version=\"%s\", cksum=\"%s\"\n",
805 schema->name, schema->version, schema->cksum);
806 } else {
807 print_change_record(json, schema, &names);
808 }
809 json_destroy(json);
810 putchar('\n');
811 }
812
813 ovsdb_schema_destroy(schema);
814 smap_destroy(&names);
815 }
816
817 static void
818 print_servers(const char *name, const struct json *servers)
819 {
820 if (!servers) {
821 return;
822 }
823
824 printf(" %s: ", name);
825
826 const struct shash_node **nodes = shash_sort(json_object(servers));
827 size_t n = shash_count(json_object(servers));
828 for (size_t i = 0; i < n; i++) {
829 if (i > 0) {
830 printf(", ");
831 }
832
833 const struct shash_node *node = nodes[i];
834 printf("%.4s(", node->name);
835
836 const struct json *address = node->data;
837 char *s = json_to_string(address, JSSF_SORT);
838 fputs(s, stdout);
839 free(s);
840
841 putchar(')');
842 }
843 free(nodes);
844 putchar('\n');
845 }
846
847 static void
848 print_data(const char *prefix, const struct json *data,
849 struct ovsdb_schema **schemap, struct smap *names)
850 {
851 if (!data) {
852 return;
853 }
854
855 if (json_array(data)->n != 2) {
856 printf(" ***invalid data***\n");
857 return;
858 }
859
860 const struct json *schema_json = json_array(data)->elems[0];
861 if (schema_json->type != JSON_NULL) {
862 struct ovsdb_schema *schema;
863
864 check_ovsdb_error(ovsdb_schema_from_json(schema_json, &schema));
865 printf(" %sschema: \"%s\", version=\"%s\", cksum=\"%s\"\n",
866 prefix, schema->name, schema->version, schema->cksum);
867
868 ovsdb_schema_destroy(*schemap);
869 *schemap = schema;
870 }
871
872 print_change_record(json_array(data)->elems[1], *schemap, names);
873 }
874
875 static void
876 print_raft_header(const struct raft_header *h,
877 struct ovsdb_schema **schemap, struct smap *names)
878 {
879 printf(" name: \"%s\'\n", h->name);
880 printf(" local address: \"%s\"\n", h->local_address);
881 printf(" server_id: "SID_FMT"\n", SID_ARGS(&h->sid));
882 if (!uuid_is_zero(&h->cid)) {
883 printf(" cluster_id: "CID_FMT"\n", CID_ARGS(&h->cid));
884 }
885 if (!sset_is_empty(&h->remote_addresses)) {
886 printf(" remote_addresses:");
887
888 const char *s;
889 SSET_FOR_EACH (s, &h->remote_addresses) {
890 printf(" %s", s);
891 }
892 putchar('\n');
893 }
894 if (h->snap_index) {
895 printf(" prev_index: %"PRIu64"\n", h->snap_index);
896 printf(" prev_term: %"PRIu64"\n", h->snap.term);
897 print_servers("prev_servers", h->snap.servers);
898 if (!uuid_is_zero(&h->snap.eid)) {
899 printf(" prev_eid: %04x\n", uuid_prefix(&h->snap.eid, 4));
900 }
901 print_data("prev_", h->snap.data, schemap, names);
902 }
903 }
904
905 static void
906 print_raft_record(const struct raft_record *r,
907 struct ovsdb_schema **schemap, struct smap *names)
908 {
909 if (r->comment) {
910 printf(" comment: \"%s\"\n", r->comment);
911 }
912 if (r->term) {
913 printf(" term: %"PRIu64"\n", r->term);
914 }
915
916 switch (r->type) {
917 case RAFT_REC_ENTRY:
918 printf(" index: %"PRIu64"\n", r->entry.index);
919 print_servers("servers", r->entry.servers);
920 if (!uuid_is_zero(&r->entry.eid)) {
921 printf(" eid: %04x\n", uuid_prefix(&r->entry.eid, 4));
922 }
923 print_data("", r->entry.data, schemap, names);
924 break;
925
926 case RAFT_REC_TERM:
927 break;
928
929 case RAFT_REC_VOTE:
930 printf(" vote: "SID_FMT"\n", SID_ARGS(&r->sid));
931 break;
932
933 case RAFT_REC_NOTE:
934 printf(" note: \"%s\"\n", r->note);
935 break;
936
937 case RAFT_REC_COMMIT_INDEX:
938 printf(" commit_index: %"PRIu64"\n", r->commit_index);
939 break;
940
941 case RAFT_REC_LEADER:
942 printf(" leader: "SID_FMT"\n", SID_ARGS(&r->sid));
943 break;
944
945 default:
946 OVS_NOT_REACHED();
947 }
948 }
949
950 static void
951 raft_header_to_standalone_log(const struct raft_header *h,
952 struct ovsdb_log *db_log_data)
953 {
954 if (h->snap_index) {
955 if (!h->snap.data || json_array(h->snap.data)->n != 2) {
956 ovs_fatal(0, "Incorrect raft header data array length");
957 }
958
959 struct json *schema_json = json_array(h->snap.data)->elems[0];
960 if (schema_json->type != JSON_NULL) {
961 struct ovsdb_schema *schema;
962 check_ovsdb_error(ovsdb_schema_from_json(schema_json, &schema));
963 ovsdb_schema_destroy(schema);
964 check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
965 schema_json));
966 }
967
968 struct json *data_json = json_array(h->snap.data)->elems[1];
969 if (!data_json || data_json->type != JSON_OBJECT) {
970 ovs_fatal(0, "Invalid raft header data");
971 }
972 if (data_json->type != JSON_NULL) {
973 check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
974 data_json));
975 }
976 }
977 }
978
979 static void
980 raft_record_to_standalone_log(const struct raft_record *r,
981 struct ovsdb_log *db_log_data)
982 {
983 if (r->type == RAFT_REC_ENTRY) {
984 if (!r->entry.data) {
985 return;
986 }
987 if (json_array(r->entry.data)->n != 2) {
988 ovs_fatal(0, "Incorrect raft record array length");
989 }
990
991 struct json *data_json = json_array(r->entry.data)->elems[1];
992 if (data_json->type != JSON_NULL) {
993 check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
994 data_json));
995 }
996 }
997 }
998
999 static void
1000 do_show_log_cluster(struct ovsdb_log *log)
1001 {
1002 struct smap names = SMAP_INITIALIZER(&names);
1003 struct ovsdb_schema *schema = NULL;
1004 for (unsigned int i = 0; ; i++) {
1005 struct json *json;
1006 check_ovsdb_error(ovsdb_log_read(log, &json));
1007 if (!json) {
1008 break;
1009 }
1010
1011 printf("record %u:\n", i);
1012 struct ovsdb_error *error;
1013 if (i == 0) {
1014 struct raft_header h;
1015 error = raft_header_from_json(&h, json);
1016 if (!error) {
1017 print_raft_header(&h, &schema, &names);
1018 raft_header_uninit(&h);
1019 }
1020 } else {
1021 struct raft_record r;
1022 error = raft_record_from_json(&r, json);
1023 if (!error) {
1024 print_raft_record(&r, &schema, &names);
1025 raft_record_uninit(&r);
1026 }
1027 }
1028 if (error) {
1029 char *s = ovsdb_error_to_string_free(error);
1030 puts(s);
1031 free(s);
1032 }
1033
1034 putchar('\n');
1035 }
1036
1037 ovsdb_schema_destroy(schema);
1038 smap_destroy(&names);
1039 }
1040
1041 static void
1042 do_show_log(struct ovs_cmdl_context *ctx)
1043 {
1044 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
1045 struct ovsdb_log *log;
1046
1047 check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC"|"RAFT_MAGIC,
1048 OVSDB_LOG_READ_ONLY, -1, &log));
1049 if (!strcmp(ovsdb_log_get_magic(log), OVSDB_MAGIC)) {
1050 do_show_log_standalone(log);
1051 } else {
1052 do_show_log_cluster(log);
1053 }
1054 ovsdb_log_close(log);
1055 }
1056
1057 struct server {
1058 struct ovsdb_log *log;
1059 const char *filename;
1060 const char *nickname;
1061
1062 struct raft_header header;
1063
1064 struct raft_record *records;
1065 size_t n_records;
1066
1067 struct raft_entry *snap;
1068 struct raft_entry *entries;
1069 uint64_t log_start, log_end;
1070 };
1071
1072 struct leader {
1073 /* In struct cluster's 'leaders', indexed by 'term'. */
1074 struct hmap_node hmap_node;
1075
1076 /* This structure indicates that in 'term', 'server' reported that 'leader'
1077 * was elected leader. When 'log_end' is nonzero, it additionally
1078 * indicates 'leader''s log_end at the time it was elected. */
1079 uint64_t term;
1080 struct server *server;
1081 struct uuid leader;
1082 uint64_t log_end;
1083 };
1084
1085 struct commit {
1086 /* In struct cluster's 'commits', indexed by 'term'. */
1087 struct hmap_node hmap_node;
1088
1089 /* This structure indicates that in 'term', 'server' reported the commit
1090 * index as 'index'. */
1091 uint64_t term;
1092 struct server *server;
1093 uint64_t index; /* Commit index. */
1094 };
1095
1096 struct cluster {
1097 struct server *servers;
1098 size_t n_servers;
1099
1100 struct hmap leaders; /* Contains 'struct leader's. */
1101
1102 struct hmap commits; /* Contains 'struct commit's. */
1103 };
1104
1105 static const char *
1106 get_server_name(const struct cluster *c, const struct uuid *sid,
1107 char buf[SID_LEN + 1], size_t bufsize)
1108 {
1109 for (size_t i = 0; i < c->n_servers; i++) {
1110 struct server *s = &c->servers[i];
1111 if (uuid_equals(&s->header.sid, sid)) {
1112 return s->filename;
1113 }
1114 }
1115
1116 snprintf(buf, bufsize, SID_FMT, SID_ARGS(sid));
1117 return buf;
1118 }
1119
1120 static struct leader *
1121 find_leader(struct cluster *c, uint64_t term)
1122 {
1123 struct leader *leader;
1124 HMAP_FOR_EACH_WITH_HASH (leader, hmap_node, hash_uint64(term),
1125 &c->leaders) {
1126 if (term == leader->term) {
1127 return leader;
1128 }
1129 }
1130 return NULL;
1131 }
1132
1133 /* Records that 'server' reported that 'leader' was elected leader in 'term'.
1134 *
1135 * Checks the Election Safety Property: at most one leader may be elected in a
1136 * single term (see Figure 3.2). */
1137 static void
1138 record_leader(struct cluster *c, uint64_t term, struct server *server,
1139 const struct uuid *leader)
1140 {
1141 bool server_is_leader = uuid_equals(&server->header.sid, leader);
1142 struct leader *p = find_leader(c, term);
1143 if (p) {
1144 if (!uuid_equals(&p->leader, leader)) {
1145 char buf1[SID_LEN + 1];
1146 char buf2[SID_LEN + 1];
1147 ovs_fatal(0, "term %"PRIu64" has two different leaders: "
1148 "%s says that the leader is %s and "
1149 "%s says that the leader is %s",
1150 term,
1151 p->server->filename,
1152 get_server_name(c, &p->leader, buf1, sizeof buf1),
1153 server->filename,
1154 get_server_name(c, leader, buf2, sizeof buf2));
1155 }
1156 if (server_is_leader && server->log_end > p->log_end) {
1157 p->log_end = server->log_end;
1158 }
1159 } else {
1160 p = xmalloc(sizeof *p);
1161 hmap_insert(&c->leaders, &p->hmap_node, hash_uint64(term));
1162 p->term = term;
1163 p->server = server;
1164 p->leader = *leader;
1165 if (server_is_leader) {
1166 p->log_end = server->log_end;
1167 } else {
1168 p->log_end = 0;
1169 }
1170 }
1171 }
1172
1173 static struct commit *
1174 find_commit(struct cluster *c, uint64_t term)
1175 {
1176 struct commit *commit;
1177 HMAP_FOR_EACH_WITH_HASH (commit, hmap_node, hash_uint64(term),
1178 &c->commits) {
1179 if (term == commit->term) {
1180 return commit;
1181 }
1182 }
1183 return NULL;
1184 }
1185
1186 static void
1187 record_commit(struct cluster *c, uint64_t term, struct server *server,
1188 uint64_t commit_index)
1189 {
1190 struct commit *commit = find_commit(c, term);
1191 if (commit) {
1192 if (commit_index > commit->index) {
1193 commit->server = server;
1194 commit->index = commit_index;
1195 }
1196 } else {
1197 commit = xmalloc(sizeof *commit);
1198 hmap_insert(&c->commits, &commit->hmap_node, hash_uint64(term));
1199 commit->term = term;
1200 commit->server = server;
1201 commit->index = commit_index;
1202 }
1203 }
1204
1205 static void
1206 do_check_cluster(struct ovs_cmdl_context *ctx)
1207 {
1208 struct cluster c = {
1209 .servers = xzalloc((ctx->argc - 1) * sizeof *c.servers),
1210 .n_servers = 0,
1211 .leaders = HMAP_INITIALIZER(&c.leaders),
1212 .commits = HMAP_INITIALIZER(&c.commits),
1213 };
1214
1215 uint64_t min_term = UINT64_MAX;
1216 uint64_t max_term = 0;
1217
1218 for (int i = 1; i < ctx->argc; i++) {
1219 struct server *s = &c.servers[c.n_servers];
1220 s->filename = ctx->argv[i];
1221
1222 check_ovsdb_error(ovsdb_log_open(s->filename, RAFT_MAGIC,
1223 OVSDB_LOG_READ_ONLY, -1, &s->log));
1224
1225 struct json *json;
1226 check_ovsdb_error(ovsdb_log_read(s->log, &json));
1227 check_ovsdb_error(raft_header_from_json(&s->header, json));
1228 json_destroy(json);
1229
1230 if (s->header.joining) {
1231 printf("%s has not joined the cluster, omitting\n", s->filename);
1232 ovsdb_log_close(s->log);
1233 continue;
1234 }
1235 for (size_t j = 0; j < c.n_servers; j++) {
1236 if (uuid_equals(&s->header.sid, &c.servers[j].header.sid)) {
1237 ovs_fatal(0, "Duplicate server ID "SID_FMT" in %s and %s.",
1238 SID_ARGS(&s->header.sid),
1239 s->filename, c.servers[j].filename);
1240 }
1241 }
1242 if (c.n_servers > 0) {
1243 struct server *s0 = &c.servers[0];
1244 if (!uuid_equals(&s0->header.cid, &s->header.cid)) {
1245 ovs_fatal(0, "%s has cluster ID "CID_FMT" but %s "
1246 "has cluster ID "CID_FMT,
1247 s0->filename, CID_ARGS(&s0->header.cid),
1248 s->filename, CID_ARGS(&s->header.cid));
1249 }
1250 if (strcmp(s0->header.name, s->header.name)) {
1251 ovs_fatal(0, "%s is named \"%s\" but %s is named \"%s\"",
1252 s0->filename, s0->header.name,
1253 s->filename, s->header.name);
1254 }
1255 }
1256 c.n_servers++;
1257 }
1258
1259 for (struct server *s = c.servers; s < &c.servers[c.n_servers]; s++) {
1260 s->snap = &s->header.snap;
1261 s->log_start = s->log_end = s->header.snap_index + 1;
1262
1263 size_t allocated_records = 0;
1264 size_t allocated_entries = 0;
1265
1266 uint64_t term = 0; /* Current term. */
1267 struct uuid vote = UUID_ZERO; /* Server 's''s vote in 'term'. */
1268 struct uuid leader = UUID_ZERO; /* Cluster leader in 'term'. */
1269 uint64_t leader_rec_idx = 0; /* Index of last "leader" record. */
1270
1271 uint64_t commit_index = s->header.snap_index;
1272
1273 for (unsigned long long int rec_idx = 1; ; rec_idx++) {
1274 if (s->n_records >= allocated_records) {
1275 s->records = x2nrealloc(s->records, &allocated_records,
1276 sizeof *s->records);
1277 }
1278
1279 struct json *json;
1280 check_ovsdb_error(ovsdb_log_read(s->log, &json));
1281 if (!json) {
1282 break;
1283 }
1284 struct raft_record *r = &s->records[s->n_records++];
1285 check_ovsdb_error(raft_record_from_json(r, json));
1286 json_destroy(json);
1287
1288 if (r->term > term) {
1289 term = r->term;
1290 vote = UUID_ZERO;
1291 leader = UUID_ZERO;
1292 leader_rec_idx = 0;
1293 }
1294 if (term < min_term) {
1295 min_term = term;
1296 }
1297 if (term > max_term) {
1298 max_term = term;
1299 }
1300
1301 switch (r->type) {
1302 case RAFT_REC_ENTRY:
1303 if (r->entry.index < commit_index) {
1304 ovs_fatal(0, "%s: record %llu attempts to truncate log "
1305 "from %"PRIu64" to %"PRIu64" entries, but "
1306 "commit index is already %"PRIu64,
1307 s->filename, rec_idx,
1308 s->log_end, r->entry.index,
1309 commit_index);
1310 } else if (r->entry.index > s->log_end) {
1311 ovs_fatal(0, "%s: record %llu with index %"PRIu64" skips "
1312 "past expected index %"PRIu64, s->filename,
1313 rec_idx, r->entry.index, s->log_end);
1314 }
1315
1316 if (r->entry.index < s->log_end) {
1317 bool is_leader = uuid_equals(&s->header.sid, &leader);
1318 if (is_leader) {
1319 /* Leader Append-Only property (see Figure 3.2). */
1320 ovs_fatal(0, "%s: record %llu truncates log from "
1321 "%"PRIu64" to %"PRIu64" entries while "
1322 "server is leader", s->filename, rec_idx,
1323 s->log_end, r->entry.index);
1324 } else {
1325 /* This can happen, but it is unusual. */
1326 printf("%s: record %llu truncates log from %"PRIu64
1327 " to %"PRIu64" entries\n", s->filename, rec_idx,
1328 s->log_end, r->entry.index);
1329 }
1330 s->log_end = r->entry.index;
1331 }
1332
1333 uint64_t prev_term = (s->log_end > s->log_start
1334 ? s->entries[s->log_end
1335 - s->log_start - 1].term
1336 : s->snap->term);
1337 if (r->term < prev_term) {
1338 ovs_fatal(0, "%s: record %llu with index %"PRIu64" term "
1339 "%"PRIu64" precedes previous entry's term "
1340 "%"PRIu64, s->filename, rec_idx,
1341 r->entry.index, r->term, prev_term);
1342 }
1343
1344 uint64_t log_idx = s->log_end++ - s->log_start;
1345 if (log_idx >= allocated_entries) {
1346 s->entries = x2nrealloc(s->entries, &allocated_entries,
1347 sizeof *s->entries);
1348 }
1349 struct raft_entry *e = &s->entries[log_idx];
1350 e->term = r->term;
1351 e->data = r->entry.data;
1352 e->eid = r->entry.eid;
1353 e->servers = r->entry.servers;
1354 break;
1355
1356 case RAFT_REC_TERM:
1357 break;
1358
1359 case RAFT_REC_VOTE:
1360 if (r->term < term) {
1361 ovs_fatal(0, "%s: record %llu votes for term %"PRIu64" "
1362 "but current term is %"PRIu64, s->filename,
1363 rec_idx, r->term, term);
1364 } else if (!uuid_is_zero(&vote)
1365 && !uuid_equals(&vote, &r->sid)) {
1366 char buf1[SID_LEN + 1];
1367 char buf2[SID_LEN + 1];
1368 ovs_fatal(0, "%s: record %llu votes for %s in term "
1369 "%"PRIu64" but a previous record for the "
1370 "same term voted for %s", s->filename,
1371 rec_idx,
1372 get_server_name(&c, &vote, buf1, sizeof buf1),
1373 r->term,
1374 get_server_name(&c, &r->sid, buf2, sizeof buf2));
1375 } else {
1376 vote = r->sid;
1377 }
1378 break;
1379
1380 case RAFT_REC_NOTE:
1381 if (!strcmp(r->note, "left")) {
1382 printf("%s: record %llu shows that the server left the "
1383 "cluster\n", s->filename, rec_idx);
1384 }
1385 break;
1386
1387 case RAFT_REC_COMMIT_INDEX:
1388 if (r->commit_index < commit_index) {
1389 ovs_fatal(0, "%s: record %llu regresses commit index "
1390 "from %"PRIu64 " to %"PRIu64, s->filename,
1391 rec_idx, commit_index, r->commit_index);
1392 } else if (r->commit_index >= s->log_end) {
1393 ovs_fatal(0, "%s: record %llu advances commit index to "
1394 "%"PRIu64 " but last log index is %"PRIu64,
1395 s->filename, rec_idx, r->commit_index,
1396 s->log_end - 1);
1397 } else {
1398 commit_index = r->commit_index;
1399 }
1400
1401 record_commit(&c, term, s, r->commit_index);
1402 break;
1403
1404 case RAFT_REC_LEADER:
1405 if (!uuid_equals(&r->sid, &leader)) {
1406 if (uuid_is_zero(&leader)) {
1407 leader = r->sid;
1408 leader_rec_idx = rec_idx;
1409 } else {
1410 char buf1[SID_LEN + 1];
1411 char buf2[SID_LEN + 1];
1412 ovs_fatal(0, "%s: record %llu reports leader %s "
1413 "for term %"PRIu64" but record %"PRIu64" "
1414 "previously reported the leader as %s "
1415 "in that term",
1416 s->filename, rec_idx,
1417 get_server_name(&c, &r->sid,
1418 buf1, sizeof buf1),
1419 term, leader_rec_idx,
1420 get_server_name(&c, &leader,
1421 buf2, sizeof buf2));
1422 }
1423 }
1424 record_leader(&c, term, s, &r->sid);
1425 break;
1426 }
1427 }
1428
1429 ovsdb_log_close(s->log);
1430 s->log = NULL;
1431 }
1432
1433 /* Check the Leader Completeness property from Figure 3.2: If a log entry
1434 * is committed in a given term, then that entry will be present in the
1435 * logs of the leaders for all higher-numbered terms. */
1436 if (min_term == UINT64_MAX || max_term == 0) {
1437 ovs_fatal(0, "all logs are empty");
1438 }
1439 struct commit *commit = NULL;
1440 for (uint64_t term = min_term; term <= max_term; term++) {
1441 struct leader *leader = find_leader(&c, term);
1442 if (leader && leader->log_end
1443 && commit && commit->index >= leader->log_end) {
1444 ovs_fatal(0, "leader %s for term %"PRIu64" has log entries only "
1445 "up to index %"PRIu64", but index %"PRIu64" was "
1446 "committed in a previous term (e.g. by %s)",
1447 leader->server->filename, term, leader->log_end - 1,
1448 commit->index, commit->server->filename);
1449 }
1450
1451 struct commit *next = find_commit(&c, term);
1452 if (next && (!commit || next->index > commit->index)) {
1453 commit = next;
1454 }
1455 }
1456
1457 /* Section 3.5: Check the Log Matching Property in Figure 3.2:
1458 *
1459 * - If two entries in different logs have the same index and term, then
1460 * they store the same command.
1461 *
1462 * - If two entries in different logs have the same index and term, then
1463 * the logs are identical in all preceding entries.
1464 */
1465 for (size_t i = 0; i < c.n_servers; i++) {
1466 for (size_t j = 0; j < c.n_servers; j++) {
1467 struct server *a = &c.servers[i];
1468 struct server *b = &c.servers[j];
1469
1470 if (a == b) {
1471 continue;
1472 }
1473
1474 bool must_equal = false;
1475 for (uint64_t idx = MIN(a->log_end, b->log_end) - 1;
1476 idx >= MAX(a->log_start, b->log_start);
1477 idx--) {
1478 const struct raft_entry *ae = &a->entries[idx - a->log_start];
1479 const struct raft_entry *be = &b->entries[idx - b->log_start];
1480 if (ae->term == be->term) {
1481 must_equal = true;
1482 }
1483 if (!must_equal || raft_entry_equals(ae, be)) {
1484 continue;
1485 }
1486 char *as = json_to_string(raft_entry_to_json(ae), JSSF_SORT);
1487 char *bs = json_to_string(raft_entry_to_json(be), JSSF_SORT);
1488 ovs_fatal(0, "log entries with index %"PRIu64" differ:\n"
1489 "%s has %s\n"
1490 "%s has %s",
1491 idx, a->filename, as, b->filename, bs);
1492 }
1493 }
1494 }
1495
1496 /* Clean up. */
1497
1498 for (size_t i = 0; i < c.n_servers; i++) {
1499 struct server *s = &c.servers[i];
1500
1501 raft_header_uninit(&s->header);
1502 for (size_t j = 0; j < s->n_records; j++) {
1503 struct raft_record *r = &s->records[j];
1504
1505 raft_record_uninit(r);
1506 }
1507 free(s->records);
1508 free(s->entries);
1509 }
1510 free(c.servers);
1511
1512 struct commit *next_commit;
1513 HMAP_FOR_EACH_SAFE (commit, next_commit, hmap_node, &c.commits) {
1514 hmap_remove(&c.commits, &commit->hmap_node);
1515 free(commit);
1516 }
1517 hmap_destroy(&c.commits);
1518
1519 struct leader *leader, *next_leader;
1520 HMAP_FOR_EACH_SAFE (leader, next_leader, hmap_node, &c.leaders) {
1521 hmap_remove(&c.leaders, &leader->hmap_node);
1522 free(leader);
1523 }
1524 hmap_destroy(&c.leaders);
1525 }
1526
1527 static struct ovsdb_version
1528 parse_version(const char *s)
1529 {
1530 struct ovsdb_version version;
1531 if (!ovsdb_parse_version(s, &version)) {
1532 ovs_fatal(0, "%s: not an OVSDB version number in format x.y.z", s);
1533 }
1534 return version;
1535 }
1536
1537 static void
1538 do_compare_versions(struct ovs_cmdl_context *ctx)
1539 {
1540 struct ovsdb_version a = parse_version(ctx->argv[1]);
1541 struct ovsdb_version b = parse_version(ctx->argv[3]);
1542 int cmp = (a.x != b.x ? (a.x > b.x ? 1 : -1)
1543 : a.y != b.y ? (a.y > b.y ? 1 : -1)
1544 : a.z != b.z ? (a.z > b.z ? 1 : -1)
1545 : 0);
1546
1547 const char *op = ctx->argv[2];
1548 bool result;
1549 if (!strcmp(op, "<")) {
1550 result = cmp < 0;
1551 } else if (!strcmp(op, "<=")) {
1552 result = cmp <= 0;
1553 } else if (!strcmp(op, "==")) {
1554 result = cmp == 0;
1555 } else if (!strcmp(op, ">=")) {
1556 result = cmp >= 0;
1557 } else if (!strcmp(op, ">")) {
1558 result = cmp > 0;
1559 } else if (!strcmp(op, "!=")) {
1560 result = cmp != 0;
1561 } else {
1562 ovs_fatal(0, "%s: not a relational operator", op);
1563 }
1564
1565 exit(result ? 0 : 2);
1566 }
1567
1568 static void
1569 do_convert_to_standalone(struct ovsdb_log *log, struct ovsdb_log *db_log_data)
1570 {
1571 for (unsigned int i = 0; ; i++) {
1572 struct json *json;
1573 check_ovsdb_error(ovsdb_log_read(log, &json));
1574 if (!json) {
1575 break;
1576 }
1577
1578 if (i == 0) {
1579 struct raft_header h;
1580 check_ovsdb_error(raft_header_from_json(&h, json));
1581 raft_header_to_standalone_log(&h, db_log_data);
1582 raft_header_uninit(&h);
1583 } else {
1584 struct raft_record r;
1585 check_ovsdb_error(raft_record_from_json(&r, json));
1586 raft_record_to_standalone_log(&r, db_log_data);
1587 raft_record_uninit(&r);
1588 }
1589 }
1590 }
1591
1592 static void
1593 do_cluster_standalone(struct ovs_cmdl_context *ctx)
1594 {
1595 const char *db_file_name = ctx->argv[1];
1596 const char *cluster_db_file_name = ctx->argv[2];
1597 struct ovsdb_log *log;
1598 struct ovsdb_log *db_log_data;
1599
1600 check_ovsdb_error(ovsdb_log_open(cluster_db_file_name,
1601 OVSDB_MAGIC"|"RAFT_MAGIC,
1602 OVSDB_LOG_READ_ONLY, -1, &log));
1603 check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC,
1604 OVSDB_LOG_CREATE_EXCL, -1, &db_log_data));
1605 if (strcmp(ovsdb_log_get_magic(log), RAFT_MAGIC) != 0) {
1606 ovs_fatal(0, "Database is not clustered db.\n");
1607 }
1608 do_convert_to_standalone(log, db_log_data);
1609 check_ovsdb_error(ovsdb_log_commit_block(db_log_data));
1610 ovsdb_log_close(db_log_data);
1611 ovsdb_log_close(log);
1612 }
1613
1614 static void
1615 do_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
1616 {
1617 usage();
1618 }
1619
1620 static void
1621 do_list_commands(struct ovs_cmdl_context *ctx OVS_UNUSED)
1622 {
1623 ovs_cmdl_print_commands(get_all_commands());
1624 }
1625
1626 static const struct ovs_cmdl_command all_commands[] = {
1627 { "create", "[db [schema]]", 0, 2, do_create, OVS_RW },
1628 { "create-cluster", "db contents local", 3, 3, do_create_cluster, OVS_RW },
1629 { "join-cluster", "db name local remote...", 4, INT_MAX, do_join_cluster,
1630 OVS_RW },
1631 { "compact", "[db [dst]]", 0, 2, do_compact, OVS_RW },
1632 { "convert", "[db [schema [dst]]]", 0, 3, do_convert, OVS_RW },
1633 { "needs-conversion", NULL, 0, 2, do_needs_conversion, OVS_RO },
1634 { "db-name", "[db]", 0, 1, do_db_name, OVS_RO },
1635 { "db-version", "[db]", 0, 1, do_db_version, OVS_RO },
1636 { "db-cksum", "[db]", 0, 1, do_db_cksum, OVS_RO },
1637 { "db-cid", "db", 1, 1, do_db_cid, OVS_RO },
1638 { "db-sid", "db", 1, 1, do_db_sid, OVS_RO },
1639 { "db-local-address", "db", 1, 1, do_db_local_address, OVS_RO },
1640 { "db-is-clustered", "db", 1, 1, do_db_is_clustered, OVS_RO },
1641 { "db-is-standalone", "db", 1, 1, do_db_is_standalone, OVS_RO },
1642 { "schema-name", "[schema]", 0, 1, do_schema_name, OVS_RO },
1643 { "schema-version", "[schema]", 0, 1, do_schema_version, OVS_RO },
1644 { "schema-cksum", "[schema]", 0, 1, do_schema_cksum, OVS_RO },
1645 { "query", "[db] trns", 1, 2, do_query, OVS_RO },
1646 { "transact", "[db] trns", 1, 2, do_transact, OVS_RO },
1647 { "show-log", "[db]", 0, 1, do_show_log, OVS_RO },
1648 { "check-cluster", "db...", 1, INT_MAX, do_check_cluster, OVS_RO },
1649 { "compare-versions", "a op b", 3, 3, do_compare_versions, OVS_RO },
1650 { "help", NULL, 0, INT_MAX, do_help, OVS_RO },
1651 { "list-commands", NULL, 0, INT_MAX, do_list_commands, OVS_RO },
1652 { "cluster-to-standalone", "db clusterdb", 2, 2,
1653 do_cluster_standalone, OVS_RW },
1654 { NULL, NULL, 2, 2, NULL, OVS_RO },
1655 };
1656
1657 static const struct ovs_cmdl_command *get_all_commands(void)
1658 {
1659 return all_commands;
1660 }