2 * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2016, 2017 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
26 #include "command-line.h"
29 #include "openvswitch/dynamic-string.h"
30 #include "fatal-signal.h"
35 #include "openvswitch/hmap.h"
36 #include "openvswitch/json.h"
38 #include "ovsdb-data.h"
39 #include "ovsdb-error.h"
40 #include "ovsdb-parser.h"
42 #include "raft-private.h"
44 #include "socket-util.h"
48 #include "transaction.h"
50 #include "openvswitch/vlog.h"
52 /* -m, --more: Verbosity level for "show-log" command output. */
53 static int show_log_verbosity
;
55 /* --role: RBAC role to use for "transact" and "query" commands. */
56 static const char *rbac_role
;
58 /* --cid: Cluster ID for "join-cluster" command. */
59 static struct uuid cid
;
61 static const struct ovs_cmdl_command
*get_all_commands(void);
63 OVS_NO_RETURN
static void usage(void);
64 static void parse_options(int argc
, char *argv
[]);
66 static const char *default_db(void);
67 static const char *default_schema(void);
70 main(int argc
, char *argv
[])
72 struct ovs_cmdl_context ctx
= { .argc
= 0, };
73 set_program_name(argv
[0]);
74 parse_options(argc
, argv
);
75 fatal_ignore_sigpipe();
77 ctx
.argc
= argc
- optind
;
78 ctx
.argv
= argv
+ optind
;
79 ovs_cmdl_run_command(&ctx
, get_all_commands());
84 parse_options(int argc
, char *argv
[])
87 OPT_RBAC_ROLE
= UCHAR_MAX
+ 1,
90 static const struct option long_options
[] = {
91 {"more", no_argument
, NULL
, 'm'},
92 {"rbac-role", required_argument
, NULL
, OPT_RBAC_ROLE
},
93 {"cid", required_argument
, NULL
, OPT_CID
},
94 {"verbose", optional_argument
, NULL
, 'v'},
95 {"help", no_argument
, NULL
, 'h'},
96 {"option", no_argument
, NULL
, 'o'},
97 {"version", no_argument
, NULL
, 'V'},
100 char *short_options
= ovs_cmdl_long_options_to_short_options(long_options
);
105 c
= getopt_long(argc
, argv
, short_options
, long_options
, NULL
);
112 show_log_verbosity
++;
120 if (!uuid_from_string(&cid
, optarg
) || uuid_is_zero(&cid
)) {
121 ovs_fatal(0, "%s: not a valid UUID", optarg
);
129 ovs_cmdl_print_options(long_options
);
133 ovs_print_version(0, 0);
137 vlog_set_verbosity(optarg
);
153 printf("%s: Open vSwitch database management utility\n"
154 "usage: %s [OPTIONS] COMMAND [ARG...]\n"
155 " create [DB [SCHEMA]] create DB with the given SCHEMA\n"
156 " create-cluster DB CONTENTS LOCAL\n"
157 " create clustered DB with given CONTENTS and LOCAL address\n"
158 " [--cid=UUID] join-cluster DB NAME LOCAL REMOTE...\n"
159 " join clustered DB with given NAME and LOCAL and REMOTE addrs\n"
160 " compact [DB [DST]] compact DB in-place (or to DST)\n"
161 " convert [DB [SCHEMA [DST]]] convert DB to SCHEMA (to DST)\n"
162 " db-name [DB] report name of schema used by DB\n"
163 " db-version [DB] report version of schema used by DB\n"
164 " db-cksum [DB] report checksum of schema used by DB\n"
165 " db-cid DB report cluster ID of clustered DB\n"
166 " db-sid DB report server ID of clustered DB\n"
167 " db-local-address DB report local address of clustered DB\n"
168 " db-is-clustered DB test whether DB is clustered\n"
169 " db-is-standalone DB test whether DB is standalone\n"
170 " schema-name [SCHEMA] report SCHEMA's name\n"
171 " schema-version [SCHEMA] report SCHEMA's schema version\n"
172 " schema-cksum [SCHEMA] report SCHEMA's checksum\n"
173 " compare-versions A OP B compare OVSDB schema version numbers\n"
174 " query [DB] TRNS execute read-only transaction on DB\n"
175 " transact [DB] TRNS execute read/write transaction on DB\n"
176 " [-m]... show-log [DB] print DB's log entries\n"
177 "The default DB is %s.\n"
178 "The default SCHEMA is %s.\n",
179 program_name
, program_name
, default_db(), default_schema());
183 -m, --more increase show-log verbosity\n\
184 --rbac-role=ROLE RBAC role for transact and query commands\n\
185 -h, --help display this help message\n\
186 -V, --version display version information\n");
195 db
= xasprintf("%s/conf.db", ovs_dbdir());
205 schema
= xasprintf("%s/vswitch.ovsschema", ovs_pkgdatadir());
211 parse_json(const char *s
)
213 struct json
*json
= json_from_string(s
);
214 if (json
->type
== JSON_STRING
) {
215 ovs_fatal(0, "\"%s\": %s", s
, json
->string
);
221 print_and_free_json(struct json
*json
)
223 char *string
= json_to_string(json
, JSSF_SORT
);
230 check_ovsdb_error(struct ovsdb_error
*error
)
233 ovs_fatal(0, "%s", ovsdb_error_to_string(error
));
237 /* Opens the standalone database 'filename' and returns its schema. */
238 static struct ovsdb_schema
*
239 read_standalone_schema(const char *filename
)
241 struct ovsdb_storage
*storage
= ovsdb_storage_open_standalone(filename
,
243 struct ovsdb_schema
*schema
= ovsdb_storage_read_schema(storage
);
244 ovsdb_storage_close(storage
);
249 do_create(struct ovs_cmdl_context
*ctx
)
251 const char *db_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
252 const char *schema_file_name
= ctx
->argc
>= 3 ? ctx
->argv
[2] : default_schema();
253 struct ovsdb_schema
*schema
;
254 struct ovsdb_log
*log
;
257 /* Read schema from file and convert to JSON. */
258 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name
, &schema
));
259 json
= ovsdb_schema_to_json(schema
);
260 ovsdb_schema_destroy(schema
);
262 /* Create database file. */
263 check_ovsdb_error(ovsdb_log_open(db_file_name
, OVSDB_MAGIC
,
264 OVSDB_LOG_CREATE_EXCL
, -1, &log
));
265 check_ovsdb_error(ovsdb_log_write_and_free(log
, json
));
266 check_ovsdb_error(ovsdb_log_commit_block(log
));
267 ovsdb_log_close(log
);
271 do_create_cluster(struct ovs_cmdl_context
*ctx
)
273 const char *db_file_name
= ctx
->argv
[1];
274 const char *src_file_name
= ctx
->argv
[2];
275 const char *local
= ctx
->argv
[3];
277 struct ovsdb_schema
*schema
;
280 struct ovsdb_error
*error
= ovsdb_schema_from_file(src_file_name
, &schema
);
282 /* It's just a schema file. */
283 data
= json_object_create();
285 /* Not a schema file. Try reading it as a standalone database. */
286 ovsdb_error_destroy(error
);
288 struct ovsdb
*ovsdb
= ovsdb_file_read(src_file_name
, false);
289 char *comment
= xasprintf("created from %s", src_file_name
);
290 data
= ovsdb_to_txn_json(ovsdb
, comment
);
292 schema
= ovsdb_schema_clone(ovsdb
->schema
);
293 ovsdb_destroy(ovsdb
);
296 ovsdb_schema_persist_ephemeral_columns(schema
, src_file_name
);
298 struct json
*schema_json
= ovsdb_schema_to_json(schema
);
300 /* Create database file. */
301 struct json
*snapshot
= json_array_create_2(schema_json
, data
);
302 check_ovsdb_error(raft_create_cluster(db_file_name
, schema
->name
,
304 ovsdb_schema_destroy(schema
);
305 json_destroy(snapshot
);
309 do_join_cluster(struct ovs_cmdl_context
*ctx
)
311 const char *db_file_name
= ctx
->argv
[1];
312 const char *name
= ctx
->argv
[2];
313 const char *local
= ctx
->argv
[3];
315 /* Check for a plausible 'name'. */
316 if (!ovsdb_parser_is_id(name
)) {
317 ovs_fatal(0, "%s: not a valid schema name (use \"schema-name\" "
318 "command to find the correct name)", name
);
321 /* Create database file. */
322 struct sset remote_addrs
= SSET_INITIALIZER(&remote_addrs
);
323 for (size_t i
= 4; i
< ctx
->argc
; i
++) {
324 sset_add(&remote_addrs
, ctx
->argv
[i
]);
326 check_ovsdb_error(raft_join_cluster(db_file_name
, name
, local
,
328 uuid_is_zero(&cid
) ? NULL
: &cid
));
329 sset_destroy(&remote_addrs
);
332 static struct ovsdb_error
*
333 write_standalone_db(const char *file_name
, const char *comment
,
334 const struct ovsdb
*db
)
336 struct ovsdb_log
*log
;
337 struct ovsdb_error
*error
= ovsdb_log_open(file_name
, OVSDB_MAGIC
,
338 OVSDB_LOG_CREATE
, false, &log
);
343 error
= ovsdb_log_write_and_free(log
, ovsdb_schema_to_json(db
->schema
));
345 error
= ovsdb_log_write_and_free(log
, ovsdb_to_txn_json(db
, comment
));
347 ovsdb_log_close(log
);
355 /* Reads 'src_name' and writes it back, compacted, to 'dst_name', adding the
356 * specified 'comment'. If 'new_schema' is nonull, converts the databse to
359 * Standalone databases only. */
361 compact_or_convert(const char *src_name_
, const char *dst_name_
,
362 struct ovsdb_schema
*new_schema
, const char *comment
)
364 bool in_place
= dst_name_
== NULL
;
366 /* Dereference symlinks for source and destination names. In the in-place
367 * case this ensures that, if the source name is a symlink, we replace its
368 * target instead of replacing the symlink by a regular file. In the
369 * non-in-place, this has the same effect for the destination name. */
370 char *src_name
= follow_symlinks(src_name_
);
371 char *dst_name
= (in_place
372 ? xasprintf("%s.tmp", src_name
)
373 : follow_symlinks(dst_name_
));
375 /* Lock the source, if we will be replacing it. */
376 struct lockfile
*src_lock
= NULL
;
378 int retval
= lockfile_lock(src_name
, &src_lock
);
380 ovs_fatal(retval
, "%s: failed to lock lockfile", src_name
);
384 /* Get (temporary) destination and lock it. */
385 struct lockfile
*dst_lock
= NULL
;
386 int retval
= lockfile_lock(dst_name
, &dst_lock
);
388 ovs_fatal(retval
, "%s: failed to lock lockfile", dst_name
);
392 struct ovsdb
*ovsdb
= (new_schema
393 ? ovsdb_file_read_as_schema(src_name
, new_schema
)
394 : ovsdb_file_read(src_name
, false));
395 ovsdb_storage_close(ovsdb
->storage
);
396 ovsdb
->storage
= NULL
;
397 check_ovsdb_error(write_standalone_db(dst_name
, comment
, ovsdb
));
398 ovsdb_destroy(ovsdb
);
400 /* Replace source. */
405 if (rename(dst_name
, src_name
)) {
406 ovs_fatal(errno
, "failed to rename \"%s\" to \"%s\"",
409 fsync_parent_dir(dst_name
);
410 lockfile_unlock(src_lock
);
413 lockfile_unlock(dst_lock
);
420 do_compact(struct ovs_cmdl_context
*ctx
)
422 const char *db
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
423 const char *target
= ctx
->argc
>= 3 ? ctx
->argv
[2] : NULL
;
425 compact_or_convert(db
, target
, NULL
, "compacted by ovsdb-tool "VERSION
);
429 do_convert(struct ovs_cmdl_context
*ctx
)
431 const char *db
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
432 const char *schema
= ctx
->argc
>= 3 ? ctx
->argv
[2] : default_schema();
433 const char *target
= ctx
->argc
>= 4 ? ctx
->argv
[3] : NULL
;
434 struct ovsdb_schema
*new_schema
;
436 check_ovsdb_error(ovsdb_schema_from_file(schema
, &new_schema
));
437 compact_or_convert(db
, target
, new_schema
,
438 "converted by ovsdb-tool "VERSION
);
442 do_needs_conversion(struct ovs_cmdl_context
*ctx
)
444 const char *db_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
445 const char *schema_file_name
= ctx
->argc
>= 3 ? ctx
->argv
[2] : default_schema();
446 struct ovsdb_schema
*schema1
= read_standalone_schema(db_file_name
);
447 struct ovsdb_schema
*schema2
;
449 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name
, &schema2
));
450 puts(ovsdb_schema_equal(schema1
, schema2
) ? "no" : "yes");
451 ovsdb_schema_destroy(schema1
);
452 ovsdb_schema_destroy(schema2
);
456 do_db_name(struct ovs_cmdl_context
*ctx
)
458 const char *db_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
460 struct ovsdb_log
*log
;
461 check_ovsdb_error(ovsdb_log_open(db_file_name
, OVSDB_MAGIC
"|"RAFT_MAGIC
,
462 OVSDB_LOG_READ_ONLY
, -1, &log
));
463 if (!strcmp(ovsdb_log_get_magic(log
), OVSDB_MAGIC
)) {
464 struct json
*schema_json
;
465 check_ovsdb_error(ovsdb_log_read(log
, &schema_json
));
467 struct ovsdb_schema
*schema
;
468 check_ovsdb_error(ovsdb_schema_from_json(schema_json
, &schema
));
472 ovsdb_schema_destroy(schema
);
473 json_destroy(schema_json
);
474 } else if (!strcmp(ovsdb_log_get_magic(log
), RAFT_MAGIC
)) {
475 struct raft_metadata md
;
476 check_ovsdb_error(raft_read_metadata(log
, &md
));
478 raft_metadata_destroy(&md
);
483 ovsdb_log_close(log
);
487 do_db_version(struct ovs_cmdl_context
*ctx
)
489 const char *db_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
490 struct ovsdb_schema
*schema
= read_standalone_schema(db_file_name
);
492 puts(schema
->version
);
493 ovsdb_schema_destroy(schema
);
497 do_db_cksum(struct ovs_cmdl_context
*ctx
)
499 const char *db_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
500 struct ovsdb_schema
*schema
= read_standalone_schema(db_file_name
);
502 ovsdb_schema_destroy(schema
);
505 static struct raft_metadata
506 read_cluster_metadata(const char *filename
)
508 struct ovsdb_log
*log
;
509 check_ovsdb_error(ovsdb_log_open(filename
, OVSDB_MAGIC
"|"RAFT_MAGIC
,
510 OVSDB_LOG_READ_ONLY
, -1, &log
));
511 if (strcmp(ovsdb_log_get_magic(log
), RAFT_MAGIC
)) {
512 ovs_fatal(0, "%s: not a clustered database", filename
);
515 struct raft_metadata md
;
516 check_ovsdb_error(raft_read_metadata(log
, &md
));
518 ovsdb_log_close(log
);
524 do_db_cid(struct ovs_cmdl_context
*ctx
)
526 const char *db_file_name
= ctx
->argv
[1];
527 struct raft_metadata md
= read_cluster_metadata(db_file_name
);
528 if (uuid_is_zero(&md
.cid
)) {
529 fprintf(stderr
, "%s: cluster ID not yet known\n", db_file_name
);
532 printf(UUID_FMT
"\n", UUID_ARGS(&md
.cid
));
533 raft_metadata_destroy(&md
);
537 do_db_sid(struct ovs_cmdl_context
*ctx
)
539 const char *db_file_name
= ctx
->argv
[1];
540 struct raft_metadata md
= read_cluster_metadata(db_file_name
);
541 printf(UUID_FMT
"\n", UUID_ARGS(&md
.sid
));
542 raft_metadata_destroy(&md
);
546 do_db_local_address(struct ovs_cmdl_context
*ctx
)
548 const char *db_file_name
= ctx
->argv
[1];
549 struct raft_metadata md
= read_cluster_metadata(db_file_name
);
551 raft_metadata_destroy(&md
);
555 do_db_has_magic(struct ovs_cmdl_context
*ctx
, const char *magic
)
557 const char *filename
= ctx
->argv
[1];
558 struct ovsdb_log
*log
;
560 check_ovsdb_error(ovsdb_log_open(filename
, OVSDB_MAGIC
"|"RAFT_MAGIC
,
561 OVSDB_LOG_READ_ONLY
, -1, &log
));
562 if (strcmp(ovsdb_log_get_magic(log
), magic
)) {
568 do_db_is_clustered(struct ovs_cmdl_context
*ctx
)
570 do_db_has_magic(ctx
, RAFT_MAGIC
);
574 do_db_is_standalone(struct ovs_cmdl_context
*ctx
)
576 do_db_has_magic(ctx
, OVSDB_MAGIC
);
580 do_schema_name(struct ovs_cmdl_context
*ctx
)
582 const char *schema_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_schema();
583 struct ovsdb_schema
*schema
;
585 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name
, &schema
));
587 ovsdb_schema_destroy(schema
);
591 do_schema_version(struct ovs_cmdl_context
*ctx
)
593 const char *schema_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_schema();
594 struct ovsdb_schema
*schema
;
596 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name
, &schema
));
597 puts(schema
->version
);
598 ovsdb_schema_destroy(schema
);
602 do_schema_cksum(struct ovs_cmdl_context
*ctx
)
604 const char *schema_file_name
605 = ctx
->argc
>= 2 ? ctx
->argv
[1] : default_schema();
606 struct ovsdb_schema
*schema
;
608 check_ovsdb_error(ovsdb_schema_from_file(schema_file_name
, &schema
));
610 ovsdb_schema_destroy(schema
);
613 /* Standalone databases only. */
615 transact(struct ovs_cmdl_context
*ctx
, bool rw
)
617 const char *db_file_name
= ctx
->argc
>= 3 ? ctx
->argv
[1] : default_db();
618 const char *transaction
= ctx
->argv
[ctx
->argc
- 1];
620 struct ovsdb
*ovsdb
= ovsdb_file_read(db_file_name
, rw
);
621 struct json
*request
= parse_json(transaction
);
622 struct json
*result
= ovsdb_execute(ovsdb
, NULL
, request
, false,
623 rbac_role
, NULL
, 0, NULL
);
624 json_destroy(request
);
626 print_and_free_json(result
);
627 ovsdb_destroy(ovsdb
);
631 do_query(struct ovs_cmdl_context
*ctx
)
633 transact(ctx
, false);
637 do_transact(struct ovs_cmdl_context
*ctx
)
643 print_db_changes(struct shash
*tables
, struct smap
*names
,
644 const struct ovsdb_schema
*schema
)
646 struct shash_node
*n1
;
649 SHASH_FOR_EACH (n1
, tables
) {
650 const char *table
= n1
->name
;
651 struct ovsdb_table_schema
*table_schema
;
652 struct json
*rows
= n1
->data
;
653 struct shash_node
*n2
;
655 if (n1
->name
[0] == '_' || rows
->type
!= JSON_OBJECT
) {
663 table_schema
= schema
? shash_find_data(&schema
->tables
, table
) : NULL
;
664 SHASH_FOR_EACH (n2
, json_object(rows
)) {
665 const char *row_uuid
= n2
->name
;
666 struct json
*columns
= n2
->data
;
667 struct shash_node
*n3
;
669 const char *old_name
= smap_get(names
, row_uuid
);
670 char *new_name
= NULL
;
671 if (columns
->type
== JSON_OBJECT
) {
672 struct json
*new_name_json
;
674 new_name_json
= shash_find_data(json_object(columns
), "name");
676 new_name
= json_to_string(new_name_json
, JSSF_SORT
);
680 printf(" table %s", table
);
684 printf(" insert row %s (%.8s):\n", new_name
, row_uuid
);
686 printf(" insert row %.8s:\n", row_uuid
);
689 printf(" row %s (%.8s):\n", old_name
, row_uuid
);
692 if (columns
->type
== JSON_OBJECT
) {
693 if (show_log_verbosity
> 1) {
694 SHASH_FOR_EACH (n3
, json_object(columns
)) {
695 const char *column
= n3
->name
;
696 const struct ovsdb_column
*column_schema
;
697 struct json
*value
= n3
->data
;
698 char *value_string
= NULL
;
702 ? shash_find_data(&table_schema
->columns
, column
)
705 const struct ovsdb_type
*type
;
706 struct ovsdb_error
*error
;
707 struct ovsdb_datum datum
;
709 type
= &column_schema
->type
;
710 error
= ovsdb_datum_from_json(&datum
, type
,
716 ovsdb_datum_to_string(&datum
, type
, &s
);
717 value_string
= ds_steal_cstr(&s
);
719 ovsdb_error_destroy(error
);
723 value_string
= json_to_string(value
, JSSF_SORT
);
725 printf(" %s=%s\n", column
, value_string
);
730 if (new_name
&& (!old_name
|| strcmp(old_name
, new_name
))) {
731 smap_replace_nocopy(names
, row_uuid
, new_name
);
733 } else if (!old_name
) {
734 smap_add_nocopy(names
, xstrdup(row_uuid
),
735 xmemdup0(row_uuid
, 8));
737 } else if (columns
->type
== JSON_NULL
) {
738 printf(" delete row\n");
739 smap_remove(names
, row_uuid
);
748 print_change_record(const struct json
*json
, const struct ovsdb_schema
*schema
,
751 if (!json
|| json
->type
!= JSON_OBJECT
) {
755 struct json
*date
, *comment
;
757 date
= shash_find_data(json_object(json
), "_date");
758 if (date
&& date
->type
== JSON_INTEGER
) {
759 long long int t
= json_integer(date
);
763 /* Older versions of ovsdb wrote timestamps in seconds. */
767 s
= xastrftime_msec(" %Y-%m-%d %H:%M:%S.###", t
, true);
772 comment
= shash_find_data(json_object(json
), "_comment");
773 if (comment
&& comment
->type
== JSON_STRING
) {
774 printf(" \"%s\"", json_string(comment
));
777 if (show_log_verbosity
> 0) {
778 print_db_changes(json_object(json
), names
, schema
);
783 do_show_log_standalone(struct ovsdb_log
*log
)
785 struct smap names
= SMAP_INITIALIZER(&names
);
786 struct ovsdb_schema
*schema
= NULL
;
788 for (unsigned int i
= 0; ; i
++) {
791 check_ovsdb_error(ovsdb_log_read(log
, &json
));
796 printf("record %u:", i
);
798 check_ovsdb_error(ovsdb_schema_from_json(json
, &schema
));
799 printf(" \"%s\" schema, version=\"%s\", cksum=\"%s\"\n",
800 schema
->name
, schema
->version
, schema
->cksum
);
802 print_change_record(json
, schema
, &names
);
808 ovsdb_schema_destroy(schema
);
809 smap_destroy(&names
);
813 print_servers(const char *name
, const struct json
*servers
)
819 printf(" %s: ", name
);
821 const struct shash_node
**nodes
= shash_sort(json_object(servers
));
822 size_t n
= shash_count(json_object(servers
));
823 for (size_t i
= 0; i
< n
; i
++) {
828 const struct shash_node
*node
= nodes
[i
];
829 printf("%.4s(", node
->name
);
831 const struct json
*address
= node
->data
;
832 char *s
= json_to_string(address
, JSSF_SORT
);
843 print_data(const char *prefix
, const struct json
*data
,
844 struct ovsdb_schema
**schemap
, struct smap
*names
)
850 if (json_array(data
)->n
!= 2) {
851 printf(" ***invalid data***\n");
855 const struct json
*schema_json
= json_array(data
)->elems
[0];
856 if (schema_json
->type
!= JSON_NULL
) {
857 struct ovsdb_schema
*schema
;
859 check_ovsdb_error(ovsdb_schema_from_json(schema_json
, &schema
));
860 printf(" %sschema: \"%s\", version=\"%s\", cksum=\"%s\"\n",
861 prefix
, schema
->name
, schema
->version
, schema
->cksum
);
863 ovsdb_schema_destroy(*schemap
);
867 print_change_record(json_array(data
)->elems
[1], *schemap
, names
);
871 print_raft_header(const struct raft_header
*h
,
872 struct ovsdb_schema
**schemap
, struct smap
*names
)
874 printf(" name: \"%s\'\n", h
->name
);
875 printf(" local address: \"%s\"\n", h
->local_address
);
876 printf(" server_id: "SID_FMT
"\n", SID_ARGS(&h
->sid
));
877 if (!uuid_is_zero(&h
->cid
)) {
878 printf(" cluster_id: "CID_FMT
"\n", CID_ARGS(&h
->cid
));
880 if (!sset_is_empty(&h
->remote_addresses
)) {
881 printf(" remote_addresses:");
884 SSET_FOR_EACH (s
, &h
->remote_addresses
) {
890 printf(" prev_index: %"PRIu64
"\n", h
->snap_index
);
891 printf(" prev_term: %"PRIu64
"\n", h
->snap
.term
);
892 print_servers("prev_servers", h
->snap
.servers
);
893 if (!uuid_is_zero(&h
->snap
.eid
)) {
894 printf(" prev_eid: %04x\n", uuid_prefix(&h
->snap
.eid
, 4));
896 print_data("prev_", h
->snap
.data
, schemap
, names
);
901 print_raft_record(const struct raft_record
*r
,
902 struct ovsdb_schema
**schemap
, struct smap
*names
)
905 printf(" comment: \"%s\"\n", r
->comment
);
908 printf(" term: %"PRIu64
"\n", r
->term
);
913 printf(" index: %"PRIu64
"\n", r
->entry
.index
);
914 print_servers("servers", r
->entry
.servers
);
915 if (!uuid_is_zero(&r
->entry
.eid
)) {
916 printf(" eid: %04x\n", uuid_prefix(&r
->entry
.eid
, 4));
918 print_data("", r
->entry
.data
, schemap
, names
);
925 printf(" vote: "SID_FMT
"\n", SID_ARGS(&r
->sid
));
929 printf(" note: \"%s\"\n", r
->note
);
932 case RAFT_REC_COMMIT_INDEX
:
933 printf(" commit_index: %"PRIu64
"\n", r
->commit_index
);
936 case RAFT_REC_LEADER
:
937 printf(" leader: "SID_FMT
"\n", SID_ARGS(&r
->sid
));
946 do_show_log_cluster(struct ovsdb_log
*log
)
948 struct smap names
= SMAP_INITIALIZER(&names
);
949 struct ovsdb_schema
*schema
= NULL
;
950 for (unsigned int i
= 0; ; i
++) {
952 check_ovsdb_error(ovsdb_log_read(log
, &json
));
957 printf("record %u:\n", i
);
958 struct ovsdb_error
*error
;
960 struct raft_header h
;
961 error
= raft_header_from_json(&h
, json
);
963 print_raft_header(&h
, &schema
, &names
);
964 raft_header_uninit(&h
);
967 struct raft_record r
;
968 error
= raft_record_from_json(&r
, json
);
970 print_raft_record(&r
, &schema
, &names
);
971 raft_record_uninit(&r
);
975 char *s
= ovsdb_error_to_string_free(error
);
983 ovsdb_schema_destroy(schema
);
984 smap_destroy(&names
);
988 do_show_log(struct ovs_cmdl_context
*ctx
)
990 const char *db_file_name
= ctx
->argc
>= 2 ? ctx
->argv
[1] : default_db();
991 struct ovsdb_log
*log
;
993 check_ovsdb_error(ovsdb_log_open(db_file_name
, OVSDB_MAGIC
"|"RAFT_MAGIC
,
994 OVSDB_LOG_READ_ONLY
, -1, &log
));
995 if (!strcmp(ovsdb_log_get_magic(log
), OVSDB_MAGIC
)) {
996 do_show_log_standalone(log
);
998 do_show_log_cluster(log
);
1000 ovsdb_log_close(log
);
1004 struct ovsdb_log
*log
;
1005 const char *filename
;
1006 const char *nickname
;
1008 struct raft_header header
;
1010 struct raft_record
*records
;
1013 struct raft_entry
*snap
;
1014 struct raft_entry
*entries
;
1015 uint64_t log_start
, log_end
;
1019 /* In struct cluster's 'leaders', indexed by 'term'. */
1020 struct hmap_node hmap_node
;
1022 /* This structure indicates that in 'term', 'server' reported that 'leader'
1023 * was elected leader. When 'log_end' is nonzero, it additionally
1024 * indicates 'leader''s log_end at the time it was elected. */
1026 struct server
*server
;
1032 /* In struct cluster's 'commits', indexed by 'term'. */
1033 struct hmap_node hmap_node
;
1035 /* This structure indicates that in 'term', 'server' reported the commit
1036 * index as 'index'. */
1038 struct server
*server
;
1039 uint64_t index
; /* Commit index. */
1043 struct server
*servers
;
1046 struct hmap leaders
; /* Contains 'struct leader's. */
1048 struct hmap commits
; /* Contains 'struct commit's. */
1052 get_server_name(const struct cluster
*c
, const struct uuid
*sid
,
1053 char buf
[SID_LEN
+ 1], size_t bufsize
)
1055 for (size_t i
= 0; i
< c
->n_servers
; i
++) {
1056 struct server
*s
= &c
->servers
[i
];
1057 if (uuid_equals(&s
->header
.sid
, sid
)) {
1062 snprintf(buf
, bufsize
, SID_FMT
, SID_ARGS(sid
));
1066 static struct leader
*
1067 find_leader(struct cluster
*c
, uint64_t term
)
1069 struct leader
*leader
;
1070 HMAP_FOR_EACH_WITH_HASH (leader
, hmap_node
, hash_uint64(term
),
1072 if (term
== leader
->term
) {
1079 /* Records that 'server' reported that 'leader' was elected leader in 'term'.
1081 * Checks the Election Safety Property: at most one leader may be elected in a
1082 * single term (see Figure 3.2). */
1084 record_leader(struct cluster
*c
, uint64_t term
, struct server
*server
,
1085 const struct uuid
*leader
)
1087 bool server_is_leader
= uuid_equals(&server
->header
.sid
, leader
);
1088 struct leader
*p
= find_leader(c
, term
);
1090 if (!uuid_equals(&p
->leader
, leader
)) {
1091 char buf1
[SID_LEN
+ 1];
1092 char buf2
[SID_LEN
+ 1];
1093 ovs_fatal(0, "term %"PRIu64
" has two different leaders: "
1094 "%s says that the leader is %s and "
1095 "%s says that the leader is %s",
1097 p
->server
->filename
,
1098 get_server_name(c
, &p
->leader
, buf1
, sizeof buf1
),
1100 get_server_name(c
, leader
, buf2
, sizeof buf2
));
1102 if (server_is_leader
&& server
->log_end
> p
->log_end
) {
1103 p
->log_end
= server
->log_end
;
1106 p
= xmalloc(sizeof *p
);
1107 hmap_insert(&c
->leaders
, &p
->hmap_node
, hash_uint64(term
));
1110 p
->leader
= *leader
;
1111 if (server_is_leader
) {
1112 p
->log_end
= server
->log_end
;
1119 static struct commit
*
1120 find_commit(struct cluster
*c
, uint64_t term
)
1122 struct commit
*commit
;
1123 HMAP_FOR_EACH_WITH_HASH (commit
, hmap_node
, hash_uint64(term
),
1125 if (term
== commit
->term
) {
1133 record_commit(struct cluster
*c
, uint64_t term
, struct server
*server
,
1134 uint64_t commit_index
)
1136 struct commit
*commit
= find_commit(c
, term
);
1138 if (commit_index
> commit
->index
) {
1139 commit
->server
= server
;
1140 commit
->index
= commit_index
;
1143 commit
= xmalloc(sizeof *commit
);
1144 hmap_insert(&c
->commits
, &commit
->hmap_node
, hash_uint64(term
));
1145 commit
->term
= term
;
1146 commit
->server
= server
;
1147 commit
->index
= commit_index
;
1152 do_check_cluster(struct ovs_cmdl_context
*ctx
)
1154 struct cluster c
= {
1155 .servers
= xzalloc((ctx
->argc
- 1) * sizeof *c
.servers
),
1157 .leaders
= HMAP_INITIALIZER(&c
.leaders
),
1158 .commits
= HMAP_INITIALIZER(&c
.commits
),
1161 uint64_t min_term
= UINT64_MAX
;
1162 uint64_t max_term
= 0;
1164 for (int i
= 1; i
< ctx
->argc
; i
++) {
1165 struct server
*s
= &c
.servers
[c
.n_servers
];
1166 s
->filename
= ctx
->argv
[i
];
1168 check_ovsdb_error(ovsdb_log_open(s
->filename
, RAFT_MAGIC
,
1169 OVSDB_LOG_READ_ONLY
, -1, &s
->log
));
1172 check_ovsdb_error(ovsdb_log_read(s
->log
, &json
));
1173 check_ovsdb_error(raft_header_from_json(&s
->header
, json
));
1176 if (s
->header
.joining
) {
1177 printf("%s has not joined the cluster, omitting\n", s
->filename
);
1178 ovsdb_log_close(s
->log
);
1181 for (size_t j
= 0; j
< c
.n_servers
; j
++) {
1182 if (uuid_equals(&s
->header
.sid
, &c
.servers
[j
].header
.sid
)) {
1183 ovs_fatal(0, "Duplicate server ID "SID_FMT
" in %s and %s.",
1184 SID_ARGS(&s
->header
.sid
),
1185 s
->filename
, c
.servers
[j
].filename
);
1188 if (c
.n_servers
> 0) {
1189 struct server
*s0
= &c
.servers
[0];
1190 if (!uuid_equals(&s0
->header
.cid
, &s
->header
.cid
)) {
1191 ovs_fatal(0, "%s has cluster ID "CID_FMT
" but %s "
1192 "has cluster ID "CID_FMT
,
1193 s0
->filename
, CID_ARGS(&s0
->header
.cid
),
1194 s
->filename
, CID_ARGS(&s
->header
.cid
));
1196 if (strcmp(s0
->header
.name
, s
->header
.name
)) {
1197 ovs_fatal(0, "%s is named \"%s\" but %s is named \"%s\"",
1198 s0
->filename
, s0
->header
.name
,
1199 s
->filename
, s
->header
.name
);
1205 for (struct server
*s
= c
.servers
; s
< &c
.servers
[c
.n_servers
]; s
++) {
1206 s
->snap
= &s
->header
.snap
;
1207 s
->log_start
= s
->log_end
= s
->header
.snap_index
+ 1;
1209 size_t allocated_records
= 0;
1210 size_t allocated_entries
= 0;
1212 uint64_t term
= 0; /* Current term. */
1213 struct uuid vote
= UUID_ZERO
; /* Server 's''s vote in 'term'. */
1214 struct uuid leader
= UUID_ZERO
; /* Cluster leader in 'term'. */
1215 uint64_t leader_rec_idx
= 0; /* Index of last "leader" record. */
1217 uint64_t commit_index
= s
->header
.snap_index
;
1219 for (unsigned long long int rec_idx
= 1; ; rec_idx
++) {
1220 if (s
->n_records
>= allocated_records
) {
1221 s
->records
= x2nrealloc(s
->records
, &allocated_records
,
1222 sizeof *s
->records
);
1226 check_ovsdb_error(ovsdb_log_read(s
->log
, &json
));
1230 struct raft_record
*r
= &s
->records
[s
->n_records
++];
1231 check_ovsdb_error(raft_record_from_json(r
, json
));
1234 if (r
->term
> term
) {
1240 if (term
< min_term
) {
1243 if (term
> max_term
) {
1248 case RAFT_REC_ENTRY
:
1249 if (r
->entry
.index
< commit_index
) {
1250 ovs_fatal(0, "%s: record %llu attempts to truncate log "
1251 "from %"PRIu64
" to %"PRIu64
" entries, but "
1252 "commit index is already %"PRIu64
,
1253 s
->filename
, rec_idx
,
1254 s
->log_end
, r
->entry
.index
,
1256 } else if (r
->entry
.index
> s
->log_end
) {
1257 ovs_fatal(0, "%s: record %llu with index %"PRIu64
" skips "
1258 "past expected index %"PRIu64
, s
->filename
,
1259 rec_idx
, r
->entry
.index
, s
->log_end
);
1262 if (r
->entry
.index
< s
->log_end
) {
1263 bool is_leader
= uuid_equals(&s
->header
.sid
, &leader
);
1265 /* Leader Append-Only property (see Figure 3.2). */
1266 ovs_fatal(0, "%s: record %llu truncates log from "
1267 "%"PRIu64
" to %"PRIu64
" entries while "
1268 "server is leader", s
->filename
, rec_idx
,
1269 s
->log_end
, r
->entry
.index
);
1271 /* This can happen, but it is unusual. */
1272 printf("%s: record %llu truncates log from %"PRIu64
1273 " to %"PRIu64
" entries\n", s
->filename
, rec_idx
,
1274 s
->log_end
, r
->entry
.index
);
1276 s
->log_end
= r
->entry
.index
;
1279 uint64_t prev_term
= (s
->log_end
> s
->log_start
1280 ? s
->entries
[s
->log_end
1281 - s
->log_start
- 1].term
1283 if (r
->term
< prev_term
) {
1284 ovs_fatal(0, "%s: record %llu with index %"PRIu64
" term "
1285 "%"PRIu64
" precedes previous entry's term "
1286 "%"PRIu64
, s
->filename
, rec_idx
,
1287 r
->entry
.index
, r
->term
, prev_term
);
1290 uint64_t log_idx
= s
->log_end
++ - s
->log_start
;
1291 if (log_idx
>= allocated_entries
) {
1292 s
->entries
= x2nrealloc(s
->entries
, &allocated_entries
,
1293 sizeof *s
->entries
);
1295 struct raft_entry
*e
= &s
->entries
[log_idx
];
1297 e
->data
= r
->entry
.data
;
1298 e
->eid
= r
->entry
.eid
;
1299 e
->servers
= r
->entry
.servers
;
1306 if (r
->term
< term
) {
1307 ovs_fatal(0, "%s: record %llu votes for term %"PRIu64
" "
1308 "but current term is %"PRIu64
, s
->filename
,
1309 rec_idx
, r
->term
, term
);
1310 } else if (!uuid_is_zero(&vote
)
1311 && !uuid_equals(&vote
, &r
->sid
)) {
1312 char buf1
[SID_LEN
+ 1];
1313 char buf2
[SID_LEN
+ 1];
1314 ovs_fatal(0, "%s: record %llu votes for %s in term "
1315 "%"PRIu64
" but a previous record for the "
1316 "same term voted for %s", s
->filename
,
1318 get_server_name(&c
, &vote
, buf1
, sizeof buf1
),
1320 get_server_name(&c
, &r
->sid
, buf2
, sizeof buf2
));
1327 if (!strcmp(r
->note
, "left")) {
1328 printf("%s: record %llu shows that the server left the "
1329 "cluster\n", s
->filename
, rec_idx
);
1333 case RAFT_REC_COMMIT_INDEX
:
1334 if (r
->commit_index
< commit_index
) {
1335 ovs_fatal(0, "%s: record %llu regresses commit index "
1336 "from %"PRIu64
" to %"PRIu64
, s
->filename
,
1337 rec_idx
, commit_index
, r
->commit_index
);
1338 } else if (r
->commit_index
>= s
->log_end
) {
1339 ovs_fatal(0, "%s: record %llu advances commit index to "
1340 "%"PRIu64
" but last log index is %"PRIu64
,
1341 s
->filename
, rec_idx
, r
->commit_index
,
1344 commit_index
= r
->commit_index
;
1347 record_commit(&c
, term
, s
, r
->commit_index
);
1350 case RAFT_REC_LEADER
:
1351 if (!uuid_equals(&r
->sid
, &leader
)) {
1352 if (uuid_is_zero(&leader
)) {
1354 leader_rec_idx
= rec_idx
;
1356 char buf1
[SID_LEN
+ 1];
1357 char buf2
[SID_LEN
+ 1];
1358 ovs_fatal(0, "%s: record %llu reports leader %s "
1359 "for term %"PRIu64
" but record %"PRIu64
" "
1360 "previously reported the leader as %s "
1362 s
->filename
, rec_idx
,
1363 get_server_name(&c
, &r
->sid
,
1365 term
, leader_rec_idx
,
1366 get_server_name(&c
, &leader
,
1367 buf2
, sizeof buf2
));
1370 record_leader(&c
, term
, s
, &r
->sid
);
1375 ovsdb_log_close(s
->log
);
1379 /* Check the Leader Completeness property from Figure 3.2: If a log entry
1380 * is committed in a given term, then that entry will be present in the
1381 * logs of the leaders for all higher-numbered terms. */
1382 if (min_term
== UINT64_MAX
|| max_term
== 0) {
1383 ovs_fatal(0, "all logs are empty");
1385 struct commit
*commit
= NULL
;
1386 for (uint64_t term
= min_term
; term
<= max_term
; term
++) {
1387 struct leader
*leader
= find_leader(&c
, term
);
1388 if (leader
&& commit
&& commit
->index
>= leader
->log_end
) {
1389 ovs_fatal(0, "leader %s for term %"PRIu64
" has log entries only "
1390 "up to index %"PRIu64
", but index %"PRIu64
" was "
1391 "committed in a previous term (e.g. by %s)",
1392 leader
->server
->filename
, term
, leader
->log_end
- 1,
1393 commit
->index
, commit
->server
->filename
);
1396 struct commit
*next
= find_commit(&c
, term
);
1397 if (next
&& (!commit
|| next
->index
> commit
->index
)) {
1402 /* Section 3.5: Check the Log Matching Property in Figure 3.2:
1404 * - If two entries in different logs have the same index and term, then
1405 * they store the same command.
1407 * - If two entries in different logs have the same index and term, then
1408 * the logs are identical in all preceding entries.
1410 for (size_t i
= 0; i
< c
.n_servers
; i
++) {
1411 for (size_t j
= 0; j
< c
.n_servers
; j
++) {
1412 struct server
*a
= &c
.servers
[i
];
1413 struct server
*b
= &c
.servers
[j
];
1419 bool must_equal
= false;
1420 for (uint64_t idx
= MIN(a
->log_end
, b
->log_end
) - 1;
1421 idx
>= MAX(a
->log_start
, b
->log_start
);
1423 const struct raft_entry
*ae
= &a
->entries
[idx
- a
->log_start
];
1424 const struct raft_entry
*be
= &b
->entries
[idx
- b
->log_start
];
1425 if (ae
->term
== be
->term
) {
1428 if (!must_equal
|| raft_entry_equals(ae
, be
)) {
1431 char *as
= json_to_string(raft_entry_to_json(ae
), JSSF_SORT
);
1432 char *bs
= json_to_string(raft_entry_to_json(be
), JSSF_SORT
);
1433 ovs_fatal(0, "log entries with index %"PRIu64
" differ:\n"
1436 idx
, a
->filename
, as
, b
->filename
, bs
);
1443 for (size_t i
= 0; i
< c
.n_servers
; i
++) {
1444 struct server
*s
= &c
.servers
[i
];
1446 raft_header_uninit(&s
->header
);
1447 for (size_t j
= 0; j
< s
->n_records
; j
++) {
1448 struct raft_record
*r
= &s
->records
[j
];
1450 raft_record_uninit(r
);
1457 struct commit
*next_commit
;
1458 HMAP_FOR_EACH_SAFE (commit
, next_commit
, hmap_node
, &c
.commits
) {
1459 hmap_remove(&c
.commits
, &commit
->hmap_node
);
1462 hmap_destroy(&c
.commits
);
1464 struct leader
*leader
, *next_leader
;
1465 HMAP_FOR_EACH_SAFE (leader
, next_leader
, hmap_node
, &c
.leaders
) {
1466 hmap_remove(&c
.leaders
, &leader
->hmap_node
);
1469 hmap_destroy(&c
.leaders
);
1472 static struct ovsdb_version
1473 parse_version(const char *s
)
1475 struct ovsdb_version version
;
1476 if (!ovsdb_parse_version(s
, &version
)) {
1477 ovs_fatal(0, "%s: not an OVSDB version number in format x.y.z", s
);
1483 do_compare_versions(struct ovs_cmdl_context
*ctx
)
1485 struct ovsdb_version a
= parse_version(ctx
->argv
[1]);
1486 struct ovsdb_version b
= parse_version(ctx
->argv
[3]);
1487 int cmp
= (a
.x
!= b
.x
? (a
.x
> b
.x
? 1 : -1)
1488 : a
.y
!= b
.y
? (a
.y
> b
.y
? 1 : -1)
1489 : a
.z
!= b
.z
? (a
.z
> b
.z
? 1 : -1)
1492 const char *op
= ctx
->argv
[2];
1494 if (!strcmp(op
, "<")) {
1496 } else if (!strcmp(op
, "<=")) {
1498 } else if (!strcmp(op
, "==")) {
1500 } else if (!strcmp(op
, ">=")) {
1502 } else if (!strcmp(op
, ">")) {
1504 } else if (!strcmp(op
, "!=")) {
1507 ovs_fatal(0, "%s: not a relational operator", op
);
1510 exit(result
? 0 : 2);
1515 do_help(struct ovs_cmdl_context
*ctx OVS_UNUSED
)
1521 do_list_commands(struct ovs_cmdl_context
*ctx OVS_UNUSED
)
1523 ovs_cmdl_print_commands(get_all_commands());
1526 static const struct ovs_cmdl_command all_commands
[] = {
1527 { "create", "[db [schema]]", 0, 2, do_create
, OVS_RW
},
1528 { "create-cluster", "db contents local", 3, 3, do_create_cluster
, OVS_RW
},
1529 { "join-cluster", "db name local remote...", 4, INT_MAX
, do_join_cluster
,
1531 { "compact", "[db [dst]]", 0, 2, do_compact
, OVS_RW
},
1532 { "convert", "[db [schema [dst]]]", 0, 3, do_convert
, OVS_RW
},
1533 { "needs-conversion", NULL
, 0, 2, do_needs_conversion
, OVS_RO
},
1534 { "db-name", "[db]", 0, 1, do_db_name
, OVS_RO
},
1535 { "db-version", "[db]", 0, 1, do_db_version
, OVS_RO
},
1536 { "db-cksum", "[db]", 0, 1, do_db_cksum
, OVS_RO
},
1537 { "db-cid", "db", 1, 1, do_db_cid
, OVS_RO
},
1538 { "db-sid", "db", 1, 1, do_db_sid
, OVS_RO
},
1539 { "db-local-address", "db", 1, 1, do_db_local_address
, OVS_RO
},
1540 { "db-is-clustered", "db", 1, 1, do_db_is_clustered
, OVS_RO
},
1541 { "db-is-standalone", "db", 1, 1, do_db_is_standalone
, OVS_RO
},
1542 { "schema-name", "[schema]", 0, 1, do_schema_name
, OVS_RO
},
1543 { "schema-version", "[schema]", 0, 1, do_schema_version
, OVS_RO
},
1544 { "schema-cksum", "[schema]", 0, 1, do_schema_cksum
, OVS_RO
},
1545 { "query", "[db] trns", 1, 2, do_query
, OVS_RO
},
1546 { "transact", "[db] trns", 1, 2, do_transact
, OVS_RO
},
1547 { "show-log", "[db]", 0, 1, do_show_log
, OVS_RO
},
1548 { "check-cluster", "db...", 1, INT_MAX
, do_check_cluster
, OVS_RO
},
1549 { "compare-versions", "a op b", 3, 3, do_compare_versions
, OVS_RO
},
1550 { "help", NULL
, 0, INT_MAX
, do_help
, OVS_RO
},
1551 { "list-commands", NULL
, 0, INT_MAX
, do_list_commands
, OVS_RO
},
1552 { NULL
, NULL
, 0, 0, NULL
, OVS_RO
},
1555 static const struct ovs_cmdl_command
*get_all_commands(void)
1557 return all_commands
;