Filter outgoing monitor requests in the IDL layer based on the
db-schema received from the server, skipping tables and columns not
available in the schema.
To support this, store the user-provided monitor condition in pre-JSON
format in ovsdb_idl_table when ovsdb_idl_set_condition() is called,
allowing ovsdb_idl_compose_monitor_request() to build monitor
conditional requests that only include tables and columns present in
the db-schema.

Reported-at: https://issues.redhat.com/browse/FDP-3114
Signed-off-by: Lorenzo Bianconi <[email protected]>
---
Changes in v4:
- Filter monitor requests in ovsdb_idl_set_condition__()
- Cosmetics
- Move ovsdb_cs_db_sync_condition() in ovsdb_cs_send_monitor_request() to
  properly update ack_cond json struct
- Store original user monitor request in req_cond in ovsdb_idl_set_condition()
- Add more uni-tests
- Add more comments
Changes in v3:
- Fix seqno reporting when the filtered condition is empty.
Changes in v2:
- Add missing unit-test.
- squash with patch 'ovsdb: Add ovsdb_cs_clear_condition routine to remove
  stable ovsdb_cs_db_table entries.'.
- fix the error condition reported by Ilya.
- Remove unnecessary ovsdb_cs_db_sync_condition() in
  ovsdb_cs_send_monitor_request().
- cosmetics.
---
 lib/ovsdb-cs.c           |  44 +++++++++++-----
 lib/ovsdb-cs.h           |   1 +
 lib/ovsdb-idl-provider.h |   3 ++
 lib/ovsdb-idl.c          | 109 +++++++++++++++++++++++++++++++++++++--
 tests/ovsdb-idl.at       |  19 ++++++-
 tests/test-ovsdb.c       |  19 ++++++-
 6 files changed, 176 insertions(+), 19 deletions(-)

diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
index df33a835d..a88eedb77 100644
--- a/lib/ovsdb-cs.c
+++ b/lib/ovsdb-cs.c
@@ -412,12 +412,6 @@ ovsdb_cs_retry_at(struct ovsdb_cs *cs, const char *where)
 static void
 ovsdb_cs_restart_fsm(struct ovsdb_cs *cs)
 {
-    /* Resync data DB table conditions to avoid missing updates due to
-     * conditions that were in flight or changed locally while the connection
-     * was down.
-     */
-    ovsdb_cs_db_sync_condition(&cs->data);
-
     ovsdb_cs_send_schema_request(cs, &cs->server);
     ovsdb_cs_transition(cs, CS_S_SERVER_SCHEMA_REQUESTED);
     cs->data.monitor_version = 0;
@@ -912,17 +906,41 @@ ovsdb_cs_db_get_table(struct ovsdb_cs_db *db, const char 
*table)
     return t;
 }
 
+static void
+ovsdb_cs_db_destroy_table(struct ovsdb_cs_db_table *table,
+                          struct ovsdb_cs_db *db)
+{
+    json_destroy(table->ack_cond);
+    json_destroy(table->req_cond);
+    json_destroy(table->new_cond);
+    hmap_remove(&db->tables, &table->hmap_node);
+    free(table->name);
+    free(table);
+}
+
+/* Destroy a given ovsdb_cs_db_table according to the table name. */
+void
+ovsdb_cs_clear_condition(struct ovsdb_cs *cs, const char *table)
+{
+    uint32_t hash = hash_string(table, 0);
+    struct ovsdb_cs_db *db = &cs->data;
+
+    struct ovsdb_cs_db_table *t;
+    HMAP_FOR_EACH_WITH_HASH (t, hmap_node, hash, &db->tables) {
+        if (!strcmp(t->name, table)) {
+            ovsdb_cs_db_destroy_table(t, db);
+            db->last_id = UUID_ZERO;
+            return;
+        }
+    }
+}
+
 static void
 ovsdb_cs_db_destroy_tables(struct ovsdb_cs_db *db)
 {
     struct ovsdb_cs_db_table *table;
     HMAP_FOR_EACH_SAFE (table, hmap_node, &db->tables) {
-        json_destroy(table->ack_cond);
-        json_destroy(table->req_cond);
-        json_destroy(table->new_cond);
-        hmap_remove(&db->tables, &table->hmap_node);
-        free(table->name);
-        free(table);
+        ovsdb_cs_db_destroy_table(table, db);
     }
     hmap_destroy(&db->tables);
 }
