ovsdb-client: Add optional table and columns to dump command
[cascardo/ovs.git] / ovsdb / monitor.c
index 257959c..7805c60 100644 (file)
@@ -412,9 +412,7 @@ ovsdb_monitor_table_find_changes(struct ovsdb_monitor_table *mt,
     return NULL;
 }
 
-/* Stop currently tracking changes to table 'mt' since 'transaction'.
- *
- * Return 'true' if the 'transaction' is being tracked. 'false' otherwise. */
+/* Stop currently tracking changes to table 'mt' since 'transaction'. */
 static void
 ovsdb_monitor_table_untrack_changes(struct ovsdb_monitor_table *mt,
                                     uint64_t transaction)
@@ -458,6 +456,15 @@ ovsdb_monitor_changes_destroy(struct ovsdb_monitor_changes *changes)
     free(changes);
 }
 
+static enum ovsdb_monitor_selection
+ovsdb_monitor_row_update_type(bool initial, const bool old, const bool new)
+{
+    return initial ? OJMS_INITIAL
+            : !old ? OJMS_INSERT
+            : !new ? OJMS_DELETE
+            : OJMS_MODIFY;
+}
+
 /* Returns JSON for a <row-update> (as described in RFC 7047) for 'row' within
  * 'mt', or NULL if no row update should be sent.
  *
@@ -478,10 +485,7 @@ ovsdb_monitor_compose_row_update(
     struct json *row_json;
     size_t i;
 
-    type = (initial ? OJMS_INITIAL
-            : !row->old ? OJMS_INSERT
-            : !row->new ? OJMS_DELETE
-            : OJMS_MODIFY);
+    type = ovsdb_monitor_row_update_type(initial, row->old, row->new);
     if (!(mt->select & type)) {
         return NULL;
     }
@@ -661,9 +665,36 @@ ovsdb_monitor_table_add_select(struct ovsdb_monitor *dbmon,
     mt->select |= select;
 }
 
+ /*
+ * If a row's change type (insert, delete or modify) matches that of
+ * the monitor, they should be sent to the monitor's clients as updates.
+ * Of cause, the monitor should also internally update with this change.
+ *
+ * When a change type does not require client side update, the monitor
+ * may still need to keep track of certain changes in order to generate
+ * correct future updates.  For example, the monitor internal state should
+ * be updated whenever a new row is inserted, in order to generate the
+ * correct initial state, regardless if a insert change type is being
+ * monitored.
+ *
+ * On the other hand, if a transaction only contains changes to columns
+ * that are not monitored, this transaction can be safely ignored by the
+ * monitor.
+ *
+ * Thus, the order of the declaration is important:
+ * 'OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE' always implies
+ * 'OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE', but not vice versa.  */
+enum ovsdb_monitor_changes_efficacy {
+    OVSDB_CHANGES_NO_EFFECT,                /* Monitor does not care about this
+                                               change.  */
+    OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE,  /* Monitor internal updates. */
+    OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE,  /* Client needs to be updated.  */
+};
+
 struct ovsdb_monitor_aux {
     const struct ovsdb_monitor *monitor;
     struct ovsdb_monitor_table *mt;
+    enum ovsdb_monitor_changes_efficacy efficacy;
 };
 
 static void
@@ -672,6 +703,7 @@ ovsdb_monitor_init_aux(struct ovsdb_monitor_aux *aux,
 {
     aux->monitor = m;
     aux->mt = NULL;
+    aux->efficacy = OVSDB_CHANGES_NO_EFFECT;
 }
 
 static void
@@ -706,10 +738,46 @@ ovsdb_monitor_changes_update(const struct ovsdb_row *old,
     }
 }
 
+static bool
+ovsdb_monitor_columns_changed(const struct ovsdb_monitor_table *mt,
+                              const unsigned long int *changed)
+{
+    size_t i;
+
+    for (i = 0; i < mt->n_columns; i++) {
+        size_t column_index = mt->columns[i].column->index;
+
+        if (bitmap_is_set(changed, column_index)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/* Return the efficacy of a row's change to a monitor table.
+ *
+ * Please see the block comment above 'ovsdb_monitor_changes_efficacy'
+ * definition form more information.  */
+static enum ovsdb_monitor_changes_efficacy
+ovsdb_monitor_changes_classify(enum ovsdb_monitor_selection type,
+                               const struct ovsdb_monitor_table *mt,
+                               const unsigned long int *changed)
+{
+    if (type == OJMS_MODIFY &&
+        !ovsdb_monitor_columns_changed(mt, changed)) {
+        return OVSDB_CHANGES_NO_EFFECT;
+    }
+
+    return (mt->select & type)
+                ?  OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE
+                :  OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE;
+}
+
 static bool
 ovsdb_monitor_change_cb(const struct ovsdb_row *old,
                         const struct ovsdb_row *new,
-                        const unsigned long int *changed OVS_UNUSED,
+                        const unsigned long int *changed,
                         void *aux_)
 {
     struct ovsdb_monitor_aux *aux = aux_;
@@ -729,8 +797,20 @@ ovsdb_monitor_change_cb(const struct ovsdb_row *old,
     mt = aux->mt;
 
     HMAP_FOR_EACH(changes, hmap_node, &mt->changes) {
-        ovsdb_monitor_changes_update(old, new, mt, changes);
+        enum ovsdb_monitor_changes_efficacy efficacy;
+        enum ovsdb_monitor_selection type;
+
+        type = ovsdb_monitor_row_update_type(false, old, new);
+        efficacy = ovsdb_monitor_changes_classify(type, mt, changed);
+        if (efficacy > OVSDB_CHANGES_NO_EFFECT) {
+            ovsdb_monitor_changes_update(old, new, mt, changes);
+        }
+
+        if (aux->efficacy < efficacy) {
+            aux->efficacy = efficacy;
+        }
     }
+
     return true;
 }
 
@@ -908,6 +988,7 @@ ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon)
             hmap_remove(&mt->changes, &changes->hmap_node);
             ovsdb_monitor_changes_destroy(changes);
         }
+        hmap_destroy(&mt->changes);
         free(mt->columns);
         free(mt);
     }
@@ -923,10 +1004,13 @@ ovsdb_monitor_commit(struct ovsdb_replica *replica,
     struct ovsdb_monitor *m = ovsdb_monitor_cast(replica);
     struct ovsdb_monitor_aux aux;
 
-    ovsdb_monitor_json_cache_flush(m);
     ovsdb_monitor_init_aux(&aux, m);
     ovsdb_txn_for_each_change(txn, ovsdb_monitor_change_cb, &aux);
-    m->n_transactions++;
+
+    if (aux.efficacy == OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE) {
+        ovsdb_monitor_json_cache_flush(m);
+        m->n_transactions++;
+    }
 
     return NULL;
 }