Skip to content

Commit

Permalink
ovsdb-tool: Convert clustered db to standalone db.
Browse files Browse the repository at this point in the history
Add support in ovsdb-tool for migrating clustered dbs to standalone dbs.
E.g. usage to migrate nb/sb db to standalone db from raft:
ovsdb-tool cluster-to-standalone ovnnb_db.db ovnnb_db_cluster.db

Acked-by: Han Zhou <[email protected]>
Signed-off-by: Aliasgar Ginwala <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
noah8713 authored and blp committed Sep 23, 2019
1 parent 45bd8c5 commit 00de46f
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Documentation/ref/ovsdb.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,9 @@ standalone database from the contents of a running clustered database.
When the cluster is down and cannot be revived, ``ovsdb-client backup`` will
not work.

Use ``ovsdb-tool cluster-to-standalone`` to convert clustered database to
standalone database when the cluster is down and cannot be revived.

Upgrading or Downgrading a Database
-----------------------------------

Expand Down
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ v2.12.0 - 03 Sep 2019
quickly after a brief disconnection, saving bandwidth and CPU time.
See section 4.1.15 of ovsdb-server(7) for details of related OVSDB
protocol extension.
* Support to convert from cluster database to standalone database is now
available when clustered is down and cannot be revived using ovsdb-tool
. Check "Database Migration Commands" in ovsdb-tool man section.
- OVN:
* IPAM/MACAM:
- select IPAM mac_prefix in a random manner if not provided by the user
Expand Down
8 changes: 8 additions & 0 deletions ovsdb/ovsdb-tool.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ avoid this possibility, specify \fB\-\-cid=\fIuuid\fR, where
\fIuuid\fR is the cluster ID of the cluster to join, as printed by
\fBovsdb\-tool get\-cid\fR.
.
.SS "Database Migration Commands"
This commands will convert cluster database to standalone database.
.
.IP "\fBcluster\-to\-standalone\fI db clusterdb"
Use this command to convert to standalone database from clustered database
when the cluster is down and cannot be revived. It creates new standalone
\fIdb\fR file from the given cluster \fIdb\fR file.
.
.SS "Version Management Commands"
.so ovsdb/ovsdb-schemas.man
.PP
Expand Down
101 changes: 100 additions & 1 deletion ovsdb/ovsdb-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ usage(void)
" compare-versions A OP B compare OVSDB schema version numbers\n"
" query [DB] TRNS execute read-only transaction on DB\n"
" transact [DB] TRNS execute read/write transaction on DB\n"
" cluster-to-standalone DB DB Convert clustered DB to\n"
" standalone DB when cluster is down and cannot be\n"
" revived\n"
" [-m]... show-log [DB] print DB's log entries\n"
"The default DB is %s.\n"
"The default SCHEMA is %s.\n",
Expand Down Expand Up @@ -942,6 +945,55 @@ print_raft_record(const struct raft_record *r,
}
}

static void
raft_header_to_standalone_log(const struct raft_header *h,
struct ovsdb_log *db_log_data)
{
if (h->snap_index) {
if (!h->snap.data || json_array(h->snap.data)->n != 2) {
ovs_fatal(0, "Incorrect raft header data array length");
}

struct json *schema_json = json_array(h->snap.data)->elems[0];
if (schema_json->type != JSON_NULL) {
struct ovsdb_schema *schema;
check_ovsdb_error(ovsdb_schema_from_json(schema_json, &schema));
ovsdb_schema_destroy(schema);
check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
schema_json));
}

struct json *data_json = json_array(h->snap.data)->elems[1];
if (!data_json || data_json->type != JSON_OBJECT) {
ovs_fatal(0, "Invalid raft header data");
}
if (data_json->type != JSON_NULL) {
check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
data_json));
}
}
}

static void
raft_record_to_standalone_log(const struct raft_record *r,
struct ovsdb_log *db_log_data)
{
if (r->type == RAFT_REC_ENTRY) {
if (!r->entry.data) {
return;
}
if (json_array(r->entry.data)->n != 2) {
ovs_fatal(0, "Incorrect raft record array length");
}

struct json *data_json = json_array(r->entry.data)->elems[1];
if (data_json->type != JSON_NULL) {
check_ovsdb_error(ovsdb_log_write_and_free(db_log_data,
data_json));
}
}
}

