1 /* Copyright (c) 2009 Nicira Networks.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include "ovsdb-idl.h"
28 #include "ovsdb-data.h"
29 #include "ovsdb-error.h"
30 #include "ovsdb-idl-provider.h"
34 #define THIS_MODULE VLM_ovsdb_idl
37 /* An arc from one idl_row to another. When row A contains a UUID that
38 * references row B, this is represented by an arc from A (the source) to B
41 * Arcs from a row to itself are omitted, that is, src and dst are always
44 * Arcs are never duplicated, that is, even if there are multiple references
45 * from A to B, there is only a single arc from A to B.
47 * Arcs are directed: an arc from A to B is the converse of an an arc from B to
48 * A. Both an arc and its converse may both be present, if each row refers
49 * to the other circularly.
51 * The source and destination row may be in the same table or in different
54 struct ovsdb_idl_arc {
55 struct list src_node; /* In src->src_arcs list. */
56 struct list dst_node; /* In dst->dst_arcs list. */
57 struct ovsdb_idl_row *src; /* Source row. */
58 struct ovsdb_idl_row *dst; /* Destination row. */
62 const struct ovsdb_idl_class *class;
63 struct jsonrpc_session *session;
64 struct shash table_by_name;
65 struct ovsdb_idl_table *tables;
66 struct json *monitor_request_id;
67 unsigned int last_monitor_request_seqno;
68 unsigned int change_seqno;
70 /* Transaction support. */
71 struct ovsdb_idl_txn *txn;
72 struct hmap outstanding_txns;
75 struct ovsdb_idl_txn {
76 struct hmap_node hmap_node;
77 struct json *request_id;
78 struct ovsdb_idl *idl;
80 enum ovsdb_idl_txn_status status;
83 static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
84 static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5);
86 static void ovsdb_idl_clear(struct ovsdb_idl *);
87 static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *);
88 static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *);
89 static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *,
91 static void ovsdb_idl_process_update(struct ovsdb_idl_table *,
93 const struct json *old,
94 const struct json *new);
95 static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *);
96 static void ovsdb_idl_delete_row(struct ovsdb_idl_row *);
97 static void ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *);
99 static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *);
100 static struct ovsdb_idl_row *ovsdb_idl_row_create__(
101 const struct ovsdb_idl_table_class *);
102 static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *,
103 const struct uuid *);
104 static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *);
106 static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *);
107 static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *);
109 static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *);
110 static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl *,
111 const struct jsonrpc_msg *msg);
114 ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class)
116 struct ovsdb_idl *idl;
119 idl = xzalloc(sizeof *idl);
121 idl->session = jsonrpc_session_open(remote);
122 shash_init(&idl->table_by_name);
123 idl->tables = xmalloc(class->n_tables * sizeof *idl->tables);
124 for (i = 0; i < class->n_tables; i++) {
125 const struct ovsdb_idl_table_class *tc = &class->tables[i];
126 struct ovsdb_idl_table *table = &idl->tables[i];
129 assert(!shash_find(&idl->table_by_name, tc->name));
130 shash_add(&idl->table_by_name, tc->name, table);
132 shash_init(&table->columns);
133 for (j = 0; j < tc->n_columns; j++) {
134 const struct ovsdb_idl_column *column = &tc->columns[j];
136 assert(!shash_find(&table->columns, column->name));
137 shash_add(&table->columns, column->name, column);
139 hmap_init(&table->rows);
142 idl->last_monitor_request_seqno = UINT_MAX;
143 hmap_init(&idl->outstanding_txns);
149 ovsdb_idl_destroy(struct ovsdb_idl *idl)
155 ovsdb_idl_clear(idl);
156 jsonrpc_session_close(idl->session);
158 for (i = 0; i < idl->class->n_tables; i++) {
159 struct ovsdb_idl_table *table = &idl->tables[i];
160 shash_destroy(&table->columns);
161 hmap_destroy(&table->rows);
163 shash_destroy(&idl->table_by_name);
165 json_destroy(idl->monitor_request_id);
171 ovsdb_idl_clear(struct ovsdb_idl *idl)
173 bool changed = false;
176 for (i = 0; i < idl->class->n_tables; i++) {
177 struct ovsdb_idl_table *table = &idl->tables[i];
178 struct ovsdb_idl_row *row, *next_row;
180 if (hmap_is_empty(&table->rows)) {
185 HMAP_FOR_EACH_SAFE (row, next_row, struct ovsdb_idl_row, hmap_node,
187 struct ovsdb_idl_arc *arc, *next_arc;
189 if (!ovsdb_idl_row_is_orphan(row)) {
190 (row->table->class->unparse)(row);
191 ovsdb_idl_row_clear_old(row);
193 hmap_remove(&table->rows, &row->hmap_node);
194 LIST_FOR_EACH_SAFE (arc, next_arc, struct ovsdb_idl_arc, src_node,
198 /* No need to do anything with dst_arcs: some node has those arcs
199 * as forward arcs and will destroy them itself. */
211 ovsdb_idl_run(struct ovsdb_idl *idl)
215 jsonrpc_session_run(idl->session);
216 for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) {
217 struct jsonrpc_msg *msg, *reply;
220 seqno = jsonrpc_session_get_seqno(idl->session);
221 if (idl->last_monitor_request_seqno != seqno) {
222 idl->last_monitor_request_seqno = seqno;
223 ovsdb_idl_txn_abort_all(idl);
224 ovsdb_idl_send_monitor_request(idl);
228 msg = jsonrpc_session_recv(idl->session);
234 if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
235 reply = jsonrpc_create_reply(json_clone(msg->params), msg->id);
236 } else if (msg->type == JSONRPC_NOTIFY
237 && !strcmp(msg->method, "update")
238 && msg->params->type == JSON_ARRAY
239 && msg->params->u.array.n == 2
240 && msg->params->u.array.elems[0]->type == JSON_NULL) {
241 ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1]);
242 } else if (msg->type == JSONRPC_REPLY
243 && idl->monitor_request_id
244 && json_equal(idl->monitor_request_id, msg->id)) {
245 json_destroy(idl->monitor_request_id);
246 idl->monitor_request_id = NULL;
247 ovsdb_idl_clear(idl);
248 ovsdb_idl_parse_update(idl, msg->result);
249 } else if (msg->type == JSONRPC_REPLY
250 && msg->id && msg->id->type == JSON_STRING
251 && !strcmp(msg->id->u.string, "echo")) {
252 /* It's a reply to our echo request. Ignore it. */
253 } else if ((msg->type == JSONRPC_ERROR
254 || msg->type == JSONRPC_REPLY)
255 && ovsdb_idl_txn_process_reply(idl, msg)) {
256 /* ovsdb_idl_txn_process_reply() did everything needful. */
258 VLOG_WARN("%s: received unexpected %s message",
259 jsonrpc_session_get_name(idl->session),
260 jsonrpc_msg_type_to_string(msg->type));
261 jsonrpc_session_force_reconnect(idl->session);
264 jsonrpc_session_send(idl->session, reply);
266 jsonrpc_msg_destroy(msg);
271 ovsdb_idl_wait(struct ovsdb_idl *idl)
273 jsonrpc_session_wait(idl->session);
274 jsonrpc_session_recv_wait(idl->session);
278 ovsdb_idl_get_seqno(const struct ovsdb_idl *idl)
280 return idl->change_seqno;
284 ovsdb_idl_force_reconnect(struct ovsdb_idl *idl)
286 jsonrpc_session_force_reconnect(idl->session);
290 ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl)
292 struct json *monitor_requests;
293 struct jsonrpc_msg *msg;
296 monitor_requests = json_object_create();
297 for (i = 0; i < idl->class->n_tables; i++) {
298 const struct ovsdb_idl_table *table = &idl->tables[i];
299 const struct ovsdb_idl_table_class *tc = table->class;
300 struct json *monitor_request, *columns;
303 monitor_request = json_object_create();
304 columns = json_array_create_empty();
305 for (i = 0; i < tc->n_columns; i++) {
306 const struct ovsdb_idl_column *column = &tc->columns[i];
307 json_array_add(columns, json_string_create(column->name));
309 json_object_put(monitor_request, "columns", columns);
310 json_object_put(monitor_requests, tc->name, monitor_request);
313 json_destroy(idl->monitor_request_id);
314 msg = jsonrpc_create_request(
315 "monitor", json_array_create_2(json_null_create(), monitor_requests),
316 &idl->monitor_request_id);
317 jsonrpc_session_send(idl->session, msg);
321 ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates)
323 struct ovsdb_error *error;
327 error = ovsdb_idl_parse_update__(idl, table_updates);
329 if (!VLOG_DROP_WARN(&syntax_rl)) {
330 char *s = ovsdb_error_to_string(error);
331 VLOG_WARN_RL(&syntax_rl, "%s", s);
334 ovsdb_error_destroy(error);
338 static struct ovsdb_error *
339 ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
340 const struct json *table_updates)
342 const struct shash_node *tables_node;
344 if (table_updates->type != JSON_OBJECT) {
345 return ovsdb_syntax_error(table_updates, NULL,
346 "<table-updates> is not an object");
348 SHASH_FOR_EACH (tables_node, json_object(table_updates)) {
349 const struct json *table_update = tables_node->data;
350 const struct shash_node *table_node;
351 struct ovsdb_idl_table *table;
353 table = shash_find_data(&idl->table_by_name, tables_node->name);
355 return ovsdb_syntax_error(
357 "<table-updates> includes unknown table \"%s\"",
361 if (table_update->type != JSON_OBJECT) {
362 return ovsdb_syntax_error(table_update, NULL,
363 "<table-update> for table \"%s\" is "
364 "not an object", table->class->name);
366 SHASH_FOR_EACH (table_node, json_object(table_update)) {
367 const struct json *row_update = table_node->data;
368 const struct json *old_json, *new_json;
371 if (!uuid_from_string(&uuid, table_node->name)) {
372 return ovsdb_syntax_error(table_update, NULL,
373 "<table-update> for table \"%s\" "
375 "\"%s\" as member name",
379 if (row_update->type != JSON_OBJECT) {
380 return ovsdb_syntax_error(row_update, NULL,
381 "<table-update> for table \"%s\" "
382 "contains <row-update> for %s that "
388 old_json = shash_find_data(json_object(row_update), "old");
389 new_json = shash_find_data(json_object(row_update), "new");
390 if (old_json && old_json->type != JSON_OBJECT) {
391 return ovsdb_syntax_error(old_json, NULL,
392 "\"old\" <row> is not object");
393 } else if (new_json && new_json->type != JSON_OBJECT) {
394 return ovsdb_syntax_error(new_json, NULL,
395 "\"new\" <row> is not object");
396 } else if ((old_json != NULL) + (new_json != NULL)
397 != shash_count(json_object(row_update))) {
398 return ovsdb_syntax_error(row_update, NULL,
399 "<row-update> contains unexpected "
401 } else if (!old_json && !new_json) {
402 return ovsdb_syntax_error(row_update, NULL,
403 "<row-update> missing \"old\" "
404 "and \"new\" members");
407 ovsdb_idl_process_update(table, &uuid, old_json, new_json);
414 static struct ovsdb_idl_row *
415 ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid)
417 struct ovsdb_idl_row *row;
419 HMAP_FOR_EACH_WITH_HASH (row, struct ovsdb_idl_row, hmap_node,
420 uuid_hash(uuid), &table->rows) {
421 if (uuid_equals(&row->uuid, uuid)) {
429 ovsdb_idl_process_update(struct ovsdb_idl_table *table,
430 const struct uuid *uuid, const struct json *old,
431 const struct json *new)
433 struct ovsdb_idl_row *row;
435 row = ovsdb_idl_get_row(table, uuid);
438 if (row && !ovsdb_idl_row_is_orphan(row)) {
439 /* XXX perhaps we should check the 'old' values? */
440 ovsdb_idl_delete_row(row);
442 VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" "
444 UUID_ARGS(uuid), table->class->name);
449 ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
450 } else if (ovsdb_idl_row_is_orphan(row)) {
451 ovsdb_idl_insert_row(row, new);
453 VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to "
454 "table %s", UUID_ARGS(uuid), table->class->name);
455 ovsdb_idl_modify_row(row, new);
460 /* XXX perhaps we should check the 'old' values? */
461 if (!ovsdb_idl_row_is_orphan(row)) {
462 ovsdb_idl_modify_row(row, new);
464 VLOG_WARN_RL(&semantic_rl, "cannot modify missing but "
465 "referenced row "UUID_FMT" in table %s",
466 UUID_ARGS(uuid), table->class->name);
467 ovsdb_idl_insert_row(row, new);
470 VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" "
471 "in table %s", UUID_ARGS(uuid), table->class->name);
472 ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
478 ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json)
480 struct ovsdb_idl_table *table = row->table;
481 struct shash_node *node;
483 SHASH_FOR_EACH (node, json_object(row_json)) {
484 const char *column_name = node->name;
485 const struct ovsdb_idl_column *column;
486 struct ovsdb_datum datum;
487 struct ovsdb_error *error;
489 column = shash_find_data(&table->columns, column_name);
491 VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT,
492 column_name, UUID_ARGS(&row->uuid));
496 error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL);
498 ovsdb_datum_swap(&row->old[column - table->class->columns],
500 ovsdb_datum_destroy(&datum, &column->type);
502 char *s = ovsdb_error_to_string(error);
503 VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT
504 " in table %s: %s", column_name,
505 UUID_ARGS(&row->uuid), table->class->name, s);
507 ovsdb_error_destroy(error);
513 ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row)
519 ovsdb_idl_row_clear_old(struct ovsdb_idl_row *row)
521 assert(row->old == row->new);
522 if (!ovsdb_idl_row_is_orphan(row)) {
523 const struct ovsdb_idl_table_class *class = row->table->class;
526 for (i = 0; i < class->n_columns; i++) {
527 ovsdb_datum_destroy(&row->old[i], &class->columns[i].type);
530 row->old = row->new = NULL;
535 ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row)
537 if (row->old != row->new) {
539 const struct ovsdb_idl_table_class *class = row->table->class;
542 BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) {
543 ovsdb_datum_destroy(&row->new[i], &class->columns[i].type);
554 ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts)
556 struct ovsdb_idl_arc *arc, *next;
558 /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows
559 * that this causes to be unreferenced. */
560 LIST_FOR_EACH_SAFE (arc, next, struct ovsdb_idl_arc, src_node,
562 list_remove(&arc->dst_node);
564 && ovsdb_idl_row_is_orphan(arc->dst)
565 && list_is_empty(&arc->dst->dst_arcs)) {
566 ovsdb_idl_row_destroy(arc->dst);
570 list_init(&row->src_arcs);
573 /* Force nodes that reference 'row' to reparse. */
575 ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row, bool destroy_dsts)
577 struct ovsdb_idl_arc *arc, *next;
579 /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy
580 * 'arc', so we need to use the "safe" variant of list traversal. However,
581 * calling ref->table->class->parse will add an arc equivalent to 'arc' to
582 * row->arcs. That could be a problem for traversal, but it adds it at the
583 * beginning of the list to prevent us from stumbling upon it again.
585 * (If duplicate arcs were possible then we would need to make sure that
586 * 'next' didn't also point into 'arc''s destination, but we forbid
587 * duplicate arcs.) */
588 LIST_FOR_EACH_SAFE (arc, next, struct ovsdb_idl_arc, dst_node,
590 struct ovsdb_idl_row *ref = arc->src;
592 (ref->table->class->unparse)(ref);
593 ovsdb_idl_row_clear_arcs(ref, destroy_dsts);
594 (ref->table->class->parse)(ref);
598 static struct ovsdb_idl_row *
599 ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class)
601 struct ovsdb_idl_row *row = xmalloc(class->allocation_size);
602 memset(row, 0, sizeof *row);
603 list_init(&row->src_arcs);
604 list_init(&row->dst_arcs);
605 hmap_node_nullify(&row->txn_node);
609 static struct ovsdb_idl_row *
610 ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid)
612 struct ovsdb_idl_row *row = ovsdb_idl_row_create__(table->class);
613 hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid));
620 ovsdb_idl_row_destroy(struct ovsdb_idl_row *row)
623 ovsdb_idl_row_clear_old(row);
624 hmap_remove(&row->table->rows, &row->hmap_node);
630 ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json)
632 const struct ovsdb_idl_table_class *class = row->table->class;
635 assert(!row->old && !row->new);
636 row->old = row->new = xmalloc(class->n_columns * sizeof *row->old);
637 for (i = 0; i < class->n_columns; i++) {
638 ovsdb_datum_init_default(&row->old[i], &class->columns[i].type);
640 ovsdb_idl_row_update(row, row_json);
643 ovsdb_idl_row_reparse_backrefs(row, false);
647 ovsdb_idl_delete_row(struct ovsdb_idl_row *row)
649 (row->table->class->unparse)(row);
650 ovsdb_idl_row_clear_arcs(row, true);
651 ovsdb_idl_row_clear_old(row);
652 if (list_is_empty(&row->dst_arcs)) {
653 ovsdb_idl_row_destroy(row);
655 ovsdb_idl_row_reparse_backrefs(row, true);
660 ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json)
662 (row->table->class->unparse)(row);
663 ovsdb_idl_row_clear_arcs(row, true);
664 ovsdb_idl_row_update(row, row_json);
665 (row->table->class->parse)(row);
669 may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst)
671 const struct ovsdb_idl_arc *arc;
678 /* No duplicate arcs.
680 * We only need to test whether the first arc in dst->dst_arcs originates
681 * at 'src', since we add all of the arcs from a given source in a clump
682 * (in a single call to a row's ->parse function) and new arcs are always
683 * added at the front of the dst_arcs list. */
684 if (list_is_empty(&dst->dst_arcs)) {
687 arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node);
688 return arc->src != src;
691 static struct ovsdb_idl_table *
692 ovsdb_idl_table_from_class(const struct ovsdb_idl *idl,
693 const struct ovsdb_idl_table_class *table_class)
695 return &idl->tables[table_class - idl->class->tables];
698 struct ovsdb_idl_row *
699 ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src,
700 struct ovsdb_idl_table_class *dst_table_class,
701 const struct uuid *dst_uuid)
703 struct ovsdb_idl *idl = src->table->idl;
704 struct ovsdb_idl_table *dst_table;
705 struct ovsdb_idl_arc *arc;
706 struct ovsdb_idl_row *dst;
708 dst_table = ovsdb_idl_table_from_class(idl, dst_table_class);
709 dst = ovsdb_idl_get_row(dst_table, dst_uuid);
711 dst = ovsdb_idl_row_create(dst_table, dst_uuid);
714 /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */
715 if (may_add_arc(src, dst)) {
716 /* The arc *must* be added at the front of the dst_arcs list. See
717 * ovsdb_idl_row_reparse_backrefs() for details. */
718 arc = xmalloc(sizeof *arc);
719 list_push_front(&src->src_arcs, &arc->src_node);
720 list_push_front(&dst->dst_arcs, &arc->dst_node);
725 return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL;
728 static struct ovsdb_idl_row *
729 next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node)
731 for (; node; node = hmap_next(&table->rows, node)) {
732 struct ovsdb_idl_row *row;
734 row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node);
735 if (!ovsdb_idl_row_is_orphan(row)) {
742 struct ovsdb_idl_row *
743 ovsdb_idl_first_row(const struct ovsdb_idl *idl,
744 const struct ovsdb_idl_table_class *table_class)
746 struct ovsdb_idl_table *table
747 = ovsdb_idl_table_from_class(idl, table_class);
748 return next_real_row(table, hmap_first(&table->rows));
751 struct ovsdb_idl_row *
752 ovsdb_idl_next_row(const struct ovsdb_idl_row *row)
754 struct ovsdb_idl_table *table = row->table;
756 return next_real_row(table, hmap_next(&table->rows, &row->hmap_node));
761 static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn,
762 enum ovsdb_idl_txn_status);
765 ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status)
782 struct ovsdb_idl_txn *
783 ovsdb_idl_txn_create(struct ovsdb_idl *idl)
785 struct ovsdb_idl_txn *txn;
788 idl->txn = txn = xmalloc(sizeof *txn);
790 txn->status = TXN_INCOMPLETE;
791 hmap_init(&txn->txn_rows);
796 ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn)
798 ovsdb_idl_txn_abort(txn);
803 where_uuid_equals(const struct uuid *uuid)
808 json_string_create("_uuid"),
809 json_string_create("=="),
811 json_string_create("uuid"),
812 json_string_create_nocopy(
813 xasprintf(UUID_FMT, UUID_ARGS(uuid))))));
817 uuid_name_from_uuid(const struct uuid *uuid)
822 name = xasprintf("row"UUID_FMT, UUID_ARGS(uuid));
823 for (p = name; *p != '\0'; p++) {
832 static const struct ovsdb_idl_row *
833 ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid)
835 const struct ovsdb_idl_row *row;
837 HMAP_FOR_EACH_WITH_HASH (row, struct ovsdb_idl_row, txn_node,
838 uuid_hash(uuid), &txn->txn_rows) {
839 if (uuid_equals(&row->uuid, uuid)) {
846 /* XXX there must be a cleaner way to do this */
848 substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
850 if (json->type == JSON_ARRAY) {
854 if (json->u.array.n == 2
855 && json->u.array.elems[0]->type == JSON_STRING
856 && json->u.array.elems[1]->type == JSON_STRING
857 && !strcmp(json->u.array.elems[0]->u.string, "uuid")
858 && uuid_from_string(&uuid, json->u.array.elems[1]->u.string)) {
859 const struct ovsdb_idl_row *row;
861 row = ovsdb_idl_txn_get_row(txn, &uuid);
862 if (row && !row->old && row->new) {
865 return json_array_create_2(
866 json_string_create("named-uuid"),
867 json_string_create_nocopy(uuid_name_from_uuid(&uuid)));
871 for (i = 0; i < json->u.array.n; i++) {
872 json->u.array.elems[i] = substitute_uuids(json->u.array.elems[i],
875 } else if (json->type == JSON_OBJECT) {
876 struct shash_node *node;
878 SHASH_FOR_EACH (node, json_object(json)) {
879 node->data = substitute_uuids(node->data, txn);
886 ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn)
888 struct ovsdb_idl_row *row, *next;
890 HMAP_FOR_EACH_SAFE (row, next, struct ovsdb_idl_row, txn_node,
892 ovsdb_idl_row_clear_new(row);
900 hmap_remove(&txn->txn_rows, &row->txn_node);
901 hmap_node_nullify(&row->txn_node);
903 hmap_destroy(&txn->txn_rows);
904 hmap_init(&txn->txn_rows);
907 enum ovsdb_idl_txn_status
908 ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)
910 struct ovsdb_idl_row *row;
911 struct json *operations;
913 enum ovsdb_idl_txn_status status;
915 if (txn != txn->idl->txn) {
919 operations = json_array_create_empty();
921 /* Add prerequisites and declarations of new rows. */
922 HMAP_FOR_EACH (row, struct ovsdb_idl_row, txn_node, &txn->txn_rows) {
923 /* XXX check that deleted rows exist even if no prereqs? */
925 const struct ovsdb_idl_table_class *class = row->table->class;
926 size_t n_columns = class->n_columns;
927 struct json *op, *columns, *row_json;
930 op = json_object_create();
931 json_array_add(operations, op);
932 json_object_put_string(op, "op", "wait");
933 json_object_put_string(op, "table", class->name);
934 json_object_put(op, "timeout", json_integer_create(0));
935 json_object_put(op, "where", where_uuid_equals(&row->uuid));
936 json_object_put_string(op, "until", "==");
937 columns = json_array_create_empty();
938 json_object_put(op, "columns", columns);
939 row_json = json_object_create();
940 json_object_put(op, "rows", json_array_create_1(row_json));
942 BITMAP_FOR_EACH_1 (idx, n_columns, row->prereqs) {
943 const struct ovsdb_idl_column *column = &class->columns[idx];
944 json_array_add(columns, json_string_create(column->name));
945 json_object_put(row_json, column->name,
946 ovsdb_datum_to_json(&row->old[idx],
950 if (row->new && !row->old) {
953 op = json_object_create();
954 json_array_add(operations, op);
955 json_object_put_string(op, "op", "declare");
956 json_object_put(op, "uuid-name",
957 json_string_create_nocopy(
958 uuid_name_from_uuid(&row->uuid)));
964 HMAP_FOR_EACH (row, struct ovsdb_idl_row, txn_node, &txn->txn_rows) {
965 const struct ovsdb_idl_table_class *class = row->table->class;
966 size_t n_columns = class->n_columns;
967 struct json *row_json;
970 if (row->old == row->new) {
972 } else if (!row->new) {
973 struct json *op = json_object_create();
974 json_object_put_string(op, "op", "delete");
975 json_object_put_string(op, "table", class->name);
976 json_object_put(op, "where", where_uuid_equals(&row->uuid));
977 json_array_add(operations, op);
980 BITMAP_FOR_EACH_1 (idx, n_columns, row->written) {
981 const struct ovsdb_idl_column *column = &class->columns[idx];
984 && ovsdb_datum_equals(&row->old[idx], &row->new[idx],
989 struct json *op = json_object_create();
990 json_array_add(operations, op);
991 json_object_put_string(op, "op",
992 row->old ? "update" : "insert");
993 json_object_put_string(op, "table", class->name);
995 json_object_put(op, "where",
996 where_uuid_equals(&row->uuid));
998 json_object_put(op, "uuid-name",
999 json_string_create_nocopy(
1000 uuid_name_from_uuid(&row->uuid)));
1002 row_json = json_object_create();
1003 json_object_put(op, "row", row_json);
1006 json_object_put(row_json, column->name,
1008 ovsdb_datum_to_json(&row->new[idx],
1015 status = (!any_updates ? TXN_SUCCESS
1016 : jsonrpc_session_send(
1018 jsonrpc_create_request(
1019 "transact", operations, &txn->request_id))
1023 hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node,
1024 json_hash(txn->request_id, 0));
1025 txn->idl->txn = NULL;
1027 ovsdb_idl_txn_disassemble(txn);
1028 if (status != TXN_INCOMPLETE) {
1029 ovsdb_idl_txn_complete(txn, status);
1035 ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn)
1037 ovsdb_idl_txn_disassemble(txn);
1038 if (txn->status == TXN_INCOMPLETE) {
1039 txn->status = TXN_ABORTED;
1044 ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn,
1045 enum ovsdb_idl_txn_status status)
1047 txn->status = status;
1048 hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node);
1052 ovsdb_idl_txn_write(struct ovsdb_idl_row *row,
1053 const struct ovsdb_idl_column *column,
1054 struct ovsdb_datum *datum)
1056 const struct ovsdb_idl_table_class *class = row->table->class;
1057 size_t column_idx = column - class->columns;
1060 if (hmap_node_is_null(&row->txn_node)) {
1061 hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
1062 uuid_hash(&row->uuid));
1064 if (row->old == row->new) {
1065 row->new = xmalloc(class->n_columns * sizeof *row->new);
1067 if (!row->written) {
1068 row->written = bitmap_allocate(class->n_columns);
1070 if (bitmap_is_set(row->written, column_idx)) {
1071 ovsdb_datum_destroy(&row->new[column_idx], &column->type);
1073 bitmap_set1(row->written, column_idx);
1075 row->new[column_idx] = *datum;
1079 ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_,
1080 const struct ovsdb_idl_column *column)
1082 struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_;
1083 const struct ovsdb_idl_table_class *class = row->table->class;
1084 size_t column_idx = column - class->columns;
1088 || (row->written && bitmap_is_set(row->written, column_idx))) {
1092 if (hmap_node_is_null(&row->txn_node)) {
1093 hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
1094 uuid_hash(&row->uuid));
1096 if (!row->prereqs) {
1097 row->prereqs = bitmap_allocate(class->n_columns);
1099 bitmap_set1(row->prereqs, column_idx);
1103 ovsdb_idl_txn_delete(struct ovsdb_idl_row *row)
1107 ovsdb_idl_row_clear_new(row);
1108 assert(!row->prereqs);
1109 hmap_remove(&row->table->idl->txn->txn_rows, &row->txn_node);
1112 if (hmap_node_is_null(&row->txn_node)) {
1113 hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
1114 uuid_hash(&row->uuid));
1116 if (row->new == row->old) {
1119 ovsdb_idl_row_clear_new(row);
1123 struct ovsdb_idl_row *
1124 ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn,
1125 const struct ovsdb_idl_table_class *class)
1127 struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
1128 uuid_generate(&row->uuid);
1129 row->table = ovsdb_idl_table_from_class(txn->idl, class);
1130 row->new = xmalloc(class->n_columns * sizeof *row->new);
1131 row->written = bitmap_allocate(class->n_columns);
1132 hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
1137 ovsdb_idl_txn_abort_all(struct ovsdb_idl *idl)
1139 struct ovsdb_idl_txn *txn;
1141 HMAP_FOR_EACH (txn, struct ovsdb_idl_txn, hmap_node,
1142 &idl->outstanding_txns) {
1143 ovsdb_idl_txn_complete(txn, TXN_TRY_AGAIN);
1147 static struct ovsdb_idl_txn *
1148 ovsdb_idl_txn_find(struct ovsdb_idl *idl, const struct json *id)
1150 struct ovsdb_idl_txn *txn;
1152 HMAP_FOR_EACH_WITH_HASH (txn, struct ovsdb_idl_txn, hmap_node,
1153 json_hash(id, 0), &idl->outstanding_txns) {
1154 if (json_equal(id, txn->request_id)) {
1162 ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl,
1163 const struct jsonrpc_msg *msg)
1165 struct ovsdb_idl_txn *txn;
1166 enum ovsdb_idl_txn_status status;
1168 txn = ovsdb_idl_txn_find(idl, msg->id);
1173 if (msg->type == JSONRPC_ERROR) {
1175 } else if (msg->result->type != JSON_ARRAY) {
1176 VLOG_WARN_RL(&syntax_rl, "reply to \"transact\" is not JSON array");
1179 int hard_errors = 0;
1180 int soft_errors = 0;
1183 for (i = 0; i < msg->result->u.array.n; i++) {
1184 struct json *json = msg->result->u.array.elems[i];
1186 if (json->type == JSON_NULL) {
1187 /* This isn't an error in itself but indicates that some prior
1188 * operation failed, so make sure that we know about it. */
1190 } else if (json->type == JSON_OBJECT) {
1193 error = shash_find_data(json_object(json), "error");
1195 if (error->type == JSON_STRING) {
1196 if (!strcmp(error->u.string, "timed out")) {
1203 VLOG_WARN_RL(&syntax_rl,
1204 "\"error\" in reply is not JSON string");
1209 VLOG_WARN_RL(&syntax_rl,
1210 "operation reply is not JSON null or object");
1214 status = (hard_errors ? TXN_ERROR
1215 : soft_errors ? TXN_TRY_AGAIN
1219 ovsdb_idl_txn_complete(txn, status);