{"id":815902,"url":"http://patchwork.ozlabs.org/api/patches/815902/?format=json","web_url":"http://patchwork.ozlabs.org/project/openvswitch/patch/20170919220125.32535-50-blp@ovn.org/","project":{"id":47,"url":"http://patchwork.ozlabs.org/api/projects/47/?format=json","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-50-blp@ovn.org>","list_archive_url":null,"date":"2017-09-19T22:01:22","name":"[ovs-dev,RFC,49/52] ovsdb-client: Add new \"restore\" command.","commit_ref":null,"pull_url":null,"state":"rfc","archived":false,"hash":"4c0b90b226488963d7aa1dfa61fc4452d0693e88","submitter":{"id":67603,"url":"http://patchwork.ozlabs.org/api/people/67603/?format=json","name":"Ben Pfaff","email":"blp@ovn.org"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/openvswitch/patch/20170919220125.32535-50-blp@ovn.org/mbox/","series":[{"id":3975,"url":"http://patchwork.ozlabs.org/api/series/3975/?format=json","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/815902/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/815902/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 3xxctX44khz9sNV\n\tfor <incoming@patchwork.ozlabs.org>;\n\tWed, 20 Sep 2017 08:27:00 +1000 (AEST)","from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id 82CDCD5E;\n\tTue, 19 Sep 2017 22:03:01 +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 C2CE7CFB\n\tfor <dev@openvswitch.org>; Tue, 19 Sep 2017 22:02:58 +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 3FBDB3D4\n\tfor <dev@openvswitch.org>; Tue, 19 Sep 2017 22:02:57 +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 13B2D17209B;\n\tWed, 20 Sep 2017 00:02:54 +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:22 -0700","Message-Id":"<20170919220125.32535-50-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 49/52] ovsdb-client: Add new \"restore\" command.","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":"Signed-off-by: Ben Pfaff <blp@ovn.org>\n---\n NEWS                    |   2 +-\n lib/ovsdb-data.c        |  76 +++++++++---\n lib/ovsdb-data.h        |   5 +\n lib/ovsdb-idl.c         |  20 +---\n manpages.mk             | 299 ++++++++++++++++++++++++++++++++++++++++++++++++\n ovsdb/log.c             |   8 +-\n ovsdb/ovsdb-client.1.in |  28 ++++-\n ovsdb/ovsdb-client.c    | 108 +++++++++++++++++\n ovsdb/ovsdb.7.xml       |  13 ++-\n 9 files changed, 522 insertions(+), 37 deletions(-)","diff":"diff --git a/NEWS b/NEWS\nindex 686c387d782a..9782818a1b0d 100644\n--- a/NEWS\n+++ b/NEWS\n@@ -8,7 +8,7 @@ Post-v2.8.0\n      * ovsdb-server now always hosts a built-in database named _Server.  See\n        ovsdb-server(5) for more details.\n      * ovsdb-client: New \"get-schema-cksum\" command.\n-     * ovsdb-client: New \"backup\" command.\n+     * ovsdb-client: New \"backup\" and \"restore\" commands.\n    - ovs-vsctl and other commands that display data in tables now support a\n      --max-column-width option to limit column width.\n    - OVN:\ndiff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c\nindex b4ea3dcac227..69122dc10432 100644\n--- a/lib/ovsdb-data.c\n+++ b/lib/ovsdb-data.c\n@@ -1358,15 +1358,26 @@ ovsdb_unconstrained_datum_from_json(struct ovsdb_datum *datum,\n     return ovsdb_datum_from_json(datum, &relaxed_type, json, NULL);\n }\n \n-/* Converts 'datum', of the specified 'type', to JSON format, and returns the\n- * JSON.  The caller is responsible for freeing the returned JSON.\n- *\n- * 'type' constraints on datum->n are ignored.\n- *\n- * Refer to RFC 7047 for the format of the JSON that this function produces. */\n-struct json *\n-ovsdb_datum_to_json(const struct ovsdb_datum *datum,\n-                    const struct ovsdb_type *type)\n+static struct json *\n+ovsdb_base_to_json(const union ovsdb_atom *atom,\n+                   const struct ovsdb_base_type *base,\n+                   bool use_row_names)\n+{\n+    if (!use_row_names\n+        || base->type != OVSDB_TYPE_UUID\n+        || !base->u.uuid.refTableName) {\n+        return ovsdb_atom_to_json(atom, base->type);\n+    } else {\n+        return json_array_create_2(\n+            json_string_create(\"named-uuid\"),\n+            json_string_create_nocopy(ovsdb_data_row_name(&atom->uuid)));\n+    }\n+}\n+\n+static struct json *\n+ovsdb_datum_to_json__(const struct ovsdb_datum *datum,\n+                      const struct ovsdb_type *type,\n+                      bool use_row_names)\n {\n     if (ovsdb_type_is_map(type)) {\n         struct json **elems;\n@@ -1375,26 +1386,49 @@ ovsdb_datum_to_json(const struct ovsdb_datum *datum,\n         elems = xmalloc(datum->n * sizeof *elems);\n         for (i = 0; i < datum->n; i++) {\n             elems[i] = json_array_create_2(\n-                ovsdb_atom_to_json(&datum->keys[i], type->key.type),\n-                ovsdb_atom_to_json(&datum->values[i], type->value.type));\n+                ovsdb_base_to_json(&datum->keys[i], &type->key,\n+                                   use_row_names),\n+                ovsdb_base_to_json(&datum->values[i], &type->value,\n+                                   use_row_names));\n         }\n \n         return wrap_json(\"map\", json_array_create(elems, datum->n));\n     } else if (datum->n == 1) {\n-        return ovsdb_atom_to_json(&datum->keys[0], type->key.type);\n+        return ovsdb_base_to_json(&datum->keys[0], &type->key, use_row_names);\n     } else {\n         struct json **elems;\n         size_t i;\n \n         elems = xmalloc(datum->n * sizeof *elems);\n         for (i = 0; i < datum->n; i++) {\n-            elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key.type);\n+            elems[i] = ovsdb_base_to_json(&datum->keys[i], &type->key,\n+                                          use_row_names);\n         }\n \n         return wrap_json(\"set\", json_array_create(elems, datum->n));\n     }\n }\n \n+/* Converts 'datum', of the specified 'type', to JSON format, and returns the\n+ * JSON.  The caller is responsible for freeing the returned JSON.\n+ *\n+ * 'type' constraints on datum->n are ignored.\n+ *\n+ * Refer to RFC 7047 for the format of the JSON that this function produces. */\n+struct json *\n+ovsdb_datum_to_json(const struct ovsdb_datum *datum,\n+                    const struct ovsdb_type *type)\n+{\n+    return ovsdb_datum_to_json__(datum, type, false);\n+}\n+\n+struct json *\n+ovsdb_datum_to_json_with_row_names(const struct ovsdb_datum *datum,\n+                                   const struct ovsdb_type *type)\n+{\n+    return ovsdb_datum_to_json__(datum, type, true);\n+}\n+\n static const char *\n skip_spaces(const char *p)\n {\n@@ -2180,3 +2214,19 @@ ovsdb_atom_range_check_size(int64_t range_start, int64_t range_end)\n     }\n     return NULL;\n }\n+\f\n+char *\n+ovsdb_data_row_name(const struct uuid *uuid)\n+{\n+    char *name;\n+    char *p;\n+\n+    name = xasprintf(\"row\"UUID_FMT, UUID_ARGS(uuid));\n+    for (p = name; *p != '\\0'; p++) {\n+        if (*p == '-') {\n+            *p = '_';\n+        }\n+    }\n+\n+    return name;\n+}\ndiff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h\nindex 835d0a8a14e7..14716912ea49 100644\n--- a/lib/ovsdb-data.h\n+++ b/lib/ovsdb-data.h\n@@ -251,6 +251,11 @@ void ovsdb_datum_add_unsafe(struct ovsdb_datum *,\n                             const struct ovsdb_type *,\n                             const union ovsdb_atom *range_end_atom);\n \n+/* Transactions with named-uuid row names. */\n+struct json *ovsdb_datum_to_json_with_row_names(const struct ovsdb_datum *,\n+                                                const struct ovsdb_type *);\n+char *ovsdb_data_row_name(const struct uuid *);\n+\n /* Type checking. */\n static inline bool\n ovsdb_datum_conforms_to_type(const struct ovsdb_datum *datum,\ndiff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c\nindex 6cb1404735d1..949c15e11940 100644\n--- a/lib/ovsdb-idl.c\n+++ b/lib/ovsdb-idl.c\n@@ -3178,22 +3178,6 @@ where_uuid_equals(const struct uuid *uuid)\n                         xasprintf(UUID_FMT, UUID_ARGS(uuid))))));\n }\n \n-static char *\n-uuid_name_from_uuid(const struct uuid *uuid)\n-{\n-    char *name;\n-    char *p;\n-\n-    name = xasprintf(\"row\"UUID_FMT, UUID_ARGS(uuid));\n-    for (p = name; *p != '\\0'; p++) {\n-        if (*p == '-') {\n-            *p = '_';\n-        }\n-    }\n-\n-    return name;\n-}\n-\n static const struct ovsdb_idl_row *\n ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid)\n {\n@@ -3228,7 +3212,7 @@ substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)\n \n                 return json_array_create_2(\n                     json_string_create(\"named-uuid\"),\n-                    json_string_create_nocopy(uuid_name_from_uuid(&uuid)));\n+                    json_string_create_nocopy(ovsdb_data_row_name(&uuid)));\n             }\n         }\n \n@@ -3643,7 +3627,7 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)\n \n                 json_object_put(op, \"uuid-name\",\n                                 json_string_create_nocopy(\n-                                    uuid_name_from_uuid(&row->uuid)));\n+                                    ovsdb_data_row_name(&row->uuid)));\n \n                 insert = xmalloc(sizeof *insert);\n                 insert->dummy = row->uuid;\ndiff --git a/manpages.mk b/manpages.mk\nindex e69de29bb2d1..7d6a507e0039 100644\n--- a/manpages.mk\n+++ b/manpages.mk\n@@ -0,0 +1,299 @@\n+# Generated automatically -- do not modify!    -*- buffer-read-only: t -*-\n+\n+ovn/utilities/ovn-detrace.1: \\\n+\tovn/utilities/ovn-detrace.1.in \\\n+\tlib/common-syn.man \\\n+\tlib/common.man\n+ovn/utilities/ovn-detrace.1.in:\n+lib/common-syn.man:\n+lib/common.man:\n+\n+ovn/utilities/ovn-sbctl.8: \\\n+\tovn/utilities/ovn-sbctl.8.in \\\n+\tlib/common.man \\\n+\tlib/db-ctl-base.man \\\n+\tlib/ssl-bootstrap.man \\\n+\tlib/ssl-peer-ca-cert.man \\\n+\tlib/ssl.man \\\n+\tlib/table.man \\\n+\tlib/vlog.man\n+ovn/utilities/ovn-sbctl.8.in:\n+lib/common.man:\n+lib/db-ctl-base.man:\n+lib/ssl-bootstrap.man:\n+lib/ssl-peer-ca-cert.man:\n+lib/ssl.man:\n+lib/table.man:\n+lib/vlog.man:\n+\n+ovsdb/ovsdb-client.1: \\\n+\tovsdb/ovsdb-client.1.in \\\n+\tlib/common-syn.man \\\n+\tlib/common.man \\\n+\tlib/daemon-syn.man \\\n+\tlib/daemon.man \\\n+\tlib/ssl-bootstrap-syn.man \\\n+\tlib/ssl-bootstrap.man \\\n+\tlib/ssl-connect-syn.man \\\n+\tlib/ssl-connect.man \\\n+\tlib/ssl-syn.man \\\n+\tlib/ssl.man \\\n+\tlib/table.man \\\n+\tlib/vlog-syn.man \\\n+\tlib/vlog.man \\\n+\tovsdb/ovsdb-schemas.man\n+ovsdb/ovsdb-client.1.in:\n+lib/common-syn.man:\n+lib/common.man:\n+lib/daemon-syn.man:\n+lib/daemon.man:\n+lib/ssl-bootstrap-syn.man:\n+lib/ssl-bootstrap.man:\n+lib/ssl-connect-syn.man:\n+lib/ssl-connect.man:\n+lib/ssl-syn.man:\n+lib/ssl.man:\n+lib/table.man:\n+lib/vlog-syn.man:\n+lib/vlog.man:\n+ovsdb/ovsdb-schemas.man:\n+\n+ovsdb/ovsdb-server.1: \\\n+\tovsdb/ovsdb-server.1.in \\\n+\tlib/common-syn.man \\\n+\tlib/common.man \\\n+\tlib/coverage-unixctl.man \\\n+\tlib/daemon-syn.man \\\n+\tlib/daemon.man \\\n+\tlib/memory-unixctl.man \\\n+\tlib/service-syn.man \\\n+\tlib/service.man \\\n+\tlib/ssl-bootstrap-syn.man \\\n+\tlib/ssl-bootstrap.man \\\n+\tlib/ssl-connect-syn.man \\\n+\tlib/ssl-connect.man \\\n+\tlib/ssl-peer-ca-cert-syn.man \\\n+\tlib/ssl-peer-ca-cert.man \\\n+\tlib/ssl-syn.man \\\n+\tlib/ssl.man \\\n+\tlib/unixctl-syn.man \\\n+\tlib/unixctl.man \\\n+\tlib/vlog-syn.man \\\n+\tlib/vlog-unixctl.man \\\n+\tlib/vlog.man\n+ovsdb/ovsdb-server.1.in:\n+lib/common-syn.man:\n+lib/common.man:\n+lib/coverage-unixctl.man:\n+lib/daemon-syn.man:\n+lib/daemon.man:\n+lib/memory-unixctl.man:\n+lib/service-syn.man:\n+lib/service.man:\n+lib/ssl-bootstrap-syn.man:\n+lib/ssl-bootstrap.man:\n+lib/ssl-connect-syn.man:\n+lib/ssl-connect.man:\n+lib/ssl-peer-ca-cert-syn.man:\n+lib/ssl-peer-ca-cert.man:\n+lib/ssl-syn.man:\n+lib/ssl.man:\n+lib/unixctl-syn.man:\n+lib/unixctl.man:\n+lib/vlog-syn.man:\n+lib/vlog-unixctl.man:\n+lib/vlog.man:\n+\n+ovsdb/ovsdb-tool.1: \\\n+\tovsdb/ovsdb-tool.1.in \\\n+\tlib/common-syn.man \\\n+\tlib/common.man \\\n+\tlib/vlog-syn.man \\\n+\tlib/vlog.man \\\n+\tovsdb/ovsdb-schemas.man\n+ovsdb/ovsdb-tool.1.in:\n+lib/common-syn.man:\n+lib/common.man:\n+lib/vlog-syn.man:\n+lib/vlog.man:\n+ovsdb/ovsdb-schemas.man:\n+\n+utilities/bugtool/ovs-bugtool.8: \\\n+\tutilities/bugtool/ovs-bugtool.8.in\n+utilities/bugtool/ovs-bugtool.8.in:\n+\n+utilities/ovs-appctl.8: \\\n+\tutilities/ovs-appctl.8.in \\\n+\tlib/common.man\n+utilities/ovs-appctl.8.in:\n+lib/common.man:\n+\n+utilities/ovs-dpctl-top.8: \\\n+\tutilities/ovs-dpctl-top.8.in\n+utilities/ovs-dpctl-top.8.in:\n+\n+utilities/ovs-dpctl.8: \\\n+\tutilities/ovs-dpctl.8.in \\\n+\tlib/common.man \\\n+\tlib/dpctl.man \\\n+\tlib/vlog.man\n+utilities/ovs-dpctl.8.in:\n+lib/common.man:\n+lib/dpctl.man:\n+lib/vlog.man:\n+\n+utilities/ovs-l3ping.8: \\\n+\tutilities/ovs-l3ping.8.in \\\n+\tlib/common-syn.man \\\n+\tlib/common.man\n+utilities/ovs-l3ping.8.in:\n+lib/common-syn.man:\n+lib/common.man:\n+\n+utilities/ovs-ofctl.8: \\\n+\tutilities/ovs-ofctl.8.in \\\n+\tlib/colors.man \\\n+\tlib/common.man \\\n+\tlib/daemon.man \\\n+\tlib/ofp-version.man \\\n+\tlib/ssl.man \\\n+\tlib/unixctl.man \\\n+\tlib/vconn-active.man \\\n+\tlib/vlog.man\n+utilities/ovs-ofctl.8.in:\n+lib/colors.man:\n+lib/common.man:\n+lib/daemon.man:\n+lib/ofp-version.man:\n+lib/ssl.man:\n+lib/unixctl.man:\n+lib/vconn-active.man:\n+lib/vlog.man:\n+\n+utilities/ovs-pcap.1: \\\n+\tutilities/ovs-pcap.1.in \\\n+\tlib/common-syn.man \\\n+\tlib/common.man\n+utilities/ovs-pcap.1.in:\n+lib/common-syn.man:\n+lib/common.man:\n+\n+utilities/ovs-pki.8: \\\n+\tutilities/ovs-pki.8.in\n+utilities/ovs-pki.8.in:\n+\n+utilities/ovs-tcpdump.8: \\\n+\tutilities/ovs-tcpdump.8.in \\\n+\tlib/common.man\n+utilities/ovs-tcpdump.8.in:\n+lib/common.man:\n+\n+utilities/ovs-tcpundump.1: \\\n+\tutilities/ovs-tcpundump.1.in \\\n+\tlib/common-syn.man \\\n+\tlib/common.man\n+utilities/ovs-tcpundump.1.in:\n+lib/common-syn.man:\n+lib/common.man:\n+\n+utilities/ovs-testcontroller.8: \\\n+\tutilities/ovs-testcontroller.8.in \\\n+\tlib/common.man \\\n+\tlib/daemon.man \\\n+\tlib/ofp-version.man \\\n+\tlib/ssl-peer-ca-cert.man \\\n+\tlib/ssl.man \\\n+\tlib/unixctl.man \\\n+\tlib/vconn-active.man \\\n+\tlib/vconn-passive.man \\\n+\tlib/vlog.man\n+utilities/ovs-testcontroller.8.in:\n+lib/common.man:\n+lib/daemon.man:\n+lib/ofp-version.man:\n+lib/ssl-peer-ca-cert.man:\n+lib/ssl.man:\n+lib/unixctl.man:\n+lib/vconn-active.man:\n+lib/vconn-passive.man:\n+lib/vlog.man:\n+\n+utilities/ovs-vlan-bug-workaround.8: \\\n+\tutilities/ovs-vlan-bug-workaround.8.in \\\n+\tlib/common.man \\\n+\tutilities/ovs-vlan-bugs.man\n+utilities/ovs-vlan-bug-workaround.8.in:\n+lib/common.man:\n+utilities/ovs-vlan-bugs.man:\n+\n+utilities/ovs-vsctl.8: \\\n+\tutilities/ovs-vsctl.8.in \\\n+\tlib/common.man \\\n+\tlib/db-ctl-base.man \\\n+\tlib/ssl-bootstrap.man \\\n+\tlib/ssl-peer-ca-cert.man \\\n+\tlib/ssl.man \\\n+\tlib/table.man \\\n+\tlib/vconn-active.man \\\n+\tlib/vconn-passive.man \\\n+\tlib/vlog.man\n+utilities/ovs-vsctl.8.in:\n+lib/common.man:\n+lib/db-ctl-base.man:\n+lib/ssl-bootstrap.man:\n+lib/ssl-peer-ca-cert.man:\n+lib/ssl.man:\n+lib/table.man:\n+lib/vconn-active.man:\n+lib/vconn-passive.man:\n+lib/vlog.man:\n+\n+vswitchd/ovs-vswitchd.8: \\\n+\tvswitchd/ovs-vswitchd.8.in \\\n+\tlib/common.man \\\n+\tlib/coverage-unixctl.man \\\n+\tlib/daemon.man \\\n+\tlib/dpctl.man \\\n+\tlib/memory-unixctl.man \\\n+\tlib/service.man \\\n+\tlib/ssl-bootstrap.man \\\n+\tlib/ssl.man \\\n+\tlib/unixctl.man \\\n+\tlib/vlog-unixctl.man \\\n+\tlib/vlog.man \\\n+\tofproto/ofproto-dpif-unixctl.man \\\n+\tofproto/ofproto-tnl-unixctl.man \\\n+\tofproto/ofproto-unixctl.man\n+vswitchd/ovs-vswitchd.8.in:\n+lib/common.man:\n+lib/coverage-unixctl.man:\n+lib/daemon.man:\n+lib/dpctl.man:\n+lib/memory-unixctl.man:\n+lib/service.man:\n+lib/ssl-bootstrap.man:\n+lib/ssl.man:\n+lib/unixctl.man:\n+lib/vlog-unixctl.man:\n+lib/vlog.man:\n+ofproto/ofproto-dpif-unixctl.man:\n+ofproto/ofproto-tnl-unixctl.man:\n+ofproto/ofproto-unixctl.man:\n+\n+vtep/vtep-ctl.8: \\\n+\tvtep/vtep-ctl.8.in \\\n+\tlib/common.man \\\n+\tlib/db-ctl-base.man \\\n+\tlib/ssl-bootstrap.man \\\n+\tlib/ssl-peer-ca-cert.man \\\n+\tlib/ssl.man \\\n+\tlib/table.man \\\n+\tlib/vlog.man\n+vtep/vtep-ctl.8.in:\n+lib/common.man:\n+lib/db-ctl-base.man:\n+lib/ssl-bootstrap.man:\n+lib/ssl-peer-ca-cert.man:\n+lib/ssl.man:\n+lib/table.man:\n+lib/vlog.man:\ndiff --git a/ovsdb/log.c b/ovsdb/log.c\nindex adc14761cd8a..7f05fb083246 100644\n--- a/ovsdb/log.c\n+++ b/ovsdb/log.c\n@@ -116,7 +116,13 @@ ovsdb_log_open(const char *name, const char *magic,\n #ifdef _WIN32\n     flags = flags | O_BINARY;\n #endif\n-    fd = open(name, flags, 0666);\n+    /* Special case for /dev/stdin to make it work even if the operating system\n+     * doesn't support it under that name. */\n+    if (!strcmp(name, \"/dev/stdin\") && open_mode == OVSDB_LOG_READ_ONLY) {\n+        fd = dup(STDIN_FILENO);\n+    } else {\n+        fd = open(name, flags, 0666);\n+    }\n     if (fd < 0) {\n         const char *op = (open_mode == OVSDB_LOG_CREATE_EXCL ? \"create\"\n             : open_mode == OVSDB_LOG_CREATE ? \"create or open\"\ndiff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in\nindex 26f007258c09..cd17467147da 100644\n--- a/ovsdb/ovsdb-client.1.in\n+++ b/ovsdb/ovsdb-client.1.in\n@@ -36,6 +36,9 @@ ovsdb\\-client \\- command-line interface to \\fBovsdb-server\\fR(1)\n \\fBovsdb\\-client \\fR[\\fIoptions\\fR]\n \\fBbackup\\fI \\fR[\\fIserver\\fR] \\fR[\\fIdatabase\\fR] > \\fIsnapshot\\fR\n .br\n+\\fBovsdb\\-client \\fR[\\fIoptions\\fR] [\\fB\\-\\-force\\fR]\n+\\fBrestore\\fI \\fR[\\fIserver\\fR] \\fR[\\fIdatabase\\fR] < \\fIsnapshot\\fR\n+.br\n \\fBovsdb\\-client \\fR[\\fIoptions\\fR] \\fBmonitor\\fI \\fR[\\fIserver\\fR] \\fR[\\fIdatabase\\fR] \\fItable\\fR\n [\\fIcolumn\\fR[\\fB,\\fIcolumn\\fR]...]...\n .br\n@@ -44,7 +47,6 @@ ovsdb\\-client \\- command-line interface to \\fBovsdb-server\\fR(1)\n \\fBovsdb\\-client \\fR[\\fIoptions\\fR] \\fBmonitor\\-cond\\fI \\fR[\\fIserver\\fR] \\fR[\\fIdatabase\\fR] \\fIconditions\n \\fItable\\fR [\\fIcolumn\\fR[\\fB,\\fIcolumn\\fR]...]...\n .IP \"Testing Commands:\"\n-.br\n \\fBovsdb\\-client \\fR[\\fIoptions\\fR] \\fBlock\\fI \\fR[\\fIserver\\fR] \\fIlock\\fR\n .br\n \\fBovsdb\\-client \\fR[\\fIoptions\\fR] \\fBsteal\\fI \\fR[\\fIserver\\fR] \\fIlock\\fR\n@@ -203,6 +205,30 @@ database is in use.\n The output does not include ephemeral columns, which by design do not\n survive across restarts of \\fBovsdb\\-server\\fR.\n .\n+.IP \"[\\fB\\-\\-force\\fR] \\fBrestore\\fI \\fR[\\fIserver\\fR] \\fR[\\fIdatabase\\fR] < \\fIsnapshot\\fR\"\n+Reads \\fIsnapshot\\fR, which must be in the format used for OVSDB\n+standalone and active-backup databases.  Then, connects to\n+\\fIserver\\fR, verifies that \\fIdatabase\\fR and \\fIsnapshot\\fR have the\n+same schema, then deletes all of the data in \\fIdatabase\\fR and\n+replaces it by \\fIsnapshot\\fR.  The replacement happens atomically, in a\n+single transaction.\n+.IP\n+UUIDs for rows in the restored database will differ from those in\n+\\fIsnapshot\\fR, because the OVSDB protocol does not allow clients to\n+specify row UUIDs.  Another way to restore a database,\n+which does also restore row UUIDs, is to stop\n+the server or servers, replace the database file by the snapshot, then\n+restart the database.  Either way, ephemeral columns are not restored,\n+since by design they do not survive across restarts of\n+\\fBovsdb\\-server\\fR.\n+.IP\n+Normally \\fBrestore\\fR exits with a failure if \\fBsnapshot\\fR and the\n+server's database have different schemas.  In such a case, it is a\n+good idea to convert the database to the new schema before restoring,\n+e.g. with \\fBovsdb\\-client convert\\fR.  Use \\fB\\-\\-force\\fR to proceed\n+regardless of schema differences even though the restore might fail\n+with an error or succeed with surprising results.\n+.\n .IP \"\\fBmonitor\\fI \\fR[\\fIserver\\fR] \\fR[\\fIdatabase\\fR] \\fItable\\fR [\\fIcolumn\\fR[\\fB,\\fIcolumn\\fR]...]...\"\n .IQ \"\\fBmonitor\\-cond\\fI \\fR[\\fIserver\\fR] \\fR[\\fIdatabase\\fR] \\fIconditions\\fR \\fItable\\fR [\\fIcolumn\\fR[\\fB,\\fIcolumn\\fR]...]...\"\n Connects to \\fIserver\\fR and monitors the contents of rows that match conditions in\ndiff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c\nindex bfffc08effb0..056aa25c8983 100644\n--- a/ovsdb/ovsdb-client.c\n+++ b/ovsdb/ovsdb-client.c\n@@ -41,6 +41,7 @@\n #include \"ovsdb-data.h\"\n #include \"ovsdb-error.h\"\n #include \"poll-loop.h\"\n+#include \"row.h\"\n #include \"sort.h\"\n #include \"svec.h\"\n #include \"stream.h\"\n@@ -85,6 +86,9 @@ static bool timestamp;\n  * actually works.) */\n static int db_change_aware = -1;\n \n+/* --force: Ignore schema differences for \"restore\" command? */\n+static bool force;\n+\n /* Format for table output. */\n static struct table_style table_style = TABLE_STYLE_DEFAULT;\n \n@@ -196,6 +200,7 @@ parse_options(int argc, char *argv[])\n     enum {\n         OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,\n         OPT_TIMESTAMP,\n+        OPT_FORCE,\n         VLOG_OPTION_ENUMS,\n         DAEMON_OPTION_ENUMS,\n         TABLE_OPTION_ENUMS,\n@@ -207,6 +212,7 @@ parse_options(int argc, char *argv[])\n         {\"timestamp\", no_argument, NULL, OPT_TIMESTAMP},\n         {\"db-change-aware\", no_argument, &db_change_aware, 1},\n         {\"no-db-change-aware\", no_argument, &db_change_aware, 0},\n+        {\"force\", no_argument, NULL, OPT_FORCE},\n         VLOG_LONG_OPTIONS,\n         DAEMON_LONG_OPTIONS,\n #ifdef HAVE_OPENSSL\n@@ -249,6 +255,10 @@ parse_options(int argc, char *argv[])\n             timestamp = true;\n             break;\n \n+        case OPT_FORCE:\n+            force = true;\n+            break;\n+\n         case '?':\n             exit(EXIT_FAILURE);\n \n@@ -304,6 +314,8 @@ usage(void)\n            \"    dump contents of DATABASE on SERVER to stdout\\n\"\n            \"\\n  backup [SERVER] [DATABASE] > DB\\n\"\n            \"    dump database contents in the form of a database file\\n\"\n+           \"\\n  [--force] restore [SERVER] [DATABASE] < DB\\n\"\n+           \"    restore database contents from a database file\\n\"\n            \"\\n  lock [SERVER] LOCK\\n\"\n            \"    create or wait for LOCK in SERVER\\n\"\n            \"\\n  steal [SERVER] LOCK\\n\"\n@@ -1607,6 +1619,101 @@ do_backup(struct jsonrpc *rpc, const char *database,\n }\n \n static void\n+do_restore(struct jsonrpc *rpc, const char *database,\n+           int argc OVS_UNUSED, char *argv[] OVS_UNUSED)\n+{\n+    if (isatty(STDIN_FILENO)) {\n+        ovs_fatal(0, \"not reading backup from a terminal; \"\n+                  \"please redirect stdin from a file\");\n+    }\n+\n+    struct ovsdb *backup;\n+    check_ovsdb_error(ovsdb_file_open(\"/dev/stdin\", true, &backup, NULL));\n+\n+    const struct ovsdb_schema *schema = backup->schema;\n+    struct ovsdb_schema *schema2 = fetch_schema(rpc, database);\n+    if (!ovsdb_schema_equal(schema, schema2)) {\n+        struct ds s = DS_EMPTY_INITIALIZER;\n+        if (strcmp(schema->version, schema2->version)) {\n+            ds_put_format(&s, \"backup schema has version \\\"%s\\\" but \"\n+                          \"database schema has version \\\"%s\\\"\",\n+                          schema->version, schema2->version);\n+        } else {\n+            ds_put_format(&s, \"backup schema and database schema are \"\n+                          \"both version %s but still differ\",\n+                          schema->version);\n+        }\n+        if (!force) {\n+            ovs_fatal(0, \"%s (use --force to override differences, or \"\n+                      \"\\\"ovsdb-client convert\\\" to change the schema)\",\n+                      ds_cstr(&s));\n+        }\n+        VLOG_INFO(\"%s\", ds_cstr(&s));\n+        ds_destroy(&s);\n+    }\n+\n+    struct json *txn = json_array_create_empty();\n+    json_array_add(txn, json_string_create(schema->name));\n+    struct shash_node *node;\n+    SHASH_FOR_EACH (node, &backup->tables) {\n+        const char *table_name = node->name;\n+        struct ovsdb_table *table = node->data;\n+\n+        struct json *del_op = json_object_create();\n+        json_object_put_string(del_op, \"op\", \"delete\");\n+        json_object_put_string(del_op, \"table\", table_name);\n+        json_object_put(del_op, \"where\", json_array_create_empty());\n+        json_array_add(txn, del_op);\n+\n+        const struct ovsdb_row *row;\n+        HMAP_FOR_EACH (row, hmap_node, &table->rows) {\n+            struct json *ins_op = json_object_create();\n+            json_object_put_string(ins_op, \"op\", \"insert\");\n+            json_object_put_string(ins_op, \"table\", table_name);\n+            json_object_put(ins_op, \"uuid-name\",\n+                            json_string_create_nocopy(\n+                                ovsdb_data_row_name(ovsdb_row_get_uuid(row))));\n+            struct json *row_json = json_object_create();\n+            json_object_put(ins_op, \"row\", row_json);\n+\n+            struct shash_node *node2;\n+            SHASH_FOR_EACH (node2, &table->schema->columns) {\n+                const struct ovsdb_column *column = node2->data;\n+                const struct ovsdb_datum *datum = &row->fields[column->index];\n+                const struct ovsdb_type *type = &column->type;\n+                if (column->persistent\n+                    && column->index >= OVSDB_N_STD_COLUMNS\n+                    && !ovsdb_datum_is_default(datum, type)) {\n+                    struct json *value = ovsdb_datum_to_json_with_row_names(\n+                        datum, type);\n+                    json_object_put(row_json, column->name, value);\n+                }\n+            }\n+            json_array_add(txn, ins_op);\n+        }\n+    }\n+    struct jsonrpc_msg *rq = jsonrpc_create_request(\"transact\", txn, NULL);\n+    struct jsonrpc_msg *reply;\n+    check_txn(jsonrpc_transact_block(rpc, rq, &reply), &reply);\n+    if (reply->result->type != JSON_ARRAY) {\n+        ovs_fatal(0, \"result is not array\");\n+    }\n+    for (size_t i = 0; i < json_array(reply->result)->n; i++) {\n+        struct json *json = json_array(reply->result)->elems[i];\n+        if (json->type != JSON_OBJECT) {\n+            ovs_fatal(0, \"result array element is not object\");\n+        }\n+        struct shash *object = json_object(json);\n+        if (shash_find(object, \"error\")) {\n+            ovs_fatal(0, \"server returned error reply: %s\",\n+                      json_to_string(json, JSSF_SORT));\n+        }\n+    }\n+    jsonrpc_msg_destroy(reply);\n+}\n+\n+\n+static void\n do_help(struct jsonrpc *rpc OVS_UNUSED, const char *database OVS_UNUSED,\n         int argc OVS_UNUSED, char *argv[] OVS_UNUSED)\n {\n@@ -1821,6 +1928,7 @@ static const struct ovsdb_client_command all_commands[] = {\n     { \"needs-conversion\",   NEED_RPC,      1, 1,       do_needs_conversion },\n     { \"dump\",               NEED_DATABASE, 0, INT_MAX, do_dump },\n     { \"backup\",             NEED_DATABASE, 0, 0,       do_backup },\n+    { \"restore\",            NEED_DATABASE, 0, 0,       do_restore },\n     { \"lock\",               NEED_RPC,      1, 1,       do_lock_create },\n     { \"steal\",              NEED_RPC,      1, 1,       do_lock_steal },\n     { \"unlock\",             NEED_RPC,      1, 1,       do_lock_unlock },\ndiff --git a/ovsdb/ovsdb.7.xml b/ovsdb/ovsdb.7.xml\nindex 5461f252a03a..8169120c88f2 100644\n--- a/ovsdb/ovsdb.7.xml\n+++ b/ovsdb/ovsdb.7.xml\n@@ -410,9 +410,16 @@\n   </p>\n \n   <p>\n-    To restore from a backup, stop the database server or servers, overwrite\n-    the database file with the backup (e.g. with <code>cp</code>), and then\n-    restart the servers.\n+    Multiple options are also available when the time comes to restore a\n+    database from a backup.  One option is to stop the database server or\n+    servers, overwrite the database file with the backup (e.g. with\n+    <code>cp</code>), and then restart the servers.  Another way is to use\n+    <code>ovsdb-client restore</code>, which connects to a running database\n+    server and replaces the data in one of its databases by a provided\n+    snapshot.  Using <code>ovsdb-client restore</code> has the disadvantage\n+    that UUIDs of rows in the restored database will differ from those in the\n+    snapshot, because the OVSDB protocol does not allow clients to specify row\n+    UUIDs.\n   </p>\n \n   <p>\n","prefixes":["ovs-dev","RFC","49/52"]}