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