]>
Commit | Line | Data |
---|---|---|
8d668224 | 1 | /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. |
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 | ||
475281c0 | 20 | #include <errno.h> |
b54e22e9 | 21 | #include <inttypes.h> |
c3bb4bd7 BP |
22 | #include <limits.h> |
23 | #include <stdlib.h> | |
24 | ||
475281c0 | 25 | #include "bitmap.h" |
60032110 | 26 | #include "coverage.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" | |
e6211adc | 37 | #include "openvswitch/vlog.h" |
c3bb4bd7 | 38 | |
d98e6007 | 39 | VLOG_DEFINE_THIS_MODULE(ovsdb_idl); |
5136ce49 | 40 | |
60032110 RW |
41 | COVERAGE_DEFINE(txn_uncommitted); |
42 | COVERAGE_DEFINE(txn_unchanged); | |
43 | COVERAGE_DEFINE(txn_incomplete); | |
44 | COVERAGE_DEFINE(txn_aborted); | |
45 | COVERAGE_DEFINE(txn_success); | |
46 | COVERAGE_DEFINE(txn_try_again); | |
47 | COVERAGE_DEFINE(txn_not_locked); | |
48 | COVERAGE_DEFINE(txn_error); | |
49 | ||
c3bb4bd7 BP |
50 | /* An arc from one idl_row to another. When row A contains a UUID that |
51 | * references row B, this is represented by an arc from A (the source) to B | |
52 | * (the destination). | |
53 | * | |
54 | * Arcs from a row to itself are omitted, that is, src and dst are always | |
55 | * different. | |
56 | * | |
57 | * Arcs are never duplicated, that is, even if there are multiple references | |
58 | * from A to B, there is only a single arc from A to B. | |
59 | * | |
60 | * Arcs are directed: an arc from A to B is the converse of an an arc from B to | |
61 | * A. Both an arc and its converse may both be present, if each row refers | |
62 | * to the other circularly. | |
63 | * | |
64 | * The source and destination row may be in the same table or in different | |
65 | * tables. | |
66 | */ | |
67 | struct ovsdb_idl_arc { | |
ca6ba700 TG |
68 | struct ovs_list src_node; /* In src->src_arcs list. */ |
69 | struct ovs_list dst_node; /* In dst->dst_arcs list. */ | |
c3bb4bd7 BP |
70 | struct ovsdb_idl_row *src; /* Source row. */ |
71 | struct ovsdb_idl_row *dst; /* Destination row. */ | |
72 | }; | |
73 | ||
74 | struct ovsdb_idl { | |
115f1e4d | 75 | const struct ovsdb_idl_class *class; |
c3bb4bd7 | 76 | struct jsonrpc_session *session; |
115f1e4d | 77 | struct shash table_by_name; |
ef73f86c | 78 | struct ovsdb_idl_table *tables; /* Contains "struct ovsdb_idl_table *"s.*/ |
c3bb4bd7 BP |
79 | struct json *monitor_request_id; |
80 | unsigned int last_monitor_request_seqno; | |
81 | unsigned int change_seqno; | |
8cdec725 | 82 | bool verify_write_only; |
475281c0 | 83 | |
06b6d651 BP |
84 | /* Database locking. */ |
85 | char *lock_name; /* Name of lock we need, NULL if none. */ | |
86 | bool has_lock; /* Has db server told us we have the lock? */ | |
87 | bool is_lock_contended; /* Has db server told us we can't get lock? */ | |
88 | struct json *lock_request_id; /* JSON-RPC ID of in-flight lock request. */ | |
89 | ||
475281c0 BP |
90 | /* Transaction support. */ |
91 | struct ovsdb_idl_txn *txn; | |
92 | struct hmap outstanding_txns; | |
93 | }; | |
94 | ||
95 | struct ovsdb_idl_txn { | |
96 | struct hmap_node hmap_node; | |
97 | struct json *request_id; | |
98 | struct ovsdb_idl *idl; | |
99 | struct hmap txn_rows; | |
100 | enum ovsdb_idl_txn_status status; | |
91e310a5 | 101 | char *error; |
577aebdf | 102 | bool dry_run; |
d171b584 | 103 | struct ds comment; |
b54e22e9 BP |
104 | |
105 | /* Increments. */ | |
94fbe1aa BP |
106 | const char *inc_table; |
107 | const char *inc_column; | |
108 | struct uuid inc_row; | |
b54e22e9 BP |
109 | unsigned int inc_index; |
110 | int64_t inc_new_value; | |
69490970 BP |
111 | |
112 | /* Inserted rows. */ | |
d95d1510 | 113 | struct hmap inserted_rows; /* Contains "struct ovsdb_idl_txn_insert"s. */ |
69490970 BP |
114 | }; |
115 | ||
116 | struct ovsdb_idl_txn_insert { | |
117 | struct hmap_node hmap_node; /* In struct ovsdb_idl_txn's inserted_rows. */ | |
118 | struct uuid dummy; /* Dummy UUID used locally. */ | |
119 | int op_index; /* Index into transaction's operation array. */ | |
120 | struct uuid real; /* Real UUID used by database server. */ | |
c3bb4bd7 BP |
121 | }; |
122 | ||
123 | static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
124 | static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
125 | ||
126 | static void ovsdb_idl_clear(struct ovsdb_idl *); | |
127 | static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *); | |
128 | static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *); | |
129 | static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *, | |
130 | const struct json *); | |
c547535a | 131 | static bool ovsdb_idl_process_update(struct ovsdb_idl_table *, |
c3bb4bd7 BP |
132 | const struct uuid *, |
133 | const struct json *old, | |
134 | const struct json *new); | |
135 | static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *); | |
136 | static void ovsdb_idl_delete_row(struct ovsdb_idl_row *); | |
c547535a | 137 | static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *); |
c3bb4bd7 BP |
138 | |
139 | static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *); | |
475281c0 BP |
140 | static struct ovsdb_idl_row *ovsdb_idl_row_create__( |
141 | const struct ovsdb_idl_table_class *); | |
c3bb4bd7 BP |
142 | static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *, |
143 | const struct uuid *); | |
144 | static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *); | |
145 | ||
979821c0 BP |
146 | static void ovsdb_idl_row_parse(struct ovsdb_idl_row *); |
147 | static void ovsdb_idl_row_unparse(struct ovsdb_idl_row *); | |
475281c0 BP |
148 | static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *); |
149 | static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *); | |
150 | ||
151 | static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *); | |
152 | static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl *, | |
153 | const struct jsonrpc_msg *msg); | |
c3bb4bd7 | 154 | |
06b6d651 BP |
155 | static void ovsdb_idl_send_lock_request(struct ovsdb_idl *); |
156 | static void ovsdb_idl_send_unlock_request(struct ovsdb_idl *); | |
157 | static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl *, | |
158 | const struct json *); | |
159 | static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl *, | |
160 | const struct json *params, | |
161 | bool new_has_lock); | |
162 | ||
2ce42c88 BP |
163 | /* Creates and returns a connection to database 'remote', which should be in a |
164 | * form acceptable to jsonrpc_session_open(). The connection will maintain an | |
165 | * in-memory replica of the remote database whose schema is described by | |
166 | * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically | |
ef73f86c BP |
167 | * by ovsdb-idlc.) |
168 | * | |
fba6bd1d BP |
169 | * Passes 'retry' to jsonrpc_session_open(). See that function for |
170 | * documentation. | |
171 | * | |
ef73f86c BP |
172 | * If 'monitor_everything_by_default' is true, then everything in the remote |
173 | * database will be replicated by default. ovsdb_idl_omit() and | |
174 | * ovsdb_idl_omit_alert() may be used to selectively drop some columns from | |
175 | * monitoring. | |
176 | * | |
177 | * If 'monitor_everything_by_default' is false, then no columns or tables will | |
178 | * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table() | |
179 | * must be used to choose some columns or tables to replicate. | |
180 | */ | |
c3bb4bd7 | 181 | struct ovsdb_idl * |
ef73f86c | 182 | ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class, |
fba6bd1d | 183 | bool monitor_everything_by_default, bool retry) |
c3bb4bd7 BP |
184 | { |
185 | struct ovsdb_idl *idl; | |
ef73f86c | 186 | uint8_t default_mode; |
c3bb4bd7 BP |
187 | size_t i; |
188 | ||
ef73f86c BP |
189 | default_mode = (monitor_everything_by_default |
190 | ? OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT | |
191 | : 0); | |
192 | ||
c3bb4bd7 | 193 | idl = xzalloc(sizeof *idl); |
115f1e4d | 194 | idl->class = class; |
fba6bd1d | 195 | idl->session = jsonrpc_session_open(remote, retry); |
115f1e4d BP |
196 | shash_init(&idl->table_by_name); |
197 | idl->tables = xmalloc(class->n_tables * sizeof *idl->tables); | |
c3bb4bd7 BP |
198 | for (i = 0; i < class->n_tables; i++) { |
199 | const struct ovsdb_idl_table_class *tc = &class->tables[i]; | |
115f1e4d | 200 | struct ovsdb_idl_table *table = &idl->tables[i]; |
c3bb4bd7 BP |
201 | size_t j; |
202 | ||
efdd9088 | 203 | shash_add_assert(&idl->table_by_name, tc->name, table); |
c3bb4bd7 | 204 | table->class = tc; |
c547535a | 205 | table->modes = xmalloc(tc->n_columns); |
ef73f86c BP |
206 | memset(table->modes, default_mode, tc->n_columns); |
207 | table->need_table = false; | |
c3bb4bd7 BP |
208 | shash_init(&table->columns); |
209 | for (j = 0; j < tc->n_columns; j++) { | |
210 | const struct ovsdb_idl_column *column = &tc->columns[j]; | |
211 | ||
efdd9088 | 212 | shash_add_assert(&table->columns, column->name, column); |
c3bb4bd7 BP |
213 | } |
214 | hmap_init(&table->rows); | |
215 | table->idl = idl; | |
216 | } | |
217 | idl->last_monitor_request_seqno = UINT_MAX; | |
475281c0 | 218 | hmap_init(&idl->outstanding_txns); |
c3bb4bd7 BP |
219 | |
220 | return idl; | |
221 | } | |
222 | ||
2ce42c88 | 223 | /* Destroys 'idl' and all of the data structures that it manages. */ |
c3bb4bd7 BP |
224 | void |
225 | ovsdb_idl_destroy(struct ovsdb_idl *idl) | |
226 | { | |
227 | if (idl) { | |
115f1e4d | 228 | size_t i; |
c3bb4bd7 | 229 | |
cb22974d | 230 | ovs_assert(!idl->txn); |
c3bb4bd7 BP |
231 | ovsdb_idl_clear(idl); |
232 | jsonrpc_session_close(idl->session); | |
233 | ||
115f1e4d BP |
234 | for (i = 0; i < idl->class->n_tables; i++) { |
235 | struct ovsdb_idl_table *table = &idl->tables[i]; | |
c3bb4bd7 BP |
236 | shash_destroy(&table->columns); |
237 | hmap_destroy(&table->rows); | |
c547535a | 238 | free(table->modes); |
c3bb4bd7 | 239 | } |
115f1e4d BP |
240 | shash_destroy(&idl->table_by_name); |
241 | free(idl->tables); | |
c3bb4bd7 | 242 | json_destroy(idl->monitor_request_id); |
06b6d651 BP |
243 | free(idl->lock_name); |
244 | json_destroy(idl->lock_request_id); | |
da157f1e | 245 | hmap_destroy(&idl->outstanding_txns); |
c3bb4bd7 BP |
246 | free(idl); |
247 | } | |
248 | } | |
249 | ||
250 | static void | |
251 | ovsdb_idl_clear(struct ovsdb_idl *idl) | |
252 | { | |
c3bb4bd7 | 253 | bool changed = false; |
115f1e4d | 254 | size_t i; |
c3bb4bd7 | 255 | |
115f1e4d BP |
256 | for (i = 0; i < idl->class->n_tables; i++) { |
257 | struct ovsdb_idl_table *table = &idl->tables[i]; | |
c3bb4bd7 BP |
258 | struct ovsdb_idl_row *row, *next_row; |
259 | ||
260 | if (hmap_is_empty(&table->rows)) { | |
261 | continue; | |
262 | } | |
263 | ||
264 | changed = true; | |
4e8e4213 | 265 | HMAP_FOR_EACH_SAFE (row, next_row, hmap_node, &table->rows) { |
c3bb4bd7 BP |
266 | struct ovsdb_idl_arc *arc, *next_arc; |
267 | ||
268 | if (!ovsdb_idl_row_is_orphan(row)) { | |
979821c0 | 269 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 270 | } |
4e8e4213 | 271 | LIST_FOR_EACH_SAFE (arc, next_arc, src_node, &row->src_arcs) { |
c3bb4bd7 BP |
272 | free(arc); |
273 | } | |
274 | /* No need to do anything with dst_arcs: some node has those arcs | |
275 | * as forward arcs and will destroy them itself. */ | |
276 | ||
0196cc15 | 277 | ovsdb_idl_row_destroy(row); |
c3bb4bd7 BP |
278 | } |
279 | } | |
280 | ||
281 | if (changed) { | |
282 | idl->change_seqno++; | |
283 | } | |
284 | } | |
285 | ||
854a94d9 BP |
286 | /* Processes a batch of messages from the database server on 'idl'. This may |
287 | * cause the IDL's contents to change. The client may check for that with | |
288 | * ovsdb_idl_get_seqno(). */ | |
289 | void | |
c3bb4bd7 BP |
290 | ovsdb_idl_run(struct ovsdb_idl *idl) |
291 | { | |
292 | int i; | |
293 | ||
cb22974d | 294 | ovs_assert(!idl->txn); |
c3bb4bd7 BP |
295 | jsonrpc_session_run(idl->session); |
296 | for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) { | |
828cd4c7 | 297 | struct jsonrpc_msg *msg; |
c3bb4bd7 BP |
298 | unsigned int seqno; |
299 | ||
300 | seqno = jsonrpc_session_get_seqno(idl->session); | |
301 | if (idl->last_monitor_request_seqno != seqno) { | |
302 | idl->last_monitor_request_seqno = seqno; | |
475281c0 | 303 | ovsdb_idl_txn_abort_all(idl); |
c3bb4bd7 | 304 | ovsdb_idl_send_monitor_request(idl); |
06b6d651 BP |
305 | if (idl->lock_name) { |
306 | ovsdb_idl_send_lock_request(idl); | |
307 | } | |
c3bb4bd7 BP |
308 | break; |
309 | } | |
310 | ||
311 | msg = jsonrpc_session_recv(idl->session); | |
312 | if (!msg) { | |
313 | break; | |
314 | } | |
315 | ||
4931f33a | 316 | if (msg->type == JSONRPC_NOTIFY |
d95d1510 BP |
317 | && !strcmp(msg->method, "update") |
318 | && msg->params->type == JSON_ARRAY | |
319 | && msg->params->u.array.n == 2 | |
320 | && msg->params->u.array.elems[0]->type == JSON_NULL) { | |
06b6d651 | 321 | /* Database contents changed. */ |
c3bb4bd7 BP |
322 | ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1]); |
323 | } else if (msg->type == JSONRPC_REPLY | |
324 | && idl->monitor_request_id | |
325 | && json_equal(idl->monitor_request_id, msg->id)) { | |
06b6d651 | 326 | /* Reply to our "monitor" request. */ |
c547535a | 327 | idl->change_seqno++; |
c3bb4bd7 BP |
328 | json_destroy(idl->monitor_request_id); |
329 | idl->monitor_request_id = NULL; | |
330 | ovsdb_idl_clear(idl); | |
331 | ovsdb_idl_parse_update(idl, msg->result); | |
06b6d651 BP |
332 | } else if (msg->type == JSONRPC_REPLY |
333 | && idl->lock_request_id | |
334 | && json_equal(idl->lock_request_id, msg->id)) { | |
335 | /* Reply to our "lock" request. */ | |
336 | ovsdb_idl_parse_lock_reply(idl, msg->result); | |
337 | } else if (msg->type == JSONRPC_NOTIFY | |
338 | && !strcmp(msg->method, "locked")) { | |
339 | /* We got our lock. */ | |
340 | ovsdb_idl_parse_lock_notify(idl, msg->params, true); | |
341 | } else if (msg->type == JSONRPC_NOTIFY | |
342 | && !strcmp(msg->method, "stolen")) { | |
343 | /* Someone else stole our lock. */ | |
344 | ovsdb_idl_parse_lock_notify(idl, msg->params, false); | |
475281c0 BP |
345 | } else if ((msg->type == JSONRPC_ERROR |
346 | || msg->type == JSONRPC_REPLY) | |
347 | && ovsdb_idl_txn_process_reply(idl, msg)) { | |
348 | /* ovsdb_idl_txn_process_reply() did everything needful. */ | |
c3bb4bd7 | 349 | } else { |
c5a80c70 BP |
350 | /* This can happen if ovsdb_idl_txn_destroy() is called to destroy |
351 | * a transaction before we receive the reply, so keep the log level | |
352 | * low. */ | |
353 | VLOG_DBG("%s: received unexpected %s message", | |
354 | jsonrpc_session_get_name(idl->session), | |
355 | jsonrpc_msg_type_to_string(msg->type)); | |
c3bb4bd7 | 356 | } |
c3bb4bd7 BP |
357 | jsonrpc_msg_destroy(msg); |
358 | } | |
359 | } | |
360 | ||
2ce42c88 BP |
361 | /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to |
362 | * do or when activity occurs on a transaction on 'idl'. */ | |
c3bb4bd7 BP |
363 | void |
364 | ovsdb_idl_wait(struct ovsdb_idl *idl) | |
365 | { | |
366 | jsonrpc_session_wait(idl->session); | |
367 | jsonrpc_session_recv_wait(idl->session); | |
368 | } | |
369 | ||
2f926787 BP |
370 | /* Returns a "sequence number" that represents the state of 'idl'. When |
371 | * ovsdb_idl_run() changes the database, the sequence number changes. The | |
372 | * initial fetch of the entire contents of the remote database is considered to | |
373 | * be one kind of change. Successfully acquiring a lock, if one has been | |
374 | * configured with ovsdb_idl_set_lock(), is also considered to be a change. | |
375 | * | |
376 | * As long as the sequence number does not change, the client may continue to | |
377 | * use any data structures it obtains from 'idl'. But when it changes, the | |
378 | * client must not access any of these data structures again, because they | |
379 | * could have freed or reused for other purposes. | |
380 | * | |
381 | * The sequence number can occasionally change even if the database does not. | |
382 | * This happens if the connection to the database drops and reconnects, which | |
383 | * causes the database contents to be reloaded even if they didn't change. (It | |
384 | * could also happen if the database server sends out a "change" that reflects | |
385 | * what the IDL already thought was in the database. The database server is | |
386 | * not supposed to do that, but bugs could in theory cause it to do so.) */ | |
c3bb4bd7 BP |
387 | unsigned int |
388 | ovsdb_idl_get_seqno(const struct ovsdb_idl *idl) | |
389 | { | |
390 | return idl->change_seqno; | |
391 | } | |
392 | ||
2ce42c88 BP |
393 | /* Returns true if 'idl' successfully connected to the remote database and |
394 | * retrieved its contents (even if the connection subsequently dropped and is | |
395 | * in the process of reconnecting). If so, then 'idl' contains an atomic | |
396 | * snapshot of the database's contents (but it might be arbitrarily old if the | |
397 | * connection dropped). | |
398 | * | |
399 | * Returns false if 'idl' has never connected or retrieved the database's | |
400 | * contents. If so, 'idl' is empty. */ | |
f3d64521 JP |
401 | bool |
402 | ovsdb_idl_has_ever_connected(const struct ovsdb_idl *idl) | |
403 | { | |
404 | return ovsdb_idl_get_seqno(idl) != 0; | |
405 | } | |
406 | ||
705d7a39 AA |
407 | /* Reconfigures 'idl' so that it would reconnect to the database, if |
408 | * connection was dropped. */ | |
409 | void | |
410 | ovsdb_idl_enable_reconnect(struct ovsdb_idl *idl) | |
411 | { | |
412 | jsonrpc_session_enable_reconnect(idl->session); | |
413 | } | |
414 | ||
2ce42c88 BP |
415 | /* Forces 'idl' to drop its connection to the database and reconnect. In the |
416 | * meantime, the contents of 'idl' will not change. */ | |
c3bb4bd7 BP |
417 | void |
418 | ovsdb_idl_force_reconnect(struct ovsdb_idl *idl) | |
419 | { | |
420 | jsonrpc_session_force_reconnect(idl->session); | |
421 | } | |
8cdec725 EJ |
422 | |
423 | /* Some IDL users should only write to write-only columns. Furthermore, | |
424 | * writing to a column which is not write-only can cause serious performance | |
425 | * degradations for these users. This function causes 'idl' to reject writes | |
426 | * to columns which are not marked write only using ovsdb_idl_omit_alert(). */ | |
427 | void | |
428 | ovsdb_idl_verify_write_only(struct ovsdb_idl *idl) | |
429 | { | |
430 | idl->verify_write_only = true; | |
431 | } | |
fba6bd1d | 432 | |
8d668224 | 433 | /* Returns true if 'idl' is currently connected or trying to connect. */ |
fba6bd1d BP |
434 | bool |
435 | ovsdb_idl_is_alive(const struct ovsdb_idl *idl) | |
436 | { | |
437 | return jsonrpc_session_is_alive(idl->session); | |
438 | } | |
439 | ||
8d668224 BP |
440 | /* Returns the last error reported on a connection by 'idl'. The return value |
441 | * is 0 only if no connection made by 'idl' has ever encountered an error. See | |
442 | * jsonrpc_get_status() for return value interpretation. */ | |
fba6bd1d BP |
443 | int |
444 | ovsdb_idl_get_last_error(const struct ovsdb_idl *idl) | |
445 | { | |
446 | return jsonrpc_session_get_last_error(idl->session); | |
447 | } | |
ef73f86c BP |
448 | \f |
449 | static unsigned char * | |
450 | ovsdb_idl_get_mode(struct ovsdb_idl *idl, | |
451 | const struct ovsdb_idl_column *column) | |
c547535a BP |
452 | { |
453 | size_t i; | |
454 | ||
cb22974d | 455 | ovs_assert(!idl->change_seqno); |
ef73f86c | 456 | |
c547535a BP |
457 | for (i = 0; i < idl->class->n_tables; i++) { |
458 | const struct ovsdb_idl_table *table = &idl->tables[i]; | |
459 | const struct ovsdb_idl_table_class *tc = table->class; | |
460 | ||
461 | if (column >= tc->columns && column < &tc->columns[tc->n_columns]) { | |
ef73f86c | 462 | return &table->modes[column - tc->columns]; |
c547535a BP |
463 | } |
464 | } | |
465 | ||
428b2edd | 466 | OVS_NOT_REACHED(); |
c547535a BP |
467 | } |
468 | ||
ef73f86c BP |
469 | static void |
470 | add_ref_table(struct ovsdb_idl *idl, const struct ovsdb_base_type *base) | |
471 | { | |
472 | if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { | |
473 | struct ovsdb_idl_table *table; | |
474 | ||
475 | table = shash_find_data(&idl->table_by_name, | |
476 | base->u.uuid.refTableName); | |
477 | if (table) { | |
478 | table->need_table = true; | |
479 | } else { | |
480 | VLOG_WARN("%s IDL class missing referenced table %s", | |
481 | idl->class->database, base->u.uuid.refTableName); | |
482 | } | |
483 | } | |
484 | } | |
485 | ||
486 | /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also | |
487 | * ensures that any tables referenced by 'column' will be replicated, even if | |
488 | * no columns in that table are selected for replication (see | |
489 | * ovsdb_idl_add_table() for more information). | |
c547535a | 490 | * |
ef73f86c BP |
491 | * This function is only useful if 'monitor_everything_by_default' was false in |
492 | * the call to ovsdb_idl_create(). This function should be called between | |
493 | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). | |
494 | */ | |
495 | void | |
496 | ovsdb_idl_add_column(struct ovsdb_idl *idl, | |
497 | const struct ovsdb_idl_column *column) | |
498 | { | |
499 | *ovsdb_idl_get_mode(idl, column) = OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT; | |
500 | add_ref_table(idl, &column->type.key); | |
501 | add_ref_table(idl, &column->type.value); | |
502 | } | |
503 | ||
504 | /* Ensures that the table with class 'tc' will be replicated on 'idl' even if | |
505 | * no columns are selected for replication. This can be useful because it | |
506 | * allows 'idl' to keep track of what rows in the table actually exist, which | |
507 | * in turn allows columns that reference the table to have accurate contents. | |
508 | * (The IDL presents the database with references to rows that do not exist | |
509 | * removed.) | |
c547535a | 510 | * |
ef73f86c BP |
511 | * This function is only useful if 'monitor_everything_by_default' was false in |
512 | * the call to ovsdb_idl_create(). This function should be called between | |
513 | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). | |
514 | */ | |
515 | void | |
516 | ovsdb_idl_add_table(struct ovsdb_idl *idl, | |
517 | const struct ovsdb_idl_table_class *tc) | |
518 | { | |
519 | size_t i; | |
520 | ||
521 | for (i = 0; i < idl->class->n_tables; i++) { | |
522 | struct ovsdb_idl_table *table = &idl->tables[i]; | |
523 | ||
524 | if (table->class == tc) { | |
525 | table->need_table = true; | |
526 | return; | |
527 | } | |
528 | } | |
529 | ||
428b2edd | 530 | OVS_NOT_REACHED(); |
ef73f86c BP |
531 | } |
532 | ||
533 | /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'. | |
c547535a | 534 | * |
ef73f86c BP |
535 | * This function should be called between ovsdb_idl_create() and the first call |
536 | * to ovsdb_idl_run(). | |
537 | */ | |
c547535a | 538 | void |
ef73f86c BP |
539 | ovsdb_idl_omit_alert(struct ovsdb_idl *idl, |
540 | const struct ovsdb_idl_column *column) | |
c547535a | 541 | { |
ef73f86c | 542 | *ovsdb_idl_get_mode(idl, column) &= ~OVSDB_IDL_ALERT; |
c547535a BP |
543 | } |
544 | ||
ef73f86c BP |
545 | /* Sets the mode for 'column' in 'idl' to 0. See the big comment above |
546 | * OVSDB_IDL_MONITOR for details. | |
c547535a | 547 | * |
ef73f86c BP |
548 | * This function should be called between ovsdb_idl_create() and the first call |
549 | * to ovsdb_idl_run(). | |
550 | */ | |
c547535a BP |
551 | void |
552 | ovsdb_idl_omit(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) | |
553 | { | |
ef73f86c | 554 | *ovsdb_idl_get_mode(idl, column) = 0; |
c547535a | 555 | } |
c3bb4bd7 BP |
556 | \f |
557 | static void | |
558 | ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl) | |
559 | { | |
560 | struct json *monitor_requests; | |
c3bb4bd7 | 561 | struct jsonrpc_msg *msg; |
115f1e4d | 562 | size_t i; |
c3bb4bd7 BP |
563 | |
564 | monitor_requests = json_object_create(); | |
115f1e4d BP |
565 | for (i = 0; i < idl->class->n_tables; i++) { |
566 | const struct ovsdb_idl_table *table = &idl->tables[i]; | |
c3bb4bd7 BP |
567 | const struct ovsdb_idl_table_class *tc = table->class; |
568 | struct json *monitor_request, *columns; | |
2a022368 | 569 | size_t j; |
c3bb4bd7 | 570 | |
ef73f86c | 571 | columns = table->need_table ? json_array_create_empty() : NULL; |
2a022368 BP |
572 | for (j = 0; j < tc->n_columns; j++) { |
573 | const struct ovsdb_idl_column *column = &tc->columns[j]; | |
ef73f86c BP |
574 | if (table->modes[j] & OVSDB_IDL_MONITOR) { |
575 | if (!columns) { | |
576 | columns = json_array_create_empty(); | |
577 | } | |
c547535a BP |
578 | json_array_add(columns, json_string_create(column->name)); |
579 | } | |
c3bb4bd7 | 580 | } |
ef73f86c BP |
581 | |
582 | if (columns) { | |
583 | monitor_request = json_object_create(); | |
584 | json_object_put(monitor_request, "columns", columns); | |
585 | json_object_put(monitor_requests, tc->name, monitor_request); | |
586 | } | |
c3bb4bd7 BP |
587 | } |
588 | ||
589 | json_destroy(idl->monitor_request_id); | |
590 | msg = jsonrpc_create_request( | |
9cb53f26 BP |
591 | "monitor", |
592 | json_array_create_3(json_string_create(idl->class->database), | |
593 | json_null_create(), monitor_requests), | |
c3bb4bd7 BP |
594 | &idl->monitor_request_id); |
595 | jsonrpc_session_send(idl->session, msg); | |
596 | } | |
597 | ||
598 | static void | |
599 | ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates) | |
600 | { | |
c547535a | 601 | struct ovsdb_error *error = ovsdb_idl_parse_update__(idl, table_updates); |
c3bb4bd7 BP |
602 | if (error) { |
603 | if (!VLOG_DROP_WARN(&syntax_rl)) { | |
604 | char *s = ovsdb_error_to_string(error); | |
605 | VLOG_WARN_RL(&syntax_rl, "%s", s); | |
606 | free(s); | |
607 | } | |
608 | ovsdb_error_destroy(error); | |
609 | } | |
610 | } | |
611 | ||
612 | static struct ovsdb_error * | |
613 | ovsdb_idl_parse_update__(struct ovsdb_idl *idl, | |
614 | const struct json *table_updates) | |
615 | { | |
616 | const struct shash_node *tables_node; | |
617 | ||
618 | if (table_updates->type != JSON_OBJECT) { | |
619 | return ovsdb_syntax_error(table_updates, NULL, | |
620 | "<table-updates> is not an object"); | |
621 | } | |
622 | SHASH_FOR_EACH (tables_node, json_object(table_updates)) { | |
623 | const struct json *table_update = tables_node->data; | |
624 | const struct shash_node *table_node; | |
625 | struct ovsdb_idl_table *table; | |
626 | ||
115f1e4d | 627 | table = shash_find_data(&idl->table_by_name, tables_node->name); |
c3bb4bd7 BP |
628 | if (!table) { |
629 | return ovsdb_syntax_error( | |
630 | table_updates, NULL, | |
631 | "<table-updates> includes unknown table \"%s\"", | |
632 | tables_node->name); | |
633 | } | |
634 | ||
635 | if (table_update->type != JSON_OBJECT) { | |
636 | return ovsdb_syntax_error(table_update, NULL, | |
637 | "<table-update> for table \"%s\" is " | |
638 | "not an object", table->class->name); | |
639 | } | |
640 | SHASH_FOR_EACH (table_node, json_object(table_update)) { | |
641 | const struct json *row_update = table_node->data; | |
642 | const struct json *old_json, *new_json; | |
643 | struct uuid uuid; | |
644 | ||
645 | if (!uuid_from_string(&uuid, table_node->name)) { | |
646 | return ovsdb_syntax_error(table_update, NULL, | |
647 | "<table-update> for table \"%s\" " | |
648 | "contains bad UUID " | |
649 | "\"%s\" as member name", | |
650 | table->class->name, | |
651 | table_node->name); | |
652 | } | |
653 | if (row_update->type != JSON_OBJECT) { | |
654 | return ovsdb_syntax_error(row_update, NULL, | |
655 | "<table-update> for table \"%s\" " | |
656 | "contains <row-update> for %s that " | |
657 | "is not an object", | |
658 | table->class->name, | |
659 | table_node->name); | |
660 | } | |
661 | ||
662 | old_json = shash_find_data(json_object(row_update), "old"); | |
663 | new_json = shash_find_data(json_object(row_update), "new"); | |
664 | if (old_json && old_json->type != JSON_OBJECT) { | |
665 | return ovsdb_syntax_error(old_json, NULL, | |
666 | "\"old\" <row> is not object"); | |
667 | } else if (new_json && new_json->type != JSON_OBJECT) { | |
668 | return ovsdb_syntax_error(new_json, NULL, | |
669 | "\"new\" <row> is not object"); | |
670 | } else if ((old_json != NULL) + (new_json != NULL) | |
671 | != shash_count(json_object(row_update))) { | |
672 | return ovsdb_syntax_error(row_update, NULL, | |
673 | "<row-update> contains unexpected " | |
674 | "member"); | |
675 | } else if (!old_json && !new_json) { | |
676 | return ovsdb_syntax_error(row_update, NULL, | |
677 | "<row-update> missing \"old\" " | |
678 | "and \"new\" members"); | |
679 | } | |
680 | ||
c547535a BP |
681 | if (ovsdb_idl_process_update(table, &uuid, old_json, new_json)) { |
682 | idl->change_seqno++; | |
683 | } | |
c3bb4bd7 BP |
684 | } |
685 | } | |
686 | ||
687 | return NULL; | |
688 | } | |
689 | ||
690 | static struct ovsdb_idl_row * | |
691 | ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid) | |
692 | { | |
693 | struct ovsdb_idl_row *row; | |
694 | ||
4e8e4213 | 695 | HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) { |
c3bb4bd7 BP |
696 | if (uuid_equals(&row->uuid, uuid)) { |
697 | return row; | |
698 | } | |
699 | } | |
700 | return NULL; | |
701 | } | |
702 | ||
c547535a BP |
703 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
704 | * otherwise. */ | |
705 | static bool | |
c3bb4bd7 BP |
706 | ovsdb_idl_process_update(struct ovsdb_idl_table *table, |
707 | const struct uuid *uuid, const struct json *old, | |
708 | const struct json *new) | |
709 | { | |
710 | struct ovsdb_idl_row *row; | |
711 | ||
712 | row = ovsdb_idl_get_row(table, uuid); | |
713 | if (!new) { | |
714 | /* Delete row. */ | |
715 | if (row && !ovsdb_idl_row_is_orphan(row)) { | |
716 | /* XXX perhaps we should check the 'old' values? */ | |
717 | ovsdb_idl_delete_row(row); | |
718 | } else { | |
719 | VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" " | |
720 | "from table %s", | |
721 | UUID_ARGS(uuid), table->class->name); | |
c547535a | 722 | return false; |
c3bb4bd7 BP |
723 | } |
724 | } else if (!old) { | |
725 | /* Insert row. */ | |
726 | if (!row) { | |
727 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); | |
728 | } else if (ovsdb_idl_row_is_orphan(row)) { | |
729 | ovsdb_idl_insert_row(row, new); | |
730 | } else { | |
731 | VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to " | |
732 | "table %s", UUID_ARGS(uuid), table->class->name); | |
c547535a | 733 | return ovsdb_idl_modify_row(row, new); |
c3bb4bd7 BP |
734 | } |
735 | } else { | |
736 | /* Modify row. */ | |
737 | if (row) { | |
738 | /* XXX perhaps we should check the 'old' values? */ | |
739 | if (!ovsdb_idl_row_is_orphan(row)) { | |
c547535a | 740 | return ovsdb_idl_modify_row(row, new); |
c3bb4bd7 BP |
741 | } else { |
742 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing but " | |
743 | "referenced row "UUID_FMT" in table %s", | |
744 | UUID_ARGS(uuid), table->class->name); | |
745 | ovsdb_idl_insert_row(row, new); | |
746 | } | |
747 | } else { | |
748 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" " | |
749 | "in table %s", UUID_ARGS(uuid), table->class->name); | |
750 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); | |
751 | } | |
752 | } | |
c547535a BP |
753 | |
754 | return true; | |
c3bb4bd7 BP |
755 | } |
756 | ||
c547535a BP |
757 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
758 | * otherwise. */ | |
759 | static bool | |
c3bb4bd7 BP |
760 | ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json) |
761 | { | |
762 | struct ovsdb_idl_table *table = row->table; | |
763 | struct shash_node *node; | |
c547535a | 764 | bool changed = false; |
c3bb4bd7 BP |
765 | |
766 | SHASH_FOR_EACH (node, json_object(row_json)) { | |
767 | const char *column_name = node->name; | |
768 | const struct ovsdb_idl_column *column; | |
769 | struct ovsdb_datum datum; | |
770 | struct ovsdb_error *error; | |
771 | ||
772 | column = shash_find_data(&table->columns, column_name); | |
773 | if (!column) { | |
774 | VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT, | |
775 | column_name, UUID_ARGS(&row->uuid)); | |
776 | continue; | |
777 | } | |
778 | ||
779 | error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL); | |
780 | if (!error) { | |
c547535a | 781 | unsigned int column_idx = column - table->class->columns; |
e85bbd75 BP |
782 | struct ovsdb_datum *old = &row->old[column_idx]; |
783 | ||
784 | if (!ovsdb_datum_equals(old, &datum, &column->type)) { | |
785 | ovsdb_datum_swap(old, &datum); | |
ef73f86c | 786 | if (table->modes[column_idx] & OVSDB_IDL_ALERT) { |
e85bbd75 BP |
787 | changed = true; |
788 | } | |
789 | } else { | |
790 | /* Didn't really change but the OVSDB monitor protocol always | |
791 | * includes every value in a row. */ | |
c547535a | 792 | } |
e85bbd75 BP |
793 | |
794 | ovsdb_datum_destroy(&datum, &column->type); | |
c3bb4bd7 BP |
795 | } else { |
796 | char *s = ovsdb_error_to_string(error); | |
797 | VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT | |
798 | " in table %s: %s", column_name, | |
799 | UUID_ARGS(&row->uuid), table->class->name, s); | |
800 | free(s); | |
801 | ovsdb_error_destroy(error); | |
802 | } | |
803 | } | |
c547535a | 804 | return changed; |
c3bb4bd7 BP |
805 | } |
806 | ||
90887925 BP |
807 | /* When a row A refers to row B through a column with a "refTable" constraint, |
808 | * but row B does not exist, row B is called an "orphan row". Orphan rows | |
809 | * should not persist, because the database enforces referential integrity, but | |
810 | * they can appear transiently as changes from the database are received (the | |
811 | * database doesn't try to topologically sort them and circular references mean | |
812 | * it isn't always possible anyhow). | |
813 | * | |
814 | * This function returns true if 'row' is an orphan row, otherwise false. | |
815 | */ | |
c3bb4bd7 BP |
816 | static bool |
817 | ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row) | |
818 | { | |
90887925 BP |
819 | return !row->old && !row->new; |
820 | } | |
821 | ||
f2ba3c0a BP |
822 | /* Returns true if 'row' is conceptually part of the database as modified by |
823 | * the current transaction (if any), false otherwise. | |
824 | * | |
825 | * This function will return true if 'row' is not an orphan (see the comment on | |
826 | * ovsdb_idl_row_is_orphan()) and: | |
827 | * | |
828 | * - 'row' exists in the database and has not been deleted within the | |
829 | * current transaction (if any). | |
830 | * | |
831 | * - 'row' was inserted within the current transaction and has not been | |
832 | * deleted. (In the latter case you should not have passed 'row' in at | |
833 | * all, because ovsdb_idl_txn_delete() freed it.) | |
834 | * | |
835 | * This function will return false if 'row' is an orphan or if 'row' was | |
836 | * deleted within the current transaction. | |
837 | */ | |
838 | static bool | |
839 | ovsdb_idl_row_exists(const struct ovsdb_idl_row *row) | |
840 | { | |
841 | return row->new != NULL; | |
c3bb4bd7 BP |
842 | } |
843 | ||
979821c0 BP |
844 | static void |
845 | ovsdb_idl_row_parse(struct ovsdb_idl_row *row) | |
846 | { | |
847 | const struct ovsdb_idl_table_class *class = row->table->class; | |
848 | size_t i; | |
849 | ||
850 | for (i = 0; i < class->n_columns; i++) { | |
851 | const struct ovsdb_idl_column *c = &class->columns[i]; | |
852 | (c->parse)(row, &row->old[i]); | |
853 | } | |
854 | } | |
855 | ||
856 | static void | |
857 | ovsdb_idl_row_unparse(struct ovsdb_idl_row *row) | |
858 | { | |
859 | const struct ovsdb_idl_table_class *class = row->table->class; | |
860 | size_t i; | |
861 | ||
862 | for (i = 0; i < class->n_columns; i++) { | |
863 | const struct ovsdb_idl_column *c = &class->columns[i]; | |
864 | (c->unparse)(row); | |
865 | } | |
866 | } | |
867 | ||
c3bb4bd7 | 868 | static void |
475281c0 | 869 | ovsdb_idl_row_clear_old(struct ovsdb_idl_row *row) |
c3bb4bd7 | 870 | { |
cb22974d | 871 | ovs_assert(row->old == row->new); |
c3bb4bd7 BP |
872 | if (!ovsdb_idl_row_is_orphan(row)) { |
873 | const struct ovsdb_idl_table_class *class = row->table->class; | |
874 | size_t i; | |
875 | ||
876 | for (i = 0; i < class->n_columns; i++) { | |
475281c0 BP |
877 | ovsdb_datum_destroy(&row->old[i], &class->columns[i].type); |
878 | } | |
879 | free(row->old); | |
880 | row->old = row->new = NULL; | |
881 | } | |
882 | } | |
883 | ||
884 | static void | |
885 | ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row) | |
886 | { | |
887 | if (row->old != row->new) { | |
888 | if (row->new) { | |
889 | const struct ovsdb_idl_table_class *class = row->table->class; | |
890 | size_t i; | |
891 | ||
35c2ce98 JG |
892 | if (row->written) { |
893 | BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { | |
894 | ovsdb_datum_destroy(&row->new[i], &class->columns[i].type); | |
895 | } | |
475281c0 BP |
896 | } |
897 | free(row->new); | |
898 | free(row->written); | |
899 | row->written = NULL; | |
c3bb4bd7 | 900 | } |
475281c0 | 901 | row->new = row->old; |
c3bb4bd7 BP |
902 | } |
903 | } | |
904 | ||
905 | static void | |
906 | ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts) | |
907 | { | |
908 | struct ovsdb_idl_arc *arc, *next; | |
909 | ||
910 | /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows | |
911 | * that this causes to be unreferenced. */ | |
4e8e4213 | 912 | LIST_FOR_EACH_SAFE (arc, next, src_node, &row->src_arcs) { |
c3bb4bd7 BP |
913 | list_remove(&arc->dst_node); |
914 | if (destroy_dsts | |
915 | && ovsdb_idl_row_is_orphan(arc->dst) | |
916 | && list_is_empty(&arc->dst->dst_arcs)) { | |
917 | ovsdb_idl_row_destroy(arc->dst); | |
918 | } | |
919 | free(arc); | |
920 | } | |
921 | list_init(&row->src_arcs); | |
922 | } | |
923 | ||
924 | /* Force nodes that reference 'row' to reparse. */ | |
925 | static void | |
0196cc15 | 926 | ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row) |
c3bb4bd7 BP |
927 | { |
928 | struct ovsdb_idl_arc *arc, *next; | |
929 | ||
930 | /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy | |
931 | * 'arc', so we need to use the "safe" variant of list traversal. However, | |
979821c0 BP |
932 | * calling an ovsdb_idl_column's 'parse' function will add an arc |
933 | * equivalent to 'arc' to row->arcs. That could be a problem for | |
934 | * traversal, but it adds it at the beginning of the list to prevent us | |
935 | * from stumbling upon it again. | |
c3bb4bd7 BP |
936 | * |
937 | * (If duplicate arcs were possible then we would need to make sure that | |
938 | * 'next' didn't also point into 'arc''s destination, but we forbid | |
939 | * duplicate arcs.) */ | |
4e8e4213 | 940 | LIST_FOR_EACH_SAFE (arc, next, dst_node, &row->dst_arcs) { |
c3bb4bd7 BP |
941 | struct ovsdb_idl_row *ref = arc->src; |
942 | ||
979821c0 | 943 | ovsdb_idl_row_unparse(ref); |
0196cc15 | 944 | ovsdb_idl_row_clear_arcs(ref, false); |
979821c0 | 945 | ovsdb_idl_row_parse(ref); |
c3bb4bd7 BP |
946 | } |
947 | } | |
948 | ||
475281c0 BP |
949 | static struct ovsdb_idl_row * |
950 | ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class) | |
951 | { | |
72c6edc5 | 952 | struct ovsdb_idl_row *row = xzalloc(class->allocation_size); |
a699f614 | 953 | class->row_init(row); |
475281c0 BP |
954 | list_init(&row->src_arcs); |
955 | list_init(&row->dst_arcs); | |
956 | hmap_node_nullify(&row->txn_node); | |
957 | return row; | |
958 | } | |
959 | ||
c3bb4bd7 BP |
960 | static struct ovsdb_idl_row * |
961 | ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid) | |
962 | { | |
475281c0 | 963 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(table->class); |
c3bb4bd7 BP |
964 | hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid)); |
965 | row->uuid = *uuid; | |
c3bb4bd7 | 966 | row->table = table; |
c3bb4bd7 BP |
967 | return row; |
968 | } | |
969 | ||
970 | static void | |
971 | ovsdb_idl_row_destroy(struct ovsdb_idl_row *row) | |
972 | { | |
973 | if (row) { | |
475281c0 | 974 | ovsdb_idl_row_clear_old(row); |
c3bb4bd7 BP |
975 | hmap_remove(&row->table->rows, &row->hmap_node); |
976 | free(row); | |
977 | } | |
978 | } | |
979 | ||
980 | static void | |
981 | ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json) | |
982 | { | |
983 | const struct ovsdb_idl_table_class *class = row->table->class; | |
984 | size_t i; | |
985 | ||
cb22974d | 986 | ovs_assert(!row->old && !row->new); |
475281c0 | 987 | row->old = row->new = xmalloc(class->n_columns * sizeof *row->old); |
c3bb4bd7 | 988 | for (i = 0; i < class->n_columns; i++) { |
475281c0 | 989 | ovsdb_datum_init_default(&row->old[i], &class->columns[i].type); |
c3bb4bd7 BP |
990 | } |
991 | ovsdb_idl_row_update(row, row_json); | |
979821c0 | 992 | ovsdb_idl_row_parse(row); |
c3bb4bd7 | 993 | |
0196cc15 | 994 | ovsdb_idl_row_reparse_backrefs(row); |
c3bb4bd7 BP |
995 | } |
996 | ||
997 | static void | |
998 | ovsdb_idl_delete_row(struct ovsdb_idl_row *row) | |
999 | { | |
979821c0 | 1000 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 1001 | ovsdb_idl_row_clear_arcs(row, true); |
475281c0 | 1002 | ovsdb_idl_row_clear_old(row); |
c3bb4bd7 BP |
1003 | if (list_is_empty(&row->dst_arcs)) { |
1004 | ovsdb_idl_row_destroy(row); | |
1005 | } else { | |
0196cc15 | 1006 | ovsdb_idl_row_reparse_backrefs(row); |
c3bb4bd7 BP |
1007 | } |
1008 | } | |
1009 | ||
c547535a BP |
1010 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
1011 | * otherwise. */ | |
1012 | static bool | |
c3bb4bd7 BP |
1013 | ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json) |
1014 | { | |
c547535a BP |
1015 | bool changed; |
1016 | ||
979821c0 | 1017 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 1018 | ovsdb_idl_row_clear_arcs(row, true); |
c547535a | 1019 | changed = ovsdb_idl_row_update(row, row_json); |
979821c0 | 1020 | ovsdb_idl_row_parse(row); |
c547535a BP |
1021 | |
1022 | return changed; | |
c3bb4bd7 BP |
1023 | } |
1024 | ||
1025 | static bool | |
1026 | may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst) | |
1027 | { | |
1028 | const struct ovsdb_idl_arc *arc; | |
1029 | ||
1030 | /* No self-arcs. */ | |
1031 | if (src == dst) { | |
1032 | return false; | |
1033 | } | |
1034 | ||
1035 | /* No duplicate arcs. | |
1036 | * | |
1037 | * We only need to test whether the first arc in dst->dst_arcs originates | |
1038 | * at 'src', since we add all of the arcs from a given source in a clump | |
979821c0 | 1039 | * (in a single call to ovsdb_idl_row_parse()) and new arcs are always |
c3bb4bd7 BP |
1040 | * added at the front of the dst_arcs list. */ |
1041 | if (list_is_empty(&dst->dst_arcs)) { | |
1042 | return true; | |
1043 | } | |
1044 | arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node); | |
1045 | return arc->src != src; | |
1046 | } | |
1047 | ||
115f1e4d | 1048 | static struct ovsdb_idl_table * |
475281c0 BP |
1049 | ovsdb_idl_table_from_class(const struct ovsdb_idl *idl, |
1050 | const struct ovsdb_idl_table_class *table_class) | |
115f1e4d BP |
1051 | { |
1052 | return &idl->tables[table_class - idl->class->tables]; | |
1053 | } | |
1054 | ||
2f926787 | 1055 | /* Called by ovsdb-idlc generated code. */ |
c3bb4bd7 BP |
1056 | struct ovsdb_idl_row * |
1057 | ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src, | |
1058 | struct ovsdb_idl_table_class *dst_table_class, | |
1059 | const struct uuid *dst_uuid) | |
1060 | { | |
1061 | struct ovsdb_idl *idl = src->table->idl; | |
e9011ac8 | 1062 | struct ovsdb_idl_table *dst_table; |
c3bb4bd7 BP |
1063 | struct ovsdb_idl_arc *arc; |
1064 | struct ovsdb_idl_row *dst; | |
1065 | ||
475281c0 | 1066 | dst_table = ovsdb_idl_table_from_class(idl, dst_table_class); |
e9011ac8 | 1067 | dst = ovsdb_idl_get_row(dst_table, dst_uuid); |
979821c0 BP |
1068 | if (idl->txn) { |
1069 | /* We're being called from ovsdb_idl_txn_write(). We must not update | |
1070 | * any arcs, because the transaction will be backed out at commit or | |
1071 | * abort time and we don't want our graph screwed up. | |
1072 | * | |
1073 | * Just return the destination row, if there is one and it has not been | |
1074 | * deleted. */ | |
1075 | if (dst && (hmap_node_is_null(&dst->txn_node) || dst->new)) { | |
1076 | return dst; | |
1077 | } | |
1078 | return NULL; | |
1079 | } else { | |
1080 | /* We're being called from some other context. Update the graph. */ | |
1081 | if (!dst) { | |
1082 | dst = ovsdb_idl_row_create(dst_table, dst_uuid); | |
1083 | } | |
c3bb4bd7 | 1084 | |
979821c0 BP |
1085 | /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */ |
1086 | if (may_add_arc(src, dst)) { | |
1087 | /* The arc *must* be added at the front of the dst_arcs list. See | |
1088 | * ovsdb_idl_row_reparse_backrefs() for details. */ | |
1089 | arc = xmalloc(sizeof *arc); | |
1090 | list_push_front(&src->src_arcs, &arc->src_node); | |
1091 | list_push_front(&dst->dst_arcs, &arc->dst_node); | |
1092 | arc->src = src; | |
1093 | arc->dst = dst; | |
1094 | } | |
1095 | ||
1096 | return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL; | |
c3bb4bd7 | 1097 | } |
979821c0 | 1098 | } |
c3bb4bd7 | 1099 | |
2f926787 BP |
1100 | /* Searches 'tc''s table in 'idl' for a row with UUID 'uuid'. Returns a |
1101 | * pointer to the row if there is one, otherwise a null pointer. */ | |
979821c0 BP |
1102 | const struct ovsdb_idl_row * |
1103 | ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl *idl, | |
1104 | const struct ovsdb_idl_table_class *tc, | |
1105 | const struct uuid *uuid) | |
1106 | { | |
1107 | return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl, tc), uuid); | |
c3bb4bd7 BP |
1108 | } |
1109 | ||
1110 | static struct ovsdb_idl_row * | |
1111 | next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node) | |
1112 | { | |
1113 | for (; node; node = hmap_next(&table->rows, node)) { | |
1114 | struct ovsdb_idl_row *row; | |
1115 | ||
1116 | row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node); | |
f2ba3c0a | 1117 | if (ovsdb_idl_row_exists(row)) { |
c3bb4bd7 BP |
1118 | return row; |
1119 | } | |
1120 | } | |
1121 | return NULL; | |
1122 | } | |
1123 | ||
2f926787 BP |
1124 | /* Returns a row in 'table_class''s table in 'idl', or a null pointer if that |
1125 | * table is empty. | |
1126 | * | |
1127 | * Database tables are internally maintained as hash tables, so adding or | |
1128 | * removing rows while traversing the same table can cause some rows to be | |
1129 | * visited twice or not at apply. */ | |
979821c0 | 1130 | const struct ovsdb_idl_row * |
c3bb4bd7 BP |
1131 | ovsdb_idl_first_row(const struct ovsdb_idl *idl, |
1132 | const struct ovsdb_idl_table_class *table_class) | |
1133 | { | |
475281c0 BP |
1134 | struct ovsdb_idl_table *table |
1135 | = ovsdb_idl_table_from_class(idl, table_class); | |
c3bb4bd7 BP |
1136 | return next_real_row(table, hmap_first(&table->rows)); |
1137 | } | |
1138 | ||
2f926787 BP |
1139 | /* Returns a row following 'row' within its table, or a null pointer if 'row' |
1140 | * is the last row in its table. */ | |
979821c0 | 1141 | const struct ovsdb_idl_row * |
c3bb4bd7 BP |
1142 | ovsdb_idl_next_row(const struct ovsdb_idl_row *row) |
1143 | { | |
1144 | struct ovsdb_idl_table *table = row->table; | |
1145 | ||
1146 | return next_real_row(table, hmap_next(&table->rows, &row->hmap_node)); | |
1147 | } | |
8c3c2f30 BP |
1148 | |
1149 | /* Reads and returns the value of 'column' within 'row'. If an ongoing | |
1150 | * transaction has changed 'column''s value, the modified value is returned. | |
1151 | * | |
1152 | * The caller must not modify or free the returned value. | |
1153 | * | |
1154 | * Various kinds of changes can invalidate the returned value: writing to the | |
1155 | * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row' | |
1156 | * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction | |
1157 | * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the | |
1158 | * returned value is needed for a long time, it is best to make a copy of it | |
1159 | * with ovsdb_datum_clone(). */ | |
1160 | const struct ovsdb_datum * | |
1161 | ovsdb_idl_read(const struct ovsdb_idl_row *row, | |
1162 | const struct ovsdb_idl_column *column) | |
1163 | { | |
942d31c8 BP |
1164 | const struct ovsdb_idl_table_class *class; |
1165 | size_t column_idx; | |
1166 | ||
cb22974d | 1167 | ovs_assert(!ovsdb_idl_row_is_synthetic(row)); |
942d31c8 BP |
1168 | |
1169 | class = row->table->class; | |
1170 | column_idx = column - class->columns; | |
8c3c2f30 | 1171 | |
cb22974d BP |
1172 | ovs_assert(row->new != NULL); |
1173 | ovs_assert(column_idx < class->n_columns); | |
8c3c2f30 BP |
1174 | |
1175 | if (row->written && bitmap_is_set(row->written, column_idx)) { | |
1176 | return &row->new[column_idx]; | |
1177 | } else if (row->old) { | |
1178 | return &row->old[column_idx]; | |
1179 | } else { | |
1180 | return ovsdb_datum_default(&column->type); | |
1181 | } | |
1182 | } | |
1183 | ||
1184 | /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key | |
1185 | * type 'key_type' and value type 'value_type'. (Scalar and set types will | |
1186 | * have a value type of OVSDB_TYPE_VOID.) | |
1187 | * | |
1188 | * This is useful in code that "knows" that a particular column has a given | |
1189 | * type, so that it will abort if someone changes the column's type without | |
1190 | * updating the code that uses it. */ | |
1191 | const struct ovsdb_datum * | |
1192 | ovsdb_idl_get(const struct ovsdb_idl_row *row, | |
1193 | const struct ovsdb_idl_column *column, | |
1194 | enum ovsdb_atomic_type key_type OVS_UNUSED, | |
1195 | enum ovsdb_atomic_type value_type OVS_UNUSED) | |
1196 | { | |
cb22974d BP |
1197 | ovs_assert(column->type.key.type == key_type); |
1198 | ovs_assert(column->type.value.type == value_type); | |
8c3c2f30 BP |
1199 | |
1200 | return ovsdb_idl_read(row, column); | |
1201 | } | |
cfea354b | 1202 | |
ff495b63 BP |
1203 | /* Returns true if the field represented by 'column' in 'row' may be modified, |
1204 | * false if it is immutable. | |
1205 | * | |
1206 | * Normally, whether a field is mutable is controlled by its column's schema. | |
1207 | * However, an immutable column can be set to any initial value at the time of | |
1208 | * insertion, so if 'row' is a new row (one that is being added as part of the | |
1209 | * current transaction, supposing that a transaction is in progress) then even | |
1210 | * its "immutable" fields are actually mutable. */ | |
1211 | bool | |
1212 | ovsdb_idl_is_mutable(const struct ovsdb_idl_row *row, | |
1213 | const struct ovsdb_idl_column *column) | |
1214 | { | |
1215 | return column->mutable || (row->new && !row->old); | |
1216 | } | |
1217 | ||
cfea354b BP |
1218 | /* Returns false if 'row' was obtained from the IDL, true if it was initialized |
1219 | * to all-zero-bits by some other entity. If 'row' was set up some other way | |
1220 | * then the return value is indeterminate. */ | |
1221 | bool | |
1222 | ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row *row) | |
1223 | { | |
1224 | return row->table == NULL; | |
1225 | } | |
475281c0 BP |
1226 | \f |
1227 | /* Transactions. */ | |
1228 | ||
1229 | static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, | |
1230 | enum ovsdb_idl_txn_status); | |
1231 | ||
2f926787 BP |
1232 | /* Returns a string representation of 'status'. The caller must not modify or |
1233 | * free the returned string. | |
1234 | * | |
1235 | * The return value is probably useful only for debug log messages and unit | |
1236 | * tests. */ | |
475281c0 BP |
1237 | const char * |
1238 | ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status) | |
1239 | { | |
1240 | switch (status) { | |
2096903b BP |
1241 | case TXN_UNCOMMITTED: |
1242 | return "uncommitted"; | |
b54e22e9 BP |
1243 | case TXN_UNCHANGED: |
1244 | return "unchanged"; | |
475281c0 BP |
1245 | case TXN_INCOMPLETE: |
1246 | return "incomplete"; | |
1247 | case TXN_ABORTED: | |
1248 | return "aborted"; | |
1249 | case TXN_SUCCESS: | |
1250 | return "success"; | |
854a94d9 BP |
1251 | case TXN_TRY_AGAIN: |
1252 | return "try again"; | |
06b6d651 BP |
1253 | case TXN_NOT_LOCKED: |
1254 | return "not locked"; | |
475281c0 BP |
1255 | case TXN_ERROR: |
1256 | return "error"; | |
1257 | } | |
1258 | return "<unknown>"; | |
1259 | } | |
1260 | ||
2f926787 BP |
1261 | /* Starts a new transaction on 'idl'. A given ovsdb_idl may only have a single |
1262 | * active transaction at a time. See the large comment in ovsdb-idl.h for | |
1263 | * general information on transactions. */ | |
475281c0 BP |
1264 | struct ovsdb_idl_txn * |
1265 | ovsdb_idl_txn_create(struct ovsdb_idl *idl) | |
1266 | { | |
1267 | struct ovsdb_idl_txn *txn; | |
1268 | ||
cb22974d | 1269 | ovs_assert(!idl->txn); |
475281c0 | 1270 | idl->txn = txn = xmalloc(sizeof *txn); |
0196cc15 | 1271 | txn->request_id = NULL; |
475281c0 | 1272 | txn->idl = idl; |
475281c0 | 1273 | hmap_init(&txn->txn_rows); |
2096903b | 1274 | txn->status = TXN_UNCOMMITTED; |
91e310a5 | 1275 | txn->error = NULL; |
577aebdf | 1276 | txn->dry_run = false; |
d171b584 | 1277 | ds_init(&txn->comment); |
0196cc15 | 1278 | |
b54e22e9 BP |
1279 | txn->inc_table = NULL; |
1280 | txn->inc_column = NULL; | |
0196cc15 | 1281 | |
69490970 | 1282 | hmap_init(&txn->inserted_rows); |
0196cc15 | 1283 | |
475281c0 BP |
1284 | return txn; |
1285 | } | |
1286 | ||
e1c0e2d1 BP |
1287 | /* Appends 's', which is treated as a printf()-type format string, to the |
1288 | * comments that will be passed to the OVSDB server when 'txn' is committed. | |
1289 | * (The comment will be committed to the OVSDB log, which "ovsdb-tool | |
1290 | * show-log" can print in a relatively human-readable form.) */ | |
d171b584 | 1291 | void |
e1c0e2d1 | 1292 | ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...) |
d171b584 | 1293 | { |
e1c0e2d1 BP |
1294 | va_list args; |
1295 | ||
d171b584 BP |
1296 | if (txn->comment.length) { |
1297 | ds_put_char(&txn->comment, '\n'); | |
1298 | } | |
e1c0e2d1 BP |
1299 | |
1300 | va_start(args, s); | |
1301 | ds_put_format_valist(&txn->comment, s, args); | |
1302 | va_end(args); | |
d171b584 BP |
1303 | } |
1304 | ||
2f926787 BP |
1305 | /* Marks 'txn' as a transaction that will not actually modify the database. In |
1306 | * almost every way, the transaction is treated like other transactions. It | |
1307 | * must be committed or aborted like other transactions, it will be sent to the | |
1308 | * database server like other transactions, and so on. The only difference is | |
1309 | * that the operations sent to the database server will include, as the last | |
1310 | * step, an "abort" operation, so that any changes made by the transaction will | |
1311 | * not actually take effect. */ | |
577aebdf BP |
1312 | void |
1313 | ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn *txn) | |
1314 | { | |
1315 | txn->dry_run = true; | |
1316 | } | |
1317 | ||
94fbe1aa BP |
1318 | /* Causes 'txn', when committed, to increment the value of 'column' within |
1319 | * 'row' by 1. 'column' must have an integer type. After 'txn' commits | |
1320 | * successfully, the client may retrieve the final (incremented) value of | |
1321 | * 'column' with ovsdb_idl_txn_get_increment_new_value(). | |
1322 | * | |
1323 | * The client could accomplish something similar with ovsdb_idl_read(), | |
1324 | * ovsdb_idl_txn_verify() and ovsdb_idl_txn_write(), or with ovsdb-idlc | |
1325 | * generated wrappers for these functions. However, ovsdb_idl_txn_increment() | |
1326 | * will never (by itself) fail because of a verify error. | |
1327 | * | |
1328 | * The intended use is for incrementing the "next_cfg" column in the | |
1329 | * Open_vSwitch table. */ | |
b54e22e9 | 1330 | void |
94fbe1aa BP |
1331 | ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn, |
1332 | const struct ovsdb_idl_row *row, | |
1333 | const struct ovsdb_idl_column *column) | |
b54e22e9 | 1334 | { |
cb22974d BP |
1335 | ovs_assert(!txn->inc_table); |
1336 | ovs_assert(column->type.key.type == OVSDB_TYPE_INTEGER); | |
1337 | ovs_assert(column->type.value.type == OVSDB_TYPE_VOID); | |
94fbe1aa BP |
1338 | |
1339 | txn->inc_table = row->table->class->name; | |
1340 | txn->inc_column = column->name; | |
1341 | txn->inc_row = row->uuid; | |
b54e22e9 BP |
1342 | } |
1343 | ||
2f926787 BP |
1344 | /* Destroys 'txn' and frees all associated memory. If ovsdb_idl_txn_commit() |
1345 | * has been called for 'txn' but the commit is still incomplete (that is, the | |
1346 | * last call returned TXN_INCOMPLETE) then the transaction may or may not still | |
1347 | * end up committing at the database server, but the client will not be able to | |
1348 | * get any further status information back. */ | |
475281c0 BP |
1349 | void |
1350 | ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn) | |
1351 | { | |
69490970 BP |
1352 | struct ovsdb_idl_txn_insert *insert, *next; |
1353 | ||
0196cc15 | 1354 | json_destroy(txn->request_id); |
fbd8fd40 BP |
1355 | if (txn->status == TXN_INCOMPLETE) { |
1356 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); | |
1357 | } | |
475281c0 | 1358 | ovsdb_idl_txn_abort(txn); |
d171b584 | 1359 | ds_destroy(&txn->comment); |
91e310a5 | 1360 | free(txn->error); |
4e8e4213 | 1361 | HMAP_FOR_EACH_SAFE (insert, next, hmap_node, &txn->inserted_rows) { |
69490970 BP |
1362 | free(insert); |
1363 | } | |
0196cc15 | 1364 | hmap_destroy(&txn->inserted_rows); |
475281c0 BP |
1365 | free(txn); |
1366 | } | |
1367 | ||
2f926787 | 1368 | /* Causes poll_block() to wake up if 'txn' has completed committing. */ |
586bb84a BP |
1369 | void |
1370 | ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *txn) | |
1371 | { | |
2096903b | 1372 | if (txn->status != TXN_UNCOMMITTED && txn->status != TXN_INCOMPLETE) { |
586bb84a BP |
1373 | poll_immediate_wake(); |
1374 | } | |
1375 | } | |
1376 | ||
475281c0 BP |
1377 | static struct json * |
1378 | where_uuid_equals(const struct uuid *uuid) | |
1379 | { | |
1380 | return | |
1381 | json_array_create_1( | |
1382 | json_array_create_3( | |
1383 | json_string_create("_uuid"), | |
1384 | json_string_create("=="), | |
1385 | json_array_create_2( | |
1386 | json_string_create("uuid"), | |
1387 | json_string_create_nocopy( | |
1388 | xasprintf(UUID_FMT, UUID_ARGS(uuid)))))); | |
1389 | } | |
1390 | ||
1391 | static char * | |
1392 | uuid_name_from_uuid(const struct uuid *uuid) | |
1393 | { | |
1394 | char *name; | |
1395 | char *p; | |
1396 | ||
1397 | name = xasprintf("row"UUID_FMT, UUID_ARGS(uuid)); | |
1398 | for (p = name; *p != '\0'; p++) { | |
1399 | if (*p == '-') { | |
1400 | *p = '_'; | |
1401 | } | |
1402 | } | |
1403 | ||
1404 | return name; | |
1405 | } | |
1406 | ||
1407 | static const struct ovsdb_idl_row * | |
1408 | ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid) | |
1409 | { | |
1410 | const struct ovsdb_idl_row *row; | |
1411 | ||
4e8e4213 | 1412 | HMAP_FOR_EACH_WITH_HASH (row, txn_node, uuid_hash(uuid), &txn->txn_rows) { |
475281c0 BP |
1413 | if (uuid_equals(&row->uuid, uuid)) { |
1414 | return row; | |
1415 | } | |
1416 | } | |
1417 | return NULL; | |
1418 | } | |
1419 | ||
1420 | /* XXX there must be a cleaner way to do this */ | |
1421 | static struct json * | |
1422 | substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn) | |
1423 | { | |
1424 | if (json->type == JSON_ARRAY) { | |
1425 | struct uuid uuid; | |
1426 | size_t i; | |
1427 | ||
1428 | if (json->u.array.n == 2 | |
1429 | && json->u.array.elems[0]->type == JSON_STRING | |
1430 | && json->u.array.elems[1]->type == JSON_STRING | |
1431 | && !strcmp(json->u.array.elems[0]->u.string, "uuid") | |
1432 | && uuid_from_string(&uuid, json->u.array.elems[1]->u.string)) { | |
1433 | const struct ovsdb_idl_row *row; | |
1434 | ||
1435 | row = ovsdb_idl_txn_get_row(txn, &uuid); | |
1436 | if (row && !row->old && row->new) { | |
1437 | json_destroy(json); | |
1438 | ||
1439 | return json_array_create_2( | |
1440 | json_string_create("named-uuid"), | |
1441 | json_string_create_nocopy(uuid_name_from_uuid(&uuid))); | |
1442 | } | |
1443 | } | |
1444 | ||
1445 | for (i = 0; i < json->u.array.n; i++) { | |
1446 | json->u.array.elems[i] = substitute_uuids(json->u.array.elems[i], | |
1447 | txn); | |
1448 | } | |
1449 | } else if (json->type == JSON_OBJECT) { | |
1450 | struct shash_node *node; | |
1451 | ||
1452 | SHASH_FOR_EACH (node, json_object(json)) { | |
1453 | node->data = substitute_uuids(node->data, txn); | |
1454 | } | |
1455 | } | |
1456 | return json; | |
1457 | } | |
1458 | ||
1459 | static void | |
1460 | ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn) | |
1461 | { | |
1462 | struct ovsdb_idl_row *row, *next; | |
1463 | ||
979821c0 BP |
1464 | /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an |
1465 | * ovsdb_idl_column's 'parse' function, which will call | |
1466 | * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a | |
1467 | * transaction and fail to update the graph. */ | |
1468 | txn->idl->txn = NULL; | |
1469 | ||
4e8e4213 | 1470 | HMAP_FOR_EACH_SAFE (row, next, txn_node, &txn->txn_rows) { |
979821c0 BP |
1471 | if (row->old) { |
1472 | if (row->written) { | |
1473 | ovsdb_idl_row_unparse(row); | |
1474 | ovsdb_idl_row_clear_arcs(row, false); | |
1475 | ovsdb_idl_row_parse(row); | |
1476 | } | |
1477 | } else { | |
0196cc15 | 1478 | ovsdb_idl_row_unparse(row); |
8bc915de | 1479 | } |
475281c0 BP |
1480 | ovsdb_idl_row_clear_new(row); |
1481 | ||
1482 | free(row->prereqs); | |
1483 | row->prereqs = NULL; | |
1484 | ||
1485 | free(row->written); | |
1486 | row->written = NULL; | |
1487 | ||
1488 | hmap_remove(&txn->txn_rows, &row->txn_node); | |
1489 | hmap_node_nullify(&row->txn_node); | |
0196cc15 BP |
1490 | if (!row->old) { |
1491 | hmap_remove(&row->table->rows, &row->hmap_node); | |
1492 | free(row); | |
1493 | } | |
475281c0 BP |
1494 | } |
1495 | hmap_destroy(&txn->txn_rows); | |
1496 | hmap_init(&txn->txn_rows); | |
1497 | } | |
1498 | ||
2f926787 BP |
1499 | /* Attempts to commit 'txn'. Returns the status of the commit operation, one |
1500 | * of the following TXN_* constants: | |
1501 | * | |
1502 | * TXN_INCOMPLETE: | |
1503 | * | |
1504 | * The transaction is in progress, but not yet complete. The caller | |
1505 | * should call again later, after calling ovsdb_idl_run() to let the IDL | |
1506 | * do OVSDB protocol processing. | |
1507 | * | |
1508 | * TXN_UNCHANGED: | |
1509 | * | |
1510 | * The transaction is complete. (It didn't actually change the database, | |
1511 | * so the IDL didn't send any request to the database server.) | |
1512 | * | |
1513 | * TXN_ABORTED: | |
1514 | * | |
1515 | * The caller previously called ovsdb_idl_txn_abort(). | |
1516 | * | |
1517 | * TXN_SUCCESS: | |
1518 | * | |
1519 | * The transaction was successful. The update made by the transaction | |
1520 | * (and possibly other changes made by other database clients) should | |
1521 | * already be visible in the IDL. | |
1522 | * | |
1523 | * TXN_TRY_AGAIN: | |
1524 | * | |
1525 | * The transaction failed for some transient reason, e.g. because a | |
1526 | * "verify" operation reported an inconsistency or due to a network | |
1527 | * problem. The caller should wait for a change to the database, then | |
1528 | * compose a new transaction, and commit the new transaction. | |
1529 | * | |
1530 | * Use the return value of ovsdb_idl_get_seqno() to wait for a change in | |
1531 | * the database. It is important to use its return value *before* the | |
1532 | * initial call to ovsdb_idl_txn_commit() as the baseline for this | |
1533 | * purpose, because the change that one should wait for can happen after | |
1534 | * the initial call but before the call that returns TXN_TRY_AGAIN, and | |
1535 | * using some other baseline value in that situation could cause an | |
1536 | * indefinite wait if the database rarely changes. | |
1537 | * | |
1538 | * TXN_NOT_LOCKED: | |
1539 | * | |
1540 | * The transaction failed because the IDL has been configured to require | |
1541 | * a database lock (with ovsdb_idl_set_lock()) but didn't get it yet or | |
1542 | * has already lost it. | |
1543 | * | |
1544 | * Committing a transaction rolls back all of the changes that it made to the | |
1545 | * IDL's copy of the database. If the transaction commits successfully, then | |
1546 | * the database server will send an update and, thus, the IDL will be updated | |
1547 | * with the committed changes. */ | |
475281c0 BP |
1548 | enum ovsdb_idl_txn_status |
1549 | ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) | |
1550 | { | |
1551 | struct ovsdb_idl_row *row; | |
1552 | struct json *operations; | |
1553 | bool any_updates; | |
475281c0 BP |
1554 | |
1555 | if (txn != txn->idl->txn) { | |
60032110 | 1556 | goto coverage_out; |
475281c0 BP |
1557 | } |
1558 | ||
06b6d651 BP |
1559 | /* If we need a lock but don't have it, give up quickly. */ |
1560 | if (txn->idl->lock_name && !ovsdb_idl_has_lock(txn->idl)) { | |
1561 | txn->status = TXN_NOT_LOCKED; | |
60032110 | 1562 | goto disassemble_out; |
06b6d651 BP |
1563 | } |
1564 | ||
9cb53f26 BP |
1565 | operations = json_array_create_1( |
1566 | json_string_create(txn->idl->class->database)); | |
475281c0 | 1567 | |
06b6d651 BP |
1568 | /* Assert that we have the required lock (avoiding a race). */ |
1569 | if (txn->idl->lock_name) { | |
1570 | struct json *op = json_object_create(); | |
1571 | json_array_add(operations, op); | |
1572 | json_object_put_string(op, "op", "assert"); | |
1573 | json_object_put_string(op, "lock", txn->idl->lock_name); | |
1574 | } | |
1575 | ||
475281c0 | 1576 | /* Add prerequisites and declarations of new rows. */ |
4e8e4213 | 1577 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
475281c0 BP |
1578 | /* XXX check that deleted rows exist even if no prereqs? */ |
1579 | if (row->prereqs) { | |
1580 | const struct ovsdb_idl_table_class *class = row->table->class; | |
1581 | size_t n_columns = class->n_columns; | |
1582 | struct json *op, *columns, *row_json; | |
1583 | size_t idx; | |
1584 | ||
1585 | op = json_object_create(); | |
1586 | json_array_add(operations, op); | |
1587 | json_object_put_string(op, "op", "wait"); | |
1588 | json_object_put_string(op, "table", class->name); | |
1589 | json_object_put(op, "timeout", json_integer_create(0)); | |
1590 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
1591 | json_object_put_string(op, "until", "=="); | |
1592 | columns = json_array_create_empty(); | |
1593 | json_object_put(op, "columns", columns); | |
1594 | row_json = json_object_create(); | |
1595 | json_object_put(op, "rows", json_array_create_1(row_json)); | |
1596 | ||
1597 | BITMAP_FOR_EACH_1 (idx, n_columns, row->prereqs) { | |
1598 | const struct ovsdb_idl_column *column = &class->columns[idx]; | |
1599 | json_array_add(columns, json_string_create(column->name)); | |
1600 | json_object_put(row_json, column->name, | |
1601 | ovsdb_datum_to_json(&row->old[idx], | |
1602 | &column->type)); | |
1603 | } | |
1604 | } | |
475281c0 BP |
1605 | } |
1606 | ||
1607 | /* Add updates. */ | |
1608 | any_updates = false; | |
4e8e4213 | 1609 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
475281c0 | 1610 | const struct ovsdb_idl_table_class *class = row->table->class; |
475281c0 | 1611 | |
d95d1510 | 1612 | if (!row->new) { |
dcd1dbc5 BP |
1613 | if (class->is_root) { |
1614 | struct json *op = json_object_create(); | |
1615 | json_object_put_string(op, "op", "delete"); | |
1616 | json_object_put_string(op, "table", class->name); | |
1617 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
1618 | json_array_add(operations, op); | |
1619 | any_updates = true; | |
1620 | } else { | |
1621 | /* Let ovsdb-server decide whether to really delete it. */ | |
1622 | } | |
d95d1510 | 1623 | } else if (row->old != row->new) { |
f0f54cb4 BP |
1624 | struct json *row_json; |
1625 | struct json *op; | |
1626 | size_t idx; | |
1627 | ||
1628 | op = json_object_create(); | |
1629 | json_object_put_string(op, "op", row->old ? "update" : "insert"); | |
1630 | json_object_put_string(op, "table", class->name); | |
1631 | if (row->old) { | |
1632 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
1633 | } else { | |
69490970 BP |
1634 | struct ovsdb_idl_txn_insert *insert; |
1635 | ||
c15f1d11 BP |
1636 | any_updates = true; |
1637 | ||
f0f54cb4 BP |
1638 | json_object_put(op, "uuid-name", |
1639 | json_string_create_nocopy( | |
1640 | uuid_name_from_uuid(&row->uuid))); | |
69490970 BP |
1641 | |
1642 | insert = xmalloc(sizeof *insert); | |
1643 | insert->dummy = row->uuid; | |
9cb53f26 | 1644 | insert->op_index = operations->u.array.n - 1; |
69490970 BP |
1645 | uuid_zero(&insert->real); |
1646 | hmap_insert(&txn->inserted_rows, &insert->hmap_node, | |
1647 | uuid_hash(&insert->dummy)); | |
f0f54cb4 BP |
1648 | } |
1649 | row_json = json_object_create(); | |
1650 | json_object_put(op, "row", row_json); | |
1651 | ||
35c2ce98 JG |
1652 | if (row->written) { |
1653 | BITMAP_FOR_EACH_1 (idx, class->n_columns, row->written) { | |
1654 | const struct ovsdb_idl_column *column = | |
1655 | &class->columns[idx]; | |
1656 | ||
1657 | if (row->old | |
1cc618c3 | 1658 | || !ovsdb_datum_is_default(&row->new[idx], |
35c2ce98 JG |
1659 | &column->type)) { |
1660 | json_object_put(row_json, column->name, | |
1661 | substitute_uuids( | |
1662 | ovsdb_datum_to_json(&row->new[idx], | |
1663 | &column->type), | |
1664 | txn)); | |
c15f1d11 BP |
1665 | |
1666 | /* If anything really changed, consider it an update. | |
1667 | * We can't suppress not-really-changed values earlier | |
1668 | * or transactions would become nonatomic (see the big | |
1669 | * comment inside ovsdb_idl_txn_write()). */ | |
1670 | if (!any_updates && row->old && | |
1671 | !ovsdb_datum_equals(&row->old[idx], &row->new[idx], | |
1672 | &column->type)) { | |
1673 | any_updates = true; | |
1674 | } | |
35c2ce98 | 1675 | } |
475281c0 | 1676 | } |
f0f54cb4 BP |
1677 | } |
1678 | ||
1679 | if (!row->old || !shash_is_empty(json_object(row_json))) { | |
1680 | json_array_add(operations, op); | |
f0f54cb4 BP |
1681 | } else { |
1682 | json_destroy(op); | |
475281c0 BP |
1683 | } |
1684 | } | |
1685 | } | |
1686 | ||
b54e22e9 BP |
1687 | /* Add increment. */ |
1688 | if (txn->inc_table && any_updates) { | |
1689 | struct json *op; | |
1690 | ||
9cb53f26 | 1691 | txn->inc_index = operations->u.array.n - 1; |
b54e22e9 BP |
1692 | |
1693 | op = json_object_create(); | |
1694 | json_object_put_string(op, "op", "mutate"); | |
1695 | json_object_put_string(op, "table", txn->inc_table); | |
1696 | json_object_put(op, "where", | |
94fbe1aa BP |
1697 | substitute_uuids(where_uuid_equals(&txn->inc_row), |
1698 | txn)); | |
b54e22e9 BP |
1699 | json_object_put(op, "mutations", |
1700 | json_array_create_1( | |
1701 | json_array_create_3( | |
1702 | json_string_create(txn->inc_column), | |
1703 | json_string_create("+="), | |
1704 | json_integer_create(1)))); | |
1705 | json_array_add(operations, op); | |
1706 | ||
1707 | op = json_object_create(); | |
1708 | json_object_put_string(op, "op", "select"); | |
1709 | json_object_put_string(op, "table", txn->inc_table); | |
1710 | json_object_put(op, "where", | |
94fbe1aa BP |
1711 | substitute_uuids(where_uuid_equals(&txn->inc_row), |
1712 | txn)); | |
b54e22e9 BP |
1713 | json_object_put(op, "columns", |
1714 | json_array_create_1(json_string_create( | |
1715 | txn->inc_column))); | |
1716 | json_array_add(operations, op); | |
1717 | } | |
1718 | ||
d171b584 BP |
1719 | if (txn->comment.length) { |
1720 | struct json *op = json_object_create(); | |
1721 | json_object_put_string(op, "op", "comment"); | |
1722 | json_object_put_string(op, "comment", ds_cstr(&txn->comment)); | |
1723 | json_array_add(operations, op); | |
1724 | } | |
1725 | ||
577aebdf BP |
1726 | if (txn->dry_run) { |
1727 | struct json *op = json_object_create(); | |
1728 | json_object_put_string(op, "op", "abort"); | |
1729 | json_array_add(operations, op); | |
1730 | } | |
1731 | ||
72c6edc5 | 1732 | if (!any_updates) { |
b54e22e9 | 1733 | txn->status = TXN_UNCHANGED; |
2f3ca7ea | 1734 | json_destroy(operations); |
72c6edc5 BP |
1735 | } else if (!jsonrpc_session_send( |
1736 | txn->idl->session, | |
1737 | jsonrpc_create_request( | |
1738 | "transact", operations, &txn->request_id))) { | |
1739 | hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node, | |
1740 | json_hash(txn->request_id, 0)); | |
2096903b | 1741 | txn->status = TXN_INCOMPLETE; |
72c6edc5 | 1742 | } else { |
854a94d9 | 1743 | txn->status = TXN_TRY_AGAIN; |
72c6edc5 | 1744 | } |
475281c0 | 1745 | |
60032110 | 1746 | disassemble_out: |
475281c0 | 1747 | ovsdb_idl_txn_disassemble(txn); |
60032110 RW |
1748 | coverage_out: |
1749 | switch (txn->status) { | |
1750 | case TXN_UNCOMMITTED: COVERAGE_INC(txn_uncommitted); break; | |
1751 | case TXN_UNCHANGED: COVERAGE_INC(txn_unchanged); break; | |
1752 | case TXN_INCOMPLETE: COVERAGE_INC(txn_incomplete); break; | |
1753 | case TXN_ABORTED: COVERAGE_INC(txn_aborted); break; | |
1754 | case TXN_SUCCESS: COVERAGE_INC(txn_success); break; | |
1755 | case TXN_TRY_AGAIN: COVERAGE_INC(txn_try_again); break; | |
1756 | case TXN_NOT_LOCKED: COVERAGE_INC(txn_not_locked); break; | |
1757 | case TXN_ERROR: COVERAGE_INC(txn_error); break; | |
1758 | } | |
1759 | ||
475281c0 BP |
1760 | return txn->status; |
1761 | } | |
1762 | ||
af96ccd2 BP |
1763 | /* Attempts to commit 'txn', blocking until the commit either succeeds or |
1764 | * fails. Returns the final commit status, which may be any TXN_* value other | |
2f926787 BP |
1765 | * than TXN_INCOMPLETE. |
1766 | * | |
1767 | * This function calls ovsdb_idl_run() on 'txn''s IDL, so it may cause the | |
1768 | * return value of ovsdb_idl_get_seqno() to change. */ | |
af96ccd2 BP |
1769 | enum ovsdb_idl_txn_status |
1770 | ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn) | |
1771 | { | |
1772 | enum ovsdb_idl_txn_status status; | |
1773 | ||
b302749b | 1774 | fatal_signal_run(); |
af96ccd2 BP |
1775 | while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { |
1776 | ovsdb_idl_run(txn->idl); | |
1777 | ovsdb_idl_wait(txn->idl); | |
1778 | ovsdb_idl_txn_wait(txn); | |
1779 | poll_block(); | |
1780 | } | |
1781 | return status; | |
1782 | } | |
1783 | ||
2f926787 BP |
1784 | /* Returns the final (incremented) value of the column in 'txn' that was set to |
1785 | * be incremented by ovsdb_idl_txn_increment(). 'txn' must have committed | |
1786 | * successfully. */ | |
b54e22e9 BP |
1787 | int64_t |
1788 | ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn) | |
1789 | { | |
cb22974d | 1790 | ovs_assert(txn->status == TXN_SUCCESS); |
b54e22e9 BP |
1791 | return txn->inc_new_value; |
1792 | } | |
1793 | ||
2f926787 BP |
1794 | /* Aborts 'txn' without sending it to the database server. This is effective |
1795 | * only if ovsdb_idl_txn_commit() has not yet been called for 'txn'. | |
1796 | * Otherwise, it has no effect. | |
1797 | * | |
1798 | * Aborting a transaction doesn't free its memory. Use | |
1799 | * ovsdb_idl_txn_destroy() to do that. */ | |
475281c0 BP |
1800 | void |
1801 | ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn) | |
1802 | { | |
1803 | ovsdb_idl_txn_disassemble(txn); | |
2096903b | 1804 | if (txn->status == TXN_UNCOMMITTED || txn->status == TXN_INCOMPLETE) { |
475281c0 BP |
1805 | txn->status = TXN_ABORTED; |
1806 | } | |
1807 | } | |
1808 | ||
2f926787 BP |
1809 | /* Returns a string that reports the error status for 'txn'. The caller must |
1810 | * not modify or free the returned string. A call to ovsdb_idl_txn_destroy() | |
1811 | * for 'txn' may free the returned string. | |
1812 | * | |
1813 | * The return value is ordinarily one of the strings that | |
1814 | * ovsdb_idl_txn_status_to_string() would return, but if the transaction failed | |
1815 | * due to an error reported by the database server, the return value is that | |
1816 | * error. */ | |
91e310a5 BP |
1817 | const char * |
1818 | ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn) | |
1819 | { | |
1820 | if (txn->status != TXN_ERROR) { | |
1821 | return ovsdb_idl_txn_status_to_string(txn->status); | |
1822 | } else if (txn->error) { | |
1823 | return txn->error; | |
1824 | } else { | |
1825 | return "no error details available"; | |
1826 | } | |
1827 | } | |
1828 | ||
1829 | static void | |
1830 | ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn, | |
1831 | const struct json *json) | |
1832 | { | |
1833 | if (txn->error == NULL) { | |
1834 | txn->error = json_to_string(json, JSSF_SORT); | |
1835 | } | |
1836 | } | |
1837 | ||
69490970 BP |
1838 | /* For transaction 'txn' that completed successfully, finds and returns the |
1839 | * permanent UUID that the database assigned to a newly inserted row, given the | |
1840 | * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row. | |
1841 | * | |
1842 | * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or | |
1843 | * if it was assigned by that function and then deleted by | |
1844 | * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted | |
1845 | * and then deleted within a single transaction are never sent to the database | |
1846 | * server, so it never assigns them a permanent UUID.) */ | |
1847 | const struct uuid * | |
1848 | ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn *txn, | |
1849 | const struct uuid *uuid) | |
1850 | { | |
1851 | const struct ovsdb_idl_txn_insert *insert; | |
1852 | ||
cb22974d | 1853 | ovs_assert(txn->status == TXN_SUCCESS || txn->status == TXN_UNCHANGED); |
4e8e4213 | 1854 | HMAP_FOR_EACH_IN_BUCKET (insert, hmap_node, |
69490970 BP |
1855 | uuid_hash(uuid), &txn->inserted_rows) { |
1856 | if (uuid_equals(uuid, &insert->dummy)) { | |
1857 | return &insert->real; | |
1858 | } | |
1859 | } | |
1860 | return NULL; | |
1861 | } | |
1862 | ||
475281c0 BP |
1863 | static void |
1864 | ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, | |
1865 | enum ovsdb_idl_txn_status status) | |
1866 | { | |
1867 | txn->status = status; | |
1868 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); | |
1869 | } | |
1870 | ||
b827c67b BP |
1871 | /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_' |
1872 | * itself and the structs derived from it (e.g. the "struct ovsrec_*", for | |
1873 | * ovs-vswitchd). | |
1874 | * | |
1875 | * 'datum' must have the correct type for its column. The IDL does not check | |
1876 | * that it meets schema constraints, but ovsdb-server will do so at commit time | |
1877 | * so it had better be correct. | |
1878 | * | |
1879 | * A transaction must be in progress. Replication of 'column' must not have | |
1880 | * been disabled (by calling ovsdb_idl_omit()). | |
1881 | * | |
1882 | * Usually this function is used indirectly through one of the "set" functions | |
27a32516 BP |
1883 | * generated by ovsdb-idlc. |
1884 | * | |
1885 | * Takes ownership of what 'datum' points to (and in some cases destroys that | |
1886 | * data before returning) but makes a copy of 'datum' itself. (Commonly | |
1887 | * 'datum' is on the caller's stack.) */ | |
fe19569a BP |
1888 | static void |
1889 | ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_, | |
1890 | const struct ovsdb_idl_column *column, | |
1891 | struct ovsdb_datum *datum, bool owns_datum) | |
475281c0 | 1892 | { |
ebc56baa | 1893 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
9b446bfa BP |
1894 | const struct ovsdb_idl_table_class *class; |
1895 | size_t column_idx; | |
8cdec725 | 1896 | bool write_only; |
9b446bfa BP |
1897 | |
1898 | if (ovsdb_idl_row_is_synthetic(row)) { | |
fe19569a | 1899 | goto discard_datum; |
9b446bfa BP |
1900 | } |
1901 | ||
1902 | class = row->table->class; | |
1903 | column_idx = column - class->columns; | |
8cdec725 | 1904 | write_only = row->table->modes[column_idx] == OVSDB_IDL_MONITOR; |
475281c0 | 1905 | |
cb22974d BP |
1906 | ovs_assert(row->new != NULL); |
1907 | ovs_assert(column_idx < class->n_columns); | |
1908 | ovs_assert(row->old == NULL || | |
1909 | row->table->modes[column_idx] & OVSDB_IDL_MONITOR); | |
c547535a | 1910 | |
8cdec725 EJ |
1911 | if (row->table->idl->verify_write_only && !write_only) { |
1912 | VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when" | |
1913 | " explicitly configured not to.", class->name, column->name); | |
fe19569a | 1914 | goto discard_datum; |
8cdec725 EJ |
1915 | } |
1916 | ||
1cc618c3 BP |
1917 | /* If this is a write-only column and the datum being written is the same |
1918 | * as the one already there, just skip the update entirely. This is worth | |
1919 | * optimizing because we have a lot of columns that get periodically | |
1920 | * refreshed into the database but don't actually change that often. | |
1921 | * | |
1922 | * We don't do this for read/write columns because that would break | |
1923 | * atomicity of transactions--some other client might have written a | |
c15f1d11 BP |
1924 | * different value in that column since we read it. (But if a whole |
1925 | * transaction only does writes of existing values, without making any real | |
1926 | * changes, we will drop the whole transaction later in | |
1927 | * ovsdb_idl_txn_commit().) */ | |
8cdec725 EJ |
1928 | if (write_only && ovsdb_datum_equals(ovsdb_idl_read(row, column), |
1929 | datum, &column->type)) { | |
fe19569a | 1930 | goto discard_datum; |
1cc618c3 BP |
1931 | } |
1932 | ||
475281c0 BP |
1933 | if (hmap_node_is_null(&row->txn_node)) { |
1934 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
1935 | uuid_hash(&row->uuid)); | |
1936 | } | |
1937 | if (row->old == row->new) { | |
1938 | row->new = xmalloc(class->n_columns * sizeof *row->new); | |
1939 | } | |
1940 | if (!row->written) { | |
1941 | row->written = bitmap_allocate(class->n_columns); | |
1942 | } | |
1943 | if (bitmap_is_set(row->written, column_idx)) { | |
1944 | ovsdb_datum_destroy(&row->new[column_idx], &column->type); | |
1945 | } else { | |
1946 | bitmap_set1(row->written, column_idx); | |
1947 | } | |
fe19569a BP |
1948 | if (owns_datum) { |
1949 | row->new[column_idx] = *datum; | |
1950 | } else { | |
1951 | ovsdb_datum_clone(&row->new[column_idx], datum, &column->type); | |
1952 | } | |
979821c0 BP |
1953 | (column->unparse)(row); |
1954 | (column->parse)(row, &row->new[column_idx]); | |
fe19569a BP |
1955 | return; |
1956 | ||
1957 | discard_datum: | |
1958 | if (owns_datum) { | |
1959 | ovsdb_datum_destroy(datum, &column->type); | |
1960 | } | |
1961 | } | |
1962 | ||
1963 | void | |
1964 | ovsdb_idl_txn_write(const struct ovsdb_idl_row *row, | |
1965 | const struct ovsdb_idl_column *column, | |
1966 | struct ovsdb_datum *datum) | |
1967 | { | |
1968 | ovsdb_idl_txn_write__(row, column, datum, true); | |
1969 | } | |
1970 | ||
1971 | void | |
1972 | ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *row, | |
1973 | const struct ovsdb_idl_column *column, | |
1974 | const struct ovsdb_datum *datum) | |
1975 | { | |
1976 | ovsdb_idl_txn_write__(row, column, | |
1977 | CONST_CAST(struct ovsdb_datum *, datum), false); | |
475281c0 BP |
1978 | } |
1979 | ||
b827c67b BP |
1980 | /* Causes the original contents of 'column' in 'row_' to be verified as a |
1981 | * prerequisite to completing the transaction. That is, if 'column' in 'row_' | |
1982 | * changed (or if 'row_' was deleted) between the time that the IDL originally | |
1983 | * read its contents and the time that the transaction commits, then the | |
4fdfe5cc BP |
1984 | * transaction aborts and ovsdb_idl_txn_commit() returns TXN_AGAIN_WAIT or |
1985 | * TXN_AGAIN_NOW (depending on whether the database change has already been | |
1986 | * received). | |
b827c67b BP |
1987 | * |
1988 | * The intention is that, to ensure that no transaction commits based on dirty | |
1989 | * reads, an application should call ovsdb_idl_txn_verify() on each data item | |
1990 | * read as part of a read-modify-write operation. | |
1991 | * | |
1992 | * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current | |
1993 | * value of 'column' is already known: | |
1994 | * | |
1995 | * - If 'row_' is a row created by the current transaction (returned by | |
1996 | * ovsdb_idl_txn_insert()). | |
1997 | * | |
1998 | * - If 'column' has already been modified (with ovsdb_idl_txn_write()) | |
1999 | * within the current transaction. | |
2000 | * | |
2001 | * Because of the latter property, always call ovsdb_idl_txn_verify() *before* | |
2002 | * ovsdb_idl_txn_write() for a given read-modify-write. | |
2003 | * | |
2004 | * A transaction must be in progress. | |
2005 | * | |
2006 | * Usually this function is used indirectly through one of the "verify" | |
2007 | * functions generated by ovsdb-idlc. */ | |
475281c0 BP |
2008 | void |
2009 | ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_, | |
2010 | const struct ovsdb_idl_column *column) | |
2011 | { | |
ebc56baa | 2012 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
9b446bfa BP |
2013 | const struct ovsdb_idl_table_class *class; |
2014 | size_t column_idx; | |
2015 | ||
2016 | if (ovsdb_idl_row_is_synthetic(row)) { | |
2017 | return; | |
2018 | } | |
2019 | ||
2020 | class = row->table->class; | |
2021 | column_idx = column - class->columns; | |
475281c0 | 2022 | |
cb22974d BP |
2023 | ovs_assert(row->new != NULL); |
2024 | ovs_assert(row->old == NULL || | |
2025 | row->table->modes[column_idx] & OVSDB_IDL_MONITOR); | |
475281c0 BP |
2026 | if (!row->old |
2027 | || (row->written && bitmap_is_set(row->written, column_idx))) { | |
2028 | return; | |
2029 | } | |
2030 | ||
2031 | if (hmap_node_is_null(&row->txn_node)) { | |
2032 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
2033 | uuid_hash(&row->uuid)); | |
2034 | } | |
2035 | if (!row->prereqs) { | |
2036 | row->prereqs = bitmap_allocate(class->n_columns); | |
2037 | } | |
2038 | bitmap_set1(row->prereqs, column_idx); | |
2039 | } | |
2040 | ||
b827c67b BP |
2041 | /* Deletes 'row_' from its table. May free 'row_', so it must not be |
2042 | * accessed afterward. | |
2043 | * | |
2044 | * A transaction must be in progress. | |
2045 | * | |
2046 | * Usually this function is used indirectly through one of the "delete" | |
2047 | * functions generated by ovsdb-idlc. */ | |
475281c0 | 2048 | void |
9e336f49 | 2049 | ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_) |
475281c0 | 2050 | { |
ebc56baa | 2051 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
9e336f49 | 2052 | |
9b446bfa BP |
2053 | if (ovsdb_idl_row_is_synthetic(row)) { |
2054 | return; | |
2055 | } | |
2056 | ||
cb22974d | 2057 | ovs_assert(row->new != NULL); |
475281c0 | 2058 | if (!row->old) { |
0196cc15 | 2059 | ovsdb_idl_row_unparse(row); |
475281c0 | 2060 | ovsdb_idl_row_clear_new(row); |
cb22974d | 2061 | ovs_assert(!row->prereqs); |
979821c0 | 2062 | hmap_remove(&row->table->rows, &row->hmap_node); |
475281c0 BP |
2063 | hmap_remove(&row->table->idl->txn->txn_rows, &row->txn_node); |
2064 | free(row); | |
c7f7adb7 | 2065 | return; |
475281c0 BP |
2066 | } |
2067 | if (hmap_node_is_null(&row->txn_node)) { | |
2068 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
2069 | uuid_hash(&row->uuid)); | |
2070 | } | |
1ebeed63 BP |
2071 | ovsdb_idl_row_clear_new(row); |
2072 | row->new = NULL; | |
475281c0 BP |
2073 | } |
2074 | ||
b827c67b BP |
2075 | /* Inserts and returns a new row in the table with the specified 'class' in the |
2076 | * database with open transaction 'txn'. | |
2077 | * | |
2078 | * The new row is assigned a provisional UUID. If 'uuid' is null then one is | |
2079 | * randomly generated; otherwise 'uuid' should specify a randomly generated | |
2080 | * UUID not otherwise in use. ovsdb-server will assign a different UUID when | |
2081 | * 'txn' is committed, but the IDL will replace any uses of the provisional | |
2082 | * UUID in the data to be to be committed by the UUID assigned by | |
2083 | * ovsdb-server. | |
2084 | * | |
2085 | * Usually this function is used indirectly through one of the "insert" | |
2086 | * functions generated by ovsdb-idlc. */ | |
9e336f49 | 2087 | const struct ovsdb_idl_row * |
475281c0 | 2088 | ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn, |
ce5a3e38 BP |
2089 | const struct ovsdb_idl_table_class *class, |
2090 | const struct uuid *uuid) | |
475281c0 BP |
2091 | { |
2092 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class); | |
ce5a3e38 BP |
2093 | |
2094 | if (uuid) { | |
cb22974d | 2095 | ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid)); |
ce5a3e38 BP |
2096 | row->uuid = *uuid; |
2097 | } else { | |
2098 | uuid_generate(&row->uuid); | |
2099 | } | |
2100 | ||
475281c0 BP |
2101 | row->table = ovsdb_idl_table_from_class(txn->idl, class); |
2102 | row->new = xmalloc(class->n_columns * sizeof *row->new); | |
979821c0 | 2103 | hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid)); |
475281c0 BP |
2104 | hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); |
2105 | return row; | |
2106 | } | |
2107 | ||
2108 | static void | |
2109 | ovsdb_idl_txn_abort_all(struct ovsdb_idl *idl) | |
2110 | { | |
2111 | struct ovsdb_idl_txn *txn; | |
2112 | ||
4e8e4213 | 2113 | HMAP_FOR_EACH (txn, hmap_node, &idl->outstanding_txns) { |
854a94d9 | 2114 | ovsdb_idl_txn_complete(txn, TXN_TRY_AGAIN); |
475281c0 BP |
2115 | } |
2116 | } | |
2117 | ||
2118 | static struct ovsdb_idl_txn * | |
2119 | ovsdb_idl_txn_find(struct ovsdb_idl *idl, const struct json *id) | |
2120 | { | |
2121 | struct ovsdb_idl_txn *txn; | |
2122 | ||
4e8e4213 | 2123 | HMAP_FOR_EACH_WITH_HASH (txn, hmap_node, |
475281c0 BP |
2124 | json_hash(id, 0), &idl->outstanding_txns) { |
2125 | if (json_equal(id, txn->request_id)) { | |
2126 | return txn; | |
2127 | } | |
2128 | } | |
2129 | return NULL; | |
2130 | } | |
2131 | ||
b54e22e9 BP |
2132 | static bool |
2133 | check_json_type(const struct json *json, enum json_type type, const char *name) | |
2134 | { | |
2135 | if (!json) { | |
2136 | VLOG_WARN_RL(&syntax_rl, "%s is missing", name); | |
2137 | return false; | |
2138 | } else if (json->type != type) { | |
2139 | VLOG_WARN_RL(&syntax_rl, "%s is %s instead of %s", | |
2140 | name, json_type_to_string(json->type), | |
2141 | json_type_to_string(type)); | |
2142 | return false; | |
2143 | } else { | |
2144 | return true; | |
2145 | } | |
2146 | } | |
2147 | ||
2148 | static bool | |
2149 | ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn, | |
2150 | const struct json_array *results) | |
2151 | { | |
2152 | struct json *count, *rows, *row, *column; | |
2153 | struct shash *mutate, *select; | |
2154 | ||
2155 | if (txn->inc_index + 2 > results->n) { | |
2156 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " | |
34582733 | 2157 | "for increment (has %"PRIuSIZE", needs %u)", |
b54e22e9 BP |
2158 | results->n, txn->inc_index + 2); |
2159 | return false; | |
2160 | } | |
2161 | ||
69490970 | 2162 | /* We know that this is a JSON object because the loop in |
b54e22e9 BP |
2163 | * ovsdb_idl_txn_process_reply() checked. */ |
2164 | mutate = json_object(results->elems[txn->inc_index]); | |
2165 | count = shash_find_data(mutate, "count"); | |
2166 | if (!check_json_type(count, JSON_INTEGER, "\"mutate\" reply \"count\"")) { | |
2167 | return false; | |
2168 | } | |
2169 | if (count->u.integer != 1) { | |
2170 | VLOG_WARN_RL(&syntax_rl, | |
6fce4487 | 2171 | "\"mutate\" reply \"count\" is %lld instead of 1", |
b54e22e9 BP |
2172 | count->u.integer); |
2173 | return false; | |
2174 | } | |
2175 | ||
2176 | select = json_object(results->elems[txn->inc_index + 1]); | |
2177 | rows = shash_find_data(select, "rows"); | |
2178 | if (!check_json_type(rows, JSON_ARRAY, "\"select\" reply \"rows\"")) { | |
2179 | return false; | |
2180 | } | |
2181 | if (rows->u.array.n != 1) { | |
34582733 | 2182 | VLOG_WARN_RL(&syntax_rl, "\"select\" reply \"rows\" has %"PRIuSIZE" elements " |
b54e22e9 BP |
2183 | "instead of 1", |
2184 | rows->u.array.n); | |
2185 | return false; | |
2186 | } | |
2187 | row = rows->u.array.elems[0]; | |
2188 | if (!check_json_type(row, JSON_OBJECT, "\"select\" reply row")) { | |
2189 | return false; | |
2190 | } | |
2191 | column = shash_find_data(json_object(row), txn->inc_column); | |
2192 | if (!check_json_type(column, JSON_INTEGER, | |
2193 | "\"select\" reply inc column")) { | |
2194 | return false; | |
2195 | } | |
2196 | txn->inc_new_value = column->u.integer; | |
2197 | return true; | |
2198 | } | |
2199 | ||
69490970 BP |
2200 | static bool |
2201 | ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert, | |
2202 | const struct json_array *results) | |
2203 | { | |
bd76d25d | 2204 | static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT; |
69490970 BP |
2205 | struct ovsdb_error *error; |
2206 | struct json *json_uuid; | |
2207 | union ovsdb_atom uuid; | |
2208 | struct shash *reply; | |
2209 | ||
2210 | if (insert->op_index >= results->n) { | |
2211 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " | |
34582733 | 2212 | "for insert (has %"PRIuSIZE", needs %u)", |
69490970 BP |
2213 | results->n, insert->op_index); |
2214 | return false; | |
2215 | } | |
2216 | ||
2217 | /* We know that this is a JSON object because the loop in | |
2218 | * ovsdb_idl_txn_process_reply() checked. */ | |
2219 | reply = json_object(results->elems[insert->op_index]); | |
2220 | json_uuid = shash_find_data(reply, "uuid"); | |
2221 | if (!check_json_type(json_uuid, JSON_ARRAY, "\"insert\" reply \"uuid\"")) { | |
2222 | return false; | |
2223 | } | |
2224 | ||
bd76d25d | 2225 | error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL); |
69490970 BP |
2226 | if (error) { |
2227 | char *s = ovsdb_error_to_string(error); | |
2228 | VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON " | |
2229 | "UUID: %s", s); | |
2230 | free(s); | |
9582c4f5 | 2231 | ovsdb_error_destroy(error); |
69490970 BP |
2232 | return false; |
2233 | } | |
2234 | ||
2235 | insert->real = uuid.uuid; | |
2236 | ||
2237 | return true; | |
2238 | } | |
b54e22e9 | 2239 | |
475281c0 BP |
2240 | static bool |
2241 | ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl, | |
2242 | const struct jsonrpc_msg *msg) | |
2243 | { | |
2244 | struct ovsdb_idl_txn *txn; | |
2245 | enum ovsdb_idl_txn_status status; | |
2246 | ||
2247 | txn = ovsdb_idl_txn_find(idl, msg->id); | |
2248 | if (!txn) { | |
2249 | return false; | |
2250 | } | |
2251 | ||
2252 | if (msg->type == JSONRPC_ERROR) { | |
2253 | status = TXN_ERROR; | |
2254 | } else if (msg->result->type != JSON_ARRAY) { | |
2255 | VLOG_WARN_RL(&syntax_rl, "reply to \"transact\" is not JSON array"); | |
2256 | status = TXN_ERROR; | |
2257 | } else { | |
69490970 | 2258 | struct json_array *ops = &msg->result->u.array; |
475281c0 BP |
2259 | int hard_errors = 0; |
2260 | int soft_errors = 0; | |
06b6d651 | 2261 | int lock_errors = 0; |
475281c0 BP |
2262 | size_t i; |
2263 | ||
69490970 BP |
2264 | for (i = 0; i < ops->n; i++) { |
2265 | struct json *op = ops->elems[i]; | |
475281c0 | 2266 | |
69490970 | 2267 | if (op->type == JSON_NULL) { |
475281c0 BP |
2268 | /* This isn't an error in itself but indicates that some prior |
2269 | * operation failed, so make sure that we know about it. */ | |
2270 | soft_errors++; | |
69490970 | 2271 | } else if (op->type == JSON_OBJECT) { |
475281c0 BP |
2272 | struct json *error; |
2273 | ||
69490970 | 2274 | error = shash_find_data(json_object(op), "error"); |
475281c0 BP |
2275 | if (error) { |
2276 | if (error->type == JSON_STRING) { | |
2277 | if (!strcmp(error->u.string, "timed out")) { | |
2278 | soft_errors++; | |
06b6d651 BP |
2279 | } else if (!strcmp(error->u.string, "not owner")) { |
2280 | lock_errors++; | |
577aebdf | 2281 | } else if (strcmp(error->u.string, "aborted")) { |
475281c0 | 2282 | hard_errors++; |
91e310a5 | 2283 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
2284 | } |
2285 | } else { | |
2286 | hard_errors++; | |
91e310a5 | 2287 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
2288 | VLOG_WARN_RL(&syntax_rl, |
2289 | "\"error\" in reply is not JSON string"); | |
2290 | } | |
2291 | } | |
2292 | } else { | |
2293 | hard_errors++; | |
91e310a5 | 2294 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
2295 | VLOG_WARN_RL(&syntax_rl, |
2296 | "operation reply is not JSON null or object"); | |
2297 | } | |
2298 | } | |
2299 | ||
06b6d651 | 2300 | if (!soft_errors && !hard_errors && !lock_errors) { |
69490970 BP |
2301 | struct ovsdb_idl_txn_insert *insert; |
2302 | ||
2303 | if (txn->inc_table && !ovsdb_idl_txn_process_inc_reply(txn, ops)) { | |
2304 | hard_errors++; | |
2305 | } | |
2306 | ||
4e8e4213 | 2307 | HMAP_FOR_EACH (insert, hmap_node, &txn->inserted_rows) { |
69490970 BP |
2308 | if (!ovsdb_idl_txn_process_insert_reply(insert, ops)) { |
2309 | hard_errors++; | |
2310 | } | |
2311 | } | |
b54e22e9 BP |
2312 | } |
2313 | ||
475281c0 | 2314 | status = (hard_errors ? TXN_ERROR |
06b6d651 | 2315 | : lock_errors ? TXN_NOT_LOCKED |
854a94d9 | 2316 | : soft_errors ? TXN_TRY_AGAIN |
475281c0 BP |
2317 | : TXN_SUCCESS); |
2318 | } | |
2319 | ||
2320 | ovsdb_idl_txn_complete(txn, status); | |
2321 | return true; | |
2322 | } | |
76c91af9 | 2323 | |
2f926787 BP |
2324 | /* Returns the transaction currently active for 'row''s IDL. A transaction |
2325 | * must currently be active. */ | |
76c91af9 BP |
2326 | struct ovsdb_idl_txn * |
2327 | ovsdb_idl_txn_get(const struct ovsdb_idl_row *row) | |
2328 | { | |
2329 | struct ovsdb_idl_txn *txn = row->table->idl->txn; | |
cb22974d | 2330 | ovs_assert(txn != NULL); |
76c91af9 BP |
2331 | return txn; |
2332 | } | |
1e86ae6f | 2333 | |
2f926787 | 2334 | /* Returns the IDL on which 'txn' acts. */ |
1e86ae6f BP |
2335 | struct ovsdb_idl * |
2336 | ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn) | |
2337 | { | |
2338 | return txn->idl; | |
2339 | } | |
06b6d651 BP |
2340 | \f |
2341 | /* If 'lock_name' is nonnull, configures 'idl' to obtain the named lock from | |
2342 | * the database server and to avoid modifying the database when the lock cannot | |
2343 | * be acquired (that is, when another client has the same lock). | |
2344 | * | |
2345 | * If 'lock_name' is NULL, drops the locking requirement and releases the | |
2346 | * lock. */ | |
2347 | void | |
2348 | ovsdb_idl_set_lock(struct ovsdb_idl *idl, const char *lock_name) | |
2349 | { | |
cb22974d BP |
2350 | ovs_assert(!idl->txn); |
2351 | ovs_assert(hmap_is_empty(&idl->outstanding_txns)); | |
06b6d651 BP |
2352 | |
2353 | if (idl->lock_name && (!lock_name || strcmp(lock_name, idl->lock_name))) { | |
2354 | /* Release previous lock. */ | |
2355 | ovsdb_idl_send_unlock_request(idl); | |
2356 | free(idl->lock_name); | |
2357 | idl->lock_name = NULL; | |
2358 | idl->is_lock_contended = false; | |
2359 | } | |
2360 | ||
2361 | if (lock_name && !idl->lock_name) { | |
2362 | /* Acquire new lock. */ | |
2363 | idl->lock_name = xstrdup(lock_name); | |
2364 | ovsdb_idl_send_lock_request(idl); | |
2365 | } | |
2366 | } | |
2367 | ||
2368 | /* Returns true if 'idl' is configured to obtain a lock and owns that lock. | |
2369 | * | |
2370 | * Locking and unlocking happens asynchronously from the database client's | |
2371 | * point of view, so the information is only useful for optimization (e.g. if | |
2372 | * the client doesn't have the lock then there's no point in trying to write to | |
2373 | * the database). */ | |
2374 | bool | |
2375 | ovsdb_idl_has_lock(const struct ovsdb_idl *idl) | |
2376 | { | |
2377 | return idl->has_lock; | |
2378 | } | |
2379 | ||
2380 | /* Returns true if 'idl' is configured to obtain a lock but the database server | |
2381 | * has indicated that some other client already owns the requested lock. */ | |
2382 | bool | |
2383 | ovsdb_idl_is_lock_contended(const struct ovsdb_idl *idl) | |
2384 | { | |
2385 | return idl->is_lock_contended; | |
2386 | } | |
2387 | ||
2388 | static void | |
2389 | ovsdb_idl_update_has_lock(struct ovsdb_idl *idl, bool new_has_lock) | |
2390 | { | |
2391 | if (new_has_lock && !idl->has_lock) { | |
2392 | if (!idl->monitor_request_id) { | |
2393 | idl->change_seqno++; | |
2394 | } else { | |
2395 | /* We're waiting for a monitor reply, so don't signal that the | |
2396 | * database changed. The monitor reply will increment change_seqno | |
2397 | * anyhow. */ | |
2398 | } | |
2399 | idl->is_lock_contended = false; | |
2400 | } | |
2401 | idl->has_lock = new_has_lock; | |
2402 | } | |
2403 | ||
2404 | static void | |
2405 | ovsdb_idl_send_lock_request__(struct ovsdb_idl *idl, const char *method, | |
2406 | struct json **idp) | |
2407 | { | |
2408 | ovsdb_idl_update_has_lock(idl, false); | |
2409 | ||
2410 | json_destroy(idl->lock_request_id); | |
2411 | idl->lock_request_id = NULL; | |
2412 | ||
2413 | if (jsonrpc_session_is_connected(idl->session)) { | |
2414 | struct json *params; | |
2415 | ||
2416 | params = json_array_create_1(json_string_create(idl->lock_name)); | |
2417 | jsonrpc_session_send(idl->session, | |
2418 | jsonrpc_create_request(method, params, idp)); | |
2419 | } | |
2420 | } | |
1e86ae6f | 2421 | |
06b6d651 BP |
2422 | static void |
2423 | ovsdb_idl_send_lock_request(struct ovsdb_idl *idl) | |
2424 | { | |
2425 | ovsdb_idl_send_lock_request__(idl, "lock", &idl->lock_request_id); | |
2426 | } | |
2427 | ||
2428 | static void | |
2429 | ovsdb_idl_send_unlock_request(struct ovsdb_idl *idl) | |
2430 | { | |
2431 | ovsdb_idl_send_lock_request__(idl, "unlock", NULL); | |
2432 | } | |
2433 | ||
2434 | static void | |
2435 | ovsdb_idl_parse_lock_reply(struct ovsdb_idl *idl, const struct json *result) | |
2436 | { | |
2437 | bool got_lock; | |
2438 | ||
2439 | json_destroy(idl->lock_request_id); | |
2440 | idl->lock_request_id = NULL; | |
2441 | ||
2442 | if (result->type == JSON_OBJECT) { | |
2443 | const struct json *locked; | |
2444 | ||
2445 | locked = shash_find_data(json_object(result), "locked"); | |
2446 | got_lock = locked && locked->type == JSON_TRUE; | |
2447 | } else { | |
2448 | got_lock = false; | |
2449 | } | |
2450 | ||
2451 | ovsdb_idl_update_has_lock(idl, got_lock); | |
2452 | if (!got_lock) { | |
2453 | idl->is_lock_contended = true; | |
2454 | } | |
2455 | } | |
2456 | ||
2457 | static void | |
2458 | ovsdb_idl_parse_lock_notify(struct ovsdb_idl *idl, | |
2459 | const struct json *params, | |
2460 | bool new_has_lock) | |
2461 | { | |
2462 | if (idl->lock_name | |
2463 | && params->type == JSON_ARRAY | |
2464 | && json_array(params)->n > 0 | |
2465 | && json_array(params)->elems[0]->type == JSON_STRING) { | |
2466 | const char *lock_name = json_string(json_array(params)->elems[0]); | |
2467 | ||
2468 | if (!strcmp(idl->lock_name, lock_name)) { | |
2469 | ovsdb_idl_update_has_lock(idl, new_has_lock); | |
2470 | if (!new_has_lock) { | |
2471 | idl->is_lock_contended = true; | |
2472 | } | |
2473 | } | |
2474 | } | |
2475 | } |