2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-30 22:05:19 +00:00

ovsdb-idl: Change interface to conditional monitoring.

Most users of OVSDB react to whatever is currently in their view of the
database, as opposed to keeping track of changes and reacting to those
changes individually.  The interface to conditional monitoring was
different, in that it expected the client to say what to add or remove from
monitoring instead of what to monitor.  This seemed reasonable at the time,
but in practice it turns out that the usual approach actually works better,
because the condition is generally a function of the data visible in the
database.  This commit changes the approach.

This commit also changes the meaning of an empty condition for a table.
Previously, an empty condition meant to replicate every row.  Now, an empty
condition means to replicate no rows.  This is more convenient for code
that gradually constructs conditions, because it does not need special
cases for replicating nothing.

This commit also changes the internal implementation of conditions from
linked lists to arrays.  I just couldn't see an advantage to using linked
lists.

Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Liran Schour <lirans@il.ibm.com>
This commit is contained in:
Ben Pfaff
2016-12-19 20:55:35 -08:00
parent 3a60b7cd5f
commit 0164e367f5
9 changed files with 309 additions and 501 deletions

View File

@@ -24,6 +24,7 @@
#include "bitmap.h"
#include "coverage.h"
#include "hash.h"
#include "openvswitch/dynamic-string.h"
#include "fatal-signal.h"
#include "openvswitch/json.h"
@@ -219,7 +220,6 @@ ovsdb_idl_table_from_class(const struct ovsdb_idl *,
const struct ovsdb_idl_table_class *);
static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table *table);
static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl);
static void ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd);
/* Creates and returns a connection to database 'remote', which should be in a
* form acceptable to jsonrpc_session_open(). The connection will maintain an
@@ -279,6 +279,7 @@ ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class,
= table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0;
table->idl = idl;
ovsdb_idl_condition_init(&table->condition);
ovsdb_idl_condition_add_clause_true(&table->condition);
table->cond_changed = false;
}
@@ -837,147 +838,210 @@ ovsdb_idl_add_table(struct ovsdb_idl *idl,
OVS_NOT_REACHED();
}
/* A single clause within an ovsdb_idl_condition. */
struct ovsdb_idl_clause {
struct ovs_list node;
enum ovsdb_function function;
const struct ovsdb_idl_column *column;
struct ovsdb_datum arg;
struct hmap_node hmap_node; /* In struct ovsdb_idl_condition. */
enum ovsdb_function function; /* Never OVSDB_F_TRUE or OVSDB_F_FALSE. */
const struct ovsdb_idl_column *column; /* Must be nonnull. */
struct ovsdb_datum arg; /* Has ovsdb_type ->column->type. */
};
static uint32_t
ovsdb_idl_clause_hash(const struct ovsdb_idl_clause *clause)
{
uint32_t hash = hash_pointer(clause->column, clause->function);
return ovsdb_datum_hash(&clause->arg, &clause->column->type, hash);
}
static int
ovsdb_idl_clause_equals(const struct ovsdb_idl_clause *a,
const struct ovsdb_idl_clause *b)
{
return (a->function == b->function
&& a->column == b->column
&& ovsdb_datum_equals(&a->arg, &b->arg, &a->column->type));
}
static struct json *
ovsdb_idl_clause_to_json(const struct ovsdb_idl_clause *clause)
{
if (clause->function != OVSDB_F_TRUE &&
clause->function != OVSDB_F_FALSE) {
const char *function = ovsdb_function_to_string(clause->function);
return json_array_create_3(json_string_create(clause->column->name),
json_string_create(function),
ovsdb_datum_to_json(&clause->arg,
&clause->column->type));
}
return json_boolean_create(clause->function == OVSDB_F_TRUE ?
true : false);
const char *function = ovsdb_function_to_string(clause->function);
return json_array_create_3(json_string_create(clause->column->name),
json_string_create(function),
ovsdb_datum_to_json(&clause->arg,
&clause->column->type));
}
static void
ovsdb_idl_clause_free(struct ovsdb_idl_clause *clause)
ovsdb_idl_clause_destroy(struct ovsdb_idl_clause *clause)
{
if (clause->function != OVSDB_F_TRUE &&
clause->function != OVSDB_F_FALSE) {
if (clause) {
ovsdb_datum_destroy(&clause->arg, &clause->column->type);
free(clause);
}
ovs_list_remove(&clause->node);
free(clause);
}
/* ovsdb_idl_condition. */
/* Clears all of the conditional clauses from table 'tc', so that all of the
* rows in the table will be replicated. (This is the default, so this
* function has an effect only if some clauses were added to 'tc' using
* ovsdb_idl_condition_add_clause().) */
void
ovsdb_idl_condition_reset(struct ovsdb_idl *idl,
const struct ovsdb_idl_table_class *tc)
{
struct ovsdb_idl_clause *c, *next;
struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, tc);
LIST_FOR_EACH_SAFE (c, next, node, &table->condition.clauses) {
ovsdb_idl_clause_free(c);
}
idl->cond_changed = table->cond_changed = true;
}
static void
ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd)
{
ovs_list_init(&cnd->clauses);
hmap_init(&cnd->clauses);
cnd->is_true = false;
}
void
ovsdb_idl_condition_destroy(struct ovsdb_idl_condition *cond)
{
if (cond) {
ovsdb_idl_condition_clear(cond);
hmap_destroy(&cond->clauses);
}
}
void
ovsdb_idl_condition_clear(struct ovsdb_idl_condition *cond)
{
struct ovsdb_idl_clause *clause, *next;
HMAP_FOR_EACH_SAFE (clause, next, hmap_node, &cond->clauses) {
hmap_remove(&cond->clauses, &clause->hmap_node);
ovsdb_idl_clause_destroy(clause);
}
cond->is_true = false;
}
bool
ovsdb_idl_condition_is_true(const struct ovsdb_idl_condition *condition)
{
return condition->is_true;
}
static struct ovsdb_idl_clause *
ovsdb_idl_condition_find_clause(struct ovsdb_idl_table *table,
enum ovsdb_function function,
const struct ovsdb_idl_column *column,
const struct ovsdb_datum *arg)
ovsdb_idl_condition_find_clause(const struct ovsdb_idl_condition *condition,
const struct ovsdb_idl_clause *target,
uint32_t hash)
{
struct ovsdb_idl_clause *c;
LIST_FOR_EACH (c, node, &table->condition.clauses) {
if (c->function == function &&
(!column || (c->column == column &&
ovsdb_datum_equals(&c->arg,
arg, &column->type)))) {
return c;
struct ovsdb_idl_clause *clause;
HMAP_FOR_EACH_WITH_HASH (clause, hmap_node, hash, &condition->clauses) {
if (ovsdb_idl_clause_equals(clause, target)) {
return clause;
}
}
return NULL;
}
static void
ovsdb_idl_condition_add_clause__(struct ovsdb_idl_condition *condition,
const struct ovsdb_idl_clause *src,
uint32_t hash)
{
struct ovsdb_idl_clause *clause = xmalloc(sizeof *clause);
clause->function = src->function;
clause->column = src->column;
ovsdb_datum_clone(&clause->arg, &src->arg, &src->column->type);
hmap_insert(&condition->clauses, &clause->hmap_node, hash);
}
/* Adds a clause to the condition for replicating the table with class 'tc' in
* 'idl'.
*
* By default, a table has no clauses, and in that case the IDL replicates all
* its rows. When a table has one or more clauses, the IDL replicates only
* rows that satisfy at least one clause.
* The IDL replicates only rows in a table that satisfy at least one clause in
* the table's condition. The default condition for a table has a single
* clause with function OVSDB_F_TRUE, so that the IDL replicates all rows in
* the table. When the IDL client replaces the default condition by one of its
* own, the condition can have any number of clauses. If it has no conditions,
* then no rows are replicated.
*
* Two distinct of clauses can be added:
* Two distinct of clauses can usefully be added:
*
* - A 'function' of OVSDB_F_FALSE or OVSDB_F_TRUE adds a Boolean clause. A
* "false" clause by itself prevents any rows from being replicated; in
* combination with other clauses it has no effect. A "true" clause
* causes every row to be replicated, regardless of whether other clauses
* exist (thus, a condition that includes "true" is like a condition
* without any clauses at all).
* - A 'function' of OVSDB_F_TRUE. A "true" clause causes every row to be
* replicated, regardless of whether other clauses exist. 'column' and
* 'arg' are ignored.
*
* 'column' should be NULL and 'arg' should be an empty datum (initialized
* with ovsdb_datum_init_empty()).
*
* - Other 'functions' add a clause of the form "<column> <function> <arg>",
* e.g. "column == 5" or "column <= 10". In this case, 'arg' must have a
* type that is compatible with 'column'.
* - Binary 'functions' add a clause of the form "<column> <function>
* <arg>", e.g. "column == 5" or "column <= 10". In this case, 'arg' must
* have a type that is compatible with 'column'.
*/
void
ovsdb_idl_condition_add_clause(struct ovsdb_idl *idl,
const struct ovsdb_idl_table_class *tc,
ovsdb_idl_condition_add_clause(struct ovsdb_idl_condition *condition,
enum ovsdb_function function,
const struct ovsdb_idl_column *column,
const struct ovsdb_datum *arg)
{
struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, tc);
/* Return without doing anything, if this would be a duplicate clause. */
if (ovsdb_idl_condition_find_clause(table, function, column, arg)) {
return;
if (condition->is_true) {
/* Adding a clause to an always-true condition has no effect. */
} else if (function == OVSDB_F_TRUE) {
ovsdb_idl_condition_add_clause_true(condition);
} else if (function == OVSDB_F_FALSE) {
/* Adding a "false" clause never has any effect. */
} else {
struct ovsdb_idl_clause clause = {
.function = function,
.column = column,
.arg = *arg,
};
uint32_t hash = ovsdb_idl_clause_hash(&clause);
if (!ovsdb_idl_condition_find_clause(condition, &clause, hash)) {
ovsdb_idl_condition_add_clause__(condition, &clause, hash);
}
}
struct ovsdb_idl_clause *clause = xzalloc(sizeof *clause);
ovs_list_init(&clause->node);
clause->function = function;
clause->column = column;
ovsdb_datum_clone(&clause->arg, arg,
column ? &column->type : &ovsdb_type_boolean);
ovs_list_push_back(&table->condition.clauses, &clause->node);
idl->cond_changed = table->cond_changed = true;
poll_immediate_wake();
}
/* If a clause matching (function, column, arg) is included in the condition
* for 'tc' within 'idl', removes it. (If this was the last clause included in
* the table's condition, then this means that the IDL will begin replicating
* every row in the table.) */
void
ovsdb_idl_condition_remove_clause(struct ovsdb_idl *idl,
const struct ovsdb_idl_table_class *tc,
enum ovsdb_function function,
const struct ovsdb_idl_column *column,
const struct ovsdb_datum *arg)
ovsdb_idl_condition_add_clause_true(struct ovsdb_idl_condition *condition)
{
if (!condition->is_true) {
ovsdb_idl_condition_clear(condition);
condition->is_true = true;
}
}
static bool
ovsdb_idl_condition_equals(const struct ovsdb_idl_condition *a,
const struct ovsdb_idl_condition *b)
{
if (hmap_count(&a->clauses) != hmap_count(&b->clauses)) {
return false;
}
if (a->is_true != b->is_true) {
return false;
}
const struct ovsdb_idl_clause *clause;
HMAP_FOR_EACH (clause, hmap_node, &a->clauses) {
if (!ovsdb_idl_condition_find_clause(b, clause,
clause->hmap_node.hash)) {
return false;
}
}
return true;
}
static void
ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dst,
const struct ovsdb_idl_condition *src)
{
ovsdb_idl_condition_init(dst);
dst->is_true = src->is_true;
const struct ovsdb_idl_clause *clause;
HMAP_FOR_EACH (clause, hmap_node, &src->clauses) {
ovsdb_idl_condition_add_clause__(dst, clause, clause->hmap_node.hash);
}
}
/* Sets the replication condition for 'tc' in 'idl' to 'condition' and arranges
* to send the new condition to the database server. */
void
ovsdb_idl_set_condition(struct ovsdb_idl *idl,
const struct ovsdb_idl_table_class *tc,
const struct ovsdb_idl_condition *condition)
{
struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, tc);
struct ovsdb_idl_clause *c
= ovsdb_idl_condition_find_clause(table, function, column, arg);
if (c) {
ovsdb_idl_clause_free(c);
if (!ovsdb_idl_condition_equals(condition, &table->condition)) {
ovsdb_idl_condition_destroy(&table->condition);
ovsdb_idl_condition_clone(&table->condition, condition);
idl->cond_changed = table->cond_changed = true;
poll_immediate_wake();
}
@@ -986,18 +1050,25 @@ ovsdb_idl_condition_remove_clause(struct ovsdb_idl *idl,
static struct json *
ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd)
{
struct json **clauses;
size_t i = 0, n_clauses = ovs_list_size(&cnd->clauses);
struct ovsdb_idl_clause *c;
clauses = xmalloc(n_clauses * sizeof *clauses);
LIST_FOR_EACH (c, node, &cnd->clauses) {
clauses[i++] = ovsdb_idl_clause_to_json(c);
if (cnd->is_true) {
return json_array_create_empty();
}
return json_array_create(clauses, n_clauses);
}
size_t n = hmap_count(&cnd->clauses);
if (!n) {
return json_array_create_1(json_boolean_create(false));
}
struct json **clauses = xmalloc(n * sizeof *clauses);
const struct ovsdb_idl_clause *clause;
size_t i = 0;
HMAP_FOR_EACH (clause, hmap_node, &cnd->clauses) {
clauses[i++] = ovsdb_idl_clause_to_json(clause);
}
ovs_assert(i == n);
return json_array_create(clauses, n);
}
static struct json *
ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table *table)
{
@@ -1398,8 +1469,8 @@ ovsdb_idl_send_monitor_request__(struct ovsdb_idl *idl,
monitor_request = json_object_create();
json_object_put(monitor_request, "columns", columns);
if (!strcmp(method, "monitor_cond") && table->cond_changed &&
ovs_list_size(&table->condition.clauses) > 0) {
if (!strcmp(method, "monitor_cond")
&& !ovsdb_idl_condition_is_true(&table->condition)) {
where = ovsdb_idl_condition_to_json(&table->condition);
json_object_put(monitor_request, "where", where);
table->cond_changed = false;