X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fovsdb-idl.c;h=3f3ce555ac3f4ec413352107ef65e40730ade696;hb=fb0d597fb64308c60001e3afc9b31eb295dedb6b;hp=39d65951312f8db120c06184ddf9469001dd7b57;hpb=69490970b3555fd45f844b4f6304bc2dc6a01b0b;p=openvswitch diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c index 39d65951..3f3ce555 100644 --- a/lib/ovsdb-idl.c +++ b/lib/ovsdb-idl.c @@ -25,6 +25,7 @@ #include "bitmap.h" #include "dynamic-string.h" +#include "fatal-signal.h" #include "json.h" #include "jsonrpc.h" #include "ovsdb-data.h" @@ -81,6 +82,7 @@ struct ovsdb_idl_txn { struct ovsdb_idl *idl; struct hmap txn_rows; enum ovsdb_idl_txn_status status; + char *error; bool dry_run; struct ds comment; @@ -212,9 +214,7 @@ ovsdb_idl_clear(struct ovsdb_idl *idl) if (!ovsdb_idl_row_is_orphan(row)) { ovsdb_idl_row_unparse(row); - ovsdb_idl_row_clear_old(row); } - hmap_remove(&table->rows, &row->hmap_node); LIST_FOR_EACH_SAFE (arc, next_arc, struct ovsdb_idl_arc, src_node, &row->src_arcs) { free(arc); @@ -222,7 +222,7 @@ ovsdb_idl_clear(struct ovsdb_idl *idl) /* No need to do anything with dst_arcs: some node has those arcs * as forward arcs and will destroy them itself. */ - free(row); + ovsdb_idl_row_destroy(row); } } @@ -343,7 +343,9 @@ ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl) json_destroy(idl->monitor_request_id); msg = jsonrpc_create_request( - "monitor", json_array_create_2(json_null_create(), monitor_requests), + "monitor", + json_array_create_3(json_string_create(idl->class->database), + json_null_create(), monitor_requests), &idl->monitor_request_id); jsonrpc_session_send(idl->session, msg); } @@ -540,10 +542,41 @@ ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json) } } +/* 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 + * they can appear transiently as changes from the database are received (the + * database doesn't try to topologically sort them and circular references mean + * it isn't always possible anyhow). + * + * This function returns true if 'row' is an orphan row, otherwise false. + */ static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row) { - return !row->old; + return !row->old && !row->new; +} + +/* Returns true if 'row' is conceptually part of the database as modified by + * the current transaction (if any), false otherwise. + * + * This function will return true if 'row' is not an orphan (see the comment on + * ovsdb_idl_row_is_orphan()) and: + * + * - 'row' exists in the database and has not been deleted within the + * current transaction (if any). + * + * - 'row' was inserted within the current transaction and has not been + * deleted. (In the latter case you should not have passed 'row' in at + * all, because ovsdb_idl_txn_delete() freed it.) + * + * This function will return false if 'row' is an orphan or if 'row' was + * deleted within the current transaction. + */ +static bool +ovsdb_idl_row_exists(const struct ovsdb_idl_row *row) +{ + return row->new != NULL; } static void @@ -627,7 +660,7 @@ ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts) /* Force nodes that reference 'row' to reparse. */ static void -ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row, bool destroy_dsts) +ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row) { struct ovsdb_idl_arc *arc, *next; @@ -646,7 +679,7 @@ ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row, bool destroy_dsts) struct ovsdb_idl_row *ref = arc->src; ovsdb_idl_row_unparse(ref); - ovsdb_idl_row_clear_arcs(ref, destroy_dsts); + ovsdb_idl_row_clear_arcs(ref, false); ovsdb_idl_row_parse(ref); } } @@ -695,7 +728,7 @@ ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json) ovsdb_idl_row_update(row, row_json); ovsdb_idl_row_parse(row); - ovsdb_idl_row_reparse_backrefs(row, false); + ovsdb_idl_row_reparse_backrefs(row); } static void @@ -707,7 +740,7 @@ ovsdb_idl_delete_row(struct ovsdb_idl_row *row) if (list_is_empty(&row->dst_arcs)) { ovsdb_idl_row_destroy(row); } else { - ovsdb_idl_row_reparse_backrefs(row, true); + ovsdb_idl_row_reparse_backrefs(row); } } @@ -809,7 +842,7 @@ next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node) struct ovsdb_idl_row *row; row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node); - if (row->new || !ovsdb_idl_row_is_orphan(row)) { + if (ovsdb_idl_row_exists(row)) { return row; } } @@ -865,25 +898,39 @@ ovsdb_idl_txn_create(struct ovsdb_idl *idl) assert(!idl->txn); idl->txn = txn = xmalloc(sizeof *txn); + txn->request_id = NULL; txn->idl = idl; - txn->status = TXN_INCOMPLETE; hmap_init(&txn->txn_rows); + txn->status = TXN_INCOMPLETE; + txn->error = NULL; txn->dry_run = false; ds_init(&txn->comment); + txn->inc_table = NULL; txn->inc_column = NULL; txn->inc_where = NULL; + hmap_init(&txn->inserted_rows); + return txn; } +/* Appends 's', which is treated as a printf()-type format string, to the + * comments that will be passed to the OVSDB server when 'txn' is committed. + * (The comment will be committed to the OVSDB log, which "ovsdb-tool + * show-log" can print in a relatively human-readable form.) */ void -ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s) +ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...) { + va_list args; + if (txn->comment.length) { ds_put_char(&txn->comment, '\n'); } - ds_put_cstr(&txn->comment, s); + + va_start(args, s); + ds_put_format_valist(&txn->comment, s, args); + va_end(args); } void @@ -907,11 +954,13 @@ ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn) { struct ovsdb_idl_txn_insert *insert, *next; + json_destroy(txn->request_id); if (txn->status == TXN_INCOMPLETE) { hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); } ovsdb_idl_txn_abort(txn); ds_destroy(&txn->comment); + free(txn->error); free(txn->inc_table); free(txn->inc_column); json_destroy(txn->inc_where); @@ -919,6 +968,7 @@ ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn) &txn->inserted_rows) { free(insert); } + hmap_destroy(&txn->inserted_rows); free(txn); } @@ -1033,7 +1083,7 @@ ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn) ovsdb_idl_row_parse(row); } } else { - hmap_remove(&row->table->rows, &row->hmap_node); + ovsdb_idl_row_unparse(row); } ovsdb_idl_row_clear_new(row); @@ -1045,6 +1095,10 @@ ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn) hmap_remove(&txn->txn_rows, &row->txn_node); hmap_node_nullify(&row->txn_node); + if (!row->old) { + hmap_remove(&row->table->rows, &row->hmap_node); + free(row); + } } hmap_destroy(&txn->txn_rows); hmap_init(&txn->txn_rows); @@ -1061,7 +1115,8 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) return txn->status; } - operations = json_array_create_empty(); + operations = json_array_create_1( + json_string_create(txn->idl->class->database)); /* Add prerequisites and declarations of new rows. */ HMAP_FOR_EACH (row, struct ovsdb_idl_row, txn_node, &txn->txn_rows) { @@ -1092,16 +1147,6 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) &column->type)); } } - if (row->new && !row->old) { - struct json *op; - - op = json_object_create(); - json_array_add(operations, op); - json_object_put_string(op, "op", "declare"); - json_object_put(op, "uuid-name", - json_string_create_nocopy( - uuid_name_from_uuid(&row->uuid))); - } } /* Add updates. */ @@ -1137,7 +1182,7 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) insert = xmalloc(sizeof *insert); insert->dummy = row->uuid; - insert->op_index = operations->u.array.n; + insert->op_index = operations->u.array.n - 1; uuid_zero(&insert->real); hmap_insert(&txn->inserted_rows, &insert->hmap_node, uuid_hash(&insert->dummy)); @@ -1173,7 +1218,7 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) if (txn->inc_table && any_updates) { struct json *op; - txn->inc_index = operations->u.array.n; + txn->inc_index = operations->u.array.n - 1; op = json_object_create(); json_object_put_string(op, "op", "mutate"); @@ -1222,13 +1267,31 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node, json_hash(txn->request_id, 0)); } else { - txn->status = TXN_INCOMPLETE; + txn->status = TXN_TRY_AGAIN; } ovsdb_idl_txn_disassemble(txn); return txn->status; } +/* Attempts to commit 'txn', blocking until the commit either succeeds or + * fails. Returns the final commit status, which may be any TXN_* value other + * than TXN_INCOMPLETE. */ +enum ovsdb_idl_txn_status +ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn) +{ + enum ovsdb_idl_txn_status status; + + fatal_signal_run(); + while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { + ovsdb_idl_run(txn->idl); + ovsdb_idl_wait(txn->idl); + ovsdb_idl_txn_wait(txn); + poll_block(); + } + return status; +} + int64_t ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn) { @@ -1245,6 +1308,27 @@ ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn) } } +const char * +ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn) +{ + if (txn->status != TXN_ERROR) { + return ovsdb_idl_txn_status_to_string(txn->status); + } else if (txn->error) { + return txn->error; + } else { + return "no error details available"; + } +} + +static void +ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn, + const struct json *json) +{ + if (txn->error == NULL) { + txn->error = json_to_string(json, JSSF_SORT); + } +} + /* For transaction 'txn' that completed successfully, finds and returns the * permanent UUID that the database assigned to a newly inserted row, given the * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row. @@ -1306,6 +1390,7 @@ ovsdb_idl_txn_write(const struct ovsdb_idl_row *row_, size_t column_idx = column - class->columns; assert(row->new != NULL); + assert(column_idx < class->n_columns); if (hmap_node_is_null(&row->txn_node)) { hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); @@ -1357,6 +1442,7 @@ ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_) assert(row->new != NULL); if (!row->old) { + ovsdb_idl_row_unparse(row); ovsdb_idl_row_clear_new(row); assert(!row->prereqs); hmap_remove(&row->table->rows, &row->hmap_node); @@ -1483,6 +1569,7 @@ static bool ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert, const struct json_array *results) { + static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT; struct ovsdb_error *error; struct json *json_uuid; union ovsdb_atom uuid; @@ -1503,7 +1590,7 @@ ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert, return false; } - error = ovsdb_atom_from_json(&uuid, OVSDB_TYPE_UUID, json_uuid, NULL); + error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL); if (error) { char *s = ovsdb_error_to_string(error); VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON " @@ -1557,15 +1644,18 @@ ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl, soft_errors++; } else if (strcmp(error->u.string, "aborted")) { hard_errors++; + ovsdb_idl_txn_set_error_json(txn, op); } } else { hard_errors++; + ovsdb_idl_txn_set_error_json(txn, op); VLOG_WARN_RL(&syntax_rl, "\"error\" in reply is not JSON string"); } } } else { hard_errors++; + ovsdb_idl_txn_set_error_json(txn, op); VLOG_WARN_RL(&syntax_rl, "operation reply is not JSON null or object"); } @@ -1602,3 +1692,10 @@ ovsdb_idl_txn_get(const struct ovsdb_idl_row *row) assert(txn != NULL); return txn; } + +struct ovsdb_idl * +ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn) +{ + return txn->idl; +} +