@@ -1511,6 +1529,8 @@ ovsdb_cs_send_monitor_request(struct ovsdb_cs *cs, struct 
ovsdb_cs_db *db,
     /* XXX handle failure */
     ovs_assert(mrs->type == JSON_OBJECT);
 
+    ovsdb_cs_db_sync_condition(db);
+
     if (version > 1) {
         struct ovsdb_cs_db_table *table;
         HMAP_FOR_EACH (table, hmap_node, &db->tables) {
diff --git a/lib/ovsdb-cs.h b/lib/ovsdb-cs.h
index bcc3dcd71..0ac691352 100644
--- a/lib/ovsdb-cs.h
+++ b/lib/ovsdb-cs.h
@@ -144,6 +144,7 @@ void ovsdb_cs_set_probe_interval(const struct ovsdb_cs *, 
int probe_interval);
 unsigned int ovsdb_cs_set_condition(struct ovsdb_cs *, const char *table,
                                     const struct json *condition);
 unsigned int ovsdb_cs_get_condition_seqno(const struct ovsdb_cs *);
+void ovsdb_cs_clear_condition(struct ovsdb_cs *, const char *table);
 
 /* Database change awareness. */
 void ovsdb_cs_set_db_change_aware(struct ovsdb_cs *, bool set_db_change_aware);
diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
index 6cf32fb50..9244bbcd5 100644
--- a/lib/ovsdb-idl-provider.h
+++ b/lib/ovsdb-idl-provider.h
@@ -130,6 +130,9 @@ struct ovsdb_idl_table {
                               * or not. */
     struct ovs_list indexes;    /* Contains "struct ovsdb_idl_index"s */
     struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */
+
+    struct ovsdb_idl_condition req_cond; /* User requested monitor
+                                          * condition. */
 };
 
 struct ovsdb_idl_class {
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index fe90deda7..bc242f054 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -99,6 +99,8 @@ struct ovsdb_idl {
     struct ovs_list rows_to_reparse; /* Stores rows that might need to be
                                       * re-parsed due to insertion of a
                                       * referenced row. */
+    bool server_schema_received; /* Set to true when the IDL has received
+                                  * the DB schema from the servers. */
 };
 
 static struct ovsdb_cs_ops ovsdb_idl_cs_ops;
@@ -205,6 +207,18 @@ static void ovsdb_idl_add_to_indexes(const struct 
ovsdb_idl_row *);
 static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *);
 static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop,
                                          bool *may_need_wakeup);
+static void ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dest,
+                                      const struct ovsdb_idl_condition *);
+static void ovsdb_idl_create_req_condition(
+        struct ovsdb_idl *,
+        const struct ovsdb_idl_table_class *,
+        const struct ovsdb_idl_condition *);
+static void ovsdb_idl_destroy_req_condition(struct ovsdb_idl_table *);
+static bool ovsdb_idl_condition_is_set(struct ovsdb_idl_condition *);
+static unsigned int ovsdb_idl_set_condition__(
+        struct ovsdb_idl *,
+        const struct ovsdb_idl_table_class *,
+        const struct ovsdb_idl_condition *);
 
 static void add_tracked_change_for_references(struct ovsdb_idl_row *);
 
@@ -266,6 +280,7 @@ ovsdb_idl_create_unconnected(const struct ovsdb_idl_class 
*class,
         .txn = NULL,
         .outstanding_txns = HMAP_INITIALIZER(&idl->outstanding_txns),
         .verify_write_only = false,
+        .server_schema_received = false,
         .deleted_untracked_rows
             = OVS_LIST_INITIALIZER(&idl->deleted_untracked_rows),
         .rows_to_reparse
@@ -298,6 +313,7 @@ ovsdb_idl_create_unconnected(const struct ovsdb_idl_class 
*class,
             = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0;
         table->idl = idl;
         table->in_server_schema = false;
+        ovsdb_idl_condition_init(&table->req_cond);
         shash_init(&table->schema_columns);
     }
 
@@ -372,6 +388,7 @@ ovsdb_idl_destroy(struct ovsdb_idl *idl)
 
             ovsdb_idl_schema_columns_clear(&table->schema_columns);
             shash_destroy(&table->schema_columns);
+            ovsdb_idl_destroy_req_condition(table);
 
             hmap_destroy(&table->rows);
             free(table->modes);
@@ -467,6 +484,7 @@ ovsdb_idl_run(struct ovsdb_idl *idl)
     LIST_FOR_EACH_POP (event, list_node, &events) {
         switch (event->type) {
         case OVSDB_CS_EVENT_TYPE_RECONNECT:
+            idl->server_schema_received = false;
             ovsdb_idl_txn_abort_all(idl);
             break;
 
@@ -788,6 +806,7 @@ ovsdb_idl_compose_monitor_request(const struct json 
*schema_json, void *idl_)
     struct shash *schema = ovsdb_cs_parse_schema(schema_json);
     struct json *monitor_requests = json_object_create();
 
+    idl->server_schema_received = true;
     for (size_t i = 0; i < idl->class_->n_tables; i++) {
         struct ovsdb_idl_table *table = &idl->tables[i];
         const struct ovsdb_idl_table_class *tc = table->class_;
@@ -842,6 +861,7 @@ ovsdb_idl_compose_monitor_request(const struct json 
*schema_json, void *idl_)
                           idl->class_->database, table->class_->name);
                 json_destroy(columns);
                 table->in_server_schema = false;
+                ovsdb_cs_clear_condition(idl->cs, table->class_->name);
                 continue;
             } else if (schema && table_schema) {
                 table->in_server_schema = true;
@@ -852,6 +872,14 @@ ovsdb_idl_compose_monitor_request(const struct json 
*schema_json, void *idl_)
             json_object_put(monitor_requests, tc->name,
                             json_array_create_1(monitor_request));
         }
+
+        if (!table->in_server_schema) {
+            ovsdb_cs_clear_condition(idl->cs, table->class_->name);
+        } else if (ovsdb_idl_condition_is_set(&table->req_cond)) {
+            /* Update the monitor condition request according to the
+             * db schema. */
+            ovsdb_idl_set_condition__(idl, tc, &table->req_cond);
+        }
     }
     ovsdb_cs_free_schema(schema);
 
@@ -1156,6 +1184,44 @@ ovsdb_idl_condition_add_clause__(struct 
ovsdb_idl_condition *condition,
     hmap_insert(&condition->clauses, &clause->hmap_node, hash);
 }
 
+static void
+ovsdb_idl_destroy_req_condition(struct ovsdb_idl_table *table)
+{
+    ovsdb_idl_condition_destroy(&table->req_cond);
+}
+
+static bool
+ovsdb_idl_condition_is_set(struct ovsdb_idl_condition *condition)
+{
+    return !hmap_is_empty(&condition->clauses) || condition->is_true;
+}
+
+static void
+ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dest,
+                          const struct ovsdb_idl_condition *source)
+{
+    ovsdb_idl_condition_init(dest);
+
+    struct ovsdb_idl_clause *clause;
+    HMAP_FOR_EACH (clause, hmap_node, &source->clauses) {
+        ovsdb_idl_condition_add_clause__(dest, clause, clause->hmap_node.hash);
+    }
+    dest->is_true = source->is_true;
+}
+
+static void
+ovsdb_idl_create_req_condition(struct ovsdb_idl *idl,
+                               const struct ovsdb_idl_table_class *tc,
+                               const struct ovsdb_idl_condition *condition)
+{
+    struct ovsdb_idl_table *table = shash_find_data(&idl->table_by_name,
+                                                    tc->name);
+    if (table) {
+        ovsdb_idl_destroy_req_condition(table);
+        ovsdb_idl_condition_clone(&table->req_cond, condition);
+    }
+}
+
 /* Adds a clause to the condition for replicating the table with class 'tc' in
  * 'idl'.
  *
@@ -1234,6 +1300,43 @@ ovsdb_idl_condition_to_json(const struct 
ovsdb_idl_condition *cnd)
     return json_array_create(clauses, n);
 }
 
+static unsigned int
+ovsdb_idl_set_condition__(struct ovsdb_idl *idl,
+                          const struct ovsdb_idl_table_class *tc,
+                          const struct ovsdb_idl_condition *condition)
+{
+    struct ovsdb_idl_condition filter_cond =
+        OVSDB_IDL_CONDITION_INIT(&filter_cond);
+    struct json *cond_json;
+    unsigned int seqno;
+
+    if (!idl->server_schema_received || hmap_is_empty(&condition->clauses)) {
+        goto out;
+    }
+
+    struct ovsdb_idl_table *t = ovsdb_idl_table_from_class(idl, tc);
+    if (!t || !t->in_server_schema) {
+        return ovsdb_idl_get_condition_seqno(idl);
+    }
+
+    struct ovsdb_idl_clause *clause;
+    HMAP_FOR_EACH (clause, hmap_node, &condition->clauses) {
+        if (ovsdb_idl_server_has_column(idl, clause->column)) {
+            uint32_t hash = ovsdb_idl_clause_hash(clause);
+            ovsdb_idl_condition_add_clause__(&filter_cond, clause, hash);
+        }
+    }
+    condition = &filter_cond;
+out:
+    cond_json = ovsdb_idl_condition_to_json(condition);
+    seqno = ovsdb_cs_set_condition(idl->cs, tc->name, cond_json);
+    json_destroy(cond_json);
+
+    ovsdb_idl_condition_destroy(&filter_cond);
+
+    return seqno;
+}
+
 /* Sets the replication condition for 'tc' in 'idl' to 'condition' and
  * arranges to send the new condition to the database server.
  *
@@ -1245,10 +1348,8 @@ ovsdb_idl_set_condition(struct ovsdb_idl *idl,
                         const struct ovsdb_idl_table_class *tc,
                         const struct ovsdb_idl_condition *condition)
 {
-    struct json *cond_json = ovsdb_idl_condition_to_json(condition);
-    unsigned int seqno = ovsdb_cs_set_condition(idl->cs, tc->name, cond_json);
-    json_destroy(cond_json);
-    return seqno;
+    ovsdb_idl_create_req_condition(idl, tc, condition);
+    return ovsdb_idl_set_condition__(idl, tc, condition);
 }
 
 /* Turns off OVSDB_IDL_ALERT and OVSDB_IDL_TRACK for 'column' in 'idl'.
diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
index 728d761d4..af682aabf 100644
--- a/tests/ovsdb-idl.at
+++ b/tests/ovsdb-idl.at
@@ -2701,7 +2701,7 @@ dnl idltest2.ovsschema and outputs the presence of tables 
and columns.
 dnl And then it connectes to the server with the schema idltest.ovsschema
 dnl and does the same.
 AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 dnl
-                 idl-table-column-check unix:socket unix:socket2], [0], [dnl
+                 idl-table-column-check unix:socket unix:socket2 unix:socket], 
[0], [dnl
 unix:socket remote has table simple
 unix:socket remote has table link1
 unix:socket remote doesn't have table link2
@@ -2720,8 +2720,25 @@ unix:socket2 remote has col l2 in table link1, type: set 
of up to 1 uuids
 unix:socket2 remote has col i in table link1, type: integer
 unix:socket2 remote has col id in table simple7, type: string
 --- remote unix:socket2 done ---
+unix:socket remote has table simple
+unix:socket remote has table link1
+unix:socket remote doesn't have table link2
+unix:socket remote doesn't have table simple5
+unix:socket remote doesn't have col irefmap in table simple5
+unix:socket remote doesn't have col l2 in table link1
+unix:socket remote has col i in table link1, type: integer
+unix:socket remote doesn't have col id in table simple7
+--- remote unix:socket done ---
 ], [stderr])
 
+# Check we do not have any errors related to conditional monitoring.
+AT_CHECK([grep -q "received error, error={\"details\":\"No column l2 in table 
link1.\"" stderr], [1])
+AT_CHECK([grep -q "received error, error={\"details\":\"no table named 
link2\"" stderr], [1])
+
+# Check the monitor_cond_since request has properly formatted conditions 
connecting to the idltest2 server.
+AT_CHECK([grep monitor_cond_since stderr | grep -qF 
'"l2","==",@<:@"uuid","00000000-0000-0000-0000-000000000000"@:>@'])
+AT_CHECK([grep monitor_cond_since stderr | grep -qF '"i","==",1'])
+
 OVSDB_SERVER_SHUTDOWN
 AT_CLEANUP
 
diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
index 95290ae78..71c28cf25 100644
--- a/tests/test-ovsdb.c
+++ b/tests/test-ovsdb.c
@@ -3583,6 +3583,18 @@ do_idl_table_column_check(struct ovs_cmdl_context *ctx)
     ovsdb_idl_omit(idl, &idltest_link1_col_i);
     ovsdb_idl_omit(idl, &idltest_simple7_col_id);
     ovsdb_idl_set_leader_only(idl, false);
+
+    struct ovsdb_idl_condition cond_link1 =
+        OVSDB_IDL_CONDITION_INIT(&cond_link1);
+    struct uuid uuid = UUID_ZERO;
+    idltest_link1_add_clause_l2(&cond_link1, OVSDB_F_EQ, &uuid);
+    idltest_link1_set_condition(idl, &cond_link1);
+
+    struct ovsdb_idl_condition cond_link2 =
+        OVSDB_IDL_CONDITION_INIT(&cond_link2);
+    idltest_link2_add_clause_i(&cond_link2, OVSDB_F_EQ, 1);
+    idltest_link2_set_condition(idl, &cond_link2);
+
     struct stream *stream;
 
     error = stream_open_block(jsonrpc_stream_open(ctx->argv[1], &stream,
@@ -3592,7 +3604,7 @@ do_idl_table_column_check(struct ovs_cmdl_context *ctx)
     }
     rpc = jsonrpc_open(stream);
 
-    for (int r = 1; r <= 2; r++) {
+    for (int r = 1; r <= 3; r++) {
         ovsdb_idl_set_remote(idl, ctx->argv[r], true);
         ovsdb_idl_force_reconnect(idl);
 
@@ -3664,6 +3676,9 @@ do_idl_table_column_check(struct ovs_cmdl_context *ctx)
         printf("--- remote %s done ---\n", ctx->argv[r]);
     }
 
+    ovsdb_idl_condition_destroy(&cond_link1);
+    ovsdb_idl_condition_destroy(&cond_link2);
+
     jsonrpc_close(rpc);
     ovsdb_idl_destroy(idl);
 }
@@ -3706,7 +3721,7 @@ static struct ovs_cmdl_command all_commands[] = {
         do_idl_partial_update_map_column, OVS_RO },
     { "idl-partial-update-set-column", NULL, 1, INT_MAX,
         do_idl_partial_update_set_column, OVS_RO },
-    { "idl-table-column-check", NULL, 2, 2,
+    { "idl-table-column-check", NULL, 3, 3,
         do_idl_table_column_check, OVS_RO },
     { "help", NULL, 0, INT_MAX, do_help, OVS_RO },
     { NULL, NULL, 0, 0, NULL, OVS_RO },
-- 
2.53.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to