mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 06:45:17 +00:00
lib: add monitor2 support in ovsdb-idl.
Add support for monitor2. When idl starts to run, monitor2 will be attempted first. In case the server is an older version that does not recognize monitor2. IDL will then fall back to use "monitor" method. Signed-off-by: Andy Zhou <azhou@nicira.com> Acked-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
441
lib/ovsdb-idl.c
441
lib/ovsdb-idl.c
@@ -78,7 +78,9 @@ struct ovsdb_idl_arc {
|
|||||||
enum ovsdb_idl_state {
|
enum ovsdb_idl_state {
|
||||||
IDL_S_SCHEMA_REQUESTED,
|
IDL_S_SCHEMA_REQUESTED,
|
||||||
IDL_S_MONITOR_REQUESTED,
|
IDL_S_MONITOR_REQUESTED,
|
||||||
IDL_S_MONITORING
|
IDL_S_MONITORING,
|
||||||
|
IDL_S_MONITOR2_REQUESTED,
|
||||||
|
IDL_S_MONITORING2
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ovsdb_idl {
|
struct ovsdb_idl {
|
||||||
@@ -93,6 +95,7 @@ struct ovsdb_idl {
|
|||||||
unsigned int state_seqno;
|
unsigned int state_seqno;
|
||||||
enum ovsdb_idl_state state;
|
enum ovsdb_idl_state state;
|
||||||
struct json *request_id;
|
struct json *request_id;
|
||||||
|
struct json *schema;
|
||||||
|
|
||||||
/* Database locking. */
|
/* Database locking. */
|
||||||
char *lock_name; /* Name of lock we need, NULL if none. */
|
char *lock_name; /* Name of lock we need, NULL if none. */
|
||||||
@@ -133,23 +136,42 @@ struct ovsdb_idl_txn_insert {
|
|||||||
struct uuid real; /* Real UUID used by database server. */
|
struct uuid real; /* Real UUID used by database server. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ovsdb_update_version {
|
||||||
|
OVSDB_UPDATE, /* RFC 7047 "update" method. */
|
||||||
|
OVSDB_UPDATE2 /* "update2" Extension to RFC 7047.
|
||||||
|
See ovsdb-server(1) for more information. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Name arrays indexed by 'enum ovsdb_update_version'. */
|
||||||
|
const char *table_updates_names[] = {"table_updates", "table_updates2"};
|
||||||
|
const char *table_update_names[] = {"table_update", "table_update2"};
|
||||||
|
const char *row_update_names[] = {"row_update", "row_update2"};
|
||||||
|
|
||||||
static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
||||||
static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
||||||
|
|
||||||
static void ovsdb_idl_clear(struct ovsdb_idl *);
|
static void ovsdb_idl_clear(struct ovsdb_idl *);
|
||||||
static void ovsdb_idl_send_schema_request(struct ovsdb_idl *);
|
static void ovsdb_idl_send_schema_request(struct ovsdb_idl *);
|
||||||
static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *,
|
static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *);
|
||||||
const struct json *schema_json);
|
static void ovsdb_idl_send_monitor2_request(struct ovsdb_idl *);
|
||||||
static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *);
|
static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *,
|
||||||
|
enum ovsdb_update_version);
|
||||||
static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *,
|
static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *,
|
||||||
const struct json *);
|
const struct json *,
|
||||||
|
enum ovsdb_update_version);
|
||||||
static bool ovsdb_idl_process_update(struct ovsdb_idl_table *,
|
static bool ovsdb_idl_process_update(struct ovsdb_idl_table *,
|
||||||
const struct uuid *,
|
const struct uuid *,
|
||||||
const struct json *old,
|
const struct json *old,
|
||||||
const struct json *new);
|
const struct json *new);
|
||||||
|
static bool ovsdb_idl_process_update2(struct ovsdb_idl_table *,
|
||||||
|
const struct uuid *,
|
||||||
|
const char *operation,
|
||||||
|
const struct json *row);
|
||||||
static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *);
|
static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *);
|
||||||
static void ovsdb_idl_delete_row(struct ovsdb_idl_row *);
|
static void ovsdb_idl_delete_row(struct ovsdb_idl_row *);
|
||||||
static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *);
|
static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *);
|
||||||
|
static bool ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row *,
|
||||||
|
const struct json *);
|
||||||
|
|
||||||
static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *);
|
static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *);
|
||||||
static struct ovsdb_idl_row *ovsdb_idl_row_create__(
|
static struct ovsdb_idl_row *ovsdb_idl_row_create__(
|
||||||
@@ -242,6 +264,7 @@ ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class,
|
|||||||
|
|
||||||
idl->state_seqno = UINT_MAX;
|
idl->state_seqno = UINT_MAX;
|
||||||
idl->request_id = NULL;
|
idl->request_id = NULL;
|
||||||
|
idl->schema = NULL;
|
||||||
|
|
||||||
hmap_init(&idl->outstanding_txns);
|
hmap_init(&idl->outstanding_txns);
|
||||||
|
|
||||||
@@ -270,6 +293,7 @@ ovsdb_idl_destroy(struct ovsdb_idl *idl)
|
|||||||
json_destroy(idl->request_id);
|
json_destroy(idl->request_id);
|
||||||
free(idl->lock_name);
|
free(idl->lock_name);
|
||||||
json_destroy(idl->lock_request_id);
|
json_destroy(idl->lock_request_id);
|
||||||
|
json_destroy(idl->schema);
|
||||||
hmap_destroy(&idl->outstanding_txns);
|
hmap_destroy(&idl->outstanding_txns);
|
||||||
free(idl);
|
free(idl);
|
||||||
}
|
}
|
||||||
@@ -351,38 +375,59 @@ ovsdb_idl_run(struct ovsdb_idl *idl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (msg->type == JSONRPC_NOTIFY
|
if (msg->type == JSONRPC_NOTIFY
|
||||||
|
&& !strcmp(msg->method, "update2")
|
||||||
|
&& msg->params->type == JSON_ARRAY
|
||||||
|
&& msg->params->u.array.n == 2
|
||||||
|
&& msg->params->u.array.elems[0]->type == JSON_NULL) {
|
||||||
|
/* Database contents changed. */
|
||||||
|
ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1],
|
||||||
|
OVSDB_UPDATE2);
|
||||||
|
} else if (msg->type == JSONRPC_REPLY
|
||||||
|
&& idl->request_id
|
||||||
|
&& json_equal(idl->request_id, msg->id)) {
|
||||||
|
json_destroy(idl->request_id);
|
||||||
|
idl->request_id = NULL;
|
||||||
|
|
||||||
|
switch (idl->state) {
|
||||||
|
case IDL_S_SCHEMA_REQUESTED:
|
||||||
|
/* Reply to our "get_schema" request. */
|
||||||
|
idl->schema = json_clone(msg->result);
|
||||||
|
ovsdb_idl_send_monitor2_request(idl);
|
||||||
|
idl->state = IDL_S_MONITOR2_REQUESTED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IDL_S_MONITOR_REQUESTED:
|
||||||
|
case IDL_S_MONITOR2_REQUESTED:
|
||||||
|
/* Reply to our "monitor" or "monitor2" request. */
|
||||||
|
idl->change_seqno++;
|
||||||
|
ovsdb_idl_clear(idl);
|
||||||
|
if (idl->state == IDL_S_MONITOR_REQUESTED) {
|
||||||
|
idl->state = IDL_S_MONITORING;
|
||||||
|
ovsdb_idl_parse_update(idl, msg->result, OVSDB_UPDATE);
|
||||||
|
} else { /* IDL_S_MONITOR2_REQUESTED. */
|
||||||
|
idl->state = IDL_S_MONITORING2;
|
||||||
|
ovsdb_idl_parse_update(idl, msg->result, OVSDB_UPDATE2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Schema is not useful after monitor request is accepted
|
||||||
|
* by the server. */
|
||||||
|
json_destroy(idl->schema);
|
||||||
|
idl->schema = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IDL_S_MONITORING:
|
||||||
|
case IDL_S_MONITORING2:
|
||||||
|
default:
|
||||||
|
OVS_NOT_REACHED();
|
||||||
|
}
|
||||||
|
} else if (msg->type == JSONRPC_NOTIFY
|
||||||
&& !strcmp(msg->method, "update")
|
&& !strcmp(msg->method, "update")
|
||||||
&& msg->params->type == JSON_ARRAY
|
&& msg->params->type == JSON_ARRAY
|
||||||
&& msg->params->u.array.n == 2
|
&& msg->params->u.array.n == 2
|
||||||
&& msg->params->u.array.elems[0]->type == JSON_NULL) {
|
&& msg->params->u.array.elems[0]->type == JSON_NULL) {
|
||||||
/* Database contents changed. */
|
/* Database contents changed. */
|
||||||
ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1]);
|
ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1],
|
||||||
} else if (msg->type == JSONRPC_REPLY
|
OVSDB_UPDATE);
|
||||||
&& idl->request_id
|
|
||||||
&& json_equal(idl->request_id, msg->id)) {
|
|
||||||
switch (idl->state) {
|
|
||||||
case IDL_S_SCHEMA_REQUESTED:
|
|
||||||
/* Reply to our "get_schema" request. */
|
|
||||||
json_destroy(idl->request_id);
|
|
||||||
idl->request_id = NULL;
|
|
||||||
ovsdb_idl_send_monitor_request(idl, msg->result);
|
|
||||||
idl->state = IDL_S_MONITOR_REQUESTED;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IDL_S_MONITOR_REQUESTED:
|
|
||||||
/* Reply to our "monitor" request. */
|
|
||||||
idl->change_seqno++;
|
|
||||||
json_destroy(idl->request_id);
|
|
||||||
idl->request_id = NULL;
|
|
||||||
idl->state = IDL_S_MONITORING;
|
|
||||||
ovsdb_idl_clear(idl);
|
|
||||||
ovsdb_idl_parse_update(idl, msg->result);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IDL_S_MONITORING:
|
|
||||||
default:
|
|
||||||
OVS_NOT_REACHED();
|
|
||||||
}
|
|
||||||
} else if (msg->type == JSONRPC_REPLY
|
} else if (msg->type == JSONRPC_REPLY
|
||||||
&& idl->lock_request_id
|
&& idl->lock_request_id
|
||||||
&& json_equal(idl->lock_request_id, msg->id)) {
|
&& json_equal(idl->lock_request_id, msg->id)) {
|
||||||
@@ -396,6 +441,18 @@ ovsdb_idl_run(struct ovsdb_idl *idl)
|
|||||||
&& !strcmp(msg->method, "stolen")) {
|
&& !strcmp(msg->method, "stolen")) {
|
||||||
/* Someone else stole our lock. */
|
/* Someone else stole our lock. */
|
||||||
ovsdb_idl_parse_lock_notify(idl, msg->params, false);
|
ovsdb_idl_parse_lock_notify(idl, msg->params, false);
|
||||||
|
} else if (msg->type == JSONRPC_ERROR
|
||||||
|
&& idl->state == IDL_S_MONITOR2_REQUESTED
|
||||||
|
&& idl->request_id
|
||||||
|
&& json_equal(idl->request_id, msg->id)) {
|
||||||
|
if (msg->error && !strcmp(json_string(msg->error),
|
||||||
|
"unknown method")) {
|
||||||
|
/* Fall back to using "monitor" method. */
|
||||||
|
json_destroy(idl->request_id);
|
||||||
|
idl->request_id = NULL;
|
||||||
|
ovsdb_idl_send_monitor_request(idl);
|
||||||
|
idl->state = IDL_S_MONITOR_REQUESTED;
|
||||||
|
}
|
||||||
} else if ((msg->type == JSONRPC_ERROR
|
} else if ((msg->type == JSONRPC_ERROR
|
||||||
|| msg->type == JSONRPC_REPLY)
|
|| msg->type == JSONRPC_REPLY)
|
||||||
&& ovsdb_idl_txn_process_reply(idl, msg)) {
|
&& ovsdb_idl_txn_process_reply(idl, msg)) {
|
||||||
@@ -845,14 +902,15 @@ parse_schema(const struct json *schema_json)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl,
|
ovsdb_idl_send_monitor_request__(struct ovsdb_idl *idl,
|
||||||
const struct json *schema_json)
|
const char *method)
|
||||||
{
|
{
|
||||||
struct shash *schema = parse_schema(schema_json);
|
struct shash *schema;
|
||||||
struct json *monitor_requests;
|
struct json *monitor_requests;
|
||||||
struct jsonrpc_msg *msg;
|
struct jsonrpc_msg *msg;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
schema = parse_schema(idl->schema);
|
||||||
monitor_requests = json_object_create();
|
monitor_requests = json_object_create();
|
||||||
for (i = 0; i < idl->class->n_tables; i++) {
|
for (i = 0; i < idl->class->n_tables; i++) {
|
||||||
const struct ovsdb_idl_table *table = &idl->tables[i];
|
const struct ovsdb_idl_table *table = &idl->tables[i];
|
||||||
@@ -902,7 +960,7 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl,
|
|||||||
|
|
||||||
json_destroy(idl->request_id);
|
json_destroy(idl->request_id);
|
||||||
msg = jsonrpc_create_request(
|
msg = jsonrpc_create_request(
|
||||||
"monitor",
|
method,
|
||||||
json_array_create_3(json_string_create(idl->class->database),
|
json_array_create_3(json_string_create(idl->class->database),
|
||||||
json_null_create(), monitor_requests),
|
json_null_create(), monitor_requests),
|
||||||
&idl->request_id);
|
&idl->request_id);
|
||||||
@@ -910,29 +968,55 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates)
|
ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl)
|
||||||
|
{
|
||||||
|
ovsdb_idl_send_monitor_request__(idl, "monitor");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
log_parse_update_error(struct ovsdb_error *error)
|
||||||
{
|
{
|
||||||
struct ovsdb_error *error = ovsdb_idl_parse_update__(idl, table_updates);
|
|
||||||
if (error) {
|
|
||||||
if (!VLOG_DROP_WARN(&syntax_rl)) {
|
if (!VLOG_DROP_WARN(&syntax_rl)) {
|
||||||
char *s = ovsdb_error_to_string(error);
|
char *s = ovsdb_error_to_string(error);
|
||||||
VLOG_WARN_RL(&syntax_rl, "%s", s);
|
VLOG_WARN_RL(&syntax_rl, "%s", s);
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
ovsdb_error_destroy(error);
|
ovsdb_error_destroy(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ovsdb_idl_send_monitor2_request(struct ovsdb_idl *idl)
|
||||||
|
{
|
||||||
|
ovsdb_idl_send_monitor_request__(idl, "monitor2");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates,
|
||||||
|
enum ovsdb_update_version version)
|
||||||
|
{
|
||||||
|
struct ovsdb_error *error = ovsdb_idl_parse_update__(idl, table_updates,
|
||||||
|
version);
|
||||||
|
if (error) {
|
||||||
|
log_parse_update_error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ovsdb_error *
|
static struct ovsdb_error *
|
||||||
ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
|
ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
|
||||||
const struct json *table_updates)
|
const struct json *table_updates,
|
||||||
|
enum ovsdb_update_version version)
|
||||||
{
|
{
|
||||||
const struct shash_node *tables_node;
|
const struct shash_node *tables_node;
|
||||||
|
const char *table_updates_name = table_updates_names[version];
|
||||||
|
const char *table_update_name = table_update_names[version];
|
||||||
|
const char *row_update_name = row_update_names[version];
|
||||||
|
|
||||||
if (table_updates->type != JSON_OBJECT) {
|
if (table_updates->type != JSON_OBJECT) {
|
||||||
return ovsdb_syntax_error(table_updates, NULL,
|
return ovsdb_syntax_error(table_updates, NULL,
|
||||||
"<table-updates> is not an object");
|
"<%s> is not an object",
|
||||||
|
table_updates_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHASH_FOR_EACH (tables_node, json_object(table_updates)) {
|
SHASH_FOR_EACH (tables_node, json_object(table_updates)) {
|
||||||
const struct json *table_update = tables_node->data;
|
const struct json *table_update = tables_node->data;
|
||||||
const struct shash_node *table_node;
|
const struct shash_node *table_node;
|
||||||
@@ -942,14 +1026,17 @@ ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
|
|||||||
if (!table) {
|
if (!table) {
|
||||||
return ovsdb_syntax_error(
|
return ovsdb_syntax_error(
|
||||||
table_updates, NULL,
|
table_updates, NULL,
|
||||||
"<table-updates> includes unknown table \"%s\"",
|
"<%s> includes unknown table \"%s\"",
|
||||||
|
table_updates_name,
|
||||||
tables_node->name);
|
tables_node->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (table_update->type != JSON_OBJECT) {
|
if (table_update->type != JSON_OBJECT) {
|
||||||
return ovsdb_syntax_error(table_update, NULL,
|
return ovsdb_syntax_error(table_update, NULL,
|
||||||
"<table-update> for table \"%s\" is "
|
"<%s> for table \"%s\" is "
|
||||||
"not an object", table->class->name);
|
"not an object",
|
||||||
|
table_update_name,
|
||||||
|
table->class->name);
|
||||||
}
|
}
|
||||||
SHASH_FOR_EACH (table_node, json_object(table_update)) {
|
SHASH_FOR_EACH (table_node, json_object(table_update)) {
|
||||||
const struct json *row_update = table_node->data;
|
const struct json *row_update = table_node->data;
|
||||||
@@ -958,42 +1045,81 @@ ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
|
|||||||
|
|
||||||
if (!uuid_from_string(&uuid, table_node->name)) {
|
if (!uuid_from_string(&uuid, table_node->name)) {
|
||||||
return ovsdb_syntax_error(table_update, NULL,
|
return ovsdb_syntax_error(table_update, NULL,
|
||||||
"<table-update> for table \"%s\" "
|
"<%s> for table \"%s\" "
|
||||||
"contains bad UUID "
|
"contains bad UUID "
|
||||||
"\"%s\" as member name",
|
"\"%s\" as member name",
|
||||||
|
table_update_name,
|
||||||
table->class->name,
|
table->class->name,
|
||||||
table_node->name);
|
table_node->name);
|
||||||
}
|
}
|
||||||
if (row_update->type != JSON_OBJECT) {
|
if (row_update->type != JSON_OBJECT) {
|
||||||
return ovsdb_syntax_error(row_update, NULL,
|
return ovsdb_syntax_error(row_update, NULL,
|
||||||
"<table-update> for table \"%s\" "
|
"<%s> for table \"%s\" "
|
||||||
"contains <row-update> for %s that "
|
"contains <%s> for %s that "
|
||||||
"is not an object",
|
"is not an object",
|
||||||
|
table_update_name,
|
||||||
table->class->name,
|
table->class->name,
|
||||||
|
row_update_name,
|
||||||
table_node->name);
|
table_node->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
old_json = shash_find_data(json_object(row_update), "old");
|
switch(version) {
|
||||||
new_json = shash_find_data(json_object(row_update), "new");
|
case OVSDB_UPDATE:
|
||||||
if (old_json && old_json->type != JSON_OBJECT) {
|
old_json = shash_find_data(json_object(row_update), "old");
|
||||||
return ovsdb_syntax_error(old_json, NULL,
|
new_json = shash_find_data(json_object(row_update), "new");
|
||||||
"\"old\" <row> is not object");
|
if (old_json && old_json->type != JSON_OBJECT) {
|
||||||
} else if (new_json && new_json->type != JSON_OBJECT) {
|
return ovsdb_syntax_error(old_json, NULL,
|
||||||
return ovsdb_syntax_error(new_json, NULL,
|
"\"old\" <row> is not object");
|
||||||
"\"new\" <row> is not object");
|
} else if (new_json && new_json->type != JSON_OBJECT) {
|
||||||
} else if ((old_json != NULL) + (new_json != NULL)
|
return ovsdb_syntax_error(new_json, NULL,
|
||||||
!= shash_count(json_object(row_update))) {
|
"\"new\" <row> is not object");
|
||||||
return ovsdb_syntax_error(row_update, NULL,
|
} else if ((old_json != NULL) + (new_json != NULL)
|
||||||
"<row-update> contains unexpected "
|
!= shash_count(json_object(row_update))) {
|
||||||
"member");
|
return ovsdb_syntax_error(row_update, NULL,
|
||||||
} else if (!old_json && !new_json) {
|
"<row-update> contains "
|
||||||
return ovsdb_syntax_error(row_update, NULL,
|
"unexpected member");
|
||||||
"<row-update> missing \"old\" "
|
} else if (!old_json && !new_json) {
|
||||||
"and \"new\" members");
|
return ovsdb_syntax_error(row_update, NULL,
|
||||||
|
"<row-update> missing \"old\" "
|
||||||
|
"and \"new\" members");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ovsdb_idl_process_update(table, &uuid, old_json,
|
||||||
|
new_json)) {
|
||||||
|
idl->change_seqno++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OVSDB_UPDATE2: {
|
||||||
|
const char *ops[] = {"modify", "insert", "delete", "initial"};
|
||||||
|
const char *operation;
|
||||||
|
const struct json *row;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(ops); i++) {
|
||||||
|
operation = ops[i];
|
||||||
|
row = shash_find_data(json_object(row_update), operation);
|
||||||
|
|
||||||
|
if (row) {
|
||||||
|
if (ovsdb_idl_process_update2(table, &uuid, operation,
|
||||||
|
row)) {
|
||||||
|
idl->change_seqno++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* row_update2 should contain one of the objects */
|
||||||
|
if (i == ARRAY_SIZE(ops)) {
|
||||||
|
return ovsdb_syntax_error(row_update, NULL,
|
||||||
|
"<row_update2> includes unknown "
|
||||||
|
"object");
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ovsdb_idl_process_update(table, &uuid, old_json, new_json)) {
|
default:
|
||||||
idl->change_seqno++;
|
OVS_NOT_REACHED();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1071,17 +1197,73 @@ ovsdb_idl_process_update(struct ovsdb_idl_table *table,
|
|||||||
/* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
|
/* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
|
||||||
* otherwise. */
|
* otherwise. */
|
||||||
static bool
|
static bool
|
||||||
ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json,
|
ovsdb_idl_process_update2(struct ovsdb_idl_table *table,
|
||||||
enum ovsdb_idl_change change)
|
const struct uuid *uuid,
|
||||||
|
const char *operation,
|
||||||
|
const struct json *json_row)
|
||||||
|
{
|
||||||
|
struct ovsdb_idl_row *row;
|
||||||
|
|
||||||
|
row = ovsdb_idl_get_row(table, uuid);
|
||||||
|
if (!strcmp(operation, "delete")) {
|
||||||
|
/* Delete row. */
|
||||||
|
if (row && !ovsdb_idl_row_is_orphan(row)) {
|
||||||
|
ovsdb_idl_delete_row(row);
|
||||||
|
} else {
|
||||||
|
VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" "
|
||||||
|
"from table %s",
|
||||||
|
UUID_ARGS(uuid), table->class->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(operation, "insert") || !strcmp(operation, "initial")) {
|
||||||
|
/* Insert row. */
|
||||||
|
if (!row) {
|
||||||
|
ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), json_row);
|
||||||
|
} else if (ovsdb_idl_row_is_orphan(row)) {
|
||||||
|
ovsdb_idl_insert_row(row, json_row);
|
||||||
|
} else {
|
||||||
|
VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to "
|
||||||
|
"table %s", UUID_ARGS(uuid), table->class->name);
|
||||||
|
ovsdb_idl_delete_row(row);
|
||||||
|
ovsdb_idl_insert_row(row, json_row);
|
||||||
|
}
|
||||||
|
} else if (!strcmp(operation, "modify")) {
|
||||||
|
/* Modify row. */
|
||||||
|
if (row) {
|
||||||
|
if (!ovsdb_idl_row_is_orphan(row)) {
|
||||||
|
return ovsdb_idl_modify_row_by_diff(row, json_row);
|
||||||
|
} else {
|
||||||
|
VLOG_WARN_RL(&semantic_rl, "cannot modify missing but "
|
||||||
|
"referenced row "UUID_FMT" in table %s",
|
||||||
|
UUID_ARGS(uuid), table->class->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" "
|
||||||
|
"in table %s", UUID_ARGS(uuid), table->class->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VLOG_WARN_RL(&semantic_rl, "unknown operation %s to "
|
||||||
|
"table %s", operation, table->class->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
ovsdb_idl_row_apply_diff(struct ovsdb_idl_row *row,
|
||||||
|
const struct json *diff_json)
|
||||||
{
|
{
|
||||||
struct ovsdb_idl_table *table = row->table;
|
struct ovsdb_idl_table *table = row->table;
|
||||||
struct shash_node *node;
|
struct shash_node *node;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
SHASH_FOR_EACH (node, json_object(row_json)) {
|
SHASH_FOR_EACH (node, json_object(diff_json)) {
|
||||||
const char *column_name = node->name;
|
const char *column_name = node->name;
|
||||||
const struct ovsdb_idl_column *column;
|
const struct ovsdb_idl_column *column;
|
||||||
struct ovsdb_datum datum;
|
struct ovsdb_datum diff;
|
||||||
struct ovsdb_error *error;
|
struct ovsdb_error *error;
|
||||||
|
|
||||||
column = shash_find_data(&table->columns, column_name);
|
column = shash_find_data(&table->columns, column_name);
|
||||||
@@ -1091,11 +1273,95 @@ ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL);
|
error = ovsdb_transient_datum_from_json(&diff, &column->type,
|
||||||
|
node->data);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
unsigned int column_idx = column - table->class->columns;
|
unsigned int column_idx = column - table->class->columns;
|
||||||
struct ovsdb_datum *old = &row->old[column_idx];
|
struct ovsdb_datum *old = &row->old[column_idx];
|
||||||
|
struct ovsdb_datum new;
|
||||||
|
struct ovsdb_error *error;
|
||||||
|
|
||||||
|
error = ovsdb_datum_apply_diff(&new, old, &diff, &column->type);
|
||||||
|
if (error) {
|
||||||
|
VLOG_WARN_RL(&syntax_rl, "update2 failed to modify column "
|
||||||
|
"%s row "UUID_FMT, column_name,
|
||||||
|
UUID_ARGS(&row->uuid));
|
||||||
|
ovsdb_error_destroy(error);
|
||||||
|
} else {
|
||||||
|
ovsdb_datum_swap(old, &new);
|
||||||
|
ovsdb_datum_destroy(&new, &column->type);
|
||||||
|
if (table->modes[column_idx] & OVSDB_IDL_ALERT) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ovsdb_datum_destroy(&diff, &column->type);
|
||||||
|
} else {
|
||||||
|
char *s = ovsdb_error_to_string(error);
|
||||||
|
VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT
|
||||||
|
" in table %s: %s", column_name,
|
||||||
|
UUID_ARGS(&row->uuid), table->class->name, s);
|
||||||
|
free(s);
|
||||||
|
ovsdb_error_destroy(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* Change 'row' either with the content of 'row_json' or by apply 'diff'.
|
||||||
|
* Caller needs to provide either valid 'row_json' or 'diff', but not
|
||||||
|
* both. */
|
||||||
|
static bool
|
||||||
|
ovsdb_idl_row_change__(struct ovsdb_idl_row *row, const struct json *row_json,
|
||||||
|
const struct json *diff_json,
|
||||||
|
enum ovsdb_idl_change change)
|
||||||
|
{
|
||||||
|
struct ovsdb_idl_table *table = row->table;
|
||||||
|
struct shash_node *node;
|
||||||
|
bool changed = false;
|
||||||
|
bool apply_diff = diff_json != NULL;
|
||||||
|
const struct json *json = apply_diff ? diff_json : row_json;
|
||||||
|
|
||||||
|
SHASH_FOR_EACH (node, json_object(json)) {
|
||||||
|
const char *column_name = node->name;
|
||||||
|
const struct ovsdb_idl_column *column;
|
||||||
|
struct ovsdb_datum datum;
|
||||||
|
struct ovsdb_error *error;
|
||||||
|
unsigned int column_idx;
|
||||||
|
struct ovsdb_datum *old;
|
||||||
|
|
||||||
|
column = shash_find_data(&table->columns, column_name);
|
||||||
|
if (!column) {
|
||||||
|
VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT,
|
||||||
|
column_name, UUID_ARGS(&row->uuid));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
column_idx = column - table->class->columns;
|
||||||
|
old = &row->old[column_idx];
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (apply_diff) {
|
||||||
|
struct ovsdb_datum diff;
|
||||||
|
|
||||||
|
ovs_assert(!row_json);
|
||||||
|
error = ovsdb_transient_datum_from_json(&diff, &column->type,
|
||||||
|
node->data);
|
||||||
|
if (!error) {
|
||||||
|
error = ovsdb_datum_apply_diff(&datum, old, &diff,
|
||||||
|
&column->type);
|
||||||
|
ovsdb_datum_destroy(&diff, &column->type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ovs_assert(!diff_json);
|
||||||
|
error = ovsdb_datum_from_json(&datum, &column->type, node->data,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
if (!ovsdb_datum_equals(old, &datum, &column->type)) {
|
if (!ovsdb_datum_equals(old, &datum, &column->type)) {
|
||||||
ovsdb_datum_swap(old, &datum);
|
ovsdb_datum_swap(old, &datum);
|
||||||
if (table->modes[column_idx] & OVSDB_IDL_ALERT) {
|
if (table->modes[column_idx] & OVSDB_IDL_ALERT) {
|
||||||
@@ -1128,6 +1394,21 @@ ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json,
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json,
|
||||||
|
enum ovsdb_idl_change change)
|
||||||
|
{
|
||||||
|
return ovsdb_idl_row_change__(row, row_json, NULL, change);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ovsdb_idl_row_apply_diff(struct ovsdb_idl_row *row,
|
||||||
|
const struct json *diff_json,
|
||||||
|
enum ovsdb_idl_change change)
|
||||||
|
{
|
||||||
|
return ovsdb_idl_row_change__(row, NULL, diff_json, change);
|
||||||
|
}
|
||||||
|
|
||||||
/* When a row A refers to row B through a column with a "refTable" constraint,
|
/* When a row A refers to row B through a column with a "refTable" constraint,
|
||||||
* but row B does not exist, row B is called an "orphan row". Orphan rows
|
* but row B does not exist, row B is called an "orphan row". Orphan rows
|
||||||
* should not persist, because the database enforces referential integrity, but
|
* should not persist, because the database enforces referential integrity, but
|
||||||
@@ -1378,6 +1659,21 @@ ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json)
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row *row,
|
||||||
|
const struct json *diff_json)
|
||||||
|
{
|
||||||
|
bool changed;
|
||||||
|
|
||||||
|
ovsdb_idl_row_unparse(row);
|
||||||
|
ovsdb_idl_row_clear_arcs(row, true);
|
||||||
|
changed = ovsdb_idl_row_apply_diff(row, diff_json,
|
||||||
|
OVSDB_IDL_CHANGE_MODIFY);
|
||||||
|
ovsdb_idl_row_parse(row);
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst)
|
may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst)
|
||||||
{
|
{
|
||||||
@@ -2760,7 +3056,8 @@ static void
|
|||||||
ovsdb_idl_update_has_lock(struct ovsdb_idl *idl, bool new_has_lock)
|
ovsdb_idl_update_has_lock(struct ovsdb_idl *idl, bool new_has_lock)
|
||||||
{
|
{
|
||||||
if (new_has_lock && !idl->has_lock) {
|
if (new_has_lock && !idl->has_lock) {
|
||||||
if (idl->state == IDL_S_MONITORING) {
|
if (idl->state == IDL_S_MONITORING ||
|
||||||
|
idl->state == IDL_S_MONITORING2) {
|
||||||
idl->change_seqno++;
|
idl->change_seqno++;
|
||||||
} else {
|
} else {
|
||||||
/* We're setting up a session, so don't signal that the database
|
/* We're setting up a session, so don't signal that the database
|
||||||
|
@@ -588,13 +588,13 @@ test-ovsdb|ovsdb_idl|link1 table in idltest database lacks l2 column (database n
|
|||||||
# Check that ovsdb-idl sent on "monitor" request and that it didn't
|
# Check that ovsdb-idl sent on "monitor" request and that it didn't
|
||||||
# mention that table or column, and (for paranoia) that it did mention another
|
# mention that table or column, and (for paranoia) that it did mention another
|
||||||
# table and column.
|
# table and column.
|
||||||
AT_CHECK([grep -c '"monitor"' stderr], [0], [1
|
AT_CHECK([grep -c '"monitor\|monitor2"' stderr], [0], [1
|
||||||
])
|
])
|
||||||
AT_CHECK([grep '"monitor"' stderr | grep link2], [1])
|
AT_CHECK([grep '"monitor\|monitor2"' stderr | grep link2], [1])
|
||||||
AT_CHECK([grep '"monitor"' stderr | grep l2], [1])
|
AT_CHECK([grep '"monitor\|monitor2"' stderr | grep l2], [1])
|
||||||
AT_CHECK([grep '"monitor"' stderr | grep -c '"link1"'], [0], [1
|
AT_CHECK([grep '"monitor\|monitor2"' stderr | grep -c '"link1"'], [0], [1
|
||||||
])
|
])
|
||||||
AT_CHECK([grep '"monitor"' stderr | grep -c '"ua"'], [0], [1
|
AT_CHECK([grep '"monitor\|monitor2"' stderr | grep -c '"ua"'], [0], [1
|
||||||
])
|
])
|
||||||
OVSDB_SERVER_SHUTDOWN
|
OVSDB_SERVER_SHUTDOWN
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
Reference in New Issue
Block a user