From patchwork Tue Nov 24 22:16:05 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Zhou X-Patchwork-Id: 548319 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (unknown [IPv6:2600:3c00::f03c:91ff:fe6e:bdf7]) by ozlabs.org (Postfix) with ESMTP id 8CC041402A8 for ; Wed, 25 Nov 2015 09:17:11 +1100 (AEDT) Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id 3EB2810A49; Tue, 24 Nov 2015 14:16:36 -0800 (PST) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v3.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id 8F87E10A20 for ; Tue, 24 Nov 2015 14:16:32 -0800 (PST) Received: from bar3.cudamail.com (localhost [127.0.0.1]) by mx3v3.cudamail.com (Postfix) with ESMTPS id 24F441615E8 for ; Tue, 24 Nov 2015 15:16:32 -0700 (MST) X-ASG-Debug-ID: 1448403391-03dd7b4647350dc0001-byXFYA Received: from mx3-pf2.cudamail.com ([192.168.14.1]) by bar3.cudamail.com with ESMTP id ztY47LDIsKh4vt8O (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 24 Nov 2015 15:16:31 -0700 (MST) X-Barracuda-Envelope-From: azhou.ovn@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.1 Received: from unknown (HELO mail-pa0-f54.google.com) (209.85.220.54) by mx3-pf2.cudamail.com with ESMTPS (RC4-SHA encrypted); 24 Nov 2015 22:16:31 -0000 Received-SPF: pass (mx3-pf2.cudamail.com: SPF record at _netblocks.google.com designates 209.85.220.54 as permitted sender) X-Barracuda-RBL-Trusted-Forwarder: 209.85.220.54 Received: by pacej9 with SMTP id ej9so34924081pac.2 for ; Tue, 24 Nov 2015 14:16:30 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8B1WVRkX8oCO6wrqGRmIXQw83u7xl9FGXrxRULrZmCA=; b=WfzOmNX9WBImXn/8d/LLX0bOYMF21ayh0xx0M7NNH15n1wpL//l0WF00wIhfVVtoPC tiII4kyOKpHaZrkELmqg+S1LEFbNp5rMjORyQ/eEbs8za7zNKdWrjYw9yhMthYuAj1j9 zKDZMpQlEVrL8r39A0I1u6zwBCl8TM6rY4bd6MZWVoV8OAWpag4OYdXM/Mlt3qWZfm0B rHBapgdS0jTYmaIFH90XzyckRZAByxMHqyZqh4Xdu5G9TWF4hvzcGkGl5wiNOB0nbz/f 7IkSKuaFigrL9pJALsF3iurHaCkKIQp89QrV/d8l0dMTg1EVuAtw3IVsh7YzE0W3f35s htqw== X-Received: by 10.98.66.80 with SMTP id p77mr26313605pfa.100.1448403390866; Tue, 24 Nov 2015 14:16:30 -0800 (PST) Received: from ubuntu.localdomain ([208.91.1.34]) by smtp.gmail.com with ESMTPSA id b15sm16374570pfj.7.2015.11.24.14.16.29 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 24 Nov 2015 14:16:30 -0800 (PST) X-CudaMail-Envelope-Sender: azhou.ovn@gmail.com X-Barracuda-Apparent-Source-IP: 208.91.1.34 From: Andy Zhou To: dev@openvswitch.org X-CudaMail-Whitelist-To: dev@openvswitch.org X-CudaMail-MID: CM-V2-1123060688 X-CudaMail-DTE: 112415 X-CudaMail-Originating-IP: 209.85.220.54 Date: Tue, 24 Nov 2015 14:16:05 -0800 X-ASG-Orig-Subj: [##CM-V2-1123060688##][monitor2 v2 8/9] lib: add monitor2 support in ovsdb-idl. Message-Id: <1448403366-21460-8-git-send-email-azhou@ovn.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1448403366-21460-1-git-send-email-azhou@ovn.org> References: <1448403366-21460-1-git-send-email-azhou@ovn.org> X-Barracuda-Connect: UNKNOWN[192.168.14.1] X-Barracuda-Start-Time: 1448403391 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-ASG-Whitelist: Header =?UTF-8?B?eFwtY3VkYW1haWxcLXdoaXRlbGlzdFwtdG8=?= X-Barracuda-BRTS-Status: 1 X-Virus-Scanned: by bsmtpd at cudamail.com Subject: [ovs-dev] [monitor2 v2 8/9] lib: add monitor2 support in ovsdb-idl. X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" From: Andy Zhou 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 --- v1->v2: * extra check in error case: In the error case here, it could be worthwhile to check that the msg->id matches idl->request_id: } else if (msg->type == JSONRPC_ERROR && idl->state == IDL_S_MONITOR2_REQUESTED) { * merge ovsdb_idl_parse_update2__() and ovsdb_idl_parse_update__() into a single function to reduce code duplication. * Rebase and merge with change tracking feature introduced by: commit 932104f483ef4384d15dec1d26661da8da58de8d Author: Shad Ansari Date: Tue Oct 27 13:55:35 2015 -0700 ovsdb-idl: Add support for change tracking. --- lib/ovsdb-idl.c | 409 +++++++++++++++++++++++++++++++++++++++++++++-------- tests/ovsdb-idl.at | 10 +- 2 files changed, 358 insertions(+), 61 deletions(-) diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c index 588582a..6353cf8 100644 --- a/lib/ovsdb-idl.c +++ b/lib/ovsdb-idl.c @@ -78,7 +78,9 @@ struct ovsdb_idl_arc { enum ovsdb_idl_state { IDL_S_SCHEMA_REQUESTED, IDL_S_MONITOR_REQUESTED, - IDL_S_MONITORING + IDL_S_MONITORING, + IDL_S_MONITOR2_REQUESTED, + IDL_S_MONITORING2 }; struct ovsdb_idl { @@ -93,6 +95,7 @@ struct ovsdb_idl { unsigned int state_seqno; enum ovsdb_idl_state state; struct json *request_id; + struct json *schema; /* Database locking. */ 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. */ }; +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 semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5); static void ovsdb_idl_clear(struct ovsdb_idl *); static void ovsdb_idl_send_schema_request(struct ovsdb_idl *); -static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *, - const struct json *schema_json); -static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *); +static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *); +static void ovsdb_idl_send_monitor2_request(struct ovsdb_idl *); +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 *, - const struct json *); + const struct json *, + enum ovsdb_update_version); static bool ovsdb_idl_process_update(struct ovsdb_idl_table *, const struct uuid *, const struct json *old, 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_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_by_diff(struct ovsdb_idl_row *, + const struct json *); static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *); 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->request_id = NULL; + idl->schema = NULL; hmap_init(&idl->outstanding_txns); @@ -270,6 +293,7 @@ ovsdb_idl_destroy(struct ovsdb_idl *idl) json_destroy(idl->request_id); free(idl->lock_name); json_destroy(idl->lock_request_id); + json_destroy(idl->schema); hmap_destroy(&idl->outstanding_txns); free(idl); } @@ -351,38 +375,59 @@ ovsdb_idl_run(struct ovsdb_idl *idl) } if (msg->type == JSONRPC_NOTIFY - && !strcmp(msg->method, "update") + && !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_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. */ - json_destroy(idl->request_id); - idl->request_id = NULL; - ovsdb_idl_send_monitor_request(idl, msg->result); - idl->state = IDL_S_MONITOR_REQUESTED; + idl->schema = json_clone(msg->result); + ovsdb_idl_send_monitor2_request(idl); + idl->state = IDL_S_MONITOR2_REQUESTED; break; case IDL_S_MONITOR_REQUESTED: - /* Reply to our "monitor" request. */ + case IDL_S_MONITOR2_REQUESTED: + /* Reply to our "monitor" or "monitor2" 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); + 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") + && 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_UPDATE); } else if (msg->type == JSONRPC_REPLY && idl->lock_request_id && json_equal(idl->lock_request_id, msg->id)) { @@ -396,6 +441,18 @@ ovsdb_idl_run(struct ovsdb_idl *idl) && !strcmp(msg->method, "stolen")) { /* Someone else stole our lock. */ 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 || msg->type == JSONRPC_REPLY) && ovsdb_idl_txn_process_reply(idl, msg)) { @@ -843,14 +900,15 @@ parse_schema(const struct json *schema_json) } static void -ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, - const struct json *schema_json) +ovsdb_idl_send_monitor_request__(struct ovsdb_idl *idl, + const char *method) { - struct shash *schema = parse_schema(schema_json); + struct shash *schema; struct json *monitor_requests; struct jsonrpc_msg *msg; size_t i; + schema = parse_schema(idl->schema); monitor_requests = json_object_create(); for (i = 0; i < idl->class->n_tables; i++) { const struct ovsdb_idl_table *table = &idl->tables[i]; @@ -900,7 +958,7 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, json_destroy(idl->request_id); msg = jsonrpc_create_request( - "monitor", + method, json_array_create_3(json_string_create(idl->class->database), json_null_create(), monitor_requests), &idl->request_id); @@ -908,29 +966,55 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, } 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)) { char *s = ovsdb_error_to_string(error); VLOG_WARN_RL(&syntax_rl, "%s", s); free(s); } 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 * 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 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) { return ovsdb_syntax_error(table_updates, NULL, - " is not an object"); + "<%s> is not an object", + table_updates_name); } + SHASH_FOR_EACH (tables_node, json_object(table_updates)) { const struct json *table_update = tables_node->data; const struct shash_node *table_node; @@ -940,14 +1024,17 @@ ovsdb_idl_parse_update__(struct ovsdb_idl *idl, if (!table) { return ovsdb_syntax_error( table_updates, NULL, - " includes unknown table \"%s\"", + "<%s> includes unknown table \"%s\"", + table_updates_name, tables_node->name); } if (table_update->type != JSON_OBJECT) { return ovsdb_syntax_error(table_update, NULL, - " for table \"%s\" is " - "not an object", table->class->name); + "<%s> for table \"%s\" is " + "not an object", + table_update_name, + table->class->name); } SHASH_FOR_EACH (table_node, json_object(table_update)) { const struct json *row_update = table_node->data; @@ -956,42 +1043,81 @@ ovsdb_idl_parse_update__(struct ovsdb_idl *idl, if (!uuid_from_string(&uuid, table_node->name)) { return ovsdb_syntax_error(table_update, NULL, - " for table \"%s\" " + "<%s> for table \"%s\" " "contains bad UUID " "\"%s\" as member name", + table_update_name, table->class->name, table_node->name); } if (row_update->type != JSON_OBJECT) { return ovsdb_syntax_error(row_update, NULL, - " for table \"%s\" " - "contains for %s that " + "<%s> for table \"%s\" " + "contains <%s> for %s that " "is not an object", + table_update_name, table->class->name, + row_update_name, table_node->name); } - old_json = shash_find_data(json_object(row_update), "old"); - new_json = shash_find_data(json_object(row_update), "new"); - if (old_json && old_json->type != JSON_OBJECT) { - return ovsdb_syntax_error(old_json, NULL, - "\"old\" is not object"); - } else if (new_json && new_json->type != JSON_OBJECT) { - return ovsdb_syntax_error(new_json, NULL, - "\"new\" is not object"); - } else if ((old_json != NULL) + (new_json != NULL) - != shash_count(json_object(row_update))) { - return ovsdb_syntax_error(row_update, NULL, - " contains unexpected " - "member"); - } else if (!old_json && !new_json) { - return ovsdb_syntax_error(row_update, NULL, - " missing \"old\" " - "and \"new\" members"); + switch(version) { + case OVSDB_UPDATE: + old_json = shash_find_data(json_object(row_update), "old"); + new_json = shash_find_data(json_object(row_update), "new"); + if (old_json && old_json->type != JSON_OBJECT) { + return ovsdb_syntax_error(old_json, NULL, + "\"old\" is not object"); + } else if (new_json && new_json->type != JSON_OBJECT) { + return ovsdb_syntax_error(new_json, NULL, + "\"new\" is not object"); + } else if ((old_json != NULL) + (new_json != NULL) + != shash_count(json_object(row_update))) { + return ovsdb_syntax_error(row_update, NULL, + " contains " + "unexpected member"); + } else if (!old_json && !new_json) { + return ovsdb_syntax_error(row_update, NULL, + " 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, + " includes unknown " + "object"); + } + break; } - if (ovsdb_idl_process_update(table, &uuid, old_json, new_json)) { - idl->change_seqno++; + default: + OVS_NOT_REACHED(); } } } @@ -1069,17 +1195,73 @@ ovsdb_idl_process_update(struct ovsdb_idl_table *table, /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false * otherwise. */ static bool -ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json, - enum ovsdb_idl_change change) +ovsdb_idl_process_update2(struct ovsdb_idl_table *table, + 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 shash_node *node; 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 struct ovsdb_idl_column *column; - struct ovsdb_datum datum; + struct ovsdb_datum diff; struct ovsdb_error *error; column = shash_find_data(&table->columns, column_name); @@ -1089,11 +1271,95 @@ ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json, 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) { unsigned int column_idx = column - table->class->columns; 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)) { ovsdb_datum_swap(old, &datum); if (table->modes[column_idx] & OVSDB_IDL_ALERT) { @@ -1126,6 +1392,21 @@ ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json, 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, * 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 @@ -1377,6 +1658,21 @@ ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json) } 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 may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst) { const struct ovsdb_idl_arc *arc; @@ -2758,7 +3054,8 @@ static void ovsdb_idl_update_has_lock(struct ovsdb_idl *idl, bool new_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++; } else { /* We're setting up a session, so don't signal that the database diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at index 244f3e9..2009dd7 100644 --- a/tests/ovsdb-idl.at +++ b/tests/ovsdb-idl.at @@ -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 # mention that table or column, and (for paranoia) that it did mention another # 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"' stderr | grep l2], [1]) -AT_CHECK([grep '"monitor"' stderr | grep -c '"link1"'], [0], [1 +AT_CHECK([grep '"monitor\|monitor2"' stderr | grep link2], [1]) +AT_CHECK([grep '"monitor\|monitor2"' stderr | grep l2], [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 AT_CLEANUP