2016-06-24 17:13:06 -07:00
|
|
|
|
/*
|
2017-12-31 21:15:58 -08:00
|
|
|
|
* (c) Copyright 2016, 2017 Hewlett Packard Enterprise Development LP
|
2017-02-06 14:00:22 -08:00
|
|
|
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Nicira, Inc.
|
2016-06-24 17:13:06 -07:00
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "condition.h"
|
2016-08-17 13:56:02 -07:00
|
|
|
|
#include "jsonrpc.h"
|
2016-08-16 14:56:19 -07:00
|
|
|
|
#include "openvswitch/dynamic-string.h"
|
2016-08-17 13:56:02 -07:00
|
|
|
|
#include "openvswitch/hmap.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "openvswitch/json.h"
|
2016-08-16 14:13:35 -07:00
|
|
|
|
#include "openvswitch/vlog.h"
|
2016-06-24 17:13:06 -07:00
|
|
|
|
#include "ovsdb-error.h"
|
2016-08-17 13:56:02 -07:00
|
|
|
|
#include "ovsdb.h"
|
2016-06-24 17:13:06 -07:00
|
|
|
|
#include "query.h"
|
2016-08-17 13:56:02 -07:00
|
|
|
|
#include "replication.h"
|
2016-06-24 17:13:06 -07:00
|
|
|
|
#include "row.h"
|
|
|
|
|
#include "sset.h"
|
2016-08-17 13:56:02 -07:00
|
|
|
|
#include "stream.h"
|
2016-06-24 17:13:06 -07:00
|
|
|
|
#include "svec.h"
|
|
|
|
|
#include "table.h"
|
|
|
|
|
#include "transaction.h"
|
2017-02-06 14:00:22 -08:00
|
|
|
|
#include "uuid.h"
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
2016-08-16 14:13:35 -07:00
|
|
|
|
VLOG_DEFINE_THIS_MODULE(replication);
|
|
|
|
|
|
2017-02-06 14:00:22 -08:00
|
|
|
|
static struct uuid server_uuid;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
2025-06-24 21:54:33 +02:00
|
|
|
|
static struct ovsdb_error *process_notification(const struct json *,
|
|
|
|
|
struct ovsdb *);
|
|
|
|
|
static struct ovsdb_error *process_table_update(
|
|
|
|
|
const struct json *table_update, const char *table_name,
|
|
|
|
|
struct ovsdb *database, struct ovsdb_txn *txn);
|
2016-08-16 14:56:19 -07:00
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
enum ovsdb_replication_state {
|
2016-08-23 04:05:11 -07:00
|
|
|
|
RPL_S_INIT,
|
2017-02-06 14:00:22 -08:00
|
|
|
|
RPL_S_SERVER_ID_REQUESTED,
|
2016-08-23 13:57:37 -07:00
|
|
|
|
RPL_S_DB_REQUESTED,
|
|
|
|
|
RPL_S_SCHEMA_REQUESTED,
|
|
|
|
|
RPL_S_MONITOR_REQUESTED,
|
|
|
|
|
RPL_S_REPLICATING,
|
|
|
|
|
RPL_S_ERR /* Error, no longer replicating. */
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-21 22:26:51 +05:30
|
|
|
|
struct replication_db {
|
|
|
|
|
struct ovsdb *db;
|
2024-01-09 23:49:05 +01:00
|
|
|
|
|
2019-10-21 22:26:51 +05:30
|
|
|
|
bool schema_version_higher;
|
|
|
|
|
/* Points to the schema received from the active server if
|
|
|
|
|
* the local db schema version is higher. NULL otherwise. */
|
|
|
|
|
struct ovsdb_schema *active_db_schema;
|
2024-01-09 23:49:05 +01:00
|
|
|
|
|
|
|
|
|
char *sync_from;
|
|
|
|
|
char *excluded_tables_str;
|
|
|
|
|
struct sset excluded_tables;
|
|
|
|
|
|
|
|
|
|
struct json *request_id; /* Id of the outstanding OVSDB request. */
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_session *session;
|
|
|
|
|
unsigned int session_seqno;
|
|
|
|
|
|
|
|
|
|
enum ovsdb_replication_state state;
|
2019-10-21 22:26:51 +05:30
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool is_replication_possible(struct ovsdb_schema *local_db_schema,
|
|
|
|
|
struct ovsdb_schema *active_db_schema);
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
static struct jsonrpc_msg *create_monitor_request(struct replication_db *,
|
|
|
|
|
struct ovsdb_schema *);
|
|
|
|
|
static void add_monitored_table(struct ovsdb_table_schema *table,
|
|
|
|
|
struct json *monitor_requests);
|
|
|
|
|
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
/* All DBs known to ovsdb-server. The actual replication dbs are stored
|
|
|
|
|
* in 'replication dbs', which is a subset of all dbs and remote dbs whose
|
|
|
|
|
* schema matches. */
|
2024-01-09 23:49:05 +01:00
|
|
|
|
static struct shash replication_dbs = SHASH_INITIALIZER(&replication_dbs);
|
|
|
|
|
|
|
|
|
|
static void replication_db_destroy(struct replication_db *);
|
|
|
|
|
static struct ovsdb_error *reset_database(struct replication_db *);
|
2016-08-17 13:56:02 -07:00
|
|
|
|
|
2016-08-18 17:20:08 -07:00
|
|
|
|
/* Find 'struct ovsdb' by name within 'replication_dbs' */
|
2019-10-21 22:26:51 +05:30
|
|
|
|
static struct replication_db *find_db(const char *db_name);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
|
|
|
|
|
static char *set_excluded_tables(struct replication_db *, const char *excluded)
|
|
|
|
|
OVS_WARN_UNUSED_RESULT;
|
|
|
|
|
|
|
|
|
|
static void request_id_set(struct replication_db *, const struct json *id);
|
|
|
|
|
static void request_id_clear(struct replication_db *);
|
|
|
|
|
static bool request_id_compare_and_free(struct replication_db *,
|
|
|
|
|
const struct json *id);
|
2016-08-18 17:20:08 -07:00
|
|
|
|
|
|
|
|
|
|
2016-03-29 11:01:00 -06:00
|
|
|
|
void
|
2024-01-09 23:49:05 +01:00
|
|
|
|
replication_set_db(struct ovsdb *db, const char *sync_from,
|
|
|
|
|
const char *exclude_tables, const struct uuid *server,
|
2024-01-09 23:49:12 +01:00
|
|
|
|
const struct jsonrpc_session_options *options)
|
2016-03-29 11:01:00 -06:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
struct replication_db *rdb = find_db(db->name);
|
2016-08-23 04:05:11 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (uuid_is_zero(&server_uuid)) {
|
|
|
|
|
/* Keep a copy of local server uuid. */
|
|
|
|
|
server_uuid = *server;
|
|
|
|
|
} else {
|
|
|
|
|
ovs_assert(uuid_equals(&server_uuid, server));
|
|
|
|
|
}
|
2016-08-23 13:57:37 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
ovs_assert(sync_from);
|
|
|
|
|
|
|
|
|
|
if (rdb
|
|
|
|
|
&& nullable_string_is_equal(rdb->excluded_tables_str, exclude_tables)
|
|
|
|
|
&& nullable_string_is_equal(rdb->sync_from, sync_from)) {
|
2024-01-09 23:49:12 +01:00
|
|
|
|
jsonrpc_session_set_options(rdb->session, options);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
return;
|
2016-08-16 14:56:19 -07:00
|
|
|
|
}
|
2016-08-23 13:57:37 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!rdb) {
|
|
|
|
|
rdb = xzalloc(sizeof *rdb);
|
|
|
|
|
rdb->db = db;
|
|
|
|
|
sset_init(&rdb->excluded_tables);
|
|
|
|
|
rdb->schema_version_higher = false;
|
|
|
|
|
shash_add(&replication_dbs, db->name, rdb);
|
|
|
|
|
} else {
|
|
|
|
|
replication_db_destroy(rdb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rdb->sync_from = xstrdup(sync_from);
|
|
|
|
|
rdb->excluded_tables_str = nullable_xstrdup(exclude_tables);
|
|
|
|
|
/* Caller should have verified that the 'exclude_tables' is
|
|
|
|
|
* parseable. An error here is unexpected. */
|
|
|
|
|
ovs_assert(!set_excluded_tables(rdb, exclude_tables));
|
2017-02-06 14:00:22 -08:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->session = jsonrpc_session_open(rdb->sync_from, true);
|
|
|
|
|
rdb->session_seqno = UINT_MAX;
|
2020-01-07 10:24:48 +05:30
|
|
|
|
|
2024-01-09 23:49:12 +01:00
|
|
|
|
jsonrpc_session_set_options(rdb->session, options);
|
2017-02-06 14:00:22 -08:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_INIT;
|
2024-01-09 23:49:06 +01:00
|
|
|
|
rdb->db->read_only = true;
|
2016-03-29 11:01:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-24 17:13:06 -07:00
|
|
|
|
void
|
2024-01-09 23:49:05 +01:00
|
|
|
|
replication_remove_db(const struct ovsdb *db)
|
2016-08-18 17:20:08 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
struct replication_db *rdb;
|
|
|
|
|
|
|
|
|
|
rdb = shash_find_and_delete(&replication_dbs, db->name);
|
|
|
|
|
if (rdb) {
|
|
|
|
|
replication_db_destroy(rdb);
|
|
|
|
|
free(rdb);
|
|
|
|
|
}
|
2016-08-18 17:20:08 -07:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-02 15:03:06 -07:00
|
|
|
|
static void
|
2024-01-09 23:49:05 +01:00
|
|
|
|
send_schema_request(struct replication_db *rdb)
|
2017-08-02 15:03:06 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
struct jsonrpc_msg *request =
|
|
|
|
|
jsonrpc_create_request(
|
|
|
|
|
"get_schema",
|
|
|
|
|
json_array_create_1(json_string_create(rdb->db->name)),
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
request_id_set(rdb, request->id);
|
|
|
|
|
jsonrpc_session_send(rdb->session, request);
|
2017-08-02 15:03:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
static void
|
|
|
|
|
replication_run_db(struct replication_db *rdb)
|
2016-06-24 17:13:06 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!rdb->session) {
|
2016-08-23 13:57:37 -07:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
jsonrpc_session_run(rdb->session);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
for (int i = 0; i < 50; i++) {
|
2016-08-23 13:57:37 -07:00
|
|
|
|
struct jsonrpc_msg *msg;
|
|
|
|
|
unsigned int seqno;
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!jsonrpc_session_is_connected(rdb->session)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seqno = jsonrpc_session_get_seqno(rdb->session);
|
|
|
|
|
if (seqno != rdb->session_seqno || rdb->state == RPL_S_INIT) {
|
|
|
|
|
rdb->session_seqno = seqno;
|
|
|
|
|
request_id_clear(rdb);
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
struct jsonrpc_msg *request;
|
2017-02-06 14:00:22 -08:00
|
|
|
|
request = jsonrpc_create_request("get_server_id",
|
2016-08-23 13:57:37 -07:00
|
|
|
|
json_array_create_empty(), NULL);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
request_id_set(rdb, request->id);
|
|
|
|
|
jsonrpc_session_send(rdb->session, request);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_SERVER_ID_REQUESTED;
|
|
|
|
|
VLOG_DBG("%s: send server ID request.", rdb->db->name);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
msg = jsonrpc_session_recv(rdb->session);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
if (!msg) {
|
|
|
|
|
continue;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (msg->type == JSONRPC_NOTIFY && rdb->state != RPL_S_ERR
|
2016-08-23 13:57:37 -07:00
|
|
|
|
&& !strcmp(msg->method, "update")) {
|
|
|
|
|
if (msg->params->type == JSON_ARRAY
|
2025-06-24 21:54:33 +02:00
|
|
|
|
&& json_array_size(msg->params) == 2
|
|
|
|
|
&& json_array_at(msg->params, 0)->type == JSON_STRING) {
|
|
|
|
|
const char *db_name = json_string(
|
|
|
|
|
json_array_at(msg->params, 0));
|
2024-01-09 23:49:05 +01:00
|
|
|
|
|
|
|
|
|
if (!strcmp(db_name, rdb->db->name)) {
|
2016-08-23 13:57:37 -07:00
|
|
|
|
struct ovsdb_error *error;
|
2025-06-24 21:54:33 +02:00
|
|
|
|
error = process_notification(json_array_at(msg->params, 1),
|
2019-10-21 22:26:51 +05:30
|
|
|
|
rdb->db);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovsdb_error_assert(error);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_ERR;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
2024-01-09 23:49:05 +01:00
|
|
|
|
} else {
|
|
|
|
|
VLOG_WARN("%s: received update for unexpected database %s",
|
|
|
|
|
rdb->db->name, db_name);
|
|
|
|
|
rdb->state = RPL_S_ERR;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (msg->type == JSONRPC_REPLY) {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!request_id_compare_and_free(rdb, msg->id)) {
|
|
|
|
|
VLOG_WARN("%s: received unexpected reply.", rdb->db->name);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
goto next;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
switch (rdb->state) {
|
2017-02-06 14:00:22 -08:00
|
|
|
|
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",
|
2024-01-09 23:49:05 +01:00
|
|
|
|
"%s: Server ID is not valid UUID",
|
|
|
|
|
rdb->db->name);
|
2017-02-06 14:00:22 -08:00
|
|
|
|
|
|
|
|
|
ovsdb_error_assert(error);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_ERR;
|
2017-02-06 14:00:22 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (uuid_equals(&uuid, &server_uuid)) {
|
|
|
|
|
struct ovsdb_error *error;
|
|
|
|
|
error = ovsdb_error("Server ID check failed",
|
2024-01-09 23:49:05 +01:00
|
|
|
|
"%s: Self replicating is not allowed",
|
|
|
|
|
rdb->db->name);
|
2017-02-06 14:00:22 -08:00
|
|
|
|
|
|
|
|
|
ovsdb_error_assert(error);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_ERR;
|
2017-02-06 14:00:22 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct jsonrpc_msg *request;
|
|
|
|
|
request = jsonrpc_create_request("list_dbs",
|
|
|
|
|
json_array_create_empty(),
|
|
|
|
|
NULL);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
request_id_set(rdb, request->id);
|
|
|
|
|
jsonrpc_session_send(rdb->session, request);
|
2017-02-06 14:00:22 -08:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_DB_REQUESTED;
|
2017-02-06 14:00:22 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-08-23 13:57:37 -07:00
|
|
|
|
case RPL_S_DB_REQUESTED:
|
|
|
|
|
if (msg->result->type != JSON_ARRAY) {
|
|
|
|
|
struct ovsdb_error *error;
|
2017-02-06 14:00:22 -08:00
|
|
|
|
error = ovsdb_error("list_dbs failed",
|
2024-01-09 23:49:05 +01:00
|
|
|
|
"%s: list_dbs response is not array",
|
|
|
|
|
rdb->db->name);
|
|
|
|
|
ovsdb_error_assert(error);
|
|
|
|
|
rdb->state = RPL_S_ERR;
|
|
|
|
|
} else if (!json_array_contains_string(msg->result,
|
|
|
|
|
rdb->db->name)) {
|
|
|
|
|
struct ovsdb_error *error;
|
|
|
|
|
error = ovsdb_error("list_dbs failed",
|
|
|
|
|
"%s: database name is not in the list",
|
|
|
|
|
rdb->db->name);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
ovsdb_error_assert(error);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_ERR;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
} else {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
send_schema_request(rdb);
|
|
|
|
|
VLOG_DBG("%s: send schema request.", rdb->db->name);
|
|
|
|
|
rdb->state = RPL_S_SCHEMA_REQUESTED;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RPL_S_SCHEMA_REQUESTED: {
|
|
|
|
|
struct ovsdb_schema *schema;
|
|
|
|
|
struct ovsdb_error *error;
|
|
|
|
|
|
|
|
|
|
error = ovsdb_schema_from_json(msg->result, &schema);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovsdb_error_assert(error);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_ERR;
|
|
|
|
|
break;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (strcmp(rdb->db->name, schema->name)) {
|
2016-08-23 13:57:37 -07:00
|
|
|
|
/* Unexpected schema. */
|
2024-01-09 23:49:05 +01:00
|
|
|
|
VLOG_WARN("%s: unexpected schema %s.",
|
|
|
|
|
rdb->db->name, schema->name);
|
|
|
|
|
rdb->state = RPL_S_ERR;
|
|
|
|
|
ovsdb_schema_destroy(schema);
|
|
|
|
|
break;
|
2019-10-21 22:26:51 +05:30
|
|
|
|
} else if (!ovsdb_schema_equal(schema, rdb->db->schema)) {
|
2016-08-23 13:57:37 -07:00
|
|
|
|
/* Schmea version mismatch. */
|
2024-01-09 23:49:05 +01:00
|
|
|
|
VLOG_INFO("%s: Schema version mismatch, checking if %s can"
|
|
|
|
|
" still be replicated or not.",
|
|
|
|
|
rdb->db->name, schema->name);
|
2019-10-21 22:26:51 +05:30
|
|
|
|
if (is_replication_possible(rdb->db->schema, schema)) {
|
|
|
|
|
VLOG_INFO("%s can be replicated.", schema->name);
|
|
|
|
|
rdb->schema_version_higher = true;
|
|
|
|
|
if (rdb->active_db_schema) {
|
|
|
|
|
ovsdb_schema_destroy(rdb->active_db_schema);
|
|
|
|
|
}
|
|
|
|
|
rdb->active_db_schema = schema;
|
|
|
|
|
} else {
|
|
|
|
|
VLOG_INFO("%s cannot be replicated.", schema->name);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_ERR;
|
2019-10-21 22:26:51 +05:30
|
|
|
|
ovsdb_schema_destroy(schema);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
break;
|
2019-10-21 22:26:51 +05:30
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ovsdb_schema_destroy(schema);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
/* Send out a monitor request. */
|
|
|
|
|
struct jsonrpc_msg *request =
|
|
|
|
|
create_monitor_request(rdb, rdb->schema_version_higher
|
|
|
|
|
? rdb->active_db_schema
|
|
|
|
|
: rdb->db->schema);
|
|
|
|
|
|
|
|
|
|
request_id_set(rdb, request->id);
|
|
|
|
|
jsonrpc_session_send(rdb->session, request);
|
|
|
|
|
VLOG_DBG("%s: send monitor request.", rdb->db->name);
|
|
|
|
|
rdb->state = RPL_S_MONITOR_REQUESTED;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case RPL_S_MONITOR_REQUESTED: {
|
|
|
|
|
/* Reply to monitor requests. */
|
|
|
|
|
struct ovsdb_error *error;
|
2024-01-09 23:49:05 +01:00
|
|
|
|
VLOG_INFO("%s: Monitor reply received. "
|
|
|
|
|
"Resetting the database.", rdb->db->name);
|
2018-09-11 22:59:58 +05:30
|
|
|
|
/* Resetting the database here has few risks. If the
|
|
|
|
|
* process_notification() fails, the database is completely
|
|
|
|
|
* lost locally. In case that node becomes active, then
|
|
|
|
|
* there is a chance of complete data loss in the active/standy
|
|
|
|
|
* cluster. */
|
2024-01-09 23:49:05 +01:00
|
|
|
|
error = reset_database(rdb);
|
2018-09-11 22:59:58 +05:30
|
|
|
|
if (!error) {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
error = process_notification(msg->result, rdb->db);
|
2018-09-11 22:59:58 +05:30
|
|
|
|
}
|
2016-08-23 13:57:37 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovsdb_error_assert(error);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->state = RPL_S_ERR;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
} else {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
VLOG_DBG("%s: Listening to monitor updates.",
|
|
|
|
|
rdb->db->name);
|
|
|
|
|
rdb->state = RPL_S_REPLICATING;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case RPL_S_ERR:
|
|
|
|
|
/* Ignore all messages */
|
|
|
|
|
break;
|
|
|
|
|
|
2016-08-23 04:05:11 -07:00
|
|
|
|
case RPL_S_INIT:
|
2016-08-23 13:57:37 -07:00
|
|
|
|
case RPL_S_REPLICATING:
|
|
|
|
|
default:
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
next:
|
|
|
|
|
jsonrpc_msg_destroy(msg);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
void
|
|
|
|
|
replication_run(void)
|
|
|
|
|
{
|
|
|
|
|
struct shash_node *node;
|
|
|
|
|
|
|
|
|
|
SHASH_FOR_EACH (node, &replication_dbs) {
|
|
|
|
|
replication_run_db(node->data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-28 15:48:28 -07:00
|
|
|
|
void
|
|
|
|
|
replication_wait(void)
|
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
struct shash_node *node;
|
|
|
|
|
|
|
|
|
|
SHASH_FOR_EACH (node, &replication_dbs) {
|
|
|
|
|
struct replication_db *rdb = node->data;
|
|
|
|
|
|
|
|
|
|
if (rdb->session) {
|
|
|
|
|
jsonrpc_session_wait(rdb->session);
|
|
|
|
|
jsonrpc_session_recv_wait(rdb->session);
|
|
|
|
|
}
|
2016-07-28 15:48:28 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
/* Parse 'excluded' to rebuild 'rdb->excluded_tables'. If 'rdb' is not NULL,
|
|
|
|
|
* the current set of excluded tables will be wiped out, regardless of whether
|
|
|
|
|
* 'excluded' can be parsed. If 'rdb' is NULL, only parses 'excluded' and
|
2020-06-17 14:22:47 -07:00
|
|
|
|
* reports any errors, without modifying the list of exclusions.
|
2016-08-16 14:56:19 -07:00
|
|
|
|
*
|
2024-01-09 23:49:05 +01:00
|
|
|
|
* On error, returns the error string, which the caller is responsible for
|
|
|
|
|
* freeing. Returns NULL otherwise. */
|
|
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
|
|
|
|
set_excluded_tables__(struct replication_db *rdb, const char *excluded)
|
2016-03-29 11:01:00 -06:00
|
|
|
|
{
|
2016-08-16 14:56:19 -07:00
|
|
|
|
struct sset set = SSET_INITIALIZER(&set);
|
|
|
|
|
char *err = NULL;
|
|
|
|
|
|
2020-06-17 14:22:47 -07:00
|
|
|
|
if (excluded) {
|
2016-08-16 14:56:19 -07:00
|
|
|
|
const char *longname;
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (rdb) {
|
|
|
|
|
/* Can only add to an empty set. */
|
|
|
|
|
sset_clear(&rdb->excluded_tables);
|
2016-08-16 14:56:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-17 14:22:47 -07:00
|
|
|
|
sset_from_delimited_string(&set, excluded, " ,");
|
2016-08-16 14:56:19 -07:00
|
|
|
|
SSET_FOR_EACH (longname, &set) {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (rdb && !strchr(longname, ':')) {
|
|
|
|
|
sset_add(&rdb->excluded_tables, longname);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-16 14:56:19 -07:00
|
|
|
|
char *database = xstrdup(longname), *table = NULL;
|
|
|
|
|
strtok_r(database, ":", &table);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (table && rdb && !strcmp(rdb->db->name, database)) {
|
|
|
|
|
sset_add(&rdb->excluded_tables, table);
|
2016-08-16 14:56:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(database);
|
|
|
|
|
if (!table) {
|
2020-06-17 14:22:47 -07:00
|
|
|
|
err = xasprintf("Can't parse excluded table: %s", longname);
|
2016-08-16 14:56:19 -07:00
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
sset_destroy(&set);
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (err && rdb) {
|
2020-06-17 14:22:47 -07:00
|
|
|
|
/* On error, destroy the partially built 'excluded_tables'. */
|
2024-01-09 23:49:05 +01:00
|
|
|
|
sset_clear(&rdb->excluded_tables);
|
2016-08-16 14:56:19 -07:00
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2024-01-09 23:49:05 +01:00
|
|
|
|
parse_excluded_tables(const char *excluded)
|
2016-08-16 14:56:19 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
return set_excluded_tables__(NULL, excluded);
|
2016-03-29 11:01:00 -06:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
|
|
|
|
set_excluded_tables(struct replication_db *rdb, const char *excluded)
|
2016-07-19 14:54:51 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
return set_excluded_tables__(rdb, excluded);
|
2016-08-16 14:56:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
|
|
|
get_excluded_tables(const struct ovsdb *db)
|
2016-08-16 14:56:19 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
const struct replication_db *rdb = find_db(db->name);
|
2016-08-16 14:56:19 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!rdb) {
|
|
|
|
|
return xstrdup("");
|
2016-08-16 14:56:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
struct sset set = SSET_INITIALIZER(&set);
|
|
|
|
|
const char *table;
|
|
|
|
|
char *result;
|
2016-08-16 14:56:19 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
SSET_FOR_EACH (table, &rdb->excluded_tables) {
|
|
|
|
|
sset_add_and_free(&set, xasprintf("%s:%s", rdb->db->name, table));
|
|
|
|
|
}
|
2016-07-19 14:54:51 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
result = sset_join(&set, ",", "");
|
|
|
|
|
sset_destroy(&set);
|
|
|
|
|
|
|
|
|
|
return result;
|
2016-06-28 15:14:53 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-08-16 14:56:19 -07:00
|
|
|
|
replication_destroy(void)
|
2016-06-24 17:13:06 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
struct shash_node *node;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
SHASH_FOR_EACH (node, &replication_dbs) {
|
|
|
|
|
replication_db_destroy(node->data);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
2024-01-09 23:49:05 +01:00
|
|
|
|
shash_destroy_free_data(&replication_dbs);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-21 22:26:51 +05:30
|
|
|
|
static struct replication_db *
|
2016-08-18 17:20:08 -07:00
|
|
|
|
find_db(const char *db_name)
|
2016-06-24 17:13:06 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
return shash_find_data(&replication_dbs, db_name);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ovsdb_error *
|
2024-01-09 23:49:05 +01:00
|
|
|
|
reset_database(struct replication_db *rdb)
|
2016-06-24 17:13:06 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
struct ovsdb_txn *txn = ovsdb_txn_create(rdb->db);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
struct shash_node *table_node;
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
SHASH_FOR_EACH (table_node, &rdb->db->tables) {
|
2020-06-17 14:22:47 -07:00
|
|
|
|
/* Delete all rows if the table is not excluded. */
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!sset_contains(&rdb->excluded_tables, table_node->name)) {
|
2016-08-16 14:56:19 -07:00
|
|
|
|
struct ovsdb_table *table = table_node->data;
|
2022-03-23 12:56:17 +01:00
|
|
|
|
struct ovsdb_row *row;
|
|
|
|
|
HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) {
|
2016-03-29 11:01:00 -06:00
|
|
|
|
ovsdb_txn_row_delete(txn, row);
|
|
|
|
|
}
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-31 21:15:58 -08:00
|
|
|
|
return ovsdb_txn_propose_commit_block(txn, false);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
/* Create a monitor request for 'db'. The monitor request will include
|
2020-06-17 14:22:47 -07:00
|
|
|
|
* any tables from 'excluded_tables'
|
2016-08-23 13:57:37 -07:00
|
|
|
|
*
|
|
|
|
|
* Caller is responsible for disposing 'request'.
|
|
|
|
|
*/
|
|
|
|
|
static struct jsonrpc_msg *
|
2024-01-09 23:49:05 +01:00
|
|
|
|
create_monitor_request(struct replication_db *rdb, struct ovsdb_schema *schema)
|
2016-06-24 17:13:06 -07:00
|
|
|
|
{
|
2016-08-23 13:57:37 -07:00
|
|
|
|
struct jsonrpc_msg *request;
|
|
|
|
|
struct json *monitor;
|
|
|
|
|
const char *db_name = schema->name;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
struct json *monitor_request = json_object_create();
|
|
|
|
|
size_t n = shash_count(&schema->tables);
|
|
|
|
|
const struct shash_node **nodes = shash_sort(&schema->tables);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
for (int j = 0; j < n; j++) {
|
|
|
|
|
struct ovsdb_table_schema *table = nodes[j]->data;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
2020-06-17 14:22:47 -07:00
|
|
|
|
/* Monitor all tables not excluded. */
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!sset_contains(&rdb->excluded_tables, table->name)) {
|
2016-08-23 13:57:37 -07:00
|
|
|
|
add_monitored_table(table, monitor_request);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-23 13:57:37 -07:00
|
|
|
|
free(nodes);
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
/* Create a monitor request. */
|
|
|
|
|
monitor = json_array_create_3(
|
|
|
|
|
json_string_create(db_name),
|
|
|
|
|
json_string_create(db_name),
|
|
|
|
|
monitor_request);
|
|
|
|
|
request = jsonrpc_create_request("monitor", monitor, NULL);
|
2016-07-25 19:23:02 -07:00
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
return request;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_monitored_table(struct ovsdb_table_schema *table,
|
|
|
|
|
struct json *monitor_request)
|
|
|
|
|
{
|
|
|
|
|
struct json *monitor_request_array;
|
|
|
|
|
|
|
|
|
|
monitor_request_array = json_array_create_empty();
|
|
|
|
|
json_array_add(monitor_request_array, json_object_create());
|
|
|
|
|
|
|
|
|
|
json_object_put(monitor_request, table->name, monitor_request_array);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
|
|
|
|
|
static struct ovsdb_error *
|
2025-06-24 21:54:33 +02:00
|
|
|
|
process_notification(const struct json *table_updates, struct ovsdb *db)
|
2016-06-24 17:13:06 -07:00
|
|
|
|
{
|
2016-08-23 13:57:37 -07:00
|
|
|
|
struct ovsdb_error *error = NULL;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
struct ovsdb_txn *txn;
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
if (table_updates->type == JSON_OBJECT) {
|
|
|
|
|
txn = ovsdb_txn_create(db);
|
|
|
|
|
|
|
|
|
|
/* Process each table update. */
|
|
|
|
|
struct shash_node *node;
|
|
|
|
|
SHASH_FOR_EACH (node, json_object(table_updates)) {
|
|
|
|
|
struct json *table_update = node->data;
|
|
|
|
|
if (table_update) {
|
|
|
|
|
error = process_table_update(table_update, node->name, db, txn);
|
|
|
|
|
if (error) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovsdb_txn_abort(txn);
|
|
|
|
|
return error;
|
|
|
|
|
} else {
|
|
|
|
|
/* Commit transaction. */
|
2017-12-31 21:15:58 -08:00
|
|
|
|
error = ovsdb_txn_propose_commit_block(txn, false);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
return error;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ovsdb_error *
|
2025-06-24 21:54:33 +02:00
|
|
|
|
process_table_update(const struct json *table_update, const char *table_name,
|
2016-06-24 17:13:06 -07:00
|
|
|
|
struct ovsdb *database, struct ovsdb_txn *txn)
|
|
|
|
|
{
|
2016-09-10 21:23:22 -07:00
|
|
|
|
struct ovsdb_table *table = ovsdb_get_table(database, table_name);
|
|
|
|
|
if (!table) {
|
|
|
|
|
return ovsdb_error("unknown table", "unknown table %s", table_name);
|
|
|
|
|
}
|
2016-06-24 17:13:06 -07:00
|
|
|
|
|
|
|
|
|
if (table_update->type != JSON_OBJECT) {
|
2016-06-28 22:02:28 -07:00
|
|
|
|
return ovsdb_error("Not a JSON object",
|
|
|
|
|
"<table-update> for table is not object");
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-10 21:23:22 -07:00
|
|
|
|
struct shash_node *node;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
SHASH_FOR_EACH (node, json_object(table_update)) {
|
|
|
|
|
struct json *row_update = node->data;
|
|
|
|
|
struct json *old, *new;
|
|
|
|
|
|
|
|
|
|
if (row_update->type != JSON_OBJECT) {
|
2016-09-10 21:23:22 -07:00
|
|
|
|
return ovsdb_error("Not a JSON object",
|
|
|
|
|
"<row-update> is not object");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct uuid uuid;
|
|
|
|
|
if (!uuid_from_string(&uuid, node->name)) {
|
|
|
|
|
return ovsdb_syntax_error(table_update, "bad row UUID",
|
|
|
|
|
"<table-update> names must be UUIDs");
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
2016-09-10 21:23:22 -07:00
|
|
|
|
|
2016-06-24 17:13:06 -07:00
|
|
|
|
old = shash_find_data(json_object(row_update), "old");
|
|
|
|
|
new = shash_find_data(json_object(row_update), "new");
|
|
|
|
|
|
2016-09-10 21:23:22 -07:00
|
|
|
|
struct ovsdb_error *error;
|
2021-06-01 22:54:26 +02:00
|
|
|
|
error = (!new ? ovsdb_table_execute_delete(txn, &uuid, table)
|
|
|
|
|
: !old ? ovsdb_table_execute_insert(txn, &uuid, table, new)
|
2021-06-01 23:01:22 +02:00
|
|
|
|
: ovsdb_table_execute_update(txn, &uuid, table, new, false));
|
2016-06-28 15:14:53 -06:00
|
|
|
|
if (error) {
|
2021-06-01 22:54:26 +02:00
|
|
|
|
if (!strcmp(ovsdb_error_get_tag(error), "consistency violation")) {
|
|
|
|
|
ovsdb_error_assert(error);
|
|
|
|
|
error = NULL;
|
|
|
|
|
}
|
2016-09-10 21:23:22 -07:00
|
|
|
|
return error;
|
2016-06-28 15:14:53 -06:00
|
|
|
|
}
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
2016-09-10 21:23:22 -07:00
|
|
|
|
return NULL;
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
static void
|
|
|
|
|
request_id_set(struct replication_db *rdb, const struct json *id)
|
2016-08-17 13:56:02 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
ovs_assert(!rdb->request_id);
|
|
|
|
|
rdb->request_id = json_clone(id);
|
|
|
|
|
}
|
2016-08-17 13:56:02 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
static void
|
|
|
|
|
request_id_clear(struct replication_db *rdb)
|
|
|
|
|
{
|
|
|
|
|
json_destroy(rdb->request_id);
|
|
|
|
|
rdb->request_id = NULL;
|
2016-08-17 13:56:02 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
/* Compare 'id' with sent 'request_id'. If it matches, clear the current
|
|
|
|
|
* 'request_id'. If it doesn't match, 'request_id' does not change.
|
2016-08-17 13:56:02 -07:00
|
|
|
|
*
|
2024-01-09 23:49:05 +01:00
|
|
|
|
* Return true if 'id' matches, false otherwise.
|
2016-08-17 13:56:02 -07:00
|
|
|
|
*/
|
2024-01-09 23:49:05 +01:00
|
|
|
|
static bool
|
|
|
|
|
request_id_compare_and_free(struct replication_db *rdb, const struct json *id)
|
2016-08-17 13:56:02 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (rdb->request_id && json_equal(id, rdb->request_id)) {
|
|
|
|
|
request_id_clear(rdb);
|
|
|
|
|
return true;
|
2016-08-17 13:56:02 -07:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2024-01-09 23:49:05 +01:00
|
|
|
|
replication_db_destroy(struct replication_db *rdb)
|
2016-08-17 13:56:02 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!rdb) {
|
|
|
|
|
return;
|
2016-08-17 13:56:02 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
free(rdb->sync_from);
|
|
|
|
|
rdb->sync_from = NULL;
|
2016-08-17 13:56:02 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
free(rdb->excluded_tables_str);
|
|
|
|
|
rdb->excluded_tables_str = NULL;
|
|
|
|
|
sset_destroy(&rdb->excluded_tables);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
request_id_clear(rdb);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (rdb->session) {
|
|
|
|
|
jsonrpc_session_close(rdb->session);
|
|
|
|
|
rdb->session = NULL;
|
2019-10-21 22:26:51 +05:30
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (rdb->active_db_schema) {
|
|
|
|
|
ovsdb_schema_destroy(rdb->active_db_schema);
|
|
|
|
|
rdb->active_db_schema = NULL;
|
2019-10-21 22:26:51 +05:30
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
rdb->schema_version_higher = false;
|
2024-01-09 23:49:06 +01:00
|
|
|
|
rdb->db->read_only = false;
|
2016-09-20 14:12:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-23 13:57:37 -07:00
|
|
|
|
/* Return true if replication just started or is ongoing.
|
|
|
|
|
* Return false if the connection failed, or the replication
|
|
|
|
|
* was not able to start. */
|
|
|
|
|
bool
|
2024-01-09 23:49:05 +01:00
|
|
|
|
replication_is_alive(const struct ovsdb *db)
|
2016-08-23 13:57:37 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
const struct replication_db *rdb = find_db(db->name);
|
|
|
|
|
|
|
|
|
|
if (!rdb || !rdb->session) {
|
|
|
|
|
return false;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
2024-01-09 23:49:05 +01:00
|
|
|
|
return jsonrpc_session_is_alive(rdb->session) && rdb->state != RPL_S_ERR;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the last error reported on a connection by 'session'. The
|
|
|
|
|
* return value is 0 if replication is not currently running, or
|
|
|
|
|
* if replication session has not encountered any error.
|
|
|
|
|
*
|
|
|
|
|
* Return a negative value if replication session has error, or the
|
|
|
|
|
* replication was not able to start. */
|
|
|
|
|
int
|
2024-01-09 23:49:05 +01:00
|
|
|
|
replication_get_last_error(const struct ovsdb *db)
|
2016-08-23 13:57:37 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
const struct replication_db *rdb = find_db(db->name);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
int err = 0;
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (rdb && rdb->session) {
|
|
|
|
|
err = jsonrpc_session_get_last_error(rdb->session);
|
2016-08-23 13:57:37 -07:00
|
|
|
|
if (!err) {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
err = (rdb->state == RPL_S_ERR) ? ENOENT : 0;
|
2016-08-23 13:57:37 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
|
|
|
replication_status(const struct ovsdb *db)
|
2016-08-23 04:05:11 -07:00
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
const struct replication_db *rdb = find_db(db->name);
|
|
|
|
|
|
|
|
|
|
if (!rdb) {
|
|
|
|
|
return xasprintf("%s is not configured for replication", db->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool alive = rdb->session && jsonrpc_session_is_alive(rdb->session);
|
2016-08-23 04:05:11 -07:00
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
if (alive) {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
switch (rdb->state) {
|
2016-08-23 04:05:11 -07:00
|
|
|
|
case RPL_S_INIT:
|
2017-02-06 14:00:22 -08:00
|
|
|
|
case RPL_S_SERVER_ID_REQUESTED:
|
2016-08-23 04:05:11 -07:00
|
|
|
|
case RPL_S_DB_REQUESTED:
|
|
|
|
|
case RPL_S_SCHEMA_REQUESTED:
|
|
|
|
|
case RPL_S_MONITOR_REQUESTED:
|
2024-01-09 23:49:05 +01:00
|
|
|
|
ds_put_format(&ds, "connecting: %s", rdb->sync_from);
|
2016-08-23 04:05:11 -07:00
|
|
|
|
break;
|
|
|
|
|
case RPL_S_REPLICATING: {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
ds_put_format(&ds, "replicating: %s\n", rdb->sync_from);
|
2016-08-23 04:05:11 -07:00
|
|
|
|
|
2024-01-09 23:49:05 +01:00
|
|
|
|
if (!sset_is_empty(&rdb->excluded_tables)) {
|
2016-08-23 04:05:11 -07:00
|
|
|
|
ds_put_cstr(&ds, "exclude: ");
|
2024-01-09 23:49:05 +01:00
|
|
|
|
ds_put_and_free_cstr(&ds, get_excluded_tables(db));
|
2016-08-23 04:05:11 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case RPL_S_ERR:
|
2024-01-09 23:49:05 +01:00
|
|
|
|
ds_put_format(&ds, "Replication to (%s) failed", rdb->sync_from);
|
2016-08-23 04:05:11 -07:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-01-09 23:49:05 +01:00
|
|
|
|
ds_put_format(&ds, "not connected to %s", rdb->sync_from);
|
2016-08-23 04:05:11 -07:00
|
|
|
|
}
|
|
|
|
|
return ds_steal_cstr(&ds);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-21 22:26:51 +05:30
|
|
|
|
/* Checks if it's possible to replicate to the local db from the active db
|
|
|
|
|
* schema. Returns true, if 'local_db_schema' has all the tables and columns
|
|
|
|
|
* of 'active_db_schema', false otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
is_replication_possible(struct ovsdb_schema *local_db_schema,
|
|
|
|
|
struct ovsdb_schema *active_db_schema)
|
|
|
|
|
{
|
|
|
|
|
struct shash_node *node;
|
|
|
|
|
SHASH_FOR_EACH (node, &active_db_schema->tables) {
|
|
|
|
|
struct ovsdb_table_schema *ldb_table_schema =
|
|
|
|
|
shash_find_data(&local_db_schema->tables, node->name);
|
|
|
|
|
if (!ldb_table_schema) {
|
|
|
|
|
VLOG_INFO("Table %s not present in the local db schema",
|
|
|
|
|
node->name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Local schema table should have all the columns
|
|
|
|
|
* of active schema table. */
|
|
|
|
|
struct ovsdb_table_schema *adb_table_schema = node->data;
|
|
|
|
|
struct shash_node *n;
|
|
|
|
|
SHASH_FOR_EACH (n, &adb_table_schema->columns) {
|
|
|
|
|
struct ovsdb_column *ldb_col =
|
|
|
|
|
shash_find_data(&ldb_table_schema->columns, n->name);
|
|
|
|
|
if (!ldb_col) {
|
|
|
|
|
VLOG_INFO("Column %s not present in the local "
|
|
|
|
|
"db schema table %s.", n->name, node->name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *ldb_col_json = ovsdb_column_to_json(ldb_col);
|
|
|
|
|
struct json *adb_col_json = ovsdb_column_to_json(n->data);
|
|
|
|
|
bool cols_equal = json_equal(ldb_col_json, adb_col_json);
|
|
|
|
|
json_destroy(ldb_col_json);
|
|
|
|
|
json_destroy(adb_col_json);
|
|
|
|
|
|
|
|
|
|
if (!cols_equal) {
|
|
|
|
|
VLOG_INFO("Column %s mismatch in local "
|
|
|
|
|
"db schema table %s.", n->name, node->name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 10:24:48 +05:30
|
|
|
|
void
|
2024-01-09 23:49:05 +01:00
|
|
|
|
replication_set_probe_interval(const struct ovsdb *db, int probe_interval)
|
2020-01-07 10:24:48 +05:30
|
|
|
|
{
|
2024-01-09 23:49:05 +01:00
|
|
|
|
const struct replication_db *rdb = find_db(db->name);
|
|
|
|
|
|
|
|
|
|
if (rdb && rdb->session) {
|
|
|
|
|
jsonrpc_session_set_probe_interval(rdb->session, probe_interval);
|
2020-01-07 10:24:48 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-24 17:13:06 -07:00
|
|
|
|
void
|
|
|
|
|
replication_usage(void)
|
|
|
|
|
{
|
|
|
|
|
printf("\n\
|
|
|
|
|
Syncing options:\n\
|
2016-08-23 04:05:11 -07:00
|
|
|
|
--sync-from=SERVER sync DATABASE from active SERVER and start in\n\
|
|
|
|
|
backup mode (except with --active)\n\
|
2016-06-24 17:13:06 -07:00
|
|
|
|
--sync-exclude-tables=DB:TABLE,...\n\
|
2016-08-23 04:05:11 -07:00
|
|
|
|
exclude the TABLE in DB from syncing\n\
|
|
|
|
|
--active with --sync-from, start in active mode\n");
|
2016-06-24 17:13:06 -07:00
|
|
|
|
}
|