Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/815896/?format=api
{ "id": 815896, "url": "http://patchwork.ozlabs.org/api/patches/815896/?format=api", "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/20170919220125.32535-45-blp@ovn.org/", "project": { "id": 47, "url": "http://patchwork.ozlabs.org/api/projects/47/?format=api", "name": "Open vSwitch", "link_name": "openvswitch", "list_id": "ovs-dev.openvswitch.org", "list_email": "ovs-dev@openvswitch.org", "web_url": "http://openvswitch.org/", "scm_url": "git@github.com:openvswitch/ovs.git", "webscm_url": "https://github.com/openvswitch/ovs", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20170919220125.32535-45-blp@ovn.org>", "list_archive_url": null, "date": "2017-09-19T22:01:17", "name": "[ovs-dev,RFC,44/52] ovsdb-server: Add new RPC \"set_db_change_aware\".", "commit_ref": null, "pull_url": null, "state": "rfc", "archived": false, "hash": "9141afea368bd52c8205fc1180a09556efa2883e", "submitter": { "id": 67603, "url": "http://patchwork.ozlabs.org/api/people/67603/?format=api", "name": "Ben Pfaff", "email": "blp@ovn.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/20170919220125.32535-45-blp@ovn.org/mbox/", "series": [ { "id": 3975, "url": "http://patchwork.ozlabs.org/api/series/3975/?format=api", "web_url": "http://patchwork.ozlabs.org/project/openvswitch/list/?series=3975", "date": "2017-09-19T22:00:34", "name": "clustering implementation", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/3975/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/815896/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/815896/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<ovs-dev-bounces@openvswitch.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "dev@openvswitch.org" ], "Delivered-To": [ "patchwork-incoming@bilbo.ozlabs.org", "ovs-dev@mail.linuxfoundation.org" ], "Authentication-Results": "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=openvswitch.org\n\t(client-ip=140.211.169.12; helo=mail.linuxfoundation.org;\n\tenvelope-from=ovs-dev-bounces@openvswitch.org;\n\treceiver=<UNKNOWN>)", "Received": [ "from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xxcmg5Wmmz9sMN\n\tfor <incoming@patchwork.ozlabs.org>;\n\tWed, 20 Sep 2017 08:21:55 +1000 (AEST)", "from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id 10AD2D10;\n\tTue, 19 Sep 2017 22:02:50 +0000 (UTC)", "from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id C2396AF7\n\tfor <dev@openvswitch.org>; Tue, 19 Sep 2017 22:02:48 +0000 (UTC)", "from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net\n\t[217.70.183.196])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id 337B5174\n\tfor <dev@openvswitch.org>; Tue, 19 Sep 2017 22:02:47 +0000 (UTC)", "from sigabrt.benpfaff.org (unknown [208.91.2.3])\n\t(Authenticated sender: blp@ovn.org)\n\tby relay4-d.mail.gandi.net (Postfix) with ESMTPSA id E7F17172094;\n\tWed, 20 Sep 2017 00:02:44 +0200 (CEST)" ], "X-Greylist": "domain auto-whitelisted by SQLgrey-1.7.6", "X-Originating-IP": "208.91.2.3", "From": "Ben Pfaff <blp@ovn.org>", "To": "dev@openvswitch.org", "Date": "Tue, 19 Sep 2017 15:01:17 -0700", "Message-Id": "<20170919220125.32535-45-blp@ovn.org>", "X-Mailer": "git-send-email 2.10.2", "In-Reply-To": "<20170919220125.32535-1-blp@ovn.org>", "References": "<20170919220125.32535-1-blp@ovn.org>", "X-Spam-Status": "No, score=-0.7 required=5.0 tests=RCVD_IN_DNSWL_LOW\n\tautolearn=disabled version=3.3.1", "X-Spam-Checker-Version": "SpamAssassin 3.3.1 (2010-03-16) on\n\tsmtp1.linux-foundation.org", "Cc": "Ben Pfaff <blp@ovn.org>", "Subject": "[ovs-dev] [PATCH RFC 44/52] ovsdb-server: Add new RPC\n\t\"set_db_change_aware\".", "X-BeenThere": "ovs-dev@openvswitch.org", "X-Mailman-Version": "2.1.12", "Precedence": "list", "List-Id": "<ovs-dev.openvswitch.org>", "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>", "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>", "List-Post": "<mailto:ovs-dev@openvswitch.org>", "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>", "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Sender": "ovs-dev-bounces@openvswitch.org", "Errors-To": "ovs-dev-bounces@openvswitch.org" }, "content": "The _Server database recently added to ovsdb-server can be used to dump out\ninformation about databases, but monitoring updates to _Server is not yet\nvery useful because for historical reasons ovsdb-server drops all of its\nOVSDB connections whenever databases are added or removed or otherwise\nchange in some major way. It is not a good idea to change this behavior\nfor all clients, because some of them rely on it, but this commit\nintroduces a new RPC that allows clients that understand _Server to\nsuppress the connection-closing behavior.\n\nSigned-off-by: Ben Pfaff <blp@ovn.org>\n---\n ovsdb/_server.xml | 34 +++++++++\n ovsdb/jsonrpc-server.c | 195 +++++++++++++++++++++++++++++++++++++-----------\n ovsdb/jsonrpc-server.h | 4 +-\n ovsdb/ovsdb-client.c | 31 +++++++-\n ovsdb/ovsdb-server.1.in | 57 ++++++++++++++\n ovsdb/ovsdb-server.c | 11 +--\n tests/ovsdb-server.at | 34 ++++++++-\n 7 files changed, 311 insertions(+), 55 deletions(-)", "diff": "diff --git a/ovsdb/_server.xml b/ovsdb/_server.xml\nindex a55beb9bd6de..8ef782fb97b2 100644\n--- a/ovsdb/_server.xml\n+++ b/ovsdb/_server.xml\n@@ -13,6 +13,40 @@\n one row per database. As its database configuration and status changes,\n the server automatically and immediately updates the table to match.\n </p>\n+\n+ <p>\n+ The OVSDB protocol specified in RFC 7047 does not provide a way for an\n+ OVSDB client to find out about some kinds of configuration changes, such\n+ as about databases added or removed while a client is connected to the\n+ server, or databases changing between read/write and read-only due to a\n+ transition between active and backup roles. This table provides a\n+ solution: clients can monitor the table's contents to find out about\n+ important changes.\n+ </p>\n+\n+ <p>\n+ Traditionally, <code>ovsdb-server</code> disconnects all of its clients\n+ when a significant configuration change occurs, because this prompts a\n+ well-written client to reassess what is available from the server when it\n+ reconnects. Because this table provides an alternative and more\n+ efficient way to find out about those changes, OVS 2.9 also introduces\n+ the <code>set_db_change_aware</code> RPC, documented in\n+ <code>ovsdb-server</code>(1), to allow clients to suppress this\n+ disconnection behavior.\n+ </p>\n+\n+ <p>\n+ When a database is removed from the server, in addition to\n+ <code>Database</code> table updates, the server sends <code>cancel</code>\n+ messages, as described in RFC 7047 section 4.1.4, in reply to outstanding\n+ transactions for the removed database. The server also cancels any\n+ outstanding monitoring initiated by <code>monitor</code> or\n+ <code>monitor_cond</code> requested on the removed database, sending the\n+ <code>monitor_canceled</code> RPC described in\n+ <code>ovsdb-server</code>(5). Only clients that disable disconnection\n+ with <code>set_db_change_aware</code> receive these messages.\n+ </p>\n+\n <p>\n Clients can use the <code>_uuid</code> column in this table as a\n generation number. The server generates a fresh <code>_uuid</code> every\ndiff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c\nindex 6e5f75498fc1..a667dbe67f5f 100644\n--- a/ovsdb/jsonrpc-server.c\n+++ b/ovsdb/jsonrpc-server.c\n@@ -57,12 +57,15 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n /* Sessions. */\n static struct ovsdb_jsonrpc_session *ovsdb_jsonrpc_session_create(\n struct ovsdb_jsonrpc_remote *, struct jsonrpc_session *, bool);\n+static void ovsdb_jsonrpc_session_preremove_db(struct ovsdb_jsonrpc_remote *,\n+ struct ovsdb *);\n static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *);\n static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *);\n static void ovsdb_jsonrpc_session_get_memory_usage_all(\n const struct ovsdb_jsonrpc_remote *, struct simap *usage);\n static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *);\n-static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *);\n+static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *,\n+ bool force);\n static void ovsdb_jsonrpc_session_set_all_options(\n struct ovsdb_jsonrpc_remote *, const struct ovsdb_jsonrpc_options *);\n static bool ovsdb_jsonrpc_active_session_get_status(\n@@ -83,6 +86,8 @@ static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *,\n static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find(\n struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash);\n static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *);\n+static void ovsdb_jsonrpc_trigger_preremove_db(struct ovsdb_jsonrpc_session *,\n+ struct ovsdb *);\n static void ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *);\n static void ovsdb_jsonrpc_trigger_complete_done(\n struct ovsdb_jsonrpc_session *);\n@@ -99,6 +104,8 @@ static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel(\n struct ovsdb_jsonrpc_session *,\n struct json_array *params,\n const struct json *request_id);\n+static void ovsdb_jsonrpc_monitor_preremove_db(struct ovsdb_jsonrpc_session *,\n+ struct ovsdb *);\n static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *);\n static void ovsdb_jsonrpc_monitor_flush_all(struct ovsdb_jsonrpc_session *);\n static bool ovsdb_jsonrpc_monitor_needs_flush(struct ovsdb_jsonrpc_session *);\n@@ -157,34 +164,25 @@ ovsdb_jsonrpc_server_create(bool read_only)\n bool\n ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db)\n {\n- /* The OVSDB protocol doesn't have a way to notify a client that a\n- * database has been added. If some client tried to use the database\n- * that we're adding and failed, then forcing it to reconnect seems like\n- * a reasonable way to make it try again.\n- *\n- * If this is too big of a hammer in practice, we could be more selective,\n- * e.g. disconnect only connections that actually tried to use a database\n- * with 'db''s name. */\n- ovsdb_jsonrpc_server_reconnect(svr);\n-\n+ ovsdb_jsonrpc_server_reconnect(svr, false);\n return ovsdb_server_add_db(&svr->up, db);\n }\n \n-/* Removes 'db' from the set of databases served out by 'svr'. Returns\n- * true if successful, false if there is no database associated with 'db'. */\n-bool\n+/* Removes 'db' from the set of databases served out by 'svr'. */\n+void\n ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *svr,\n struct ovsdb *db)\n {\n- /* There might be pointers to 'db' from 'svr', such as monitors or\n- * outstanding transactions. Disconnect all JSON-RPC connections to avoid\n- * accesses to freed memory.\n- *\n- * If this is too big of a hammer in practice, we could be more selective,\n- * e.g. disconnect only connections that actually reference 'db'. */\n- ovsdb_jsonrpc_server_reconnect(svr);\n+ struct shash_node *node;\n+ SHASH_FOR_EACH (node, &svr->remotes) {\n+ struct ovsdb_jsonrpc_remote *remote = node->data;\n+\n+ ovsdb_jsonrpc_session_preremove_db(remote, db);\n+ }\n+\n+ ovsdb_jsonrpc_server_reconnect(svr, false);\n \n- return ovsdb_server_remove_db(&svr->up, db);\n+ ovsdb_server_remove_db(&svr->up, db);\n }\n \n void\n@@ -333,17 +331,20 @@ ovsdb_jsonrpc_server_free_remote_status(\n free(status->locks_lost);\n }\n \n-/* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and\n- * reconnect. */\n+/* Makes all of the JSON-RPC sessions managed by 'svr' to disconnect. (They\n+ * will then generally reconnect.).\n+ *\n+ * If 'force' is true, disconnects all sessions. Otherwise, disconnects only\n+ * sesions that aren't database change aware. */\n void\n-ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *svr)\n+ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *svr, bool force)\n {\n struct shash_node *node;\n \n SHASH_FOR_EACH (node, &svr->remotes) {\n struct ovsdb_jsonrpc_remote *remote = node->data;\n \n- ovsdb_jsonrpc_session_reconnect_all(remote);\n+ ovsdb_jsonrpc_session_reconnect_all(remote, force);\n }\n }\n \n@@ -359,7 +360,7 @@ ovsdb_jsonrpc_server_set_read_only(struct ovsdb_jsonrpc_server *svr,\n {\n if (svr->read_only != read_only) {\n svr->read_only = read_only;\n- ovsdb_jsonrpc_server_reconnect(svr);\n+ ovsdb_jsonrpc_server_reconnect(svr, false);\n }\n }\n \n@@ -432,6 +433,20 @@ struct ovsdb_jsonrpc_session {\n struct ovsdb_session up;\n struct ovsdb_jsonrpc_remote *remote;\n \n+ /* RFC 7047 does not contemplate how to alert clients to changes to the set\n+ * of databases, e.g. databases that are added or removed while the\n+ * database server is running. Traditionally, ovsdb-server disconnects all\n+ * of its clients when this happens; a well-written client will reassess\n+ * what is available from the server upon reconnection.\n+ *\n+ * OVS 2.9 introduces a way for clients to monitor changes to the databases\n+ * being served, through the Database table in the _Server database that\n+ * OVSDB adds in this version. ovsdb-server suppresses the connection\n+ * close for clients that identify themselves as taking advantage of this\n+ * mechanism.\n+ */\n+ bool db_change_aware;\n+\n /* Triggers. */\n struct hmap triggers; /* Hmap of \"struct ovsdb_jsonrpc_trigger\"s. */\n \n@@ -478,6 +493,20 @@ ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote,\n return s;\n }\n \n+/* Database 'db' is about to be removed from the database server. To prepare,\n+ * this function removes all references to 'db' from session 's'. */\n+static void\n+ovsdb_jsonrpc_session_preremove_db(struct ovsdb_jsonrpc_remote *remote,\n+ struct ovsdb *db)\n+{\n+ struct ovsdb_jsonrpc_session *s;\n+\n+ LIST_FOR_EACH (s, node, &remote->sessions) {\n+ ovsdb_jsonrpc_monitor_preremove_db(s, db);\n+ ovsdb_jsonrpc_trigger_preremove_db(s, db);\n+ }\n+}\n+\n static void\n ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s)\n {\n@@ -606,17 +635,23 @@ ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote)\n }\n }\n \n-/* Forces all of the JSON-RPC sessions managed by 'remote' to disconnect and\n- * reconnect. */\n+/* Makes all of the JSON-RPC sessions managed by 'remove' to disconnect. (They\n+ * will then generally reconnect.).\n+ *\n+ * If 'force' is true, disconnects all sessions. Otherwise, disconnects only\n+ * sesions that aren't database change aware. */\n static void\n-ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *remote)\n+ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *remote,\n+ bool force)\n {\n struct ovsdb_jsonrpc_session *s, *next;\n \n LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) {\n- jsonrpc_session_force_reconnect(s->js);\n- if (!jsonrpc_session_is_alive(s->js)) {\n- ovsdb_jsonrpc_session_close(s);\n+ if (force || !s->db_change_aware) {\n+ jsonrpc_session_force_reconnect(s->js);\n+ if (!jsonrpc_session_is_alive(s->js)) {\n+ ovsdb_jsonrpc_session_close(s);\n+ }\n }\n }\n }\n@@ -859,6 +894,17 @@ ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *waiter)\n }\n \n static struct jsonrpc_msg *\n+syntax_error_reply(const struct jsonrpc_msg *request, const char *details)\n+{\n+ struct ovsdb_error *error = ovsdb_syntax_error(\n+ request->params, NULL, \"%s: %s\", request->method, details);\n+ struct jsonrpc_msg *msg = jsonrpc_create_error(ovsdb_error_to_json(error),\n+ request->id);\n+ ovsdb_error_destroy(error);\n+ return msg;\n+}\n+\n+static struct jsonrpc_msg *\n ovsdb_jsonrpc_session_unlock(struct ovsdb_jsonrpc_session *s,\n struct jsonrpc_msg *request)\n {\n@@ -872,24 +918,21 @@ ovsdb_jsonrpc_session_unlock(struct ovsdb_jsonrpc_session *s,\n \n error = ovsdb_jsonrpc_session_parse_lock_name(request, &lock_name);\n if (error) {\n- goto error;\n+ return jsonrpc_create_error(ovsdb_error_to_json_free(error),\n+ request->id);\n }\n \n /* Report error if this session has not issued a \"lock\" or \"steal\" for this\n * lock. */\n waiter = ovsdb_session_get_lock_waiter(&s->up, lock_name);\n if (!waiter) {\n- error = ovsdb_syntax_error(\n- request->params, NULL, \"\\\"unlock\\\" without \\\"lock\\\" or \\\"steal\\\"\");\n- goto error;\n+ return syntax_error_reply(request,\n+ \"\\\"unlock\\\" without \\\"lock\\\" or \\\"steal\\\"\");\n }\n \n ovsdb_jsonrpc_session_unlock__(waiter);\n \n return jsonrpc_create_reply(json_object_create(), request->id);\n-\n-error:\n- return jsonrpc_create_error(ovsdb_error_to_json_free(error), request->id);\n }\n \n static struct jsonrpc_msg *\n@@ -903,6 +946,21 @@ execute_transaction(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,\n return NULL;\n }\n \n+static struct jsonrpc_msg *\n+ovsdb_jsonrpc_session_set_db_change_aware(struct ovsdb_jsonrpc_session *s,\n+ const struct jsonrpc_msg *request)\n+{\n+ const struct json_array *params = json_array(request->params);\n+ if (params->n != 1\n+ || (params->elems[0]->type != JSON_TRUE &&\n+ params->elems[0]->type != JSON_FALSE)) {\n+ return syntax_error_reply(request, \"true or false parameter expected\");\n+ }\n+\n+ s->db_change_aware = json_boolean(params->elems[0]);\n+ return jsonrpc_create_reply(json_object_create(), request->id);\n+}\n+\n static void\n ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,\n struct jsonrpc_msg *request)\n@@ -963,6 +1021,8 @@ ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,\n reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_STEAL);\n } else if (!strcmp(request->method, \"unlock\")) {\n reply = ovsdb_jsonrpc_session_unlock(s, request);\n+ } else if (!strcmp(request->method, \"set_db_change_aware\")) {\n+ reply = ovsdb_jsonrpc_session_set_db_change_aware(s, request);\n } else if (!strcmp(request->method, \"echo\")) {\n reply = jsonrpc_create_reply(json_clone(request->params), request->id);\n } else {\n@@ -1098,14 +1158,34 @@ ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *t)\n }\n \n static void\n-ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s)\n+ovsdb_jsonrpc_trigger_remove__(struct ovsdb_jsonrpc_session *s,\n+ struct ovsdb *db)\n {\n struct ovsdb_jsonrpc_trigger *t, *next;\n HMAP_FOR_EACH_SAFE (t, next, hmap_node, &s->triggers) {\n- ovsdb_jsonrpc_trigger_complete(t);\n+ if (!db || t->trigger.db == db) {\n+ ovsdb_jsonrpc_trigger_complete(t);\n+ }\n }\n }\n \n+/* Database 'db' is about to be removed from the database server. To prepare,\n+ * this function removes all references from triggers in 's' to 'db'. */\n+static void\n+ovsdb_jsonrpc_trigger_preremove_db(struct ovsdb_jsonrpc_session *s,\n+ struct ovsdb *db)\n+{\n+ ovs_assert(db);\n+ ovsdb_jsonrpc_trigger_remove__(s, db);\n+}\n+\n+/* Removes all triggers from 's'. */\n+static void\n+ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s)\n+{\n+ ovsdb_jsonrpc_trigger_remove__(s, NULL);\n+}\n+\n static void\n ovsdb_jsonrpc_trigger_complete_done(struct ovsdb_jsonrpc_session *s)\n {\n@@ -1526,15 +1606,42 @@ ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s,\n }\n \n static void\n-ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s)\n+ovsdb_jsonrpc_monitor_remove__(struct ovsdb_jsonrpc_session *s,\n+ struct ovsdb *db)\n {\n struct ovsdb_jsonrpc_monitor *m, *next;\n \n HMAP_FOR_EACH_SAFE (m, next, node, &s->monitors) {\n- ovsdb_jsonrpc_monitor_destroy(m);\n+ if (!db || m->db == db) {\n+ if (db && jsonrpc_session_is_connected(s->js)\n+ && s->db_change_aware) {\n+ struct jsonrpc_msg *notify = jsonrpc_create_notify(\n+ \"monitor_canceled\",\n+ json_array_create_1(json_clone(m->monitor_id)));\n+ ovsdb_jsonrpc_session_send(s, notify);\n+ }\n+ ovsdb_jsonrpc_monitor_destroy(m);\n+ }\n }\n }\n \n+/* Database 'db' is about to be removed from the database server. To prepare,\n+ * this function removes all references from monitors in 's' to 'db'. */\n+static void\n+ovsdb_jsonrpc_monitor_preremove_db(struct ovsdb_jsonrpc_session *s,\n+ struct ovsdb *db)\n+{\n+ ovs_assert(db);\n+ ovsdb_jsonrpc_monitor_remove__(s, db);\n+}\n+\n+/* Cancels all monitors in 's'. */\n+static void\n+ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s)\n+{\n+ ovsdb_jsonrpc_monitor_remove__(s, NULL);\n+}\n+\n static struct json *\n ovsdb_jsonrpc_monitor_compose_update(struct ovsdb_jsonrpc_monitor *m,\n bool initial)\ndiff --git a/ovsdb/jsonrpc-server.h b/ovsdb/jsonrpc-server.h\nindex a3acc75f8d4f..50a8b879c5a9 100644\n--- a/ovsdb/jsonrpc-server.h\n+++ b/ovsdb/jsonrpc-server.h\n@@ -27,7 +27,7 @@ struct uuid;\n struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(bool read_only);\n bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *,\n struct ovsdb *);\n-bool ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *,\n+void ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *,\n struct ovsdb *);\n void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *);\n \n@@ -64,7 +64,7 @@ bool ovsdb_jsonrpc_server_get_remote_status(\n void ovsdb_jsonrpc_server_free_remote_status(\n struct ovsdb_jsonrpc_remote_status *);\n \n-void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *);\n+void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *, bool force);\n \n void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *);\n void ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *);\ndiff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c\nindex 194ff47593f3..cecc0346eda1 100644\n--- a/ovsdb/ovsdb-client.c\n+++ b/ovsdb/ovsdb-client.c\n@@ -71,6 +71,12 @@ struct ovsdb_client_command {\n /* --timestamp: Print a timestamp before each update on \"monitor\" command? */\n static bool timestamp;\n \n+/* --db-change-aware: Enable db_change_aware feature for \"monitor\" command?\n+ *\n+ * (This option is undocumented because it is expected to be useful only for\n+ * testing that the db_change_aware feature actually works.) */\n+static bool db_change_aware;\n+\n /* Format for table output. */\n static struct table_style table_style = TABLE_STYLE_DEFAULT;\n \n@@ -182,6 +188,7 @@ parse_options(int argc, char *argv[])\n enum {\n OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,\n OPT_TIMESTAMP,\n+ OPT_DB_CHANGE_AWARE,\n VLOG_OPTION_ENUMS,\n DAEMON_OPTION_ENUMS,\n TABLE_OPTION_ENUMS,\n@@ -191,6 +198,7 @@ parse_options(int argc, char *argv[])\n {\"help\", no_argument, NULL, 'h'},\n {\"version\", no_argument, NULL, 'V'},\n {\"timestamp\", no_argument, NULL, OPT_TIMESTAMP},\n+ {\"db-change-aware\", no_argument, NULL, OPT_DB_CHANGE_AWARE},\n VLOG_LONG_OPTIONS,\n DAEMON_LONG_OPTIONS,\n #ifdef HAVE_OPENSSL\n@@ -233,6 +241,10 @@ parse_options(int argc, char *argv[])\n timestamp = true;\n break;\n \n+ case OPT_DB_CHANGE_AWARE:\n+ db_change_aware = true;\n+ break;\n+\n case '?':\n exit(EXIT_FAILURE);\n \n@@ -949,7 +961,6 @@ do_monitor__(struct jsonrpc *rpc, const char *database,\n const char *table_name = argv[0];\n struct unixctl_server *unixctl;\n struct ovsdb_schema *schema;\n- struct jsonrpc_msg *request;\n struct json *monitor, *monitor_requests, *request_id;\n bool exiting = false;\n bool blocked = false;\n@@ -1017,11 +1028,29 @@ do_monitor__(struct jsonrpc *rpc, const char *database,\n free(nodes);\n }\n \n+ if (db_change_aware) {\n+ struct jsonrpc_msg *request = jsonrpc_create_request(\n+ \"set_db_change_aware\",\n+ json_array_create_1(json_boolean_create(true)),\n+ NULL);\n+ struct jsonrpc_msg *reply;\n+ int error = jsonrpc_transact_block(rpc, request, &reply);\n+ if (error) {\n+ ovs_fatal(error, \"%s: error setting db_change_aware\", server);\n+ }\n+ if (reply->type == JSONRPC_ERROR) {\n+ ovs_fatal(0, \"%s: set_db_change_aware failed (%s)\",\n+ server, json_to_string(reply->error, 0));\n+ }\n+ jsonrpc_msg_destroy(reply);\n+ }\n+\n monitor = json_array_create_3(json_string_create(database),\n json_null_create(), monitor_requests);\n const char *method = version == OVSDB_MONITOR_V2 ? \"monitor_cond\"\n : \"monitor\";\n \n+ struct jsonrpc_msg *request;\n request = jsonrpc_create_request(method, monitor, NULL);\n request_id = json_clone(request->id);\n jsonrpc_send(rpc, request);\ndiff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in\nindex 02144125e67c..9bfd36edd351 100644\n--- a/ovsdb/ovsdb-server.1.in\n+++ b/ovsdb/ovsdb-server.1.in\n@@ -406,6 +406,24 @@ notifications (see below) to the request, it must be unique among all\n active monitors. \\fBovsdb\\-server\\fR rejects attempt to create two\n monitors with the same identifier.\n .\n+.IP \"4.1.7. Monitor Cancellation\"\n+.IP\n+When a database monitored by a session is removed, and database change\n+awareness is enabled for the session (see Section 4.1.16), the\n+database server spontaneously cancels all monitors (including\n+conditional monitors described in Section 4.1.12) for the removed\n+database. For each canceled monitor, it issues a notification in the\n+following form:\n+.\n+.PP\n+.RS\n+.nf\n+\"method\": \"monitor_canceled\"\n+\"params\": [<json-value>]\n+\"id\": null\n+.fi\n+.RE\n+.\n .IP \"4.1.12. Monitor_cond\"\n A new monitor method added in Open vSwitch version 2.6. The monitor_cond\n request enables a client to replicate subsets of tables within an OVSDB\n@@ -704,6 +722,45 @@ The response object contains the following members:\n the running OVSDB server process. A fresh UUID is generated when the\n process restarts.\n .\n+.IP \"4.1.16. Database Change Awareness\"\n+.IP\n+RFC 7047 does not provide a way for a client to find out about some\n+kinds of configuration changes, such as about databases added or\n+removed while a client is connected to the server, or databases\n+changing between read/write and read-only due to a transition between\n+active and backup roles. Traditionally, \\fBovsdb\\-server\\fR\n+disconnects all of its clients when this happens, because this prompts\n+a well-written client to reassess what is available from the server\n+when it reconnects.\n+.IP\n+OVS 2.9 provides a way for clients to keep track of these kinds of\n+changes, by monitoring the \\fBDatabase\\fR table in the \\fB_Server\\fR\n+database introduced in this release. See \\fBovsdb-server\\fR(5) for\n+details. By itself, this does not suppress \\fBovsdb\\-server\\fR\n+disconnection behavior, because a client might monitor this database\n+without understanding its special semantics. Instead,\n+\\fBovsdb\\-server\\fR provides a special request:\n+.PP\n+.RS\n+.nf\n+\"method\": \"set_db_change_aware\"\n+\"params\": [<boolean>]\n+\"id\": <nonnull-json-value>\n+.fi\n+.RE\n+.IP\n+If the boolean in the request is true, it suppresses the\n+connection-closing behavior for the current connection, and false\n+restores the default behavior. The reply is always the same:\n+.PP\n+.RS\n+.nf\n+\"result\": {}\n+\"error\": null\n+\"id\": same \"id\" as request\n+.fi\n+.RE\n+.\n .IP \"5.1. Notation\"\n For <condition>, RFC 7047 only allows the use of \\fB!=\\fR, \\fB==\\fR,\n \\fBincludes\\fR, and \\fBexcludes\\fR operators with set types. Open\ndiff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c\nindex 52eb21f89aa0..e28fca5989fc 100644\n--- a/ovsdb/ovsdb-server.c\n+++ b/ovsdb/ovsdb-server.c\n@@ -1227,7 +1227,7 @@ ovsdb_server_disable_monitor_cond(struct unixctl_conn *conn,\n struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;\n \n ovsdb_jsonrpc_disable_monitor_cond();\n- ovsdb_jsonrpc_server_reconnect(jsonrpc);\n+ ovsdb_jsonrpc_server_reconnect(jsonrpc, true);\n unixctl_command_reply(conn, NULL);\n }\n \n@@ -1284,7 +1284,7 @@ ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED,\n const char *argv[] OVS_UNUSED, void *jsonrpc_)\n {\n struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_;\n- ovsdb_jsonrpc_server_reconnect(jsonrpc);\n+ ovsdb_jsonrpc_server_reconnect(jsonrpc, true);\n unixctl_command_reply(conn, NULL);\n }\n \n@@ -1386,12 +1386,9 @@ ovsdb_server_add_database(struct unixctl_conn *conn, int argc OVS_UNUSED,\n static void\n remove_db(struct server_config *config, struct shash_node *node)\n {\n- struct db *db;\n- bool ok;\n+ struct db *db = node->data;\n \n- db = node->data;\n- ok = ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db);\n- ovs_assert(ok);\n+ ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db);\n \n close_db(db);\n shash_delete(config->all_dbs, node);\ndiff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at\nindex ccbc1ac0b717..1ad49e64139f 100644\n--- a/tests/ovsdb-server.at\n+++ b/tests/ovsdb-server.at\n@@ -179,7 +179,7 @@ AT_CLEANUP\n \n AT_SETUP([ovsdb-server/add-db and remove-db])\n AT_KEYWORDS([ovsdb server positive])\n-on_exit 'kill `cat ovsdb-server.pid`'\n+on_exit 'kill `cat *.pid`'\n ordinal_schema > schema1\n constraint_schema > schema2\n AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore])\n@@ -190,6 +190,19 @@ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:db.sock db1]\n CHECK_DBS([ordinals\n ])\n \n+# Remove the database.\n+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db ordinals], [0])\n+CHECK_DBS([])\n+\n+# Start monitoring processes.\n+AT_CHECK([ovsdb-client --detach --pidfile=ovsdb-client-1.pid --no-headings monitor _Server Database name > db-changes-unaware])\n+AT_CHECK([ovsdb-client --detach --pidfile=ovsdb-client-2.pid --db-change-aware --no-headings monitor _Server Database name > db-changes-aware])\n+\n+# Add the first database back.\n+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db1], [0])\n+CHECK_DBS([ordinals\n+])\n+\n # Add the second database.\n AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0])\n CHECK_DBS([constraints\n@@ -253,6 +266,25 @@ AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-db db2], [0])\n CHECK_DBS([constraints\n ])\n AT_CHECK([ovsdb-client list-tables unix:db.sock constraints], [0], [ignore], [ignore])\n+\n+# Check the monitoring results.\n+AT_CHECK([${PERL} $srcdir/uuidfilt.pl db-changes-aware], [0], [dnl\n+<0> initial _Server\n+\n+<1> insert ordinals\n+\n+<2> insert constraints\n+\n+<1> delete ordinals\n+\n+<2> delete constraints\n+\n+<3> insert constraints\n+])\n+AT_CHECK([${PERL} $srcdir/uuidfilt.pl db-changes-unaware], [0], [dnl\n+<0> initial _Server\n+])\n+\n OVS_APP_EXIT_AND_WAIT([ovsdb-server])\n AT_CLEANUP\n \n", "prefixes": [ "ovs-dev", "RFC", "44/52" ] }