Implement function for changing condition in an idl session. Signed-off-by: Liran Schour <lir...@il.ibm.com> --- lib/ovsdb-idl-provider.h | 33 ++++ lib/ovsdb-idl.c | 399 ++++++++++++++++++++++++++++++++++++++++++++++- lib/ovsdb-idl.h | 13 ++ 3 files changed, 444 insertions(+), 1 deletion(-)
diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h index 190acca..2722922 100644 --- a/lib/ovsdb-idl-provider.h +++ b/lib/ovsdb-idl-provider.h @@ -51,6 +51,38 @@ struct ovsdb_idl_column { void (*unparse)(struct ovsdb_idl_row *); }; + +/* These list is ordered in ascending order of the fraction of tables row that + * they are (heuristically) expected to leave in query results. */ +#define OVSDB_IDL_FUNCTIONS \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_FALSE, "false") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_TRUE, "true") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_EQ, "==") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_INCLUDES, "includes") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_LE, "<=") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_LT, "<") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_GE, ">=") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_GT, ">") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_EXCLUDES, "excludes") \ + OVSDB_IDL_FUNCTION(OVSDB_IDL_F_NE, "!=") + +enum ovsdb_idl_function { +#define OVSDB_IDL_FUNCTION(ENUM, NAME) ENUM, + OVSDB_IDL_FUNCTIONS +#undef OVSDB_IDL_FUNCTION +}; + +struct ovsdb_idl_clause { + enum ovsdb_idl_function function; + const struct ovsdb_idl_column *column; + struct ovsdb_datum arg; +}; + +struct ovsdb_idl_condition { + struct ovsdb_idl_clause *clauses; + size_t n_clauses; +}; + struct ovsdb_idl_table_class { char *name; bool is_root; @@ -70,6 +102,7 @@ struct ovsdb_idl_table { struct ovsdb_idl *idl; /* Containing idl. */ unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX]; struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */ + struct ovsdb_idl_condition condition; }; struct ovsdb_idl_class { diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c index 1b8731e..c3550b9 100644 --- a/lib/ovsdb-idl.c +++ b/lib/ovsdb-idl.c @@ -261,6 +261,7 @@ ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class, = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY] = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0; table->idl = idl; + ovsdb_idl_condition_init(&table->condition); } idl->state_seqno = UINT_MAX; @@ -650,6 +651,397 @@ ovsdb_idl_add_table(struct ovsdb_idl *idl, OVS_NOT_REACHED(); } +static const struct ovsdb_idl_table_class * +ovsdb_idl_table_name_to_class(struct ovsdb_idl *idl, const char *name) +{ + int i; + struct ovsdb_idl_table *table; + + /* Ensure table are being replicated */ + for (i = 0; i < idl->class->n_tables; i++) { + table = &idl->tables[i]; + + if (strcmp(table->class->name, name) == 0) { + return table->class; + } + } + return NULL; +} + +static const struct ovsdb_idl_column * +ovsdb_table_get_column(const struct ovsdb_idl_table_class *tc, const char *name) +{ + int i; + + for (i = 0; i < tc->n_columns; i++) { + if(!strcmp(tc->columns[i].name, name)) { + return &tc->columns[i]; + } + } + return NULL; +} + +static struct ovsdb_error * +ovsdb_idl_function_from_string(const char *name, + enum ovsdb_idl_function *function) +{ +#define OVSDB_IDL_FUNCTION(ENUM, NAME) \ + if (!strcmp(name, NAME)) { \ + *function = ENUM; \ + return NULL; \ + } + OVSDB_IDL_FUNCTIONS; +#undef OVSDB_IDL_FUNCTION + + return ovsdb_syntax_error(NULL, "unknown function", + "No function named %s.", name); +} + +static const char * +ovsdb_idl_function_to_string(enum ovsdb_idl_function function) +{ + switch (function) { +#define OVSDB_IDL_FUNCTION(ENUM, NAME) case ENUM: return NAME; + OVSDB_IDL_FUNCTIONS; +#undef OVSDB_IDL_FUNCTION + } + + return NULL; +} + +static struct json * +ovsdb_idl_clause_to_json(const struct ovsdb_idl_clause *clause) +{ + if (clause->function != OVSDB_IDL_F_TRUE && + clause->function != OVSDB_IDL_F_FALSE) { + const char *function = ovsdb_idl_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_IDL_F_TRUE ? + true : false); +} + +static void +ovsdb_idl_clause_free(struct ovsdb_idl_clause *clause) +{ + if (clause->function != OVSDB_IDL_F_TRUE && + clause->function != OVSDB_IDL_F_FALSE) { + ovsdb_datum_destroy(&clause->arg, &clause->column->type); + } +} + +void +ovsdb_idl_condition_destroy(struct ovsdb_idl_condition *cnd) +{ + size_t i; + + for (i = 0; i < cnd->n_clauses; i++) { + ovsdb_idl_clause_free(&cnd->clauses[i]); + } + free(cnd->clauses); + cnd->n_clauses = 0; +} + +void +ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd) +{ + cnd->clauses = NULL; + cnd->n_clauses = 0; +} + +static int +compare_clauses(const void *a_, const void *b_) +{ + const struct ovsdb_idl_clause *a = a_; + const struct ovsdb_idl_clause *b = b_; + + if (a->function != b->function) { + return a->function < b->function ? -1 : 1; + } else if (a->function == OVSDB_IDL_F_TRUE || + a->function == OVSDB_IDL_F_FALSE) { + return 0; + } else if (strcmp(a->column->name, b->column->name)) { + return strcmp(a->column->name, b->column->name); + } else { + return ovsdb_datum_compare_3way(&a->arg, &b->arg, &a->column->type); + } + OVS_NOT_REACHED(); +} + +static int +ovsdb_idl_clause_exists(const struct ovsdb_idl_condition *cnd, + const struct ovsdb_idl_clause *clause) +{ + size_t i; + + for (i=0; i < cnd->n_clauses; i++) { + if(!compare_clauses(&cnd->clauses[i], clause)) { + return i; + } + } + + return -1; +} + +static void +ovsdb_idl_clone_clause(struct ovsdb_idl_clause *new, + struct ovsdb_idl_clause *old) +{ + new->function = old->function; + if (new->function == OVSDB_IDL_F_TRUE || + new->function ==OVSDB_IDL_F_FALSE) { + return; + } + new->column = old->column; + ovsdb_datum_clone(&new->arg, + &old->arg, + &old->column->type); +} + +static void +ovsdb_idl_condition_add(struct ovsdb_idl_condition *to, + const struct ovsdb_idl_condition *add) +{ + size_t i, count = 0; + struct ovsdb_idl_clause *clauses; + unsigned long int *clause_map = xzalloc(bitmap_n_bytes(add->n_clauses)); + int index = to->n_clauses;; + + for (i = 0; i < add->n_clauses; i++) { + if (ovsdb_idl_clause_exists(to, &add->clauses[i]) == -1) { + bitmap_set1(clause_map, i); + count++; + } + } + + if (!count) { + free(clause_map); + return; + } + clauses = xzalloc((to->n_clauses + count) * sizeof *clauses); + + for (i = 0; i < to->n_clauses; i++) { + ovsdb_idl_clone_clause(&clauses[i], &to->clauses[i]); + ovsdb_idl_clause_free(&to->clauses[i]); + } + + for (i = 0; i < add->n_clauses; i++) { + if (bitmap_is_set(clause_map, i)) { + ovsdb_idl_clone_clause(&clauses[index++], &add->clauses[i]); + } + } + + free(to->clauses); + free(clause_map); + to->clauses = clauses; + to->n_clauses += count; +} + +static struct json * +ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd) +{ + struct json **clauses; + size_t i; + + clauses = xmalloc(cnd->n_clauses * sizeof *clauses); + for (i = 0; i < cnd->n_clauses; i++) { + clauses[i] = ovsdb_idl_clause_to_json(&cnd->clauses[i]); + } + return json_array_create(clauses, cnd->n_clauses); +} + +static struct ovsdb_error * +ovsdb_idl_clause_from_json(const struct ovsdb_idl_table_class *tc, + const struct json *json, + struct ovsdb_idl_clause *clause) +{ + const struct json_array *array; + struct ovsdb_error *error; + const char *function_name; + const char *column_name; + struct ovsdb_type type; + + if (json->type == JSON_TRUE || json->type == JSON_FALSE) { + /* column and arg fields are not being used with boolean function */ + function_name = (json->type == JSON_TRUE) ? "true" : "false"; + error = ovsdb_idl_function_from_string(function_name, &clause->function); + + return error; + } + + if (json->type != JSON_ARRAY + || json->u.array.n != 3 + || json->u.array.elems[0]->type != JSON_STRING + || json->u.array.elems[1]->type != JSON_STRING) { + return ovsdb_syntax_error(json, NULL, "Parse error in condition."); + } + array = json_array(json); + + column_name = json_string(array->elems[0]); + clause->column = ovsdb_table_get_column(tc, column_name); + if (!clause->column) { + return ovsdb_syntax_error(json, "unknown column", + "No column %s in table %s.", + column_name, tc->name); + } + type = clause->column->type; + + function_name = json_string(array->elems[1]); + error = ovsdb_idl_function_from_string(function_name, &clause->function); + if (error) { + return error; + } + + /* Type-check and relax restrictions on 'type' if appropriate. */ + switch (clause->function) { + case OVSDB_IDL_F_LT: + case OVSDB_IDL_F_LE: + case OVSDB_IDL_F_GT: + case OVSDB_IDL_F_GE: + /* Allow these operators for types with n_min == 0, n_max == 1. + * (They will always be "false" if the value is missing.) */ + if (!(ovsdb_type_is_scalar(&type) + || ovsdb_type_is_optional_scalar(&type)) + || (type.key.type != OVSDB_TYPE_INTEGER + && type.key.type != OVSDB_TYPE_REAL)) { + char *s = ovsdb_type_to_english(&type); + error = ovsdb_syntax_error( + json, NULL, "Type mismatch: \"%s\" operator may not be " + "applied to column %s of type %s.", + ovsdb_idl_function_to_string(clause->function), + clause->column->name, s); + free(s); + return error; + } + break; + case OVSDB_IDL_F_TRUE: + case OVSDB_IDL_F_FALSE: + case OVSDB_IDL_F_EQ: + case OVSDB_IDL_F_NE: + break; + + case OVSDB_IDL_F_EXCLUDES: + if (!ovsdb_type_is_scalar(&type)) { + type.n_min = 0; + type.n_max = UINT_MAX; + } + break; + + case OVSDB_IDL_F_INCLUDES: + if (!ovsdb_type_is_scalar(&type)) { + type.n_min = 0; + } + break; + } + return ovsdb_datum_from_json(&clause->arg, &type, array->elems[2], NULL); +} + +struct ovsdb_error * +ovsdb_idl_condition_from_json(struct ovsdb_idl *idl, + const char *table_name, + const struct json *json, + struct ovsdb_idl_condition *cnd) +{ + const struct ovsdb_idl_table_class *table_class = + ovsdb_idl_table_name_to_class(idl, table_name); + const struct json_array *array = json_array(json); + struct ovsdb_error *error; + size_t i; + + if (!table_class) { + error = ovsdb_syntax_error(NULL, NULL, "Error finding table %s ", + table_name); + return error; + } + + cnd->clauses = xzalloc(array->n * sizeof *cnd->clauses); + cnd->n_clauses = 0; + for (i = 0; i < array->n; i++) { + struct ovsdb_error *error; + error = ovsdb_idl_clause_from_json(table_class, array->elems[i], + &cnd->clauses[i]); + if (error) { + ovsdb_idl_condition_destroy(cnd); + cnd->clauses = NULL; + cnd->n_clauses = 0; + return error; + } + cnd->n_clauses++; + } + + return NULL; +} + +static void +ovsdb_idl_send_cond_update__(struct ovsdb_idl *idl, + struct ovsdb_idl_table *table, + const struct ovsdb_idl_condition *add, + const struct ovsdb_idl_condition *remove) +{ + char uuid[UUID_LEN + 1]; + struct json *monitor_cond_update_requests = json_object_create(); + struct json *monitor_cond_update_request = json_object_create(); + struct json *added = ovsdb_idl_condition_to_json(add); + struct json *removed = ovsdb_idl_condition_to_json(remove); + struct json *params, *json_uuid; + struct jsonrpc_msg *request; + + json_object_put(monitor_cond_update_request, "added", added); + json_object_put(monitor_cond_update_request, "removed", removed); + json_object_put(monitor_cond_update_requests, + table->class->name, + json_array_create_1(monitor_cond_update_request)); + + snprintf(uuid, sizeof uuid, UUID_FMT, + UUID_ARGS(&idl->uuid)); + json_uuid = json_string_create(uuid); + + /* Create a new uuid */ + uuid_generate(&idl->uuid); + snprintf(uuid, sizeof uuid, UUID_FMT, + UUID_ARGS(&idl->uuid)); + params = json_array_create_3(json_uuid, json_string_create(uuid), + monitor_cond_update_requests); + + request = jsonrpc_create_request("monitor_cond_update", params, NULL); + jsonrpc_session_send(idl->session, request); +} + +/* Add conditions to the replicated tables. Ensure that tables are added to the + * replication. + */ +bool +ovsdb_idl_cond_update(struct ovsdb_idl *idl, + const char *table_name, + const struct ovsdb_idl_condition *add, + const struct ovsdb_idl_condition *remove) +{ + const struct ovsdb_idl_table_class *table_class = + ovsdb_idl_table_name_to_class(idl, table_name); + struct ovsdb_idl_table *table; + + if (table_class == NULL) { + return false; + } + table = ovsdb_idl_table_from_class(idl, table_class); + + if (jsonrpc_session_is_connected(idl->session)) { + ovsdb_idl_send_cond_update__(idl, table, add, remove); + } else { + if (remove && remove->n_clauses > 0) { + /* No remove conditions on an unconnected session */ + return false; + } + ovsdb_idl_condition_add(&table->condition, add); + } + return true; +} + /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'. * * This function should be called between ovsdb_idl_create() and the first call @@ -949,7 +1341,7 @@ ovsdb_idl_send_monitor_request__(struct ovsdb_idl *idl, for (i = 0; i < idl->class->n_tables; i++) { const struct ovsdb_idl_table *table = &idl->tables[i]; const struct ovsdb_idl_table_class *tc = table->class; - struct json *monitor_request, *columns; + struct json *monitor_request, *columns, *where; const struct sset *table_schema; size_t j; @@ -987,6 +1379,11 @@ 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->condition.n_clauses > 0) { + where = ovsdb_idl_condition_to_json(&table->condition); + json_object_put(monitor_request, "where", where); + } json_object_put(monitor_requests, tc->name, monitor_request); } } diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h index 1cbaf35..413da7d 100644 --- a/lib/ovsdb-idl.h +++ b/lib/ovsdb-idl.h @@ -45,6 +45,7 @@ struct ovsdb_idl_class; struct ovsdb_idl_row; struct ovsdb_idl_column; struct ovsdb_idl_table_class; +struct ovsdb_idl_condition; struct uuid; struct ovsdb_idl *ovsdb_idl_create(const char *remote, @@ -276,5 +277,17 @@ struct ovsdb_idl_loop { void ovsdb_idl_loop_destroy(struct ovsdb_idl_loop *); struct ovsdb_idl_txn *ovsdb_idl_loop_run(struct ovsdb_idl_loop *); void ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *); +#define OVSDB_IDL_CONDITION_INITIALIZER { NULL, 0 } +void ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd); +void ovsdb_idl_condition_destroy(struct ovsdb_idl_condition *cnd); +struct ovsdb_error * +ovsdb_idl_condition_from_json(struct ovsdb_idl *idl, + const char *table_name, + const struct json *json, + struct ovsdb_idl_condition *cnd); +bool ovsdb_idl_cond_update(struct ovsdb_idl *idl, + const char *table_name, + const struct ovsdb_idl_condition *add, + const struct ovsdb_idl_condition *remove); #endif /* ovsdb-idl.h */ -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev