@@ -109,7 +109,8 @@ by setting ``copyForReplication`` flag to ``true``. For each table marked
with this flag, ``ovsdb-server`` will create one more table with the same
name and ``_synced_`` prefix (e.g., ``_synced_<table-name>``). Server in a
backup role will keep its own content in the original table and will put
-data, received from the active server, to this special table.
+data, received from the active server, to this special table. Currently
+used to replicate ``Database`` table from a ``_Server`` database.
4 Wire Protocol
---------------
@@ -4,6 +4,10 @@ Post-v2.15.0
* New command line argument '--enable-txn-forward' for ovsdb-server in
replication mode that allows to forward transactions that includes
database modifications to the primary database server.
+ * Replication (backup) server now replicates a _Server database from the
+ replication source (active server). Data from the 'Database' table of
+ the active server stored in '_synced_Database' table on backup and
+ available for monitoring.
- In ovs-vsctl and vtep-ctl, the "find" command now accept new
operators {in} and {not-in}.
- Userspace datapath:
@@ -1,6 +1,6 @@
{"name": "_Server",
- "version": "1.1.0",
- "cksum": "3236486585 698",
+ "version": "1.2.0",
+ "cksum": "88700080 731",
"tables": {
"Database": {
"columns": {
@@ -18,4 +18,5 @@
"type": {"key": {"type": "uuid"}, "min": 0, "max": 1}},
"index": {
"type": {"key": {"type": "integer"}, "min": 0, "max": 1}}},
+ "copyForReplication": true,
"isRoot": true}}}
@@ -54,6 +54,16 @@
to the server causes its row <code>_uuid</code> to change.
</p>
+ <p>
+ This table is marked with <code>copyForReplication</code> flag. This
+ means that <code>ovsdb-server</code> will create one more table with
+ name <code>_synced_Database</code> for replication purposes. Backup
+ database in active-backup model will replicate content of this table
+ from the active database and store it in <code>_synced_Database</code>
+ table. This way clients are able to monitor the state of the active
+ server, e.g., check the status of clustered databases.
+ </p>
+
<column name="name">
The database's name, as specified in its schema.
</column>
@@ -160,7 +160,7 @@ ovsdb_replication_init(const char *sync_from, const char *exclude,
struct shash_node *node;
SHASH_FOR_EACH (node, all_dbs) {
struct db *db = node->data;
- if (node->name[0] != '_' && db->db) {
+ if (db->db) {
replication_add_local_db(node->name, db->db);
}
}
@@ -49,6 +49,7 @@ static void add_monitored_table(struct ovsdb_table_schema *table,
struct json *monitor_requests);
static struct ovsdb_error *reset_database(struct ovsdb *db);
+static void reset_internal_databases(void);
static struct ovsdb_error *process_notification(struct json *, struct ovsdb *);
static struct ovsdb_error *process_table_update(struct json *table_update,
@@ -104,6 +105,7 @@ static enum ovsdb_replication_state state;
struct replication_db {
struct ovsdb *db;
bool schema_version_higher;
+ bool internal; /* True if the database name starts with '_'. */
/* 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;
@@ -118,6 +120,10 @@ static bool is_replication_possible(struct ovsdb_schema *local_db_schema,
static struct shash local_dbs = SHASH_INITIALIZER(&local_dbs);
static struct shash *replication_dbs;
+/* Number of internal databases in 'replication_dbs', i.e. databases which
+ * name starts with '_'. */
+static int n_internal_dbs = 0;
+
static struct shash *replication_dbs_create(void);
static void replication_dbs_destroy(void);
/* Find 'struct ovsdb' by name within 'replication_dbs' */
@@ -134,6 +140,7 @@ replication_init(const char *sync_from_, const char *exclude_tables,
* parseable. An error here is unexpected. */
ovs_assert(!set_excluded_tables(exclude_tables, false));
+ reset_internal_databases();
replication_dbs_destroy();
shash_clear(&local_dbs);
@@ -337,6 +344,9 @@ replication_run(void)
if (r->active_db_schema) {
ovsdb_schema_destroy(r->active_db_schema);
}
+ if (r->internal) {
+ n_internal_dbs--;
+ }
free(r);
ovsdb_schema_destroy(schema);
}
@@ -349,8 +359,10 @@ replication_run(void)
if (hmap_is_empty(&request_ids)) {
struct shash_node *node;
- if (shash_is_empty(replication_dbs)) {
+ if (shash_is_empty(replication_dbs) ||
+ n_internal_dbs == shash_count(replication_dbs)) {
VLOG_WARN("Nothing to replicate.");
+ replication_dbs_destroy();
state = RPL_S_ERR;
} else {
SHASH_FOR_EACH (node, replication_dbs) {
@@ -552,6 +564,8 @@ disconnect_active_server(void)
session = NULL;
/* Cancel all pending transactions. */
ovsdb_txn_forward_cancel_all(false);
+ /* Clear data from '_synced_' tables. */
+ reset_internal_databases();
}
void
@@ -586,9 +600,12 @@ reset_database(struct ovsdb *db)
struct shash_node *table_node;
SHASH_FOR_EACH (table_node, &db->tables) {
- /* Delete all rows if the table is not excluded. */
- if (!excluded_tables_find(db->schema->name, table_node->name)) {
- struct ovsdb_table *table = table_node->data;
+ struct ovsdb_table *table = table_node->data;
+
+ /* Delete all rows if the table is not excluded and if it has no
+ * special '_synced_' copy. */
+ if (!excluded_tables_find(db->schema->name, table_node->name)
+ && !table->schema->copy_for_replication) {
struct ovsdb_row *row, *next;
HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) {
ovsdb_txn_row_delete(txn, row);
@@ -599,6 +616,26 @@ reset_database(struct ovsdb *db)
return ovsdb_txn_propose_commit_block(txn, false);
}
+static void
+reset_internal_databases(void)
+{
+ if (!replication_dbs) {
+ return;
+ }
+
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, replication_dbs) {
+ struct replication_db *rdb = node->data;
+
+ if (rdb->internal) {
+ /* Cleaning up data from '_synced_' tables of internal DBs. */
+ VLOG_INFO("Resetting internal database %s", node->name);
+ ovsdb_error_assert(reset_database(rdb->db));
+ }
+ }
+}
+
/* Create a monitor request for 'db'. The monitor request will include
* any tables from 'excluded_tables'
*
@@ -617,9 +654,11 @@ create_monitor_request(struct ovsdb_schema *schema)
for (int j = 0; j < n; j++) {
struct ovsdb_table_schema *table = nodes[j]->data;
+ /* Not monitoring data from replicated copies of tables. */
+ bool skip = !strncmp(table->name, "_synced_", 8);
/* Monitor all tables not excluded. */
- if (!excluded_tables_find(db_name, table->name)) {
+ if (!excluded_tables_find(db_name, table->name) && !skip) {
add_monitored_table(table, monitor_request);
}
}
@@ -690,6 +729,18 @@ process_table_update(struct json *table_update, const char *table_name,
return ovsdb_error("unknown table", "unknown table %s", table_name);
}
+ if (table->schema->copy_for_replication) {
+ /* Data from this table should go to special '_synced_<name>' table. */
+ char *name = xasprintf("_synced_%s", table_name);
+
+ table = ovsdb_get_table(database, name);
+ free(name);
+ if (!table) {
+ return ovsdb_error("unknown table",
+ "unknown table _synced_%s", table_name);
+ }
+ }
+
if (table_update->type != JSON_OBJECT) {
return ovsdb_error("Not a JSON object",
"<table-update> for table is not object");
@@ -853,7 +904,11 @@ replication_dbs_create(void)
repl_db->db = node->data;
repl_db->schema_version_higher = false;
repl_db->active_db_schema = NULL;
+ repl_db->internal = (node->name[0] == '_');
shash_add(new, node->name, repl_db);
+ if (repl_db->internal) {
+ n_internal_dbs++;
+ }
}
return new;
@@ -882,6 +937,7 @@ replication_dbs_destroy(void)
hmap_destroy(&replication_dbs->map);
free(replication_dbs);
replication_dbs = NULL;
+ n_internal_dbs = 0;
}
/* Return true if replication just started or is ongoing.
@@ -1502,6 +1502,11 @@ m4_define([OVSDB_CHECK_EXECUTION],
AT_CHECK([ovsdb-client dump], [0], [stdout], [ignore])
OVS_WAIT_UNTIL([ ovsdb-client dump unix:db2.sock > dump2; diff stdout dump2])
+ AT_CHECK([ovsdb-client dump unix:db2.sock _Server _synced_Database | dnl
+ sed 's/_synced_Database/Database/' > synced_Server ])
+ AT_CHECK([ovsdb-client dump unix:db.sock _Server Database > Server])
+ AT_CHECK([diff synced_Server Server])
+
OVSDB_SERVER_SHUTDOWN
OVSDB_SERVER_SHUTDOWN2
AT_CLEANUP])
If replication server is connected to a cluster member, clients would like to know the state of the cluster from that server. This will allow them to make a decision about re-connection. Marking 'Database' table with a 'copyForReplication' flag to have a new '_synced_Database' table where records from the active server will be stored. This way client will see state of local databases in 'Database' table and state of databases of the active server in '_synced_Database' table. Signed-off-by: Ilya Maximets <i.maximets@ovn.org> --- Documentation/ref/ovsdb-server.7.rst | 3 +- NEWS | 4 ++ ovsdb/_server.ovsschema | 5 ++- ovsdb/_server.xml | 10 +++++ ovsdb/ovsdb-server.c | 2 +- ovsdb/replication.c | 66 +++++++++++++++++++++++++--- tests/ovsdb-server.at | 5 +++ 7 files changed, 86 insertions(+), 9 deletions(-)