]>
Commit | Line | Data |
---|---|---|
0f0b102d | 1 | /* Copyright (c) 2009, 2010, 2011 Nicira Networks. |
c3bb4bd7 BP |
2 | * |
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: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
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. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | ||
18 | #include "ovsdb-idl.h" | |
19 | ||
20 | #include <assert.h> | |
475281c0 | 21 | #include <errno.h> |
b54e22e9 | 22 | #include <inttypes.h> |
c3bb4bd7 BP |
23 | #include <limits.h> |
24 | #include <stdlib.h> | |
25 | ||
475281c0 | 26 | #include "bitmap.h" |
d171b584 | 27 | #include "dynamic-string.h" |
b302749b | 28 | #include "fatal-signal.h" |
c3bb4bd7 BP |
29 | #include "json.h" |
30 | #include "jsonrpc.h" | |
31 | #include "ovsdb-data.h" | |
32 | #include "ovsdb-error.h" | |
33 | #include "ovsdb-idl-provider.h" | |
586bb84a | 34 | #include "poll-loop.h" |
c3bb4bd7 BP |
35 | #include "shash.h" |
36 | #include "util.h" | |
c3bb4bd7 BP |
37 | #include "vlog.h" |
38 | ||
d98e6007 | 39 | VLOG_DEFINE_THIS_MODULE(ovsdb_idl); |
5136ce49 | 40 | |
c3bb4bd7 BP |
41 | /* An arc from one idl_row to another. When row A contains a UUID that |
42 | * references row B, this is represented by an arc from A (the source) to B | |
43 | * (the destination). | |
44 | * | |
45 | * Arcs from a row to itself are omitted, that is, src and dst are always | |
46 | * different. | |
47 | * | |
48 | * Arcs are never duplicated, that is, even if there are multiple references | |
49 | * from A to B, there is only a single arc from A to B. | |
50 | * | |
51 | * Arcs are directed: an arc from A to B is the converse of an an arc from B to | |
52 | * A. Both an arc and its converse may both be present, if each row refers | |
53 | * to the other circularly. | |
54 | * | |
55 | * The source and destination row may be in the same table or in different | |
56 | * tables. | |
57 | */ | |
58 | struct ovsdb_idl_arc { | |
59 | struct list src_node; /* In src->src_arcs list. */ | |
60 | struct list dst_node; /* In dst->dst_arcs list. */ | |
61 | struct ovsdb_idl_row *src; /* Source row. */ | |
62 | struct ovsdb_idl_row *dst; /* Destination row. */ | |
63 | }; | |
64 | ||
65 | struct ovsdb_idl { | |
115f1e4d | 66 | const struct ovsdb_idl_class *class; |
c3bb4bd7 | 67 | struct jsonrpc_session *session; |
115f1e4d | 68 | struct shash table_by_name; |
ef73f86c | 69 | struct ovsdb_idl_table *tables; /* Contains "struct ovsdb_idl_table *"s.*/ |
c3bb4bd7 BP |
70 | struct json *monitor_request_id; |
71 | unsigned int last_monitor_request_seqno; | |
72 | unsigned int change_seqno; | |
475281c0 BP |
73 | |
74 | /* Transaction support. */ | |
75 | struct ovsdb_idl_txn *txn; | |
76 | struct hmap outstanding_txns; | |
77 | }; | |
78 | ||
79 | struct ovsdb_idl_txn { | |
80 | struct hmap_node hmap_node; | |
81 | struct json *request_id; | |
82 | struct ovsdb_idl *idl; | |
83 | struct hmap txn_rows; | |
84 | enum ovsdb_idl_txn_status status; | |
91e310a5 | 85 | char *error; |
577aebdf | 86 | bool dry_run; |
d171b584 | 87 | struct ds comment; |
b54e22e9 BP |
88 | |
89 | /* Increments. */ | |
90 | char *inc_table; | |
91 | char *inc_column; | |
92 | struct json *inc_where; | |
93 | unsigned int inc_index; | |
94 | int64_t inc_new_value; | |
69490970 BP |
95 | |
96 | /* Inserted rows. */ | |
97 | struct hmap inserted_rows; | |
98 | }; | |
99 | ||
100 | struct ovsdb_idl_txn_insert { | |
101 | struct hmap_node hmap_node; /* In struct ovsdb_idl_txn's inserted_rows. */ | |
102 | struct uuid dummy; /* Dummy UUID used locally. */ | |
103 | int op_index; /* Index into transaction's operation array. */ | |
104 | struct uuid real; /* Real UUID used by database server. */ | |
c3bb4bd7 BP |
105 | }; |
106 | ||
107 | static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
108 | static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
109 | ||
110 | static void ovsdb_idl_clear(struct ovsdb_idl *); | |
111 | static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *); | |
112 | static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *); | |
113 | static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *, | |
114 | const struct json *); | |
c547535a | 115 | static bool ovsdb_idl_process_update(struct ovsdb_idl_table *, |
c3bb4bd7 BP |
116 | const struct uuid *, |
117 | const struct json *old, | |
118 | const struct json *new); | |
119 | static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *); | |
120 | static void ovsdb_idl_delete_row(struct ovsdb_idl_row *); | |
c547535a | 121 | static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *); |
c3bb4bd7 BP |
122 | |
123 | static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *); | |
475281c0 BP |
124 | static struct ovsdb_idl_row *ovsdb_idl_row_create__( |
125 | const struct ovsdb_idl_table_class *); | |
c3bb4bd7 BP |
126 | static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *, |
127 | const struct uuid *); | |
128 | static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *); | |
129 | ||
979821c0 BP |
130 | static void ovsdb_idl_row_parse(struct ovsdb_idl_row *); |
131 | static void ovsdb_idl_row_unparse(struct ovsdb_idl_row *); | |
475281c0 BP |
132 | static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *); |
133 | static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *); | |
134 | ||
135 | static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *); | |
136 | static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl *, | |
137 | const struct jsonrpc_msg *msg); | |
c3bb4bd7 | 138 | |
2ce42c88 BP |
139 | /* Creates and returns a connection to database 'remote', which should be in a |
140 | * form acceptable to jsonrpc_session_open(). The connection will maintain an | |
141 | * in-memory replica of the remote database whose schema is described by | |
142 | * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically | |
ef73f86c BP |
143 | * by ovsdb-idlc.) |
144 | * | |
145 | * If 'monitor_everything_by_default' is true, then everything in the remote | |
146 | * database will be replicated by default. ovsdb_idl_omit() and | |
147 | * ovsdb_idl_omit_alert() may be used to selectively drop some columns from | |
148 | * monitoring. | |
149 | * | |
150 | * If 'monitor_everything_by_default' is false, then no columns or tables will | |
151 | * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table() | |
152 | * must be used to choose some columns or tables to replicate. | |
153 | */ | |
c3bb4bd7 | 154 | struct ovsdb_idl * |
ef73f86c BP |
155 | ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class, |
156 | bool monitor_everything_by_default) | |
c3bb4bd7 BP |
157 | { |
158 | struct ovsdb_idl *idl; | |
ef73f86c | 159 | uint8_t default_mode; |
c3bb4bd7 BP |
160 | size_t i; |
161 | ||
ef73f86c BP |
162 | default_mode = (monitor_everything_by_default |
163 | ? OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT | |
164 | : 0); | |
165 | ||
c3bb4bd7 | 166 | idl = xzalloc(sizeof *idl); |
115f1e4d | 167 | idl->class = class; |
c3bb4bd7 | 168 | idl->session = jsonrpc_session_open(remote); |
115f1e4d BP |
169 | shash_init(&idl->table_by_name); |
170 | idl->tables = xmalloc(class->n_tables * sizeof *idl->tables); | |
c3bb4bd7 BP |
171 | for (i = 0; i < class->n_tables; i++) { |
172 | const struct ovsdb_idl_table_class *tc = &class->tables[i]; | |
115f1e4d | 173 | struct ovsdb_idl_table *table = &idl->tables[i]; |
c3bb4bd7 BP |
174 | size_t j; |
175 | ||
efdd9088 | 176 | shash_add_assert(&idl->table_by_name, tc->name, table); |
c3bb4bd7 | 177 | table->class = tc; |
c547535a | 178 | table->modes = xmalloc(tc->n_columns); |
ef73f86c BP |
179 | memset(table->modes, default_mode, tc->n_columns); |
180 | table->need_table = false; | |
c3bb4bd7 BP |
181 | shash_init(&table->columns); |
182 | for (j = 0; j < tc->n_columns; j++) { | |
183 | const struct ovsdb_idl_column *column = &tc->columns[j]; | |
184 | ||
efdd9088 | 185 | shash_add_assert(&table->columns, column->name, column); |
c3bb4bd7 BP |
186 | } |
187 | hmap_init(&table->rows); | |
188 | table->idl = idl; | |
189 | } | |
190 | idl->last_monitor_request_seqno = UINT_MAX; | |
475281c0 | 191 | hmap_init(&idl->outstanding_txns); |
c3bb4bd7 BP |
192 | |
193 | return idl; | |
194 | } | |
195 | ||
2ce42c88 | 196 | /* Destroys 'idl' and all of the data structures that it manages. */ |
c3bb4bd7 BP |
197 | void |
198 | ovsdb_idl_destroy(struct ovsdb_idl *idl) | |
199 | { | |
200 | if (idl) { | |
115f1e4d | 201 | size_t i; |
c3bb4bd7 | 202 | |
475281c0 | 203 | assert(!idl->txn); |
c3bb4bd7 BP |
204 | ovsdb_idl_clear(idl); |
205 | jsonrpc_session_close(idl->session); | |
206 | ||
115f1e4d BP |
207 | for (i = 0; i < idl->class->n_tables; i++) { |
208 | struct ovsdb_idl_table *table = &idl->tables[i]; | |
c3bb4bd7 BP |
209 | shash_destroy(&table->columns); |
210 | hmap_destroy(&table->rows); | |
c547535a | 211 | free(table->modes); |
c3bb4bd7 | 212 | } |
115f1e4d BP |
213 | shash_destroy(&idl->table_by_name); |
214 | free(idl->tables); | |
c3bb4bd7 BP |
215 | json_destroy(idl->monitor_request_id); |
216 | free(idl); | |
217 | } | |
218 | } | |
219 | ||
220 | static void | |
221 | ovsdb_idl_clear(struct ovsdb_idl *idl) | |
222 | { | |
c3bb4bd7 | 223 | bool changed = false; |
115f1e4d | 224 | size_t i; |
c3bb4bd7 | 225 | |
115f1e4d BP |
226 | for (i = 0; i < idl->class->n_tables; i++) { |
227 | struct ovsdb_idl_table *table = &idl->tables[i]; | |
c3bb4bd7 BP |
228 | struct ovsdb_idl_row *row, *next_row; |
229 | ||
230 | if (hmap_is_empty(&table->rows)) { | |
231 | continue; | |
232 | } | |
233 | ||
234 | changed = true; | |
4e8e4213 | 235 | HMAP_FOR_EACH_SAFE (row, next_row, hmap_node, &table->rows) { |
c3bb4bd7 BP |
236 | struct ovsdb_idl_arc *arc, *next_arc; |
237 | ||
238 | if (!ovsdb_idl_row_is_orphan(row)) { | |
979821c0 | 239 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 240 | } |
4e8e4213 | 241 | LIST_FOR_EACH_SAFE (arc, next_arc, src_node, &row->src_arcs) { |
c3bb4bd7 BP |
242 | free(arc); |
243 | } | |
244 | /* No need to do anything with dst_arcs: some node has those arcs | |
245 | * as forward arcs and will destroy them itself. */ | |
246 | ||
0196cc15 | 247 | ovsdb_idl_row_destroy(row); |
c3bb4bd7 BP |
248 | } |
249 | } | |
250 | ||
251 | if (changed) { | |
252 | idl->change_seqno++; | |
253 | } | |
254 | } | |
255 | ||
2ce42c88 BP |
256 | /* Processes a batch of messages from the database server on 'idl'. Returns |
257 | * true if the database as seen through 'idl' changed, false if it did not | |
258 | * change. The initial fetch of the entire contents of the remote database is | |
259 | * considered to be one kind of change. | |
260 | * | |
261 | * When this function returns false, the client may continue to use any data | |
262 | * structures it obtained from 'idl' in the past. But when it returns true, | |
263 | * the client must not access any of these data structures again, because they | |
264 | * could have freed or reused for other purposes. | |
265 | * | |
266 | * This function can return occasional false positives, that is, report that | |
267 | * the database changed even though it didn't. This happens if the connection | |
268 | * to the database drops and reconnects, which causes the database contents to | |
269 | * be reloaded even if they didn't change. (It could also happen if the | |
270 | * database server sends out a "change" that reflects what we already thought | |
271 | * was in the database, but the database server is not supposed to do that.) | |
272 | * | |
273 | * As an alternative to checking the return value, the client may check for | |
274 | * changes in the value returned by ovsdb_idl_get_seqno(). | |
275 | */ | |
4ea21243 | 276 | bool |
c3bb4bd7 BP |
277 | ovsdb_idl_run(struct ovsdb_idl *idl) |
278 | { | |
4ea21243 | 279 | unsigned int initial_change_seqno = idl->change_seqno; |
c3bb4bd7 BP |
280 | int i; |
281 | ||
8bc915de | 282 | assert(!idl->txn); |
c3bb4bd7 BP |
283 | jsonrpc_session_run(idl->session); |
284 | for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) { | |
828cd4c7 | 285 | struct jsonrpc_msg *msg; |
c3bb4bd7 BP |
286 | unsigned int seqno; |
287 | ||
288 | seqno = jsonrpc_session_get_seqno(idl->session); | |
289 | if (idl->last_monitor_request_seqno != seqno) { | |
290 | idl->last_monitor_request_seqno = seqno; | |
475281c0 | 291 | ovsdb_idl_txn_abort_all(idl); |
c3bb4bd7 BP |
292 | ovsdb_idl_send_monitor_request(idl); |
293 | break; | |
294 | } | |
295 | ||
296 | msg = jsonrpc_session_recv(idl->session); | |
297 | if (!msg) { | |
298 | break; | |
299 | } | |
300 | ||
4931f33a | 301 | if (msg->type == JSONRPC_NOTIFY |
c3bb4bd7 BP |
302 | && !strcmp(msg->method, "update") |
303 | && msg->params->type == JSON_ARRAY | |
304 | && msg->params->u.array.n == 2 | |
305 | && msg->params->u.array.elems[0]->type == JSON_NULL) { | |
306 | ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1]); | |
307 | } else if (msg->type == JSONRPC_REPLY | |
308 | && idl->monitor_request_id | |
309 | && json_equal(idl->monitor_request_id, msg->id)) { | |
c547535a | 310 | idl->change_seqno++; |
c3bb4bd7 BP |
311 | json_destroy(idl->monitor_request_id); |
312 | idl->monitor_request_id = NULL; | |
313 | ovsdb_idl_clear(idl); | |
314 | ovsdb_idl_parse_update(idl, msg->result); | |
0f0b102d | 315 | } else if (msg->type == JSONRPC_REPLY && msg->id->type == JSON_STRING |
c3bb4bd7 BP |
316 | && !strcmp(msg->id->u.string, "echo")) { |
317 | /* It's a reply to our echo request. Ignore it. */ | |
475281c0 BP |
318 | } else if ((msg->type == JSONRPC_ERROR |
319 | || msg->type == JSONRPC_REPLY) | |
320 | && ovsdb_idl_txn_process_reply(idl, msg)) { | |
321 | /* ovsdb_idl_txn_process_reply() did everything needful. */ | |
c3bb4bd7 | 322 | } else { |
c5a80c70 BP |
323 | /* This can happen if ovsdb_idl_txn_destroy() is called to destroy |
324 | * a transaction before we receive the reply, so keep the log level | |
325 | * low. */ | |
326 | VLOG_DBG("%s: received unexpected %s message", | |
327 | jsonrpc_session_get_name(idl->session), | |
328 | jsonrpc_msg_type_to_string(msg->type)); | |
c3bb4bd7 | 329 | } |
c3bb4bd7 BP |
330 | jsonrpc_msg_destroy(msg); |
331 | } | |
4ea21243 BP |
332 | |
333 | return initial_change_seqno != idl->change_seqno; | |
c3bb4bd7 BP |
334 | } |
335 | ||
2ce42c88 BP |
336 | /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to |
337 | * do or when activity occurs on a transaction on 'idl'. */ | |
c3bb4bd7 BP |
338 | void |
339 | ovsdb_idl_wait(struct ovsdb_idl *idl) | |
340 | { | |
341 | jsonrpc_session_wait(idl->session); | |
342 | jsonrpc_session_recv_wait(idl->session); | |
343 | } | |
344 | ||
2ce42c88 BP |
345 | /* Returns a number that represents the state of 'idl'. When 'idl' is updated |
346 | * (by ovsdb_idl_run()), the return value changes. */ | |
c3bb4bd7 BP |
347 | unsigned int |
348 | ovsdb_idl_get_seqno(const struct ovsdb_idl *idl) | |
349 | { | |
350 | return idl->change_seqno; | |
351 | } | |
352 | ||
2ce42c88 BP |
353 | /* Returns true if 'idl' successfully connected to the remote database and |
354 | * retrieved its contents (even if the connection subsequently dropped and is | |
355 | * in the process of reconnecting). If so, then 'idl' contains an atomic | |
356 | * snapshot of the database's contents (but it might be arbitrarily old if the | |
357 | * connection dropped). | |
358 | * | |
359 | * Returns false if 'idl' has never connected or retrieved the database's | |
360 | * contents. If so, 'idl' is empty. */ | |
f3d64521 JP |
361 | bool |
362 | ovsdb_idl_has_ever_connected(const struct ovsdb_idl *idl) | |
363 | { | |
364 | return ovsdb_idl_get_seqno(idl) != 0; | |
365 | } | |
366 | ||
2ce42c88 BP |
367 | /* Forces 'idl' to drop its connection to the database and reconnect. In the |
368 | * meantime, the contents of 'idl' will not change. */ | |
c3bb4bd7 BP |
369 | void |
370 | ovsdb_idl_force_reconnect(struct ovsdb_idl *idl) | |
371 | { | |
372 | jsonrpc_session_force_reconnect(idl->session); | |
373 | } | |
ef73f86c BP |
374 | \f |
375 | static unsigned char * | |
376 | ovsdb_idl_get_mode(struct ovsdb_idl *idl, | |
377 | const struct ovsdb_idl_column *column) | |
c547535a BP |
378 | { |
379 | size_t i; | |
380 | ||
ef73f86c BP |
381 | assert(!idl->change_seqno); |
382 | ||
c547535a BP |
383 | for (i = 0; i < idl->class->n_tables; i++) { |
384 | const struct ovsdb_idl_table *table = &idl->tables[i]; | |
385 | const struct ovsdb_idl_table_class *tc = table->class; | |
386 | ||
387 | if (column >= tc->columns && column < &tc->columns[tc->n_columns]) { | |
ef73f86c | 388 | return &table->modes[column - tc->columns]; |
c547535a BP |
389 | } |
390 | } | |
391 | ||
392 | NOT_REACHED(); | |
393 | } | |
394 | ||
ef73f86c BP |
395 | static void |
396 | add_ref_table(struct ovsdb_idl *idl, const struct ovsdb_base_type *base) | |
397 | { | |
398 | if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { | |
399 | struct ovsdb_idl_table *table; | |
400 | ||
401 | table = shash_find_data(&idl->table_by_name, | |
402 | base->u.uuid.refTableName); | |
403 | if (table) { | |
404 | table->need_table = true; | |
405 | } else { | |
406 | VLOG_WARN("%s IDL class missing referenced table %s", | |
407 | idl->class->database, base->u.uuid.refTableName); | |
408 | } | |
409 | } | |
410 | } | |
411 | ||
412 | /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also | |
413 | * ensures that any tables referenced by 'column' will be replicated, even if | |
414 | * no columns in that table are selected for replication (see | |
415 | * ovsdb_idl_add_table() for more information). | |
c547535a | 416 | * |
ef73f86c BP |
417 | * This function is only useful if 'monitor_everything_by_default' was false in |
418 | * the call to ovsdb_idl_create(). This function should be called between | |
419 | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). | |
420 | */ | |
421 | void | |
422 | ovsdb_idl_add_column(struct ovsdb_idl *idl, | |
423 | const struct ovsdb_idl_column *column) | |
424 | { | |
425 | *ovsdb_idl_get_mode(idl, column) = OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT; | |
426 | add_ref_table(idl, &column->type.key); | |
427 | add_ref_table(idl, &column->type.value); | |
428 | } | |
429 | ||
430 | /* Ensures that the table with class 'tc' will be replicated on 'idl' even if | |
431 | * no columns are selected for replication. This can be useful because it | |
432 | * allows 'idl' to keep track of what rows in the table actually exist, which | |
433 | * in turn allows columns that reference the table to have accurate contents. | |
434 | * (The IDL presents the database with references to rows that do not exist | |
435 | * removed.) | |
c547535a | 436 | * |
ef73f86c BP |
437 | * This function is only useful if 'monitor_everything_by_default' was false in |
438 | * the call to ovsdb_idl_create(). This function should be called between | |
439 | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). | |
440 | */ | |
441 | void | |
442 | ovsdb_idl_add_table(struct ovsdb_idl *idl, | |
443 | const struct ovsdb_idl_table_class *tc) | |
444 | { | |
445 | size_t i; | |
446 | ||
447 | for (i = 0; i < idl->class->n_tables; i++) { | |
448 | struct ovsdb_idl_table *table = &idl->tables[i]; | |
449 | ||
450 | if (table->class == tc) { | |
451 | table->need_table = true; | |
452 | return; | |
453 | } | |
454 | } | |
455 | ||
456 | NOT_REACHED(); | |
457 | } | |
458 | ||
459 | /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'. | |
c547535a | 460 | * |
ef73f86c BP |
461 | * This function should be called between ovsdb_idl_create() and the first call |
462 | * to ovsdb_idl_run(). | |
463 | */ | |
c547535a | 464 | void |
ef73f86c BP |
465 | ovsdb_idl_omit_alert(struct ovsdb_idl *idl, |
466 | const struct ovsdb_idl_column *column) | |
c547535a | 467 | { |
ef73f86c | 468 | *ovsdb_idl_get_mode(idl, column) &= ~OVSDB_IDL_ALERT; |
c547535a BP |
469 | } |
470 | ||
ef73f86c BP |
471 | /* Sets the mode for 'column' in 'idl' to 0. See the big comment above |
472 | * OVSDB_IDL_MONITOR for details. | |
c547535a | 473 | * |
ef73f86c BP |
474 | * This function should be called between ovsdb_idl_create() and the first call |
475 | * to ovsdb_idl_run(). | |
476 | */ | |
c547535a BP |
477 | void |
478 | ovsdb_idl_omit(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) | |
479 | { | |
ef73f86c | 480 | *ovsdb_idl_get_mode(idl, column) = 0; |
c547535a | 481 | } |
c3bb4bd7 BP |
482 | \f |
483 | static void | |
484 | ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl) | |
485 | { | |
486 | struct json *monitor_requests; | |
c3bb4bd7 | 487 | struct jsonrpc_msg *msg; |
115f1e4d | 488 | size_t i; |
c3bb4bd7 BP |
489 | |
490 | monitor_requests = json_object_create(); | |
115f1e4d BP |
491 | for (i = 0; i < idl->class->n_tables; i++) { |
492 | const struct ovsdb_idl_table *table = &idl->tables[i]; | |
c3bb4bd7 BP |
493 | const struct ovsdb_idl_table_class *tc = table->class; |
494 | struct json *monitor_request, *columns; | |
2a022368 | 495 | size_t j; |
c3bb4bd7 | 496 | |
ef73f86c | 497 | columns = table->need_table ? json_array_create_empty() : NULL; |
2a022368 BP |
498 | for (j = 0; j < tc->n_columns; j++) { |
499 | const struct ovsdb_idl_column *column = &tc->columns[j]; | |
ef73f86c BP |
500 | if (table->modes[j] & OVSDB_IDL_MONITOR) { |
501 | if (!columns) { | |
502 | columns = json_array_create_empty(); | |
503 | } | |
c547535a BP |
504 | json_array_add(columns, json_string_create(column->name)); |
505 | } | |
c3bb4bd7 | 506 | } |
ef73f86c BP |
507 | |
508 | if (columns) { | |
509 | monitor_request = json_object_create(); | |
510 | json_object_put(monitor_request, "columns", columns); | |
511 | json_object_put(monitor_requests, tc->name, monitor_request); | |
512 | } | |
c3bb4bd7 BP |
513 | } |
514 | ||
515 | json_destroy(idl->monitor_request_id); | |
516 | msg = jsonrpc_create_request( | |
9cb53f26 BP |
517 | "monitor", |
518 | json_array_create_3(json_string_create(idl->class->database), | |
519 | json_null_create(), monitor_requests), | |
c3bb4bd7 BP |
520 | &idl->monitor_request_id); |
521 | jsonrpc_session_send(idl->session, msg); | |
522 | } | |
523 | ||
524 | static void | |
525 | ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates) | |
526 | { | |
c547535a | 527 | struct ovsdb_error *error = ovsdb_idl_parse_update__(idl, table_updates); |
c3bb4bd7 BP |
528 | if (error) { |
529 | if (!VLOG_DROP_WARN(&syntax_rl)) { | |
530 | char *s = ovsdb_error_to_string(error); | |
531 | VLOG_WARN_RL(&syntax_rl, "%s", s); | |
532 | free(s); | |
533 | } | |
534 | ovsdb_error_destroy(error); | |
535 | } | |
536 | } | |
537 | ||
538 | static struct ovsdb_error * | |
539 | ovsdb_idl_parse_update__(struct ovsdb_idl *idl, | |
540 | const struct json *table_updates) | |
541 | { | |
542 | const struct shash_node *tables_node; | |
543 | ||
544 | if (table_updates->type != JSON_OBJECT) { | |
545 | return ovsdb_syntax_error(table_updates, NULL, | |
546 | "<table-updates> is not an object"); | |
547 | } | |
548 | SHASH_FOR_EACH (tables_node, json_object(table_updates)) { | |
549 | const struct json *table_update = tables_node->data; | |
550 | const struct shash_node *table_node; | |
551 | struct ovsdb_idl_table *table; | |
552 | ||
115f1e4d | 553 | table = shash_find_data(&idl->table_by_name, tables_node->name); |
c3bb4bd7 BP |
554 | if (!table) { |
555 | return ovsdb_syntax_error( | |
556 | table_updates, NULL, | |
557 | "<table-updates> includes unknown table \"%s\"", | |
558 | tables_node->name); | |
559 | } | |
560 | ||
561 | if (table_update->type != JSON_OBJECT) { | |
562 | return ovsdb_syntax_error(table_update, NULL, | |
563 | "<table-update> for table \"%s\" is " | |
564 | "not an object", table->class->name); | |
565 | } | |
566 | SHASH_FOR_EACH (table_node, json_object(table_update)) { | |
567 | const struct json *row_update = table_node->data; | |
568 | const struct json *old_json, *new_json; | |
569 | struct uuid uuid; | |
570 | ||
571 | if (!uuid_from_string(&uuid, table_node->name)) { | |
572 | return ovsdb_syntax_error(table_update, NULL, | |
573 | "<table-update> for table \"%s\" " | |
574 | "contains bad UUID " | |
575 | "\"%s\" as member name", | |
576 | table->class->name, | |
577 | table_node->name); | |
578 | } | |
579 | if (row_update->type != JSON_OBJECT) { | |
580 | return ovsdb_syntax_error(row_update, NULL, | |
581 | "<table-update> for table \"%s\" " | |
582 | "contains <row-update> for %s that " | |
583 | "is not an object", | |
584 | table->class->name, | |
585 | table_node->name); | |
586 | } | |
587 | ||
588 | old_json = shash_find_data(json_object(row_update), "old"); | |
589 | new_json = shash_find_data(json_object(row_update), "new"); | |
590 | if (old_json && old_json->type != JSON_OBJECT) { | |
591 | return ovsdb_syntax_error(old_json, NULL, | |
592 | "\"old\" <row> is not object"); | |
593 | } else if (new_json && new_json->type != JSON_OBJECT) { | |
594 | return ovsdb_syntax_error(new_json, NULL, | |
595 | "\"new\" <row> is not object"); | |
596 | } else if ((old_json != NULL) + (new_json != NULL) | |
597 | != shash_count(json_object(row_update))) { | |
598 | return ovsdb_syntax_error(row_update, NULL, | |
599 | "<row-update> contains unexpected " | |
600 | "member"); | |
601 | } else if (!old_json && !new_json) { | |
602 | return ovsdb_syntax_error(row_update, NULL, | |
603 | "<row-update> missing \"old\" " | |
604 | "and \"new\" members"); | |
605 | } | |
606 | ||
c547535a BP |
607 | if (ovsdb_idl_process_update(table, &uuid, old_json, new_json)) { |
608 | idl->change_seqno++; | |
609 | } | |
c3bb4bd7 BP |
610 | } |
611 | } | |
612 | ||
613 | return NULL; | |
614 | } | |
615 | ||
616 | static struct ovsdb_idl_row * | |
617 | ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid) | |
618 | { | |
619 | struct ovsdb_idl_row *row; | |
620 | ||
4e8e4213 | 621 | HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) { |
c3bb4bd7 BP |
622 | if (uuid_equals(&row->uuid, uuid)) { |
623 | return row; | |
624 | } | |
625 | } | |
626 | return NULL; | |
627 | } | |
628 | ||
c547535a BP |
629 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
630 | * otherwise. */ | |
631 | static bool | |
c3bb4bd7 BP |
632 | ovsdb_idl_process_update(struct ovsdb_idl_table *table, |
633 | const struct uuid *uuid, const struct json *old, | |
634 | const struct json *new) | |
635 | { | |
636 | struct ovsdb_idl_row *row; | |
637 | ||
638 | row = ovsdb_idl_get_row(table, uuid); | |
639 | if (!new) { | |
640 | /* Delete row. */ | |
641 | if (row && !ovsdb_idl_row_is_orphan(row)) { | |
642 | /* XXX perhaps we should check the 'old' values? */ | |
643 | ovsdb_idl_delete_row(row); | |
644 | } else { | |
645 | VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" " | |
646 | "from table %s", | |
647 | UUID_ARGS(uuid), table->class->name); | |
c547535a | 648 | return false; |
c3bb4bd7 BP |
649 | } |
650 | } else if (!old) { | |
651 | /* Insert row. */ | |
652 | if (!row) { | |
653 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); | |
654 | } else if (ovsdb_idl_row_is_orphan(row)) { | |
655 | ovsdb_idl_insert_row(row, new); | |
656 | } else { | |
657 | VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to " | |
658 | "table %s", UUID_ARGS(uuid), table->class->name); | |
c547535a | 659 | return ovsdb_idl_modify_row(row, new); |
c3bb4bd7 BP |
660 | } |
661 | } else { | |
662 | /* Modify row. */ | |
663 | if (row) { | |
664 | /* XXX perhaps we should check the 'old' values? */ | |
665 | if (!ovsdb_idl_row_is_orphan(row)) { | |
c547535a | 666 | return ovsdb_idl_modify_row(row, new); |
c3bb4bd7 BP |
667 | } else { |
668 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing but " | |
669 | "referenced row "UUID_FMT" in table %s", | |
670 | UUID_ARGS(uuid), table->class->name); | |
671 | ovsdb_idl_insert_row(row, new); | |
672 | } | |
673 | } else { | |
674 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" " | |
675 | "in table %s", UUID_ARGS(uuid), table->class->name); | |
676 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); | |
677 | } | |
678 | } | |
c547535a BP |
679 | |
680 | return true; | |
c3bb4bd7 BP |
681 | } |
682 | ||
c547535a BP |
683 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
684 | * otherwise. */ | |
685 | static bool | |
c3bb4bd7 BP |
686 | ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json) |
687 | { | |
688 | struct ovsdb_idl_table *table = row->table; | |
689 | struct shash_node *node; | |
c547535a | 690 | bool changed = false; |
c3bb4bd7 BP |
691 | |
692 | SHASH_FOR_EACH (node, json_object(row_json)) { | |
693 | const char *column_name = node->name; | |
694 | const struct ovsdb_idl_column *column; | |
695 | struct ovsdb_datum datum; | |
696 | struct ovsdb_error *error; | |
697 | ||
698 | column = shash_find_data(&table->columns, column_name); | |
699 | if (!column) { | |
700 | VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT, | |
701 | column_name, UUID_ARGS(&row->uuid)); | |
702 | continue; | |
703 | } | |
704 | ||
705 | error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL); | |
706 | if (!error) { | |
c547535a | 707 | unsigned int column_idx = column - table->class->columns; |
e85bbd75 BP |
708 | struct ovsdb_datum *old = &row->old[column_idx]; |
709 | ||
710 | if (!ovsdb_datum_equals(old, &datum, &column->type)) { | |
711 | ovsdb_datum_swap(old, &datum); | |
ef73f86c | 712 | if (table->modes[column_idx] & OVSDB_IDL_ALERT) { |
e85bbd75 BP |
713 | changed = true; |
714 | } | |
715 | } else { | |
716 | /* Didn't really change but the OVSDB monitor protocol always | |
717 | * includes every value in a row. */ | |
c547535a | 718 | } |
e85bbd75 BP |
719 | |
720 | ovsdb_datum_destroy(&datum, &column->type); | |
c3bb4bd7 BP |
721 | } else { |
722 | char *s = ovsdb_error_to_string(error); | |
723 | VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT | |
724 | " in table %s: %s", column_name, | |
725 | UUID_ARGS(&row->uuid), table->class->name, s); | |
726 | free(s); | |
727 | ovsdb_error_destroy(error); | |
728 | } | |
729 | } | |
c547535a | 730 | return changed; |
c3bb4bd7 BP |
731 | } |
732 | ||
90887925 BP |
733 | /* When a row A refers to row B through a column with a "refTable" constraint, |
734 | * but row B does not exist, row B is called an "orphan row". Orphan rows | |
735 | * should not persist, because the database enforces referential integrity, but | |
736 | * they can appear transiently as changes from the database are received (the | |
737 | * database doesn't try to topologically sort them and circular references mean | |
738 | * it isn't always possible anyhow). | |
739 | * | |
740 | * This function returns true if 'row' is an orphan row, otherwise false. | |
741 | */ | |
c3bb4bd7 BP |
742 | static bool |
743 | ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row) | |
744 | { | |
90887925 BP |
745 | return !row->old && !row->new; |
746 | } | |
747 | ||
f2ba3c0a BP |
748 | /* Returns true if 'row' is conceptually part of the database as modified by |
749 | * the current transaction (if any), false otherwise. | |
750 | * | |
751 | * This function will return true if 'row' is not an orphan (see the comment on | |
752 | * ovsdb_idl_row_is_orphan()) and: | |
753 | * | |
754 | * - 'row' exists in the database and has not been deleted within the | |
755 | * current transaction (if any). | |
756 | * | |
757 | * - 'row' was inserted within the current transaction and has not been | |
758 | * deleted. (In the latter case you should not have passed 'row' in at | |
759 | * all, because ovsdb_idl_txn_delete() freed it.) | |
760 | * | |
761 | * This function will return false if 'row' is an orphan or if 'row' was | |
762 | * deleted within the current transaction. | |
763 | */ | |
764 | static bool | |
765 | ovsdb_idl_row_exists(const struct ovsdb_idl_row *row) | |
766 | { | |
767 | return row->new != NULL; | |
c3bb4bd7 BP |
768 | } |
769 | ||
979821c0 BP |
770 | static void |
771 | ovsdb_idl_row_parse(struct ovsdb_idl_row *row) | |
772 | { | |
773 | const struct ovsdb_idl_table_class *class = row->table->class; | |
774 | size_t i; | |
775 | ||
776 | for (i = 0; i < class->n_columns; i++) { | |
777 | const struct ovsdb_idl_column *c = &class->columns[i]; | |
778 | (c->parse)(row, &row->old[i]); | |
779 | } | |
780 | } | |
781 | ||
782 | static void | |
783 | ovsdb_idl_row_unparse(struct ovsdb_idl_row *row) | |
784 | { | |
785 | const struct ovsdb_idl_table_class *class = row->table->class; | |
786 | size_t i; | |
787 | ||
788 | for (i = 0; i < class->n_columns; i++) { | |
789 | const struct ovsdb_idl_column *c = &class->columns[i]; | |
790 | (c->unparse)(row); | |
791 | } | |
792 | } | |
793 | ||
c3bb4bd7 | 794 | static void |
475281c0 | 795 | ovsdb_idl_row_clear_old(struct ovsdb_idl_row *row) |
c3bb4bd7 | 796 | { |
475281c0 | 797 | assert(row->old == row->new); |
c3bb4bd7 BP |
798 | if (!ovsdb_idl_row_is_orphan(row)) { |
799 | const struct ovsdb_idl_table_class *class = row->table->class; | |
800 | size_t i; | |
801 | ||
802 | for (i = 0; i < class->n_columns; i++) { | |
475281c0 BP |
803 | ovsdb_datum_destroy(&row->old[i], &class->columns[i].type); |
804 | } | |
805 | free(row->old); | |
806 | row->old = row->new = NULL; | |
807 | } | |
808 | } | |
809 | ||
810 | static void | |
811 | ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row) | |
812 | { | |
813 | if (row->old != row->new) { | |
814 | if (row->new) { | |
815 | const struct ovsdb_idl_table_class *class = row->table->class; | |
816 | size_t i; | |
817 | ||
35c2ce98 JG |
818 | if (row->written) { |
819 | BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { | |
820 | ovsdb_datum_destroy(&row->new[i], &class->columns[i].type); | |
821 | } | |
475281c0 BP |
822 | } |
823 | free(row->new); | |
824 | free(row->written); | |
825 | row->written = NULL; | |
c3bb4bd7 | 826 | } |
475281c0 | 827 | row->new = row->old; |
c3bb4bd7 BP |
828 | } |
829 | } | |
830 | ||
831 | static void | |
832 | ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts) | |
833 | { | |
834 | struct ovsdb_idl_arc *arc, *next; | |
835 | ||
836 | /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows | |
837 | * that this causes to be unreferenced. */ | |
4e8e4213 | 838 | LIST_FOR_EACH_SAFE (arc, next, src_node, &row->src_arcs) { |
c3bb4bd7 BP |
839 | list_remove(&arc->dst_node); |
840 | if (destroy_dsts | |
841 | && ovsdb_idl_row_is_orphan(arc->dst) | |
842 | && list_is_empty(&arc->dst->dst_arcs)) { | |
843 | ovsdb_idl_row_destroy(arc->dst); | |
844 | } | |
845 | free(arc); | |
846 | } | |
847 | list_init(&row->src_arcs); | |
848 | } | |
849 | ||
850 | /* Force nodes that reference 'row' to reparse. */ | |
851 | static void | |
0196cc15 | 852 | ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row) |
c3bb4bd7 BP |
853 | { |
854 | struct ovsdb_idl_arc *arc, *next; | |
855 | ||
856 | /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy | |
857 | * 'arc', so we need to use the "safe" variant of list traversal. However, | |
979821c0 BP |
858 | * calling an ovsdb_idl_column's 'parse' function will add an arc |
859 | * equivalent to 'arc' to row->arcs. That could be a problem for | |
860 | * traversal, but it adds it at the beginning of the list to prevent us | |
861 | * from stumbling upon it again. | |
c3bb4bd7 BP |
862 | * |
863 | * (If duplicate arcs were possible then we would need to make sure that | |
864 | * 'next' didn't also point into 'arc''s destination, but we forbid | |
865 | * duplicate arcs.) */ | |
4e8e4213 | 866 | LIST_FOR_EACH_SAFE (arc, next, dst_node, &row->dst_arcs) { |
c3bb4bd7 BP |
867 | struct ovsdb_idl_row *ref = arc->src; |
868 | ||
979821c0 | 869 | ovsdb_idl_row_unparse(ref); |
0196cc15 | 870 | ovsdb_idl_row_clear_arcs(ref, false); |
979821c0 | 871 | ovsdb_idl_row_parse(ref); |
c3bb4bd7 BP |
872 | } |
873 | } | |
874 | ||
475281c0 BP |
875 | static struct ovsdb_idl_row * |
876 | ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class) | |
877 | { | |
72c6edc5 | 878 | struct ovsdb_idl_row *row = xzalloc(class->allocation_size); |
475281c0 BP |
879 | list_init(&row->src_arcs); |
880 | list_init(&row->dst_arcs); | |
881 | hmap_node_nullify(&row->txn_node); | |
882 | return row; | |
883 | } | |
884 | ||
c3bb4bd7 BP |
885 | static struct ovsdb_idl_row * |
886 | ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid) | |
887 | { | |
475281c0 | 888 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(table->class); |
c3bb4bd7 BP |
889 | hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid)); |
890 | row->uuid = *uuid; | |
c3bb4bd7 | 891 | row->table = table; |
c3bb4bd7 BP |
892 | return row; |
893 | } | |
894 | ||
895 | static void | |
896 | ovsdb_idl_row_destroy(struct ovsdb_idl_row *row) | |
897 | { | |
898 | if (row) { | |
475281c0 | 899 | ovsdb_idl_row_clear_old(row); |
c3bb4bd7 BP |
900 | hmap_remove(&row->table->rows, &row->hmap_node); |
901 | free(row); | |
902 | } | |
903 | } | |
904 | ||
905 | static void | |
906 | ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json) | |
907 | { | |
908 | const struct ovsdb_idl_table_class *class = row->table->class; | |
909 | size_t i; | |
910 | ||
475281c0 BP |
911 | assert(!row->old && !row->new); |
912 | row->old = row->new = xmalloc(class->n_columns * sizeof *row->old); | |
c3bb4bd7 | 913 | for (i = 0; i < class->n_columns; i++) { |
475281c0 | 914 | ovsdb_datum_init_default(&row->old[i], &class->columns[i].type); |
c3bb4bd7 BP |
915 | } |
916 | ovsdb_idl_row_update(row, row_json); | |
979821c0 | 917 | ovsdb_idl_row_parse(row); |
c3bb4bd7 | 918 | |
0196cc15 | 919 | ovsdb_idl_row_reparse_backrefs(row); |
c3bb4bd7 BP |
920 | } |
921 | ||
922 | static void | |
923 | ovsdb_idl_delete_row(struct ovsdb_idl_row *row) | |
924 | { | |
979821c0 | 925 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 926 | ovsdb_idl_row_clear_arcs(row, true); |
475281c0 | 927 | ovsdb_idl_row_clear_old(row); |
c3bb4bd7 BP |
928 | if (list_is_empty(&row->dst_arcs)) { |
929 | ovsdb_idl_row_destroy(row); | |
930 | } else { | |
0196cc15 | 931 | ovsdb_idl_row_reparse_backrefs(row); |
c3bb4bd7 BP |
932 | } |
933 | } | |
934 | ||
c547535a BP |
935 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
936 | * otherwise. */ | |
937 | static bool | |
c3bb4bd7 BP |
938 | ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json) |
939 | { | |
c547535a BP |
940 | bool changed; |
941 | ||
979821c0 | 942 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 943 | ovsdb_idl_row_clear_arcs(row, true); |
c547535a | 944 | changed = ovsdb_idl_row_update(row, row_json); |
979821c0 | 945 | ovsdb_idl_row_parse(row); |
c547535a BP |
946 | |
947 | return changed; | |
c3bb4bd7 BP |
948 | } |
949 | ||
950 | static bool | |
951 | may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst) | |
952 | { | |
953 | const struct ovsdb_idl_arc *arc; | |
954 | ||
955 | /* No self-arcs. */ | |
956 | if (src == dst) { | |
957 | return false; | |
958 | } | |
959 | ||
960 | /* No duplicate arcs. | |
961 | * | |
962 | * We only need to test whether the first arc in dst->dst_arcs originates | |
963 | * at 'src', since we add all of the arcs from a given source in a clump | |
979821c0 | 964 | * (in a single call to ovsdb_idl_row_parse()) and new arcs are always |
c3bb4bd7 BP |
965 | * added at the front of the dst_arcs list. */ |
966 | if (list_is_empty(&dst->dst_arcs)) { | |
967 | return true; | |
968 | } | |
969 | arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node); | |
970 | return arc->src != src; | |
971 | } | |
972 | ||
115f1e4d | 973 | static struct ovsdb_idl_table * |
475281c0 BP |
974 | ovsdb_idl_table_from_class(const struct ovsdb_idl *idl, |
975 | const struct ovsdb_idl_table_class *table_class) | |
115f1e4d BP |
976 | { |
977 | return &idl->tables[table_class - idl->class->tables]; | |
978 | } | |
979 | ||
c3bb4bd7 BP |
980 | struct ovsdb_idl_row * |
981 | ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src, | |
982 | struct ovsdb_idl_table_class *dst_table_class, | |
983 | const struct uuid *dst_uuid) | |
984 | { | |
985 | struct ovsdb_idl *idl = src->table->idl; | |
e9011ac8 | 986 | struct ovsdb_idl_table *dst_table; |
c3bb4bd7 BP |
987 | struct ovsdb_idl_arc *arc; |
988 | struct ovsdb_idl_row *dst; | |
989 | ||
475281c0 | 990 | dst_table = ovsdb_idl_table_from_class(idl, dst_table_class); |
e9011ac8 | 991 | dst = ovsdb_idl_get_row(dst_table, dst_uuid); |
979821c0 BP |
992 | if (idl->txn) { |
993 | /* We're being called from ovsdb_idl_txn_write(). We must not update | |
994 | * any arcs, because the transaction will be backed out at commit or | |
995 | * abort time and we don't want our graph screwed up. | |
996 | * | |
997 | * Just return the destination row, if there is one and it has not been | |
998 | * deleted. */ | |
999 | if (dst && (hmap_node_is_null(&dst->txn_node) || dst->new)) { | |
1000 | return dst; | |
1001 | } | |
1002 | return NULL; | |
1003 | } else { | |
1004 | /* We're being called from some other context. Update the graph. */ | |
1005 | if (!dst) { | |
1006 | dst = ovsdb_idl_row_create(dst_table, dst_uuid); | |
1007 | } | |
c3bb4bd7 | 1008 | |
979821c0 BP |
1009 | /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */ |
1010 | if (may_add_arc(src, dst)) { | |
1011 | /* The arc *must* be added at the front of the dst_arcs list. See | |
1012 | * ovsdb_idl_row_reparse_backrefs() for details. */ | |
1013 | arc = xmalloc(sizeof *arc); | |
1014 | list_push_front(&src->src_arcs, &arc->src_node); | |
1015 | list_push_front(&dst->dst_arcs, &arc->dst_node); | |
1016 | arc->src = src; | |
1017 | arc->dst = dst; | |
1018 | } | |
1019 | ||
1020 | return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL; | |
c3bb4bd7 | 1021 | } |
979821c0 | 1022 | } |
c3bb4bd7 | 1023 | |
979821c0 BP |
1024 | const struct ovsdb_idl_row * |
1025 | ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl *idl, | |
1026 | const struct ovsdb_idl_table_class *tc, | |
1027 | const struct uuid *uuid) | |
1028 | { | |
1029 | return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl, tc), uuid); | |
c3bb4bd7 BP |
1030 | } |
1031 | ||
1032 | static struct ovsdb_idl_row * | |
1033 | next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node) | |
1034 | { | |
1035 | for (; node; node = hmap_next(&table->rows, node)) { | |
1036 | struct ovsdb_idl_row *row; | |
1037 | ||
1038 | row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node); | |
f2ba3c0a | 1039 | if (ovsdb_idl_row_exists(row)) { |
c3bb4bd7 BP |
1040 | return row; |
1041 | } | |
1042 | } | |
1043 | return NULL; | |
1044 | } | |
1045 | ||
979821c0 | 1046 | const struct ovsdb_idl_row * |
c3bb4bd7 BP |
1047 | ovsdb_idl_first_row(const struct ovsdb_idl *idl, |
1048 | const struct ovsdb_idl_table_class *table_class) | |
1049 | { | |
475281c0 BP |
1050 | struct ovsdb_idl_table *table |
1051 | = ovsdb_idl_table_from_class(idl, table_class); | |
c3bb4bd7 BP |
1052 | return next_real_row(table, hmap_first(&table->rows)); |
1053 | } | |
1054 | ||
979821c0 | 1055 | const struct ovsdb_idl_row * |
c3bb4bd7 BP |
1056 | ovsdb_idl_next_row(const struct ovsdb_idl_row *row) |
1057 | { | |
1058 | struct ovsdb_idl_table *table = row->table; | |
1059 | ||
1060 | return next_real_row(table, hmap_next(&table->rows, &row->hmap_node)); | |
1061 | } | |
8c3c2f30 BP |
1062 | |
1063 | /* Reads and returns the value of 'column' within 'row'. If an ongoing | |
1064 | * transaction has changed 'column''s value, the modified value is returned. | |
1065 | * | |
1066 | * The caller must not modify or free the returned value. | |
1067 | * | |
1068 | * Various kinds of changes can invalidate the returned value: writing to the | |
1069 | * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row' | |
1070 | * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction | |
1071 | * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the | |
1072 | * returned value is needed for a long time, it is best to make a copy of it | |
1073 | * with ovsdb_datum_clone(). */ | |
1074 | const struct ovsdb_datum * | |
1075 | ovsdb_idl_read(const struct ovsdb_idl_row *row, | |
1076 | const struct ovsdb_idl_column *column) | |
1077 | { | |
1078 | const struct ovsdb_idl_table_class *class = row->table->class; | |
1079 | size_t column_idx = column - class->columns; | |
1080 | ||
1081 | assert(row->new != NULL); | |
1082 | assert(column_idx < class->n_columns); | |
1083 | ||
1084 | if (row->written && bitmap_is_set(row->written, column_idx)) { | |
1085 | return &row->new[column_idx]; | |
1086 | } else if (row->old) { | |
1087 | return &row->old[column_idx]; | |
1088 | } else { | |
1089 | return ovsdb_datum_default(&column->type); | |
1090 | } | |
1091 | } | |
1092 | ||
1093 | /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key | |
1094 | * type 'key_type' and value type 'value_type'. (Scalar and set types will | |
1095 | * have a value type of OVSDB_TYPE_VOID.) | |
1096 | * | |
1097 | * This is useful in code that "knows" that a particular column has a given | |
1098 | * type, so that it will abort if someone changes the column's type without | |
1099 | * updating the code that uses it. */ | |
1100 | const struct ovsdb_datum * | |
1101 | ovsdb_idl_get(const struct ovsdb_idl_row *row, | |
1102 | const struct ovsdb_idl_column *column, | |
1103 | enum ovsdb_atomic_type key_type OVS_UNUSED, | |
1104 | enum ovsdb_atomic_type value_type OVS_UNUSED) | |
1105 | { | |
1106 | assert(column->type.key.type == key_type); | |
1107 | assert(column->type.value.type == value_type); | |
1108 | ||
1109 | return ovsdb_idl_read(row, column); | |
1110 | } | |
475281c0 BP |
1111 | \f |
1112 | /* Transactions. */ | |
1113 | ||
1114 | static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, | |
1115 | enum ovsdb_idl_txn_status); | |
1116 | ||
1117 | const char * | |
1118 | ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status) | |
1119 | { | |
1120 | switch (status) { | |
b54e22e9 BP |
1121 | case TXN_UNCHANGED: |
1122 | return "unchanged"; | |
475281c0 BP |
1123 | case TXN_INCOMPLETE: |
1124 | return "incomplete"; | |
1125 | case TXN_ABORTED: | |
1126 | return "aborted"; | |
1127 | case TXN_SUCCESS: | |
1128 | return "success"; | |
1129 | case TXN_TRY_AGAIN: | |
1130 | return "try again"; | |
1131 | case TXN_ERROR: | |
1132 | return "error"; | |
1133 | } | |
1134 | return "<unknown>"; | |
1135 | } | |
1136 | ||
1137 | struct ovsdb_idl_txn * | |
1138 | ovsdb_idl_txn_create(struct ovsdb_idl *idl) | |
1139 | { | |
1140 | struct ovsdb_idl_txn *txn; | |
1141 | ||
1142 | assert(!idl->txn); | |
1143 | idl->txn = txn = xmalloc(sizeof *txn); | |
0196cc15 | 1144 | txn->request_id = NULL; |
475281c0 | 1145 | txn->idl = idl; |
475281c0 | 1146 | hmap_init(&txn->txn_rows); |
0196cc15 | 1147 | txn->status = TXN_INCOMPLETE; |
91e310a5 | 1148 | txn->error = NULL; |
577aebdf | 1149 | txn->dry_run = false; |
d171b584 | 1150 | ds_init(&txn->comment); |
0196cc15 | 1151 | |
b54e22e9 BP |
1152 | txn->inc_table = NULL; |
1153 | txn->inc_column = NULL; | |
1154 | txn->inc_where = NULL; | |
0196cc15 | 1155 | |
69490970 | 1156 | hmap_init(&txn->inserted_rows); |
0196cc15 | 1157 | |
475281c0 BP |
1158 | return txn; |
1159 | } | |
1160 | ||
e1c0e2d1 BP |
1161 | /* Appends 's', which is treated as a printf()-type format string, to the |
1162 | * comments that will be passed to the OVSDB server when 'txn' is committed. | |
1163 | * (The comment will be committed to the OVSDB log, which "ovsdb-tool | |
1164 | * show-log" can print in a relatively human-readable form.) */ | |
d171b584 | 1165 | void |
e1c0e2d1 | 1166 | ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...) |
d171b584 | 1167 | { |
e1c0e2d1 BP |
1168 | va_list args; |
1169 | ||
d171b584 BP |
1170 | if (txn->comment.length) { |
1171 | ds_put_char(&txn->comment, '\n'); | |
1172 | } | |
e1c0e2d1 BP |
1173 | |
1174 | va_start(args, s); | |
1175 | ds_put_format_valist(&txn->comment, s, args); | |
1176 | va_end(args); | |
d171b584 BP |
1177 | } |
1178 | ||
577aebdf BP |
1179 | void |
1180 | ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn *txn) | |
1181 | { | |
1182 | txn->dry_run = true; | |
1183 | } | |
1184 | ||
b54e22e9 BP |
1185 | void |
1186 | ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn, const char *table, | |
1187 | const char *column, const struct json *where) | |
1188 | { | |
1189 | assert(!txn->inc_table); | |
1190 | txn->inc_table = xstrdup(table); | |
1191 | txn->inc_column = xstrdup(column); | |
1192 | txn->inc_where = where ? json_clone(where) : json_array_create_empty(); | |
1193 | } | |
1194 | ||
475281c0 BP |
1195 | void |
1196 | ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn) | |
1197 | { | |
69490970 BP |
1198 | struct ovsdb_idl_txn_insert *insert, *next; |
1199 | ||
0196cc15 | 1200 | json_destroy(txn->request_id); |
fbd8fd40 BP |
1201 | if (txn->status == TXN_INCOMPLETE) { |
1202 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); | |
1203 | } | |
475281c0 | 1204 | ovsdb_idl_txn_abort(txn); |
d171b584 | 1205 | ds_destroy(&txn->comment); |
91e310a5 | 1206 | free(txn->error); |
b54e22e9 BP |
1207 | free(txn->inc_table); |
1208 | free(txn->inc_column); | |
1209 | json_destroy(txn->inc_where); | |
4e8e4213 | 1210 | HMAP_FOR_EACH_SAFE (insert, next, hmap_node, &txn->inserted_rows) { |
69490970 BP |
1211 | free(insert); |
1212 | } | |
0196cc15 | 1213 | hmap_destroy(&txn->inserted_rows); |
475281c0 BP |
1214 | free(txn); |
1215 | } | |
1216 | ||
586bb84a BP |
1217 | void |
1218 | ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *txn) | |
1219 | { | |
1220 | if (txn->status != TXN_INCOMPLETE) { | |
1221 | poll_immediate_wake(); | |
1222 | } | |
1223 | } | |
1224 | ||
475281c0 BP |
1225 | static struct json * |
1226 | where_uuid_equals(const struct uuid *uuid) | |
1227 | { | |
1228 | return | |
1229 | json_array_create_1( | |
1230 | json_array_create_3( | |
1231 | json_string_create("_uuid"), | |
1232 | json_string_create("=="), | |
1233 | json_array_create_2( | |
1234 | json_string_create("uuid"), | |
1235 | json_string_create_nocopy( | |
1236 | xasprintf(UUID_FMT, UUID_ARGS(uuid)))))); | |
1237 | } | |
1238 | ||
1239 | static char * | |
1240 | uuid_name_from_uuid(const struct uuid *uuid) | |
1241 | { | |
1242 | char *name; | |
1243 | char *p; | |
1244 | ||
1245 | name = xasprintf("row"UUID_FMT, UUID_ARGS(uuid)); | |
1246 | for (p = name; *p != '\0'; p++) { | |
1247 | if (*p == '-') { | |
1248 | *p = '_'; | |
1249 | } | |
1250 | } | |
1251 | ||
1252 | return name; | |
1253 | } | |
1254 | ||
1255 | static const struct ovsdb_idl_row * | |
1256 | ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid) | |
1257 | { | |
1258 | const struct ovsdb_idl_row *row; | |
1259 | ||
4e8e4213 | 1260 | HMAP_FOR_EACH_WITH_HASH (row, txn_node, uuid_hash(uuid), &txn->txn_rows) { |
475281c0 BP |
1261 | if (uuid_equals(&row->uuid, uuid)) { |
1262 | return row; | |
1263 | } | |
1264 | } | |
1265 | return NULL; | |
1266 | } | |
1267 | ||
1268 | /* XXX there must be a cleaner way to do this */ | |
1269 | static struct json * | |
1270 | substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn) | |
1271 | { | |
1272 | if (json->type == JSON_ARRAY) { | |
1273 | struct uuid uuid; | |
1274 | size_t i; | |
1275 | ||
1276 | if (json->u.array.n == 2 | |
1277 | && json->u.array.elems[0]->type == JSON_STRING | |
1278 | && json->u.array.elems[1]->type == JSON_STRING | |
1279 | && !strcmp(json->u.array.elems[0]->u.string, "uuid") | |
1280 | && uuid_from_string(&uuid, json->u.array.elems[1]->u.string)) { | |
1281 | const struct ovsdb_idl_row *row; | |
1282 | ||
1283 | row = ovsdb_idl_txn_get_row(txn, &uuid); | |
1284 | if (row && !row->old && row->new) { | |
1285 | json_destroy(json); | |
1286 | ||
1287 | return json_array_create_2( | |
1288 | json_string_create("named-uuid"), | |
1289 | json_string_create_nocopy(uuid_name_from_uuid(&uuid))); | |
1290 | } | |
1291 | } | |
1292 | ||
1293 | for (i = 0; i < json->u.array.n; i++) { | |
1294 | json->u.array.elems[i] = substitute_uuids(json->u.array.elems[i], | |
1295 | txn); | |
1296 | } | |
1297 | } else if (json->type == JSON_OBJECT) { | |
1298 | struct shash_node *node; | |
1299 | ||
1300 | SHASH_FOR_EACH (node, json_object(json)) { | |
1301 | node->data = substitute_uuids(node->data, txn); | |
1302 | } | |
1303 | } | |
1304 | return json; | |
1305 | } | |
1306 | ||
1307 | static void | |
1308 | ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn) | |
1309 | { | |
1310 | struct ovsdb_idl_row *row, *next; | |
1311 | ||
979821c0 BP |
1312 | /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an |
1313 | * ovsdb_idl_column's 'parse' function, which will call | |
1314 | * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a | |
1315 | * transaction and fail to update the graph. */ | |
1316 | txn->idl->txn = NULL; | |
1317 | ||
4e8e4213 | 1318 | HMAP_FOR_EACH_SAFE (row, next, txn_node, &txn->txn_rows) { |
979821c0 BP |
1319 | if (row->old) { |
1320 | if (row->written) { | |
1321 | ovsdb_idl_row_unparse(row); | |
1322 | ovsdb_idl_row_clear_arcs(row, false); | |
1323 | ovsdb_idl_row_parse(row); | |
1324 | } | |
1325 | } else { | |
0196cc15 | 1326 | ovsdb_idl_row_unparse(row); |
8bc915de | 1327 | } |
475281c0 BP |
1328 | ovsdb_idl_row_clear_new(row); |
1329 | ||
1330 | free(row->prereqs); | |
1331 | row->prereqs = NULL; | |
1332 | ||
1333 | free(row->written); | |
1334 | row->written = NULL; | |
1335 | ||
1336 | hmap_remove(&txn->txn_rows, &row->txn_node); | |
1337 | hmap_node_nullify(&row->txn_node); | |
0196cc15 BP |
1338 | if (!row->old) { |
1339 | hmap_remove(&row->table->rows, &row->hmap_node); | |
1340 | free(row); | |
1341 | } | |
475281c0 BP |
1342 | } |
1343 | hmap_destroy(&txn->txn_rows); | |
1344 | hmap_init(&txn->txn_rows); | |
1345 | } | |
1346 | ||
1347 | enum ovsdb_idl_txn_status | |
1348 | ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) | |
1349 | { | |
1350 | struct ovsdb_idl_row *row; | |
1351 | struct json *operations; | |
1352 | bool any_updates; | |
475281c0 BP |
1353 | |
1354 | if (txn != txn->idl->txn) { | |
1355 | return txn->status; | |
1356 | } | |
1357 | ||
9cb53f26 BP |
1358 | operations = json_array_create_1( |
1359 | json_string_create(txn->idl->class->database)); | |
475281c0 BP |
1360 | |
1361 | /* Add prerequisites and declarations of new rows. */ | |
4e8e4213 | 1362 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
475281c0 BP |
1363 | /* XXX check that deleted rows exist even if no prereqs? */ |
1364 | if (row->prereqs) { | |
1365 | const struct ovsdb_idl_table_class *class = row->table->class; | |
1366 | size_t n_columns = class->n_columns; | |
1367 | struct json *op, *columns, *row_json; | |
1368 | size_t idx; | |
1369 | ||
1370 | op = json_object_create(); | |
1371 | json_array_add(operations, op); | |
1372 | json_object_put_string(op, "op", "wait"); | |
1373 | json_object_put_string(op, "table", class->name); | |
1374 | json_object_put(op, "timeout", json_integer_create(0)); | |
1375 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
1376 | json_object_put_string(op, "until", "=="); | |
1377 | columns = json_array_create_empty(); | |
1378 | json_object_put(op, "columns", columns); | |
1379 | row_json = json_object_create(); | |
1380 | json_object_put(op, "rows", json_array_create_1(row_json)); | |
1381 | ||
1382 | BITMAP_FOR_EACH_1 (idx, n_columns, row->prereqs) { | |
1383 | const struct ovsdb_idl_column *column = &class->columns[idx]; | |
1384 | json_array_add(columns, json_string_create(column->name)); | |
1385 | json_object_put(row_json, column->name, | |
1386 | ovsdb_datum_to_json(&row->old[idx], | |
1387 | &column->type)); | |
1388 | } | |
1389 | } | |
475281c0 BP |
1390 | } |
1391 | ||
1392 | /* Add updates. */ | |
1393 | any_updates = false; | |
4e8e4213 | 1394 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
475281c0 | 1395 | const struct ovsdb_idl_table_class *class = row->table->class; |
475281c0 BP |
1396 | |
1397 | if (row->old == row->new) { | |
1398 | continue; | |
1399 | } else if (!row->new) { | |
1400 | struct json *op = json_object_create(); | |
1401 | json_object_put_string(op, "op", "delete"); | |
1402 | json_object_put_string(op, "table", class->name); | |
1403 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
1404 | json_array_add(operations, op); | |
f0f54cb4 | 1405 | any_updates = true; |
475281c0 | 1406 | } else { |
f0f54cb4 BP |
1407 | struct json *row_json; |
1408 | struct json *op; | |
1409 | size_t idx; | |
1410 | ||
1411 | op = json_object_create(); | |
1412 | json_object_put_string(op, "op", row->old ? "update" : "insert"); | |
1413 | json_object_put_string(op, "table", class->name); | |
1414 | if (row->old) { | |
1415 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
1416 | } else { | |
69490970 BP |
1417 | struct ovsdb_idl_txn_insert *insert; |
1418 | ||
f0f54cb4 BP |
1419 | json_object_put(op, "uuid-name", |
1420 | json_string_create_nocopy( | |
1421 | uuid_name_from_uuid(&row->uuid))); | |
69490970 BP |
1422 | |
1423 | insert = xmalloc(sizeof *insert); | |
1424 | insert->dummy = row->uuid; | |
9cb53f26 | 1425 | insert->op_index = operations->u.array.n - 1; |
69490970 BP |
1426 | uuid_zero(&insert->real); |
1427 | hmap_insert(&txn->inserted_rows, &insert->hmap_node, | |
1428 | uuid_hash(&insert->dummy)); | |
f0f54cb4 BP |
1429 | } |
1430 | row_json = json_object_create(); | |
1431 | json_object_put(op, "row", row_json); | |
1432 | ||
35c2ce98 JG |
1433 | if (row->written) { |
1434 | BITMAP_FOR_EACH_1 (idx, class->n_columns, row->written) { | |
1435 | const struct ovsdb_idl_column *column = | |
1436 | &class->columns[idx]; | |
1437 | ||
1438 | if (row->old | |
1439 | ? !ovsdb_datum_equals(&row->old[idx], &row->new[idx], | |
1440 | &column->type) | |
1441 | : !ovsdb_datum_is_default(&row->new[idx], | |
1442 | &column->type)) { | |
1443 | json_object_put(row_json, column->name, | |
1444 | substitute_uuids( | |
1445 | ovsdb_datum_to_json(&row->new[idx], | |
1446 | &column->type), | |
1447 | txn)); | |
1448 | } | |
475281c0 | 1449 | } |
f0f54cb4 BP |
1450 | } |
1451 | ||
1452 | if (!row->old || !shash_is_empty(json_object(row_json))) { | |
1453 | json_array_add(operations, op); | |
1454 | any_updates = true; | |
1455 | } else { | |
1456 | json_destroy(op); | |
475281c0 BP |
1457 | } |
1458 | } | |
1459 | } | |
1460 | ||
b54e22e9 BP |
1461 | /* Add increment. */ |
1462 | if (txn->inc_table && any_updates) { | |
1463 | struct json *op; | |
1464 | ||
9cb53f26 | 1465 | txn->inc_index = operations->u.array.n - 1; |
b54e22e9 BP |
1466 | |
1467 | op = json_object_create(); | |
1468 | json_object_put_string(op, "op", "mutate"); | |
1469 | json_object_put_string(op, "table", txn->inc_table); | |
1470 | json_object_put(op, "where", | |
1471 | substitute_uuids(json_clone(txn->inc_where), txn)); | |
1472 | json_object_put(op, "mutations", | |
1473 | json_array_create_1( | |
1474 | json_array_create_3( | |
1475 | json_string_create(txn->inc_column), | |
1476 | json_string_create("+="), | |
1477 | json_integer_create(1)))); | |
1478 | json_array_add(operations, op); | |
1479 | ||
1480 | op = json_object_create(); | |
1481 | json_object_put_string(op, "op", "select"); | |
1482 | json_object_put_string(op, "table", txn->inc_table); | |
1483 | json_object_put(op, "where", | |
1484 | substitute_uuids(json_clone(txn->inc_where), txn)); | |
1485 | json_object_put(op, "columns", | |
1486 | json_array_create_1(json_string_create( | |
1487 | txn->inc_column))); | |
1488 | json_array_add(operations, op); | |
1489 | } | |
1490 | ||
d171b584 BP |
1491 | if (txn->comment.length) { |
1492 | struct json *op = json_object_create(); | |
1493 | json_object_put_string(op, "op", "comment"); | |
1494 | json_object_put_string(op, "comment", ds_cstr(&txn->comment)); | |
1495 | json_array_add(operations, op); | |
1496 | } | |
1497 | ||
577aebdf BP |
1498 | if (txn->dry_run) { |
1499 | struct json *op = json_object_create(); | |
1500 | json_object_put_string(op, "op", "abort"); | |
1501 | json_array_add(operations, op); | |
1502 | } | |
1503 | ||
72c6edc5 | 1504 | if (!any_updates) { |
b54e22e9 | 1505 | txn->status = TXN_UNCHANGED; |
2f3ca7ea | 1506 | json_destroy(operations); |
72c6edc5 BP |
1507 | } else if (!jsonrpc_session_send( |
1508 | txn->idl->session, | |
1509 | jsonrpc_create_request( | |
1510 | "transact", operations, &txn->request_id))) { | |
1511 | hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node, | |
1512 | json_hash(txn->request_id, 0)); | |
1513 | } else { | |
79554078 | 1514 | txn->status = TXN_TRY_AGAIN; |
72c6edc5 | 1515 | } |
475281c0 | 1516 | |
475281c0 | 1517 | ovsdb_idl_txn_disassemble(txn); |
475281c0 BP |
1518 | return txn->status; |
1519 | } | |
1520 | ||
af96ccd2 BP |
1521 | /* Attempts to commit 'txn', blocking until the commit either succeeds or |
1522 | * fails. Returns the final commit status, which may be any TXN_* value other | |
1523 | * than TXN_INCOMPLETE. */ | |
1524 | enum ovsdb_idl_txn_status | |
1525 | ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn) | |
1526 | { | |
1527 | enum ovsdb_idl_txn_status status; | |
1528 | ||
b302749b | 1529 | fatal_signal_run(); |
af96ccd2 BP |
1530 | while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { |
1531 | ovsdb_idl_run(txn->idl); | |
1532 | ovsdb_idl_wait(txn->idl); | |
1533 | ovsdb_idl_txn_wait(txn); | |
1534 | poll_block(); | |
1535 | } | |
1536 | return status; | |
1537 | } | |
1538 | ||
b54e22e9 BP |
1539 | int64_t |
1540 | ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn) | |
1541 | { | |
1542 | assert(txn->status == TXN_SUCCESS); | |
1543 | return txn->inc_new_value; | |
1544 | } | |
1545 | ||
475281c0 BP |
1546 | void |
1547 | ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn) | |
1548 | { | |
1549 | ovsdb_idl_txn_disassemble(txn); | |
1550 | if (txn->status == TXN_INCOMPLETE) { | |
1551 | txn->status = TXN_ABORTED; | |
1552 | } | |
1553 | } | |
1554 | ||
91e310a5 BP |
1555 | const char * |
1556 | ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn) | |
1557 | { | |
1558 | if (txn->status != TXN_ERROR) { | |
1559 | return ovsdb_idl_txn_status_to_string(txn->status); | |
1560 | } else if (txn->error) { | |
1561 | return txn->error; | |
1562 | } else { | |
1563 | return "no error details available"; | |
1564 | } | |
1565 | } | |
1566 | ||
1567 | static void | |
1568 | ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn, | |
1569 | const struct json *json) | |
1570 | { | |
1571 | if (txn->error == NULL) { | |
1572 | txn->error = json_to_string(json, JSSF_SORT); | |
1573 | } | |
1574 | } | |
1575 | ||
69490970 BP |
1576 | /* For transaction 'txn' that completed successfully, finds and returns the |
1577 | * permanent UUID that the database assigned to a newly inserted row, given the | |
1578 | * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row. | |
1579 | * | |
1580 | * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or | |
1581 | * if it was assigned by that function and then deleted by | |
1582 | * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted | |
1583 | * and then deleted within a single transaction are never sent to the database | |
1584 | * server, so it never assigns them a permanent UUID.) */ | |
1585 | const struct uuid * | |
1586 | ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn *txn, | |
1587 | const struct uuid *uuid) | |
1588 | { | |
1589 | const struct ovsdb_idl_txn_insert *insert; | |
1590 | ||
1591 | assert(txn->status == TXN_SUCCESS || txn->status == TXN_UNCHANGED); | |
4e8e4213 | 1592 | HMAP_FOR_EACH_IN_BUCKET (insert, hmap_node, |
69490970 BP |
1593 | uuid_hash(uuid), &txn->inserted_rows) { |
1594 | if (uuid_equals(uuid, &insert->dummy)) { | |
1595 | return &insert->real; | |
1596 | } | |
1597 | } | |
1598 | return NULL; | |
1599 | } | |
1600 | ||
475281c0 BP |
1601 | static void |
1602 | ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, | |
1603 | enum ovsdb_idl_txn_status status) | |
1604 | { | |
1605 | txn->status = status; | |
1606 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); | |
1607 | } | |
1608 | ||
b827c67b BP |
1609 | /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_' |
1610 | * itself and the structs derived from it (e.g. the "struct ovsrec_*", for | |
1611 | * ovs-vswitchd). | |
1612 | * | |
1613 | * 'datum' must have the correct type for its column. The IDL does not check | |
1614 | * that it meets schema constraints, but ovsdb-server will do so at commit time | |
1615 | * so it had better be correct. | |
1616 | * | |
1617 | * A transaction must be in progress. Replication of 'column' must not have | |
1618 | * been disabled (by calling ovsdb_idl_omit()). | |
1619 | * | |
1620 | * Usually this function is used indirectly through one of the "set" functions | |
1621 | * generated by ovsdb-idlc. */ | |
979821c0 BP |
1622 | void |
1623 | ovsdb_idl_txn_write(const struct ovsdb_idl_row *row_, | |
475281c0 BP |
1624 | const struct ovsdb_idl_column *column, |
1625 | struct ovsdb_datum *datum) | |
1626 | { | |
979821c0 | 1627 | struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_; |
475281c0 BP |
1628 | const struct ovsdb_idl_table_class *class = row->table->class; |
1629 | size_t column_idx = column - class->columns; | |
1630 | ||
979821c0 | 1631 | assert(row->new != NULL); |
bd76d25d | 1632 | assert(column_idx < class->n_columns); |
ef73f86c BP |
1633 | assert(row->old == NULL || |
1634 | row->table->modes[column_idx] & OVSDB_IDL_MONITOR); | |
c547535a | 1635 | |
475281c0 BP |
1636 | if (hmap_node_is_null(&row->txn_node)) { |
1637 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
1638 | uuid_hash(&row->uuid)); | |
1639 | } | |
1640 | if (row->old == row->new) { | |
1641 | row->new = xmalloc(class->n_columns * sizeof *row->new); | |
1642 | } | |
1643 | if (!row->written) { | |
1644 | row->written = bitmap_allocate(class->n_columns); | |
1645 | } | |
1646 | if (bitmap_is_set(row->written, column_idx)) { | |
1647 | ovsdb_datum_destroy(&row->new[column_idx], &column->type); | |
1648 | } else { | |
1649 | bitmap_set1(row->written, column_idx); | |
1650 | } | |
1651 | row->new[column_idx] = *datum; | |
979821c0 BP |
1652 | (column->unparse)(row); |
1653 | (column->parse)(row, &row->new[column_idx]); | |
475281c0 BP |
1654 | } |
1655 | ||
b827c67b BP |
1656 | /* Causes the original contents of 'column' in 'row_' to be verified as a |
1657 | * prerequisite to completing the transaction. That is, if 'column' in 'row_' | |
1658 | * changed (or if 'row_' was deleted) between the time that the IDL originally | |
1659 | * read its contents and the time that the transaction commits, then the | |
1660 | * transaction aborts and ovsdb_idl_txn_commit() returns TXN_TRY_AGAIN. | |
1661 | * | |
1662 | * The intention is that, to ensure that no transaction commits based on dirty | |
1663 | * reads, an application should call ovsdb_idl_txn_verify() on each data item | |
1664 | * read as part of a read-modify-write operation. | |
1665 | * | |
1666 | * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current | |
1667 | * value of 'column' is already known: | |
1668 | * | |
1669 | * - If 'row_' is a row created by the current transaction (returned by | |
1670 | * ovsdb_idl_txn_insert()). | |
1671 | * | |
1672 | * - If 'column' has already been modified (with ovsdb_idl_txn_write()) | |
1673 | * within the current transaction. | |
1674 | * | |
1675 | * Because of the latter property, always call ovsdb_idl_txn_verify() *before* | |
1676 | * ovsdb_idl_txn_write() for a given read-modify-write. | |
1677 | * | |
1678 | * A transaction must be in progress. | |
1679 | * | |
1680 | * Usually this function is used indirectly through one of the "verify" | |
1681 | * functions generated by ovsdb-idlc. */ | |
475281c0 BP |
1682 | void |
1683 | ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_, | |
1684 | const struct ovsdb_idl_column *column) | |
1685 | { | |
1686 | struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_; | |
1687 | const struct ovsdb_idl_table_class *class = row->table->class; | |
1688 | size_t column_idx = column - class->columns; | |
1689 | ||
979821c0 | 1690 | assert(row->new != NULL); |
ee21cfa7 BP |
1691 | assert(row->old == NULL || |
1692 | row->table->modes[column_idx] & OVSDB_IDL_MONITOR); | |
475281c0 BP |
1693 | if (!row->old |
1694 | || (row->written && bitmap_is_set(row->written, column_idx))) { | |
1695 | return; | |
1696 | } | |
1697 | ||
1698 | if (hmap_node_is_null(&row->txn_node)) { | |
1699 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
1700 | uuid_hash(&row->uuid)); | |
1701 | } | |
1702 | if (!row->prereqs) { | |
1703 | row->prereqs = bitmap_allocate(class->n_columns); | |
1704 | } | |
1705 | bitmap_set1(row->prereqs, column_idx); | |
1706 | } | |
1707 | ||
b827c67b BP |
1708 | /* Deletes 'row_' from its table. May free 'row_', so it must not be |
1709 | * accessed afterward. | |
1710 | * | |
1711 | * A transaction must be in progress. | |
1712 | * | |
1713 | * Usually this function is used indirectly through one of the "delete" | |
1714 | * functions generated by ovsdb-idlc. */ | |
475281c0 | 1715 | void |
9e336f49 | 1716 | ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_) |
475281c0 | 1717 | { |
9e336f49 BP |
1718 | struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_; |
1719 | ||
979821c0 | 1720 | assert(row->new != NULL); |
475281c0 | 1721 | if (!row->old) { |
0196cc15 | 1722 | ovsdb_idl_row_unparse(row); |
475281c0 BP |
1723 | ovsdb_idl_row_clear_new(row); |
1724 | assert(!row->prereqs); | |
979821c0 | 1725 | hmap_remove(&row->table->rows, &row->hmap_node); |
475281c0 BP |
1726 | hmap_remove(&row->table->idl->txn->txn_rows, &row->txn_node); |
1727 | free(row); | |
c7f7adb7 | 1728 | return; |
475281c0 BP |
1729 | } |
1730 | if (hmap_node_is_null(&row->txn_node)) { | |
1731 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
1732 | uuid_hash(&row->uuid)); | |
1733 | } | |
1ebeed63 BP |
1734 | ovsdb_idl_row_clear_new(row); |
1735 | row->new = NULL; | |
475281c0 BP |
1736 | } |
1737 | ||
b827c67b BP |
1738 | /* Inserts and returns a new row in the table with the specified 'class' in the |
1739 | * database with open transaction 'txn'. | |
1740 | * | |
1741 | * The new row is assigned a provisional UUID. If 'uuid' is null then one is | |
1742 | * randomly generated; otherwise 'uuid' should specify a randomly generated | |
1743 | * UUID not otherwise in use. ovsdb-server will assign a different UUID when | |
1744 | * 'txn' is committed, but the IDL will replace any uses of the provisional | |
1745 | * UUID in the data to be to be committed by the UUID assigned by | |
1746 | * ovsdb-server. | |
1747 | * | |
1748 | * Usually this function is used indirectly through one of the "insert" | |
1749 | * functions generated by ovsdb-idlc. */ | |
9e336f49 | 1750 | const struct ovsdb_idl_row * |
475281c0 | 1751 | ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn, |
ce5a3e38 BP |
1752 | const struct ovsdb_idl_table_class *class, |
1753 | const struct uuid *uuid) | |
475281c0 BP |
1754 | { |
1755 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class); | |
ce5a3e38 BP |
1756 | |
1757 | if (uuid) { | |
1758 | assert(!ovsdb_idl_txn_get_row(txn, uuid)); | |
1759 | row->uuid = *uuid; | |
1760 | } else { | |
1761 | uuid_generate(&row->uuid); | |
1762 | } | |
1763 | ||
475281c0 BP |
1764 | row->table = ovsdb_idl_table_from_class(txn->idl, class); |
1765 | row->new = xmalloc(class->n_columns * sizeof *row->new); | |
979821c0 | 1766 | hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid)); |
475281c0 BP |
1767 | hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); |
1768 | return row; | |
1769 | } | |
1770 | ||
1771 | static void | |
1772 | ovsdb_idl_txn_abort_all(struct ovsdb_idl *idl) | |
1773 | { | |
1774 | struct ovsdb_idl_txn *txn; | |
1775 | ||
4e8e4213 | 1776 | HMAP_FOR_EACH (txn, hmap_node, &idl->outstanding_txns) { |
475281c0 BP |
1777 | ovsdb_idl_txn_complete(txn, TXN_TRY_AGAIN); |
1778 | } | |
1779 | } | |
1780 | ||
1781 | static struct ovsdb_idl_txn * | |
1782 | ovsdb_idl_txn_find(struct ovsdb_idl *idl, const struct json *id) | |
1783 | { | |
1784 | struct ovsdb_idl_txn *txn; | |
1785 | ||
4e8e4213 | 1786 | HMAP_FOR_EACH_WITH_HASH (txn, hmap_node, |
475281c0 BP |
1787 | json_hash(id, 0), &idl->outstanding_txns) { |
1788 | if (json_equal(id, txn->request_id)) { | |
1789 | return txn; | |
1790 | } | |
1791 | } | |
1792 | return NULL; | |
1793 | } | |
1794 | ||
b54e22e9 BP |
1795 | static bool |
1796 | check_json_type(const struct json *json, enum json_type type, const char *name) | |
1797 | { | |
1798 | if (!json) { | |
1799 | VLOG_WARN_RL(&syntax_rl, "%s is missing", name); | |
1800 | return false; | |
1801 | } else if (json->type != type) { | |
1802 | VLOG_WARN_RL(&syntax_rl, "%s is %s instead of %s", | |
1803 | name, json_type_to_string(json->type), | |
1804 | json_type_to_string(type)); | |
1805 | return false; | |
1806 | } else { | |
1807 | return true; | |
1808 | } | |
1809 | } | |
1810 | ||
1811 | static bool | |
1812 | ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn, | |
1813 | const struct json_array *results) | |
1814 | { | |
1815 | struct json *count, *rows, *row, *column; | |
1816 | struct shash *mutate, *select; | |
1817 | ||
1818 | if (txn->inc_index + 2 > results->n) { | |
1819 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " | |
6fce4487 | 1820 | "for increment (has %zu, needs %u)", |
b54e22e9 BP |
1821 | results->n, txn->inc_index + 2); |
1822 | return false; | |
1823 | } | |
1824 | ||
69490970 | 1825 | /* We know that this is a JSON object because the loop in |
b54e22e9 BP |
1826 | * ovsdb_idl_txn_process_reply() checked. */ |
1827 | mutate = json_object(results->elems[txn->inc_index]); | |
1828 | count = shash_find_data(mutate, "count"); | |
1829 | if (!check_json_type(count, JSON_INTEGER, "\"mutate\" reply \"count\"")) { | |
1830 | return false; | |
1831 | } | |
1832 | if (count->u.integer != 1) { | |
1833 | VLOG_WARN_RL(&syntax_rl, | |
6fce4487 | 1834 | "\"mutate\" reply \"count\" is %lld instead of 1", |
b54e22e9 BP |
1835 | count->u.integer); |
1836 | return false; | |
1837 | } | |
1838 | ||
1839 | select = json_object(results->elems[txn->inc_index + 1]); | |
1840 | rows = shash_find_data(select, "rows"); | |
1841 | if (!check_json_type(rows, JSON_ARRAY, "\"select\" reply \"rows\"")) { | |
1842 | return false; | |
1843 | } | |
1844 | if (rows->u.array.n != 1) { | |
6fce4487 | 1845 | VLOG_WARN_RL(&syntax_rl, "\"select\" reply \"rows\" has %zu elements " |
b54e22e9 BP |
1846 | "instead of 1", |
1847 | rows->u.array.n); | |
1848 | return false; | |
1849 | } | |
1850 | row = rows->u.array.elems[0]; | |
1851 | if (!check_json_type(row, JSON_OBJECT, "\"select\" reply row")) { | |
1852 | return false; | |
1853 | } | |
1854 | column = shash_find_data(json_object(row), txn->inc_column); | |
1855 | if (!check_json_type(column, JSON_INTEGER, | |
1856 | "\"select\" reply inc column")) { | |
1857 | return false; | |
1858 | } | |
1859 | txn->inc_new_value = column->u.integer; | |
1860 | return true; | |
1861 | } | |
1862 | ||
69490970 BP |
1863 | static bool |
1864 | ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert, | |
1865 | const struct json_array *results) | |
1866 | { | |
bd76d25d | 1867 | static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT; |
69490970 BP |
1868 | struct ovsdb_error *error; |
1869 | struct json *json_uuid; | |
1870 | union ovsdb_atom uuid; | |
1871 | struct shash *reply; | |
1872 | ||
1873 | if (insert->op_index >= results->n) { | |
1874 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " | |
6fce4487 | 1875 | "for insert (has %zu, needs %u)", |
69490970 BP |
1876 | results->n, insert->op_index); |
1877 | return false; | |
1878 | } | |
1879 | ||
1880 | /* We know that this is a JSON object because the loop in | |
1881 | * ovsdb_idl_txn_process_reply() checked. */ | |
1882 | reply = json_object(results->elems[insert->op_index]); | |
1883 | json_uuid = shash_find_data(reply, "uuid"); | |
1884 | if (!check_json_type(json_uuid, JSON_ARRAY, "\"insert\" reply \"uuid\"")) { | |
1885 | return false; | |
1886 | } | |
1887 | ||
bd76d25d | 1888 | error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL); |
69490970 BP |
1889 | if (error) { |
1890 | char *s = ovsdb_error_to_string(error); | |
1891 | VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON " | |
1892 | "UUID: %s", s); | |
1893 | free(s); | |
1894 | return false; | |
1895 | } | |
1896 | ||
1897 | insert->real = uuid.uuid; | |
1898 | ||
1899 | return true; | |
1900 | } | |
b54e22e9 | 1901 | |
475281c0 BP |
1902 | static bool |
1903 | ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl, | |
1904 | const struct jsonrpc_msg *msg) | |
1905 | { | |
1906 | struct ovsdb_idl_txn *txn; | |
1907 | enum ovsdb_idl_txn_status status; | |
1908 | ||
1909 | txn = ovsdb_idl_txn_find(idl, msg->id); | |
1910 | if (!txn) { | |
1911 | return false; | |
1912 | } | |
1913 | ||
1914 | if (msg->type == JSONRPC_ERROR) { | |
1915 | status = TXN_ERROR; | |
1916 | } else if (msg->result->type != JSON_ARRAY) { | |
1917 | VLOG_WARN_RL(&syntax_rl, "reply to \"transact\" is not JSON array"); | |
1918 | status = TXN_ERROR; | |
1919 | } else { | |
69490970 | 1920 | struct json_array *ops = &msg->result->u.array; |
475281c0 BP |
1921 | int hard_errors = 0; |
1922 | int soft_errors = 0; | |
1923 | size_t i; | |
1924 | ||
69490970 BP |
1925 | for (i = 0; i < ops->n; i++) { |
1926 | struct json *op = ops->elems[i]; | |
475281c0 | 1927 | |
69490970 | 1928 | if (op->type == JSON_NULL) { |
475281c0 BP |
1929 | /* This isn't an error in itself but indicates that some prior |
1930 | * operation failed, so make sure that we know about it. */ | |
1931 | soft_errors++; | |
69490970 | 1932 | } else if (op->type == JSON_OBJECT) { |
475281c0 BP |
1933 | struct json *error; |
1934 | ||
69490970 | 1935 | error = shash_find_data(json_object(op), "error"); |
475281c0 BP |
1936 | if (error) { |
1937 | if (error->type == JSON_STRING) { | |
1938 | if (!strcmp(error->u.string, "timed out")) { | |
1939 | soft_errors++; | |
577aebdf | 1940 | } else if (strcmp(error->u.string, "aborted")) { |
475281c0 | 1941 | hard_errors++; |
91e310a5 | 1942 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
1943 | } |
1944 | } else { | |
1945 | hard_errors++; | |
91e310a5 | 1946 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
1947 | VLOG_WARN_RL(&syntax_rl, |
1948 | "\"error\" in reply is not JSON string"); | |
1949 | } | |
1950 | } | |
1951 | } else { | |
1952 | hard_errors++; | |
91e310a5 | 1953 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
1954 | VLOG_WARN_RL(&syntax_rl, |
1955 | "operation reply is not JSON null or object"); | |
1956 | } | |
1957 | } | |
1958 | ||
69490970 BP |
1959 | if (!soft_errors && !hard_errors) { |
1960 | struct ovsdb_idl_txn_insert *insert; | |
1961 | ||
1962 | if (txn->inc_table && !ovsdb_idl_txn_process_inc_reply(txn, ops)) { | |
1963 | hard_errors++; | |
1964 | } | |
1965 | ||
4e8e4213 | 1966 | HMAP_FOR_EACH (insert, hmap_node, &txn->inserted_rows) { |
69490970 BP |
1967 | if (!ovsdb_idl_txn_process_insert_reply(insert, ops)) { |
1968 | hard_errors++; | |
1969 | } | |
1970 | } | |
b54e22e9 BP |
1971 | } |
1972 | ||
475281c0 BP |
1973 | status = (hard_errors ? TXN_ERROR |
1974 | : soft_errors ? TXN_TRY_AGAIN | |
1975 | : TXN_SUCCESS); | |
1976 | } | |
1977 | ||
1978 | ovsdb_idl_txn_complete(txn, status); | |
1979 | return true; | |
1980 | } | |
76c91af9 BP |
1981 | |
1982 | struct ovsdb_idl_txn * | |
1983 | ovsdb_idl_txn_get(const struct ovsdb_idl_row *row) | |
1984 | { | |
1985 | struct ovsdb_idl_txn *txn = row->table->idl->txn; | |
1986 | assert(txn != NULL); | |
1987 | return txn; | |
1988 | } | |
1e86ae6f BP |
1989 | |
1990 | struct ovsdb_idl * | |
1991 | ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn) | |
1992 | { | |
1993 | return txn->idl; | |
1994 | } | |
1995 |