* inclusive. */
struct ovsdb_monitor_json_cache_node {
struct hmap_node hmap_node; /* Elements in json cache. */
+ enum ovsdb_monitor_version version;
uint64_t from_txn;
struct json *json; /* Null, or a cloned of json */
};
struct hmap changes;
};
+typedef struct json *
+(*compose_row_update_cb_func)(const struct ovsdb_monitor_table *mt,
+ const struct ovsdb_monitor_row *row,
+ bool initial, unsigned long int *changed);
+
static void ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon);
static struct ovsdb_monitor_changes * ovsdb_monitor_table_add_changes(
struct ovsdb_monitor_table *mt, uint64_t next_txn);
static void ovsdb_monitor_table_track_changes(struct ovsdb_monitor_table *mt,
uint64_t unflushed);
+static uint32_t
+json_cache_hash(enum ovsdb_monitor_version version, uint64_t from_txn)
+{
+ uint32_t hash;
+
+ hash = hash_uint64(version);
+ hash = hash_uint64_basis(from_txn, hash);
+
+ return hash;
+}
+
static struct ovsdb_monitor_json_cache_node *
ovsdb_monitor_json_cache_search(const struct ovsdb_monitor *dbmon,
+ enum ovsdb_monitor_version version,
uint64_t from_txn)
{
struct ovsdb_monitor_json_cache_node *node;
- uint32_t hash = hash_uint64(from_txn);
+ uint32_t hash = json_cache_hash(version, from_txn);
HMAP_FOR_EACH_WITH_HASH(node, hmap_node, hash, &dbmon->json_cache) {
- if (node->from_txn == from_txn) {
+ if (node->from_txn == from_txn && node->version == version) {
return node;
}
}
static void
ovsdb_monitor_json_cache_insert(struct ovsdb_monitor *dbmon,
+ enum ovsdb_monitor_version version,
uint64_t from_txn, struct json *json)
{
struct ovsdb_monitor_json_cache_node *node;
- uint32_t hash;
+ uint32_t hash = json_cache_hash(version, from_txn);
node = xmalloc(sizeof *node);
- hash = hash_uint64(from_txn);
+ node->version = version;
node->from_txn = from_txn;
node->json = json ? json_clone(json) : NULL;
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)
: !new ? OJMS_DELETE
: OJMS_MODIFY;
}
+static bool
+ovsdb_monitor_row_skip_update(const struct ovsdb_monitor_table *mt,
+ const struct ovsdb_monitor_row *row,
+ enum ovsdb_monitor_selection type,
+ unsigned long int *changed)
+{
+ if (!(mt->select & type)) {
+ return true;
+ }
+
+ if (type == OJMS_MODIFY) {
+ size_t i, n_changes;
+
+ n_changes = 0;
+ memset(changed, 0, bitmap_n_bytes(mt->n_columns));
+ for (i = 0; i < mt->n_columns; i++) {
+ const struct ovsdb_column *c = mt->columns[i].column;
+ if (!ovsdb_datum_equals(&row->old[i], &row->new[i], &c->type)) {
+ bitmap_set1(changed, i);
+ n_changes++;
+ }
+ }
+ if (!n_changes) {
+ /* No actual changes: presumably a row changed and then
+ * changed back later. */
+ return true;
+ }
+ }
+
+ return false;
+}
/* Returns JSON for a <row-update> (as described in RFC 7047) for 'row' within
* 'mt', or NULL if no row update should be sent.
size_t i;
type = ovsdb_monitor_row_update_type(initial, row->old, row->new);
- if (!(mt->select & type)) {
+ if (ovsdb_monitor_row_skip_update(mt, row, type, changed)) {
return NULL;
}
- if (type == OJMS_MODIFY) {
- size_t n_changes;
-
- n_changes = 0;
- memset(changed, 0, bitmap_n_bytes(mt->n_columns));
- for (i = 0; i < mt->n_columns; i++) {
- const struct ovsdb_column *c = mt->columns[i].column;
- if (!ovsdb_datum_equals(&row->old[i], &row->new[i], &c->type)) {
- bitmap_set1(changed, i);
- n_changes++;
- }
- }
- if (!n_changes) {
- /* No actual changes: presumably a row changed and then
- * changed back later. */
- return NULL;
- }
- }
-
row_json = json_object_create();
old_json = new_json = NULL;
if (type & (OJMS_DELETE | OJMS_MODIFY)) {
return row_json;
}
-/* Constructs and returns JSON for a <table-updates> object (as described in
- * RFC 7047) for all the outstanding changes within 'monitor', starting from
- * 'transaction'. */
-static struct json*
-ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon,
- bool initial, uint64_t transaction)
+/* Returns JSON for a <row-update2> (as described in ovsdb-server(1) mapage)
+ * for 'row' within * 'mt', or NULL if no row update should be sent.
+ *
+ * The caller should specify 'initial' as true if the returned JSON is
+ * going to be used as part of the initial reply to a "monitor2" request,
+ * false if it is going to be used as part of an "update2" notification.
+ *
+ * 'changed' must be a scratch buffer for internal use that is at least
+ * bitmap_n_bytes(mt->n_columns) bytes long. */
+static struct json *
+ovsdb_monitor_compose_row_update2(
+ const struct ovsdb_monitor_table *mt,
+ const struct ovsdb_monitor_row *row,
+ bool initial, unsigned long int *changed)
+{
+ enum ovsdb_monitor_selection type;
+ struct json *row_update2, *diff_json;
+ size_t i;
+
+ type = ovsdb_monitor_row_update_type(initial, row->old, row->new);
+ if (ovsdb_monitor_row_skip_update(mt, row, type, changed)) {
+ return NULL;
+ }
+
+ row_update2 = json_object_create();
+ if (type == OJMS_DELETE) {
+ json_object_put(row_update2, "delete", json_null_create());
+ } else {
+ diff_json = json_object_create();
+ const char *op;
+
+ for (i = 0; i < mt->n_columns; i++) {
+ const struct ovsdb_monitor_column *c = &mt->columns[i];
+
+ if (!(type & c->select)) {
+ /* We don't care about this type of change for this
+ * particular column (but we will care about it for some
+ * other column). */
+ continue;
+ }
+
+ if (type == OJMS_MODIFY) {
+ struct ovsdb_datum diff;
+
+ if (!bitmap_is_set(changed, i)) {
+ continue;
+ }
+
+ ovsdb_datum_diff(&diff ,&row->old[i], &row->new[i],
+ &c->column->type);
+ json_object_put(diff_json, c->column->name,
+ ovsdb_datum_to_json(&diff, &c->column->type));
+ ovsdb_datum_destroy(&diff, &c->column->type);
+ } else {
+ if (!ovsdb_datum_is_default(&row->new[i], &c->column->type)) {
+ json_object_put(diff_json, c->column->name,
+ ovsdb_datum_to_json(&row->new[i],
+ &c->column->type));
+ }
+ }
+ }
+
+ op = type == OJMS_INITIAL ? "initial"
+ : type == OJMS_MODIFY ? "modify" : "insert";
+ json_object_put(row_update2, op, diff_json);
+ }
+
+ return row_update2;
+}
+
+static size_t
+ovsdb_monitor_max_columns(struct ovsdb_monitor *dbmon)
{
struct shash_node *node;
- unsigned long int *changed;
- struct json *json;
- size_t max_columns;
+ size_t max_columns = 0;
- max_columns = 0;
SHASH_FOR_EACH (node, &dbmon->tables) {
struct ovsdb_monitor_table *mt = node->data;
max_columns = MAX(max_columns, mt->n_columns);
}
- changed = xmalloc(bitmap_n_bytes(max_columns));
+
+ return max_columns;
+}
+
+/* Constructs and returns JSON for a <table-updates> object (as described in
+ * RFC 7047) for all the outstanding changes within 'monitor', starting from
+ * 'transaction'. */
+static struct json*
+ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon,
+ bool initial, uint64_t transaction,
+ compose_row_update_cb_func row_update)
+{
+ struct shash_node *node;
+ struct json *json;
+ size_t max_columns = ovsdb_monitor_max_columns(dbmon);
+ unsigned long int *changed = xmalloc(bitmap_n_bytes(max_columns));
json = NULL;
SHASH_FOR_EACH (node, &dbmon->tables) {
HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) {
struct json *row_json;
- row_json = ovsdb_monitor_compose_row_update(
- mt, row, initial, changed);
+ row_json = (*row_update)(mt, row, initial, changed);
if (row_json) {
char uuid[UUID_LEN + 1];
* going to be used as part of an "update" notification. */
struct json *
ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon,
- bool initial, uint64_t *unflushed)
+ bool initial, uint64_t *unflushed_,
+ enum ovsdb_monitor_version version)
{
struct ovsdb_monitor_json_cache_node *cache_node;
struct shash_node *node;
struct json *json;
- uint64_t prev_txn = *unflushed;
- uint64_t next_txn = dbmon->n_transactions + 1;
+ const uint64_t unflushed = *unflushed_;
+ const uint64_t next_unflushed = dbmon->n_transactions + 1;
/* Return a clone of cached json if one exists. Otherwise,
* generate a new one and add it to the cache. */
- cache_node = ovsdb_monitor_json_cache_search(dbmon, prev_txn);
+ cache_node = ovsdb_monitor_json_cache_search(dbmon, version, unflushed);
if (cache_node) {
json = cache_node->json ? json_clone(cache_node->json) : NULL;
} else {
- json = ovsdb_monitor_compose_update(dbmon, initial, prev_txn);
- ovsdb_monitor_json_cache_insert(dbmon, prev_txn, json);
+ if (version == OVSDB_MONITOR_V1) {
+ json = ovsdb_monitor_compose_update(dbmon, initial, unflushed,
+ ovsdb_monitor_compose_row_update);
+ } else {
+ ovs_assert(version == OVSDB_MONITOR_V2);
+ json = ovsdb_monitor_compose_update(dbmon, initial, unflushed,
+ ovsdb_monitor_compose_row_update2);
+ }
+ ovsdb_monitor_json_cache_insert(dbmon, version, unflushed, json);
}
/* Maintain transaction id of 'changes'. */
SHASH_FOR_EACH (node, &dbmon->tables) {
struct ovsdb_monitor_table *mt = node->data;
- ovsdb_monitor_table_untrack_changes(mt, prev_txn);
- ovsdb_monitor_table_track_changes(mt, next_txn);
+ ovsdb_monitor_table_untrack_changes(mt, unflushed);
+ ovsdb_monitor_table_track_changes(mt, next_unflushed);
}
- *unflushed = next_txn;
+ *unflushed_ = next_unflushed;
return json;
}
struct ovsdb_monitor_aux aux;
ovsdb_monitor_init_aux(&aux, m);
+ /* Update ovsdb_monitor's transaction number for
+ * each transaction, before calling ovsdb_monitor_change_cb(). */
+ m->n_transactions++;
ovsdb_txn_for_each_change(txn, ovsdb_monitor_change_cb, &aux);
- if (aux.efficacy == OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE) {
+ switch(aux.efficacy) {
+ case OVSDB_CHANGES_NO_EFFECT:
+ /* The transaction is ignored by the monitor.
+ * Roll back the 'n_transactions' as if the transaction
+ * has never happened. */
+ m->n_transactions--;
+ break;
+ case OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE:
+ /* Nothing. */
+ break;
+ case OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE:
ovsdb_monitor_json_cache_flush(m);
- m->n_transactions++;
+ break;
}
return NULL;
}
}
+/* Add some memory usage statics for monitors into 'usage', for use with
+ * memory_report(). */
+void
+ovsdb_monitor_get_memory_usage(struct simap *usage)
+{
+ simap_put(usage, "monitors", hmap_count(&ovsdb_monitors));
+}
+
static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class = {
ovsdb_monitor_commit,
ovsdb_monitor_destroy_callback,