]> git.proxmox.com Git - mirror_ovs.git/blame - ovsdb/ovsdb-tool.c
Merge branch 'dpdk_merge' of https://github.com/istokes/ovs into HEAD
[mirror_ovs.git] / ovsdb / ovsdb-tool.c
CommitLineData
f85f8ebb 1/*
19b276cb 2 * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2016, 2017 Nicira, Inc.
f85f8ebb
BP
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
4e92542c 25#include "column.h"
f85f8ebb
BP
26#include "command-line.h"
27#include "compiler.h"
e4476f74 28#include "dirs.h"
3e8a2ad1 29#include "openvswitch/dynamic-string.h"
8a777cf6 30#include "fatal-signal.h"
bd06962a 31#include "file.h"
1b1d2e6d 32#include "hash.h"
1e19e50e 33#include "lockfile.h"
41709ccc 34#include "log.h"
1b1d2e6d 35#include "openvswitch/hmap.h"
ee89ea7b 36#include "openvswitch/json.h"
f85f8ebb 37#include "ovsdb.h"
4e92542c 38#include "ovsdb-data.h"
f85f8ebb 39#include "ovsdb-error.h"
1b1d2e6d
BP
40#include "ovsdb-parser.h"
41#include "raft.h"
42#include "raft-private.h"
43#include "smap.h"
1e19e50e 44#include "socket-util.h"
1b1d2e6d 45#include "storage.h"
f85f8ebb
BP
46#include "table.h"
47#include "timeval.h"
1b1d2e6d 48#include "transaction.h"
f85f8ebb 49#include "util.h"
e6211adc 50#include "openvswitch/vlog.h"
5136ce49 51
c6782bb0
BP
52/* -m, --more: Verbosity level for "show-log" command output. */
53static int show_log_verbosity;
54
d6db7b3c
LR
55/* --role: RBAC role to use for "transact" and "query" commands. */
56static const char *rbac_role;
57
1b1d2e6d
BP
58/* --cid: Cluster ID for "join-cluster" command. */
59static struct uuid cid;
60
5f383751 61static const struct ovs_cmdl_command *get_all_commands(void);
f85f8ebb 62
cab50449 63OVS_NO_RETURN static void usage(void);
f85f8ebb
BP
64static void parse_options(int argc, char *argv[]);
65
e4476f74
BP
66static const char *default_db(void);
67static const char *default_schema(void);
68
f85f8ebb
BP
69int
70main(int argc, char *argv[])
71{
1636c761 72 struct ovs_cmdl_context ctx = { .argc = 0, };
f85f8ebb 73 set_program_name(argv[0]);
f85f8ebb 74 parse_options(argc, argv);
8a777cf6 75 fatal_ignore_sigpipe();
1b1d2e6d 76 fatal_signal_init();
1636c761
RB
77 ctx.argc = argc - optind;
78 ctx.argv = argv + optind;
79 ovs_cmdl_run_command(&ctx, get_all_commands());
f85f8ebb
BP
80 return 0;
81}
82
83static void
84parse_options(int argc, char *argv[])
85{
d6db7b3c 86 enum {
1b1d2e6d
BP
87 OPT_RBAC_ROLE = UCHAR_MAX + 1,
88 OPT_CID
d6db7b3c 89 };
07fc4ed3 90 static const struct option long_options[] = {
e3c17733 91 {"more", no_argument, NULL, 'm'},
d6db7b3c 92 {"rbac-role", required_argument, NULL, OPT_RBAC_ROLE},
1b1d2e6d 93 {"cid", required_argument, NULL, OPT_CID},
e3c17733
BP
94 {"verbose", optional_argument, NULL, 'v'},
95 {"help", no_argument, NULL, 'h'},
66fa2c88 96 {"option", no_argument, NULL, 'o'},
e3c17733
BP
97 {"version", no_argument, NULL, 'V'},
98 {NULL, 0, NULL, 0},
f85f8ebb 99 };
5f383751 100 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
f85f8ebb
BP
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) {
c6782bb0
BP
111 case 'm':
112 show_log_verbosity++;
113 break;
114
d6db7b3c
LR
115 case OPT_RBAC_ROLE:
116 rbac_role = optarg;
117 break;
118
1b1d2e6d
BP
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
f85f8ebb
BP
125 case 'h':
126 usage();
127
66fa2c88 128 case 'o':
5f383751 129 ovs_cmdl_print_options(long_options);
66fa2c88
AW
130 exit(EXIT_SUCCESS);
131
f85f8ebb 132 case 'V':
55d5bb44 133 ovs_print_version(0, 0);
f85f8ebb
BP
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
150static void
151usage(void)
152{
153 printf("%s: Open vSwitch database management utility\n"
154 "usage: %s [OPTIONS] COMMAND [ARG...]\n"
e4476f74 155 " create [DB [SCHEMA]] create DB with the given SCHEMA\n"
1b1d2e6d
BP
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"
e4476f74
BP
160 " compact [DB [DST]] compact DB in-place (or to DST)\n"
161 " convert [DB [SCHEMA [DST]]] convert DB to SCHEMA (to DST)\n"
439902cb 162 " db-name [DB] report name of schema used by DB\n"
e4476f74
BP
163 " db-version [DB] report version of schema used by DB\n"
164 " db-cksum [DB] report checksum of schema used by DB\n"
1b1d2e6d
BP
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"
439902cb 170 " schema-name [SCHEMA] report SCHEMA's name\n"
e4476f74
BP
171 " schema-version [SCHEMA] report SCHEMA's schema version\n"
172 " schema-cksum [SCHEMA] report SCHEMA's checksum\n"
1b1d2e6d 173 " compare-versions A OP B compare OVSDB schema version numbers\n"
e4476f74
BP
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());
f85f8ebb 180 vlog_usage();
d6db7b3c
LR
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");
f85f8ebb
BP
187 exit(EXIT_SUCCESS);
188}
e4476f74
BP
189
190static const char *
191default_db(void)
192{
193 static char *db;
194 if (!db) {
f973f2af 195 db = xasprintf("%s/conf.db", ovs_dbdir());
e4476f74
BP
196 }
197 return db;
198}
199
200static const char *
201default_schema(void)
202{
203 static char *schema;
204 if (!schema) {
205 schema = xasprintf("%s/vswitch.ovsschema", ovs_pkgdatadir());
206 }
207 return schema;
208}
f85f8ebb
BP
209\f
210static struct json *
211parse_json(const char *s)
212{
213 struct json *json = json_from_string(s);
214 if (json->type == JSON_STRING) {
fa37affa 215 ovs_fatal(0, "\"%s\": %s", s, json->string);
f85f8ebb
BP
216 }
217 return json;
218}
219
220static void
221print_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
229static void
230check_ovsdb_error(struct ovsdb_error *error)
231{
232 if (error) {
233 ovs_fatal(0, "%s", ovsdb_error_to_string(error));
234 }
235}
1b1d2e6d
BP
236
237/* Opens the standalone database 'filename' and returns its schema. */
238static struct ovsdb_schema *
239read_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}
f85f8ebb
BP
247\f
248static void
1636c761 249do_create(struct ovs_cmdl_context *ctx)
f85f8ebb 250{
1636c761
RB
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();
f85f8ebb 253 struct ovsdb_schema *schema;
41709ccc 254 struct ovsdb_log *log;
f85f8ebb
BP
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);
90cc4071 260 ovsdb_schema_destroy(schema);
f85f8ebb
BP
261
262 /* Create database file. */
19b276cb 263 check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC,
1e0b7e94 264 OVSDB_LOG_CREATE_EXCL, -1, &log));
1d0dead5 265 check_ovsdb_error(ovsdb_log_write_and_free(log, json));
f70b61d3 266 check_ovsdb_error(ovsdb_log_commit_block(log));
41709ccc 267 ovsdb_log_close(log);
1b1d2e6d 268}
f85f8ebb 269
1b1d2e6d
BP
270static void
271do_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);
f85f8ebb
BP
306}
307
1b1d2e6d
BP
308static void
309do_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
332static struct ovsdb_error *
333write_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
1d0dead5 343 error = ovsdb_log_write_and_free(log, ovsdb_schema_to_json(db->schema));
1b1d2e6d 344 if (!error) {
1d0dead5 345 error = ovsdb_log_write_and_free(log, ovsdb_to_txn_json(db, comment));
1b1d2e6d
BP
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. */
1e19e50e 360static void
a35ae81c 361compact_or_convert(const char *src_name_, const char *dst_name_,
1b1d2e6d 362 struct ovsdb_schema *new_schema, const char *comment)
1e19e50e 363{
a35ae81c 364 bool in_place = dst_name_ == NULL;
1e19e50e 365
a35ae81c
BP
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. */
1b1d2e6d
BP
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_));
a35ae81c 374
aebfc869 375 /* Lock the source, if we will be replacing it. */
1b1d2e6d 376 struct lockfile *src_lock = NULL;
1e19e50e 377 if (in_place) {
1b1d2e6d 378 int retval = lockfile_lock(src_name, &src_lock);
aebfc869
BP
379 if (retval) {
380 ovs_fatal(retval, "%s: failed to lock lockfile", src_name);
381 }
1e19e50e
BP
382 }
383
aebfc869 384 /* Get (temporary) destination and lock it. */
1b1d2e6d
BP
385 struct lockfile *dst_lock = NULL;
386 int retval = lockfile_lock(dst_name, &dst_lock);
1e19e50e
BP
387 if (retval) {
388 ovs_fatal(retval, "%s: failed to lock lockfile", dst_name);
389 }
390
391 /* Save a copy. */
1b1d2e6d
BP
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);
1e19e50e
BP
399
400 /* Replace source. */
401 if (in_place) {
10c119c3
GS
402#ifdef _WIN32
403 unlink(src_name);
404#endif
1e19e50e
BP
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);
1e19e50e
BP
410 lockfile_unlock(src_lock);
411 }
412
413 lockfile_unlock(dst_lock);
e49190c4 414
a35ae81c
BP
415 free(src_name);
416 free(dst_name);
1e19e50e
BP
417}
418
419static void
1636c761 420do_compact(struct ovs_cmdl_context *ctx)
1e19e50e 421{
1636c761
RB
422 const char *db = ctx->argc >= 2 ? ctx->argv[1] : default_db();
423 const char *target = ctx->argc >= 3 ? ctx->argv[2] : NULL;
e4476f74 424
8a07709c 425 compact_or_convert(db, target, NULL, "compacted by ovsdb-tool "VERSION);
1e19e50e
BP
426}
427
428static void
1636c761 429do_convert(struct ovs_cmdl_context *ctx)
1e19e50e 430{
1636c761
RB
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;
1e19e50e
BP
434 struct ovsdb_schema *new_schema;
435
e4476f74
BP
436 check_ovsdb_error(ovsdb_schema_from_file(schema, &new_schema));
437 compact_or_convert(db, target, new_schema,
8a07709c 438 "converted by ovsdb-tool "VERSION);
1e19e50e
BP
439}
440
403e3a25 441static void
1636c761 442do_needs_conversion(struct ovs_cmdl_context *ctx)
403e3a25 443{
1636c761
RB
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();
1b1d2e6d
BP
446 struct ovsdb_schema *schema1 = read_standalone_schema(db_file_name);
447 struct ovsdb_schema *schema2;
403e3a25 448
403e3a25
BP
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
439902cb
BP
455static void
456do_db_name(struct ovs_cmdl_context *ctx)
457{
458 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
439902cb 459
1b1d2e6d
BP
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);
439902cb
BP
484}
485
8159b984 486static void
1636c761 487do_db_version(struct ovs_cmdl_context *ctx)
8159b984 488{
1636c761 489 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
1b1d2e6d 490 struct ovsdb_schema *schema = read_standalone_schema(db_file_name);
8159b984 491
e1ebc8ce
BP
492 puts(schema->version);
493 ovsdb_schema_destroy(schema);
8159b984
BP
494}
495
6aa09313 496static void
1636c761 497do_db_cksum(struct ovs_cmdl_context *ctx)
6aa09313 498{
1636c761 499 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
1b1d2e6d 500 struct ovsdb_schema *schema = read_standalone_schema(db_file_name);
6aa09313
BP
501 puts(schema->cksum);
502 ovsdb_schema_destroy(schema);
503}
504
1b1d2e6d
BP
505static struct raft_metadata
506read_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
8159b984 523static void
1b1d2e6d
BP
524do_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
536static void
537do_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
545static void
546do_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
554static void
555do_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
567static void
568do_db_is_clustered(struct ovs_cmdl_context *ctx)
569{
570 do_db_has_magic(ctx, RAFT_MAGIC);
571}
572
573static void
574do_db_is_standalone(struct ovs_cmdl_context *ctx)
575{
576 do_db_has_magic(ctx, OVSDB_MAGIC);
577}
578
579static void
580do_schema_name(struct ovs_cmdl_context *ctx)
8159b984 581{
1636c761 582 const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
8159b984
BP
583 struct ovsdb_schema *schema;
584
585 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
1b1d2e6d 586 puts(schema->name);
8159b984
BP
587 ovsdb_schema_destroy(schema);
588}
589
6aa09313 590static void
1b1d2e6d 591do_schema_version(struct ovs_cmdl_context *ctx)
6aa09313 592{
1636c761 593 const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
6aa09313
BP
594 struct ovsdb_schema *schema;
595
596 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
1b1d2e6d 597 puts(schema->version);
6aa09313
BP
598 ovsdb_schema_destroy(schema);
599}
600
439902cb 601static void
1b1d2e6d 602do_schema_cksum(struct ovs_cmdl_context *ctx)
439902cb
BP
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));
1b1d2e6d 609 puts(schema->cksum);
439902cb
BP
610 ovsdb_schema_destroy(schema);
611}
612
1b1d2e6d 613/* Standalone databases only. */
f85f8ebb 614static void
1b1d2e6d 615transact(struct ovs_cmdl_context *ctx, bool rw)
f85f8ebb 616{
1b1d2e6d
BP
617 const char *db_file_name = ctx->argc >= 3 ? ctx->argv[1] : default_db();
618 const char *transaction = ctx->argv[ctx->argc - 1];
f85f8ebb 619
1b1d2e6d
BP
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);
f85f8ebb
BP
624 json_destroy(request);
625
626 print_and_free_json(result);
1b1d2e6d 627 ovsdb_destroy(ovsdb);
f85f8ebb
BP
628}
629
630static void
1636c761 631do_query(struct ovs_cmdl_context *ctx)
f85f8ebb 632{
1b1d2e6d 633 transact(ctx, false);
f85f8ebb
BP
634}
635
636static void
1636c761 637do_transact(struct ovs_cmdl_context *ctx)
f85f8ebb 638{
1b1d2e6d 639 transact(ctx, true);
f85f8ebb
BP
640}
641
c6782bb0 642static void
1b1d2e6d 643print_db_changes(struct shash *tables, struct smap *names,
4e92542c 644 const struct ovsdb_schema *schema)
c6782bb0
BP
645{
646 struct shash_node *n1;
647
1b1d2e6d 648 int i = 0;
c6782bb0
BP
649 SHASH_FOR_EACH (n1, tables) {
650 const char *table = n1->name;
4e92542c 651 struct ovsdb_table_schema *table_schema;
c6782bb0
BP
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
1b1d2e6d
BP
659 if (i++ == 0) {
660 putchar('\n');
661 }
662
663 table_schema = schema ? shash_find_data(&schema->tables, table) : NULL;
c6782bb0
BP
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;
c6782bb0 668
1b1d2e6d
BP
669 const char *old_name = smap_get(names, row_uuid);
670 char *new_name = NULL;
c6782bb0
BP
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);
c6782bb0
BP
677 }
678 }
679
5a0e4aec 680 printf(" table %s", table);
c6782bb0
BP
681
682 if (!old_name) {
683 if (new_name) {
a0a15a00 684 printf(" insert row %s (%.8s):\n", new_name, row_uuid);
c6782bb0
BP
685 } else {
686 printf(" insert row %.8s:\n", row_uuid);
687 }
688 } else {
a0a15a00 689 printf(" row %s (%.8s):\n", old_name, row_uuid);
c6782bb0
BP
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;
4e92542c 696 const struct ovsdb_column *column_schema;
c6782bb0 697 struct json *value = n3->data;
4e92542c
BP
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) {
4e92542c 705 const struct ovsdb_type *type;
a15fce8e 706 struct ovsdb_error *error;
4e92542c
BP
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);
a15fce8e
BP
718 } else {
719 ovsdb_error_destroy(error);
4e92542c
BP
720 }
721 }
722 if (!value_string) {
723 value_string = json_to_string(value, JSSF_SORT);
724 }
5a0e4aec 725 printf(" %s=%s\n", column, value_string);
c6782bb0
BP
726 free(value_string);
727 }
728 }
1b1d2e6d
BP
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));
c6782bb0
BP
736 }
737 } else if (columns->type == JSON_NULL) {
5a0e4aec 738 printf(" delete row\n");
1b1d2e6d 739 smap_remove(names, row_uuid);
c6782bb0
BP
740 }
741
1b1d2e6d 742 free(new_name);
c6782bb0
BP
743 }
744 }
745}
746
722f6301 747static void
1b1d2e6d
BP
748print_change_record(const struct json *json, const struct ovsdb_schema *schema,
749 struct smap *names)
722f6301 750{
1b1d2e6d
BP
751 if (!json || json->type != JSON_OBJECT) {
752 return;
753 }
722f6301 754
1b1d2e6d
BP
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
782static void
783do_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++) {
722f6301
BP
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);
4e92542c
BP
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);
1b1d2e6d
BP
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
812static void
813print_servers(const char *name, const struct json *servers)
814{
815 if (!servers) {
816 return;
817 }
722f6301 818
1b1d2e6d 819 printf(" %s: ", name);
1ff1065c 820
1b1d2e6d
BP
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
842static void
843print_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
870static void
871print_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
900static void
901print_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
945static void
946do_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
987static void
988do_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
1003struct server {
6bf2e3f6 1004 struct ovsdb_log *log;
1b1d2e6d
BP
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
1018struct 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
1031struct 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
1042struct 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
1051static const char *
1052get_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++) {
6233b87d 1056 struct server *s = &c->servers[i];
1b1d2e6d
BP
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
1066static struct leader *
1067find_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). */
1083static void
1084record_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
1119static struct commit *
1120find_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
1132static void
1133record_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
1151static void
1152do_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
1b1d2e6d 1168 check_ovsdb_error(ovsdb_log_open(s->filename, RAFT_MAGIC,
6bf2e3f6 1169 OVSDB_LOG_READ_ONLY, -1, &s->log));
1b1d2e6d
BP
1170
1171 struct json *json;
6bf2e3f6 1172 check_ovsdb_error(ovsdb_log_read(s->log, &json));
1b1d2e6d
BP
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);
6bf2e3f6 1178 ovsdb_log_close(s->log);
1b1d2e6d
BP
1179 continue;
1180 }
7073a83f
BP
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 }
1b1d2e6d
BP
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 }
6bf2e3f6
BP
1202 c.n_servers++;
1203 }
1204
1205 for (struct server *s = c.servers; s < &c.servers[c.n_servers]; s++) {
1b1d2e6d
BP
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 }
6bf2e3f6
BP
1224
1225 struct json *json;
1226 check_ovsdb_error(ovsdb_log_read(s->log, &json));
1b1d2e6d
BP
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
1b1d2e6d
BP
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;
1ff1065c
PI
1345 }
1346
1b1d2e6d
BP
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;
722f6301 1372 }
1b1d2e6d
BP
1373 }
1374
6bf2e3f6
BP
1375 ovsdb_log_close(s->log);
1376 s->log = NULL;
1b1d2e6d 1377 }
722f6301 1378
1b1d2e6d
BP
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);
c2d71875
BP
1388 if (leader && leader->log_end
1389 && commit && commit->index >= leader->log_end) {
1b1d2e6d
BP
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;
722f6301 1418 }
c6782bb0 1419
1b1d2e6d
BP
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);
c6782bb0 1438 }
722f6301 1439 }
722f6301 1440 }
c6782bb0 1441
1b1d2e6d
BP
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);
722f6301
BP
1471}
1472
1b1d2e6d
BP
1473static struct ovsdb_version
1474parse_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
1483static void
1484do_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
f85f8ebb 1515static void
1636c761 1516do_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
f85f8ebb
BP
1517{
1518 usage();
1519}
1520
451de37e 1521static void
1636c761 1522do_list_commands(struct ovs_cmdl_context *ctx OVS_UNUSED)
451de37e 1523{
5f383751 1524 ovs_cmdl_print_commands(get_all_commands());
451de37e
AW
1525}
1526
5f383751 1527static const struct ovs_cmdl_command all_commands[] = {
1f4a7252 1528 { "create", "[db [schema]]", 0, 2, do_create, OVS_RW },
1b1d2e6d
BP
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 },
1f4a7252
RM
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 },
439902cb 1535 { "db-name", "[db]", 0, 1, do_db_name, OVS_RO },
1f4a7252
RM
1536 { "db-version", "[db]", 0, 1, do_db_version, OVS_RO },
1537 { "db-cksum", "[db]", 0, 1, do_db_cksum, OVS_RO },
1b1d2e6d
BP
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 },
439902cb 1543 { "schema-name", "[schema]", 0, 1, do_schema_name, OVS_RO },
1f4a7252
RM
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 },
1b1d2e6d
BP
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 },
1f4a7252
RM
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 },
f85f8ebb 1554};
3815d6c2 1555
5f383751 1556static const struct ovs_cmdl_command *get_all_commands(void)
3815d6c2
LS
1557{
1558 return all_commands;
1559}