Add monitor_cond method to ovsdb-client. Add unit tests. See ovsdb-client(1) man page for details.
Signed-off-by: Liran Schour <lir...@il.ibm.com> --- NEWS | 3 +- ovsdb/ovsdb-client.1.in | 37 +++++----- ovsdb/ovsdb-client.c | 74 ++++++++++++++------ tests/ovsdb-monitor.at | 178 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+), 40 deletions(-) diff --git a/NEWS b/NEWS index 4433329..21a6f63 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,8 @@ Post-v2.5.0 --------------------- - ovsdb-server: - * New "monitor2" and "update2" extensions to RFC 7047. + * New "monitor_cond" "monitor_cond_chane" and "update2" extensions to + RFC 7047. - OpenFlow: * OpenFlow 1.1+ OFPT_QUEUE_GET_CONFIG_REQUEST now supports OFPP_ANY. diff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in index 5d99f59..1d58db8 100644 --- a/ovsdb/ovsdb-client.1.in +++ b/ovsdb/ovsdb-client.1.in @@ -33,10 +33,8 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1) .br \fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR .br -\fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor2\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR -[\fIcolumn\fR[\fB,\fIcolumn\fR]...]... -.br -\fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor2\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR +\fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor-cond\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR +\fIconditions [\fIcolumn\fR[\fB,\fIcolumn\fR]...]... .br \fBovsdb\-client help\fR .IP "Output formatting options:" @@ -113,12 +111,12 @@ specified, only that table is retrieved. If at least one \fIcolumn\fR is specified, only those columns are retrieved. . .IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]..." -.IQ "\fBmonitor2\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]..." -Connects to \fIserver\fR and monitors the contents of \fItable\fR in -\fIdatabase\fR. By default, the initial contents of \fItable\fR are -printed, followed by each change as it occurs. If at least one -\fIcolumn\fR is specified, only those columns are monitored. The -following \fIcolumn\fR names have special meanings: +.IQ "\fBmonitor-cond\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR \fIconditions\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]..." +Connects to \fIserver\fR and monitors the contents of rows that match conditions in +\fItable\fR in \fIdatabase\fR. By default, the initial contents of \fItable\fR are +printed, followed by each change as it occurs. If conditions is missing or emtpy, +all rows will be monitored. If at least one \fIcolumn\fR is specified, only those +columns are monitored. The following \fIcolumn\fR names have special meanings: .RS .IP "\fB!initial\fR" Do not print the initial contents of the specified columns. @@ -136,16 +134,18 @@ each group. Whether multiple groups or only a single group is specified, any given column may only be mentioned once on the command line. .IP -If \fB\-\-detach\fR is used with \fBmonitor\fR or \fBmointor2\fR, then +\fBconditions\fR is a JSON array of <condition> as defined in RFC 7047 5.1 +with the following change: A condition can be either a 3-element JSON array +as deescribed in the RFC or a boolean value.. +.IP +If \fB\-\-detach\fR is used with \fBmonitor\fR or \fBmointor-cond\fR, then \fBovsdb\-client\fR detaches after it has successfully received and printed the initial contents of \fItable\fR. .IP The \fBmonitor\fR command uses RFC 7047 "monitor" method to open a monitor -session with the server. The \fBmonitor2\fR command uses RFC 7047 -extension "monitor2" method. See \fBovsdb\-server\fR(1) for details. -. +session with the server. The \fBmonitor-cond\fR command uses RFC 7047 +extension "monitor_cond" method. See \fBovsdb\-server\fR(1) for details. .IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR" -.IQ "\fBmonitor2\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR" Connects to \fIserver\fR and monitors the contents of all tables in \fIdatabase\fR. Prints initial values and all kinds of changes to all columns in the database. The \fB\-\-detach\fR option causes @@ -153,8 +153,7 @@ columns in the database. The \fB\-\-detach\fR option causes prints the initial database contents. .IP The \fBmonitor\fR command uses RFC 7047 "monitor" method to open a monitor -session with the server. The \fBmonitor2\fR command uses RFC 7047 -extension "monitor2" method. See \fBovsdb\-server\fR(1) for details. +session with the server. . .SH OPTIONS .SS "Output Formatting Options" @@ -165,13 +164,13 @@ The following options controlling output formatting: .so lib/table.man . .IP "\fB\-\-timestamp\fR" -For the \fBmonitor\fR and \fBmonitor2\fR commands, add a timestamp to each +For the \fBmonitor\fR and \fBmonitor-cond\fR commands, add a timestamp to each table update. Most output formats add the timestamp on a line of its own just above the table. The JSON output format puts the timestamp in a member of the top-level JSON object named \fBtime\fR. . .SS "Daemon Options" -The daemon options apply only to the \fBmonitor\fR and \fBmonitor2\fR commands. +The daemon options apply only to the \fBmonitor\fR and \fBmonitor-cond\fR commands. With any other command, they have no effect. .ds DD .so lib/daemon.man diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c index 06155ec..e48677e 100644 --- a/ovsdb/ovsdb-client.c +++ b/ovsdb/ovsdb-client.c @@ -38,6 +38,7 @@ #include "ovsdb.h" #include "ovsdb-data.h" #include "ovsdb-error.h" +#include "condition.h" #include "poll-loop.h" #include "sort.h" #include "svec.h" @@ -261,7 +262,10 @@ usage(void) " in DATBASE on SERVER.\n" "\n monitor2 [SERVER] [DATABASE] ALL\n" " same usage as monitor, but uses \"monitor2\" method over" - " the wire." + " the wire.\n" + "\n monitor-cond [SERVER] [DATABASE] TABLE CONDITION [COLUMN,..]\n" + " conditionally monitor contents of COLUMNS in TABLE in\n" + " DATABASE on SERVER.\n" "\n dump [SERVER] [DATABASE]\n" " dump contents of DATABASE on SERVER to stdout\n" "\nThe default SERVER is unix:%s/db.sock.\n" @@ -734,14 +738,13 @@ add_column(const char *server, const struct ovsdb_column *column, static struct json * parse_monitor_columns(char *arg, const char *server, const char *database, const struct ovsdb_table_schema *table, - struct ovsdb_column_set *columns) + struct ovsdb_column_set *columns, struct json *mr) { bool initial, insert, delete, modify; - struct json *mr, *columns_json; + struct json *columns_json; char *save_ptr = NULL; char *token; - mr = json_object_create(); columns_json = json_array_create_empty(); json_object_put(mr, "columns", columns_json); @@ -841,12 +844,15 @@ static void add_monitored_table(int argc, char *argv[], const char *server, const char *database, struct ovsdb_table_schema *table, + const bool conditional, struct json *monitor_requests, struct monitored_table **mts, size_t *n_mts, size_t *allocated_mts) { - struct json *monitor_request_array; + struct json *monitor_request_array, *condition; struct monitored_table *mt; + struct json *mr = json_object_create();; + char *columns = NULL; if (*n_mts >= *allocated_mts) { *mts = x2nrealloc(*mts, allocated_mts, sizeof **mts); @@ -856,23 +862,38 @@ add_monitored_table(int argc, char *argv[], ovsdb_column_set_init(&mt->columns); monitor_request_array = json_array_create_empty(); - if (argc > 1) { - int i; - for (i = 1; i < argc; i++) { - json_array_add( - monitor_request_array, - parse_monitor_columns(argv[i], server, database, table, - &mt->columns)); + if (!conditional) { + if (argc > 1) { + columns = argv[1]; } } else { + struct ovsdb_condition cnd; + + condition = parse_json(argv[1]); + check_ovsdb_error(ovsdb_condition_from_json(table, condition, + NULL, &cnd)); + ovsdb_condition_destroy(&cnd); + + json_object_put(mr, "where", condition); + + if(argc > 2) { + columns = argv[2]; + } + } + + if (columns) { + json_array_add(monitor_request_array, + parse_monitor_columns(columns, server, database, table, + &mt->columns, mr)); + } else { /* Allocate a writable empty string since parse_monitor_columns() * is going to strtok() it and that's risky with literal "". */ char empty[] = ""; json_array_add( monitor_request_array, parse_monitor_columns(empty, server, database, - table, &mt->columns)); + table, &mt->columns, mr)); } json_object_put(monitor_requests, table->name, monitor_request_array); @@ -893,7 +914,7 @@ destroy_monitored_table(struct monitored_table *mts, size_t n) static void do_monitor__(struct jsonrpc *rpc, const char *database, - enum ovsdb_monitor_version version, + enum ovsdb_monitor_version version, const bool conditional, int argc, char *argv[]) { const char *server = jsonrpc_get_name(rpc); @@ -945,7 +966,7 @@ do_monitor__(struct jsonrpc *rpc, const char *database, server, database, table_name); } - add_monitored_table(argc, argv, server, database, table, + add_monitored_table(argc, argv, server, database, table, conditional, monitor_requests, &mts, &n_mts, &allocated_mts); } else { size_t n = shash_count(&schema->tables); @@ -956,7 +977,7 @@ do_monitor__(struct jsonrpc *rpc, const char *database, struct ovsdb_table_schema *table = nodes[i]->data; add_monitored_table(argc, argv, server, database, table, - monitor_requests, + conditional, monitor_requests, &mts, &n_mts, &allocated_mts); } free(nodes); @@ -964,8 +985,13 @@ do_monitor__(struct jsonrpc *rpc, const char *database, monitor = json_array_create_3(json_string_create(database), json_null_create(), monitor_requests); - const char *method = version == OVSDB_MONITOR_V2 ? "monitor2" - : "monitor"; + const char *method; + if (!conditional) { + method = version == OVSDB_MONITOR_V2 ? "monitor2" + : "monitor"; + } else { + method = "monitor_cond"; + } request = jsonrpc_create_request(method, monitor, NULL); request_id = json_clone(request->id); @@ -1048,14 +1074,21 @@ static void do_monitor(struct jsonrpc *rpc, const char *database, int argc, char *argv[]) { - do_monitor__(rpc, database, OVSDB_MONITOR_V1, argc, argv); + do_monitor__(rpc, database, OVSDB_MONITOR_V1, false, argc, argv); } static void do_monitor2(struct jsonrpc *rpc, const char *database, int argc, char *argv[]) { - do_monitor__(rpc, database, OVSDB_MONITOR_V2, argc, argv); + do_monitor__(rpc, database, OVSDB_MONITOR_V2, false, argc, argv); +} + +static void +do_monitor_cond(struct jsonrpc *rpc, const char *database, + int argc, char *argv[]) +{ + do_monitor__(rpc, database, OVSDB_MONITOR_V2, true, argc, argv); } struct dump_table_aux { @@ -1329,6 +1362,7 @@ static const struct ovsdb_client_command all_commands[] = { { "transact", NEED_RPC, 1, 1, do_transact }, { "monitor", NEED_DATABASE, 1, INT_MAX, do_monitor }, { "monitor2", NEED_DATABASE, 1, INT_MAX, do_monitor2 }, + { "monitor-cond", NEED_DATABASE, 2, 3, do_monitor_cond }, { "dump", NEED_DATABASE, 0, INT_MAX, do_dump }, { "help", NEED_NONE, 0, INT_MAX, do_help }, diff --git a/tests/ovsdb-monitor.at b/tests/ovsdb-monitor.at index 0dbf5b0..9646a91 100644 --- a/tests/ovsdb-monitor.at +++ b/tests/ovsdb-monitor.at @@ -44,6 +44,50 @@ m4_define([OVSDB_CHECK_MONITOR], AT_CHECK([${PERL} $srcdir/ovsdb-monitor-sort.pl < output | ${PERL} $srcdir/uuidfilt.pl], [0], [$7], [ignore]) AT_CLEANUP]) +# OVSDB_CHECK_MONITOR_COND(TITLE, SCHEMA, [PRE-MONITOR-TXN], DB, TABLE, +# TRANSACTIONS, OUTPUT, CONDITIONS, [COLUMNS], [KEYWORDS]) +# +# Creates a database with the given SCHEMA, starts an ovsdb-server on +# that database, and runs each of the TRANSACTIONS (which should be a +# quoted list of quoted strings) against it with ovsdb-client one at a +# time. COLUMNS, if specified, is passed to ovsdb-client as the set +# of columns and operations to select. +# +# Checks that the overall output is OUTPUT, but UUIDs in the output +# are replaced by markers of the form <N> where N is a number. The +# first unique UUID is replaced by <0>, the next by <1>, and so on. +# If a given UUID appears more than once it is always replaced by the +# same marker. +# +# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS. +m4_define([OVSDB_CHECK_MONITOR_COND], + [AT_SETUP([$1]) + AT_KEYWORDS([ovsdb server monitor monitor-cond positive $10]) + $2 > schema + AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore]) + m4_foreach([txn], [$3], + [AT_CHECK([ovsdb-tool transact db 'txn'], [0], [ignore], [ignore])]) + AT_CAPTURE_FILE([ovsdb-server-log]) + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/server-pid --remote=punix:socket --unixctl="`pwd`"/unixctl --log-file="`pwd`"/ovsdb-server-log db >/dev/null 2>&1], + [0], [], []) + if test "$IS_WIN32" = "yes"; then + AT_CHECK([ovsdb-client -vjsonrpc --pidfile="`pwd`"/client-pid -d json monitor-cond --format=csv unix:socket $4 $5 '[$8]' $9 > output &], + [0], [ignore], [ignore], [kill `cat server-pid`]) + sleep 1 + else + AT_CHECK([ ovsdb-client -vjsonrpc --detach --no-chdir --pidfile="`pwd`"/client-pid -d json monitor-cond --format=csv unix:socket $4 $5 '[$8]' $9 > output], + [0], [ignore], [ignore], [kill `cat server-pid`]) + fi + m4_foreach([txn], [$6], + [AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0], + [ignore], [ignore], [kill `cat server-pid client-pid`])]) + AT_CHECK([ovsdb-client transact unix:socket '[["$4"]]'], [0], + [ignore], [ignore], [kill `cat server-pid client-pid`]) + AT_CHECK([ovs-appctl -t "`pwd`"/unixctl -e exit], [0], [ignore], [ignore]) + OVS_WAIT_UNTIL([test ! -e server-pid && test ! -e client-pid]) + AT_CHECK([${PERL} $srcdir/ovsdb-monitor-sort.pl < output | ${PERL} $srcdir/uuidfilt.pl], [0], [$7], [ignore]) + AT_CLEANUP]) + OVSDB_CHECK_MONITOR([monitor insert into empty table], [ordinal_schema], [], @@ -322,3 +366,137 @@ OVSDB_CHECK_MONITOR([monitor modify only], <0>,old,"""five""",,"[""uuid"",""<1>""]" ,new,"""FIVE""",5,"[""uuid"",""<2>""]" ]], [!initial,!insert,!delete]) + +AT_BANNER([ovsdb -- ovsdb-monitor-cond conditional monitor only some operations]) + +OVSDB_CHECK_MONITOR_COND([monitor-cond empty condition], + [ordinal_schema], + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 0, "name": "zero"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 1, "name": "one"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 2, "name": "two"}}]]]], + [ordinals], [ordinals], + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 10, "name": "ten"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 11, "name": "eleven"}}]]]], + [[row,action,name,number,_version +<0>,initial,"""one""",1,"[""uuid"",""<1>""]" +<2>,initial,"""two""",2,"[""uuid"",""<3>""]" +<4>,initial,"""zero""",,"[""uuid"",""<5>""]" + +row,action,name,number,_version +<6>,insert,"""eleven""",11,"[""uuid"",""<7>""]" +<8>,insert,"""ten""",10,"[""uuid"",""<9>""]" +]], + [[]]) + +OVSDB_CHECK_MONITOR_COND([monitor-cond multiple conditions], + [ordinal_schema], + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 0, "name": "zero"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 1, "name": "one"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 2, "name": "two"}}]]]], + [ordinals], [ordinals], + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 10, "name": "ten"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 11, "name": "eleven"}}]]]], + [[row,action,name,number,_version +<0>,initial,"""one""",1,"[""uuid"",""<1>""]" + +row,action,name,number,_version +<2>,insert,"""ten""",10,"[""uuid"",""<3>""]" +]], + [[["name","==","one"],["name","==","ten"]]]) + +OVSDB_CHECK_MONITOR_COND([monitor-cond delete from populated table], + [ordinal_schema], + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 0, "name": "zero"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 1, "name": "one"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 2, "name": "two"}}]]]], + [ordinals], [ordinals], + [[[["ordinals", + {"op": "delete", + "table": "ordinals", + "where": []}]]]], + [[row,action,name,number,_version +<0>,initial,"""one""",1,"[""uuid"",""<1>""]" + +row,action,name,number,_version +<0>,delete,,, +]], + [[["name","==","one"],["name","==","ten"]]]) + +OVSDB_CHECK_MONITOR_COND([monitor-cond insert due to modify], + [ordinal_schema], + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 0, "name": "zero"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 1, "name": "one"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 2, "name": "two"}}]]]], + [ordinals], [ordinals], + [[[["ordinals", + {"op": "update", + "table": "ordinals", + "where": [["name", "==", "one"]], + "row": {"name": "ONE"}}]]]], + [[row,action,name,number,_version +<0>,insert,"""ONE""",1,"[""uuid"",""<1>""]" +]], + [[["name","==","ONE"]]], + [!initial,!delete,!modify]) + +OVSDB_CHECK_MONITOR_COND([monitor-cond delete due to modify], + [ordinal_schema], + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 0, "name": "zero"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 1, "name": "one"}}, + {"op": "insert", + "table": "ordinals", + "row": {"number": 2, "name": "two"}}]]]], + [ordinals], [ordinals], + [[[["ordinals", + {"op": "update", + "table": "ordinals", + "where": [["name", "==", "one"]], + "row": {"name": "ONE"}}]]]], + [[row,action,name,number,_version +<0>,delete,,, +]], + [[["name","==","one"]]], + [!initial,!insert,!modify]) -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev