[ovs-dev] [PATCH v3 7/8] ovsdb-client: Add new "query" command.

Ben Pfaff blp at ovn.org
Wed Dec 13 23:10:18 UTC 2017


This is mostly for symmetry with ovsdb-tool, but it might come in handy
from time to time.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 NEWS                    |  2 +-
 ovsdb/ovsdb-client.1.in | 11 ++++++++
 ovsdb/ovsdb-client.c    | 67 ++++++++++++++++++++++++++++++++++++++++---------
 tests/ovsdb-client.at   | 53 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 120 insertions(+), 13 deletions(-)

diff --git a/NEWS b/NEWS
index 85bae499b25e..85ad5cfa322e 100644
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,7 @@ Post-v2.8.0
      * New high-level documentation in ovsdb(7).
      * New file format documentation for developers in ovsdb(5).
      * Protocol documentation moved from ovsdb-server(1) to ovsdb-server(7).
-     * ovsdb-client: New "get-schema-cksum" command.
+     * ovsdb-client: New "get-schema-cksum" and "query" commands.
      * ovsdb-client: New "backup" and "restore" commands.
    - OVN:
      * The "requested-chassis" option for a logical switch port now accepts a
diff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in
index 30de9c536600..70529a1e6600 100644
--- a/ovsdb/ovsdb-client.1.in
+++ b/ovsdb/ovsdb-client.1.in
@@ -27,6 +27,8 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
 .IP "Data Management Commands:"
 \fBovsdb\-client \fR[\fIoptions\fR] \fBtransact\fI \fR[\fIserver\fR] \fItransaction\fR
 .br
+\fBovsdb\-client \fR[\fIoptions\fR] \fBquery\fI \fR[\fIserver\fR] \fItransaction\fR
+.br
 \fBovsdb\-client \fR[\fIoptions\fR] \fBdump\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]\fR [\fItable\fR
 [\fIcolumn\fR...]]
 .br
@@ -137,6 +139,15 @@ which must be a JSON array appropriate for use as the \fBparams\fR to
 a JSON-RPC \fBtransact\fR request, and prints the received reply on
 stdout.
 .
+.IP "\fBquery\fI \fR[\fIserver\fR] \fItransaction\fR"
+Connects to \fIserver\fR, sends it the specified \fItransaction\fR,
+which must be a JSON array appropriate for use as the \fBparams\fR to
+a JSON-RPC \fBtransact\fR request, and prints the received reply on
+stdout.  To ensure that the transaction does not modify the database,
+this command appends an \fBabort\fR operation to the set of operations
+included in \fItransaction\fR before sending it to the database, and
+then removes the \fBabort\fR result from the reply (if it is present).
+.
 .IP "\fBdump\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]\fR [\fItable \fR[\fIcolumn\fR...]]"
 Connects to \fIserver\fR, retrieves all of the data in \fIdatabase\fR,
 and prints it on stdout as a series of tables. If \fItable\fR is
diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c
index 1af19d5dbc28..55d7d8b430a5 100644
--- a/ovsdb/ovsdb-client.c
+++ b/ovsdb/ovsdb-client.c
@@ -271,8 +271,11 @@ usage(void)
            "\n  list-columns [SERVER] [DATABASE] [TABLE]\n"
            "    list columns in TABLE (or all tables) in DATABASE on SERVER\n"
            "\n  transact [SERVER] TRANSACTION\n"
-           "    run TRANSACTION (a JSON array of operations) on SERVER\n"
+           "    run TRANSACTION (params for \"transact\" request) on SERVER\n"
            "    and print the results as JSON on stdout\n"
+           "\n  query [SERVER] TRANSACTION\n"
+           "    run TRANSACTION (params for \"transact\" request) on SERVER,\n"
+           "    as read-only, and print the results as JSON on stdout\n"
            "\n  monitor [SERVER] [DATABASE] TABLE [COLUMN,...]...\n"
            "    monitor contents of COLUMNs in TABLE in DATABASE on SERVER.\n"
            "    COLUMNs may include !initial, !insert, !delete, !modify\n"
@@ -370,7 +373,7 @@ static void
 print_json(struct json *json)
 {
     char *string = json_to_string(json, table_style.json_flags);
-    fputs(string, stdout);
+    puts(string);
     free(string);
 }
 
@@ -536,20 +539,61 @@ do_list_columns(struct jsonrpc *rpc, const char *database,
     table_destroy(&t);
 }
 
-static void
-do_transact(struct jsonrpc *rpc, const char *database OVS_UNUSED,
-            int argc OVS_UNUSED, char *argv[])
+static struct json *
+do_transact__(struct jsonrpc *rpc, struct json *transaction)
 {
     struct jsonrpc_msg *request, *reply;
-    struct json *transaction;
-
-    transaction = parse_json(argv[0]);
 
     request = jsonrpc_create_request("transact", transaction, NULL);
     check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply);
-    print_json(reply->result);
-    putchar('\n');
+    struct json *result = json_clone(reply->result);
     jsonrpc_msg_destroy(reply);
