]> git.proxmox.com Git - mirror_ovs.git/blame - ovsdb/ovsdb-tool.c
ovsdb-tool: Convert clustered db to standalone db.
[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));
565 if (strcmp(ovsdb_log_get_magic(log), magic)) {
566 exit(2);
567 }
568}
569
570static void
571do_db_is_clustered(struct ovs_cmdl_context *ctx)
572{
573 do_db_has_magic(ctx, RAFT_MAGIC);
574}
575
576static void
577do_db_is_standalone(struct ovs_cmdl_context *ctx)
578{
579 do_db_has_magic(ctx, OVSDB_MAGIC);
580}
581
582static void
583do_schema_name(struct ovs_cmdl_context *ctx)
8159b984 584{
1636c761 585 const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
8159b984
BP
586 struct ovsdb_schema *schema;
587
588 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
1b1d2e6d 589 puts(schema->name);
8159b984
BP
590 ovsdb_schema_destroy(schema);
591}
592
6aa09313 593static void
1b1d2e6d 594do_schema_version(struct ovs_cmdl_context *ctx)
6aa09313 595{
1636c761 596 const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
6aa09313
BP
597 struct ovsdb_schema *schema;
598
599 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
1b1d2e6d 600 puts(schema->version);
6aa09313
BP
601 ovsdb_schema_destroy(schema);
602}
603
439902cb 604static void
1b1d2e6d 605do_schema_cksum(struct ovs_cmdl_context *ctx)
439902cb
BP
606{
607 const char *schema_file_name
608 = ctx->argc >= 2 ? ctx->argv[1] : default_schema();
609 struct ovsdb_schema *schema;
610
611 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
1b1d2e6d 612 puts(schema->cksum);
439902cb
BP
613 ovsdb_schema_destroy(schema);
614}
615
1b1d2e6d 616/* Standalone databases only. */
f85f8ebb 617static void
1b1d2e6d 618transact(struct ovs_cmdl_context *ctx, bool rw)
f85f8ebb 619{
1b1d2e6d
BP
620 const char *db_file_name = ctx->argc >= 3 ? ctx->argv[1] : default_db();
621 const char *transaction = ctx->argv[ctx->argc - 1];
f85f8ebb 622
1b1d2e6d
BP
623 struct ovsdb *ovsdb = ovsdb_file_read(db_file_name, rw);
624 struct json *request = parse_json(transaction);
625 struct json *result = ovsdb_execute(ovsdb, NULL, request, false,
626 rbac_role, NULL, 0, NULL);
f85f8ebb
BP
627 json_destroy(request);
628
629 print_and_free_json(result);
1b1d2e6d 630 ovsdb_destroy(ovsdb);
f85f8ebb
BP
631}
632
633static void
1636c761 634do_query(struct ovs_cmdl_context *ctx)
f85f8ebb 635{
1b1d2e6d 636 transact(ctx, false);
f85f8ebb
BP
637}
638
639static void
1636c761 640do_transact(struct ovs_cmdl_context *ctx)
f85f8ebb 641{
1b1d2e6d 642 transact(ctx, true);
f85f8ebb
BP
643}
644
c6782bb0 645static void
1b1d2e6d 646print_db_changes(struct shash *tables, struct smap *names,
4e92542c 647 const struct ovsdb_schema *schema)
c6782bb0
BP
648{
649 struct shash_node *n1;
650
1b1d2e6d 651 int i = 0;
c6782bb0
BP
652 SHASH_FOR_EACH (n1, tables) {
653 const char *table = n1->name;
4e92542c 654 struct ovsdb_table_schema *table_schema;
c6782bb0
BP
655 struct json *rows = n1->data;
656 struct shash_node *n2;
657
658 if (n1->name[0] == '_' || rows->type != JSON_OBJECT) {
659 continue;
660 }
661
1b1d2e6d
BP
662 if (i++ == 0) {
663 putchar('\n');
664 }
665
666 table_schema = schema ? shash_find_data(&schema->tables, table) : NULL;
c6782bb0
BP
667 SHASH_FOR_EACH (n2, json_object(rows)) {
668 const char *row_uuid = n2->name;
669 struct json *columns = n2->data;
670 struct shash_node *n3;
c6782bb0 671
1b1d2e6d
BP
672 const char *old_name = smap_get(names, row_uuid);
673 char *new_name = NULL;
c6782bb0
BP
674 if (columns->type == JSON_OBJECT) {
675 struct json *new_name_json;
676
677 new_name_json = shash_find_data(json_object(columns), "name");
678 if (new_name_json) {
679 new_name = json_to_string(new_name_json, JSSF_SORT);
c6782bb0
BP
680 }
681 }
682
5a0e4aec 683 printf(" table %s", table);
c6782bb0
BP
684
685 if (!old_name) {
686 if (new_name) {
a0a15a00 687 printf(" insert row %s (%.8s):\n", new_name, row_uuid);
c6782bb0
BP
688 } else {
689 printf(" insert row %.8s:\n", row_uuid);
690 }
691 } else {
a0a15a00 692 printf(" row %s (%.8s):\n", old_name, row_uuid);
c6782bb0
BP
693 }
694
695 if (columns->type == JSON_OBJECT) {
696 if (show_log_verbosity > 1) {
697 SHASH_FOR_EACH (n3, json_object(columns)) {
698 const char *column = n3->name;
4e92542c 699 const struct ovsdb_column *column_schema;
c6782bb0 700 struct json *value = n3->data;
4e92542c
BP
701 char *value_string = NULL;
702
703 column_schema =
704 (table_schema
705 ? shash_find_data(&table_schema->columns, column)
706 : NULL);
707 if (column_schema) {
4e92542c 708 const struct ovsdb_type *type;
a15fce8e 709 struct ovsdb_error *error;
4e92542c
BP
710 struct ovsdb_datum datum;
711
712 type = &column_schema->type;
713 error = ovsdb_datum_from_json(&datum, type,
714 value, NULL);
715 if (!error) {
716 struct ds s;
717
718 ds_init(&s);
719 ovsdb_datum_to_string(&datum, type, &s);
720 value_string = ds_steal_cstr(&s);
a15fce8e
BP
721 } else {
722 ovsdb_error_destroy(error);
4e92542c
BP
723 }
724 }
725 if (!value_string) {
726 value_string = json_to_string(value, JSSF_SORT);
727 }
5a0e4aec 728 printf(" %s=%s\n", column, value_string);
c6782bb0
BP
729 free(value_string);
730 }
731 }
1b1d2e6d
BP
732
733 if (new_name && (!old_name || strcmp(old_name, new_name))) {
734 smap_replace_nocopy(names, row_uuid, new_name);
735 new_name = NULL;
736 } else if (!old_name) {
737 smap_add_nocopy(names, xstrdup(row_uuid),
738 xmemdup0(row_uuid, 8));
c6782bb0
BP
739 }
740 } else if (columns->type == JSON_NULL) {
5a0e4aec 741 printf(" delete row\n");
1b1d2e6d 742 smap_remove(names, row_uuid);
c6782bb0
BP
743 }
744
1b1d2e6d 745 free(new_name);
c6782bb0
BP
746 }
747 }
748}
749
722f6301 750static void
1b1d2e6d
BP
751print_change_record(const struct json *json, const struct ovsdb_schema *schema,
752 struct smap *names)
722f6301 753{
1b1d2e6d
BP
754 if (!json || json->type != JSON_OBJECT) {
755 return;
756 }
722f6301 757
1b1d2e6d
BP
758 struct json *date, *comment;
759
760 date = shash_find_data(json_object(json), "_date");
761 if (date && date->type == JSON_INTEGER) {
762 long long int t = json_integer(date);
763 char *s;
764
765 if (t < INT32_MAX) {
766 /* Older versions of ovsdb wrote timestamps in seconds. */
767 t *= 1000;
768 }
769
770 s = xastrftime_msec(" %Y-%m-%d %H:%M:%S.###", t, true);
771 fputs(s, stdout);
772 free(s);
773 }
774
775 comment = shash_find_data(json_object(json), "_comment");
776 if (comment && comment->type == JSON_STRING) {
777 printf(" \"%s\"", json_string(comment));
778 }
779
780 if (show_log_verbosity > 0) {
781 print_db_changes(json_object(json), names, schema);
782 }
783}
784
785static void
786do_show_log_standalone(struct ovsdb_log *log)
787{
788 struct smap names = SMAP_INITIALIZER(&names);
789 struct ovsdb_schema *schema = NULL;
790
791 for (unsigned int i = 0; ; i++) {
722f6301
BP
792 struct json *json;
793
794 check_ovsdb_error(ovsdb_log_read(log, &json));
795 if (!json) {
796 break;
797 }
798
799 printf("record %u:", i);
4e92542c
BP
800 if (i == 0) {
801 check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
802 printf(" \"%s\" schema, version=\"%s\", cksum=\"%s\"\n",
803 schema->name, schema->version, schema->cksum);
1b1d2e6d
BP
804 } else {
805 print_change_record(json, schema, &names);
806 }
807 json_destroy(json);
808 putchar('\n');
809 }
810
811 ovsdb_schema_destroy(schema);
812 smap_destroy(&names);
813}
814
815static void
816print_servers(const char *name, const struct json *servers)
817{
818 if (!servers) {
819 return;
820 }
722f6301 821
1b1d2e6d 822 printf(" %s: ", name);
1ff1065c 823
1b1d2e6d
BP
824 const struct shash_node **nodes = shash_sort(json_object(servers));
825 size_t n = shash_count(json_object(servers));
826 for (size_t i = 0; i < n; i++) {
827 if (i > 0) {
828 printf(", ");
829 }
830
831 const struct shash_node *node = nodes[i];
832 printf("%.4s(", node->name);
833
834 const struct json *address = node->data;
835 char *s = json_to_string(address, JSSF_SORT);
836 fputs(s, stdout);
837 free(s);
838
839 putchar(')');
840 }
841 free(nodes);
842 putchar('\n');
843}
844
845static void
846print_data(const char *prefix, const struct json *data,
847 struct ovsdb_schema **schemap, struct smap *names)
848{
849 if (!data) {
850 return;
851 }
852
853 if (json_array(data)->n != 2) {
854 printf(" ***invalid data***\n");
855 return;
856 }
857
858 const struct json *schema_json = json_array(data)->elems[0];
859 if (schema_json->type != JSON_NULL) {
860 struct ovsdb_schema *schema;
861
862 check_ovsdb_error(ovsdb_schema_from_json(schema_json, &schema));
863 printf(" %sschema: \"%s\", version=\"%s\", cksum=\"%s\"\n",
864 prefix, schema->name, schema->version, schema->cksum);
865
866 ovsdb_schema_destroy(*schemap);
867 *schemap = schema;
868 }
869
870 print_change_record(json_array(data)->elems[1], *schemap, names);
871}
872
873static void
874print_raft_header(const struct raft_header *h,
875 struct ovsdb_schema **schemap, struct smap *names)
876{
877 printf(" name: \"%s\'\n", h->name);
878 printf(" local address: \"%s\"\n", h->local_address);
879 printf(" server_id: "SID_FMT"\n", SID_ARGS(&h->sid));
880 if (!uuid_is_zero(&h->cid)) {
881 printf(" cluster_id: "CID_FMT"\n", CID_ARGS(&h->cid));
882 }
883 if (!sset_is_empty(&h->remote_addresses)) {
884 printf(" remote_addresses:");
885
886 const char *s;
887 SSET_FOR_EACH (s, &h->remote_addresses) {
888 printf(" %s", s);
889 }
890 putchar('\n');
891 }
892 if (h->snap_index) {
893 printf(" prev_index: %"PRIu64"\n", h->snap_index);
894 printf(" prev_term: %"PRIu64"\n", h->snap.term);
895 print_servers("prev_servers", h->snap.servers);
896 if (!uuid_is_zero(&h->snap.eid)) {
897 printf(" prev_eid: %04x\n", uuid_prefix(&h->snap.eid, 4));
898 }
899 print_data("prev_", h->snap.data, schemap, names);
900 }
901}
902
903static void
904print_raft_record(const struct raft_record *r,
905 struct ovsdb_schema **schemap, struct smap *names)
906{
907 if (r->comment) {
908 printf(" comment: \"%s\"\n", r->comment);
909 }
910 if (r->term) {
911 printf(" term: %"PRIu64"\n", r->term);
912 }
913
914 switch (r->type) {
915 case RAFT_REC_ENTRY:
916 printf(" index: %"PRIu64"\n", r->entry.index);
917 print_servers("servers", r->entry.servers);
918 if (!uuid_is_zero(&r->entry.eid)) {
919 printf(" eid: %04x\n", uuid_prefix(&r->entry.eid, 4));
920 }
921 print_data("", r->entry.data, schemap, names);
922 break;
923
924 case RAFT_REC_TERM:
925 break;
926
927 case RAFT_REC_VOTE:
928 printf(" vote: "SID_FMT"\n", SID_ARGS(&r->sid));
929 break;
930
931 case RAFT_REC_NOTE:
932 printf(" note: \"%s\"\n", r->note);
933 break;
934
935 case RAFT_REC_COMMIT_INDEX:
936 printf(" commit_index: %"PRIu64"\n", r->commit_index);
937 break;
938
939 case RAFT_REC_LEADER:
940 printf(" leader: "SID_FMT"\n", SID_ARGS(&r->sid));
941 break;
942
943 default:
944 OVS_NOT_REACHED();
945 }
946}
947
00de46f9
AG
948static void
949raft_header_to_standalone_log(const struct raft_header *h,
950 struct ovsdb_log *db_log_data)
951{
952 if (h->snap_index) {
953 if (!h->snap.data || json_array(h->snap.data)->n != 2) {
954 ovs_fatal(0, "Incorrect raft header data array length");
955 }
956
957 struct json *schema_json = json_array(h->snap.data)->elems[0];
958 if (schema_json->type != JSON_NULL) {
959 struct ovsdb_schema *schema;
960 check_ovsdb_error(ovsdb_schema_from_json(schema_json, &schema));
961 ovsdb_schema_destroy(schema);
962 check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
963 schema_json));
964 }
965
966 struct json *data_json = json_array(h->snap.data)->elems[1];
967 if (!data_json || data_json->type != JSON_OBJECT) {
968 ovs_fatal(0, "Invalid raft header data");
969 }
970 if (data_json->type != JSON_NULL) {
971 check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
972 data_json));
973 }
974 }
975}
976
977static void
978raft_record_to_standalone_log(const struct raft_record *r,
979 struct ovsdb_log *db_log_data)
980{
981 if (r->type == RAFT_REC_ENTRY) {
982 if (!r->entry.data) {
983 return;
984 }
985 if (json_array(r->entry.data)->n != 2) {
986 ovs_fatal(0, "Incorrect raft record array length");
987 }
988
989 struct json *data_json = json_array(r->entry.data)->elems[1];
990 if (data_json->type != JSON_NULL) {
991 check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
992 data_json));
993 }
994 }
995}
996
1b1d2e6d
BP
997static void
998do_show_log_cluster(struct ovsdb_log *log)
999{
1000 struct smap names = SMAP_INITIALIZER(&names);
1001 struct ovsdb_schema *schema = NULL;
1002 for (unsigned int i = 0; ; i++) {
1003 struct json *json;
1004 check_ovsdb_error(ovsdb_log_read(log, &json));
1005 if (!json) {
1006 break;
1007 }
1008
1009 printf("record %u:\n", i);
1010 struct ovsdb_error *error;
1011 if (i == 0) {
1012 struct raft_header h;
1013 error = raft_header_from_json(&h, json);
1014 if (!error) {
1015 print_raft_header(&h, &schema, &names);
1016 raft_header_uninit(&h);
1017 }
1018 } else {
1019 struct raft_record r;
1020 error = raft_record_from_json(&r, json);
1021 if (!error) {
1022 print_raft_record(&r, &schema, &names);
1023 raft_record_uninit(&r);
1024 }
1025 }
1026 if (error) {
1027 char *s = ovsdb_error_to_string_free(error);
1028 puts(s);
1029 free(s);
1030 }
1031
1032 putchar('\n');
1033 }
1034
1035 ovsdb_schema_destroy(schema);
1036 smap_destroy(&names);
1037}
1038
1039static void
1040do_show_log(struct ovs_cmdl_context *ctx)
1041{
1042 const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db();
1043 struct ovsdb_log *log;
1044
1045 check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC"|"RAFT_MAGIC,
1046 OVSDB_LOG_READ_ONLY, -1, &log));
1047 if (!strcmp(ovsdb_log_get_magic(log), OVSDB_MAGIC)) {
1048 do_show_log_standalone(log);
1049 } else {
1050 do_show_log_cluster(log);
1051 }
1052 ovsdb_log_close(log);
1053}
1054
1055struct server {
6bf2e3f6 1056 struct ovsdb_log *log;
1b1d2e6d
BP
1057 const char *filename;
1058 const char *nickname;
1059
1060 struct raft_header header;
1061
1062 struct raft_record *records;
1063 size_t n_records;
1064
1065 struct raft_entry *snap;
1066 struct raft_entry *entries;
1067 uint64_t log_start, log_end;
1068};
1069
1070struct leader {
1071 /* In struct cluster's 'leaders', indexed by 'term'. */
1072 struct hmap_node hmap_node;
1073
1074 /* This structure indicates that in 'term', 'server' reported that 'leader'
1075 * was elected leader. When 'log_end' is nonzero, it additionally
1076 * indicates 'leader''s log_end at the time it was elected. */
1077 uint64_t term;
1078 struct server *server;
1079 struct uuid leader;
1080 uint64_t log_end;
1081};
1082
1083struct commit {
1084 /* In struct cluster's 'commits', indexed by 'term'. */
1085 struct hmap_node hmap_node;
1086
1087 /* This structure indicates that in 'term', 'server' reported the commit
1088 * index as 'index'. */
1089 uint64_t term;
1090 struct server *server;
1091 uint64_t index; /* Commit index. */
1092};
1093
1094struct cluster {
1095 struct server *servers;
1096 size_t n_servers;
1097
1098 struct hmap leaders; /* Contains 'struct leader's. */
1099
1100 struct hmap commits; /* Contains 'struct commit's. */
1101};
1102
1103static const char *
1104get_server_name(const struct cluster *c, const struct uuid *sid,
1105 char buf[SID_LEN + 1], size_t bufsize)
1106{
1107 for (size_t i = 0; i < c->n_servers; i++) {
6233b87d 1108 struct server *s = &c->servers[i];
1b1d2e6d
BP
1109 if (uuid_equals(&s->header.sid, sid)) {
1110 return s->filename;
1111 }
1112 }
1113
1114 snprintf(buf, bufsize, SID_FMT, SID_ARGS(sid));
1115 return buf;
1116}
1117
1118static struct leader *
1119find_leader(struct cluster *c, uint64_t term)
1120{
1121 struct leader *leader;
1122 HMAP_FOR_EACH_WITH_HASH (leader, hmap_node, hash_uint64(term),
1123 &c->leaders) {
1124 if (term == leader->term) {
1125 return leader;
1126 }
1127 }
1128 return NULL;
1129}
1130
1131/* Records that 'server' reported that 'leader' was elected leader in 'term'.
1132 *
1133 * Checks the Election Safety Property: at most one leader may be elected in a
1134 * single term (see Figure 3.2). */
1135static void
1136record_leader(struct cluster *c, uint64_t term, struct server *server,
1137 const struct uuid *leader)
1138{
1139 bool server_is_leader = uuid_equals(&server->header.sid, leader);
1140 struct leader *p = find_leader(c, term);
1141 if (p) {
1142 if (!uuid_equals(&p->leader, leader)) {
1143 char buf1[SID_LEN + 1];
1144 char buf2[SID_LEN + 1];
1145 ovs_fatal(0, "term %"PRIu64" has two different leaders: "
1146 "%s says that the leader is %s and "
1147 "%s says that the leader is %s",
1148 term,
1149 p->server->filename,
1150 get_server_name(c, &p->leader, buf1, sizeof buf1),
1151 server->filename,
1152 get_server_name(c, leader, buf2, sizeof buf2));
1153 }
1154 if (server_is_leader && server->log_end > p->log_end) {
1155 p->log_end = server->log_end;
1156 }
1157 } else {
1158 p = xmalloc(sizeof *p);
1159 hmap_insert(&c->leaders, &p->hmap_node, hash_uint64(term));
1160 p->term = term;
1161 p->server = server;
1162 p->leader = *leader;
1163 if (server_is_leader) {
1164 p->log_end = server->log_end;
1165 } else {
1166 p->log_end = 0;
1167 }
1168 }
1169}
1170
1171static struct commit *
1172find_commit(struct cluster *c, uint64_t term)
1173{
1174 struct commit *commit;
1175 HMAP_FOR_EACH_WITH_HASH (commit, hmap_node, hash_uint64(term),
1176 &c->commits) {
1177 if (term == commit->term) {
1178 return commit;
1179 }
1180 }
1181 return NULL;
1182}
1183
1184static void
1185record_commit(struct cluster *c, uint64_t term, struct server *server,
1186 uint64_t commit_index)
1187{
1188 struct commit *commit = find_commit(c, term);
1189 if (commit) {
1190 if (commit_index > commit->index) {
1191 commit->server = server;
1192 commit->index = commit_index;
1193 }
1194 } else {
1195 commit = xmalloc(sizeof *commit);
1196 hmap_insert(&c->commits, &commit->hmap_node, hash_uint64(term));
1197 commit->term = term;
1198 commit->server = server;
1199 commit->index = commit_index;
1200 }
1201}
1202
1203static void
1204do_check_cluster(struct ovs_cmdl_context *ctx)
1205{
1206 struct cluster c = {
1207 .servers = xzalloc((ctx->argc - 1) * sizeof *c.servers),
1208 .n_servers = 0,
1209 .leaders = HMAP_INITIALIZER(&c.leaders),
1210 .commits = HMAP_INITIALIZER(&c.commits),
1211 };
1212
1213 uint64_t min_term = UINT64_MAX;
1214 uint64_t max_term = 0;
1215
1216 for (int i = 1; i < ctx->argc; i++) {
1217 struct server *s = &c.servers[c.n_servers];
1218 s->filename = ctx->argv[i];
1219
1b1d2e6d 1220 check_ovsdb_error(ovsdb_log_open(s->filename, RAFT_MAGIC,
6bf2e3f6 1221 OVSDB_LOG_READ_ONLY, -1, &s->log));
1b1d2e6d
BP
1222
1223 struct json *json;
6bf2e3f6 1224 check_ovsdb_error(ovsdb_log_read(s->log, &json));
1b1d2e6d
BP
1225 check_ovsdb_error(raft_header_from_json(&s->header, json));
1226 json_destroy(json);
1227
1228 if (s->header.joining) {
1229 printf("%s has not joined the cluster, omitting\n", s->filename);
6bf2e3f6 1230 ovsdb_log_close(s->log);
1b1d2e6d
BP
1231 continue;
1232 }
7073a83f
BP
1233 for (size_t j = 0; j < c.n_servers; j++) {
1234 if (uuid_equals(&s->header.sid, &c.servers[j].header.sid)) {
1235 ovs_fatal(0, "Duplicate server ID "SID_FMT" in %s and %s.",
1236 SID_ARGS(&s->header.sid),
1237 s->filename, c.servers[j].filename);
1238 }
1239 }
1b1d2e6d
BP
1240 if (c.n_servers > 0) {
1241 struct server *s0 = &c.servers[0];
1242 if (!uuid_equals(&s0->header.cid, &s->header.cid)) {
1243 ovs_fatal(0, "%s has cluster ID "CID_FMT" but %s "
1244 "has cluster ID "CID_FMT,
1245 s0->filename, CID_ARGS(&s0->header.cid),
1246 s->filename, CID_ARGS(&s->header.cid));
1247 }
1248 if (strcmp(s0->header.name, s->header.name)) {
1249 ovs_fatal(0, "%s is named \"%s\" but %s is named \"%s\"",
1250 s0->filename, s0->header.name,
1251 s->filename, s->header.name);
1252 }
1253 }
6bf2e3f6
BP
1254 c.n_servers++;
1255 }
1256
1257 for (struct server *s = c.servers; s < &c.servers[c.n_servers]; s++) {
1b1d2e6d
BP
1258 s->snap = &s->header.snap;
1259 s->log_start = s->log_end = s->header.snap_index + 1;
1260
1261 size_t allocated_records = 0;
1262 size_t allocated_entries = 0;
1263
1264 uint64_t term = 0; /* Current term. */
1265 struct uuid vote = UUID_ZERO; /* Server 's''s vote in 'term'. */
1266 struct uuid leader = UUID_ZERO; /* Cluster leader in 'term'. */
1267 uint64_t leader_rec_idx = 0; /* Index of last "leader" record. */
1268
1269 uint64_t commit_index = s->header.snap_index;
1270
1271 for (unsigned long long int rec_idx = 1; ; rec_idx++) {
1272 if (s->n_records >= allocated_records) {
1273 s->records = x2nrealloc(s->records, &allocated_records,
1274 sizeof *s->records);
1275 }
6bf2e3f6
BP
1276
1277 struct json *json;
1278 check_ovsdb_error(ovsdb_log_read(s->log, &json));
1b1d2e6d
BP
1279 if (!json) {
1280 break;
1281 }
1282 struct raft_record *r = &s->records[s->n_records++];
1283 check_ovsdb_error(raft_record_from_json(r, json));
1284 json_destroy(json);
1285
1286 if (r->term > term) {
1287 term = r->term;
1288 vote = UUID_ZERO;
1289 leader = UUID_ZERO;
1290 leader_rec_idx = 0;
1291 }
1292 if (term < min_term) {
1293 min_term = term;
1294 }
1295 if (term > max_term) {
1296 max_term = term;
1297 }
1298
1b1d2e6d
BP
1299 switch (r->type) {
1300 case RAFT_REC_ENTRY:
1301 if (r->entry.index < commit_index) {
1302 ovs_fatal(0, "%s: record %llu attempts to truncate log "
1303 "from %"PRIu64" to %"PRIu64" entries, but "
1304 "commit index is already %"PRIu64,
1305 s->filename, rec_idx,
1306 s->log_end, r->entry.index,
1307 commit_index);
1308 } else if (r->entry.index > s->log_end) {
1309 ovs_fatal(0, "%s: record %llu with index %"PRIu64" skips "
1310 "past expected index %"PRIu64, s->filename,
1311 rec_idx, r->entry.index, s->log_end);
1312 }
1313
1314 if (r->entry.index < s->log_end) {
1315 bool is_leader = uuid_equals(&s->header.sid, &leader);
1316 if (is_leader) {
1317 /* Leader Append-Only property (see Figure 3.2). */
1318 ovs_fatal(0, "%s: record %llu truncates log from "
1319 "%"PRIu64" to %"PRIu64" entries while "
1320 "server is leader", s->filename, rec_idx,
1321 s->log_end, r->entry.index);
1322 } else {
1323 /* This can happen, but it is unusual. */
1324 printf("%s: record %llu truncates log from %"PRIu64
1325 " to %"PRIu64" entries\n", s->filename, rec_idx,
1326 s->log_end, r->entry.index);
1327 }
1328 s->log_end = r->entry.index;
1329 }
1330
1331 uint64_t prev_term = (s->log_end > s->log_start
1332 ? s->entries[s->log_end
1333 - s->log_start - 1].term
1334 : s->snap->term);
1335 if (r->term < prev_term) {
1336 ovs_fatal(0, "%s: record %llu with index %"PRIu64" term "
1337 "%"PRIu64" precedes previous entry's term "
1338 "%"PRIu64, s->filename, rec_idx,
1339 r->entry.index, r->term, prev_term);
1340 }
1341
1342 uint64_t log_idx = s->log_end++ - s->log_start;
1343 if (log_idx >= allocated_entries) {
1344 s->entries = x2nrealloc(s->entries, &allocated_entries,
1345 sizeof *s->entries);
1346 }
1347 struct raft_entry *e = &s->entries[log_idx];
1348 e->term = r->term;
1349 e->data = r->entry.data;
1350 e->eid = r->entry.eid;
1351 e->servers = r->entry.servers;
1352 break;
1353
1354 case RAFT_REC_TERM:
1355 break;
1356
1357 case RAFT_REC_VOTE:
1358 if (r->term < term) {
1359 ovs_fatal(0, "%s: record %llu votes for term %"PRIu64" "
1360 "but current term is %"PRIu64, s->filename,
1361 rec_idx, r->term, term);
1362 } else if (!uuid_is_zero(&vote)
1363 && !uuid_equals(&vote, &r->sid)) {
1364 char buf1[SID_LEN + 1];
1365 char buf2[SID_LEN + 1];
1366 ovs_fatal(0, "%s: record %llu votes for %s in term "
1367 "%"PRIu64" but a previous record for the "
1368 "same term voted for %s", s->filename,
1369 rec_idx,
1370 get_server_name(&c, &vote, buf1, sizeof buf1),
1371 r->term,
1372 get_server_name(&c, &r->sid, buf2, sizeof buf2));
1373 } else {
1374 vote = r->sid;
1375 }
1376 break;
1377
1378 case RAFT_REC_NOTE:
1379 if (!strcmp(r->note, "left")) {
1380 printf("%s: record %llu shows that the server left the "
1381 "cluster\n", s->filename, rec_idx);
1382 }
1383 break;
1384
1385 case RAFT_REC_COMMIT_INDEX:
1386 if (r->commit_index < commit_index) {
1387 ovs_fatal(0, "%s: record %llu regresses commit index "
1388 "from %"PRIu64 " to %"PRIu64, s->filename,
1389 rec_idx, commit_index, r->commit_index);
1390 } else if (r->commit_index >= s->log_end) {
1391 ovs_fatal(0, "%s: record %llu advances commit index to "
1392 "%"PRIu64 " but last log index is %"PRIu64,
1393 s->filename, rec_idx, r->commit_index,
1394 s->log_end - 1);
1395 } else {
1396 commit_index = r->commit_index;
1ff1065c
PI
1397 }
1398
1b1d2e6d
BP
1399 record_commit(&c, term, s, r->commit_index);
1400 break;
1401
1402 case RAFT_REC_LEADER:
1403 if (!uuid_equals(&r->sid, &leader)) {
1404 if (uuid_is_zero(&leader)) {
1405 leader = r->sid;
1406 leader_rec_idx = rec_idx;
1407 } else {
1408 char buf1[SID_LEN + 1];
1409 char buf2[SID_LEN + 1];
1410 ovs_fatal(0, "%s: record %llu reports leader %s "
1411 "for term %"PRIu64" but record %"PRIu64" "
1412 "previously reported the leader as %s "
1413 "in that term",
1414 s->filename, rec_idx,
1415 get_server_name(&c, &r->sid,
1416 buf1, sizeof buf1),
1417 term, leader_rec_idx,
1418 get_server_name(&c, &leader,
1419 buf2, sizeof buf2));
1420 }
1421 }
1422 record_leader(&c, term, s, &r->sid);
1423 break;
722f6301 1424 }
1b1d2e6d
BP
1425 }
1426
6bf2e3f6
BP
1427 ovsdb_log_close(s->log);
1428 s->log = NULL;
1b1d2e6d 1429 }
722f6301 1430
1b1d2e6d
BP
1431 /* Check the Leader Completeness property from Figure 3.2: If a log entry
1432 * is committed in a given term, then that entry will be present in the
1433 * logs of the leaders for all higher-numbered terms. */
1434 if (min_term == UINT64_MAX || max_term == 0) {
1435 ovs_fatal(0, "all logs are empty");
1436 }
1437 struct commit *commit = NULL;
1438 for (uint64_t term = min_term; term <= max_term; term++) {
1439 struct leader *leader = find_leader(&c, term);
c2d71875
BP
1440 if (leader && leader->log_end
1441 && commit && commit->index >= leader->log_end) {
1b1d2e6d
BP
1442 ovs_fatal(0, "leader %s for term %"PRIu64" has log entries only "
1443 "up to index %"PRIu64", but index %"PRIu64" was "
1444 "committed in a previous term (e.g. by %s)",
1445 leader->server->filename, term, leader->log_end - 1,
1446 commit->index, commit->server->filename);
1447 }
1448
1449 struct commit *next = find_commit(&c, term);
1450 if (next && (!commit || next->index > commit->index)) {
1451 commit = next;
1452 }
1453 }
1454
1455 /* Section 3.5: Check the Log Matching Property in Figure 3.2:
1456 *
1457 * - If two entries in different logs have the same index and term, then
1458 * they store the same command.
1459 *
1460 * - If two entries in different logs have the same index and term, then
1461 * the logs are identical in all preceding entries.
1462 */
1463 for (size_t i = 0; i < c.n_servers; i++) {
1464 for (size_t j = 0; j < c.n_servers; j++) {
1465 struct server *a = &c.servers[i];
1466 struct server *b = &c.servers[j];
1467
1468 if (a == b) {
1469 continue;
722f6301 1470 }
c6782bb0 1471
1b1d2e6d
BP
1472 bool must_equal = false;
1473 for (uint64_t idx = MIN(a->log_end, b->log_end) - 1;
1474 idx >= MAX(a->log_start, b->log_start);
1475 idx--) {
1476 const struct raft_entry *ae = &a->entries[idx - a->log_start];
1477 const struct raft_entry *be = &b->entries[idx - b->log_start];
1478 if (ae->term == be->term) {
1479 must_equal = true;
1480 }
1481 if (!must_equal || raft_entry_equals(ae, be)) {
1482 continue;
1483 }
1484 char *as = json_to_string(raft_entry_to_json(ae), JSSF_SORT);
1485 char *bs = json_to_string(raft_entry_to_json(be), JSSF_SORT);
1486 ovs_fatal(0, "log entries with index %"PRIu64" differ:\n"
1487 "%s has %s\n"
1488 "%s has %s",
1489 idx, a->filename, as, b->filename, bs);
c6782bb0 1490 }
722f6301 1491 }
722f6301 1492 }
c6782bb0 1493
1b1d2e6d
BP
1494 /* Clean up. */
1495
1496 for (size_t i = 0; i < c.n_servers; i++) {
1497 struct server *s = &c.servers[i];
1498
1499 raft_header_uninit(&s->header);
1500 for (size_t j = 0; j < s->n_records; j++) {
1501 struct raft_record *r = &s->records[j];
1502
1503 raft_record_uninit(r);
1504 }
1505 free(s->records);
1506 free(s->entries);
1507 }
1508 free(c.servers);
1509
1510 struct commit *next_commit;
1511 HMAP_FOR_EACH_SAFE (commit, next_commit, hmap_node, &c.commits) {
1512 hmap_remove(&c.commits, &commit->hmap_node);
1513 free(commit);
1514 }
1515 hmap_destroy(&c.commits);
1516
1517 struct leader *leader, *next_leader;
1518 HMAP_FOR_EACH_SAFE (leader, next_leader, hmap_node, &c.leaders) {
1519 hmap_remove(&c.leaders, &leader->hmap_node);
1520 free(leader);
1521 }
1522 hmap_destroy(&c.leaders);
722f6301
BP
1523}
1524
1b1d2e6d
BP
1525static struct ovsdb_version
1526parse_version(const char *s)
1527{
1528 struct ovsdb_version version;
1529 if (!ovsdb_parse_version(s, &version)) {
1530 ovs_fatal(0, "%s: not an OVSDB version number in format x.y.z", s);
1531 }
1532 return version;
1533}
1534
1535static void
1536do_compare_versions(struct ovs_cmdl_context *ctx)
1537{
1538 struct ovsdb_version a = parse_version(ctx->argv[1]);
1539 struct ovsdb_version b = parse_version(ctx->argv[3]);
1540 int cmp = (a.x != b.x ? (a.x > b.x ? 1 : -1)
1541 : a.y != b.y ? (a.y > b.y ? 1 : -1)
1542 : a.z != b.z ? (a.z > b.z ? 1 : -1)
1543 : 0);
1544
1545 const char *op = ctx->argv[2];
1546 bool result;
1547 if (!strcmp(op, "<")) {
1548 result = cmp < 0;
1549 } else if (!strcmp(op, "<=")) {
1550 result = cmp <= 0;
1551 } else if (!strcmp(op, "==")) {
1552 result = cmp == 0;
1553 } else if (!strcmp(op, ">=")) {
1554 result = cmp >= 0;
1555 } else if (!strcmp(op, ">")) {
1556 result = cmp > 0;
1557 } else if (!strcmp(op, "!=")) {
1558 result = cmp != 0;
1559 } else {
1560 ovs_fatal(0, "%s: not a relational operator", op);
1561 }
1562
1563 exit(result ? 0 : 2);
1564}
1565
00de46f9
AG
1566static void
1567do_convert_to_standalone(struct ovsdb_log *log, struct ovsdb_log *db_log_data)
1568{
1569 for (unsigned int i = 0; ; i++) {
1570 struct json *json;
1571 check_ovsdb_error(ovsdb_log_read(log, &json));
1572 if (!json) {
1573 break;
1574 }
1575
1576 if (i == 0) {
1577 struct raft_header h;
1578 check_ovsdb_error(raft_header_from_json(&h, json));
1579 raft_header_to_standalone_log(&h, db_log_data);
1580 raft_header_uninit(&h);
1581 } else {
1582 struct raft_record r;
1583 check_ovsdb_error(raft_record_from_json(&r, json));
1584 raft_record_to_standalone_log(&r, db_log_data);
1585 raft_record_uninit(&r);
1586 }
1587 }
1588}
1589
1590static void
1591do_cluster_standalone(struct ovs_cmdl_context *ctx)
1592{
1593 const char *db_file_name = ctx->argv[1];
1594 const char *cluster_db_file_name = ctx->argv[2];
1595 struct ovsdb_log *log;
1596 struct ovsdb_log *db_log_data;
1597
1598 check_ovsdb_error(ovsdb_log_open(cluster_db_file_name,
1599 OVSDB_MAGIC"|"RAFT_MAGIC,
1600 OVSDB_LOG_READ_ONLY, -1, &log));
1601 check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC,
1602 OVSDB_LOG_CREATE_EXCL, -1, &db_log_data));
1603 if (strcmp(ovsdb_log_get_magic(log), RAFT_MAGIC) != 0) {
1604 ovs_fatal(0, "Database is not clustered db.\n");
1605 }
1606 do_convert_to_standalone(log, db_log_data);
1607 check_ovsdb_error(ovsdb_log_commit_block(db_log_data));
1608 ovsdb_log_close(db_log_data);
1609 ovsdb_log_close(log);
1610}
1b1d2e6d 1611
f85f8ebb 1612static void
1636c761 1613do_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
f85f8ebb
BP
1614{
1615 usage();
1616}
1617
451de37e 1618static void
1636c761 1619do_list_commands(struct ovs_cmdl_context *ctx OVS_UNUSED)
451de37e 1620{
5f383751 1621 ovs_cmdl_print_commands(get_all_commands());
451de37e
AW
1622}
1623
5f383751 1624static const struct ovs_cmdl_command all_commands[] = {
1f4a7252 1625 { "create", "[db [schema]]", 0, 2, do_create, OVS_RW },
1b1d2e6d
BP
1626 { "create-cluster", "db contents local", 3, 3, do_create_cluster, OVS_RW },
1627 { "join-cluster", "db name local remote...", 4, INT_MAX, do_join_cluster,
1628 OVS_RW },
1f4a7252
RM
1629 { "compact", "[db [dst]]", 0, 2, do_compact, OVS_RW },
1630 { "convert", "[db [schema [dst]]]", 0, 3, do_convert, OVS_RW },
1631 { "needs-conversion", NULL, 0, 2, do_needs_conversion, OVS_RO },
439902cb 1632 { "db-name", "[db]", 0, 1, do_db_name, OVS_RO },
1f4a7252
RM
1633 { "db-version", "[db]", 0, 1, do_db_version, OVS_RO },
1634 { "db-cksum", "[db]", 0, 1, do_db_cksum, OVS_RO },
1b1d2e6d
BP
1635 { "db-cid", "db", 1, 1, do_db_cid, OVS_RO },
1636 { "db-sid", "db", 1, 1, do_db_sid, OVS_RO },
1637 { "db-local-address", "db", 1, 1, do_db_local_address, OVS_RO },
1638 { "db-is-clustered", "db", 1, 1, do_db_is_clustered, OVS_RO },
1639 { "db-is-standalone", "db", 1, 1, do_db_is_standalone, OVS_RO },
439902cb 1640 { "schema-name", "[schema]", 0, 1, do_schema_name, OVS_RO },
1f4a7252
RM
1641 { "schema-version", "[schema]", 0, 1, do_schema_version, OVS_RO },
1642 { "schema-cksum", "[schema]", 0, 1, do_schema_cksum, OVS_RO },
1643 { "query", "[db] trns", 1, 2, do_query, OVS_RO },
1644 { "transact", "[db] trns", 1, 2, do_transact, OVS_RO },
1645 { "show-log", "[db]", 0, 1, do_show_log, OVS_RO },
1b1d2e6d
BP
1646 { "check-cluster", "db...", 1, INT_MAX, do_check_cluster, OVS_RO },
1647 { "compare-versions", "a op b", 3, 3, do_compare_versions, OVS_RO },
1f4a7252
RM
1648 { "help", NULL, 0, INT_MAX, do_help, OVS_RO },
1649 { "list-commands", NULL, 0, INT_MAX, do_list_commands, OVS_RO },
00de46f9
AG
1650 { "cluster-to-standalone", "db clusterdb", 2, 2,
1651 do_cluster_standalone, OVS_RW },
1652 { NULL, NULL, 2, 2, NULL, OVS_RO },
f85f8ebb 1653};
3815d6c2 1654
5f383751 1655static const struct ovs_cmdl_command *get_all_commands(void)
3815d6c2
LS
1656{
1657 return all_commands;
1658}