diff options
author | Ben Pfaff <blp@ovn.org> | 2017-12-21 16:41:30 -0800 |
---|---|---|
committer | Ben Pfaff <blp@ovn.org> | 2017-12-21 16:41:30 -0800 |
commit | fe0fb88551b4cc5b4bee6814f1027f78c451daa2 (patch) | |
tree | 5cac6efc848ef3dba694a9219e2da8632bdcf23f /ovsdb | |
parent | 62705b81108ff6d011362331847cf0ba22494779 (diff) | |
download | openvswitch-fe0fb88551b4cc5b4bee6814f1027f78c451daa2.tar.gz |
ovsdb-client: Add new "restore" command.
Signed-off-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'ovsdb')
-rw-r--r-- | ovsdb/ovsdb-client.1.in | 33 | ||||
-rw-r--r-- | ovsdb/ovsdb-client.c | 110 |
2 files changed, 139 insertions, 4 deletions
diff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in index 61cd792af..7ab452f6d 100644 --- a/ovsdb/ovsdb-client.1.in +++ b/ovsdb/ovsdb-client.1.in @@ -31,7 +31,10 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1) [\fIcolumn\fR...]] .br \fBovsdb\-client \fR[\fIoptions\fR] -\fBbackup \fR[\fIserver\fR] [\fIdatabase\fR] > \fIsnapshot\fR +\fBbackup \fR[\fIserver\fR] [\fIdatabase\fR] \fB> \fIsnapshot\fR +.br +\fBovsdb\-client \fR[\fIoptions\fR] [\fB\-\-force\fR] +\fBrestore \fR[\fIserver\fR] [\fIdatabase\fR] \fB< \fIsnapshot\fR .br \fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]... @@ -41,7 +44,6 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1) \fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\-cond\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fIconditions \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]... .IP "Testing Commands:" -.br \fBovsdb\-client \fR[\fIoptions\fR] \fBlock\fI \fR[\fIserver\fR] \fIlock\fR .br \fBovsdb\-client \fR[\fIoptions\fR] \fBsteal\fI \fR[\fIserver\fR] \fIlock\fR @@ -141,7 +143,7 @@ and prints it on stdout as a series of tables. If \fItable\fR is specified, only that table is retrieved. If at least one \fIcolumn\fR is specified, only those columns are retrieved. . -.IP "\fBbackup \fR[\fIserver\fR] [\fIdatabase\fR] > \fIsnapshot\fR" +.IP "\fBbackup \fR[\fIserver\fR] [\fIdatabase\fR] \fB> \fIsnapshot\fR" Connects to \fIserver\fR, retrieves a snapshot of the schema and data in \fIdatabase\fR, and prints it on stdout in the format used for OVSDB database files. This is an appropriate @@ -156,6 +158,31 @@ database is in use. The output does not include ephemeral columns, which by design do not survive across restarts of \fBovsdb\-server\fR. . +.IP "[\fB\-\-force\fR] \fBrestore \fR[\fIserver\fR] [\fIdatabase\fR] \fB< \fIsnapshot\fR" +Reads \fIsnapshot\fR, which must be a OVSDB standalone or +active-backup database (possibly but not necessarily created by +\fBovsdb\-client backup). Then, connects to \fIserver\fR, verifies +that \fIdatabase\fR and \fIsnapshot\fR have the same schema, then +deletes all of the data in \fIdatabase\fR and replaces it by +\fIsnapshot\fR. The replacement happens atomically, in a single +transaction. +.IP +UUIDs for rows in the restored database will differ from those in +\fIsnapshot\fR, because the OVSDB protocol does not allow clients to +specify row UUIDs. Another way to restore a database, +which does also restore row UUIDs, is to stop +the server or servers, replace the database file by the snapshot, then +restart the database. Either way, ephemeral columns are not restored, +since by design they do not survive across restarts of +\fBovsdb\-server\fR. +.IP +Normally \fBrestore\fR exits with a failure if \fBsnapshot\fR and the +server's database have different schemas. In such a case, it is a +good idea to convert the database to the new schema before restoring, +e.g. with \fBovsdb\-client convert\fR. Use \fB\-\-force\fR to proceed +regardless of schema differences even though the restore might fail +with an error or succeed with surprising results. +. .IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]..." .IQ "\fBmonitor\-cond\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fIconditions\fR \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]..." Connects to \fIserver\fR and monitors the contents of rows that match conditions in diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c index 568c46b84..349bc55d4 100644 --- a/ovsdb/ovsdb-client.c +++ b/ovsdb/ovsdb-client.c @@ -41,6 +41,7 @@ #include "ovsdb-data.h" #include "ovsdb-error.h" #include "openvswitch/poll-loop.h" +#include "row.h" #include "sort.h" #include "svec.h" #include "stream.h" @@ -73,6 +74,9 @@ struct ovsdb_client_command { /* --timestamp: Print a timestamp before each update on "monitor" command? */ static bool timestamp; +/* --force: Ignore schema differences for "restore" command? */ +static bool force; + /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; @@ -175,6 +179,7 @@ parse_options(int argc, char *argv[]) enum { OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1, OPT_TIMESTAMP, + OPT_FORCE, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS, TABLE_OPTION_ENUMS, @@ -184,6 +189,7 @@ parse_options(int argc, char *argv[]) {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"timestamp", no_argument, NULL, OPT_TIMESTAMP}, + {"force", no_argument, NULL, OPT_FORCE}, VLOG_LONG_OPTIONS, DAEMON_LONG_OPTIONS, #ifdef HAVE_OPENSSL @@ -226,6 +232,10 @@ parse_options(int argc, char *argv[]) timestamp = true; break; + case OPT_FORCE: + force = true; + break; + case '?': exit(EXIT_FAILURE); @@ -277,8 +287,10 @@ usage(void) " in DATBASE on SERVER.\n" "\n dump [SERVER] [DATABASE]\n" " dump contents of DATABASE on SERVER to stdout\n" - "\n backup [SERVER] [DATABASE] > DB\n" + "\n backup [SERVER] [DATABASE] > SNAPSHOT\n" " dump database contents in the form of a database file\n" + "\n [--force] restore [SERVER] [DATABASE] < SNAPSHOT\n" + " restore database contents from a database file\n" "\n lock [SERVER] LOCK\n" " create or wait for LOCK in SERVER\n" "\n steal [SERVER] LOCK\n" @@ -1516,6 +1528,101 @@ do_backup(struct jsonrpc *rpc, const char *database, } static void +do_restore(struct jsonrpc *rpc, const char *database, + int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + if (isatty(STDIN_FILENO)) { + ovs_fatal(0, "not reading backup from a terminal; " + "please redirect stdin from a file"); + } + + struct ovsdb *backup; + check_ovsdb_error(ovsdb_file_open("/dev/stdin", true, &backup, NULL)); + + const struct ovsdb_schema *schema = backup->schema; + struct ovsdb_schema *schema2 = fetch_schema(rpc, database); + if (!ovsdb_schema_equal(schema, schema2)) { + struct ds s = DS_EMPTY_INITIALIZER; + if (strcmp(schema->version, schema2->version)) { + ds_put_format(&s, "backup schema has version \"%s\" but " + "database schema has version \"%s\"", + schema->version, schema2->version); + } else { + ds_put_format(&s, "backup schema and database schema are " + "both version %s but still differ", + schema->version); + } + if (!force) { + ovs_fatal(0, "%s (use --force to override differences, or " + "\"ovsdb-client convert\" to change the schema)", + ds_cstr(&s)); + } + VLOG_INFO("%s", ds_cstr(&s)); + ds_destroy(&s); + } + + struct json *txn = json_array_create_empty(); + json_array_add(txn, json_string_create(schema->name)); + struct shash_node *node; + SHASH_FOR_EACH (node, &backup->tables) { + const char *table_name = node->name; + struct ovsdb_table *table = node->data; + + struct json *del_op = json_object_create(); + json_object_put_string(del_op, "op", "delete"); + json_object_put_string(del_op, "table", table_name); + json_object_put(del_op, "where", json_array_create_empty()); + json_array_add(txn, del_op); + + const struct ovsdb_row *row; + HMAP_FOR_EACH (row, hmap_node, &table->rows) { + struct json *ins_op = json_object_create(); + json_object_put_string(ins_op, "op", "insert"); + json_object_put_string(ins_op, "table", table_name); + json_object_put(ins_op, "uuid-name", + json_string_create_nocopy( + ovsdb_data_row_name(ovsdb_row_get_uuid(row)))); + struct json *row_json = json_object_create(); + json_object_put(ins_op, "row", row_json); + + struct shash_node *node2; + SHASH_FOR_EACH (node2, &table->schema->columns) { + const struct ovsdb_column *column = node2->data; + const struct ovsdb_datum *datum = &row->fields[column->index]; + const struct ovsdb_type *type = &column->type; + if (column->persistent + && column->index >= OVSDB_N_STD_COLUMNS + && !ovsdb_datum_is_default(datum, type)) { + struct json *value = ovsdb_datum_to_json_with_row_names( + datum, type); + json_object_put(row_json, column->name, value); + } + } + json_array_add(txn, ins_op); + } + } + struct jsonrpc_msg *rq = jsonrpc_create_request("transact", txn, NULL); + struct jsonrpc_msg *reply; + check_txn(jsonrpc_transact_block(rpc, rq, &reply), &reply); + if (reply->result->type != JSON_ARRAY) { + ovs_fatal(0, "result is not array"); + } + for (size_t i = 0; i < json_array(reply->result)->n; i++) { + struct json *json = json_array(reply->result)->elems[i]; + if (json->type != JSON_OBJECT) { + ovs_fatal(0, "result array element is not object"); + } + struct shash *object = json_object(json); + if (shash_find(object, "error")) { + ovs_fatal(0, "server returned error reply: %s", + json_to_string(json, JSSF_SORT)); + } + } + jsonrpc_msg_destroy(reply); +} + + +static void do_help(struct jsonrpc *rpc OVS_UNUSED, const char *database OVS_UNUSED, int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { @@ -1728,6 +1835,7 @@ static const struct ovsdb_client_command all_commands[] = { { "monitor-cond", NEED_DATABASE, 2, 3, do_monitor_cond }, { "dump", NEED_DATABASE, 0, INT_MAX, do_dump }, { "backup", NEED_DATABASE, 0, 0, do_backup }, + { "restore", NEED_DATABASE, 0, 0, do_restore }, { "lock", NEED_RPC, 1, 1, do_lock_create }, { "steal", NEED_RPC, 1, 1, do_lock_steal }, { "unlock", NEED_RPC, 1, 1, do_lock_unlock }, |