Branch data Line data Source code
1 : : /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
2 : : *
3 : : * Licensed under the Apache License, Version 2.0 (the "License");
4 : : * you may not use this file except in compliance with the License.
5 : : * You may obtain a copy of the License at:
6 : : *
7 : : * http://www.apache.org/licenses/LICENSE-2.0
8 : : *
9 : : * Unless required by applicable law or agreed to in writing, software
10 : : * distributed under the License is distributed on an "AS IS" BASIS,
11 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : * See the License for the specific language governing permissions and
13 : : * limitations under the License.
14 : : */
15 : :
16 : : #include <config.h>
17 : :
18 : : #include "ovsdb-idl.h"
19 : :
20 : : #include <errno.h>
21 : : #include <inttypes.h>
22 : : #include <limits.h>
23 : : #include <stdlib.h>
24 : :
25 : : #include "bitmap.h"
26 : : #include "coverage.h"
27 : : #include "openvswitch/dynamic-string.h"
28 : : #include "fatal-signal.h"
29 : : #include "openvswitch/json.h"
30 : : #include "jsonrpc.h"
31 : : #include "ovsdb/ovsdb.h"
32 : : #include "ovsdb/table.h"
33 : : #include "ovsdb-data.h"
34 : : #include "ovsdb-error.h"
35 : : #include "ovsdb-idl-provider.h"
36 : : #include "ovsdb-parser.h"
37 : : #include "poll-loop.h"
38 : : #include "openvswitch/shash.h"
39 : : #include "sset.h"
40 : : #include "util.h"
41 : : #include "openvswitch/vlog.h"
42 : :
43 : 15050 : VLOG_DEFINE_THIS_MODULE(ovsdb_idl);
44 : :
45 : 88364 : COVERAGE_DEFINE(txn_uncommitted);
46 : 194762 : COVERAGE_DEFINE(txn_unchanged);
47 : 171440 : COVERAGE_DEFINE(txn_incomplete);
48 : 88370 : COVERAGE_DEFINE(txn_aborted);
49 : 142322 : COVERAGE_DEFINE(txn_success);
50 : 88370 : COVERAGE_DEFINE(txn_try_again);
51 : 88364 : COVERAGE_DEFINE(txn_not_locked);
52 : 88364 : COVERAGE_DEFINE(txn_error);
53 : :
54 : : /* An arc from one idl_row to another. When row A contains a UUID that
55 : : * references row B, this is represented by an arc from A (the source) to B
56 : : * (the destination).
57 : : *
58 : : * Arcs from a row to itself are omitted, that is, src and dst are always
59 : : * different.
60 : : *
61 : : * Arcs are never duplicated, that is, even if there are multiple references
62 : : * from A to B, there is only a single arc from A to B.
63 : : *
64 : : * Arcs are directed: an arc from A to B is the converse of an an arc from B to
65 : : * A. Both an arc and its converse may both be present, if each row refers
66 : : * to the other circularly.
67 : : *
68 : : * The source and destination row may be in the same table or in different
69 : : * tables.
70 : : */
71 : : struct ovsdb_idl_arc {
72 : : struct ovs_list src_node; /* In src->src_arcs list. */
73 : : struct ovs_list dst_node; /* In dst->dst_arcs list. */
74 : : struct ovsdb_idl_row *src; /* Source row. */
75 : : struct ovsdb_idl_row *dst; /* Destination row. */
76 : : };
77 : :
78 : : enum ovsdb_idl_state {
79 : : IDL_S_SCHEMA_REQUESTED,
80 : : IDL_S_MONITOR_REQUESTED,
81 : : IDL_S_MONITORING,
82 : : IDL_S_MONITOR_COND_REQUESTED,
83 : : IDL_S_MONITORING_COND,
84 : : IDL_S_NO_SCHEMA
85 : : };
86 : :
87 : : struct ovsdb_idl {
88 : : const struct ovsdb_idl_class *class;
89 : : struct jsonrpc_session *session;
90 : : struct uuid uuid;
91 : : struct shash table_by_name;
92 : : struct ovsdb_idl_table *tables; /* Contains "struct ovsdb_idl_table *"s.*/
93 : : unsigned int change_seqno;
94 : : bool verify_write_only;
95 : :
96 : : /* Session state. */
97 : : unsigned int state_seqno;
98 : : enum ovsdb_idl_state state;
99 : : struct json *request_id;
100 : : struct json *schema;
101 : :
102 : : /* Database locking. */
103 : : char *lock_name; /* Name of lock we need, NULL if none. */
104 : : bool has_lock; /* Has db server told us we have the lock? */
105 : : bool is_lock_contended; /* Has db server told us we can't get lock? */
106 : : struct json *lock_request_id; /* JSON-RPC ID of in-flight lock request. */
107 : :
108 : : /* Transaction support. */
109 : : struct ovsdb_idl_txn *txn;
110 : : struct hmap outstanding_txns;
111 : : bool cond_changed;
112 : : };
113 : :
114 : : struct ovsdb_idl_txn {
115 : : struct hmap_node hmap_node;
116 : : struct json *request_id;
117 : : struct ovsdb_idl *idl;
118 : : struct hmap txn_rows;
119 : : enum ovsdb_idl_txn_status status;
120 : : char *error;
121 : : bool dry_run;
122 : : struct ds comment;
123 : :
124 : : /* Increments. */
125 : : const char *inc_table;
126 : : const char *inc_column;
127 : : struct uuid inc_row;
128 : : bool inc_force;
129 : : unsigned int inc_index;
130 : : int64_t inc_new_value;
131 : :
132 : : /* Inserted rows. */
133 : : struct hmap inserted_rows; /* Contains "struct ovsdb_idl_txn_insert"s. */
134 : : };
135 : :
136 : : struct ovsdb_idl_txn_insert {
137 : : struct hmap_node hmap_node; /* In struct ovsdb_idl_txn's inserted_rows. */
138 : : struct uuid dummy; /* Dummy UUID used locally. */
139 : : int op_index; /* Index into transaction's operation array. */
140 : : struct uuid real; /* Real UUID used by database server. */
141 : : };
142 : :
143 : : enum ovsdb_update_version {
144 : : OVSDB_UPDATE, /* RFC 7047 "update" method. */
145 : : OVSDB_UPDATE2 /* "update2" Extension to RFC 7047.
146 : : See ovsdb-server(1) for more information. */
147 : : };
148 : :
149 : : /* Name arrays indexed by 'enum ovsdb_update_version'. */
150 : : static const char *table_updates_names[] = {"table_updates", "table_updates2"};
151 : : static const char *table_update_names[] = {"table_update", "table_update2"};
152 : : static const char *row_update_names[] = {"row_update", "row_update2"};
153 : :
154 : : static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
155 : : static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5);
156 : :
157 : : static void ovsdb_idl_clear(struct ovsdb_idl *);
158 : : static void ovsdb_idl_send_schema_request(struct ovsdb_idl *);
159 : : static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *);
160 : : static void ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl *);
161 : : static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *,
162 : : enum ovsdb_update_version);
163 : : static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *,
164 : : const struct json *,
165 : : enum ovsdb_update_version);
166 : : static bool ovsdb_idl_process_update(struct ovsdb_idl_table *,
167 : : const struct uuid *,
168 : : const struct json *old,
169 : : const struct json *new);
170 : : static bool ovsdb_idl_process_update2(struct ovsdb_idl_table *,
171 : : const struct uuid *,
172 : : const char *operation,
173 : : const struct json *row);
174 : : static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *);
175 : : static void ovsdb_idl_delete_row(struct ovsdb_idl_row *);
176 : : static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *);
177 : : static bool ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row *,
178 : : const struct json *);
179 : :
180 : : static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *);
181 : : static struct ovsdb_idl_row *ovsdb_idl_row_create__(
182 : : const struct ovsdb_idl_table_class *);
183 : : static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *,
184 : : const struct uuid *);
185 : : static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *);
186 : : static void ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *);
187 : : static void ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row *);
188 : : static void ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row *);
189 : :
190 : : static void ovsdb_idl_row_parse(struct ovsdb_idl_row *);
191 : : static void ovsdb_idl_row_unparse(struct ovsdb_idl_row *);
192 : : static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *);
193 : : static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *);
194 : : static void ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *, bool destroy_dsts);
195 : :
196 : : static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *);
197 : : static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl *,
198 : : const struct jsonrpc_msg *msg);
199 : : static bool ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *,
200 : : struct json *);
201 : : static void ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row *,
202 : : const struct ovsdb_idl_column *,
203 : : struct ovsdb_datum *,
204 : : enum map_op_type);
205 : : static void ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row *,
206 : : const struct ovsdb_idl_column *,
207 : : struct ovsdb_datum *,
208 : : enum set_op_type);
209 : :
210 : : static void ovsdb_idl_send_lock_request(struct ovsdb_idl *);
211 : : static void ovsdb_idl_send_unlock_request(struct ovsdb_idl *);
212 : : static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl *,
213 : : const struct json *);
214 : : static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl *,
215 : : const struct json *params,
216 : : bool new_has_lock);
217 : : static struct ovsdb_idl_table *
218 : : ovsdb_idl_table_from_class(const struct ovsdb_idl *,
219 : : const struct ovsdb_idl_table_class *);
220 : : static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table *table);
221 : : static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl);
222 : : static void ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd,
223 : : const struct ovsdb_idl_table_class *tc);
224 : :
225 : : /* Creates and returns a connection to database 'remote', which should be in a
226 : : * form acceptable to jsonrpc_session_open(). The connection will maintain an
227 : : * in-memory replica of the remote database whose schema is described by
228 : : * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically
229 : : * by ovsdb-idlc.)
230 : : *
231 : : * Passes 'retry' to jsonrpc_session_open(). See that function for
232 : : * documentation.
233 : : *
234 : : * If 'monitor_everything_by_default' is true, then everything in the remote
235 : : * database will be replicated by default. ovsdb_idl_omit() and
236 : : * ovsdb_idl_omit_alert() may be used to selectively drop some columns from
237 : : * monitoring.
238 : : *
239 : : * If 'monitor_everything_by_default' is false, then no columns or tables will
240 : : * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table()
241 : : * must be used to choose some columns or tables to replicate.
242 : : */
243 : : struct ovsdb_idl *
244 : 7158 : ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class,
245 : : bool monitor_everything_by_default, bool retry)
246 : : {
247 : : struct ovsdb_idl *idl;
248 : : uint8_t default_mode;
249 : : size_t i;
250 : :
251 [ + + ]: 7158 : default_mode = (monitor_everything_by_default
252 : : ? OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT
253 : : : 0);
254 : :
255 : 7158 : idl = xzalloc(sizeof *idl);
256 : 7158 : idl->class = class;
257 : 7158 : idl->session = jsonrpc_session_open(remote, retry);
258 : 7158 : shash_init(&idl->table_by_name);
259 : 7158 : idl->tables = xmalloc(class->n_tables * sizeof *idl->tables);
260 [ + + ]: 116923 : for (i = 0; i < class->n_tables; i++) {
261 : 109765 : const struct ovsdb_idl_table_class *tc = &class->tables[i];
262 : 109765 : struct ovsdb_idl_table *table = &idl->tables[i];
263 : : size_t j;
264 : :
265 : 109765 : shash_add_assert(&idl->table_by_name, tc->name, table);
266 : 109765 : table->class = tc;
267 : 109765 : table->modes = xmalloc(tc->n_columns);
268 : 109765 : memset(table->modes, default_mode, tc->n_columns);
269 : 109765 : table->need_table = false;
270 : 109765 : shash_init(&table->columns);
271 [ + + ]: 1113287 : for (j = 0; j < tc->n_columns; j++) {
272 : 1003522 : const struct ovsdb_idl_column *column = &tc->columns[j];
273 : :
274 : 1003522 : shash_add_assert(&table->columns, column->name, column);
275 : : }
276 : 109765 : hmap_init(&table->rows);
277 : 109765 : ovs_list_init(&table->track_list);
278 : : table->change_seqno[OVSDB_IDL_CHANGE_INSERT]
279 : 109765 : = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]
280 : 109765 : = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0;
281 : 109765 : table->idl = idl;
282 : 109765 : ovsdb_idl_condition_init(&table->condition, tc);
283 : 109765 : table->cond_changed = false;
284 : : }
285 : :
286 : 7158 : idl->cond_changed = false;
287 : 7158 : idl->state_seqno = UINT_MAX;
288 : 7158 : idl->request_id = NULL;
289 : 7158 : idl->schema = NULL;
290 : :
291 : 7158 : hmap_init(&idl->outstanding_txns);
292 : 7158 : uuid_generate(&idl->uuid);
293 : :
294 : 7158 : return idl;
295 : : }
296 : :
297 : : /* Changes the remote and creates a new session. */
298 : : void
299 : 2 : ovsdb_idl_set_remote(struct ovsdb_idl *idl, const char *remote,
300 : : bool retry)
301 : : {
302 [ + - ]: 2 : if (idl) {
303 [ - + ]: 2 : ovs_assert(!idl->txn);
304 : 2 : jsonrpc_session_close(idl->session);
305 : 2 : idl->session = jsonrpc_session_open(remote, retry);
306 : 2 : idl->state_seqno = UINT_MAX;
307 : : }
308 : 2 : }
309 : :
310 : : /* Destroys 'idl' and all of the data structures that it manages. */
311 : : void
312 : 7159 : ovsdb_idl_destroy(struct ovsdb_idl *idl)
313 : : {
314 [ + + ]: 7159 : if (idl) {
315 : : size_t i;
316 : :
317 [ - + ]: 7156 : ovs_assert(!idl->txn);
318 : 7156 : ovsdb_idl_clear(idl);
319 : 7156 : jsonrpc_session_close(idl->session);
320 : :
321 [ + + ]: 116909 : for (i = 0; i < idl->class->n_tables; i++) {
322 : 109753 : struct ovsdb_idl_table *table = &idl->tables[i];
323 : 109753 : shash_destroy(&table->columns);
324 : 109753 : hmap_destroy(&table->rows);
325 : 109753 : free(table->modes);
326 : : }
327 : 7156 : shash_destroy(&idl->table_by_name);
328 : 7156 : free(idl->tables);
329 : 7156 : json_destroy(idl->request_id);
330 : 7156 : free(idl->lock_name);
331 : 7156 : json_destroy(idl->lock_request_id);
332 : 7156 : json_destroy(idl->schema);
333 : 7156 : hmap_destroy(&idl->outstanding_txns);
334 : 7156 : free(idl);
335 : : }
336 : 7159 : }
337 : :
338 : : static void
339 : 14308 : ovsdb_idl_clear(struct ovsdb_idl *idl)
340 : : {
341 : 14308 : bool changed = false;
342 : : size_t i;
343 : :
344 [ + + ]: 233700 : for (i = 0; i < idl->class->n_tables; i++) {
345 : 219392 : struct ovsdb_idl_table *table = &idl->tables[i];
346 : : struct ovsdb_idl_row *row, *next_row;
347 : :
348 [ + + ]: 219392 : if (hmap_is_empty(&table->rows)) {
349 : 196449 : continue;
350 : : }
351 : :
352 : 22943 : changed = true;
353 [ + + ][ - + ]: 132558 : HMAP_FOR_EACH_SAFE (row, next_row, hmap_node, &table->rows) {
[ + + ]
354 : : struct ovsdb_idl_arc *arc, *next_arc;
355 : :
356 [ + + ]: 109615 : if (!ovsdb_idl_row_is_orphan(row)) {
357 : 109349 : ovsdb_idl_row_unparse(row);
358 : : }
359 [ + + ][ + + ]: 201585 : LIST_FOR_EACH_SAFE (arc, next_arc, src_node, &row->src_arcs) {
360 : 91970 : free(arc);
361 : : }
362 : : /* No need to do anything with dst_arcs: some node has those arcs
363 : : * as forward arcs and will destroy them itself. */
364 : :
365 [ + + ]: 109615 : if (!ovs_list_is_empty(&row->track_node)) {
366 : 128 : ovs_list_remove(&row->track_node);
367 : : }
368 : :
369 : 109615 : ovsdb_idl_row_destroy(row);
370 : : }
371 : : }
372 : :
373 : 14308 : ovsdb_idl_track_clear(idl);
374 : :
375 [ + + ]: 14308 : if (changed) {
376 : 7145 : idl->change_seqno++;
377 : : }
378 : 14308 : }
379 : :
380 : : /* Processes a batch of messages from the database server on 'idl'. This may
381 : : * cause the IDL's contents to change. The client may check for that with
382 : : * ovsdb_idl_get_seqno(). */
383 : : void
384 : 147927 : ovsdb_idl_run(struct ovsdb_idl *idl)
385 : : {
386 : : int i;
387 : :
388 [ - + ]: 147927 : ovs_assert(!idl->txn);
389 : :
390 : 147927 : ovsdb_idl_send_cond_change(idl);
391 : :
392 : 147927 : jsonrpc_session_run(idl->session);
393 [ + + ][ + - ]: 191688 : for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) {
394 : : struct jsonrpc_msg *msg;
395 : : unsigned int seqno;
396 : :
397 : 184287 : seqno = jsonrpc_session_get_seqno(idl->session);
398 [ + + ]: 184287 : if (idl->state_seqno != seqno) {
399 : 7152 : idl->state_seqno = seqno;
400 : 7152 : json_destroy(idl->request_id);
401 : 7152 : idl->request_id = NULL;
402 : 7152 : ovsdb_idl_txn_abort_all(idl);
403 : :
404 : 7152 : ovsdb_idl_send_schema_request(idl);
405 : 7152 : idl->state = IDL_S_SCHEMA_REQUESTED;
406 [ + + ]: 7152 : if (idl->lock_name) {
407 : 618 : ovsdb_idl_send_lock_request(idl);
408 : : }
409 : : }
410 : :
411 : 184287 : msg = jsonrpc_session_recv(idl->session);
412 [ + + ]: 184287 : if (!msg) {
413 : 140526 : break;
414 : : }
415 : :
416 [ + + ]: 43761 : if (msg->type == JSONRPC_NOTIFY
417 [ + + ]: 17498 : && !strcmp(msg->method, "update2")
418 [ + - ]: 17488 : && msg->params->type == JSON_ARRAY
419 [ + - ]: 17488 : && msg->params->u.array.n == 2
420 [ + - ]: 17488 : && msg->params->u.array.elems[0]->type == JSON_STRING) {
421 : : /* Database contents changed. */
422 : 17488 : ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1],
423 : : OVSDB_UPDATE2);
424 [ + + ]: 26273 : } else if (msg->type == JSONRPC_REPLY
425 [ + + ]: 26260 : && idl->request_id
426 [ + + ]: 14922 : && json_equal(idl->request_id, msg->id)) {
427 : 14304 : json_destroy(idl->request_id);
428 : 14304 : idl->request_id = NULL;
429 : :
430 [ + + - ]: 14304 : switch (idl->state) {
431 : : case IDL_S_SCHEMA_REQUESTED:
432 : : /* Reply to our "get_schema" request. */
433 : 7152 : idl->schema = json_clone(msg->result);
434 : 7152 : ovsdb_idl_send_monitor_cond_request(idl);
435 : 7152 : idl->state = IDL_S_MONITOR_COND_REQUESTED;
436 : 7152 : break;
437 : :
438 : : case IDL_S_MONITOR_REQUESTED:
439 : : case IDL_S_MONITOR_COND_REQUESTED:
440 : : /* Reply to our "monitor" or "monitor_cond" request. */
441 : 7152 : idl->change_seqno++;
442 : 7152 : ovsdb_idl_clear(idl);
443 [ + + ]: 7152 : if (idl->state == IDL_S_MONITOR_REQUESTED) {
444 : 3 : idl->state = IDL_S_MONITORING;
445 : 3 : ovsdb_idl_parse_update(idl, msg->result, OVSDB_UPDATE);
446 : : } else { /* IDL_S_MONITOR_COND_REQUESTED. */
447 : 7149 : idl->state = IDL_S_MONITORING_COND;
448 : 7149 : ovsdb_idl_parse_update(idl, msg->result, OVSDB_UPDATE2);
449 : : }
450 : :
451 : : /* Schema is not useful after monitor request is accepted
452 : : * by the server. */
453 : 7152 : json_destroy(idl->schema);
454 : 7152 : idl->schema = NULL;
455 : 7152 : break;
456 : :
457 : : case IDL_S_MONITORING:
458 : : case IDL_S_MONITORING_COND:
459 : : case IDL_S_NO_SCHEMA:
460 : : default:
461 : 0 : OVS_NOT_REACHED();
462 : : }
463 [ + + ]: 26273 : } else if (msg->type == JSONRPC_NOTIFY
464 [ + + ]: 10 : && !strcmp(msg->method, "update")
465 [ + - ]: 8 : && msg->params->type == JSON_ARRAY
466 [ + - ]: 8 : && msg->params->u.array.n == 2
467 [ + - ]: 8 : && msg->params->u.array.elems[0]->type == JSON_STRING) {
468 : : /* Database contents changed. */
469 : 8 : ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1],
470 : : OVSDB_UPDATE);
471 [ + + ]: 11961 : } else if (msg->type == JSONRPC_REPLY
472 [ + + ]: 11956 : && idl->lock_request_id
473 [ + - ]: 618 : && json_equal(idl->lock_request_id, msg->id)) {
474 : : /* Reply to our "lock" request. */
475 : 618 : ovsdb_idl_parse_lock_reply(idl, msg->result);
476 [ + + ]: 11343 : } else if (msg->type == JSONRPC_NOTIFY
477 [ + - ]: 2 : && !strcmp(msg->method, "locked")) {
478 : : /* We got our lock. */
479 : 2 : ovsdb_idl_parse_lock_notify(idl, msg->params, true);
480 [ - + ]: 11341 : } else if (msg->type == JSONRPC_NOTIFY
481 [ # # ]: 0 : && !strcmp(msg->method, "stolen")) {
482 : : /* Someone else stole our lock. */
483 : 0 : ovsdb_idl_parse_lock_notify(idl, msg->params, false);
484 [ + + ]: 11341 : } else if (msg->type == JSONRPC_ERROR
485 [ + - ]: 3 : && idl->state == IDL_S_MONITOR_COND_REQUESTED
486 [ + - ]: 3 : && idl->request_id
487 [ + - ]: 3 : && json_equal(idl->request_id, msg->id)) {
488 [ + - ][ + - ]: 6 : if (msg->error && !strcmp(json_string(msg->error),
489 : : "unknown method")) {
490 : : /* Fall back to using "monitor" method. */
491 : 3 : json_destroy(idl->request_id);
492 : 3 : idl->request_id = NULL;
493 : 3 : ovsdb_idl_send_monitor_request(idl);
494 : 3 : idl->state = IDL_S_MONITOR_REQUESTED;
495 : : }
496 [ - + ]: 11338 : } else if (msg->type == JSONRPC_ERROR
497 [ # # ]: 0 : && idl->state == IDL_S_SCHEMA_REQUESTED
498 [ # # ]: 0 : && idl->request_id
499 [ # # ]: 0 : && json_equal(idl->request_id, msg->id)) {
500 : 0 : json_destroy(idl->request_id);
501 : 0 : idl->request_id = NULL;
502 [ # # ]: 0 : VLOG_ERR("%s: requested schema not found",
503 : : jsonrpc_session_get_name(idl->session));
504 : 0 : idl->state = IDL_S_NO_SCHEMA;
505 [ + - ]: 11338 : } else if ((msg->type == JSONRPC_ERROR
506 [ + - ]: 11338 : || msg->type == JSONRPC_REPLY)
507 [ + + ]: 11338 : && ovsdb_idl_txn_process_reply(idl, msg)) {
508 : : /* ovsdb_idl_txn_process_reply() did everything needful. */
509 : : } else {
510 : : /* This can happen if ovsdb_idl_txn_destroy() is called to destroy
511 : : * a transaction before we receive the reply, so keep the log level
512 : : * low. */
513 [ - + ]: 2344 : VLOG_DBG("%s: received unexpected %s message",
514 : : jsonrpc_session_get_name(idl->session),
515 : : jsonrpc_msg_type_to_string(msg->type));
516 : : }
517 : 43761 : jsonrpc_msg_destroy(msg);
518 : : }
519 : 147927 : ovsdb_idl_row_destroy_postprocess(idl);
520 : 147927 : }
521 : :
522 : : /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to
523 : : * do or when activity occurs on a transaction on 'idl'. */
524 : : void
525 : 136036 : ovsdb_idl_wait(struct ovsdb_idl *idl)
526 : : {
527 : 136036 : jsonrpc_session_wait(idl->session);
528 : 136036 : jsonrpc_session_recv_wait(idl->session);
529 : 136036 : }
530 : :
531 : : /* Returns a "sequence number" that represents the state of 'idl'. When
532 : : * ovsdb_idl_run() changes the database, the sequence number changes. The
533 : : * initial fetch of the entire contents of the remote database is considered to
534 : : * be one kind of change. Successfully acquiring a lock, if one has been
535 : : * configured with ovsdb_idl_set_lock(), is also considered to be a change.
536 : : *
537 : : * As long as the sequence number does not change, the client may continue to
538 : : * use any data structures it obtains from 'idl'. But when it changes, the
539 : : * client must not access any of these data structures again, because they
540 : : * could have freed or reused for other purposes.
541 : : *
542 : : * The sequence number can occasionally change even if the database does not.
543 : : * This happens if the connection to the database drops and reconnects, which
544 : : * causes the database contents to be reloaded even if they didn't change. (It
545 : : * could also happen if the database server sends out a "change" that reflects
546 : : * what the IDL already thought was in the database. The database server is
547 : : * not supposed to do that, but bugs could in theory cause it to do so.) */
548 : : unsigned int
549 : 279268 : ovsdb_idl_get_seqno(const struct ovsdb_idl *idl)
550 : : {
551 : 279268 : return idl->change_seqno;
552 : : }
553 : :
554 : : /* Returns true if 'idl' successfully connected to the remote database and
555 : : * retrieved its contents (even if the connection subsequently dropped and is
556 : : * in the process of reconnecting). If so, then 'idl' contains an atomic
557 : : * snapshot of the database's contents (but it might be arbitrarily old if the
558 : : * connection dropped).
559 : : *
560 : : * Returns false if 'idl' has never connected or retrieved the database's
561 : : * contents. If so, 'idl' is empty. */
562 : : bool
563 : 107425 : ovsdb_idl_has_ever_connected(const struct ovsdb_idl *idl)
564 : : {
565 : 107425 : return ovsdb_idl_get_seqno(idl) != 0;
566 : : }
567 : :
568 : : /* Reconfigures 'idl' so that it would reconnect to the database, if
569 : : * connection was dropped. */
570 : : void
571 : 2164 : ovsdb_idl_enable_reconnect(struct ovsdb_idl *idl)
572 : : {
573 : 2164 : jsonrpc_session_enable_reconnect(idl->session);
574 : 2164 : }
575 : :
576 : : /* Forces 'idl' to drop its connection to the database and reconnect. In the
577 : : * meantime, the contents of 'idl' will not change. */
578 : : void
579 : 2 : ovsdb_idl_force_reconnect(struct ovsdb_idl *idl)
580 : : {
581 : 2 : jsonrpc_session_force_reconnect(idl->session);
582 : 2 : }
583 : :
584 : : /* Some IDL users should only write to write-only columns. Furthermore,
585 : : * writing to a column which is not write-only can cause serious performance
586 : : * degradations for these users. This function causes 'idl' to reject writes
587 : : * to columns which are not marked write only using ovsdb_idl_omit_alert(). */
588 : : void
589 : 617 : ovsdb_idl_verify_write_only(struct ovsdb_idl *idl)
590 : : {
591 : 617 : idl->verify_write_only = true;
592 : 617 : }
593 : :
594 : : /* Returns true if 'idl' is currently connected or trying to connect
595 : : * and a negative response to a schema request has not been received */
596 : : bool
597 : 16379 : ovsdb_idl_is_alive(const struct ovsdb_idl *idl)
598 : : {
599 [ + + ][ + - ]: 16379 : return jsonrpc_session_is_alive(idl->session) &&
600 : 16374 : idl->state != IDL_S_NO_SCHEMA;
601 : : }
602 : :
603 : : /* Returns the last error reported on a connection by 'idl'. The return value
604 : : * is 0 only if no connection made by 'idl' has ever encountered an error and
605 : : * a negative response to a schema request has never been received. See
606 : : * jsonrpc_get_status() for jsonrpc_session_get_last_error() return value
607 : : * interpretation. */
608 : : int
609 : 5 : ovsdb_idl_get_last_error(const struct ovsdb_idl *idl)
610 : : {
611 : : int err;
612 : :
613 : 5 : err = jsonrpc_session_get_last_error(idl->session);
614 : :
615 [ + - ]: 5 : if (err) {
616 : 5 : return err;
617 [ # # ]: 0 : } else if (idl->state == IDL_S_NO_SCHEMA) {
618 : 0 : return ENOENT;
619 : : } else {
620 : 0 : return 0;
621 : : }
622 : : }
623 : :
624 : : /* Sets the "probe interval" for 'idl->session' to 'probe_interval', in
625 : : * milliseconds.
626 : : */
627 : : void
628 : 3167 : ovsdb_idl_set_probe_interval(const struct ovsdb_idl *idl, int probe_interval)
629 : : {
630 : 3167 : jsonrpc_session_set_probe_interval(idl->session, probe_interval);
631 : 3167 : }
632 : :
633 : : static unsigned char *
634 : 131671 : ovsdb_idl_get_mode(struct ovsdb_idl *idl,
635 : : const struct ovsdb_idl_column *column)
636 : : {
637 : : size_t i;
638 : :
639 [ - + ]: 131671 : ovs_assert(!idl->change_seqno);
640 : :
641 [ + - ]: 1047862 : for (i = 0; i < idl->class->n_tables; i++) {
642 : 1047862 : const struct ovsdb_idl_table *table = &idl->tables[i];
643 : 1047862 : const struct ovsdb_idl_table_class *tc = table->class;
644 : :
645 [ + + ][ + + ]: 1047862 : if (column >= tc->columns && column < &tc->columns[tc->n_columns]) {
646 : 131671 : return &table->modes[column - tc->columns];
647 : : }
648 : : }
649 : :
650 : 0 : OVS_NOT_REACHED();
651 : : }
652 : :
653 : : static void
654 : 174678 : add_ref_table(struct ovsdb_idl *idl, const struct ovsdb_base_type *base)
655 : : {
656 [ + + ][ + + ]: 174678 : if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) {
657 : : struct ovsdb_idl_table *table;
658 : :
659 : 27244 : table = shash_find_data(&idl->table_by_name,
660 : 27244 : base->u.uuid.refTableName);
661 [ + - ]: 27244 : if (table) {
662 : 27244 : table->need_table = true;
663 : : } else {
664 [ # # ]: 0 : VLOG_WARN("%s IDL class missing referenced table %s",
665 : : idl->class->database, base->u.uuid.refTableName);
666 : : }
667 : : }
668 : 174678 : }
669 : :
670 : : /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also
671 : : * ensures that any tables referenced by 'column' will be replicated, even if
672 : : * no columns in that table are selected for replication (see
673 : : * ovsdb_idl_add_table() for more information).
674 : : *
675 : : * This function is only useful if 'monitor_everything_by_default' was false in
676 : : * the call to ovsdb_idl_create(). This function should be called between
677 : : * ovsdb_idl_create() and the first call to ovsdb_idl_run().
678 : : */
679 : : void
680 : 87339 : ovsdb_idl_add_column(struct ovsdb_idl *idl,
681 : : const struct ovsdb_idl_column *column)
682 : : {
683 : 87339 : *ovsdb_idl_get_mode(idl, column) = OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT;
684 : 87339 : add_ref_table(idl, &column->type.key);
685 : 87339 : add_ref_table(idl, &column->type.value);
686 : 87339 : }
687 : :
688 : : /* Ensures that the table with class 'tc' will be replicated on 'idl' even if
689 : : * no columns are selected for replication. Just the necessary data for table
690 : : * references will be replicated (the UUID of the rows, for instance), any
691 : : * columns not selected for replication will remain unreplicated.
692 : : * This can be useful because it allows 'idl' to keep track of what rows in the
693 : : * table actually exist, which in turn allows columns that reference the table
694 : : * to have accurate contents. (The IDL presents the database with references to
695 : : * rows that do not exist removed.)
696 : : *
697 : : * This function is only useful if 'monitor_everything_by_default' was false in
698 : : * the call to ovsdb_idl_create(). This function should be called between
699 : : * ovsdb_idl_create() and the first call to ovsdb_idl_run().
700 : : */
701 : : void
702 : 18394 : ovsdb_idl_add_table(struct ovsdb_idl *idl,
703 : : const struct ovsdb_idl_table_class *tc)
704 : : {
705 : : size_t i;
706 : :
707 [ + - ]: 142229 : for (i = 0; i < idl->class->n_tables; i++) {
708 : 142229 : struct ovsdb_idl_table *table = &idl->tables[i];
709 : :
710 [ + + ]: 142229 : if (table->class == tc) {
711 : 18394 : table->need_table = true;
712 : 18394 : return;
713 : : }
714 : : }
715 : :
716 : 0 : OVS_NOT_REACHED();
717 : : }
718 : :
719 : : struct ovsdb_idl_clause {
720 : : struct ovs_list node;
721 : : enum ovsdb_function function;
722 : : const struct ovsdb_idl_column *column;
723 : : struct ovsdb_datum arg;
724 : : };
725 : :
726 : : static struct json *
727 : 26 : ovsdb_idl_clause_to_json(const struct ovsdb_idl_clause *clause)
728 : : {
729 [ + + ][ + + ]: 26 : if (clause->function != OVSDB_F_TRUE &&
730 : 25 : clause->function != OVSDB_F_FALSE) {
731 : 9 : const char *function = ovsdb_function_to_string(clause->function);
732 : :
733 : 9 : return json_array_create_3(json_string_create(clause->column->name),
734 : : json_string_create(function),
735 : : ovsdb_datum_to_json(&clause->arg,
736 : 9 : &clause->column->type));
737 : : }
738 : :
739 : 17 : return json_boolean_create(clause->function == OVSDB_F_TRUE ?
740 : : true : false);
741 : : }
742 : :
743 : : static void
744 : 2 : ovsdb_idl_clause_free(struct ovsdb_idl_clause *clause)
745 : : {
746 [ + - ][ + + ]: 2 : if (clause->function != OVSDB_F_TRUE &&
747 : 2 : clause->function != OVSDB_F_FALSE) {
748 : 1 : ovsdb_datum_destroy(&clause->arg, &clause->column->type);
749 : : }
750 : :
751 : 2 : ovs_list_remove(&clause->node);
752 : 2 : free(clause);
753 : 2 : }
754 : :
755 : : /* Clears all of the conditional clauses from table 'tc', so that all of the
756 : : * rows in the table will be replicated. (This is the default, so this
757 : : * function has an effect only if some clauses were added to 'tc' using
758 : : * ovsdb_idl_condition_add_clause().) */
759 : : void
760 : 0 : ovsdb_idl_condition_reset(struct ovsdb_idl *idl,
761 : : const struct ovsdb_idl_table_class *tc)
762 : : {
763 : : struct ovsdb_idl_clause *c, *next;
764 : 0 : struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, tc);
765 : :
766 [ # # ][ # # ]: 0 : LIST_FOR_EACH_SAFE (c, next, node, &table->condition.clauses) {
767 : 0 : ovsdb_idl_clause_free(c);
768 : : }
769 : 0 : idl->cond_changed = table->cond_changed = true;
770 : 0 : }
771 : :
772 : : static void
773 : 109765 : ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd,
774 : : const struct ovsdb_idl_table_class *tc)
775 : : {
776 : 109765 : cnd->tc = tc;
777 : 109765 : ovs_list_init(&cnd->clauses);
778 : 109765 : }
779 : :
780 : : static struct ovsdb_idl_clause *
781 : 19 : ovsdb_idl_condition_find_clause(struct ovsdb_idl_table *table,
782 : : enum ovsdb_function function,
783 : : const struct ovsdb_idl_column *column,
784 : : const struct ovsdb_datum *arg)
785 : : {
786 : : struct ovsdb_idl_clause *c;
787 [ + + ]: 31 : LIST_FOR_EACH (c, node, &table->condition.clauses) {
788 [ + + ][ + + ]: 14 : if (c->function == function &&
789 [ + - + + ]: 6 : (!column || (c->column == column &&
790 : 3 : ovsdb_datum_equals(&c->arg,
791 : : arg, &column->type)))) {
792 : 2 : return c;
793 : : }
794 : : }
795 : 17 : return NULL;
796 : : }
797 : :
798 : : /* Adds a clause to the condition for replicating the table with class 'tc' in
799 : : * 'idl'.
800 : : *
801 : : * By default, a table has no clauses, and in that case the IDL replicates all
802 : : * its rows. When a table has one or more clauses, the IDL replicates only
803 : : * rows that satisfy at least one clause.
804 : : *
805 : : * Two distinct of clauses can be added:
806 : : *
807 : : * - A 'function' of OVSDB_F_FALSE or OVSDB_F_TRUE adds a Boolean clause. A
808 : : * "false" clause by itself prevents any rows from being replicated; in
809 : : * combination with other clauses it has no effect. A "true" clause
810 : : * causes every row to be replicated, regardless of whether other clauses
811 : : * exist (thus, a condition that includes "true" is like a condition
812 : : * without any clauses at all).
813 : : *
814 : : * 'column' should be NULL and 'arg' should be an empty datum (initialized
815 : : * with ovsdb_datum_init_empty()).
816 : : *
817 : : * - Other 'functions' add a clause of the form "<column> <function> <arg>",
818 : : * e.g. "column == 5" or "column <= 10". In this case, 'arg' must have a
819 : : * type that is compatible with 'column'.
820 : : */
821 : : void
822 : 17 : ovsdb_idl_condition_add_clause(struct ovsdb_idl *idl,
823 : : const struct ovsdb_idl_table_class *tc,
824 : : enum ovsdb_function function,
825 : : const struct ovsdb_idl_column *column,
826 : : const struct ovsdb_datum *arg)
827 : : {
828 : 17 : struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, tc);
829 : :
830 : : /* Return without doing anything, if this would be a duplicate clause. */
831 [ - + ]: 17 : if (ovsdb_idl_condition_find_clause(table, function, column, arg)) {
832 : 0 : return;
833 : : }
834 : :
835 : 17 : struct ovsdb_idl_clause *clause = xzalloc(sizeof *clause);
836 : 17 : ovs_list_init(&clause->node);
837 : 17 : clause->function = function;
838 : 17 : clause->column = column;
839 [ + + ]: 17 : ovsdb_datum_clone(&clause->arg, arg,
840 : : column ? &column->type : &ovsdb_type_boolean);
841 : 17 : ovs_list_push_back(&table->condition.clauses, &clause->node);
842 : 17 : idl->cond_changed = table->cond_changed = true;
843 : 17 : poll_immediate_wake();
844 : : }
845 : :
846 : : /* If a clause matching (function, column, arg) is included in the condition
847 : : * for 'tc' within 'idl', removes it. (If this was the last clause included in
848 : : * the table's condition, then this means that the IDL will begin replicating
849 : : * every row in the table.) */
850 : : void
851 : 2 : ovsdb_idl_condition_remove_clause(struct ovsdb_idl *idl,
852 : : const struct ovsdb_idl_table_class *tc,
853 : : enum ovsdb_function function,
854 : : const struct ovsdb_idl_column *column,
855 : : const struct ovsdb_datum *arg)
856 : : {
857 : 2 : struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, tc);
858 : 2 : struct ovsdb_idl_clause *c
859 : : = ovsdb_idl_condition_find_clause(table, function, column, arg);
860 [ + - ]: 2 : if (c) {
861 : 2 : ovsdb_idl_clause_free(c);
862 : 2 : idl->cond_changed = table->cond_changed = true;
863 : 2 : poll_immediate_wake();
864 : : }
865 : 2 : }
866 : :
867 : : static struct json *
868 : 17 : ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd)
869 : : {
870 : : struct json **clauses;
871 : 17 : size_t i = 0, n_clauses = ovs_list_size(&cnd->clauses);
872 : : struct ovsdb_idl_clause *c;
873 : :
874 : 17 : clauses = xmalloc(n_clauses * sizeof *clauses);
875 [ + + ]: 43 : LIST_FOR_EACH (c, node, &cnd->clauses) {
876 : 26 : clauses[i++] = ovsdb_idl_clause_to_json(c);
877 : : }
878 : :
879 : 17 : return json_array_create(clauses, n_clauses);
880 : : }
881 : :
882 : : static struct json *
883 : 9 : ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table *table)
884 : : {
885 : 9 : const struct ovsdb_idl_condition *cond = &table->condition;
886 : 9 : struct json *monitor_cond_change_request = json_object_create();
887 : 9 : struct json *cond_json = ovsdb_idl_condition_to_json(cond);
888 : :
889 : 9 : json_object_put(monitor_cond_change_request, "where", cond_json);
890 : :
891 : 9 : return monitor_cond_change_request;
892 : : }
893 : :
894 : : static void
895 : 147927 : ovsdb_idl_send_cond_change(struct ovsdb_idl *idl)
896 : : {
897 : : int i;
898 : : char uuid[UUID_LEN + 1];
899 : : struct json *params, *json_uuid;
900 : : struct jsonrpc_msg *request;
901 : :
902 [ + + ][ + + ]: 147927 : if (!idl->cond_changed || !jsonrpc_session_is_connected(idl->session) ||
[ + + ]
903 : 11 : idl->state != IDL_S_MONITORING_COND) {
904 : 147918 : return;
905 : : }
906 : :
907 : 9 : struct json *monitor_cond_change_requests = NULL;
908 : :
909 [ + + ]: 63 : for (i = 0; i < idl->class->n_tables; i++) {
910 : 54 : struct ovsdb_idl_table *table = &idl->tables[i];
911 : :
912 [ + + ]: 54 : if (table->cond_changed) {
913 : 9 : struct json *req = ovsdb_idl_create_cond_change_req(table);
914 [ + - ]: 9 : if (req) {
915 [ + - ]: 9 : if (!monitor_cond_change_requests) {
916 : 9 : monitor_cond_change_requests = json_object_create();
917 : : }
918 : 9 : json_object_put(monitor_cond_change_requests,
919 : 9 : table->class->name,
920 : : json_array_create_1(req));
921 : : }
922 : 9 : table->cond_changed = false;
923 : : }
924 : : }
925 : :
926 : : /* Send request if not empty. */
927 [ + - ]: 9 : if (monitor_cond_change_requests) {
928 : 27 : snprintf(uuid, sizeof uuid, UUID_FMT,
929 : 27 : UUID_ARGS(&idl->uuid));
930 : 9 : json_uuid = json_string_create(uuid);
931 : :
932 : : /* Create a new uuid */
933 : 9 : uuid_generate(&idl->uuid);
934 : 27 : snprintf(uuid, sizeof uuid, UUID_FMT,
935 : 27 : UUID_ARGS(&idl->uuid));
936 : 9 : params = json_array_create_3(json_uuid, json_string_create(uuid),
937 : : monitor_cond_change_requests);
938 : :
939 : 9 : request = jsonrpc_create_request("monitor_cond_change", params, NULL);
940 : 9 : jsonrpc_session_send(idl->session, request);
941 : : }
942 : 9 : idl->cond_changed = false;
943 : : }
944 : :
945 : : /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'.
946 : : *
947 : : * This function should be called between ovsdb_idl_create() and the first call
948 : : * to ovsdb_idl_run().
949 : : */
950 : : void
951 : 26706 : ovsdb_idl_omit_alert(struct ovsdb_idl *idl,
952 : : const struct ovsdb_idl_column *column)
953 : : {
954 : 26706 : *ovsdb_idl_get_mode(idl, column) &= ~OVSDB_IDL_ALERT;
955 : 26706 : }
956 : :
957 : : /* Sets the mode for 'column' in 'idl' to 0. See the big comment above
958 : : * OVSDB_IDL_MONITOR for details.
959 : : *
960 : : * This function should be called between ovsdb_idl_create() and the first call
961 : : * to ovsdb_idl_run().
962 : : */
963 : : void
964 : 13574 : ovsdb_idl_omit(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column)
965 : : {
966 : 13574 : *ovsdb_idl_get_mode(idl, column) = 0;
967 : 13574 : }
968 : :
969 : : /* Returns the most recent IDL change sequence number that caused a
970 : : * insert, modify or delete update to the table with class 'table_class'.
971 : : */
972 : : unsigned int
973 : 0 : ovsdb_idl_table_get_seqno(const struct ovsdb_idl *idl,
974 : : const struct ovsdb_idl_table_class *table_class)
975 : : {
976 : 0 : struct ovsdb_idl_table *table
977 : : = ovsdb_idl_table_from_class(idl, table_class);
978 : 0 : unsigned int max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_INSERT];
979 : :
980 [ # # ]: 0 : if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]) {
981 : 0 : max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY];
982 : : }
983 [ # # ]: 0 : if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_DELETE]) {
984 : 0 : max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_DELETE];
985 : : }
986 : 0 : return max_seqno;
987 : : }
988 : :
989 : : /* For each row that contains tracked columns, IDL stores the most
990 : : * recent IDL change sequence numbers associateed with insert, modify
991 : : * and delete updates to the table.
992 : : */
993 : : unsigned int
994 : 9 : ovsdb_idl_row_get_seqno(const struct ovsdb_idl_row *row,
995 : : enum ovsdb_idl_change change)
996 : : {
997 : 9 : return row->change_seqno[change];
998 : : }
999 : :
1000 : : /* Turns on OVSDB_IDL_TRACK for 'column' in 'idl', ensuring that
1001 : : * all rows whose 'column' is modified are traced. Similarly, insert
1002 : : * or delete of rows having 'column' are tracked. Clients are able
1003 : : * to retrive the tracked rows with the ovsdb_idl_track_get_*()
1004 : : * functions.
1005 : : *
1006 : : * This function should be called between ovsdb_idl_create() and
1007 : : * the first call to ovsdb_idl_run(). The column to be tracked
1008 : : * should have OVSDB_IDL_ALERT turned on.
1009 : : */
1010 : : void
1011 : 2026 : ovsdb_idl_track_add_column(struct ovsdb_idl *idl,
1012 : : const struct ovsdb_idl_column *column)
1013 : : {
1014 [ + + ]: 2026 : if (!(*ovsdb_idl_get_mode(idl, column) & OVSDB_IDL_ALERT)) {
1015 : 44 : ovsdb_idl_add_column(idl, column);
1016 : : }
1017 : 2026 : *ovsdb_idl_get_mode(idl, column) |= OVSDB_IDL_TRACK;
1018 : 2026 : }
1019 : :
1020 : : void
1021 : 46 : ovsdb_idl_track_add_all(struct ovsdb_idl *idl)
1022 : : {
1023 : : size_t i, j;
1024 : :
1025 [ + + ]: 542 : for (i = 0; i < idl->class->n_tables; i++) {
1026 : 496 : const struct ovsdb_idl_table_class *tc = &idl->class->tables[i];
1027 : :
1028 [ + + ]: 2522 : for (j = 0; j < tc->n_columns; j++) {
1029 : 2026 : const struct ovsdb_idl_column *column = &tc->columns[j];
1030 : 2026 : ovsdb_idl_track_add_column(idl, column);
1031 : : }
1032 : : }
1033 : 46 : }
1034 : :
1035 : : /* Returns true if 'table' has any tracked column. */
1036 : : static bool
1037 : 134805 : ovsdb_idl_track_is_set(struct ovsdb_idl_table *table)
1038 : : {
1039 : : size_t i;
1040 : :
1041 [ + + ]: 2509971 : for (i = 0; i < table->class->n_columns; i++) {
1042 [ + + ]: 2400346 : if (table->modes[i] & OVSDB_IDL_TRACK) {
1043 : 25180 : return true;
1044 : : }
1045 : : }
1046 : 109625 : return false;
1047 : : }
1048 : :
1049 : : /* Returns the first tracked row in table with class 'table_class'
1050 : : * for the specified 'idl'. Returns NULL if there are no tracked rows */
1051 : : const struct ovsdb_idl_row *
1052 : 24 : ovsdb_idl_track_get_first(const struct ovsdb_idl *idl,
1053 : : const struct ovsdb_idl_table_class *table_class)
1054 : : {
1055 : 24 : struct ovsdb_idl_table *table
1056 : : = ovsdb_idl_table_from_class(idl, table_class);
1057 : :
1058 [ + + ]: 24 : if (!ovs_list_is_empty(&table->track_list)) {
1059 : 7 : return CONTAINER_OF(ovs_list_front(&table->track_list), struct ovsdb_idl_row, track_node);
1060 : : }
1061 : 17 : return NULL;
1062 : : }
1063 : :
1064 : : /* Returns the next tracked row in table after the specified 'row'
1065 : : * (in no particular order). Returns NULL if there are no tracked rows */
1066 : : const struct ovsdb_idl_row *
1067 : 9 : ovsdb_idl_track_get_next(const struct ovsdb_idl_row *row)
1068 : : {
1069 [ + + ]: 9 : if (row->track_node.next != &row->table->track_list) {
1070 : 2 : return CONTAINER_OF(row->track_node.next, struct ovsdb_idl_row, track_node);
1071 : : }
1072 : :
1073 : 7 : return NULL;
1074 : : }
1075 : :
1076 : : /* Returns true if a tracked 'column' in 'row' was updated by IDL, false
1077 : : * otherwise. The tracking data is cleared by ovsdb_idl_track_clear()
1078 : : *
1079 : : * Function returns false if 'column' is not tracked (see
1080 : : * ovsdb_idl_track_add_column()).
1081 : : */
1082 : : bool
1083 : 740 : ovsdb_idl_track_is_updated(const struct ovsdb_idl_row *row,
1084 : : const struct ovsdb_idl_column *column)
1085 : : {
1086 : : const struct ovsdb_idl_table_class *class;
1087 : : size_t column_idx;
1088 : :
1089 : 740 : class = row->table->class;
1090 : 740 : column_idx = column - class->columns;
1091 : :
1092 [ + + ][ + + ]: 740 : if (row->updated && bitmap_is_set(row->updated, column_idx)) {
1093 : 47 : return true;
1094 : : } else {
1095 : 693 : return false;
1096 : : }
1097 : : }
1098 : :
1099 : : /* Flushes the tracked rows. Client calls this function after calling
1100 : : * ovsdb_idl_run() and read all tracked rows with the ovsdb_idl_track_get_*()
1101 : : * functions. This is usually done at the end of the client's processing
1102 : : * loop when it is ready to do ovsdb_idl_run() again.
1103 : : */
1104 : : void
1105 : 20671 : ovsdb_idl_track_clear(const struct ovsdb_idl *idl)
1106 : : {
1107 : : size_t i;
1108 : :
1109 [ + + ]: 325746 : for (i = 0; i < idl->class->n_tables; i++) {
1110 : 305075 : struct ovsdb_idl_table *table = &idl->tables[i];
1111 : :
1112 [ + + ]: 305075 : if (!ovs_list_is_empty(&table->track_list)) {
1113 : : struct ovsdb_idl_row *row, *next;
1114 : :
1115 [ + + ][ + + ]: 144619 : LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) {
1116 [ + + ]: 119699 : if (row->updated) {
1117 : 9578 : free(row->updated);
1118 : 9578 : row->updated = NULL;
1119 : : }
1120 : 119699 : ovs_list_remove(&row->track_node);
1121 : 119699 : ovs_list_init(&row->track_node);
1122 [ + + ]: 119699 : if (ovsdb_idl_row_is_orphan(row)) {
1123 : 110252 : ovsdb_idl_row_clear_old(row);
1124 : 110252 : free(row);
1125 : : }
1126 : : }
1127 : : }
1128 : : }
1129 : 20671 : }
1130 : :
1131 : :
1132 : : static void
1133 : 7152 : ovsdb_idl_send_schema_request(struct ovsdb_idl *idl)
1134 : : {
1135 : : struct jsonrpc_msg *msg;
1136 : :
1137 : 7152 : json_destroy(idl->request_id);
1138 : 7152 : msg = jsonrpc_create_request(
1139 : : "get_schema",
1140 : 7152 : json_array_create_1(json_string_create(idl->class->database)),
1141 : : &idl->request_id);
1142 : 7152 : jsonrpc_session_send(idl->session, msg);
1143 : 7152 : }
1144 : :
1145 : : static void
1146 : 0 : log_error(struct ovsdb_error *error)
1147 : : {
1148 : 0 : char *s = ovsdb_error_to_string(error);
1149 [ # # ]: 0 : VLOG_WARN("error parsing database schema: %s", s);
1150 : 0 : free(s);
1151 : 0 : ovsdb_error_destroy(error);
1152 : 0 : }
1153 : :
1154 : : /* Frees 'schema', which is in the format returned by parse_schema(). */
1155 : : static void
1156 : 7155 : free_schema(struct shash *schema)
1157 : : {
1158 [ + - ]: 7155 : if (schema) {
1159 : : struct shash_node *node, *next;
1160 : :
1161 [ + + ][ - + ]: 116841 : SHASH_FOR_EACH_SAFE (node, next, schema) {
[ + + ]
1162 : 109686 : struct sset *sset = node->data;
1163 : 109686 : sset_destroy(sset);
1164 : 109686 : free(sset);
1165 : 109686 : shash_delete(schema, node);
1166 : : }
1167 : 7155 : shash_destroy(schema);
1168 : 7155 : free(schema);
1169 : : }
1170 : 7155 : }
1171 : :
1172 : : /* Parses 'schema_json', an OVSDB schema in JSON format as described in RFC
1173 : : * 7047, to obtain the names of its rows and columns. If successful, returns
1174 : : * an shash whose keys are table names and whose values are ssets, where each
1175 : : * sset contains the names of its table's columns. On failure (due to a parse
1176 : : * error), returns NULL.
1177 : : *
1178 : : * It would also be possible to use the general-purpose OVSDB schema parser in
1179 : : * ovsdb-server, but that's overkill, possibly too strict for the current use
1180 : : * case, and would require restructuring ovsdb-server to separate the schema
1181 : : * code from the rest. */
1182 : : static struct shash *
1183 : 7155 : parse_schema(const struct json *schema_json)
1184 : : {
1185 : : struct ovsdb_parser parser;
1186 : : const struct json *tables_json;
1187 : : struct ovsdb_error *error;
1188 : : struct shash_node *node;
1189 : : struct shash *schema;
1190 : :
1191 : 7155 : ovsdb_parser_init(&parser, schema_json, "database schema");
1192 : 7155 : tables_json = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
1193 : 7155 : error = ovsdb_parser_destroy(&parser);
1194 [ - + ]: 7155 : if (error) {
1195 : 0 : log_error(error);
1196 : 0 : return NULL;
1197 : : }
1198 : :
1199 : 7155 : schema = xmalloc(sizeof *schema);
1200 : 7155 : shash_init(schema);
1201 [ + + ][ - + ]: 116841 : SHASH_FOR_EACH (node, json_object(tables_json)) {
1202 : 109686 : const char *table_name = node->name;
1203 : 109686 : const struct json *json = node->data;
1204 : : const struct json *columns_json;
1205 : :
1206 : 109686 : ovsdb_parser_init(&parser, json, "table schema for table %s",
1207 : : table_name);
1208 : 109686 : columns_json = ovsdb_parser_member(&parser, "columns", OP_OBJECT);
1209 : 109686 : error = ovsdb_parser_destroy(&parser);
1210 [ - + ]: 109686 : if (error) {
1211 : 0 : log_error(error);
1212 : 0 : free_schema(schema);
1213 : 0 : return NULL;
1214 : : }
1215 : :
1216 : 109686 : struct sset *columns = xmalloc(sizeof *columns);
1217 : 109686 : sset_init(columns);
1218 : :
1219 : : struct shash_node *node2;
1220 [ + + ][ - + ]: 1112172 : SHASH_FOR_EACH (node2, json_object(columns_json)) {
1221 : 1002486 : const char *column_name = node2->name;
1222 : 1002486 : sset_add(columns, column_name);
1223 : : }
1224 : 109686 : shash_add(schema, table_name, columns);
1225 : : }
1226 : 7155 : return schema;
1227 : : }
1228 : :
1229 : : static void
1230 : 7155 : ovsdb_idl_send_monitor_request__(struct ovsdb_idl *idl,
1231 : : const char *method)
1232 : : {
1233 : : struct shash *schema;
1234 : : struct json *monitor_requests;
1235 : : struct jsonrpc_msg *msg;
1236 : : char uuid[UUID_LEN + 1];
1237 : : size_t i;
1238 : :
1239 : 7155 : schema = parse_schema(idl->schema);
1240 : 7155 : monitor_requests = json_object_create();
1241 [ + + ]: 116842 : for (i = 0; i < idl->class->n_tables; i++) {
1242 : 109687 : struct ovsdb_idl_table *table = &idl->tables[i];
1243 : 109687 : const struct ovsdb_idl_table_class *tc = table->class;
1244 : : struct json *monitor_request, *columns, *where;
1245 : : const struct sset *table_schema;
1246 : : size_t j;
1247 : :
1248 : 109687 : table_schema = (schema
1249 : 109687 : ? shash_find_data(schema, table->class->name)
1250 [ + - ]: 109687 : : NULL);
1251 : :
1252 [ + + ]: 109687 : columns = table->need_table ? json_array_create_empty() : NULL;
1253 [ + + ]: 1112176 : for (j = 0; j < tc->n_columns; j++) {
1254 : 1002489 : const struct ovsdb_idl_column *column = &tc->columns[j];
1255 [ + + ]: 1002489 : if (table->modes[j] & OVSDB_IDL_MONITOR) {
1256 [ + + ]: 204808 : if (table_schema
1257 [ + + ]: 204806 : && !sset_contains(table_schema, column->name)) {
1258 [ + - ]: 1 : VLOG_WARN("%s table in %s database lacks %s column "
1259 : : "(database needs upgrade?)",
1260 : : table->class->name, idl->class->database,
1261 : : column->name);
1262 : 1 : continue;
1263 : : }
1264 [ + + ]: 204807 : if (!columns) {
1265 : 22308 : columns = json_array_create_empty();
1266 : : }
1267 : 204807 : json_array_add(columns, json_string_create(column->name));
1268 : : }
1269 : : }
1270 : :
1271 [ + + ]: 109687 : if (columns) {
1272 [ + - ][ + + ]: 45023 : if (schema && !table_schema) {
1273 [ + - ]: 1 : VLOG_WARN("%s database lacks %s table "
1274 : : "(database needs upgrade?)",
1275 : : idl->class->database, table->class->name);
1276 : 1 : json_destroy(columns);
1277 : 1 : continue;
1278 : : }
1279 : :
1280 : 45022 : monitor_request = json_object_create();
1281 : 45022 : json_object_put(monitor_request, "columns", columns);
1282 [ + + ]: 45030 : if (!strcmp(method, "monitor_cond") && table->cond_changed &&
[ + + + - ]
1283 : 8 : ovs_list_size(&table->condition.clauses) > 0) {
1284 : 8 : where = ovsdb_idl_condition_to_json(&table->condition);
1285 : 8 : json_object_put(monitor_request, "where", where);
1286 : 8 : table->cond_changed = false;
1287 : : }
1288 : 45022 : json_object_put(monitor_requests, tc->name, monitor_request);
1289 : : }
1290 : : }
1291 : 7155 : free_schema(schema);
1292 : :
1293 : 7155 : json_destroy(idl->request_id);
1294 : :
1295 : 7155 : snprintf(uuid, sizeof uuid, UUID_FMT, UUID_ARGS(&idl->uuid));
1296 : 7155 : msg = jsonrpc_create_request(
1297 : : method,
1298 : 7155 : json_array_create_3(json_string_create(idl->class->database),
1299 : : json_string_create(uuid), monitor_requests),
1300 : : &idl->request_id);
1301 : 7155 : jsonrpc_session_send(idl->session, msg);
1302 : 7155 : idl->cond_changed = false;
1303 : 7155 : }
1304 : :
1305 : : static void
1306 : 3 : ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl)
1307 : : {
1308 : 3 : ovsdb_idl_send_monitor_request__(idl, "monitor");
1309 : 3 : }
1310 : :
1311 : : static void
1312 : 0 : log_parse_update_error(struct ovsdb_error *error)
1313 : : {
1314 [ # # ]: 0 : if (!VLOG_DROP_WARN(&syntax_rl)) {
1315 : 0 : char *s = ovsdb_error_to_string(error);
1316 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "%s", s);
1317 : 0 : free(s);
1318 : : }
1319 : 0 : ovsdb_error_destroy(error);
1320 : 0 : }
1321 : :
1322 : : static void
1323 : 7152 : ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl *idl)
1324 : : {
1325 : 7152 : ovsdb_idl_send_monitor_request__(idl, "monitor_cond");
1326 : 7152 : }
1327 : :
1328 : : static void
1329 : 24648 : ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates,
1330 : : enum ovsdb_update_version version)
1331 : : {
1332 : 24648 : struct ovsdb_error *error = ovsdb_idl_parse_update__(idl, table_updates,
1333 : : version);
1334 [ - + ]: 24648 : if (error) {
1335 : 0 : log_parse_update_error(error);
1336 : : }
1337 : 24648 : }
1338 : :
1339 : : static struct ovsdb_error *
1340 : 24648 : ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
1341 : : const struct json *table_updates,
1342 : : enum ovsdb_update_version version)
1343 : : {
1344 : : const struct shash_node *tables_node;
1345 : 24648 : const char *table_updates_name = table_updates_names[version];
1346 : 24648 : const char *table_update_name = table_update_names[version];
1347 : 24648 : const char *row_update_name = row_update_names[version];
1348 : :
1349 [ - + ]: 24648 : if (table_updates->type != JSON_OBJECT) {
1350 : 0 : return ovsdb_syntax_error(table_updates, NULL,
1351 : : "<%s> is not an object",
1352 : : table_updates_name);
1353 : : }
1354 : :
1355 [ + + ][ - + ]: 77379 : SHASH_FOR_EACH (tables_node, json_object(table_updates)) {
1356 : 52731 : const struct json *table_update = tables_node->data;
1357 : : const struct shash_node *table_node;
1358 : : struct ovsdb_idl_table *table;
1359 : :
1360 : 52731 : table = shash_find_data(&idl->table_by_name, tables_node->name);
1361 [ - + ]: 52731 : if (!table) {
1362 : 0 : return ovsdb_syntax_error(
1363 : : table_updates, NULL,
1364 : : "<%s> includes unknown table \"%s\"",
1365 : : table_updates_name,
1366 : : tables_node->name);
1367 : : }
1368 : :
1369 [ - + ]: 52731 : if (table_update->type != JSON_OBJECT) {
1370 : 0 : return ovsdb_syntax_error(table_update, NULL,
1371 : : "<%s> for table \"%s\" is "
1372 : : "not an object",
1373 : : table_update_name,
1374 : 0 : table->class->name);
1375 : : }
1376 [ + + ][ - + ]: 198037 : SHASH_FOR_EACH (table_node, json_object(table_update)) {
1377 : 145306 : const struct json *row_update = table_node->data;
1378 : : const struct json *old_json, *new_json;
1379 : : struct uuid uuid;
1380 : :
1381 [ - + ]: 145306 : if (!uuid_from_string(&uuid, table_node->name)) {
1382 : 0 : return ovsdb_syntax_error(table_update, NULL,
1383 : : "<%s> for table \"%s\" "
1384 : : "contains bad UUID "
1385 : : "\"%s\" as member name",
1386 : : table_update_name,
1387 : 0 : table->class->name,
1388 : : table_node->name);
1389 : : }
1390 [ - + ]: 145306 : if (row_update->type != JSON_OBJECT) {
1391 : 0 : return ovsdb_syntax_error(row_update, NULL,
1392 : : "<%s> for table \"%s\" "
1393 : : "contains <%s> for %s that "
1394 : : "is not an object",
1395 : : table_update_name,
1396 : 0 : table->class->name,
1397 : : row_update_name,
1398 : : table_node->name);
1399 : : }
1400 : :
1401 [ + + - ]: 145306 : switch(version) {
1402 : : case OVSDB_UPDATE:
1403 : 36 : old_json = shash_find_data(json_object(row_update), "old");
1404 : 36 : new_json = shash_find_data(json_object(row_update), "new");
1405 [ + + ][ - + ]: 36 : if (old_json && old_json->type != JSON_OBJECT) {
1406 : 0 : return ovsdb_syntax_error(old_json, NULL,
1407 : : "\"old\" <row> is not object");
1408 [ + - ][ - + ]: 36 : } else if (new_json && new_json->type != JSON_OBJECT) {
1409 : 0 : return ovsdb_syntax_error(new_json, NULL,
1410 : : "\"new\" <row> is not object");
1411 [ - + ]: 36 : } else if ((old_json != NULL) + (new_json != NULL)
1412 : 36 : != shash_count(json_object(row_update))) {
1413 : 0 : return ovsdb_syntax_error(row_update, NULL,
1414 : : "<row-update> contains "
1415 : : "unexpected member");
1416 [ + + ][ - + ]: 36 : } else if (!old_json && !new_json) {
1417 : 0 : return ovsdb_syntax_error(row_update, NULL,
1418 : : "<row-update> missing \"old\" "
1419 : : "and \"new\" members");
1420 : : }
1421 : :
1422 [ + + ]: 36 : if (ovsdb_idl_process_update(table, &uuid, old_json,
1423 : : new_json)) {
1424 : 32 : idl->change_seqno++;
1425 : : }
1426 : 36 : break;
1427 : :
1428 : : case OVSDB_UPDATE2: {
1429 : 145270 : const char *ops[] = {"modify", "insert", "delete", "initial"};
1430 : : const char *operation;
1431 : : const struct json *row;
1432 : : int i;
1433 : :
1434 [ + - ]: 443696 : for (i = 0; i < ARRAY_SIZE(ops); i++) {
1435 : 443696 : operation = ops[i];
1436 : 443696 : row = shash_find_data(json_object(row_update), operation);
1437 : :
1438 [ + + ]: 443696 : if (row) {
1439 [ + + ]: 145270 : if (ovsdb_idl_process_update2(table, &uuid, operation,
1440 : : row)) {
1441 : 133394 : idl->change_seqno++;
1442 : : }
1443 : 145270 : break;
1444 : : }
1445 : : }
1446 : :
1447 : : /* row_update2 should contain one of the objects */
1448 [ - + ]: 145270 : if (i == ARRAY_SIZE(ops)) {
1449 : 0 : return ovsdb_syntax_error(row_update, NULL,
1450 : : "<row_update2> includes unknown "
1451 : : "object");
1452 : : }
1453 : 145270 : break;
1454 : : }
1455 : :
1456 : : default:
1457 : 0 : OVS_NOT_REACHED();
1458 : : }
1459 : : }
1460 : : }
1461 : :
1462 : 24648 : return NULL;
1463 : : }
1464 : :
1465 : : static struct ovsdb_idl_row *
1466 : 687041 : ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid)
1467 : : {
1468 : : struct ovsdb_idl_row *row;
1469 : :
1470 [ + + ][ - + ]: 687041 : HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) {
1471 [ + - ]: 572521 : if (uuid_equals(&row->uuid, uuid)) {
1472 : 572521 : return row;
1473 : : }
1474 : : }
1475 : 114520 : return NULL;
1476 : : }
1477 : :
1478 : : /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1479 : : * otherwise. */
1480 : : static bool
1481 : 36 : ovsdb_idl_process_update(struct ovsdb_idl_table *table,
1482 : : const struct uuid *uuid, const struct json *old,
1483 : : const struct json *new)
1484 : : {
1485 : : struct ovsdb_idl_row *row;
1486 : :
1487 : 36 : row = ovsdb_idl_get_row(table, uuid);
1488 [ - + ]: 36 : if (!new) {
1489 : : /* Delete row. */
1490 [ # # ][ # # ]: 0 : if (row && !ovsdb_idl_row_is_orphan(row)) {
1491 : : /* XXX perhaps we should check the 'old' values? */
1492 : 0 : ovsdb_idl_delete_row(row);
1493 : : } else {
1494 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" "
1495 : : "from table %s",
1496 : : UUID_ARGS(uuid), table->class->name);
1497 : 0 : return false;
1498 : : }
1499 [ + + ]: 36 : } else if (!old) {
1500 : : /* Insert row. */
1501 [ + - ]: 22 : if (!row) {
1502 : 22 : ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
1503 [ # # ]: 0 : } else if (ovsdb_idl_row_is_orphan(row)) {
1504 : 0 : ovsdb_idl_insert_row(row, new);
1505 : : } else {
1506 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to "
1507 : : "table %s", UUID_ARGS(uuid), table->class->name);
1508 : 0 : return ovsdb_idl_modify_row(row, new);
1509 : : }
1510 : : } else {
1511 : : /* Modify row. */
1512 [ + - ]: 14 : if (row) {
1513 : : /* XXX perhaps we should check the 'old' values? */
1514 [ + - ]: 14 : if (!ovsdb_idl_row_is_orphan(row)) {
1515 : 14 : return ovsdb_idl_modify_row(row, new);
1516 : : } else {
1517 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot modify missing but "
1518 : : "referenced row "UUID_FMT" in table %s",
1519 : : UUID_ARGS(uuid), table->class->name);
1520 : 0 : ovsdb_idl_insert_row(row, new);
1521 : : }
1522 : : } else {
1523 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" "
1524 : : "in table %s", UUID_ARGS(uuid), table->class->name);
1525 : 0 : ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new);
1526 : : }
1527 : : }
1528 : :
1529 : 22 : return true;
1530 : : }
1531 : :
1532 : : /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1533 : : * otherwise. */
1534 : : static bool
1535 : 145270 : ovsdb_idl_process_update2(struct ovsdb_idl_table *table,
1536 : : const struct uuid *uuid,
1537 : : const char *operation,
1538 : : const struct json *json_row)
1539 : : {
1540 : : struct ovsdb_idl_row *row;
1541 : :
1542 : 145270 : row = ovsdb_idl_get_row(table, uuid);
1543 [ + + ]: 145270 : if (!strcmp(operation, "delete")) {
1544 : : /* Delete row. */
1545 [ + - ][ + - ]: 4782 : if (row && !ovsdb_idl_row_is_orphan(row)) {
1546 : 4782 : ovsdb_idl_delete_row(row);
1547 : : } else {
1548 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" "
1549 : : "from table %s",
1550 : : UUID_ARGS(uuid), table->class->name);
1551 : 0 : return false;
1552 : : }
1553 [ + + ][ + + ]: 140488 : } else if (!strcmp(operation, "insert") || !strcmp(operation, "initial")) {
1554 : : /* Insert row. */
1555 [ + + ]: 228224 : if (!row) {
1556 : 106805 : ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), json_row);
1557 [ + - ]: 7307 : } else if (ovsdb_idl_row_is_orphan(row)) {
1558 : 7307 : ovsdb_idl_insert_row(row, json_row);
1559 : : } else {
1560 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to "
1561 : : "table %s", UUID_ARGS(uuid), table->class->name);
1562 : 0 : ovsdb_idl_delete_row(row);
1563 : 0 : ovsdb_idl_insert_row(row, json_row);
1564 : : }
1565 [ + - ]: 26376 : } else if (!strcmp(operation, "modify")) {
1566 : : /* Modify row. */
1567 [ + - ]: 26376 : if (row) {
1568 [ + - ]: 26376 : if (!ovsdb_idl_row_is_orphan(row)) {
1569 : 26376 : return ovsdb_idl_modify_row_by_diff(row, json_row);
1570 : : } else {
1571 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot modify missing but "
1572 : : "referenced row "UUID_FMT" in table %s",
1573 : : UUID_ARGS(uuid), table->class->name);
1574 : 0 : return false;
1575 : : }
1576 : : } else {
1577 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" "
1578 : : "in table %s", UUID_ARGS(uuid), table->class->name);
1579 : 0 : return false;
1580 : : }
1581 : : } else {
1582 [ # # ]: 0 : VLOG_WARN_RL(&semantic_rl, "unknown operation %s to "
1583 : : "table %s", operation, table->class->name);
1584 : 0 : return false;
1585 : : }
1586 : :
1587 : 118894 : return true;
1588 : : }
1589 : :
1590 : : /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1591 : : * otherwise.
1592 : : *
1593 : : * Change 'row' either with the content of 'row_json' or by apply 'diff'.
1594 : : * Caller needs to provide either valid 'row_json' or 'diff', but not
1595 : : * both. */
1596 : : static bool
1597 : 140524 : ovsdb_idl_row_change__(struct ovsdb_idl_row *row, const struct json *row_json,
1598 : : const struct json *diff_json,
1599 : : enum ovsdb_idl_change change)
1600 : : {
1601 : 140524 : struct ovsdb_idl_table *table = row->table;
1602 : 140524 : const struct ovsdb_idl_table_class *class = table->class;
1603 : : struct shash_node *node;
1604 : 140524 : bool changed = false;
1605 : 140524 : bool apply_diff = diff_json != NULL;
1606 [ + + ]: 140524 : const struct json *json = apply_diff ? diff_json : row_json;
1607 : :
1608 [ + + ][ - + ]: 546812 : SHASH_FOR_EACH (node, json_object(json)) {
1609 : 406288 : const char *column_name = node->name;
1610 : : const struct ovsdb_idl_column *column;
1611 : : struct ovsdb_datum datum;
1612 : : struct ovsdb_error *error;
1613 : : unsigned int column_idx;
1614 : : struct ovsdb_datum *old;
1615 : :
1616 : 406288 : column = shash_find_data(&table->columns, column_name);
1617 [ - + ]: 406288 : if (!column) {
1618 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT,
1619 : : column_name, UUID_ARGS(&row->uuid));
1620 : 0 : continue;
1621 : : }
1622 : :
1623 : 406288 : column_idx = column - table->class->columns;
1624 : 406288 : old = &row->old[column_idx];
1625 : :
1626 : 406288 : error = NULL;
1627 [ + + ]: 406288 : if (apply_diff) {
1628 : : struct ovsdb_datum diff;
1629 : :
1630 [ - + ]: 45815 : ovs_assert(!row_json);
1631 : 45815 : error = ovsdb_transient_datum_from_json(&diff, &column->type,
1632 : 45815 : node->data);
1633 [ + - ]: 45815 : if (!error) {
1634 : 45815 : error = ovsdb_datum_apply_diff(&datum, old, &diff,
1635 : : &column->type);
1636 : 45815 : ovsdb_datum_destroy(&diff, &column->type);
1637 : : }
1638 : : } else {
1639 [ - + ]: 360473 : ovs_assert(!diff_json);
1640 : 360473 : error = ovsdb_datum_from_json(&datum, &column->type, node->data,
1641 : : NULL);
1642 : : }
1643 : :
1644 [ + - ]: 406288 : if (!error) {
1645 [ + + ]: 406288 : if (!ovsdb_datum_equals(old, &datum, &column->type)) {
1646 : 405992 : ovsdb_datum_swap(old, &datum);
1647 [ + + ]: 405992 : if (table->modes[column_idx] & OVSDB_IDL_ALERT) {
1648 : 343697 : changed = true;
1649 : : row->change_seqno[change]
1650 : 687394 : = row->table->change_seqno[change]
1651 : 343697 : = row->table->idl->change_seqno + 1;
1652 [ + + ]: 343697 : if (table->modes[column_idx] & OVSDB_IDL_TRACK) {
1653 [ + + ]: 51280 : if (!ovs_list_is_empty(&row->track_node)) {
1654 : 41702 : ovs_list_remove(&row->track_node);
1655 : : }
1656 : 51280 : ovs_list_push_back(&row->table->track_list,
1657 : : &row->track_node);
1658 [ + + ]: 51280 : if (!row->updated) {
1659 : 9578 : row->updated = bitmap_allocate(class->n_columns);
1660 : : }
1661 : 405992 : bitmap_set1(row->updated, column_idx);
1662 : : }
1663 : : }
1664 : : } else {
1665 : : /* Didn't really change but the OVSDB monitor protocol always
1666 : : * includes every value in a row. */
1667 : : }
1668 : :
1669 : 406288 : ovsdb_datum_destroy(&datum, &column->type);
1670 : : } else {
1671 : 0 : char *s = ovsdb_error_to_string(error);
1672 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT
1673 : : " in table %s: %s", column_name,
1674 : : UUID_ARGS(&row->uuid), table->class->name, s);
1675 : 0 : free(s);
1676 : 406288 : ovsdb_error_destroy(error);
1677 : : }
1678 : : }
1679 : 140524 : return changed;
1680 : : }
1681 : :
1682 : : static bool
1683 : 114148 : ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json,
1684 : : enum ovsdb_idl_change change)
1685 : : {
1686 : 114148 : return ovsdb_idl_row_change__(row, row_json, NULL, change);
1687 : : }
1688 : :
1689 : : static bool
1690 : 26376 : ovsdb_idl_row_apply_diff(struct ovsdb_idl_row *row,
1691 : : const struct json *diff_json,
1692 : : enum ovsdb_idl_change change)
1693 : : {
1694 : 26376 : return ovsdb_idl_row_change__(row, NULL, diff_json, change);
1695 : : }
1696 : :
1697 : : /* When a row A refers to row B through a column with a "refTable" constraint,
1698 : : * but row B does not exist, row B is called an "orphan row". Orphan rows
1699 : : * should not persist, because the database enforces referential integrity, but
1700 : : * they can appear transiently as changes from the database are received (the
1701 : : * database doesn't try to topologically sort them and circular references mean
1702 : : * it isn't always possible anyhow).
1703 : : *
1704 : : * This function returns true if 'row' is an orphan row, otherwise false.
1705 : : */
1706 : : static bool
1707 : 928412 : ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row)
1708 : : {
1709 [ + + ][ + - ]: 928412 : return !row->old && !row->new;
1710 : : }
1711 : :
1712 : : /* Returns true if 'row' is conceptually part of the database as modified by
1713 : : * the current transaction (if any), false otherwise.
1714 : : *
1715 : : * This function will return true if 'row' is not an orphan (see the comment on
1716 : : * ovsdb_idl_row_is_orphan()) and:
1717 : : *
1718 : : * - 'row' exists in the database and has not been deleted within the
1719 : : * current transaction (if any).
1720 : : *
1721 : : * - 'row' was inserted within the current transaction and has not been
1722 : : * deleted. (In the latter case you should not have passed 'row' in at
1723 : : * all, because ovsdb_idl_txn_delete() freed it.)
1724 : : *
1725 : : * This function will return false if 'row' is an orphan or if 'row' was
1726 : : * deleted within the current transaction.
1727 : : */
1728 : : static bool
1729 : 2167612 : ovsdb_idl_row_exists(const struct ovsdb_idl_row *row)
1730 : : {
1731 : 2167612 : return row->new != NULL;
1732 : : }
1733 : :
1734 : : static void
1735 : 185975 : ovsdb_idl_row_parse(struct ovsdb_idl_row *row)
1736 : : {
1737 : 185975 : const struct ovsdb_idl_table_class *class = row->table->class;
1738 : : size_t i;
1739 : :
1740 [ + + ]: 4129755 : for (i = 0; i < class->n_columns; i++) {
1741 : 3943780 : const struct ovsdb_idl_column *c = &class->columns[i];
1742 : 3943780 : (c->parse)(row, &row->old[i]);
1743 : : }
1744 : 185975 : }
1745 : :
1746 : : static void
1747 : 202921 : ovsdb_idl_row_unparse(struct ovsdb_idl_row *row)
1748 : : {
1749 : 202921 : const struct ovsdb_idl_table_class *class = row->table->class;
1750 : : size_t i;
1751 : :
1752 [ + + ]: 4430887 : for (i = 0; i < class->n_columns; i++) {
1753 : 4227966 : const struct ovsdb_idl_column *c = &class->columns[i];
1754 : 4227966 : (c->unparse)(row);
1755 : : }
1756 : 202921 : }
1757 : :
1758 : : static void
1759 : 229478 : ovsdb_idl_row_clear_old(struct ovsdb_idl_row *row)
1760 : : {
1761 [ - + ]: 229478 : ovs_assert(row->old == row->new);
1762 [ + + ]: 229478 : if (!ovsdb_idl_row_is_orphan(row)) {
1763 : 114131 : const struct ovsdb_idl_table_class *class = row->table->class;
1764 : : size_t i;
1765 : :
1766 [ + + ]: 2448542 : for (i = 0; i < class->n_columns; i++) {
1767 : 2334411 : ovsdb_datum_destroy(&row->old[i], &class->columns[i].type);
1768 : : }
1769 : 114131 : free(row->old);
1770 : 114131 : row->old = row->new = NULL;
1771 : : }
1772 : 229478 : }
1773 : :
1774 : : static void
1775 : 82117 : ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row)
1776 : : {
1777 [ + + ]: 82117 : if (row->old != row->new) {
1778 [ + + ]: 51740 : if (row->new) {
1779 : 48198 : const struct ovsdb_idl_table_class *class = row->table->class;
1780 : : size_t i;
1781 : :
1782 [ + + ]: 48198 : if (row->written) {
1783 [ + + ]: 184719 : BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) {
1784 : 137245 : ovsdb_datum_destroy(&row->new[i], &class->columns[i].type);
1785 : : }
1786 : : }
1787 : 48198 : free(row->new);
1788 : 48198 : free(row->written);
1789 : 48198 : row->written = NULL;
1790 : : }
1791 : 51740 : row->new = row->old;
1792 : : }
1793 : 82117 : }
1794 : :
1795 : : static void
1796 : 76623 : ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts)
1797 : : {
1798 : : struct ovsdb_idl_arc *arc, *next;
1799 : :
1800 : : /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows
1801 : : * that this causes to be unreferenced, if tracking is not enabled.
1802 : : * If tracking is enabled, orphaned nodes are removed from hmap but not
1803 : : * freed.
1804 : : */
1805 [ + + ][ + + ]: 349864 : LIST_FOR_EACH_SAFE (arc, next, src_node, &row->src_arcs) {
1806 : 273241 : ovs_list_remove(&arc->dst_node);
1807 [ + + ]: 273241 : if (destroy_dsts
1808 [ + + ]: 65669 : && ovsdb_idl_row_is_orphan(arc->dst)
1809 [ + + ]: 4131 : && ovs_list_is_empty(&arc->dst->dst_arcs)) {
1810 : 3507 : ovsdb_idl_row_destroy(arc->dst);
1811 : : }
1812 : 273241 : free(arc);
1813 : : }
1814 : 76623 : ovs_list_init(&row->src_arcs);
1815 : 76623 : }
1816 : :
1817 : : /* Force nodes that reference 'row' to reparse. */
1818 : : static void
1819 : 117594 : ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row)
1820 : : {
1821 : : struct ovsdb_idl_arc *arc, *next;
1822 : :
1823 : : /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy
1824 : : * 'arc', so we need to use the "safe" variant of list traversal. However,
1825 : : * calling an ovsdb_idl_column's 'parse' function will add an arc
1826 : : * equivalent to 'arc' to row->arcs. That could be a problem for
1827 : : * traversal, but it adds it at the beginning of the list to prevent us
1828 : : * from stumbling upon it again.
1829 : : *
1830 : : * (If duplicate arcs were possible then we would need to make sure that
1831 : : * 'next' didn't also point into 'arc''s destination, but we forbid
1832 : : * duplicate arcs.) */
1833 [ + + ][ + + ]: 131825 : LIST_FOR_EACH_SAFE (arc, next, dst_node, &row->dst_arcs) {
1834 : 14231 : struct ovsdb_idl_row *ref = arc->src;
1835 : :
1836 : 14231 : ovsdb_idl_row_unparse(ref);
1837 : 14231 : ovsdb_idl_row_clear_arcs(ref, false);
1838 : 14231 : ovsdb_idl_row_parse(ref);
1839 : : }
1840 : 117594 : }
1841 : :
1842 : : static struct ovsdb_idl_row *
1843 : 131396 : ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class)
1844 : : {
1845 : 131396 : struct ovsdb_idl_row *row = xzalloc(class->allocation_size);
1846 : 131396 : class->row_init(row);
1847 : 131396 : ovs_list_init(&row->src_arcs);
1848 : 131396 : ovs_list_init(&row->dst_arcs);
1849 : 131396 : hmap_node_nullify(&row->txn_node);
1850 : 131396 : ovs_list_init(&row->track_node);
1851 : 131396 : return row;
1852 : : }
1853 : :
1854 : : static struct ovsdb_idl_row *
1855 : 114447 : ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid)
1856 : : {
1857 : 114447 : struct ovsdb_idl_row *row = ovsdb_idl_row_create__(table->class);
1858 : 114447 : hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid));
1859 : 114447 : row->uuid = *uuid;
1860 : 114447 : row->table = table;
1861 : 114447 : row->map_op_written = NULL;
1862 : 114447 : row->map_op_lists = NULL;
1863 : 114447 : row->set_op_written = NULL;
1864 : 114447 : row->set_op_lists = NULL;
1865 : 114447 : return row;
1866 : : }
1867 : :
1868 : : static void
1869 : 114444 : ovsdb_idl_row_destroy(struct ovsdb_idl_row *row)
1870 : : {
1871 [ + - ]: 114444 : if (row) {
1872 : 114444 : ovsdb_idl_row_clear_old(row);
1873 : 114444 : hmap_remove(&row->table->rows, &row->hmap_node);
1874 : 114444 : ovsdb_idl_destroy_all_map_op_lists(row);
1875 : 114444 : ovsdb_idl_destroy_all_set_op_lists(row);
1876 [ + + ]: 114444 : if (ovsdb_idl_track_is_set(row->table)) {
1877 : : row->change_seqno[OVSDB_IDL_CHANGE_DELETE]
1878 : 9011 : = row->table->change_seqno[OVSDB_IDL_CHANGE_DELETE]
1879 : 9011 : = row->table->idl->change_seqno + 1;
1880 : : }
1881 [ + + ]: 114444 : if (!ovs_list_is_empty(&row->track_node)) {
1882 : 131 : ovs_list_remove(&row->track_node);
1883 : : }
1884 : 114444 : ovs_list_push_back(&row->table->track_list, &row->track_node);
1885 : : }
1886 : 114444 : }
1887 : :
1888 : : static void
1889 : 192996 : ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row *row)
1890 : : {
1891 [ + + ]: 192996 : if (row->map_op_written) {
1892 : : /* Clear Map Operation Lists */
1893 : : size_t idx, n_columns;
1894 : : const struct ovsdb_idl_column *columns;
1895 : : const struct ovsdb_type *type;
1896 : 4 : n_columns = row->table->class->n_columns;
1897 : 4 : columns = row->table->class->columns;
1898 [ + + ]: 9 : BITMAP_FOR_EACH_1 (idx, n_columns, row->map_op_written) {
1899 : 5 : type = &columns[idx].type;
1900 : 5 : map_op_list_destroy(row->map_op_lists[idx], type);
1901 : : }
1902 : 4 : free(row->map_op_lists);
1903 : 4 : bitmap_free(row->map_op_written);
1904 : 4 : row->map_op_lists = NULL;
1905 : 4 : row->map_op_written = NULL;
1906 : : }
1907 : 192996 : }
1908 : :
1909 : : static void
1910 : 192996 : ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row *row)
1911 : : {
1912 [ + + ]: 192996 : if (row->set_op_written) {
1913 : : /* Clear Set Operation Lists */
1914 : : size_t idx, n_columns;
1915 : : const struct ovsdb_idl_column *columns;
1916 : : const struct ovsdb_type *type;
1917 : 54 : n_columns = row->table->class->n_columns;
1918 : 54 : columns = row->table->class->columns;
1919 [ + + ]: 108 : BITMAP_FOR_EACH_1 (idx, n_columns, row->set_op_written) {
1920 : 54 : type = &columns[idx].type;
1921 : 54 : set_op_list_destroy(row->set_op_lists[idx], type);
1922 : : }
1923 : 54 : free(row->set_op_lists);
1924 : 54 : bitmap_free(row->set_op_written);
1925 : 54 : row->set_op_lists = NULL;
1926 : 54 : row->set_op_written = NULL;
1927 : : }
1928 : 192996 : }
1929 : :
1930 : : static void
1931 : 147927 : ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *idl)
1932 : : {
1933 : : size_t i;
1934 : :
1935 [ + + ]: 2444816 : for (i = 0; i < idl->class->n_tables; i++) {
1936 : 2296889 : struct ovsdb_idl_table *table = &idl->tables[i];
1937 : :
1938 [ + + ]: 2296889 : if (!ovs_list_is_empty(&table->track_list)) {
1939 : : struct ovsdb_idl_row *row, *next;
1940 : :
1941 [ + + ][ + + ]: 23886 : LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) {
1942 [ + + ]: 20361 : if (!ovsdb_idl_track_is_set(row->table)) {
1943 : 4192 : ovs_list_remove(&row->track_node);
1944 : 4192 : free(row);
1945 : : }
1946 : : }
1947 : : }
1948 : : }
1949 : 147927 : }
1950 : :
1951 : : static void
1952 : 114134 : ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json)
1953 : : {
1954 : 114134 : const struct ovsdb_idl_table_class *class = row->table->class;
1955 : : size_t i;
1956 : :
1957 [ + - ][ - + ]: 114134 : ovs_assert(!row->old && !row->new);
1958 : 114134 : row->old = row->new = xmalloc(class->n_columns * sizeof *row->old);
1959 [ + + ]: 2448552 : for (i = 0; i < class->n_columns; i++) {
1960 : 2334418 : ovsdb_datum_init_default(&row->old[i], &class->columns[i].type);
1961 : : }
1962 : 114134 : ovsdb_idl_row_update(row, row_json, OVSDB_IDL_CHANGE_INSERT);
1963 : 114134 : ovsdb_idl_row_parse(row);
1964 : :
1965 : 114134 : ovsdb_idl_row_reparse_backrefs(row);
1966 : 114134 : }
1967 : :
1968 : : static void
1969 : 4782 : ovsdb_idl_delete_row(struct ovsdb_idl_row *row)
1970 : : {
1971 : 4782 : ovsdb_idl_row_unparse(row);
1972 : 4782 : ovsdb_idl_row_clear_arcs(row, true);
1973 : 4782 : ovsdb_idl_row_clear_old(row);
1974 [ + + ]: 4782 : if (ovs_list_is_empty(&row->dst_arcs)) {
1975 : 1322 : ovsdb_idl_row_destroy(row);
1976 : : } else {
1977 : 3460 : ovsdb_idl_row_reparse_backrefs(row);
1978 : : }
1979 : 4782 : }
1980 : :
1981 : : /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1982 : : * otherwise. */
1983 : : static bool
1984 : 14 : ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json)
1985 : : {
1986 : : bool changed;
1987 : :
1988 : 14 : ovsdb_idl_row_unparse(row);
1989 : 14 : ovsdb_idl_row_clear_arcs(row, true);
1990 : 14 : changed = ovsdb_idl_row_update(row, row_json, OVSDB_IDL_CHANGE_MODIFY);
1991 : 14 : ovsdb_idl_row_parse(row);
1992 : :
1993 : 14 : return changed;
1994 : : }
1995 : :
1996 : : static bool
1997 : 26376 : ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row *row,
1998 : : const struct json *diff_json)
1999 : : {
2000 : : bool changed;
2001 : :
2002 : 26376 : ovsdb_idl_row_unparse(row);
2003 : 26376 : ovsdb_idl_row_clear_arcs(row, true);
2004 : 26376 : changed = ovsdb_idl_row_apply_diff(row, diff_json,
2005 : : OVSDB_IDL_CHANGE_MODIFY);
2006 : 26376 : ovsdb_idl_row_parse(row);
2007 : :
2008 : 26376 : return changed;
2009 : : }
2010 : :
2011 : : static bool
2012 : 365472 : may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst)
2013 : : {
2014 : : const struct ovsdb_idl_arc *arc;
2015 : :
2016 : : /* No self-arcs. */
2017 [ + + ]: 365472 : if (src == dst) {
2018 : 29 : return false;
2019 : : }
2020 : :
2021 : : /* No duplicate arcs.
2022 : : *
2023 : : * We only need to test whether the first arc in dst->dst_arcs originates
2024 : : * at 'src', since we add all of the arcs from a given source in a clump
2025 : : * (in a single call to ovsdb_idl_row_parse()) and new arcs are always
2026 : : * added at the front of the dst_arcs list. */
2027 [ + + ]: 365443 : if (ovs_list_is_empty(&dst->dst_arcs)) {
2028 : 339864 : return true;
2029 : : }
2030 : 25579 : arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node);
2031 : 25579 : return arc->src != src;
2032 : : }
2033 : :
2034 : : static struct ovsdb_idl_table *
2035 : 1024783 : ovsdb_idl_table_from_class(const struct ovsdb_idl *idl,
2036 : : const struct ovsdb_idl_table_class *table_class)
2037 : : {
2038 : 1024783 : return &idl->tables[table_class - idl->class->tables];
2039 : : }
2040 : :
2041 : : /* Called by ovsdb-idlc generated code. */
2042 : : struct ovsdb_idl_row *
2043 : 538974 : ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src,
2044 : : struct ovsdb_idl_table_class *dst_table_class,
2045 : : const struct uuid *dst_uuid)
2046 : : {
2047 : 538974 : struct ovsdb_idl *idl = src->table->idl;
2048 : : struct ovsdb_idl_table *dst_table;
2049 : : struct ovsdb_idl_arc *arc;
2050 : : struct ovsdb_idl_row *dst;
2051 : :
2052 : 538974 : dst_table = ovsdb_idl_table_from_class(idl, dst_table_class);
2053 : 538974 : dst = ovsdb_idl_get_row(dst_table, dst_uuid);
2054 [ + + ]: 538974 : if (idl->txn) {
2055 : : /* We're being called from ovsdb_idl_txn_write(). We must not update
2056 : : * any arcs, because the transaction will be backed out at commit or
2057 : : * abort time and we don't want our graph screwed up.
2058 : : *
2059 : : * Just return the destination row, if there is one and it has not been
2060 : : * deleted. */
2061 [ + + ][ + + ]: 173502 : if (dst && (hmap_node_is_null(&dst->txn_node) || dst->new)) {
[ + - ]
2062 : 173430 : return dst;
2063 : : }
2064 : 72 : return NULL;
2065 : : } else {
2066 : : /* We're being called from some other context. Update the graph. */
2067 [ + + ]: 365472 : if (!dst) {
2068 : 7620 : dst = ovsdb_idl_row_create(dst_table, dst_uuid);
2069 : : }
2070 : :
2071 : : /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */
2072 [ + + ]: 365472 : if (may_add_arc(src, dst)) {
2073 : : /* The arc *must* be added at the front of the dst_arcs list. See
2074 : : * ovsdb_idl_row_reparse_backrefs() for details. */
2075 : 365212 : arc = xmalloc(sizeof *arc);
2076 : 365212 : ovs_list_push_front(&src->src_arcs, &arc->src_node);
2077 : 365212 : ovs_list_push_front(&dst->dst_arcs, &arc->dst_node);
2078 : 365212 : arc->src = src;
2079 : 365212 : arc->dst = dst;
2080 : : }
2081 : :
2082 [ + + ]: 365472 : return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL;
2083 : : }
2084 : : }
2085 : :
2086 : : /* Searches 'tc''s table in 'idl' for a row with UUID 'uuid'. Returns a
2087 : : * pointer to the row if there is one, otherwise a null pointer. */
2088 : : const struct ovsdb_idl_row *
2089 : 2761 : ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl *idl,
2090 : : const struct ovsdb_idl_table_class *tc,
2091 : : const struct uuid *uuid)
2092 : : {
2093 : 2761 : return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl, tc), uuid);
2094 : : }
2095 : :
2096 : : static struct ovsdb_idl_row *
2097 : 2271381 : next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node)
2098 : : {
2099 [ + + ]: 2271403 : for (; node; node = hmap_next(&table->rows, node)) {
2100 : : struct ovsdb_idl_row *row;
2101 : :
2102 : 2167612 : row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node);
2103 [ + + ]: 2167612 : if (ovsdb_idl_row_exists(row)) {
2104 : 2167590 : return row;
2105 : : }
2106 : : }
2107 : 103791 : return NULL;
2108 : : }
2109 : :
2110 : : /* Returns a row in 'table_class''s table in 'idl', or a null pointer if that
2111 : : * table is empty.
2112 : : *
2113 : : * Database tables are internally maintained as hash tables, so adding or
2114 : : * removing rows while traversing the same table can cause some rows to be
2115 : : * visited twice or not at apply. */
2116 : : const struct ovsdb_idl_row *
2117 : 466056 : ovsdb_idl_first_row(const struct ovsdb_idl *idl,
2118 : : const struct ovsdb_idl_table_class *table_class)
2119 : : {
2120 : 466056 : struct ovsdb_idl_table *table
2121 : : = ovsdb_idl_table_from_class(idl, table_class);
2122 : 466056 : return next_real_row(table, hmap_first(&table->rows));
2123 : : }
2124 : :
2125 : : /* Returns a row following 'row' within its table, or a null pointer if 'row'
2126 : : * is the last row in its table. */
2127 : : const struct ovsdb_idl_row *
2128 : 1805325 : ovsdb_idl_next_row(const struct ovsdb_idl_row *row)
2129 : : {
2130 : 1805325 : struct ovsdb_idl_table *table = row->table;
2131 : :
2132 : 1805325 : return next_real_row(table, hmap_next(&table->rows, &row->hmap_node));
2133 : : }
2134 : :
2135 : : /* Reads and returns the value of 'column' within 'row'. If an ongoing
2136 : : * transaction has changed 'column''s value, the modified value is returned.
2137 : : *
2138 : : * The caller must not modify or free the returned value.
2139 : : *
2140 : : * Various kinds of changes can invalidate the returned value: writing to the
2141 : : * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row'
2142 : : * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction
2143 : : * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the
2144 : : * returned value is needed for a long time, it is best to make a copy of it
2145 : : * with ovsdb_datum_clone(). */
2146 : : const struct ovsdb_datum *
2147 : 1077395 : ovsdb_idl_read(const struct ovsdb_idl_row *row,
2148 : : const struct ovsdb_idl_column *column)
2149 : : {
2150 : : const struct ovsdb_idl_table_class *class;
2151 : : size_t column_idx;
2152 : :
2153 [ - + ]: 1077395 : ovs_assert(!ovsdb_idl_row_is_synthetic(row));
2154 : :
2155 : 1077395 : class = row->table->class;
2156 : 1077395 : column_idx = column - class->columns;
2157 : :
2158 [ - + ]: 1077395 : ovs_assert(row->new != NULL);
2159 [ - + ]: 1077395 : ovs_assert(column_idx < class->n_columns);
2160 : :
2161 [ + + ][ + + ]: 1077395 : if (row->written && bitmap_is_set(row->written, column_idx)) {
2162 : 5453 : return &row->new[column_idx];
2163 [ + + ]: 1071942 : } else if (row->old) {
2164 : 1036048 : return &row->old[column_idx];
2165 : : } else {
2166 : 35894 : return ovsdb_datum_default(&column->type);
2167 : : }
2168 : : }
2169 : :
2170 : : /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key
2171 : : * type 'key_type' and value type 'value_type'. (Scalar and set types will
2172 : : * have a value type of OVSDB_TYPE_VOID.)
2173 : : *
2174 : : * This is useful in code that "knows" that a particular column has a given
2175 : : * type, so that it will abort if someone changes the column's type without
2176 : : * updating the code that uses it. */
2177 : : const struct ovsdb_datum *
2178 : 35022 : ovsdb_idl_get(const struct ovsdb_idl_row *row,
2179 : : const struct ovsdb_idl_column *column,
2180 : : enum ovsdb_atomic_type key_type OVS_UNUSED,
2181 : : enum ovsdb_atomic_type value_type OVS_UNUSED)
2182 : : {
2183 [ - + ]: 35022 : ovs_assert(column->type.key.type == key_type);
2184 [ - + ]: 35022 : ovs_assert(column->type.value.type == value_type);
2185 : :
2186 : 35022 : return ovsdb_idl_read(row, column);
2187 : : }
2188 : :
2189 : : /* Returns true if the field represented by 'column' in 'row' may be modified,
2190 : : * false if it is immutable.
2191 : : *
2192 : : * Normally, whether a field is mutable is controlled by its column's schema.
2193 : : * However, an immutable column can be set to any initial value at the time of
2194 : : * insertion, so if 'row' is a new row (one that is being added as part of the
2195 : : * current transaction, supposing that a transaction is in progress) then even
2196 : : * its "immutable" fields are actually mutable. */
2197 : : bool
2198 : 8456 : ovsdb_idl_is_mutable(const struct ovsdb_idl_row *row,
2199 : : const struct ovsdb_idl_column *column)
2200 : : {
2201 [ + + ][ + - ]: 8456 : return column->mutable || (row->new && !row->old);
[ + + ]
2202 : : }
2203 : :
2204 : : /* Returns false if 'row' was obtained from the IDL, true if it was initialized
2205 : : * to all-zero-bits by some other entity. If 'row' was set up some other way
2206 : : * then the return value is indeterminate. */
2207 : : bool
2208 : 2468273 : ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row *row)
2209 : : {
2210 : 2468273 : return row->table == NULL;
2211 : : }
2212 : :
2213 : : /* Transactions. */
2214 : :
2215 : : static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn,
2216 : : enum ovsdb_idl_txn_status);
2217 : :
2218 : : /* Returns a string representation of 'status'. The caller must not modify or
2219 : : * free the returned string.
2220 : : *
2221 : : * The return value is probably useful only for debug log messages and unit
2222 : : * tests. */
2223 : : const char *
2224 : 6141 : ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status)
2225 : : {
2226 [ - + - + : 6141 : switch (status) {
+ + - -
- ]
2227 : : case TXN_UNCOMMITTED:
2228 : 0 : return "uncommitted";
2229 : : case TXN_UNCHANGED:
2230 : 2165 : return "unchanged";
2231 : : case TXN_INCOMPLETE:
2232 : 0 : return "incomplete";
2233 : : case TXN_ABORTED:
2234 : 1 : return "aborted";
2235 : : case TXN_SUCCESS:
2236 : 3974 : return "success";
2237 : : case TXN_TRY_AGAIN:
2238 : 1 : return "try again";
2239 : : case TXN_NOT_LOCKED:
2240 : 0 : return "not locked";
2241 : : case TXN_ERROR:
2242 : 0 : return "error";
2243 : : }
2244 : 0 : return "<unknown>";
2245 : : }
2246 : :
2247 : : /* Starts a new transaction on 'idl'. A given ovsdb_idl may only have a single
2248 : : * active transaction at a time. See the large comment in ovsdb-idl.h for
2249 : : * general information on transactions. */
2250 : : struct ovsdb_idl_txn *
2251 : 29266 : ovsdb_idl_txn_create(struct ovsdb_idl *idl)
2252 : : {
2253 : : struct ovsdb_idl_txn *txn;
2254 : :
2255 [ - + ]: 29266 : ovs_assert(!idl->txn);
2256 : 29266 : idl->txn = txn = xmalloc(sizeof *txn);
2257 : 29266 : txn->request_id = NULL;
2258 : 29266 : txn->idl = idl;
2259 : 29266 : hmap_init(&txn->txn_rows);
2260 : 29266 : txn->status = TXN_UNCOMMITTED;
2261 : 29266 : txn->error = NULL;
2262 : 29266 : txn->dry_run = false;
2263 : 29266 : ds_init(&txn->comment);
2264 : :
2265 : 29266 : txn->inc_table = NULL;
2266 : 29266 : txn->inc_column = NULL;
2267 : :
2268 : 29266 : hmap_init(&txn->inserted_rows);
2269 : :
2270 : 29266 : return txn;
2271 : : }
2272 : :
2273 : : /* Appends 's', which is treated as a printf()-type format string, to the
2274 : : * comments that will be passed to the OVSDB server when 'txn' is committed.
2275 : : * (The comment will be committed to the OVSDB log, which "ovsdb-tool
2276 : : * show-log" can print in a relatively human-readable form.) */
2277 : : void
2278 : 10636 : ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...)
2279 : : {
2280 : : va_list args;
2281 : :
2282 [ + + ]: 10636 : if (txn->comment.length) {
2283 : 733 : ds_put_char(&txn->comment, '\n');
2284 : : }
2285 : :
2286 : 10636 : va_start(args, s);
2287 : 10636 : ds_put_format_valist(&txn->comment, s, args);
2288 : 10636 : va_end(args);
2289 : 10636 : }
2290 : :
2291 : : /* Marks 'txn' as a transaction that will not actually modify the database. In
2292 : : * almost every way, the transaction is treated like other transactions. It
2293 : : * must be committed or aborted like other transactions, it will be sent to the
2294 : : * database server like other transactions, and so on. The only difference is
2295 : : * that the operations sent to the database server will include, as the last
2296 : : * step, an "abort" operation, so that any changes made by the transaction will
2297 : : * not actually take effect. */
2298 : : void
2299 : 0 : ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn *txn)
2300 : : {
2301 : 0 : txn->dry_run = true;
2302 : 0 : }
2303 : :
2304 : : /* Causes 'txn', when committed, to increment the value of 'column' within
2305 : : * 'row' by 1. 'column' must have an integer type. After 'txn' commits
2306 : : * successfully, the client may retrieve the final (incremented) value of
2307 : : * 'column' with ovsdb_idl_txn_get_increment_new_value().
2308 : : *
2309 : : * If at time of commit the transaction is otherwise empty, that is, it doesn't
2310 : : * change the database, then 'force' is important. If 'force' is false in this
2311 : : * case, the IDL suppresses the increment and skips a round trip to the
2312 : : * database server. If 'force' is true, the IDL will still increment the
2313 : : * column.
2314 : : *
2315 : : * The client could accomplish something similar with ovsdb_idl_read(),
2316 : : * ovsdb_idl_txn_verify() and ovsdb_idl_txn_write(), or with ovsdb-idlc
2317 : : * generated wrappers for these functions. However, ovsdb_idl_txn_increment()
2318 : : * will never (by itself) fail because of a verify error.
2319 : : *
2320 : : * The intended use is for incrementing the "next_cfg" column in the
2321 : : * Open_vSwitch table. */
2322 : : void
2323 : 3145 : ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn,
2324 : : const struct ovsdb_idl_row *row,
2325 : : const struct ovsdb_idl_column *column,
2326 : : bool force)
2327 : : {
2328 [ - + ]: 3145 : ovs_assert(!txn->inc_table);
2329 [ - + ]: 3145 : ovs_assert(column->type.key.type == OVSDB_TYPE_INTEGER);
2330 [ - + ]: 3145 : ovs_assert(column->type.value.type == OVSDB_TYPE_VOID);
2331 : :
2332 : 3145 : txn->inc_table = row->table->class->name;
2333 : 3145 : txn->inc_column = column->name;
2334 : 3145 : txn->inc_row = row->uuid;
2335 : 3145 : txn->inc_force = force;
2336 : 3145 : }
2337 : :
2338 : : /* Destroys 'txn' and frees all associated memory. If ovsdb_idl_txn_commit()
2339 : : * has been called for 'txn' but the commit is still incomplete (that is, the
2340 : : * last call returned TXN_INCOMPLETE) then the transaction may or may not still
2341 : : * end up committing at the database server, but the client will not be able to
2342 : : * get any further status information back. */
2343 : : void
2344 : 29266 : ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn)
2345 : : {
2346 : : struct ovsdb_idl_txn_insert *insert, *next;
2347 : :
2348 : 29266 : json_destroy(txn->request_id);
2349 [ + + ]: 29266 : if (txn->status == TXN_INCOMPLETE) {
2350 : 2335 : hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node);
2351 : : }
2352 : 29266 : ovsdb_idl_txn_abort(txn);
2353 : 29266 : ds_destroy(&txn->comment);
2354 : 29266 : free(txn->error);
2355 [ + + ][ - + ]: 46192 : HMAP_FOR_EACH_SAFE (insert, next, hmap_node, &txn->inserted_rows) {
[ + + ]
2356 : 16926 : free(insert);
2357 : : }
2358 : 29266 : hmap_destroy(&txn->inserted_rows);
2359 : 29266 : free(txn);
2360 : 29266 : }
2361 : :
2362 : : /* Causes poll_block() to wake up if 'txn' has completed committing. */
2363 : : void
2364 : 9211 : ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *txn)
2365 : : {
2366 [ + - ][ + + ]: 9211 : if (txn->status != TXN_UNCOMMITTED && txn->status != TXN_INCOMPLETE) {
2367 : 3981 : poll_immediate_wake();
2368 : : }
2369 : 9211 : }
2370 : :
2371 : : static struct json *
2372 : 65755 : where_uuid_equals(const struct uuid *uuid)
2373 : : {
2374 : : return
2375 : 65755 : json_array_create_1(
2376 : : json_array_create_3(
2377 : : json_string_create("_uuid"),
2378 : : json_string_create("=="),
2379 : : json_array_create_2(
2380 : : json_string_create("uuid"),
2381 : : json_string_create_nocopy(
2382 : 197265 : xasprintf(UUID_FMT, UUID_ARGS(uuid))))));
2383 : : }
2384 : :
2385 : : static char *
2386 : 29131 : uuid_name_from_uuid(const struct uuid *uuid)
2387 : : {
2388 : : char *name;
2389 : : char *p;
2390 : :
2391 : 29131 : name = xasprintf("row"UUID_FMT, UUID_ARGS(uuid));
2392 [ + + ]: 1165240 : for (p = name; *p != '\0'; p++) {
2393 [ + + ]: 1136109 : if (*p == '-') {
2394 : 116524 : *p = '_';
2395 : : }
2396 : : }
2397 : :
2398 : 29131 : return name;
2399 : : }
2400 : :
2401 : : static const struct ovsdb_idl_row *
2402 : 43890 : ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid)
2403 : : {
2404 : : const struct ovsdb_idl_row *row;
2405 : :
2406 [ + + ][ - + ]: 43890 : HMAP_FOR_EACH_WITH_HASH (row, txn_node, uuid_hash(uuid), &txn->txn_rows) {
2407 [ + - ]: 38010 : if (uuid_equals(&row->uuid, uuid)) {
2408 : 38010 : return row;
2409 : : }
2410 : : }
2411 : 5880 : return NULL;
2412 : : }
2413 : :
2414 : : /* XXX there must be a cleaner way to do this */
2415 : : static struct json *
2416 : 424809 : substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
2417 : : {
2418 [ + + ]: 424809 : if (json->type == JSON_ARRAY) {
2419 : : struct uuid uuid;
2420 : : size_t i;
2421 : :
2422 [ + + ]: 133187 : if (json->u.array.n == 2
2423 [ + + ]: 108004 : && json->u.array.elems[0]->type == JSON_STRING
2424 [ + + ]: 105386 : && json->u.array.elems[1]->type == JSON_STRING
2425 [ + + ]: 59422 : && !strcmp(json->u.array.elems[0]->u.string, "uuid")
2426 [ + - ]: 43782 : && uuid_from_string(&uuid, json->u.array.elems[1]->u.string)) {
2427 : : const struct ovsdb_idl_row *row;
2428 : :
2429 : 43782 : row = ovsdb_idl_txn_get_row(txn, &uuid);
2430 [ + + ][ + + ]: 43782 : if (row && !row->old && row->new) {
[ + - ]
2431 : 12205 : json_destroy(json);
2432 : :
2433 : 12205 : return json_array_create_2(
2434 : : json_string_create("named-uuid"),
2435 : : json_string_create_nocopy(uuid_name_from_uuid(&uuid)));
2436 : : }
2437 : : }
2438 : :
2439 [ + + ]: 406842 : for (i = 0; i < json->u.array.n; i++) {
2440 : 285860 : json->u.array.elems[i] = substitute_uuids(json->u.array.elems[i],
2441 : : txn);
2442 : : }
2443 [ + + ]: 291622 : } else if (json->type == JSON_OBJECT) {
2444 : : struct shash_node *node;
2445 : :
2446 [ + + ][ - + ]: 275 : SHASH_FOR_EACH (node, json_object(json)) {
2447 : 220 : node->data = substitute_uuids(node->data, txn);
2448 : : }
2449 : : }
2450 : 412604 : return json;
2451 : : }
2452 : :
2453 : : static void
2454 : 58531 : ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn)
2455 : : {
2456 : : struct ovsdb_idl_row *row, *next;
2457 : :
2458 : : /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an
2459 : : * ovsdb_idl_column's 'parse' function, which will call
2460 : : * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a
2461 : : * transaction and fail to update the graph. */
2462 : 58531 : txn->idl->txn = NULL;
2463 : :
2464 [ + + ][ - + ]: 137083 : HMAP_FOR_EACH_SAFE (row, next, txn_node, &txn->txn_rows) {
[ + + ]
2465 : 78552 : ovsdb_idl_destroy_all_map_op_lists(row);
2466 : 78552 : ovsdb_idl_destroy_all_set_op_lists(row);
2467 [ + + ]: 78552 : if (row->old) {
2468 [ + + ]: 61626 : if (row->written) {
2469 : 31220 : ovsdb_idl_row_unparse(row);
2470 : 31220 : ovsdb_idl_row_clear_arcs(row, false);
2471 : 61626 : ovsdb_idl_row_parse(row);
2472 : : }
2473 : : } else {
2474 : 16926 : ovsdb_idl_row_unparse(row);
2475 : : }
2476 : 78552 : ovsdb_idl_row_clear_new(row);
2477 : :
2478 : 78552 : free(row->prereqs);
2479 : 78552 : row->prereqs = NULL;
2480 : :
2481 : 78552 : free(row->written);
2482 : 78552 : row->written = NULL;
2483 : :
2484 : 78552 : hmap_remove(&txn->txn_rows, &row->txn_node);
2485 : 78552 : hmap_node_nullify(&row->txn_node);
2486 [ + + ]: 78552 : if (!row->old) {
2487 : 16926 : hmap_remove(&row->table->rows, &row->hmap_node);
2488 : 16926 : free(row);
2489 : : }
2490 : : }
2491 : 58531 : hmap_destroy(&txn->txn_rows);
2492 : 58531 : hmap_init(&txn->txn_rows);
2493 : 58531 : }
2494 : :
2495 : : static bool
2496 : 58 : ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row,
2497 : : struct json *mutations)
2498 : : {
2499 : 58 : const struct ovsdb_idl_table_class *class = row->table->class;
2500 : : size_t idx;
2501 : 58 : bool any_mutations = false;
2502 : :
2503 [ + + ]: 58 : if (row->map_op_written) {
2504 [ + + ]: 9 : BITMAP_FOR_EACH_1(idx, class->n_columns, row->map_op_written) {
2505 : : struct map_op_list *map_op_list;
2506 : : const struct ovsdb_idl_column *column;
2507 : : const struct ovsdb_datum *old_datum;
2508 : : enum ovsdb_atomic_type key_type, value_type;
2509 : : struct json *mutation, *map, *col_name, *mutator;
2510 : : struct json *del_set, *ins_map;
2511 : : bool any_del, any_ins;
2512 : :
2513 : 5 : map_op_list = row->map_op_lists[idx];
2514 : 5 : column = &class->columns[idx];
2515 : 5 : key_type = column->type.key.type;
2516 : 5 : value_type = column->type.value.type;
2517 : :
2518 : : /* Get the value to be changed */
2519 [ + - ][ + + ]: 5 : if (row->new && row->written && bitmap_is_set(row->written,idx)) {
[ - + ]
2520 : 0 : old_datum = &row->new[idx];
2521 [ + - ]: 5 : } else if (row->old != NULL) {
2522 : 5 : old_datum = &row->old[idx];
2523 : : } else {
2524 : 0 : old_datum = ovsdb_datum_default(&column->type);
2525 : : }
2526 : :
2527 : 5 : del_set = json_array_create_empty();
2528 : 5 : ins_map = json_array_create_empty();
2529 : 5 : any_del = false;
2530 : 5 : any_ins = false;
2531 : :
2532 [ + + ]: 10 : for (struct map_op *map_op = map_op_list_first(map_op_list); map_op;
2533 : 5 : map_op = map_op_list_next(map_op_list, map_op)) {
2534 : :
2535 [ + + ]: 5 : if (map_op_type(map_op) == MAP_OP_UPDATE) {
2536 : : /* Find out if value really changed. */
2537 : : struct ovsdb_datum *new_datum;
2538 : : unsigned int pos;
2539 : 2 : new_datum = map_op_datum(map_op);
2540 : 2 : pos = ovsdb_datum_find_key(old_datum,
2541 : 2 : &new_datum->keys[0],
2542 : : key_type);
2543 [ + + ]: 2 : if (ovsdb_atom_equals(&new_datum->values[0],
2544 : 4 : &old_datum->values[pos],
2545 : : value_type)) {
2546 : : /* No change in value. Move on to next update. */
2547 : 2 : continue;
2548 : : }
2549 [ + + ]: 3 : } else if (map_op_type(map_op) == MAP_OP_DELETE){
2550 : : /* Verify that there is a key to delete. */
2551 : : unsigned int pos;
2552 : 2 : pos = ovsdb_datum_find_key(old_datum,
2553 : 2 : &map_op_datum(map_op)->keys[0],
2554 : : key_type);
2555 [ + + ]: 2 : if (pos == UINT_MAX) {
2556 : : /* No key to delete. Move on to next update. */
2557 [ + - ]: 1 : VLOG_WARN("Trying to delete a key that doesn't "
2558 : : "exist in the map.");
2559 : 1 : continue;
2560 : : }
2561 : : }
2562 : :
2563 [ + + ]: 3 : if (map_op_type(map_op) == MAP_OP_INSERT) {
2564 : 2 : map = json_array_create_2(
2565 : 1 : ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0],
2566 : : key_type),
2567 : 1 : ovsdb_atom_to_json(&map_op_datum(map_op)->values[0],
2568 : : value_type));
2569 : 1 : json_array_add(ins_map, map);
2570 : 1 : any_ins = true;
2571 : : } else { /* MAP_OP_UPDATE or MAP_OP_DELETE */
2572 : 2 : map = ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0],
2573 : : key_type);
2574 : 2 : json_array_add(del_set, map);
2575 : 2 : any_del = true;
2576 : : }
2577 : :
2578 : : /* Generate an additional insert mutate for updates. */
2579 [ + + ]: 3 : if (map_op_type(map_op) == MAP_OP_UPDATE) {
2580 : 2 : map = json_array_create_2(
2581 : 1 : ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0],
2582 : : key_type),
2583 : 1 : ovsdb_atom_to_json(&map_op_datum(map_op)->values[0],
2584 : : value_type));
2585 : 1 : json_array_add(ins_map, map);
2586 : 1 : any_ins = true;
2587 : : }
2588 : : }
2589 : :
2590 [ + + ]: 5 : if (any_del) {
2591 : 2 : col_name = json_string_create(column->name);
2592 : 2 : mutator = json_string_create("delete");
2593 : 2 : map = json_array_create_2(json_string_create("set"), del_set);
2594 : 2 : mutation = json_array_create_3(col_name, mutator, map);
2595 : 2 : json_array_add(mutations, mutation);
2596 : 2 : any_mutations = true;
2597 : : } else {
2598 : 3 : json_destroy(del_set);
2599 : : }
2600 [ + + ]: 5 : if (any_ins) {
2601 : 2 : col_name = json_string_create(column->name);
2602 : 2 : mutator = json_string_create("insert");
2603 : 2 : map = json_array_create_2(json_string_create("map"), ins_map);
2604 : 2 : mutation = json_array_create_3(col_name, mutator, map);
2605 : 2 : json_array_add(mutations, mutation);
2606 : 2 : any_mutations = true;
2607 : : } else {
2608 : 3 : json_destroy(ins_map);
2609 : : }
2610 : : }
2611 : : }
2612 [ + + ]: 58 : if (row->set_op_written) {
2613 [ + + ]: 108 : BITMAP_FOR_EACH_1(idx, class->n_columns, row->set_op_written) {
2614 : : struct set_op_list *set_op_list;
2615 : : const struct ovsdb_idl_column *column;
2616 : : const struct ovsdb_datum *old_datum;
2617 : : enum ovsdb_atomic_type key_type;
2618 : : struct json *mutation, *set, *col_name, *mutator;
2619 : : struct json *del_set, *ins_set;
2620 : : bool any_del, any_ins;
2621 : :
2622 : 54 : set_op_list = row->set_op_lists[idx];
2623 : 54 : column = &class->columns[idx];
2624 : 54 : key_type = column->type.key.type;
2625 : :
2626 : : /* Get the value to be changed */
2627 [ + - ][ + + ]: 54 : if (row->new && row->written && bitmap_is_set(row->written,idx)) {
[ + + ]
2628 : 5 : old_datum = &row->new[idx];
2629 [ + - ]: 49 : } else if (row->old != NULL) {
2630 : 49 : old_datum = &row->old[idx];
2631 : : } else {
2632 : 0 : old_datum = ovsdb_datum_default(&column->type);
2633 : : }
2634 : :
2635 : 54 : del_set = json_array_create_empty();
2636 : 54 : ins_set = json_array_create_empty();
2637 : 54 : any_del = false;
2638 : 54 : any_ins = false;
2639 : :
2640 [ + + ]: 114 : for (struct set_op *set_op = set_op_list_first(set_op_list); set_op;
2641 : 60 : set_op = set_op_list_next(set_op_list, set_op)) {
2642 [ + + ]: 60 : if (set_op_type(set_op) == SET_OP_INSERT) {
2643 : 40 : set = ovsdb_atom_to_json(&set_op_datum(set_op)->keys[0],
2644 : : key_type);
2645 : 40 : json_array_add(ins_set, set);
2646 : 40 : any_ins = true;
2647 : : } else { /* SETP_OP_DELETE */
2648 : : /* Verify that there is a key to delete. */
2649 : : unsigned int pos;
2650 : 20 : pos = ovsdb_datum_find_key(old_datum,
2651 : 20 : &set_op_datum(set_op)->keys[0],
2652 : : key_type);
2653 [ + + ]: 20 : if (pos == UINT_MAX) {
2654 : : /* No key to delete. Move on to next update. */
2655 [ + - ]: 1 : VLOG_WARN("Trying to delete a key that doesn't "
2656 : : "exist in the set.");
2657 : 1 : continue;
2658 : : }
2659 : 19 : set = ovsdb_atom_to_json(&set_op_datum(set_op)->keys[0],
2660 : : key_type);
2661 : 19 : json_array_add(del_set, set);
2662 : 19 : any_del = true;
2663 : : }
2664 : : }
2665 [ + + ]: 54 : if (any_del) {
2666 : 19 : col_name = json_string_create(column->name);
2667 : 19 : mutator = json_string_create("delete");
2668 : 19 : set = json_array_create_2(json_string_create("set"), del_set);
2669 : 19 : mutation = json_array_create_3(col_name, mutator, set);
2670 : 19 : json_array_add(mutations, mutation);
2671 : 19 : any_mutations = true;
2672 : : } else {
2673 : 35 : json_destroy(del_set);
2674 : : }
2675 [ + + ]: 54 : if (any_ins) {
2676 : 37 : col_name = json_string_create(column->name);
2677 : 37 : mutator = json_string_create("insert");
2678 : 37 : set = json_array_create_2(json_string_create("set"), ins_set);
2679 : 37 : mutation = json_array_create_3(col_name, mutator, set);
2680 : 37 : json_array_add(mutations, mutation);
2681 : 37 : any_mutations = true;
2682 : : } else {
2683 : 17 : json_destroy(ins_set);
2684 : : }
2685 : : }
2686 : : }
2687 : 58 : return any_mutations;
2688 : : }
2689 : :
2690 : : /* Attempts to commit 'txn'. Returns the status of the commit operation, one
2691 : : * of the following TXN_* constants:
2692 : : *
2693 : : * TXN_INCOMPLETE:
2694 : : *
2695 : : * The transaction is in progress, but not yet complete. The caller
2696 : : * should call again later, after calling ovsdb_idl_run() to let the IDL
2697 : : * do OVSDB protocol processing.
2698 : : *
2699 : : * TXN_UNCHANGED:
2700 : : *
2701 : : * The transaction is complete. (It didn't actually change the database,
2702 : : * so the IDL didn't send any request to the database server.)
2703 : : *
2704 : : * TXN_ABORTED:
2705 : : *
2706 : : * The caller previously called ovsdb_idl_txn_abort().
2707 : : *
2708 : : * TXN_SUCCESS:
2709 : : *
2710 : : * The transaction was successful. The update made by the transaction
2711 : : * (and possibly other changes made by other database clients) should
2712 : : * already be visible in the IDL.
2713 : : *
2714 : : * TXN_TRY_AGAIN:
2715 : : *
2716 : : * The transaction failed for some transient reason, e.g. because a
2717 : : * "verify" operation reported an inconsistency or due to a network
2718 : : * problem. The caller should wait for a change to the database, then
2719 : : * compose a new transaction, and commit the new transaction.
2720 : : *
2721 : : * Use the return value of ovsdb_idl_get_seqno() to wait for a change in
2722 : : * the database. It is important to use its return value *before* the
2723 : : * initial call to ovsdb_idl_txn_commit() as the baseline for this
2724 : : * purpose, because the change that one should wait for can happen after
2725 : : * the initial call but before the call that returns TXN_TRY_AGAIN, and
2726 : : * using some other baseline value in that situation could cause an
2727 : : * indefinite wait if the database rarely changes.
2728 : : *
2729 : : * TXN_NOT_LOCKED:
2730 : : *
2731 : : * The transaction failed because the IDL has been configured to require
2732 : : * a database lock (with ovsdb_idl_set_lock()) but didn't get it yet or
2733 : : * has already lost it.
2734 : : *
2735 : : * Committing a transaction rolls back all of the changes that it made to the
2736 : : * IDL's copy of the database. If the transaction commits successfully, then
2737 : : * the database server will send an update and, thus, the IDL will be updated
2738 : : * with the committed changes. */
2739 : : enum ovsdb_idl_txn_status
2740 : 40574 : ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn)
2741 : : {
2742 : : struct ovsdb_idl_row *row;
2743 : : struct json *operations;
2744 : : bool any_updates;
2745 : :
2746 [ + + ]: 40574 : if (txn != txn->idl->txn) {
2747 : 11512 : goto coverage_out;
2748 : : }
2749 : :
2750 : : /* If we need a lock but don't have it, give up quickly. */
2751 [ + + ][ - + ]: 29062 : if (txn->idl->lock_name && !ovsdb_idl_has_lock(txn->idl)) {
2752 : 0 : txn->status = TXN_NOT_LOCKED;
2753 : 0 : goto disassemble_out;
2754 : : }
2755 : :
2756 : 29062 : operations = json_array_create_1(
2757 : 29062 : json_string_create(txn->idl->class->database));
2758 : :
2759 : : /* Assert that we have the required lock (avoiding a race). */
2760 [ + + ]: 29062 : if (txn->idl->lock_name) {
2761 : 11109 : struct json *op = json_object_create();
2762 : 11109 : json_array_add(operations, op);
2763 : 11109 : json_object_put_string(op, "op", "assert");
2764 : 11109 : json_object_put_string(op, "lock", txn->idl->lock_name);
2765 : : }
2766 : :
2767 : : /* Add prerequisites and declarations of new rows. */
2768 [ + + ][ - + ]: 107532 : HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) {
2769 : : /* XXX check that deleted rows exist even if no prereqs? */
2770 [ + + ]: 78470 : if (row->prereqs) {
2771 : 29526 : const struct ovsdb_idl_table_class *class = row->table->class;
2772 : 29526 : size_t n_columns = class->n_columns;
2773 : : struct json *op, *columns, *row_json;
2774 : : size_t idx;
2775 : :
2776 : 29526 : op = json_object_create();
2777 : 29526 : json_array_add(operations, op);
2778 : 29526 : json_object_put_string(op, "op", "wait");
2779 : 29526 : json_object_put_string(op, "table", class->name);
2780 : 29526 : json_object_put(op, "timeout", json_integer_create(0));
2781 : 29526 : json_object_put(op, "where", where_uuid_equals(&row->uuid));
2782 : 29526 : json_object_put_string(op, "until", "==");
2783 : 29526 : columns = json_array_create_empty();
2784 : 29526 : json_object_put(op, "columns", columns);
2785 : 29526 : row_json = json_object_create();
2786 : 29526 : json_object_put(op, "rows", json_array_create_1(row_json));
2787 : :
2788 [ + + ]: 59122 : BITMAP_FOR_EACH_1 (idx, n_columns, row->prereqs) {
2789 : 29596 : const struct ovsdb_idl_column *column = &class->columns[idx];
2790 : 29596 : json_array_add(columns, json_string_create(column->name));
2791 : 29596 : json_object_put(row_json, column->name,
2792 : 29596 : ovsdb_datum_to_json(&row->old[idx],
2793 : : &column->type));
2794 : : }
2795 : : }
2796 : : }
2797 : :
2798 : : /* Add updates. */
2799 : 29062 : any_updates = false;
2800 [ + + ][ - + ]: 107532 : HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) {
2801 : 78470 : const struct ovsdb_idl_table_class *class = row->table->class;
2802 : :
2803 [ + + ]: 78470 : if (!row->new) {
2804 [ + + ]: 3542 : if (class->is_root) {
2805 : 623 : struct json *op = json_object_create();
2806 : 623 : json_object_put_string(op, "op", "delete");
2807 : 623 : json_object_put_string(op, "table", class->name);
2808 : 623 : json_object_put(op, "where", where_uuid_equals(&row->uuid));
2809 : 623 : json_array_add(operations, op);
2810 : 623 : any_updates = true;
2811 : : } else {
2812 : : /* Let ovsdb-server decide whether to really delete it. */
2813 : : }
2814 [ + + ]: 74928 : } else if (row->old != row->new) {
2815 : : struct json *row_json;
2816 : : struct json *op;
2817 : : size_t idx;
2818 : :
2819 : 48144 : op = json_object_create();
2820 [ + + ]: 48144 : json_object_put_string(op, "op", row->old ? "update" : "insert");
2821 : 48144 : json_object_put_string(op, "table", class->name);
2822 [ + + ]: 48144 : if (row->old) {
2823 : 31218 : json_object_put(op, "where", where_uuid_equals(&row->uuid));
2824 : : } else {
2825 : : struct ovsdb_idl_txn_insert *insert;
2826 : :
2827 : 16926 : any_updates = true;
2828 : :
2829 : 16926 : json_object_put(op, "uuid-name",
2830 : : json_string_create_nocopy(
2831 : 16926 : uuid_name_from_uuid(&row->uuid)));
2832 : :
2833 : 16926 : insert = xmalloc(sizeof *insert);
2834 : 16926 : insert->dummy = row->uuid;
2835 : 16926 : insert->op_index = operations->u.array.n - 1;
2836 : 16926 : uuid_zero(&insert->real);
2837 : 16926 : hmap_insert(&txn->inserted_rows, &insert->hmap_node,
2838 : : uuid_hash(&insert->dummy));
2839 : : }
2840 : 48144 : row_json = json_object_create();
2841 : 48144 : json_object_put(op, "row", row_json);
2842 : :
2843 [ + + ]: 48144 : if (row->written) {
2844 [ + + ]: 184599 : BITMAP_FOR_EACH_1 (idx, class->n_columns, row->written) {
2845 : 137179 : const struct ovsdb_idl_column *column =
2846 : 137179 : &class->columns[idx];
2847 : :
2848 [ + + ]: 137179 : if (row->old
2849 [ + + ]: 62047 : || !ovsdb_datum_is_default(&row->new[idx],
2850 : : &column->type)) {
2851 : 134344 : json_object_put(row_json, column->name,
2852 : : substitute_uuids(
2853 : 134344 : ovsdb_datum_to_json(&row->new[idx],
2854 : : &column->type),
2855 : : txn));
2856 : :
2857 : : /* If anything really changed, consider it an update.
2858 : : * We can't suppress not-really-changed values earlier
2859 : : * or transactions would become nonatomic (see the big
2860 : : * comment inside ovsdb_idl_txn_write()). */
2861 [ + + ]: 167098 : if (!any_updates && row->old &&
[ + - + + ]
2862 : 32754 : !ovsdb_datum_equals(&row->old[idx], &row->new[idx],
2863 : : &column->type)) {
2864 : 7568 : any_updates = true;
2865 : : }
2866 : : }
2867 : : }
2868 : : }
2869 : :
2870 [ + + ][ + - ]: 48144 : if (!row->old || !shash_is_empty(json_object(row_json))) {
2871 : 48144 : json_array_add(operations, op);
2872 : : } else {
2873 : 0 : json_destroy(op);
2874 : : }
2875 : : }
2876 : :
2877 : : /* Add mutate operation, for partial map or partial set updates. */
2878 [ + + ][ + + ]: 78470 : if (row->map_op_written || row->set_op_written) {
2879 : : struct json *op, *mutations;
2880 : : bool any_mutations;
2881 : :
2882 : 58 : op = json_object_create();
2883 : 58 : json_object_put_string(op, "op", "mutate");
2884 : 58 : json_object_put_string(op, "table", class->name);
2885 : 58 : json_object_put(op, "where", where_uuid_equals(&row->uuid));
2886 : 58 : mutations = json_array_create_empty();
2887 : 58 : any_mutations = ovsdb_idl_txn_extract_mutations(row, mutations);
2888 : 58 : json_object_put(op, "mutations", mutations);
2889 : :
2890 [ + + ]: 58 : if (any_mutations) {
2891 : 55 : op = substitute_uuids(op, txn);
2892 : 55 : json_array_add(operations, op);
2893 : 55 : any_updates = true;
2894 : : } else {
2895 : 3 : json_destroy(op);
2896 : : }
2897 : : }
2898 : : }
2899 : :
2900 : : /* Add increment. */
2901 [ + + ][ + + ]: 29062 : if (txn->inc_table && (any_updates || txn->inc_force)) {
[ + + ]
2902 : 2165 : any_updates = true;
2903 : 2165 : txn->inc_index = operations->u.array.n - 1;
2904 : :
2905 : 2165 : struct json *op = json_object_create();
2906 : 2165 : json_object_put_string(op, "op", "mutate");
2907 : 2165 : json_object_put_string(op, "table", txn->inc_table);
2908 : 2165 : json_object_put(op, "where",
2909 : 2165 : substitute_uuids(where_uuid_equals(&txn->inc_row),
2910 : : txn));
2911 : 2165 : json_object_put(op, "mutations",
2912 : : json_array_create_1(
2913 : : json_array_create_3(
2914 : : json_string_create(txn->inc_column),
2915 : : json_string_create("+="),
2916 : : json_integer_create(1))));
2917 : 2165 : json_array_add(operations, op);
2918 : :
2919 : 2165 : op = json_object_create();
2920 : 2165 : json_object_put_string(op, "op", "select");
2921 : 2165 : json_object_put_string(op, "table", txn->inc_table);
2922 : 2165 : json_object_put(op, "where",
2923 : 2165 : substitute_uuids(where_uuid_equals(&txn->inc_row),
2924 : : txn));
2925 : 2165 : json_object_put(op, "columns",
2926 : : json_array_create_1(json_string_create(
2927 : : txn->inc_column)));
2928 : 2165 : json_array_add(operations, op);
2929 : : }
2930 : :
2931 [ + + ]: 29062 : if (txn->comment.length) {
2932 : 9701 : struct json *op = json_object_create();
2933 : 9701 : json_object_put_string(op, "op", "comment");
2934 : 9701 : json_object_put_string(op, "comment", ds_cstr(&txn->comment));
2935 : 9701 : json_array_add(operations, op);
2936 : : }
2937 : :
2938 [ - + ]: 29062 : if (txn->dry_run) {
2939 : 0 : struct json *op = json_object_create();
2940 : 0 : json_object_put_string(op, "op", "abort");
2941 : 0 : json_array_add(operations, op);
2942 : : }
2943 : :
2944 [ + + ]: 29062 : if (!any_updates) {
2945 : 17733 : txn->status = TXN_UNCHANGED;
2946 : 17733 : json_destroy(operations);
2947 [ + - ]: 11329 : } else if (!jsonrpc_session_send(
2948 : 11329 : txn->idl->session,
2949 : : jsonrpc_create_request(
2950 : : "transact", operations, &txn->request_id))) {
2951 : 11329 : hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node,
2952 : : json_hash(txn->request_id, 0));
2953 : 11329 : txn->status = TXN_INCOMPLETE;
2954 : : } else {
2955 : 0 : txn->status = TXN_TRY_AGAIN;
2956 : : }
2957 : :
2958 : : disassemble_out:
2959 : 29062 : ovsdb_idl_txn_disassemble(txn);
2960 : : coverage_out:
2961 [ - + + + : 40574 : switch (txn->status) {
+ + - -
- ]
2962 : 0 : case TXN_UNCOMMITTED: COVERAGE_INC(txn_uncommitted); break;
2963 : 17733 : case TXN_UNCHANGED: COVERAGE_INC(txn_unchanged); break;
2964 : 13846 : case TXN_INCOMPLETE: COVERAGE_INC(txn_incomplete); break;
2965 : 1 : case TXN_ABORTED: COVERAGE_INC(txn_aborted); break;
2966 : 8993 : case TXN_SUCCESS: COVERAGE_INC(txn_success); break;
2967 : 1 : case TXN_TRY_AGAIN: COVERAGE_INC(txn_try_again); break;
2968 : 0 : case TXN_NOT_LOCKED: COVERAGE_INC(txn_not_locked); break;
2969 : 0 : case TXN_ERROR: COVERAGE_INC(txn_error); break;
2970 : : }
2971 : :
2972 : 40574 : return txn->status;
2973 : : }
2974 : :
2975 : : /* Attempts to commit 'txn', blocking until the commit either succeeds or
2976 : : * fails. Returns the final commit status, which may be any TXN_* value other
2977 : : * than TXN_INCOMPLETE.
2978 : : *
2979 : : * This function calls ovsdb_idl_run() on 'txn''s IDL, so it may cause the
2980 : : * return value of ovsdb_idl_get_seqno() to change. */
2981 : : enum ovsdb_idl_txn_status
2982 : 6150 : ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn)
2983 : : {
2984 : : enum ovsdb_idl_txn_status status;
2985 : :
2986 : 6150 : fatal_signal_run();
2987 [ + + ]: 11068 : while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) {
2988 : 4918 : ovsdb_idl_run(txn->idl);
2989 : 4918 : ovsdb_idl_wait(txn->idl);
2990 : 4918 : ovsdb_idl_txn_wait(txn);
2991 : 4918 : poll_block();
2992 : : }
2993 : 6150 : return status;
2994 : : }
2995 : :
2996 : : /* Returns the final (incremented) value of the column in 'txn' that was set to
2997 : : * be incremented by ovsdb_idl_txn_increment(). 'txn' must have committed
2998 : : * successfully. */
2999 : : int64_t
3000 : 2165 : ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn)
3001 : : {
3002 [ - + ]: 2165 : ovs_assert(txn->status == TXN_SUCCESS);
3003 : 2165 : return txn->inc_new_value;
3004 : : }
3005 : :
3006 : : /* Aborts 'txn' without sending it to the database server. This is effective
3007 : : * only if ovsdb_idl_txn_commit() has not yet been called for 'txn'.
3008 : : * Otherwise, it has no effect.
3009 : : *
3010 : : * Aborting a transaction doesn't free its memory. Use
3011 : : * ovsdb_idl_txn_destroy() to do that. */
3012 : : void
3013 : 29469 : ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn)
3014 : : {
3015 : 29469 : ovsdb_idl_txn_disassemble(txn);
3016 [ + + ][ + + ]: 29469 : if (txn->status == TXN_UNCOMMITTED || txn->status == TXN_INCOMPLETE) {
3017 : 2539 : txn->status = TXN_ABORTED;
3018 : : }
3019 : 29469 : }
3020 : :
3021 : : /* Returns a string that reports the error status for 'txn'. The caller must
3022 : : * not modify or free the returned string. A call to ovsdb_idl_txn_destroy()
3023 : : * for 'txn' may free the returned string.
3024 : : *
3025 : : * The return value is ordinarily one of the strings that
3026 : : * ovsdb_idl_txn_status_to_string() would return, but if the transaction failed
3027 : : * due to an error reported by the database server, the return value is that
3028 : : * error. */
3029 : : const char *
3030 : 6132 : ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn)
3031 : : {
3032 [ + - ]: 6132 : if (txn->status != TXN_ERROR) {
3033 : 6132 : return ovsdb_idl_txn_status_to_string(txn->status);
3034 [ # # ]: 0 : } else if (txn->error) {
3035 : 0 : return txn->error;
3036 : : } else {
3037 : 0 : return "no error details available";
3038 : : }
3039 : : }
3040 : :
3041 : : static void
3042 : 0 : ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn,
3043 : : const struct json *json)
3044 : : {
3045 [ # # ]: 0 : if (txn->error == NULL) {
3046 : 0 : txn->error = json_to_string(json, JSSF_SORT);
3047 : : }
3048 : 0 : }
3049 : :
3050 : : /* For transaction 'txn' that completed successfully, finds and returns the
3051 : : * permanent UUID that the database assigned to a newly inserted row, given the
3052 : : * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row.
3053 : : *
3054 : : * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or
3055 : : * if it was assigned by that function and then deleted by
3056 : : * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted
3057 : : * and then deleted within a single transaction are never sent to the database
3058 : : * server, so it never assigns them a permanent UUID.) */
3059 : : const struct uuid *
3060 : 2396 : ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn *txn,
3061 : : const struct uuid *uuid)
3062 : : {
3063 : : const struct ovsdb_idl_txn_insert *insert;
3064 : :
3065 [ - + ][ # # ]: 2396 : ovs_assert(txn->status == TXN_SUCCESS || txn->status == TXN_UNCHANGED);
3066 [ + - ][ # # ]: 3141 : HMAP_FOR_EACH_IN_BUCKET (insert, hmap_node,
3067 : : uuid_hash(uuid), &txn->inserted_rows) {
3068 [ + + ]: 3141 : if (uuid_equals(uuid, &insert->dummy)) {
3069 : 2396 : return &insert->real;
3070 : : }
3071 : : }
3072 : 0 : return NULL;
3073 : : }
3074 : :
3075 : : static void
3076 : 8994 : ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn,
3077 : : enum ovsdb_idl_txn_status status)
3078 : : {
3079 : 8994 : txn->status = status;
3080 : 8994 : hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node);
3081 : 8994 : }
3082 : :
3083 : : /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_'
3084 : : * itself and the structs derived from it (e.g. the "struct ovsrec_*", for
3085 : : * ovs-vswitchd).
3086 : : *
3087 : : * 'datum' must have the correct type for its column. The IDL does not check
3088 : : * that it meets schema constraints, but ovsdb-server will do so at commit time
3089 : : * so it had better be correct.
3090 : : *
3091 : : * A transaction must be in progress. Replication of 'column' must not have
3092 : : * been disabled (by calling ovsdb_idl_omit()).
3093 : : *
3094 : : * Usually this function is used indirectly through one of the "set" functions
3095 : : * generated by ovsdb-idlc.
3096 : : *
3097 : : * Takes ownership of what 'datum' points to (and in some cases destroys that
3098 : : * data before returning) but makes a copy of 'datum' itself. (Commonly
3099 : : * 'datum' is on the caller's stack.) */
3100 : : static void
3101 : 1086194 : ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_,
3102 : : const struct ovsdb_idl_column *column,
3103 : : struct ovsdb_datum *datum, bool owns_datum)
3104 : : {
3105 : 1086194 : struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
3106 : : const struct ovsdb_idl_table_class *class;
3107 : : size_t column_idx;
3108 : : bool write_only;
3109 : :
3110 [ + + ]: 1086194 : if (ovsdb_idl_row_is_synthetic(row)) {
3111 : 7 : goto discard_datum;
3112 : : }
3113 : :
3114 : 1086187 : class = row->table->class;
3115 : 1086187 : column_idx = column - class->columns;
3116 : 1086187 : write_only = row->table->modes[column_idx] == OVSDB_IDL_MONITOR;
3117 : :
3118 [ - + ]: 1086187 : ovs_assert(row->new != NULL);
3119 [ - + ]: 1086187 : ovs_assert(column_idx < class->n_columns);
3120 [ + + ][ - + ]: 1086187 : ovs_assert(row->old == NULL ||
3121 : : row->table->modes[column_idx] & OVSDB_IDL_MONITOR);
3122 : :
3123 [ + + ][ - + ]: 1086187 : if (row->table->idl->verify_write_only && !write_only) {
3124 [ # # ]: 0 : VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when"
3125 : : " explicitly configured not to.", class->name, column->name);
3126 : 0 : goto discard_datum;
3127 : : }
3128 : :
3129 : : /* If this is a write-only column and the datum being written is the same
3130 : : * as the one already there, just skip the update entirely. This is worth
3131 : : * optimizing because we have a lot of columns that get periodically
3132 : : * refreshed into the database but don't actually change that often.
3133 : : *
3134 : : * We don't do this for read/write columns because that would break
3135 : : * atomicity of transactions--some other client might have written a
3136 : : * different value in that column since we read it. (But if a whole
3137 : : * transaction only does writes of existing values, without making any real
3138 : : * changes, we will drop the whole transaction later in
3139 : : * ovsdb_idl_txn_commit().) */
3140 [ + + ][ + + ]: 1086187 : if (write_only && ovsdb_datum_equals(ovsdb_idl_read(row, column),
3141 : : datum, &column->type)) {
3142 : 944103 : goto discard_datum;
3143 : : }
3144 : :
3145 [ + + ]: 142084 : if (hmap_node_is_null(&row->txn_node)) {
3146 : 28537 : hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
3147 : : uuid_hash(&row->uuid));
3148 : : }
3149 [ + + ]: 142084 : if (row->old == row->new) {
3150 : 31249 : row->new = xmalloc(class->n_columns * sizeof *row->new);
3151 : : }
3152 [ + + ]: 142084 : if (!row->written) {
3153 : 47474 : row->written = bitmap_allocate(class->n_columns);
3154 : : }
3155 [ + + ]: 142084 : if (bitmap_is_set(row->written, column_idx)) {
3156 : 4839 : ovsdb_datum_destroy(&row->new[column_idx], &column->type);
3157 : : } else {
3158 : 137245 : bitmap_set1(row->written, column_idx);
3159 : : }
3160 [ + + ]: 142084 : if (owns_datum) {
3161 : 34419 : row->new[column_idx] = *datum;
3162 : : } else {
3163 : 107665 : ovsdb_datum_clone(&row->new[column_idx], datum, &column->type);
3164 : : }
3165 : 142084 : (column->unparse)(row);
3166 : 142084 : (column->parse)(row, &row->new[column_idx]);
3167 : 142084 : return;
3168 : :
3169 : : discard_datum:
3170 [ + + ]: 944110 : if (owns_datum) {
3171 : 339378 : ovsdb_datum_destroy(datum, &column->type);
3172 : : }
3173 : : }
3174 : :
3175 : : void
3176 : 373797 : ovsdb_idl_txn_write(const struct ovsdb_idl_row *row,
3177 : : const struct ovsdb_idl_column *column,
3178 : : struct ovsdb_datum *datum)
3179 : : {
3180 : 373797 : ovsdb_idl_txn_write__(row, column, datum, true);
3181 : 373797 : }
3182 : :
3183 : : void
3184 : 712397 : ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *row,
3185 : : const struct ovsdb_idl_column *column,
3186 : : const struct ovsdb_datum *datum)
3187 : : {
3188 : 712397 : ovsdb_idl_txn_write__(row, column,
3189 : : CONST_CAST(struct ovsdb_datum *, datum), false);
3190 : 712397 : }
3191 : :
3192 : : /* Causes the original contents of 'column' in 'row_' to be verified as a
3193 : : * prerequisite to completing the transaction. That is, if 'column' in 'row_'
3194 : : * changed (or if 'row_' was deleted) between the time that the IDL originally
3195 : : * read its contents and the time that the transaction commits, then the
3196 : : * transaction aborts and ovsdb_idl_txn_commit() returns TXN_AGAIN_WAIT or
3197 : : * TXN_AGAIN_NOW (depending on whether the database change has already been
3198 : : * received).
3199 : : *
3200 : : * The intention is that, to ensure that no transaction commits based on dirty
3201 : : * reads, an application should call ovsdb_idl_txn_verify() on each data item
3202 : : * read as part of a read-modify-write operation.
3203 : : *
3204 : : * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current
3205 : : * value of 'column' is already known:
3206 : : *
3207 : : * - If 'row_' is a row created by the current transaction (returned by
3208 : : * ovsdb_idl_txn_insert()).
3209 : : *
3210 : : * - If 'column' has already been modified (with ovsdb_idl_txn_write())
3211 : : * within the current transaction.
3212 : : *
3213 : : * Because of the latter property, always call ovsdb_idl_txn_verify() *before*
3214 : : * ovsdb_idl_txn_write() for a given read-modify-write.
3215 : : *
3216 : : * A transaction must be in progress.
3217 : : *
3218 : : * Usually this function is used indirectly through one of the "verify"
3219 : : * functions generated by ovsdb-idlc. */
3220 : : void
3221 : 107674 : ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_,
3222 : : const struct ovsdb_idl_column *column)
3223 : : {
3224 : 107674 : struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
3225 : : const struct ovsdb_idl_table_class *class;
3226 : : size_t column_idx;
3227 : :
3228 [ - + ]: 107674 : if (ovsdb_idl_row_is_synthetic(row)) {
3229 : 0 : return;
3230 : : }
3231 : :
3232 : 107674 : class = row->table->class;
3233 : 107674 : column_idx = column - class->columns;
3234 : :
3235 [ - + ]: 107674 : ovs_assert(row->new != NULL);
3236 [ + + ][ - + ]: 107674 : ovs_assert(row->old == NULL ||
3237 : : row->table->modes[column_idx] & OVSDB_IDL_MONITOR);
3238 [ + + ]: 107674 : if (!row->old
3239 [ + + ][ + + ]: 35809 : || (row->written && bitmap_is_set(row->written, column_idx))) {
3240 : 75786 : return;
3241 : : }
3242 : :
3243 [ + + ]: 31888 : if (hmap_node_is_null(&row->txn_node)) {
3244 : 29582 : hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
3245 : : uuid_hash(&row->uuid));
3246 : : }
3247 [ + + ]: 31888 : if (!row->prereqs) {
3248 : 29606 : row->prereqs = bitmap_allocate(class->n_columns);
3249 : : }
3250 : 31888 : bitmap_set1(row->prereqs, column_idx);
3251 : : }
3252 : :
3253 : : /* Deletes 'row_' from its table. May free 'row_', so it must not be
3254 : : * accessed afterward.
3255 : : *
3256 : : * A transaction must be in progress.
3257 : : *
3258 : : * Usually this function is used indirectly through one of the "delete"
3259 : : * functions generated by ovsdb-idlc. */
3260 : : void
3261 : 3565 : ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_)
3262 : : {
3263 : 3565 : struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
3264 : :
3265 [ - + ]: 3565 : if (ovsdb_idl_row_is_synthetic(row)) {
3266 : 0 : return;
3267 : : }
3268 : :
3269 [ - + ]: 3565 : ovs_assert(row->new != NULL);
3270 [ + + ]: 3565 : if (!row->old) {
3271 : 23 : ovsdb_idl_row_unparse(row);
3272 : 23 : ovsdb_idl_row_clear_new(row);
3273 [ - + ]: 23 : ovs_assert(!row->prereqs);
3274 : 23 : hmap_remove(&row->table->rows, &row->hmap_node);
3275 : 23 : hmap_remove(&row->table->idl->txn->txn_rows, &row->txn_node);
3276 : 23 : free(row);
3277 : 23 : return;
3278 : : }
3279 [ + + ]: 3542 : if (hmap_node_is_null(&row->txn_node)) {
3280 : 3449 : hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
3281 : : uuid_hash(&row->uuid));
3282 : : }
3283 : 3542 : ovsdb_idl_row_clear_new(row);
3284 : 3542 : row->new = NULL;
3285 : : }
3286 : :
3287 : : /* Inserts and returns a new row in the table with the specified 'class' in the
3288 : : * database with open transaction 'txn'.
3289 : : *
3290 : : * The new row is assigned a provisional UUID. If 'uuid' is null then one is
3291 : : * randomly generated; otherwise 'uuid' should specify a randomly generated
3292 : : * UUID not otherwise in use. ovsdb-server will assign a different UUID when
3293 : : * 'txn' is committed, but the IDL will replace any uses of the provisional
3294 : : * UUID in the data to be to be committed by the UUID assigned by
3295 : : * ovsdb-server.
3296 : : *
3297 : : * Usually this function is used indirectly through one of the "insert"
3298 : : * functions generated by ovsdb-idlc. */
3299 : : const struct ovsdb_idl_row *
3300 : 16949 : ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn,
3301 : : const struct ovsdb_idl_table_class *class,
3302 : : const struct uuid *uuid)
3303 : : {
3304 : 16949 : struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
3305 : :
3306 [ + + ]: 16949 : if (uuid) {
3307 [ - + ]: 108 : ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
3308 : 108 : row->uuid = *uuid;
3309 : : } else {
3310 : 16841 : uuid_generate(&row->uuid);
3311 : : }
3312 : :
3313 : 16949 : row->table = ovsdb_idl_table_from_class(txn->idl, class);
3314 : 16949 : row->new = xmalloc(class->n_columns * sizeof *row->new);
3315 : 16949 : hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid));
3316 : 16949 : hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid));
3317 : 16949 : return row;
3318 : : }
3319 : :
3320 : : static void
3321 : 7152 : ovsdb_idl_txn_abort_all(struct ovsdb_idl *idl)
3322 : : {
3323 : : struct ovsdb_idl_txn *txn;
3324 : :
3325 [ - + ][ - + ]: 7152 : HMAP_FOR_EACH (txn, hmap_node, &idl->outstanding_txns) {
3326 : 0 : ovsdb_idl_txn_complete(txn, TXN_TRY_AGAIN);
3327 : : }
3328 : 7152 : }
3329 : :
3330 : : static struct ovsdb_idl_txn *
3331 : 11338 : ovsdb_idl_txn_find(struct ovsdb_idl *idl, const struct json *id)
3332 : : {
3333 : : struct ovsdb_idl_txn *txn;
3334 : :
3335 [ + + ][ - + ]: 11338 : HMAP_FOR_EACH_WITH_HASH (txn, hmap_node,
3336 : : json_hash(id, 0), &idl->outstanding_txns) {
3337 [ + - ]: 8994 : if (json_equal(id, txn->request_id)) {
3338 : 8994 : return txn;
3339 : : }
3340 : : }
3341 : 2344 : return NULL;
3342 : : }
3343 : :
3344 : : static bool
3345 : 25586 : check_json_type(const struct json *json, enum json_type type, const char *name)
3346 : : {
3347 [ - + ]: 25586 : if (!json) {
3348 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "%s is missing", name);
3349 : 0 : return false;
3350 [ - + ]: 25586 : } else if (json->type != type) {
3351 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "%s is %s instead of %s",
3352 : : name, json_type_to_string(json->type),
3353 : : json_type_to_string(type));
3354 : 0 : return false;
3355 : : } else {
3356 : 25586 : return true;
3357 : : }
3358 : : }
3359 : :
3360 : : static bool
3361 : 2165 : ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn,
3362 : : const struct json_array *results)
3363 : : {
3364 : : struct json *count, *rows, *row, *column;
3365 : : struct shash *mutate, *select;
3366 : :
3367 [ - + ]: 2165 : if (txn->inc_index + 2 > results->n) {
3368 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations "
3369 : : "for increment (has %"PRIuSIZE", needs %u)",
3370 : : results->n, txn->inc_index + 2);
3371 : 0 : return false;
3372 : : }
3373 : :
3374 : : /* We know that this is a JSON object because the loop in
3375 : : * ovsdb_idl_txn_process_reply() checked. */
3376 : 2165 : mutate = json_object(results->elems[txn->inc_index]);
3377 : 2165 : count = shash_find_data(mutate, "count");
3378 [ - + ]: 2165 : if (!check_json_type(count, JSON_INTEGER, "\"mutate\" reply \"count\"")) {
3379 : 0 : return false;
3380 : : }
3381 [ - + ]: 2165 : if (count->u.integer != 1) {
3382 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl,
3383 : : "\"mutate\" reply \"count\" is %lld instead of 1",
3384 : : count->u.integer);
3385 : 0 : return false;
3386 : : }
3387 : :
3388 : 2165 : select = json_object(results->elems[txn->inc_index + 1]);
3389 : 2165 : rows = shash_find_data(select, "rows");
3390 [ - + ]: 2165 : if (!check_json_type(rows, JSON_ARRAY, "\"select\" reply \"rows\"")) {
3391 : 0 : return false;
3392 : : }
3393 [ - + ]: 2165 : if (rows->u.array.n != 1) {
3394 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "\"select\" reply \"rows\" has %"PRIuSIZE" elements "
3395 : : "instead of 1",
3396 : : rows->u.array.n);
3397 : 0 : return false;
3398 : : }
3399 : 2165 : row = rows->u.array.elems[0];
3400 [ - + ]: 2165 : if (!check_json_type(row, JSON_OBJECT, "\"select\" reply row")) {
3401 : 0 : return false;
3402 : : }
3403 : 2165 : column = shash_find_data(json_object(row), txn->inc_column);
3404 [ - + ]: 2165 : if (!check_json_type(column, JSON_INTEGER,
3405 : : "\"select\" reply inc column")) {
3406 : 0 : return false;
3407 : : }
3408 : 2165 : txn->inc_new_value = column->u.integer;
3409 : 2165 : return true;
3410 : : }
3411 : :
3412 : : static bool
3413 : 16926 : ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert,
3414 : : const struct json_array *results)
3415 : : {
3416 : : static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT;
3417 : : struct ovsdb_error *error;
3418 : : struct json *json_uuid;
3419 : : union ovsdb_atom uuid;
3420 : : struct shash *reply;
3421 : :
3422 [ - + ]: 16926 : if (insert->op_index >= results->n) {
3423 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations "
3424 : : "for insert (has %"PRIuSIZE", needs %u)",
3425 : : results->n, insert->op_index);
3426 : 0 : return false;
3427 : : }
3428 : :
3429 : : /* We know that this is a JSON object because the loop in
3430 : : * ovsdb_idl_txn_process_reply() checked. */
3431 : 16926 : reply = json_object(results->elems[insert->op_index]);
3432 : 16926 : json_uuid = shash_find_data(reply, "uuid");
3433 [ - + ]: 16926 : if (!check_json_type(json_uuid, JSON_ARRAY, "\"insert\" reply \"uuid\"")) {
3434 : 0 : return false;
3435 : : }
3436 : :
3437 : 16926 : error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL);
3438 [ - + ]: 16926 : if (error) {
3439 : 0 : char *s = ovsdb_error_to_string(error);
3440 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON "
3441 : : "UUID: %s", s);
3442 : 0 : free(s);
3443 : 0 : ovsdb_error_destroy(error);
3444 : 0 : return false;
3445 : : }
3446 : :
3447 : 16926 : insert->real = uuid.uuid;
3448 : :
3449 : 16926 : return true;
3450 : : }
3451 : :
3452 : : static bool
3453 : 11338 : ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl,
3454 : : const struct jsonrpc_msg *msg)
3455 : : {
3456 : : struct ovsdb_idl_txn *txn;
3457 : : enum ovsdb_idl_txn_status status;
3458 : :
3459 : 11338 : txn = ovsdb_idl_txn_find(idl, msg->id);
3460 [ + + ]: 11338 : if (!txn) {
3461 : 2344 : return false;
3462 : : }
3463 : :
3464 [ - + ]: 8994 : if (msg->type == JSONRPC_ERROR) {
3465 : 0 : status = TXN_ERROR;
3466 [ - + ]: 8994 : } else if (msg->result->type != JSON_ARRAY) {
3467 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl, "reply to \"transact\" is not JSON array");
3468 : 0 : status = TXN_ERROR;
3469 : : } else {
3470 : 8994 : struct json_array *ops = &msg->result->u.array;
3471 : 8994 : int hard_errors = 0;
3472 : 8994 : int soft_errors = 0;
3473 : 8994 : int lock_errors = 0;
3474 : : size_t i;
3475 : :
3476 [ + + ]: 78059 : for (i = 0; i < ops->n; i++) {
3477 : 69065 : struct json *op = ops->elems[i];
3478 : :
3479 [ + + ]: 69065 : if (op->type == JSON_NULL) {
3480 : : /* This isn't an error in itself but indicates that some prior
3481 : : * operation failed, so make sure that we know about it. */
3482 : 1 : soft_errors++;
3483 [ + - ]: 69064 : } else if (op->type == JSON_OBJECT) {
3484 : : struct json *error;
3485 : :
3486 : 69064 : error = shash_find_data(json_object(op), "error");
3487 [ + + ]: 69064 : if (error) {
3488 [ + - ]: 1 : if (error->type == JSON_STRING) {
3489 [ + - ]: 1 : if (!strcmp(error->u.string, "timed out")) {
3490 : 1 : soft_errors++;
3491 [ # # ]: 0 : } else if (!strcmp(error->u.string, "not owner")) {
3492 : 0 : lock_errors++;
3493 [ # # ]: 0 : } else if (strcmp(error->u.string, "aborted")) {
3494 : 0 : hard_errors++;
3495 : 1 : ovsdb_idl_txn_set_error_json(txn, op);
3496 : : }
3497 : : } else {
3498 : 0 : hard_errors++;
3499 : 0 : ovsdb_idl_txn_set_error_json(txn, op);
3500 [ # # ]: 69064 : VLOG_WARN_RL(&syntax_rl,
3501 : : "\"error\" in reply is not JSON string");
3502 : : }
3503 : : }
3504 : : } else {
3505 : 0 : hard_errors++;
3506 : 0 : ovsdb_idl_txn_set_error_json(txn, op);
3507 [ # # ]: 0 : VLOG_WARN_RL(&syntax_rl,
3508 : : "operation reply is not JSON null or object");
3509 : : }
3510 : : }
3511 : :
3512 [ + + ][ + - ]: 8994 : if (!soft_errors && !hard_errors && !lock_errors) {
[ + - ]
3513 : : struct ovsdb_idl_txn_insert *insert;
3514 : :
3515 [ + + ][ - + ]: 8993 : if (txn->inc_table && !ovsdb_idl_txn_process_inc_reply(txn, ops)) {
3516 : 0 : hard_errors++;
3517 : : }
3518 : :
3519 [ + + ][ - + ]: 25919 : HMAP_FOR_EACH (insert, hmap_node, &txn->inserted_rows) {
3520 [ - + ]: 16926 : if (!ovsdb_idl_txn_process_insert_reply(insert, ops)) {
3521 : 0 : hard_errors++;
3522 : : }
3523 : : }
3524 : : }
3525 : :
3526 [ + - ][ + - ]: 8994 : status = (hard_errors ? TXN_ERROR
[ + + ]
3527 : : : lock_errors ? TXN_NOT_LOCKED
3528 : : : soft_errors ? TXN_TRY_AGAIN
3529 : : : TXN_SUCCESS);
3530 : : }
3531 : :
3532 : 8994 : ovsdb_idl_txn_complete(txn, status);
3533 : 8994 : return true;
3534 : : }
3535 : :
3536 : : /* Returns the transaction currently active for 'row''s IDL. A transaction
3537 : : * must currently be active. */
3538 : : struct ovsdb_idl_txn *
3539 : 0 : ovsdb_idl_txn_get(const struct ovsdb_idl_row *row)
3540 : : {
3541 : 0 : struct ovsdb_idl_txn *txn = row->table->idl->txn;
3542 [ # # ]: 0 : ovs_assert(txn != NULL);
3543 : 0 : return txn;
3544 : : }
3545 : :
3546 : : /* Returns the IDL on which 'txn' acts. */
3547 : : struct ovsdb_idl *
3548 : 0 : ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn)
3549 : : {
3550 : 0 : return txn->idl;
3551 : : }
3552 : :
3553 : : /* Blocks until 'idl' successfully connects to the remote database and
3554 : : * retrieves its contents. */
3555 : : void
3556 : 113 : ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *idl)
3557 : : {
3558 : : while (1) {
3559 : 372 : ovsdb_idl_run(idl);
3560 [ + + ]: 372 : if (ovsdb_idl_has_ever_connected(idl)) {
3561 : 113 : return;
3562 : : }
3563 : 259 : ovsdb_idl_wait(idl);
3564 : 259 : poll_block();
3565 : 259 : }
3566 : : }
3567 : :
3568 : : /* If 'lock_name' is nonnull, configures 'idl' to obtain the named lock from
3569 : : * the database server and to avoid modifying the database when the lock cannot
3570 : : * be acquired (that is, when another client has the same lock).
3571 : : *
3572 : : * If 'lock_name' is NULL, drops the locking requirement and releases the
3573 : : * lock. */
3574 : : void
3575 : 617 : ovsdb_idl_set_lock(struct ovsdb_idl *idl, const char *lock_name)
3576 : : {
3577 [ - + ]: 617 : ovs_assert(!idl->txn);
3578 [ - + ]: 617 : ovs_assert(hmap_is_empty(&idl->outstanding_txns));
3579 : :
3580 [ - + ][ # # ]: 617 : if (idl->lock_name && (!lock_name || strcmp(lock_name, idl->lock_name))) {
[ # # ]
3581 : : /* Release previous lock. */
3582 : 0 : ovsdb_idl_send_unlock_request(idl);
3583 : 0 : free(idl->lock_name);
3584 : 0 : idl->lock_name = NULL;
3585 : 0 : idl->is_lock_contended = false;
3586 : : }
3587 : :
3588 [ + - ][ + - ]: 617 : if (lock_name && !idl->lock_name) {
3589 : : /* Acquire new lock. */
3590 : 617 : idl->lock_name = xstrdup(lock_name);
3591 : 617 : ovsdb_idl_send_lock_request(idl);
3592 : : }
3593 : 617 : }
3594 : :
3595 : : /* Returns true if 'idl' is configured to obtain a lock and owns that lock.
3596 : : *
3597 : : * Locking and unlocking happens asynchronously from the database client's
3598 : : * point of view, so the information is only useful for optimization (e.g. if
3599 : : * the client doesn't have the lock then there's no point in trying to write to
3600 : : * the database). */
3601 : : bool
3602 : 116278 : ovsdb_idl_has_lock(const struct ovsdb_idl *idl)
3603 : : {
3604 : 116278 : return idl->has_lock;
3605 : : }
3606 : :
3607 : : /* Returns true if 'idl' is configured to obtain a lock but the database server
3608 : : * has indicated that some other client already owns the requested lock. */
3609 : : bool
3610 : 105174 : ovsdb_idl_is_lock_contended(const struct ovsdb_idl *idl)
3611 : : {
3612 : 105174 : return idl->is_lock_contended;
3613 : : }
3614 : :
3615 : : static void
3616 : 1855 : ovsdb_idl_update_has_lock(struct ovsdb_idl *idl, bool new_has_lock)
3617 : : {
3618 [ + + ][ + + ]: 1855 : if (new_has_lock && !idl->has_lock) {
3619 [ + - ][ + + ]: 617 : if (idl->state == IDL_S_MONITORING ||
3620 : 617 : idl->state == IDL_S_MONITORING_COND) {
3621 : 1 : idl->change_seqno++;
3622 : : } else {
3623 : : /* We're setting up a session, so don't signal that the database
3624 : : * changed. Finalizing the session will increment change_seqno
3625 : : * anyhow. */
3626 : : }
3627 : 617 : idl->is_lock_contended = false;
3628 : : }
3629 : 1855 : idl->has_lock = new_has_lock;
3630 : 1855 : }
3631 : :
3632 : : static void
3633 : 1235 : ovsdb_idl_send_lock_request__(struct ovsdb_idl *idl, const char *method,
3634 : : struct json **idp)
3635 : : {
3636 : 1235 : ovsdb_idl_update_has_lock(idl, false);
3637 : :
3638 : 1235 : json_destroy(idl->lock_request_id);
3639 : 1235 : idl->lock_request_id = NULL;
3640 : :
3641 [ + + ]: 1235 : if (jsonrpc_session_is_connected(idl->session)) {
3642 : : struct json *params;
3643 : :
3644 : 618 : params = json_array_create_1(json_string_create(idl->lock_name));
3645 : 618 : jsonrpc_session_send(idl->session,
3646 : : jsonrpc_create_request(method, params, idp));
3647 : : }
3648 : 1235 : }
3649 : :
3650 : : static void
3651 : 1235 : ovsdb_idl_send_lock_request(struct ovsdb_idl *idl)
3652 : : {
3653 : 1235 : ovsdb_idl_send_lock_request__(idl, "lock", &idl->lock_request_id);
3654 : 1235 : }
3655 : :
3656 : : static void
3657 : 0 : ovsdb_idl_send_unlock_request(struct ovsdb_idl *idl)
3658 : : {
3659 : 0 : ovsdb_idl_send_lock_request__(idl, "unlock", NULL);
3660 : 0 : }
3661 : :
3662 : : static void
3663 : 618 : ovsdb_idl_parse_lock_reply(struct ovsdb_idl *idl, const struct json *result)
3664 : : {
3665 : : bool got_lock;
3666 : :
3667 : 618 : json_destroy(idl->lock_request_id);
3668 : 618 : idl->lock_request_id = NULL;
3669 : :
3670 [ + - ]: 618 : if (result->type == JSON_OBJECT) {
3671 : : const struct json *locked;
3672 : :
3673 : 618 : locked = shash_find_data(json_object(result), "locked");
3674 [ + - ][ + + ]: 618 : got_lock = locked && locked->type == JSON_TRUE;
3675 : : } else {
3676 : 0 : got_lock = false;
3677 : : }
3678 : :
3679 : 618 : ovsdb_idl_update_has_lock(idl, got_lock);
3680 [ + + ]: 618 : if (!got_lock) {
3681 : 2 : idl->is_lock_contended = true;
3682 : : }
3683 : 618 : }
3684 : :
3685 : : static void
3686 : 2 : ovsdb_idl_parse_lock_notify(struct ovsdb_idl *idl,
3687 : : const struct json *params,
3688 : : bool new_has_lock)
3689 : : {
3690 [ + - ]: 2 : if (idl->lock_name
3691 [ + - ]: 2 : && params->type == JSON_ARRAY
3692 [ + - ]: 2 : && json_array(params)->n > 0
3693 [ + - ]: 2 : && json_array(params)->elems[0]->type == JSON_STRING) {
3694 : 2 : const char *lock_name = json_string(json_array(params)->elems[0]);
3695 : :
3696 [ + - ]: 2 : if (!strcmp(idl->lock_name, lock_name)) {
3697 : 2 : ovsdb_idl_update_has_lock(idl, new_has_lock);
3698 [ - + ]: 2 : if (!new_has_lock) {
3699 : 0 : idl->is_lock_contended = true;
3700 : : }
3701 : : }
3702 : : }
3703 : 2 : }
3704 : :
3705 : : /* Inserts a new Map Operation into current transaction. */
3706 : : static void
3707 : 5 : ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row *row,
3708 : : const struct ovsdb_idl_column *column,
3709 : : struct ovsdb_datum *datum,
3710 : : enum map_op_type op_type)
3711 : : {
3712 : : const struct ovsdb_idl_table_class *class;
3713 : : size_t column_idx;
3714 : : struct map_op *map_op;
3715 : :
3716 : 5 : class = row->table->class;
3717 : 5 : column_idx = column - class->columns;
3718 : :
3719 : : /* Check if a map operation list exists for this column. */
3720 [ + + ]: 5 : if (!row->map_op_written) {
3721 : 4 : row->map_op_written = bitmap_allocate(class->n_columns);
3722 : 4 : row->map_op_lists = xzalloc(class->n_columns *
3723 : : sizeof *row->map_op_lists);
3724 : : }
3725 [ + - ]: 5 : if (!row->map_op_lists[column_idx]) {
3726 : 5 : row->map_op_lists[column_idx] = map_op_list_create();
3727 : : }
3728 : :
3729 : : /* Add a map operation to the corresponding list. */
3730 : 5 : map_op = map_op_create(datum, op_type);
3731 : 5 : bitmap_set1(row->map_op_written, column_idx);
3732 : 5 : map_op_list_add(row->map_op_lists[column_idx], map_op, &column->type);
3733 : :
3734 : : /* Add this row to transaction's list of rows. */
3735 [ + + ]: 5 : if (hmap_node_is_null(&row->txn_node)) {
3736 : 4 : hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
3737 : : uuid_hash(&row->uuid));
3738 : : }
3739 : 5 : }
3740 : :
3741 : : /* Inserts a new Set Operation into current transaction. */
3742 : : static void
3743 : 60 : ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row *row,
3744 : : const struct ovsdb_idl_column *column,
3745 : : struct ovsdb_datum *datum,
3746 : : enum set_op_type op_type)
3747 : : {
3748 : : const struct ovsdb_idl_table_class *class;
3749 : : size_t column_idx;
3750 : : struct set_op *set_op;
3751 : :
3752 : 60 : class = row->table->class;
3753 : 60 : column_idx = column - class->columns;
3754 : :
3755 : : /* Check if a set operation list exists for this column. */
3756 [ + + ]: 60 : if (!row->set_op_written) {
3757 : 54 : row->set_op_written = bitmap_allocate(class->n_columns);
3758 : 54 : row->set_op_lists = xzalloc(class->n_columns *
3759 : : sizeof *row->set_op_lists);
3760 : : }
3761 [ + + ]: 60 : if (!row->set_op_lists[column_idx]) {
3762 : 54 : row->set_op_lists[column_idx] = set_op_list_create();
3763 : : }
3764 : :
3765 : : /* Add a set operation to the corresponding list. */
3766 : 60 : set_op = set_op_create(datum, op_type);
3767 : 60 : bitmap_set1(row->set_op_written, column_idx);
3768 : 60 : set_op_list_add(row->set_op_lists[column_idx], set_op, &column->type);
3769 : :
3770 : : /* Add this row to the transactions's list of rows. */
3771 [ + + ]: 60 : if (hmap_node_is_null(&row->txn_node)) {
3772 : 54 : hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
3773 : : uuid_hash(&row->uuid));
3774 : : }
3775 : 60 : }
3776 : :
3777 : : static bool
3778 : 65 : is_valid_partial_update(const struct ovsdb_idl_row *row,
3779 : : const struct ovsdb_idl_column *column,
3780 : : struct ovsdb_datum *datum)
3781 : : {
3782 : : /* Verify that this column is being monitored. */
3783 : 65 : unsigned int column_idx = column - row->table->class->columns;
3784 [ - + ]: 65 : if (!(row->table->modes[column_idx] & OVSDB_IDL_MONITOR)) {
3785 [ # # ]: 0 : VLOG_WARN("cannot partially update non-monitored column");
3786 : 0 : return false;
3787 : : }
3788 : :
3789 : : /* Verify that the update affects a single element. */
3790 [ - + ]: 65 : if (datum->n != 1) {
3791 [ # # ]: 0 : VLOG_WARN("invalid datum for partial update");
3792 : 0 : return false;
3793 : : }
3794 : :
3795 : 65 : return true;
3796 : : }
3797 : :
3798 : : /* Inserts the value described in 'datum' into the map in 'column' in
3799 : : * 'row_'. If the value doesn't already exist in 'column' then it's value
3800 : : * is added. The value in 'datum' must be of the same type as the values
3801 : : * in 'column'. This function takes ownership of 'datum'.
3802 : : *
3803 : : * Usually this function is used indirectly through one of the "update"
3804 : : * functions generated by vswitch-idl. */
3805 : : void
3806 : 40 : ovsdb_idl_txn_write_partial_set(const struct ovsdb_idl_row *row_,
3807 : : const struct ovsdb_idl_column *column,
3808 : : struct ovsdb_datum *datum)
3809 : : {
3810 : 40 : struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
3811 : : enum set_op_type op_type;
3812 : :
3813 [ - + ]: 40 : if (!is_valid_partial_update(row, column, datum)) {
3814 : 0 : ovsdb_datum_destroy(datum, &column->type);
3815 : 0 : free(datum);
3816 : 0 : return;
3817 : : }
3818 : :
3819 : 40 : op_type = SET_OP_INSERT;
3820 : :
3821 : 40 : ovsdb_idl_txn_add_set_op(row, column, datum, op_type);
3822 : : }
3823 : :
3824 : : /* Deletes the value specified in 'datum' from the set in 'column' in 'row_'.
3825 : : * The value in 'datum' must be of the same type as the keys in 'column'.
3826 : : * This function takes ownership of 'datum'.
3827 : : *
3828 : : * Usually this function is used indirectly through one of the "update"
3829 : : * functions generated by vswitch-idl. */
3830 : : void
3831 : 20 : ovsdb_idl_txn_delete_partial_set(const struct ovsdb_idl_row *row_,
3832 : : const struct ovsdb_idl_column *column,
3833 : : struct ovsdb_datum *datum)
3834 : : {
3835 : 20 : struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
3836 : :
3837 [ - + ]: 20 : if (!is_valid_partial_update(row, column, datum)) {
3838 : 0 : struct ovsdb_type type_ = column->type;
3839 : 0 : type_.value.type = OVSDB_TYPE_VOID;
3840 : 0 : ovsdb_datum_destroy(datum, &type_);
3841 : 0 : free(datum);
3842 : 0 : return;
3843 : : }
3844 : 20 : ovsdb_idl_txn_add_set_op(row, column, datum, SET_OP_DELETE);
3845 : : }
3846 : :
3847 : : /* Inserts the key-value specified in 'datum' into the map in 'column' in
3848 : : * 'row_'. If the key already exist in 'column', then it's value is updated
3849 : : * with the value in 'datum'. The key-value in 'datum' must be of the same type
3850 : : * as the keys-values in 'column'. This function takes ownership of 'datum'.
3851 : : *
3852 : : * Usually this function is used indirectly through one of the "update"
3853 : : * functions generated by vswitch-idl. */
3854 : : void
3855 : 3 : ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row *row_,
3856 : : const struct ovsdb_idl_column *column,
3857 : : struct ovsdb_datum *datum)
3858 : : {
3859 : 3 : struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
3860 : : enum ovsdb_atomic_type key_type;
3861 : : enum map_op_type op_type;
3862 : : unsigned int pos;
3863 : : const struct ovsdb_datum *old_datum;
3864 : :
3865 [ - + ]: 3 : if (!is_valid_partial_update(row, column, datum)) {
3866 : 0 : ovsdb_datum_destroy(datum, &column->type);
3867 : 0 : free(datum);
3868 : 0 : return;
3869 : : }
3870 : :
3871 : : /* Find out if this is an insert or an update. */
3872 : 3 : key_type = column->type.key.type;
3873 : 3 : old_datum = ovsdb_idl_read(row, column);
3874 : 3 : pos = ovsdb_datum_find_key(old_datum, &datum->keys[0], key_type);
3875 : 3 : op_type = pos == UINT_MAX ? MAP_OP_INSERT : MAP_OP_UPDATE;
3876 : :
3877 : 3 : ovsdb_idl_txn_add_map_op(row, column, datum, op_type);
3878 : : }
3879 : :
3880 : : /* Deletes the key specified in 'datum' from the map in 'column' in 'row_'.
3881 : : * The key in 'datum' must be of the same type as the keys in 'column'.
3882 : : * The value in 'datum' must be NULL. This function takes ownership of
3883 : : * 'datum'.
3884 : : *
3885 : : * Usually this function is used indirectly through one of the "update"
3886 : : * functions generated by vswitch-idl. */
3887 : : void
3888 : 2 : ovsdb_idl_txn_delete_partial_map(const struct ovsdb_idl_row *row_,
3889 : : const struct ovsdb_idl_column *column,
3890 : : struct ovsdb_datum *datum)
3891 : : {
3892 : 2 : struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
3893 : :
3894 [ - + ]: 2 : if (!is_valid_partial_update(row, column, datum)) {
3895 : 0 : struct ovsdb_type type_ = column->type;
3896 : 0 : type_.value.type = OVSDB_TYPE_VOID;
3897 : 0 : ovsdb_datum_destroy(datum, &type_);
3898 : 0 : free(datum);
3899 : 0 : return;
3900 : : }
3901 : 2 : ovsdb_idl_txn_add_map_op(row, column, datum, MAP_OP_DELETE);
3902 : : }
3903 : :
3904 : : void
3905 : 180 : ovsdb_idl_loop_destroy(struct ovsdb_idl_loop *loop)
3906 : : {
3907 [ + - ]: 180 : if (loop) {
3908 : 180 : ovsdb_idl_destroy(loop->idl);
3909 : : }
3910 : 180 : }
3911 : :
3912 : : struct ovsdb_idl_txn *
3913 : 14310 : ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop)
3914 : : {
3915 : 14310 : ovsdb_idl_run(loop->idl);
3916 : 28620 : loop->open_txn = (loop->committing_txn
3917 [ + + ]: 12010 : || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
3918 : : ? NULL
3919 [ + + ]: 26320 : : ovsdb_idl_txn_create(loop->idl));
3920 : 14310 : return loop->open_txn;
3921 : : }
3922 : :
3923 : : void
3924 : 14310 : ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop)
3925 : : {
3926 [ + + ]: 14310 : if (loop->open_txn) {
3927 : 11804 : loop->committing_txn = loop->open_txn;
3928 : 11804 : loop->open_txn = NULL;
3929 : :
3930 : 11804 : loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
3931 : : }
3932 : :
3933 : 14310 : struct ovsdb_idl_txn *txn = loop->committing_txn;
3934 [ + + ]: 14310 : if (txn) {
3935 : 14104 : enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
3936 [ + + ]: 14104 : if (status != TXN_INCOMPLETE) {
3937 [ - + + - : 11804 : switch (status) {
- - ]
3938 : : case TXN_TRY_AGAIN:
3939 : : /* We want to re-evaluate the database when it's changed from
3940 : : * the contents that it had when we started the commit. (That
3941 : : * might have already happened.) */
3942 : 0 : loop->skip_seqno = loop->precommit_seqno;
3943 [ # # ]: 0 : if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
3944 : 0 : poll_immediate_wake();
3945 : : }
3946 : 0 : break;
3947 : :
3948 : : case TXN_SUCCESS:
3949 : : /* Possibly some work on the database was deferred because no
3950 : : * further transaction could proceed. Wake up again. */
3951 : 1912 : loop->cur_cfg = loop->next_cfg;
3952 : 1912 : poll_immediate_wake();
3953 : 1912 : break;
3954 : :
3955 : : case TXN_UNCHANGED:
3956 : 9892 : loop->cur_cfg = loop->next_cfg;
3957 : 9892 : break;
3958 : :
3959 : : case TXN_ABORTED:
3960 : : case TXN_NOT_LOCKED:
3961 : : case TXN_ERROR:
3962 : 0 : break;
3963 : :
3964 : : case TXN_UNCOMMITTED:
3965 : : case TXN_INCOMPLETE:
3966 : 0 : OVS_NOT_REACHED();
3967 : : }
3968 : 11804 : ovsdb_idl_txn_destroy(txn);
3969 : 11804 : loop->committing_txn = NULL;
3970 : : }
3971 : : }
3972 : :
3973 : 14310 : ovsdb_idl_wait(loop->idl);
3974 : 14310 : }
|