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