+
+    return result;
+}
+
+static void
+do_transact(struct jsonrpc *rpc, const char *database OVS_UNUSED,
+            int argc OVS_UNUSED, char *argv[])
+{
+    print_and_free_json(do_transact__(rpc, parse_json(argv[0])));
+}
+
+static void
+do_query(struct jsonrpc *rpc, const char *database OVS_UNUSED,
+         int argc OVS_UNUSED, char *argv[])
+{
+    struct json *transaction = parse_json(argv[0]);
+
+    if (transaction->type != JSON_ARRAY) {
+        ovs_fatal(0, "not a valid OVSDB query");
+    }
+
+    /* Append an "abort" operation to the query. */
+    struct json *abort_op = json_object_create();
+    json_object_put_string(abort_op, "op", "abort");
+    json_array_add(transaction, abort_op);
+    size_t abort_idx = transaction->u.array.n - 2;
+
+    /* Run query. */
+    struct json *result = do_transact__(rpc, transaction);
+
+    /* If the "abort" operation ended the transaction, remove its result. */
+    if (result->type == JSON_ARRAY
+        && result->u.array.n == abort_idx + 1
+        && result->u.array.elems[abort_idx]->type == JSON_OBJECT) {
+        struct json *op_result = result->u.array.elems[abort_idx];
+        struct json *error = shash_find_data(json_object(op_result), "error");
+        if (error
+            && error->type == JSON_STRING
+            && !strcmp(json_string(error), "aborted")) {
+            result->u.array.n--;
+            json_destroy(op_result);
+        }
+    }
+
+    /* Print the result. */
+    print_and_free_json(result);
 }
 
 /* "monitor" command. */
@@ -1763,7 +1807,6 @@ do_lock(struct jsonrpc *rpc, const char *method, const char *lock)
         } else if (msg->type == JSONRPC_REPLY
                    && json_equal(msg->id, request_id)) {
             print_json(msg->result);
-            putchar('\n');
             fflush(stdout);
             enable_lock_request = true;
             json_destroy(request_id);
@@ -1772,7 +1815,6 @@ do_lock(struct jsonrpc *rpc, const char *method, const char *lock)
         } else if (msg->type == JSONRPC_NOTIFY) {
             puts(msg->method);
             print_json(msg->params);
-            putchar('\n');
             fflush(stdout);
         }
 
@@ -1831,6 +1873,7 @@ static const struct ovsdb_client_command all_commands[] = {
     { "list-tables",        NEED_DATABASE, 0, 0,       do_list_tables },
     { "list-columns",       NEED_DATABASE, 0, 1,       do_list_columns },
     { "transact",           NEED_RPC,      1, 1,       do_transact },
+    { "query",              NEED_RPC,      1, 1,       do_query },
     { "monitor",            NEED_DATABASE, 1, INT_MAX, do_monitor },
     { "monitor-cond",       NEED_DATABASE, 2, 3,       do_monitor_cond },
     { "dump",               NEED_DATABASE, 0, INT_MAX, do_dump },
diff --git a/tests/ovsdb-client.at b/tests/ovsdb-client.at
index 0aa20df576d6..ea4755e8a319 100644
--- a/tests/ovsdb-client.at
+++ b/tests/ovsdb-client.at
@@ -118,3 +118,56 @@ dnl Verify that the two dumps are the same.
 AT_CHECK([sort dump1], [0], [expout])
 
 AT_CLEANUP
+
+AT_SETUP([ovsdb-client query])
+AT_KEYWORDS([ovsdb client positive])
+
+on_exit 'kill `cat *.pid`'
+
+dnl Create a database.
+ordinal_schema > schema
+touch .db.~lock~
+AT_CHECK([ovsdb-tool create db schema])
+
+dnl Start the database server.
+AT_CHECK([ovsdb-server -vfile -vvlog:off --detach --no-chdir --pidfile --log-file --remote=punix:db.sock db], [0])
+AT_CAPTURE_FILE([ovsdb-server.log])
+
+dnl Put some data in the database.
+dnl Use "query" for some of them, which won't have any effect.
+AT_CHECK(
+  [[for txn in 'transact zero 0' \
+               'query one 1' \
+	       'transact two 2' \
+	       'query three 3' \
+	       'transact four 4' \
+	       'query five 5'
+    do
+      set -- $txn
+      ovsdb-client $1 '
+        ["ordinals",
+         {"op": "insert",
+          "table": "ordinals",
+          "row": {"name": "'$2'", "number": '$3'}},
+         {"op": "comment",
+          "comment": "add row for '"$pair"'"}]'
+    done | uuidfilt]], [0],
+[[[{"uuid":["uuid","<0>"]},{}]
+[{"uuid":["uuid","<1>"]},{}]
+[{"uuid":["uuid","<2>"]},{}]
+[{"uuid":["uuid","<3>"]},{}]
+[{"uuid":["uuid","<4>"]},{}]
+[{"uuid":["uuid","<5>"]},{}]
+]], [ignore])
+
+AT_CHECK([ovsdb-client -f csv dump | sort -t, -k 3 | uuidfilt], [0], [dnl
+ordinals table
+<0>,zero,0
+<1>,two,2
+<2>,four,4
+_uuid,name,number
+])
+
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+AT_CLEANUP
-- 
2.10.2



More information about the dev mailing list