static void
do_show_log_cluster(struct ovsdb_log *log)
{
Expand Down Expand Up @@ -1511,6 +1563,51 @@ do_compare_versions(struct ovs_cmdl_context *ctx)
exit(result ? 0 : 2);
}

static void
do_convert_to_standalone(struct ovsdb_log *log, struct ovsdb_log *db_log_data)
{
for (unsigned int i = 0; ; i++) {
struct json *json;
check_ovsdb_error(ovsdb_log_read(log, &json));
if (!json) {
break;
}

if (i == 0) {
struct raft_header h;
check_ovsdb_error(raft_header_from_json(&h, json));
raft_header_to_standalone_log(&h, db_log_data);
raft_header_uninit(&h);
} else {
struct raft_record r;
check_ovsdb_error(raft_record_from_json(&r, json));
raft_record_to_standalone_log(&r, db_log_data);
raft_record_uninit(&r);
}
}
}

static void
do_cluster_standalone(struct ovs_cmdl_context *ctx)
{
const char *db_file_name = ctx->argv[1];
const char *cluster_db_file_name = ctx->argv[2];
struct ovsdb_log *log;
struct ovsdb_log *db_log_data;

check_ovsdb_error(ovsdb_log_open(cluster_db_file_name,
OVSDB_MAGIC"|"RAFT_MAGIC,
OVSDB_LOG_READ_ONLY, -1, &log));
check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_MAGIC,
OVSDB_LOG_CREATE_EXCL, -1, &db_log_data));
if (strcmp(ovsdb_log_get_magic(log), RAFT_MAGIC) != 0) {
ovs_fatal(0, "Database is not clustered db.\n");
}
do_convert_to_standalone(log, db_log_data);
check_ovsdb_error(ovsdb_log_commit_block(db_log_data));
ovsdb_log_close(db_log_data);
ovsdb_log_close(log);
}

static void
do_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
Expand Down Expand Up @@ -1550,7 +1647,9 @@ static const struct ovs_cmdl_command all_commands[] = {
{ "compare-versions", "a op b", 3, 3, do_compare_versions, OVS_RO },
{ "help", NULL, 0, INT_MAX, do_help, OVS_RO },
{ "list-commands", NULL, 0, INT_MAX, do_list_commands, OVS_RO },
{ NULL, NULL, 0, 0, NULL, OVS_RO },
{ "cluster-to-standalone", "db clusterdb", 2, 2,
do_cluster_standalone, OVS_RW },
{ NULL, NULL, 2, 2, NULL, OVS_RO },
};

static const struct ovs_cmdl_command *get_all_commands(void)
Expand Down
38 changes: 38 additions & 0 deletions tests/ovsdb-tool.at
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,41 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
# Make sure that the clustered data matched the standalone data.
AT_CHECK([cat dump2], [0], [expout])
AT_CLEANUP

AT_SETUP([ovsdb-tool convert-to-standalone])
AT_KEYWORDS([ovsdb file positive])
ordinal_schema > schema
AT_CHECK([ovsdb-tool create-cluster db schema unix:s1.raft], [0], [stdout], [ignore])
AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db >/dev/null 2>&1])
for txn in m4_foreach([txn], [[[["ordinals",
{"op": "insert",
"table": "ordinals",
"row": {"number": 0, "name": "zero"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 1, "name": "one"}},
{"op": "insert",
"table": "ordinals",
"row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do
AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore])
done
AT_CHECK([ovsdb-client transact unix:socket '[["ordinals"]]'], [0],
[ignore], [ignore])
AT_CHECK([ovsdb-client dump unix:socket > clusterdump])
AT_CHECK([ovs-appctl -t ovsdb-server -e exit], [0], [ignore], [ignore])

# Convert to standalone database from clustered database.
AT_CHECK(ovsdb-tool cluster-to-standalone db1 db)

# Check its standalone db
AT_CHECK([ovsdb-tool db-is-standalone db1])

# Dump the standalone db data.
AT_CHECK([ovsdb-server -vconsole:off -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db1])
AT_CHECK([ovsdb_client_wait ordinals connected])
AT_CHECK([ovsdb-client dump > standalonedump])
OVS_APP_EXIT_AND_WAIT([ovsdb-server])

# Make sure both standalone and cluster db data matches.
AT_CHECK([diff standalonedump clusterdump])
AT_CLEANUP

0 comments on commit 00de46f

Please sign in to comment.