diff options
author | Andy Zhou <azhou@ovn.org> | 2017-02-06 14:00:22 -0800 |
---|---|---|
committer | Andy Zhou <azhou@ovn.org> | 2017-02-13 12:20:21 -0800 |
commit | 05ac209a5d3a5e85896f58d16b244e6b2a4cf2d0 (patch) | |
tree | 43e034cba1613025b409865894666e32cb59f00f | |
parent | 64fada26016c0d7ad8376d11c2cf653616d0db21 (diff) | |
download | openvswitch-05ac209a5d3a5e85896f58d16b244e6b2a4cf2d0.tar.gz |
ovsdb: Prevent OVSDB server from replicating itself.
Replication OVSDB server from itself is usually caused by configuration
errors. Such configuration errors can lead to OVSDB server data loss.
See "reported-at" for more details.
This patch adds logics that prevent OVSDB server from replicating
itself.
Reported-by: Guishuai Li <ligs@dtdream.com>
Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2017-January/326963.html
Suggested-by: Ben Pfaff <blp@ovn.org>
Signed-off-by: Andy Zhou <azhou@ovn.org>
Acked-by: Ben Pfaff <blp@ovn.org>
-rw-r--r-- | ovsdb/jsonrpc-server.c | 7 | ||||
-rw-r--r-- | ovsdb/ovsdb-server.1.in | 30 | ||||
-rw-r--r-- | ovsdb/ovsdb-server.c | 24 | ||||
-rw-r--r-- | ovsdb/replication.c | 59 | ||||
-rw-r--r-- | ovsdb/replication.h | 3 | ||||
-rw-r--r-- | tests/ovsdb-server.at | 32 |
6 files changed, 138 insertions, 17 deletions
diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c index 5353ba743..1ba6bb390 100644 --- a/ovsdb/jsonrpc-server.c +++ b/ovsdb/jsonrpc-server.c @@ -944,6 +944,13 @@ ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s, } reply = jsonrpc_create_reply(json_array_create(dbs, n_dbs), request->id); + } else if (!strcmp(request->method, "get_server_id")) { + const struct uuid *uuid = &s->up.server->uuid; + struct json *result; + + result = json_string_create_nocopy(xasprintf(UUID_FMT, + UUID_ARGS(uuid))); + reply = jsonrpc_create_reply(result, request->id); } else if (!strcmp(request->method, "lock")) { reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_WAIT); } else if (!strcmp(request->method, "steal")) { diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in index a55af399a..3c798dd17 100644 --- a/ovsdb/ovsdb-server.1.in +++ b/ovsdb/ovsdb-server.1.in @@ -577,6 +577,36 @@ Initial views of rows are not presented in update2 notifications, but in the response object to the monitor_cond request. The formatting of the <table-updates2> object, however, is the same in either case. . +.IP "4.1.15. Get Server ID" +A new RPC method added in Open vSwitch version 2.7. The request contains +the following members: +. +.PP +.RS +.nf +"method": "get_server_id" +"params": null +"id": <nonnull-json-value> +.fi +.RE +. +.IP +The response object contains the following members: +. +.PP +.RS +.nf +"result": "<server_id>" +"error": null +"id": same "id" as request +.fi +.RE +. +.IP +<server_id> is JSON string that contains a UUID that uniquely identifies +the running OVSDB server process. A fresh UUID is generated when the +process restarts. +. .IP "5.1. Notation" For <condition>, RFC 7047 only allows the use of \fB!=\fR, \fB==\fR, \fBincludes\fR, and \fBexcludes\fR operators with set types. Open diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c index 9b669c90a..52af4d36a 100644 --- a/ovsdb/ovsdb-server.c +++ b/ovsdb/ovsdb-server.c @@ -139,9 +139,9 @@ static void load_config(FILE *config_file, struct sset *remotes, static void ovsdb_replication_init(const char *sync_from, const char *exclude, - struct shash *all_dbs) + struct shash *all_dbs, const struct uuid *server_uuid) { - replication_init(sync_from, exclude); + replication_init(sync_from, exclude, server_uuid); struct shash_node *node; SHASH_FOR_EACH (node, all_dbs) { struct db *db = node->data; @@ -425,7 +425,9 @@ main(int argc, char *argv[]) ovsdb_server_disable_monitor_cond, jsonrpc); if (is_backup) { - ovsdb_replication_init(sync_from, sync_exclude, &all_dbs); + const struct uuid *server_uuid; + server_uuid = ovsdb_jsonrpc_server_get_uuid(jsonrpc); + ovsdb_replication_init(sync_from, sync_exclude, &all_dbs, server_uuid); } main_loop(jsonrpc, &all_dbs, unixctl, &remotes, run_process, &exiting, @@ -1185,8 +1187,10 @@ ovsdb_server_connect_active_ovsdb_server(struct unixctl_conn *conn, if ( !*config->sync_from) { msg = "Unable to connect: active server is not specified.\n"; } else { + const struct uuid *server_uuid; + server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc); ovsdb_replication_init(*config->sync_from, *config->sync_exclude, - config->all_dbs); + config->all_dbs, server_uuid); if (!*config->is_backup) { *config->is_backup = true; save_config(config); @@ -1223,8 +1227,10 @@ ovsdb_server_set_sync_exclude_tables(struct unixctl_conn *conn, *config->sync_exclude = xstrdup(argv[1]); save_config(config); if (*config->is_backup) { + const struct uuid *server_uuid; + server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc); ovsdb_replication_init(*config->sync_from, *config->sync_exclude, - config->all_dbs); + config->all_dbs, server_uuid); } err = set_blacklist_tables(argv[1], false); } @@ -1430,8 +1436,10 @@ ovsdb_server_add_database(struct unixctl_conn *conn, int argc OVS_UNUSED, if (!error) { save_config(config); if (*config->is_backup) { + const struct uuid *server_uuid; + server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc); ovsdb_replication_init(*config->sync_from, *config->sync_exclude, - config->all_dbs); + config->all_dbs, server_uuid); } unixctl_command_reply(conn, NULL); } else { @@ -1464,8 +1472,10 @@ ovsdb_server_remove_database(struct unixctl_conn *conn, int argc OVS_UNUSED, save_config(config); if (*config->is_backup) { + const struct uuid *server_uuid; + server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc); ovsdb_replication_init(*config->sync_from, *config->sync_exclude, - config->all_dbs); + config->all_dbs, server_uuid); } unixctl_command_reply(conn, NULL); } diff --git a/ovsdb/replication.c b/ovsdb/replication.c index 1c77b1876..b896adc41 100644 --- a/ovsdb/replication.c +++ b/ovsdb/replication.c @@ -1,6 +1,6 @@ /* * (c) Copyright 2016 Hewlett Packard Enterprise Development LP - * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc. + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +34,12 @@ #include "svec.h" #include "table.h" #include "transaction.h" +#include "uuid.h" VLOG_DEFINE_THIS_MODULE(replication); static char *sync_from; +static struct uuid server_uuid; static struct jsonrpc_session *session; static unsigned int session_seqno = UINT_MAX; @@ -88,6 +90,7 @@ void request_ids_clear(void); enum ovsdb_replication_state { RPL_S_INIT, + RPL_S_SERVER_ID_REQUESTED, RPL_S_DB_REQUESTED, RPL_S_SCHEMA_REQUESTED, RPL_S_MONITOR_REQUESTED, @@ -110,7 +113,8 @@ static struct ovsdb* find_db(const char *db_name); void -replication_init(const char *sync_from_, const char *exclude_tables) +replication_init(const char *sync_from_, const char *exclude_tables, + const struct uuid *server) { free(sync_from); sync_from = xstrdup(sync_from_); @@ -128,6 +132,10 @@ replication_init(const char *sync_from_, const char *exclude_tables) session = jsonrpc_session_open(sync_from, true); session_seqno = UINT_MAX; + + /* Keep a copy of local server uuid. */ + server_uuid = *server; + state = RPL_S_INIT; } @@ -155,16 +163,13 @@ replication_run(void) session_seqno = seqno; request_ids_clear(); struct jsonrpc_msg *request; - request = jsonrpc_create_request("list_dbs", + request = jsonrpc_create_request("get_server_id", json_array_create_empty(), NULL); request_ids_add(request->id, NULL); jsonrpc_session_send(session, request); - replication_dbs_destroy(); - replication_dbs = replication_db_clone(&local_dbs); - - state = RPL_S_DB_REQUESTED; - VLOG_DBG("Send list_dbs request"); + state = RPL_S_SERVER_ID_REQUESTED; + VLOG_DBG("send server ID request."); } msg = jsonrpc_session_recv(session); @@ -197,10 +202,45 @@ replication_run(void) } switch (state) { + case RPL_S_SERVER_ID_REQUESTED: { + struct uuid uuid; + if (msg->result->type != JSON_STRING || + !uuid_from_string(&uuid, json_string(msg->result))) { + struct ovsdb_error *error; + error = ovsdb_error("get_server_id failed", + "Server ID is not valid UUID"); + + ovsdb_error_assert(error); + state = RPL_S_ERR; + break; + } + + if (uuid_equals(&uuid, &server_uuid)) { + struct ovsdb_error *error; + error = ovsdb_error("Server ID check failed", + "Self replicating is not allowed"); + + ovsdb_error_assert(error); + state = RPL_S_ERR; + break; + } + + struct jsonrpc_msg *request; + request = jsonrpc_create_request("list_dbs", + json_array_create_empty(), + NULL); + request_ids_add(request->id, NULL); + jsonrpc_session_send(session, request); + + replication_dbs_destroy(); + replication_dbs = replication_db_clone(&local_dbs); + state = RPL_S_DB_REQUESTED; + break; + } case RPL_S_DB_REQUESTED: if (msg->result->type != JSON_ARRAY) { struct ovsdb_error *error; - error = ovsdb_error("list-dbs failed", + error = ovsdb_error("list_dbs failed", "list_dbs response is not array"); ovsdb_error_assert(error); state = RPL_S_ERR; @@ -802,6 +842,7 @@ replication_status(void) if (alive) { switch(state) { case RPL_S_INIT: + case RPL_S_SERVER_ID_REQUESTED: case RPL_S_DB_REQUESTED: case RPL_S_SCHEMA_REQUESTED: case RPL_S_MONITOR_REQUESTED: diff --git a/ovsdb/replication.h b/ovsdb/replication.h index 622d35541..1f9c32fa7 100644 --- a/ovsdb/replication.h +++ b/ovsdb/replication.h @@ -44,7 +44,8 @@ struct ovsdb; * used mainly by uinxctl commands. */ -void replication_init(const char *sync_from, const char *exclude_tables); +void replication_init(const char *sync_from, const char *exclude_tables, + const struct uuid *server); void replication_run(void); void replication_wait(void); void replication_destroy(void); diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at index 855c23713..e72a65b48 100644 --- a/tests/ovsdb-server.at +++ b/tests/ovsdb-server.at @@ -1440,6 +1440,38 @@ dnl OVSDB_SERVER_SHUTDOWN dnl OVSDB_SERVER_SHUTDOWN2 AT_CLEANUP +#ovsdb-server prevent self replicating +AT_SETUP([ovsdb-server prevent self replicating]) +AT_KEYWORDS([ovsdb server replication]) +replication_schema > schema +AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) + +dnl Add some data to both DBs +AT_CHECK([ovsdb-tool transact db \ +'[["mydb", + {"op": "insert", + "table": "a", + "row": {"number": 9, "name": "nine"}}]]'], [0], [ignore], [ignore]) + +dnl Start 'db', then try to be a back up server of itself. +AT_CHECK([ovsdb-server --detach --no-chdir --log-file=ovsdb-server.log --pidfile="`pwd`"/pid --remote=punix:db.sock --unixctl="`pwd`"/unixctl db --sync-from=unix:db.sock --active ], [0], [ignore], [ignore]) +on_exit 'test ! -e pid || kill `cat pid`' + +dnl Save the current content +AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout]) +cp stdout dump1 + +AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/connect-active-ovsdb-server]) +dnl Check that self replicating is blocked. +AT_CHECK([grep "Self replicating is not allowed" ovsdb-server.log], [0], [stdout]) + +dnl Check current DB content is preserved. +AT_CHECK([ovsdb-client dump unix:db.sock], [0], [stdout]) +cat stdout > dump2 + +AT_CHECK([diff dump1 dump2]) +AT_CLEANUP + AT_SETUP([ovsdb-server/read-only db:ptcp connection]) AT_KEYWORDS([ovsdb server read-only]) AT_DATA([schema], |