summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Pfaff <blp@ovn.org>2017-12-22 11:01:52 -0800
committerBen Pfaff <blp@ovn.org>2017-12-22 11:01:52 -0800
commit838f9c31043c35a321d2c7cc0042300191ebef9a (patch)
tree3974fbfe1e1f613f1b18509daa75c2ce5cc76c63
parentfe0fb88551b4cc5b4bee6814f1027f78c451daa2 (diff)
downloadopenvswitch-838f9c31043c35a321d2c7cc0042300191ebef9a.tar.gz
ovsdb-client: Add new "query" command.
This is mostly for symmetry with ovsdb-tool, but it might come in handy from time to time. Signed-off-by: Ben Pfaff <blp@ovn.org> Acked-by: Justin Pettit <jpettit@ovn.org>
-rw-r--r--NEWS2
-rw-r--r--ovsdb/ovsdb-client.1.in12
-rw-r--r--ovsdb/ovsdb-client.c67
-rw-r--r--tests/ovsdb-client.at53
4 files changed, 121 insertions, 13 deletions
diff --git a/NEWS b/NEWS
index 59ad6cb10..9d0ffab50 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.
* ovsdb-tool: New "db-name" and "schema-name" commands.
- OVN:
diff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in
index 7ab452f6d..203d70cfa 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 \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,16 @@ 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 \fR[\fIserver\fR] \fItransaction\fR"
+This commands acts like a read-only version of \fBtransact\fR.
+It 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 349bc55d4..974cc0cbc 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 0aa20df57..ea4755e8a 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