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

Reply via email to