/src/openvswitch/lib/ovsdb-idl.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc. |
2 | | * Copyright (C) 2016 Hewlett Packard Enterprise Development LP |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <config.h> |
18 | | |
19 | | #include "ovsdb-idl.h" |
20 | | |
21 | | #include <errno.h> |
22 | | #include <inttypes.h> |
23 | | #include <limits.h> |
24 | | #include <stdlib.h> |
25 | | |
26 | | #include "bitmap.h" |
27 | | #include "coverage.h" |
28 | | #include "hash.h" |
29 | | #include "openvswitch/dynamic-string.h" |
30 | | #include "fatal-signal.h" |
31 | | #include "openvswitch/json.h" |
32 | | #include "jsonrpc.h" |
33 | | #include "ovsdb/ovsdb.h" |
34 | | #include "ovsdb/table.h" |
35 | | #include "ovsdb-cs.h" |
36 | | #include "ovsdb-data.h" |
37 | | #include "ovsdb-error.h" |
38 | | #include "ovsdb-idl-provider.h" |
39 | | #include "ovsdb-parser.h" |
40 | | #include "ovsdb-server-idl.h" |
41 | | #include "ovsdb-session.h" |
42 | | #include "openvswitch/poll-loop.h" |
43 | | #include "openvswitch/shash.h" |
44 | | #include "skiplist.h" |
45 | | #include "simap.h" |
46 | | #include "sset.h" |
47 | | #include "svec.h" |
48 | | #include "util.h" |
49 | | #include "uuid.h" |
50 | | #include "openvswitch/vlog.h" |
51 | | |
52 | | VLOG_DEFINE_THIS_MODULE(ovsdb_idl); |
53 | | |
54 | | COVERAGE_DEFINE(txn_uncommitted); |
55 | | COVERAGE_DEFINE(txn_unchanged); |
56 | | COVERAGE_DEFINE(txn_incomplete); |
57 | | COVERAGE_DEFINE(txn_aborted); |
58 | | COVERAGE_DEFINE(txn_success); |
59 | | COVERAGE_DEFINE(txn_try_again); |
60 | | COVERAGE_DEFINE(txn_not_locked); |
61 | | COVERAGE_DEFINE(txn_error); |
62 | | |
63 | | /* An arc from one idl_row to another. When row A contains a UUID that |
64 | | * references row B, this is represented by an arc from A (the source) to B |
65 | | * (the destination). |
66 | | * |
67 | | * Arcs from a row to itself are omitted, that is, src and dst are always |
68 | | * different. |
69 | | * |
70 | | * Arcs are never duplicated, that is, even if there are multiple references |
71 | | * from A to B, there is only a single arc from A to B. |
72 | | * |
73 | | * Arcs are directed: an arc from A to B is the converse of an an arc from B to |
74 | | * A. Both an arc and its converse may both be present, if each row refers |
75 | | * to the other circularly. |
76 | | * |
77 | | * The source and destination row may be in the same table or in different |
78 | | * tables. |
79 | | */ |
80 | | struct ovsdb_idl_arc { |
81 | | struct ovs_list src_node; /* In src->src_arcs list. */ |
82 | | struct ovs_list dst_node; /* In dst->dst_arcs list. */ |
83 | | struct ovsdb_idl_row *src; /* Source row. */ |
84 | | struct ovsdb_idl_row *dst; /* Destination row. */ |
85 | | }; |
86 | | |
87 | | struct ovsdb_idl { |
88 | | struct ovsdb_cs *cs; |
89 | | const struct ovsdb_idl_class *class_; |
90 | | struct shash table_by_name; /* Contains "struct ovsdb_idl_table *"s.*/ |
91 | | struct ovsdb_idl_table *tables; /* Array of ->class_->n_tables elements. */ |
92 | | unsigned int change_seqno; |
93 | | struct ovsdb_idl_txn *txn; |
94 | | struct hmap outstanding_txns; |
95 | | bool verify_write_only; |
96 | | struct ovs_list deleted_untracked_rows; /* Stores rows deleted in the |
97 | | * current run, that are not yet |
98 | | * added to the track_list. */ |
99 | | struct ovs_list rows_to_reparse; /* Stores rows that might need to be |
100 | | * re-parsed due to insertion of a |
101 | | * referenced row. */ |
102 | | }; |
103 | | |
104 | | static struct ovsdb_cs_ops ovsdb_idl_cs_ops; |
105 | | |
106 | | struct ovsdb_idl_txn { |
107 | | struct hmap_node hmap_node; |
108 | | struct json *request_id; |
109 | | struct ovsdb_idl *idl; |
110 | | struct hmap txn_rows; |
111 | | enum ovsdb_idl_txn_status status; |
112 | | char *error; |
113 | | bool dry_run; |
114 | | struct ds comment; |
115 | | |
116 | | /* Increments. */ |
117 | | const char *inc_table; |
118 | | const char *inc_column; |
119 | | struct uuid inc_row; |
120 | | bool inc_force; |
121 | | unsigned int inc_index; |
122 | | int64_t inc_new_value; |
123 | | |
124 | | /* Inserted rows. */ |
125 | | struct hmap inserted_rows; /* Contains "struct ovsdb_idl_txn_insert"s. */ |
126 | | }; |
127 | | |
128 | | struct ovsdb_idl_txn_insert { |
129 | | struct hmap_node hmap_node; /* In struct ovsdb_idl_txn's inserted_rows. */ |
130 | | struct uuid dummy; /* Dummy UUID used locally. */ |
131 | | int op_index; /* Index into transaction's operation array. */ |
132 | | struct uuid real; /* Real UUID used by database server. */ |
133 | | }; |
134 | | |
135 | | static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5); |
136 | | static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5); |
137 | | static struct vlog_rate_limit other_rl = VLOG_RATE_LIMIT_INIT(1, 5); |
138 | | |
139 | | enum update_result { |
140 | | OVSDB_IDL_UPDATE_DB_CHANGED, |
141 | | OVSDB_IDL_UPDATE_NO_CHANGES, |
142 | | OVSDB_IDL_UPDATE_INCONSISTENT, |
143 | | }; |
144 | | static void ovsdb_idl_clear(struct ovsdb_idl *); |
145 | | static enum update_result ovsdb_idl_process_update( |
146 | | struct ovsdb_idl_table *, const struct ovsdb_cs_row_update *); |
147 | | static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, |
148 | | const struct shash *values); |
149 | | static void ovsdb_idl_delete_row(struct ovsdb_idl_row *); |
150 | | static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, |
151 | | const struct shash *values, bool xor); |
152 | | static void ovsdb_idl_parse_update(struct ovsdb_idl *, |
153 | | const struct ovsdb_cs_update_event *); |
154 | | static void ovsdb_idl_reparse_deleted(struct ovsdb_idl *); |
155 | | static void ovsdb_idl_reparse_refs_to_inserted(struct ovsdb_idl *); |
156 | | |
157 | | static void ovsdb_idl_txn_process_reply(struct ovsdb_idl *, |
158 | | const struct jsonrpc_msg *); |
159 | | |
160 | | static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *); |
161 | | static struct ovsdb_idl_row *ovsdb_idl_row_create__( |
162 | | const struct ovsdb_idl_table_class *); |
163 | | static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *, |
164 | | const struct uuid *); |
165 | | static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *); |
166 | | static void ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *); |
167 | | static void ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row *); |
168 | | static void ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row *); |
169 | | |
170 | | static void ovsdb_idl_row_parse(struct ovsdb_idl_row *); |
171 | | static void ovsdb_idl_row_unparse(struct ovsdb_idl_row *); |
172 | | static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *); |
173 | | static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *); |
174 | | static void ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *, bool destroy_dsts); |
175 | | static void ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *); |
176 | | static void ovsdb_idl_row_mark_backrefs_for_reparsing(struct ovsdb_idl_row *); |
177 | | static void ovsdb_idl_row_track_change(struct ovsdb_idl_row *, |
178 | | enum ovsdb_idl_change); |
179 | | static void ovsdb_idl_row_untrack_change(struct ovsdb_idl_row *); |
180 | | static void ovsdb_idl_row_clear_changeseqno(struct ovsdb_idl_row *); |
181 | | |
182 | | static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *); |
183 | | static bool ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *, |
184 | | struct json *); |
185 | | static void ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row *, |
186 | | const struct ovsdb_idl_column *, |
187 | | struct ovsdb_datum *, |
188 | | enum map_op_type); |
189 | | static void ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row *, |
190 | | const struct ovsdb_idl_column *, |
191 | | struct ovsdb_datum *, |
192 | | enum set_op_type); |
193 | | |
194 | | static struct ovsdb_idl_table * |
195 | | ovsdb_idl_table_from_class(const struct ovsdb_idl *, |
196 | | const struct ovsdb_idl_table_class *); |
197 | | static struct ovsdb_idl_table * |
198 | | ovsdb_idl_table_from_class(const struct ovsdb_idl *, |
199 | | const struct ovsdb_idl_table_class *); |
200 | | static void ovsdb_idl_track_clear__(struct ovsdb_idl *, bool flush_all); |
201 | | |
202 | | static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *); |
203 | | static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *); |
204 | | static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *); |
205 | | static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, |
206 | | bool *may_need_wakeup); |
207 | | |
208 | | static void add_tracked_change_for_references(struct ovsdb_idl_row *); |
209 | | |
210 | | /* Creates and returns a connection to database 'remote', which should be in a |
211 | | * form acceptable to jsonrpc_session_open(). The connection will maintain an |
212 | | * in-memory replica of the remote database whose schema is described by |
213 | | * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically |
214 | | * by ovsdb-idlc.) |
215 | | * |
216 | | * Passes 'retry' to jsonrpc_session_open(). See that function for |
217 | | * documentation. |
218 | | * |
219 | | * If 'monitor_everything_by_default' is true, then everything in the remote |
220 | | * database will be replicated by default. ovsdb_idl_omit() and |
221 | | * ovsdb_idl_omit_alert() may be used to selectively drop some columns from |
222 | | * monitoring. |
223 | | * |
224 | | * If 'monitor_everything_by_default' is false, then no columns or tables will |
225 | | * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table() |
226 | | * must be used to choose some columns or tables to replicate. |
227 | | */ |
228 | | struct ovsdb_idl * |
229 | | ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class, |
230 | | bool monitor_everything_by_default, bool retry) |
231 | 0 | { |
232 | 0 | struct ovsdb_idl *idl = ovsdb_idl_create_unconnected( |
233 | 0 | class, monitor_everything_by_default); |
234 | 0 | ovsdb_idl_set_remote(idl, remote, retry); |
235 | 0 | return idl; |
236 | 0 | } |
237 | | |
238 | | /* Creates and returns a connection to an in-memory replica of the remote |
239 | | * database whose schema is described by 'class'. (Ordinarily 'class' is |
240 | | * compiled from an OVSDB schema automatically by ovsdb-idlc.) |
241 | | * |
242 | | * Use ovsdb_idl_set_remote() to configure the database to which to connect. |
243 | | * Until a remote is configured, no data can be retrieved. |
244 | | * |
245 | | * If 'monitor_everything_by_default' is true, then everything in the remote |
246 | | * database will be replicated by default. ovsdb_idl_omit() and |
247 | | * ovsdb_idl_omit_alert() may be used to selectively drop some columns from |
248 | | * monitoring. |
249 | | * |
250 | | * If 'monitor_everything_by_default' is false, then no columns or tables will |
251 | | * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table() |
252 | | * must be used to choose some columns or tables to replicate. |
253 | | */ |
254 | | struct ovsdb_idl * |
255 | | ovsdb_idl_create_unconnected(const struct ovsdb_idl_class *class, |
256 | | bool monitor_everything_by_default) |
257 | 0 | { |
258 | 0 | struct ovsdb_idl *idl = xmalloc(sizeof *idl); |
259 | 0 | *idl = (struct ovsdb_idl) { |
260 | 0 | .cs = ovsdb_cs_create(class->database, 3, &ovsdb_idl_cs_ops, idl), |
261 | 0 | .class_ = class, |
262 | 0 | .table_by_name = SHASH_INITIALIZER(&idl->table_by_name), |
263 | 0 | .tables = xmalloc(class->n_tables * sizeof *idl->tables), |
264 | 0 | .change_seqno = 0, |
265 | 0 | .txn = NULL, |
266 | 0 | .outstanding_txns = HMAP_INITIALIZER(&idl->outstanding_txns), |
267 | 0 | .verify_write_only = false, |
268 | 0 | .deleted_untracked_rows |
269 | 0 | = OVS_LIST_INITIALIZER(&idl->deleted_untracked_rows), |
270 | 0 | .rows_to_reparse |
271 | 0 | = OVS_LIST_INITIALIZER(&idl->rows_to_reparse), |
272 | 0 | }; |
273 | |
|
274 | 0 | uint8_t default_mode = (monitor_everything_by_default |
275 | 0 | ? OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT |
276 | 0 | : 0); |
277 | 0 | for (size_t i = 0; i < class->n_tables; i++) { |
278 | 0 | const struct ovsdb_idl_table_class *tc = &class->tables[i]; |
279 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
280 | |
|
281 | 0 | shash_add_assert(&idl->table_by_name, tc->name, table); |
282 | 0 | table->class_ = tc; |
283 | 0 | table->modes = xmalloc(tc->n_columns); |
284 | 0 | memset(table->modes, default_mode, tc->n_columns); |
285 | 0 | table->need_table = false; |
286 | 0 | shash_init(&table->columns); |
287 | 0 | ovs_list_init(&table->indexes); |
288 | 0 | for (size_t j = 0; j < tc->n_columns; j++) { |
289 | 0 | const struct ovsdb_idl_column *column = &tc->columns[j]; |
290 | |
|
291 | 0 | shash_add_assert(&table->columns, column->name, column); |
292 | 0 | } |
293 | 0 | hmap_init(&table->rows); |
294 | 0 | ovs_list_init(&table->track_list); |
295 | 0 | table->change_seqno[OVSDB_IDL_CHANGE_INSERT] |
296 | 0 | = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY] |
297 | 0 | = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0; |
298 | 0 | table->idl = idl; |
299 | 0 | table->in_server_schema = false; |
300 | 0 | shash_init(&table->schema_columns); |
301 | 0 | } |
302 | |
|
303 | 0 | return idl; |
304 | 0 | } |
305 | | |
306 | | /* Changes the remote and creates a new session. |
307 | | * |
308 | | * If 'retry' is true, the connection to the remote will automatically retry |
309 | | * when it fails. If 'retry' is false, the connection is one-time. */ |
310 | | void |
311 | | ovsdb_idl_set_remote(struct ovsdb_idl *idl, const char *remote, bool retry) |
312 | 0 | { |
313 | 0 | ovsdb_cs_set_remote(idl->cs, remote, retry); |
314 | 0 | } |
315 | | |
316 | | /* Set whether the order of remotes should be shuffled, when there |
317 | | * are more than one remotes. The setting doesn't take effect |
318 | | * until the next time when ovsdb_idl_set_remote() is called. */ |
319 | | void |
320 | | ovsdb_idl_set_shuffle_remotes(struct ovsdb_idl *idl, bool shuffle) |
321 | 0 | { |
322 | 0 | ovsdb_cs_set_shuffle_remotes(idl->cs, shuffle); |
323 | 0 | } |
324 | | |
325 | | /* Passes 'set_db_change_aware' to ovsdb_cs_set_db_change_aware(). See that |
326 | | * function for documentation. */ |
327 | | void |
328 | | ovsdb_idl_set_db_change_aware(struct ovsdb_idl *idl, bool set_db_change_aware) |
329 | 0 | { |
330 | 0 | ovsdb_cs_set_db_change_aware(idl->cs, set_db_change_aware); |
331 | 0 | } |
332 | | |
333 | | /* Reset min_index to 0. This prevents a situation where the client |
334 | | * thinks all databases have stale data, when they actually have all |
335 | | * been destroyed and rebuilt from scratch. |
336 | | */ |
337 | | void |
338 | | ovsdb_idl_reset_min_index(struct ovsdb_idl *idl) |
339 | 0 | { |
340 | 0 | ovsdb_cs_reset_min_index(idl->cs); |
341 | 0 | } |
342 | | |
343 | | static void |
344 | | ovsdb_idl_schema_columns_clear(struct shash *schema_columns) |
345 | 0 | { |
346 | 0 | struct shash_node *node; |
347 | |
|
348 | 0 | SHASH_FOR_EACH (node, schema_columns) { |
349 | 0 | ovsdb_type_destroy(node->data); |
350 | 0 | } |
351 | 0 | shash_clear_free_data(schema_columns); |
352 | 0 | } |
353 | | |
354 | | /* Destroys 'idl' and all of the data structures that it manages. */ |
355 | | void |
356 | | ovsdb_idl_destroy(struct ovsdb_idl *idl) |
357 | 0 | { |
358 | 0 | if (idl) { |
359 | 0 | ovs_assert(!idl->txn); |
360 | |
|
361 | 0 | ovsdb_idl_txn_abort_all(idl); |
362 | 0 | hmap_destroy(&idl->outstanding_txns); |
363 | |
|
364 | 0 | ovsdb_idl_clear(idl); |
365 | 0 | ovsdb_cs_destroy(idl->cs); |
366 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
367 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
368 | |
|
369 | 0 | ovsdb_idl_destroy_indexes(table); |
370 | 0 | shash_destroy(&table->columns); |
371 | |
|
372 | 0 | ovsdb_idl_schema_columns_clear(&table->schema_columns); |
373 | 0 | shash_destroy(&table->schema_columns); |
374 | |
|
375 | 0 | hmap_destroy(&table->rows); |
376 | 0 | free(table->modes); |
377 | 0 | } |
378 | 0 | shash_destroy(&idl->table_by_name); |
379 | 0 | free(idl->tables); |
380 | 0 | free(idl); |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | | /* By default, or if 'leader_only' is true, when 'idl' connects to a clustered |
385 | | * database, the IDL will avoid servers other than the cluster leader. This |
386 | | * ensures that any data that it reads and reports is up-to-date. If |
387 | | * 'leader_only' is false, the IDL will accept any server in the cluster, which |
388 | | * means that for read-only transactions it can report and act on stale data |
389 | | * (transactions that modify the database are always serialized even with false |
390 | | * 'leader_only'). Refer to Understanding Cluster Consistency in ovsdb(7) for |
391 | | * more information. */ |
392 | | void |
393 | | ovsdb_idl_set_leader_only(struct ovsdb_idl *idl, bool leader_only) |
394 | 0 | { |
395 | 0 | ovsdb_cs_set_leader_only(idl->cs, leader_only); |
396 | 0 | } |
397 | | |
398 | | static void |
399 | | ovsdb_idl_clear(struct ovsdb_idl *db) |
400 | 0 | { |
401 | | /* Process deleted rows, removing them from the 'deleted_untracked_rows' |
402 | | * list and reparsing their backrefs. |
403 | | */ |
404 | 0 | ovsdb_idl_reparse_deleted(db); |
405 | | |
406 | | /* Process backrefs of inserted rows, removing them from the |
407 | | * 'rows_to_reparse' list. |
408 | | */ |
409 | 0 | ovsdb_idl_reparse_refs_to_inserted(db); |
410 | | |
411 | | /* Cleanup all rows; each row gets added to its own table's |
412 | | * 'track_list'. |
413 | | */ |
414 | 0 | for (size_t i = 0; i < db->class_->n_tables; i++) { |
415 | 0 | struct ovsdb_idl_table *table = &db->tables[i]; |
416 | 0 | struct ovsdb_idl_row *row; |
417 | |
|
418 | 0 | if (hmap_is_empty(&table->rows)) { |
419 | 0 | continue; |
420 | 0 | } |
421 | | |
422 | 0 | HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) { |
423 | 0 | struct ovsdb_idl_arc *arc; |
424 | |
|
425 | 0 | if (!ovsdb_idl_row_is_orphan(row)) { |
426 | 0 | ovsdb_idl_remove_from_indexes(row); |
427 | 0 | ovsdb_idl_row_unparse(row); |
428 | 0 | } |
429 | 0 | LIST_FOR_EACH_SAFE (arc, src_node, &row->src_arcs) { |
430 | 0 | ovs_list_remove(&arc->src_node); |
431 | 0 | ovs_list_remove(&arc->dst_node); |
432 | 0 | free(arc); |
433 | 0 | } |
434 | 0 | LIST_FOR_EACH_SAFE (arc, dst_node, &row->dst_arcs) { |
435 | 0 | ovs_list_remove(&arc->src_node); |
436 | 0 | ovs_list_remove(&arc->dst_node); |
437 | 0 | free(arc); |
438 | 0 | } |
439 | |
|
440 | 0 | ovsdb_idl_row_destroy(row); |
441 | 0 | } |
442 | 0 | } |
443 | | |
444 | | /* Free rows deleted from tables with change tracking disabled. */ |
445 | 0 | ovsdb_idl_row_destroy_postprocess(db); |
446 | | |
447 | | /* Free rows deleted from tables with change tracking enabled. */ |
448 | 0 | ovsdb_idl_track_clear__(db, true); |
449 | 0 | ovs_assert(ovs_list_is_empty(&db->deleted_untracked_rows)); |
450 | 0 | ovs_assert(ovs_list_is_empty(&db->rows_to_reparse)); |
451 | 0 | db->change_seqno++; |
452 | 0 | } |
453 | | |
454 | | /* Processes a batch of messages from the database server on 'idl'. This may |
455 | | * cause the IDL's contents to change. The client may check for that with |
456 | | * ovsdb_idl_get_seqno(). */ |
457 | | void |
458 | | ovsdb_idl_run(struct ovsdb_idl *idl) |
459 | 0 | { |
460 | 0 | ovs_assert(!idl->txn); |
461 | |
|
462 | 0 | struct ovs_list events; |
463 | 0 | ovsdb_cs_run(idl->cs, &events); |
464 | |
|
465 | 0 | struct ovsdb_cs_event *event; |
466 | 0 | LIST_FOR_EACH_POP (event, list_node, &events) { |
467 | 0 | switch (event->type) { |
468 | 0 | case OVSDB_CS_EVENT_TYPE_RECONNECT: |
469 | 0 | ovsdb_idl_txn_abort_all(idl); |
470 | 0 | break; |
471 | | |
472 | 0 | case OVSDB_CS_EVENT_TYPE_LOCKED: |
473 | 0 | if (ovsdb_cs_may_send_transaction(idl->cs)) { |
474 | | /* If the client couldn't run a transaction because it didn't |
475 | | * have the lock, this will encourage it to try again. */ |
476 | 0 | idl->change_seqno++; |
477 | 0 | } else { |
478 | | /* We're setting up a session, so don't signal that the |
479 | | * database changed. Finalizing the session will increment |
480 | | * change_seqno anyhow. */ |
481 | 0 | } |
482 | 0 | break; |
483 | | |
484 | 0 | case OVSDB_CS_EVENT_TYPE_UPDATE: |
485 | 0 | ovsdb_idl_parse_update(idl, &event->update); |
486 | 0 | break; |
487 | | |
488 | 0 | case OVSDB_CS_EVENT_TYPE_TXN_REPLY: |
489 | 0 | ovsdb_idl_txn_process_reply(idl, event->txn_reply); |
490 | 0 | break; |
491 | 0 | } |
492 | 0 | ovsdb_cs_event_destroy(event); |
493 | 0 | } |
494 | 0 | ovsdb_idl_reparse_refs_to_inserted(idl); |
495 | 0 | ovsdb_idl_reparse_deleted(idl); |
496 | 0 | ovsdb_idl_row_destroy_postprocess(idl); |
497 | 0 | } |
498 | | |
499 | | /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to |
500 | | * do or when activity occurs on a transaction on 'idl'. */ |
501 | | void |
502 | | ovsdb_idl_wait(struct ovsdb_idl *idl) |
503 | 0 | { |
504 | 0 | ovsdb_cs_wait(idl->cs); |
505 | 0 | } |
506 | | |
507 | | /* Returns memory usage statistics. */ |
508 | | void |
509 | | ovsdb_idl_get_memory_usage(struct ovsdb_idl *idl, struct simap *usage) |
510 | 0 | { |
511 | 0 | unsigned int cells = 0; |
512 | |
|
513 | 0 | if (!idl) { |
514 | 0 | return; |
515 | 0 | } |
516 | | |
517 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
518 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
519 | 0 | unsigned int n_columns = table->class_->n_columns; |
520 | 0 | unsigned int n_rows = hmap_count(&table->rows); |
521 | |
|
522 | 0 | cells += n_rows * n_columns; |
523 | 0 | } |
524 | |
|
525 | 0 | struct { |
526 | 0 | const char *name; |
527 | 0 | unsigned int val; |
528 | 0 | } idl_mem_stats[] = { |
529 | 0 | {"idl-outstanding-txns", hmap_count(&idl->outstanding_txns)}, |
530 | 0 | {"idl-cells", cells}, |
531 | 0 | }; |
532 | |
|
533 | 0 | for (size_t i = 0; i < ARRAY_SIZE(idl_mem_stats); i++) { |
534 | 0 | char *stat_name = xasprintf("%s-%s", idl_mem_stats[i].name, |
535 | 0 | idl->class_->database); |
536 | 0 | simap_increase(usage, stat_name, idl_mem_stats[i].val); |
537 | 0 | free(stat_name); |
538 | 0 | } |
539 | 0 | } |
540 | | |
541 | | /* Returns a "sequence number" that represents the state of 'idl'. When |
542 | | * ovsdb_idl_run() changes the database, the sequence number changes. The |
543 | | * initial fetch of the entire contents of the remote database is considered to |
544 | | * be one kind of change. Successfully acquiring a lock, if one has been |
545 | | * configured with ovsdb_idl_set_lock(), is also considered to be a change. |
546 | | * |
547 | | * As long as the sequence number does not change, the client may continue to |
548 | | * use any data structures it obtains from 'idl'. But when it changes, the |
549 | | * client must not access any of these data structures again, because they |
550 | | * could have freed or reused for other purposes. |
551 | | * |
552 | | * The sequence number can occasionally change even if the database does not. |
553 | | * This happens if the connection to the database drops and reconnects, which |
554 | | * causes the database contents to be reloaded even if they didn't change. (It |
555 | | * could also happen if the database server sends out a "change" that reflects |
556 | | * what the IDL already thought was in the database. The database server is |
557 | | * not supposed to do that, but bugs could in theory cause it to do so.) */ |
558 | | unsigned int |
559 | | ovsdb_idl_get_seqno(const struct ovsdb_idl *idl) |
560 | 0 | { |
561 | 0 | return idl->change_seqno; |
562 | 0 | } |
563 | | |
564 | | /* Returns a "sequence number" that represents the number of conditional |
565 | | * monitoring updates successfully received by the OVSDB server of an IDL |
566 | | * connection. |
567 | | * |
568 | | * ovsdb_idl_set_condition() sets a new condition that is different from |
569 | | * the current condtion, the next expected "sequence number" is returned. |
570 | | * |
571 | | * Whenever ovsdb_idl_get_cond_seqno() returns a value that matches |
572 | | * the return value of ovsdb_idl_set_condition(), The client is |
573 | | * assured that: |
574 | | * - The ovsdb_idl_set_condition() changes has been acknowledged by |
575 | | * the OVSDB sever. |
576 | | * |
577 | | * - 'idl' now contains the content matches the new conditions. */ |
578 | | unsigned int |
579 | | ovsdb_idl_get_condition_seqno(const struct ovsdb_idl *idl) |
580 | 0 | { |
581 | 0 | return ovsdb_cs_get_condition_seqno(idl->cs); |
582 | 0 | } |
583 | | |
584 | | /* Returns true if 'idl' successfully connected to the remote database and |
585 | | * retrieved its contents (even if the connection subsequently dropped and is |
586 | | * in the process of reconnecting). If so, then 'idl' contains an atomic |
587 | | * snapshot of the database's contents (but it might be arbitrarily old if the |
588 | | * connection dropped). |
589 | | * |
590 | | * Returns false if 'idl' has never connected or retrieved the database's |
591 | | * contents. If so, 'idl' is empty. */ |
592 | | bool |
593 | | ovsdb_idl_has_ever_connected(const struct ovsdb_idl *idl) |
594 | 0 | { |
595 | 0 | return ovsdb_idl_get_seqno(idl) != 0; |
596 | 0 | } |
597 | | |
598 | | /* Reconfigures 'idl' so that it would reconnect to the database, if |
599 | | * connection was dropped. */ |
600 | | void |
601 | | ovsdb_idl_enable_reconnect(struct ovsdb_idl *idl) |
602 | 0 | { |
603 | 0 | ovsdb_cs_enable_reconnect(idl->cs); |
604 | 0 | } |
605 | | |
606 | | /* Forces 'idl' to drop its connection to the database and reconnect. In the |
607 | | * meantime, the contents of 'idl' will not change. */ |
608 | | void |
609 | | ovsdb_idl_force_reconnect(struct ovsdb_idl *idl) |
610 | 0 | { |
611 | 0 | ovsdb_cs_force_reconnect(idl->cs); |
612 | 0 | } |
613 | | |
614 | | /* Some IDL users should only write to write-only columns. Furthermore, |
615 | | * writing to a column which is not write-only can cause serious performance |
616 | | * degradations for these users. This function causes 'idl' to reject writes |
617 | | * to columns which are not marked write only using ovsdb_idl_omit_alert(). */ |
618 | | void |
619 | | ovsdb_idl_verify_write_only(struct ovsdb_idl *idl) |
620 | 0 | { |
621 | 0 | idl->verify_write_only = true; |
622 | 0 | } |
623 | | |
624 | | /* Returns true if 'idl' is currently connected or trying to connect |
625 | | * and a negative response to a schema request has not been received */ |
626 | | bool |
627 | | ovsdb_idl_is_alive(const struct ovsdb_idl *idl) |
628 | 0 | { |
629 | 0 | return ovsdb_cs_is_alive(idl->cs); |
630 | 0 | } |
631 | | |
632 | | bool |
633 | | ovsdb_idl_is_connected(const struct ovsdb_idl *idl) |
634 | 0 | { |
635 | 0 | return ovsdb_cs_is_connected(idl->cs); |
636 | 0 | } |
637 | | |
638 | | /* Returns the last error reported on a connection by 'idl'. The return value |
639 | | * is 0 only if no connection made by 'idl' has ever encountered an error and |
640 | | * a negative response to a schema request has never been received. See |
641 | | * jsonrpc_get_status() for jsonrpc_session_get_last_error() return value |
642 | | * interpretation. */ |
643 | | int |
644 | | ovsdb_idl_get_last_error(const struct ovsdb_idl *idl) |
645 | 0 | { |
646 | 0 | return ovsdb_cs_get_last_error(idl->cs); |
647 | 0 | } |
648 | | |
649 | | /* Sets the "probe interval" for 'idl->session' to 'probe_interval', in |
650 | | * milliseconds. |
651 | | */ |
652 | | void |
653 | | ovsdb_idl_set_probe_interval(const struct ovsdb_idl *idl, int probe_interval) |
654 | 0 | { |
655 | 0 | ovsdb_cs_set_probe_interval(idl->cs, probe_interval); |
656 | 0 | } |
657 | | |
658 | | static size_t |
659 | | find_uuid_in_array(const struct uuid *target, |
660 | | const struct uuid *array, size_t n) |
661 | 0 | { |
662 | 0 | for (size_t i = 0; i < n; i++) { |
663 | 0 | if (uuid_equals(&array[i], target)) { |
664 | 0 | return i; |
665 | 0 | } |
666 | 0 | } |
667 | 0 | return SIZE_MAX; |
668 | 0 | } |
669 | | |
670 | | static size_t |
671 | | array_contains_uuid(const struct uuid *target, |
672 | | const struct uuid *array, size_t n) |
673 | 0 | { |
674 | 0 | return find_uuid_in_array(target, array, n) != SIZE_MAX; |
675 | 0 | } |
676 | | |
677 | | static bool |
678 | | remove_uuid_from_array(const struct uuid *target, |
679 | | struct uuid *array, size_t *n) |
680 | 0 | { |
681 | 0 | size_t i = find_uuid_in_array(target, array, *n); |
682 | 0 | if (i != SIZE_MAX) { |
683 | 0 | array[i] = array[--*n]; |
684 | 0 | return true; |
685 | 0 | } else { |
686 | 0 | return false; |
687 | 0 | } |
688 | 0 | } |
689 | | |
690 | | static void |
691 | | add_row_references(const struct ovsdb_base_type *type, |
692 | | const union ovsdb_atom *atoms, size_t n_atoms, |
693 | | const struct uuid *exclude_uuid, |
694 | | struct uuid **dstsp, size_t *n_dstsp, |
695 | | size_t *allocated_dstsp) |
696 | 0 | { |
697 | 0 | if (type->type != OVSDB_TYPE_UUID || !type->uuid.refTableName) { |
698 | 0 | return; |
699 | 0 | } |
700 | | |
701 | 0 | for (size_t i = 0; i < n_atoms; i++) { |
702 | 0 | const struct uuid *uuid = &atoms[i].uuid; |
703 | 0 | if (!uuid_equals(uuid, exclude_uuid) |
704 | 0 | && !array_contains_uuid(uuid, *dstsp, *n_dstsp)) { |
705 | 0 | if (*n_dstsp >= *allocated_dstsp) { |
706 | 0 | *dstsp = x2nrealloc(*dstsp, allocated_dstsp, |
707 | 0 | sizeof **dstsp); |
708 | |
|
709 | 0 | } |
710 | 0 | (*dstsp)[*n_dstsp] = *uuid; |
711 | 0 | ++*n_dstsp; |
712 | 0 | } |
713 | 0 | } |
714 | 0 | } |
715 | | |
716 | | /* Checks for consistency in 'idl''s graph of arcs between database rows. Each |
717 | | * reference from one row to a different row should be reflected as a "struct |
718 | | * ovsdb_idl_arc" between those rows. |
719 | | * |
720 | | * This function is slow, big-O wise, and aborts if it finds an inconsistency, |
721 | | * thus it is only for use in test programs. */ |
722 | | void |
723 | | ovsdb_idl_check_consistency(const struct ovsdb_idl *idl) |
724 | 0 | { |
725 | | /* Consistency is broken while a transaction is in progress. */ |
726 | 0 | if (!idl->txn) { |
727 | 0 | return; |
728 | 0 | } |
729 | | |
730 | 0 | bool ok = true; |
731 | |
|
732 | 0 | struct uuid *dsts = NULL; |
733 | 0 | size_t allocated_dsts = 0; |
734 | |
|
735 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
736 | 0 | const struct ovsdb_idl_table *table = &idl->tables[i]; |
737 | 0 | const struct ovsdb_idl_table_class *class = table->class_; |
738 | |
|
739 | 0 | const struct ovsdb_idl_row *row; |
740 | 0 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { |
741 | 0 | size_t n_dsts = 0; |
742 | 0 | if (row->new_datum) { |
743 | 0 | size_t n_columns = shash_count(&row->table->columns); |
744 | 0 | for (size_t j = 0; j < n_columns; j++) { |
745 | 0 | const struct ovsdb_type *type = &class->columns[j].type; |
746 | 0 | const struct ovsdb_datum *datum; |
747 | |
|
748 | 0 | datum = ovsdb_idl_read(row, &class->columns[j]); |
749 | 0 | add_row_references(&type->key, |
750 | 0 | datum->keys, datum->n, &row->uuid, |
751 | 0 | &dsts, &n_dsts, &allocated_dsts); |
752 | 0 | add_row_references(&type->value, |
753 | 0 | datum->values, datum->n, &row->uuid, |
754 | 0 | &dsts, &n_dsts, &allocated_dsts); |
755 | 0 | } |
756 | 0 | } |
757 | 0 | const struct ovsdb_idl_arc *arc; |
758 | 0 | LIST_FOR_EACH (arc, src_node, &row->src_arcs) { |
759 | 0 | if (!remove_uuid_from_array(&arc->dst->uuid, |
760 | 0 | dsts, &n_dsts)) { |
761 | 0 | VLOG_ERR("unexpected arc from %s row "UUID_FMT" to %s " |
762 | 0 | "row "UUID_FMT, |
763 | 0 | table->class_->name, |
764 | 0 | UUID_ARGS(&row->uuid), |
765 | 0 | arc->dst->table->class_->name, |
766 | 0 | UUID_ARGS(&arc->dst->uuid)); |
767 | 0 | ok = false; |
768 | 0 | } |
769 | 0 | } |
770 | 0 | for (size_t j = 0; j < n_dsts; j++) { |
771 | 0 | VLOG_ERR("%s row "UUID_FMT" missing arc to row "UUID_FMT, |
772 | 0 | table->class_->name, UUID_ARGS(&row->uuid), |
773 | 0 | UUID_ARGS(&dsts[j])); |
774 | 0 | ok = false; |
775 | 0 | } |
776 | 0 | } |
777 | 0 | } |
778 | 0 | free(dsts); |
779 | 0 | ovs_assert(ok); |
780 | 0 | } |
781 | | |
782 | | static struct json * |
783 | | ovsdb_idl_compose_monitor_request(const struct json *schema_json, void *idl_) |
784 | 0 | { |
785 | 0 | struct ovsdb_idl *idl = idl_; |
786 | |
|
787 | 0 | struct shash *schema = ovsdb_cs_parse_schema(schema_json); |
788 | 0 | struct json *monitor_requests = json_object_create(); |
789 | |
|
790 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
791 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
792 | 0 | const struct ovsdb_idl_table_class *tc = table->class_; |
793 | 0 | struct json *monitor_request; |
794 | 0 | struct shash *table_schema |
795 | 0 | = schema ? shash_find_data(schema, table->class_->name) : NULL; |
796 | |
|
797 | 0 | struct json *columns |
798 | 0 | = table->need_table ? json_array_create_empty() : NULL; |
799 | |
|
800 | 0 | ovsdb_idl_schema_columns_clear(&table->schema_columns); |
801 | 0 | for (size_t j = 0; j < tc->n_columns; j++) { |
802 | 0 | const struct ovsdb_idl_column *column = &tc->columns[j]; |
803 | 0 | bool idl_has_column = false; |
804 | 0 | struct shash_node *node; |
805 | |
|
806 | 0 | if (table_schema) { |
807 | 0 | node = shash_find(table_schema, column->name); |
808 | 0 | if (node) { |
809 | 0 | idl_has_column = true; |
810 | 0 | shash_add(&table->schema_columns, |
811 | 0 | column->name, node->data); |
812 | 0 | shash_delete(table_schema, node); |
813 | 0 | } |
814 | 0 | } |
815 | |
|
816 | 0 | if (column->is_synthetic) { |
817 | 0 | if (idl_has_column) { |
818 | 0 | VLOG_WARN("%s table in %s database has synthetic " |
819 | 0 | "column %s", table->class_->name, |
820 | 0 | idl->class_->database, column->name); |
821 | 0 | } |
822 | 0 | } else if (table->modes[j] & OVSDB_IDL_MONITOR) { |
823 | 0 | if (table_schema && !idl_has_column) { |
824 | 0 | VLOG_WARN("%s table in %s database lacks %s column " |
825 | 0 | "(database needs upgrade?)", |
826 | 0 | table->class_->name, idl->class_->database, |
827 | 0 | column->name); |
828 | 0 | continue; |
829 | 0 | } |
830 | 0 | if (!columns) { |
831 | 0 | columns = json_array_create_empty(); |
832 | 0 | } |
833 | 0 | json_array_add(columns, json_string_create(column->name)); |
834 | 0 | } |
835 | 0 | } |
836 | |
|
837 | 0 | if (columns) { |
838 | 0 | if (schema && !table_schema) { |
839 | 0 | VLOG_WARN("%s database lacks %s table " |
840 | 0 | "(database needs upgrade?)", |
841 | 0 | idl->class_->database, table->class_->name); |
842 | 0 | json_destroy(columns); |
843 | 0 | table->in_server_schema = false; |
844 | 0 | continue; |
845 | 0 | } else if (schema && table_schema) { |
846 | 0 | table->in_server_schema = true; |
847 | 0 | } |
848 | | |
849 | 0 | monitor_request = json_object_create(); |
850 | 0 | json_object_put(monitor_request, "columns", columns); |
851 | 0 | json_object_put(monitor_requests, tc->name, |
852 | 0 | json_array_create_1(monitor_request)); |
853 | 0 | } |
854 | 0 | } |
855 | 0 | ovsdb_cs_free_schema(schema); |
856 | |
|
857 | 0 | return monitor_requests; |
858 | 0 | } |
859 | | |
860 | | static struct ovsdb_cs_ops ovsdb_idl_cs_ops = { |
861 | | ovsdb_idl_compose_monitor_request, |
862 | | }; |
863 | | |
864 | | const struct ovsdb_idl_class * |
865 | | ovsdb_idl_get_class(const struct ovsdb_idl *idl) |
866 | 0 | { |
867 | 0 | return idl->class_; |
868 | 0 | } |
869 | | |
870 | | /* Given 'column' in some table in 'class', returns the table's class. */ |
871 | | const struct ovsdb_idl_table_class * |
872 | | ovsdb_idl_table_class_from_column(const struct ovsdb_idl_class *class, |
873 | | const struct ovsdb_idl_column *column) |
874 | 0 | { |
875 | 0 | for (size_t i = 0; i < class->n_tables; i++) { |
876 | 0 | const struct ovsdb_idl_table_class *tc = &class->tables[i]; |
877 | 0 | if (column >= tc->columns && column < &tc->columns[tc->n_columns]) { |
878 | 0 | return tc; |
879 | 0 | } |
880 | 0 | } |
881 | | |
882 | 0 | OVS_NOT_REACHED(); |
883 | 0 | } |
884 | | |
885 | | /* Given 'column' in some table in 'idl', returns the table. */ |
886 | | static struct ovsdb_idl_table * |
887 | | ovsdb_idl_table_from_column(const struct ovsdb_idl *idl, |
888 | | const struct ovsdb_idl_column *column) |
889 | 0 | { |
890 | 0 | const struct ovsdb_idl_table_class *tc = |
891 | 0 | ovsdb_idl_table_class_from_column(idl->class_, column); |
892 | 0 | return &idl->tables[tc - idl->class_->tables]; |
893 | 0 | } |
894 | | |
895 | | static unsigned char * |
896 | | ovsdb_idl_get_mode(struct ovsdb_idl *idl, |
897 | | const struct ovsdb_idl_column *column) |
898 | 0 | { |
899 | 0 | ovs_assert(!idl->change_seqno); |
900 | |
|
901 | 0 | const struct ovsdb_idl_table *table = ovsdb_idl_table_from_column(idl, |
902 | 0 | column); |
903 | 0 | return &table->modes[column - table->class_->columns]; |
904 | 0 | } |
905 | | |
906 | | static void |
907 | | ovsdb_idl_set_mode(struct ovsdb_idl *idl, |
908 | | const struct ovsdb_idl_column *column, |
909 | | unsigned char mode) |
910 | 0 | { |
911 | 0 | const struct ovsdb_idl_table *table = ovsdb_idl_table_from_column(idl, |
912 | 0 | column); |
913 | 0 | size_t column_idx = column - table->class_->columns; |
914 | |
|
915 | 0 | if (table->modes[column_idx] != mode) { |
916 | 0 | *ovsdb_idl_get_mode(idl, column) = mode; |
917 | 0 | } |
918 | 0 | } |
919 | | |
920 | | static void |
921 | | add_ref_table(struct ovsdb_idl *idl, const struct ovsdb_base_type *base) |
922 | 0 | { |
923 | 0 | if (base->type == OVSDB_TYPE_UUID && base->uuid.refTableName) { |
924 | 0 | struct ovsdb_idl_table *table; |
925 | |
|
926 | 0 | table = shash_find_data(&idl->table_by_name, base->uuid.refTableName); |
927 | 0 | if (table) { |
928 | 0 | table->need_table = true; |
929 | 0 | } else { |
930 | 0 | VLOG_WARN("%s IDL class missing referenced table %s", |
931 | 0 | idl->class_->database, base->uuid.refTableName); |
932 | 0 | } |
933 | 0 | } |
934 | 0 | } |
935 | | |
936 | | /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also |
937 | | * ensures that any tables referenced by 'column' will be replicated, even if |
938 | | * no columns in that table are selected for replication (see |
939 | | * ovsdb_idl_add_table() for more information). |
940 | | * |
941 | | * This function is only useful if 'monitor_everything_by_default' was false in |
942 | | * the call to ovsdb_idl_create(). This function should be called between |
943 | | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). |
944 | | */ |
945 | | void |
946 | | ovsdb_idl_add_column(struct ovsdb_idl *idl, |
947 | | const struct ovsdb_idl_column *column) |
948 | 0 | { |
949 | 0 | ovsdb_idl_set_mode(idl, column, OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT); |
950 | 0 | add_ref_table(idl, &column->type.key); |
951 | 0 | add_ref_table(idl, &column->type.value); |
952 | 0 | } |
953 | | |
954 | | /* Ensures that the table with class 'tc' will be replicated on 'idl' even if |
955 | | * no columns are selected for replication. Just the necessary data for table |
956 | | * references will be replicated (the UUID of the rows, for instance), any |
957 | | * columns not selected for replication will remain unreplicated. |
958 | | * This can be useful because it allows 'idl' to keep track of what rows in the |
959 | | * table actually exist, which in turn allows columns that reference the table |
960 | | * to have accurate contents. (The IDL presents the database with references to |
961 | | * rows that do not exist removed.) |
962 | | * |
963 | | * This function is only useful if 'monitor_everything_by_default' was false in |
964 | | * the call to ovsdb_idl_create(). This function should be called between |
965 | | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). |
966 | | */ |
967 | | void |
968 | | ovsdb_idl_add_table(struct ovsdb_idl *idl, |
969 | | const struct ovsdb_idl_table_class *tc) |
970 | 0 | { |
971 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
972 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
973 | |
|
974 | 0 | if (table->class_ == tc) { |
975 | 0 | table->need_table = true; |
976 | 0 | return; |
977 | 0 | } |
978 | 0 | } |
979 | | |
980 | 0 | OVS_NOT_REACHED(); |
981 | 0 | } |
982 | | |
983 | | /* Returns 'true' if the 'idl' has seen the table for the 'table_class' |
984 | | * in the schema reported by the server. Returns 'false' otherwise. |
985 | | * |
986 | | * Always returns 'false' if idl has never been connected. |
987 | | * |
988 | | * Please see ovsdb_idl_compose_monitor_request() which sets |
989 | | * 'struct ovsdb_idl_table *'->in_server_schema accordingly. |
990 | | * |
991 | | * Usually this function is used indirectly through one of the |
992 | | * "server_has_table" functions generated by ovsdb-idlc. */ |
993 | | bool |
994 | | ovsdb_idl_server_has_table(const struct ovsdb_idl *idl, |
995 | | const struct ovsdb_idl_table_class *table_class) |
996 | 0 | { |
997 | 0 | const struct ovsdb_idl_table *table = |
998 | 0 | ovsdb_idl_table_from_class(idl, table_class); |
999 | |
|
1000 | 0 | return (table && table->in_server_schema); |
1001 | 0 | } |
1002 | | |
1003 | | /* Returns 'true' if the 'idl' has seen the 'column' in the schema |
1004 | | * reported by the server. Returns 'false' otherwise. |
1005 | | * |
1006 | | * Always returns 'false' if idl has never been connected. |
1007 | | * |
1008 | | * Please see ovsdb_idl_compose_monitor_request() which sets |
1009 | | * 'struct ovsdb_idl_table *'->schema_columns accordingly. |
1010 | | * |
1011 | | * Usually this function is used indirectly through one of the |
1012 | | * "server_has_column" functions generated by ovsdb-idlc. */ |
1013 | | bool |
1014 | | ovsdb_idl_server_has_column(const struct ovsdb_idl *idl, |
1015 | | const struct ovsdb_idl_column *column) |
1016 | 0 | { |
1017 | 0 | const struct ovsdb_idl_table *table = |
1018 | 0 | ovsdb_idl_table_from_column(idl, column); |
1019 | |
|
1020 | 0 | return (table->in_server_schema && shash_find(&table->schema_columns, |
1021 | 0 | column->name)); |
1022 | 0 | } |
1023 | | |
1024 | | /* Returns the type of a 'column' as defined in the schema reported |
1025 | | * by the server if the 'idl' has seen the 'column' in that schema. |
1026 | | * Returns NULL otherwise. |
1027 | | * |
1028 | | * Always returns NULL if idl has never been connected. |
1029 | | * |
1030 | | * Please see ovsdb_idl_compose_monitor_request() which sets |
1031 | | * 'struct ovsdb_idl_table *'->schema_columns accordingly. |
1032 | | * |
1033 | | * Usually this function is used indirectly through one of the |
1034 | | * "server_column_type" functions generated by ovsdb-idlc. |
1035 | | * |
1036 | | * Having different types between the server and the client in many |
1037 | | * cases will result in complete communication breakdown, so this |
1038 | | * function is mostly useful for checking for type constraints (enum, |
1039 | | * n_max) in case the server's schema is older or newer. */ |
1040 | | const struct ovsdb_type * |
1041 | | ovsdb_idl_server_column_type(const struct ovsdb_idl *idl, |
1042 | | const struct ovsdb_idl_column *column) |
1043 | 0 | { |
1044 | 0 | const struct ovsdb_idl_table *table = |
1045 | 0 | ovsdb_idl_table_from_column(idl, column); |
1046 | |
|
1047 | 0 | if (!table->in_server_schema) { |
1048 | 0 | return NULL; |
1049 | 0 | } |
1050 | | |
1051 | 0 | return shash_find_data(&table->schema_columns, column->name); |
1052 | 0 | } |
1053 | | |
1054 | | /* A single clause within an ovsdb_idl_condition. */ |
1055 | | struct ovsdb_idl_clause { |
1056 | | struct hmap_node hmap_node; /* In struct ovsdb_idl_condition. */ |
1057 | | enum ovsdb_function function; /* Never OVSDB_F_TRUE or OVSDB_F_FALSE. */ |
1058 | | const struct ovsdb_idl_column *column; /* Must be nonnull. */ |
1059 | | struct ovsdb_datum arg; /* Has ovsdb_type ->column->type. */ |
1060 | | }; |
1061 | | |
1062 | | static uint32_t |
1063 | | ovsdb_idl_clause_hash(const struct ovsdb_idl_clause *clause) |
1064 | 0 | { |
1065 | 0 | uint32_t hash = hash_pointer(clause->column, clause->function); |
1066 | 0 | return ovsdb_datum_hash(&clause->arg, &clause->column->type, hash); |
1067 | 0 | } |
1068 | | |
1069 | | static int |
1070 | | ovsdb_idl_clause_equals(const struct ovsdb_idl_clause *a, |
1071 | | const struct ovsdb_idl_clause *b) |
1072 | 0 | { |
1073 | 0 | return (a->function == b->function |
1074 | 0 | && a->column == b->column |
1075 | 0 | && ovsdb_datum_equals(&a->arg, &b->arg, &a->column->type)); |
1076 | 0 | } |
1077 | | |
1078 | | static struct json * |
1079 | | ovsdb_idl_clause_to_json(const struct ovsdb_idl_clause *clause) |
1080 | 0 | { |
1081 | 0 | const char *function = ovsdb_function_to_string(clause->function); |
1082 | 0 | return json_array_create_3(json_string_create(clause->column->name), |
1083 | 0 | json_string_create(function), |
1084 | 0 | ovsdb_datum_to_json(&clause->arg, |
1085 | 0 | &clause->column->type)); |
1086 | 0 | } |
1087 | | |
1088 | | static void |
1089 | | ovsdb_idl_clause_destroy(struct ovsdb_idl_clause *clause) |
1090 | 0 | { |
1091 | 0 | if (clause) { |
1092 | 0 | ovsdb_datum_destroy(&clause->arg, &clause->column->type); |
1093 | 0 | free(clause); |
1094 | 0 | } |
1095 | 0 | } |
1096 | | |
1097 | | /* ovsdb_idl_condition. */ |
1098 | | |
1099 | | void |
1100 | | ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd) |
1101 | 0 | { |
1102 | 0 | hmap_init(&cnd->clauses); |
1103 | 0 | cnd->is_true = false; |
1104 | 0 | } |
1105 | | |
1106 | | void |
1107 | | ovsdb_idl_condition_destroy(struct ovsdb_idl_condition *cond) |
1108 | 0 | { |
1109 | 0 | if (cond) { |
1110 | 0 | ovsdb_idl_condition_clear(cond); |
1111 | 0 | hmap_destroy(&cond->clauses); |
1112 | 0 | } |
1113 | 0 | } |
1114 | | |
1115 | | void |
1116 | | ovsdb_idl_condition_clear(struct ovsdb_idl_condition *cond) |
1117 | 0 | { |
1118 | 0 | struct ovsdb_idl_clause *clause; |
1119 | 0 | HMAP_FOR_EACH_SAFE (clause, hmap_node, &cond->clauses) { |
1120 | 0 | hmap_remove(&cond->clauses, &clause->hmap_node); |
1121 | 0 | ovsdb_idl_clause_destroy(clause); |
1122 | 0 | } |
1123 | 0 | cond->is_true = false; |
1124 | 0 | } |
1125 | | |
1126 | | bool |
1127 | | ovsdb_idl_condition_is_true(const struct ovsdb_idl_condition *condition) |
1128 | 0 | { |
1129 | 0 | return condition->is_true; |
1130 | 0 | } |
1131 | | |
1132 | | static struct ovsdb_idl_clause * |
1133 | | ovsdb_idl_condition_find_clause(const struct ovsdb_idl_condition *condition, |
1134 | | const struct ovsdb_idl_clause *target, |
1135 | | uint32_t hash) |
1136 | 0 | { |
1137 | 0 | struct ovsdb_idl_clause *clause; |
1138 | 0 | HMAP_FOR_EACH_WITH_HASH (clause, hmap_node, hash, &condition->clauses) { |
1139 | 0 | if (ovsdb_idl_clause_equals(clause, target)) { |
1140 | 0 | return clause; |
1141 | 0 | } |
1142 | 0 | } |
1143 | 0 | return NULL; |
1144 | 0 | } |
1145 | | |
1146 | | static void |
1147 | | ovsdb_idl_condition_add_clause__(struct ovsdb_idl_condition *condition, |
1148 | | const struct ovsdb_idl_clause *src, |
1149 | | uint32_t hash) |
1150 | 0 | { |
1151 | 0 | struct ovsdb_idl_clause *clause = xmalloc(sizeof *clause); |
1152 | 0 | clause->function = src->function; |
1153 | 0 | clause->column = src->column; |
1154 | 0 | ovsdb_datum_clone(&clause->arg, &src->arg); |
1155 | 0 | hmap_insert(&condition->clauses, &clause->hmap_node, hash); |
1156 | 0 | } |
1157 | | |
1158 | | /* Adds a clause to the condition for replicating the table with class 'tc' in |
1159 | | * 'idl'. |
1160 | | * |
1161 | | * The IDL replicates only rows in a table that satisfy at least one clause in |
1162 | | * the table's condition. The default condition for a table has a single |
1163 | | * clause with function OVSDB_F_TRUE, so that the IDL replicates all rows in |
1164 | | * the table. When the IDL client replaces the default condition by one of its |
1165 | | * own, the condition can have any number of clauses. If it has no conditions, |
1166 | | * then no rows are replicated. |
1167 | | * |
1168 | | * Two distinct of clauses can usefully be added: |
1169 | | * |
1170 | | * - A 'function' of OVSDB_F_TRUE. A "true" clause causes every row to be |
1171 | | * replicated, regardless of whether other clauses exist. 'column' and |
1172 | | * 'arg' are ignored. |
1173 | | * |
1174 | | * - Binary 'functions' add a clause of the form "<column> <function> |
1175 | | * <arg>", e.g. "column == 5" or "column <= 10". In this case, 'arg' must |
1176 | | * have a type that is compatible with 'column'. |
1177 | | */ |
1178 | | void |
1179 | | ovsdb_idl_condition_add_clause(struct ovsdb_idl_condition *condition, |
1180 | | enum ovsdb_function function, |
1181 | | const struct ovsdb_idl_column *column, |
1182 | | const struct ovsdb_datum *arg) |
1183 | 0 | { |
1184 | 0 | if (condition->is_true) { |
1185 | | /* Adding a clause to an always-true condition has no effect. */ |
1186 | 0 | } else if (function == OVSDB_F_TRUE) { |
1187 | 0 | ovsdb_idl_condition_add_clause_true(condition); |
1188 | 0 | } else if (function == OVSDB_F_FALSE) { |
1189 | | /* Adding a "false" clause never has any effect. */ |
1190 | 0 | } else { |
1191 | 0 | struct ovsdb_idl_clause clause = { |
1192 | 0 | .function = function, |
1193 | 0 | .column = column, |
1194 | 0 | }; |
1195 | 0 | ovsdb_datum_clone(&clause.arg, arg); |
1196 | |
|
1197 | 0 | uint32_t hash = ovsdb_idl_clause_hash(&clause); |
1198 | 0 | if (!ovsdb_idl_condition_find_clause(condition, &clause, hash)) { |
1199 | 0 | ovsdb_idl_condition_add_clause__(condition, &clause, hash); |
1200 | 0 | } |
1201 | 0 | ovsdb_datum_destroy(&clause.arg, &column->type); |
1202 | 0 | } |
1203 | 0 | } |
1204 | | |
1205 | | void |
1206 | | ovsdb_idl_condition_add_clause_true(struct ovsdb_idl_condition *condition) |
1207 | 0 | { |
1208 | 0 | if (!condition->is_true) { |
1209 | 0 | ovsdb_idl_condition_clear(condition); |
1210 | 0 | condition->is_true = true; |
1211 | 0 | } |
1212 | 0 | } |
1213 | | |
1214 | | static struct json * |
1215 | | ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd) |
1216 | 0 | { |
1217 | 0 | if (cnd->is_true) { |
1218 | 0 | return NULL; |
1219 | 0 | } |
1220 | | |
1221 | 0 | size_t n = hmap_count(&cnd->clauses); |
1222 | 0 | if (!n) { |
1223 | 0 | return json_array_create_1(json_boolean_create(false)); |
1224 | 0 | } |
1225 | | |
1226 | 0 | struct json **clauses = xmalloc(n * sizeof *clauses); |
1227 | 0 | const struct ovsdb_idl_clause *clause; |
1228 | 0 | size_t i = 0; |
1229 | 0 | HMAP_FOR_EACH (clause, hmap_node, &cnd->clauses) { |
1230 | 0 | clauses[i++] = ovsdb_idl_clause_to_json(clause); |
1231 | 0 | } |
1232 | 0 | ovs_assert(i == n); |
1233 | 0 | return json_array_create(clauses, n); |
1234 | 0 | } |
1235 | | |
1236 | | /* Sets the replication condition for 'tc' in 'idl' to 'condition' and |
1237 | | * arranges to send the new condition to the database server. |
1238 | | * |
1239 | | * Return the next conditional update sequence number. When this |
1240 | | * value and ovsdb_idl_get_condition_seqno() matches, the 'idl' |
1241 | | * contains rows that match the 'condition'. */ |
1242 | | unsigned int |
1243 | | ovsdb_idl_set_condition(struct ovsdb_idl *idl, |
1244 | | const struct ovsdb_idl_table_class *tc, |
1245 | | const struct ovsdb_idl_condition *condition) |
1246 | 0 | { |
1247 | 0 | struct json *cond_json = ovsdb_idl_condition_to_json(condition); |
1248 | 0 | unsigned int seqno = ovsdb_cs_set_condition(idl->cs, tc->name, cond_json); |
1249 | 0 | json_destroy(cond_json); |
1250 | 0 | return seqno; |
1251 | 0 | } |
1252 | | |
1253 | | /* Turns off OVSDB_IDL_ALERT and OVSDB_IDL_TRACK for 'column' in 'idl'. |
1254 | | * |
1255 | | * This function should be called between ovsdb_idl_create() and the first call |
1256 | | * to ovsdb_idl_run(). |
1257 | | */ |
1258 | | void |
1259 | | ovsdb_idl_omit_alert(struct ovsdb_idl *idl, |
1260 | | const struct ovsdb_idl_column *column) |
1261 | 0 | { |
1262 | 0 | *ovsdb_idl_get_mode(idl, column) &= ~(OVSDB_IDL_ALERT | OVSDB_IDL_TRACK); |
1263 | 0 | } |
1264 | | |
1265 | | /* Sets the mode for 'column' in 'idl' to 0. See the big comment above |
1266 | | * OVSDB_IDL_MONITOR for details. |
1267 | | * |
1268 | | * This function should be called between ovsdb_idl_create() and the first call |
1269 | | * to ovsdb_idl_run(). |
1270 | | */ |
1271 | | void |
1272 | | ovsdb_idl_omit(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) |
1273 | 0 | { |
1274 | 0 | *ovsdb_idl_get_mode(idl, column) = 0; |
1275 | 0 | } |
1276 | | |
1277 | | /* Returns the most recent IDL change sequence number that caused a |
1278 | | * insert, modify or delete update to the table with class 'table_class'. |
1279 | | */ |
1280 | | unsigned int |
1281 | | ovsdb_idl_table_get_seqno(const struct ovsdb_idl *idl, |
1282 | | const struct ovsdb_idl_table_class *table_class) |
1283 | 0 | { |
1284 | 0 | struct ovsdb_idl_table *table |
1285 | 0 | = ovsdb_idl_table_from_class(idl, table_class); |
1286 | 0 | unsigned int max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_INSERT]; |
1287 | |
|
1288 | 0 | if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]) { |
1289 | 0 | max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]; |
1290 | 0 | } |
1291 | 0 | if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_DELETE]) { |
1292 | 0 | max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_DELETE]; |
1293 | 0 | } |
1294 | 0 | return max_seqno; |
1295 | 0 | } |
1296 | | |
1297 | | /* For each row that contains tracked columns, IDL stores the most |
1298 | | * recent IDL change sequence numbers associateed with insert, modify |
1299 | | * and delete updates to the table. |
1300 | | */ |
1301 | | unsigned int |
1302 | | ovsdb_idl_row_get_seqno(const struct ovsdb_idl_row *row, |
1303 | | enum ovsdb_idl_change change) |
1304 | 0 | { |
1305 | 0 | return row->change_seqno[change]; |
1306 | 0 | } |
1307 | | |
1308 | | /* Turns on OVSDB_IDL_TRACK for 'column' in 'idl', ensuring that |
1309 | | * all rows whose 'column' is modified are traced. Similarly, insert |
1310 | | * or delete of rows having 'column' are tracked. Clients are able |
1311 | | * to retrieve the tracked rows with the ovsdb_idl_track_get_*() |
1312 | | * functions. |
1313 | | * |
1314 | | * This function should be called between ovsdb_idl_create() and |
1315 | | * the first call to ovsdb_idl_run(). The column to be tracked |
1316 | | * should have OVSDB_IDL_ALERT turned on. |
1317 | | */ |
1318 | | void |
1319 | | ovsdb_idl_track_add_column(struct ovsdb_idl *idl, |
1320 | | const struct ovsdb_idl_column *column) |
1321 | 0 | { |
1322 | 0 | if (!(*ovsdb_idl_get_mode(idl, column) & OVSDB_IDL_ALERT)) { |
1323 | 0 | ovsdb_idl_add_column(idl, column); |
1324 | 0 | } |
1325 | 0 | *ovsdb_idl_get_mode(idl, column) |= OVSDB_IDL_TRACK; |
1326 | 0 | } |
1327 | | |
1328 | | void |
1329 | | ovsdb_idl_track_add_all(struct ovsdb_idl *idl) |
1330 | 0 | { |
1331 | 0 | size_t i, j; |
1332 | |
|
1333 | 0 | for (i = 0; i < idl->class_->n_tables; i++) { |
1334 | 0 | const struct ovsdb_idl_table_class *tc = &idl->class_->tables[i]; |
1335 | |
|
1336 | 0 | for (j = 0; j < tc->n_columns; j++) { |
1337 | 0 | const struct ovsdb_idl_column *column = &tc->columns[j]; |
1338 | 0 | ovsdb_idl_track_add_column(idl, column); |
1339 | 0 | } |
1340 | 0 | } |
1341 | 0 | } |
1342 | | |
1343 | | /* Returns true if 'table' has any tracked column. */ |
1344 | | bool |
1345 | | ovsdb_idl_track_is_set(struct ovsdb_idl_table *table) |
1346 | 0 | { |
1347 | 0 | size_t i; |
1348 | |
|
1349 | 0 | for (i = 0; i < table->class_->n_columns; i++) { |
1350 | 0 | if (table->modes[i] & OVSDB_IDL_TRACK) { |
1351 | 0 | return true; |
1352 | 0 | } |
1353 | 0 | } |
1354 | 0 | return false; |
1355 | 0 | } |
1356 | | |
1357 | | /* Returns the first tracked row in table with class 'table_class' |
1358 | | * for the specified 'idl'. Returns NULL if there are no tracked rows. |
1359 | | * Pure orphan rows, i.e. rows that never had any datum, are skipped. */ |
1360 | | const struct ovsdb_idl_row * |
1361 | | ovsdb_idl_track_get_first(const struct ovsdb_idl *idl, |
1362 | | const struct ovsdb_idl_table_class *table_class) |
1363 | 0 | { |
1364 | 0 | struct ovsdb_idl_table *table |
1365 | 0 | = ovsdb_idl_table_from_class(idl, table_class); |
1366 | 0 | struct ovsdb_idl_row *row; |
1367 | |
|
1368 | 0 | LIST_FOR_EACH (row, track_node, &table->track_list) { |
1369 | 0 | if (!ovsdb_idl_row_is_orphan(row) || row->tracked_old_datum) { |
1370 | 0 | return row; |
1371 | 0 | } |
1372 | 0 | } |
1373 | 0 | return NULL; |
1374 | 0 | } |
1375 | | |
1376 | | /* Returns the next tracked row in table after the specified 'row' |
1377 | | * (in no particular order). Returns NULL if there are no tracked rows. |
1378 | | * Pure orphan rows, i.e. rows that never had any datum, are skipped. */ |
1379 | | const struct ovsdb_idl_row * |
1380 | | ovsdb_idl_track_get_next(const struct ovsdb_idl_row *row) |
1381 | 0 | { |
1382 | 0 | struct ovsdb_idl_table *table = row->table; |
1383 | |
|
1384 | 0 | LIST_FOR_EACH_CONTINUE (row, track_node, &table->track_list) { |
1385 | 0 | if (!ovsdb_idl_row_is_orphan(row) || row->tracked_old_datum) { |
1386 | 0 | return row; |
1387 | 0 | } |
1388 | 0 | } |
1389 | 0 | return NULL; |
1390 | 0 | } |
1391 | | |
1392 | | /* Returns true if a tracked 'column' in 'row' was updated by IDL, false |
1393 | | * otherwise. The tracking data is cleared by ovsdb_idl_track_clear() |
1394 | | * |
1395 | | * Function returns false if 'column' is not tracked (see |
1396 | | * ovsdb_idl_track_add_column()). |
1397 | | */ |
1398 | | bool |
1399 | | ovsdb_idl_track_is_updated(const struct ovsdb_idl_row *row, |
1400 | | const struct ovsdb_idl_column *column) |
1401 | 0 | { |
1402 | 0 | const struct ovsdb_idl_table_class *class; |
1403 | 0 | size_t column_idx; |
1404 | |
|
1405 | 0 | class = row->table->class_; |
1406 | 0 | column_idx = column - class->columns; |
1407 | |
|
1408 | 0 | if (row->updated && bitmap_is_set(row->updated, column_idx)) { |
1409 | 0 | return true; |
1410 | 0 | } else { |
1411 | 0 | return false; |
1412 | 0 | } |
1413 | 0 | } |
1414 | | |
1415 | | static void |
1416 | | ovsdb_idl_track_clear__(struct ovsdb_idl *idl, bool flush_all) |
1417 | 0 | { |
1418 | 0 | size_t i; |
1419 | |
|
1420 | 0 | for (i = 0; i < idl->class_->n_tables; i++) { |
1421 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
1422 | |
|
1423 | 0 | if (!ovs_list_is_empty(&table->track_list)) { |
1424 | 0 | struct ovsdb_idl_row *row; |
1425 | |
|
1426 | 0 | LIST_FOR_EACH_SAFE (row, track_node, &table->track_list) { |
1427 | 0 | if (row->updated) { |
1428 | 0 | free(row->updated); |
1429 | 0 | row->updated = NULL; |
1430 | 0 | } |
1431 | 0 | ovsdb_idl_row_untrack_change(row); |
1432 | 0 | ovsdb_idl_row_clear_changeseqno(row); |
1433 | |
|
1434 | 0 | if (ovsdb_idl_row_is_orphan(row)) { |
1435 | 0 | ovsdb_idl_row_unparse(row); |
1436 | 0 | if (row->tracked_old_datum) { |
1437 | 0 | const struct ovsdb_idl_table_class *class = |
1438 | 0 | row->table->class_; |
1439 | 0 | for (size_t c = 0; c < class->n_columns; c++) { |
1440 | 0 | ovsdb_datum_destroy(&row->tracked_old_datum[c], |
1441 | 0 | &class->columns[c].type); |
1442 | 0 | } |
1443 | 0 | free(row->tracked_old_datum); |
1444 | 0 | row->tracked_old_datum = NULL; |
1445 | 0 | } |
1446 | | |
1447 | | /* Rows that were reused as orphan after being processed |
1448 | | * for deletion are still in the table hmap and will be |
1449 | | * cleaned up when their src arcs are removed. These rows |
1450 | | * will not be reported anymore as "deleted" to IDL |
1451 | | * clients. |
1452 | | * |
1453 | | * The exception is when 'destroy' is explicitly set to |
1454 | | * 'true' which usually happens when the complete IDL |
1455 | | * contents are being flushed. |
1456 | | */ |
1457 | 0 | if (flush_all || ovs_list_is_empty(&row->dst_arcs)) { |
1458 | 0 | free(row); |
1459 | 0 | } |
1460 | 0 | } |
1461 | 0 | } |
1462 | 0 | } |
1463 | 0 | } |
1464 | 0 | } |
1465 | | |
1466 | | /* Flushes the tracked rows. Client calls this function after calling |
1467 | | * ovsdb_idl_run() and read all tracked rows with the ovsdb_idl_track_get_*() |
1468 | | * functions. This is usually done at the end of the client's processing |
1469 | | * loop when it is ready to do ovsdb_idl_run() again. |
1470 | | */ |
1471 | | void |
1472 | | ovsdb_idl_track_clear(struct ovsdb_idl *idl) |
1473 | 0 | { |
1474 | 0 | ovsdb_idl_track_clear__(idl, false); |
1475 | 0 | } |
1476 | | |
1477 | | /* Sets or clears (depending on 'enable') OVSDB_IDL_WRITE_CHANGED_ONLY |
1478 | | * for 'column' in 'idl', ensuring that the column will be included in a |
1479 | | * transaction only if its value has actually changed locally. Normally |
1480 | | * read/write columns that are written to are always included in the |
1481 | | * transaction but, in specific cases, when the application doesn't |
1482 | | * require atomicity of writes across different columns, the ones that |
1483 | | * don't change value may be skipped. |
1484 | | * |
1485 | | * This function should be called between ovsdb_idl_create() and |
1486 | | * the first call to ovsdb_idl_run(). |
1487 | | */ |
1488 | | void |
1489 | | ovsdb_idl_set_write_changed_only(struct ovsdb_idl *idl, |
1490 | | const struct ovsdb_idl_column *column, |
1491 | | bool enable) |
1492 | 0 | { |
1493 | 0 | if (enable) { |
1494 | 0 | *ovsdb_idl_get_mode(idl, column) |= OVSDB_IDL_WRITE_CHANGED_ONLY; |
1495 | 0 | } else { |
1496 | 0 | *ovsdb_idl_get_mode(idl, column) &= ~OVSDB_IDL_WRITE_CHANGED_ONLY; |
1497 | 0 | } |
1498 | 0 | } |
1499 | | |
1500 | | /* Helper function to wrap calling ovsdb_idl_set_write_changed_only() for |
1501 | | * all columns that are part of 'idl'. |
1502 | | */ |
1503 | | void |
1504 | | ovsdb_idl_set_write_changed_only_all(struct ovsdb_idl *idl, bool enable) |
1505 | 0 | { |
1506 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
1507 | 0 | const struct ovsdb_idl_table_class *tc = &idl->class_->tables[i]; |
1508 | |
|
1509 | 0 | for (size_t j = 0; j < tc->n_columns; j++) { |
1510 | 0 | const struct ovsdb_idl_column *column = &tc->columns[j]; |
1511 | 0 | ovsdb_idl_set_write_changed_only(idl, column, enable); |
1512 | 0 | } |
1513 | 0 | } |
1514 | 0 | } |
1515 | | |
1516 | | static void |
1517 | | log_parse_update_error(struct ovsdb_error *error) |
1518 | 0 | { |
1519 | 0 | if (!VLOG_DROP_WARN(&syntax_rl)) { |
1520 | 0 | char *s = ovsdb_error_to_string(error); |
1521 | 0 | VLOG_WARN_RL(&syntax_rl, "%s", s); |
1522 | 0 | free(s); |
1523 | 0 | } |
1524 | 0 | ovsdb_error_destroy(error); |
1525 | 0 | } |
1526 | | |
1527 | | static struct ovsdb_error * |
1528 | | ovsdb_idl_parse_update__(struct ovsdb_idl *idl, |
1529 | | const struct ovsdb_cs_db_update *du) |
1530 | 0 | { |
1531 | 0 | for (size_t i = 0; i < du->n; i++) { |
1532 | 0 | const struct ovsdb_cs_table_update *tu = &du->table_updates[i]; |
1533 | |
|
1534 | 0 | struct ovsdb_idl_table *table = shash_find_data(&idl->table_by_name, |
1535 | 0 | tu->table_name); |
1536 | 0 | if (!table) { |
1537 | 0 | return ovsdb_syntax_error( |
1538 | 0 | NULL, NULL, "update to unknown table \"%s\"", tu->table_name); |
1539 | 0 | } |
1540 | | |
1541 | 0 | for (size_t j = 0; j < tu->n; j++) { |
1542 | 0 | const struct ovsdb_cs_row_update *ru = &tu->row_updates[j]; |
1543 | 0 | switch (ovsdb_idl_process_update(table, ru)) { |
1544 | 0 | case OVSDB_IDL_UPDATE_DB_CHANGED: |
1545 | 0 | idl->change_seqno++; |
1546 | 0 | break; |
1547 | 0 | case OVSDB_IDL_UPDATE_NO_CHANGES: |
1548 | 0 | break; |
1549 | 0 | case OVSDB_IDL_UPDATE_INCONSISTENT: |
1550 | 0 | ovsdb_cs_flag_inconsistency(idl->cs); |
1551 | 0 | return ovsdb_error(NULL, |
1552 | 0 | "row update received for inconsistent " |
1553 | 0 | "IDL: reconnecting IDL and resync all " |
1554 | 0 | "data"); |
1555 | 0 | } |
1556 | 0 | } |
1557 | 0 | } |
1558 | | |
1559 | 0 | return NULL; |
1560 | 0 | } |
1561 | | |
1562 | | static void |
1563 | | ovsdb_idl_parse_update(struct ovsdb_idl *idl, |
1564 | | const struct ovsdb_cs_update_event *update) |
1565 | 0 | { |
1566 | 0 | if (update->monitor_reply) { |
1567 | | /* XXX This isn't semantically required, because we only need to |
1568 | | * increment change_seqno if there's a real change, which we'll do |
1569 | | * below, but older versions of the IDL always incremented change_seqno |
1570 | | * when a monitor reply was received and if we don't do it then tests |
1571 | | * will fail. */ |
1572 | 0 | idl->change_seqno++; |
1573 | 0 | } |
1574 | |
|
1575 | 0 | struct ovsdb_cs_db_update *du; |
1576 | 0 | struct ovsdb_error *error = ovsdb_cs_parse_db_update( |
1577 | 0 | update->table_updates, update->version, &du); |
1578 | 0 | if (!error) { |
1579 | 0 | if (update->clear) { |
1580 | 0 | ovsdb_idl_clear(idl); |
1581 | 0 | } |
1582 | 0 | error = ovsdb_idl_parse_update__(idl, du); |
1583 | 0 | } |
1584 | 0 | ovsdb_cs_db_update_destroy(du); |
1585 | 0 | if (error) { |
1586 | 0 | log_parse_update_error(error); |
1587 | 0 | } |
1588 | 0 | } |
1589 | | |
1590 | | /* Reparses references to rows that have been deleted in the current IDL run. |
1591 | | * |
1592 | | * To ensure that reference sources that are deleted are not reparsed, |
1593 | | * this function must be called after all updates have been processed in |
1594 | | * the current IDL run, i.e., after all calls to ovsdb_idl_parse_update(). |
1595 | | */ |
1596 | | static void |
1597 | | ovsdb_idl_reparse_deleted(struct ovsdb_idl *db) |
1598 | 0 | { |
1599 | 0 | struct ovsdb_idl_row *row; |
1600 | |
|
1601 | 0 | LIST_FOR_EACH_SAFE (row, track_node, &db->deleted_untracked_rows) { |
1602 | 0 | ovsdb_idl_row_untrack_change(row); |
1603 | 0 | add_tracked_change_for_references(row); |
1604 | 0 | ovsdb_idl_row_reparse_backrefs(row); |
1605 | | |
1606 | | /* Orphan rows that are still unreferenced or are part of tables that |
1607 | | * have change tracking enabled should be added to their table's |
1608 | | * 'track_list'. |
1609 | | */ |
1610 | 0 | if (ovs_list_is_empty(&row->dst_arcs) |
1611 | 0 | || ovsdb_idl_track_is_set(row->table)) { |
1612 | 0 | ovsdb_idl_row_track_change(row, OVSDB_IDL_CHANGE_DELETE); |
1613 | 0 | } |
1614 | 0 | } |
1615 | 0 | } |
1616 | | |
1617 | | /* Reparses rows that refer to rows that were inserted in the |
1618 | | * current IDL run. */ |
1619 | | static void |
1620 | | ovsdb_idl_reparse_refs_to_inserted(struct ovsdb_idl *db) |
1621 | 0 | { |
1622 | 0 | struct ovsdb_idl_row *row; |
1623 | |
|
1624 | 0 | LIST_FOR_EACH_POP (row, reparse_node, &db->rows_to_reparse) { |
1625 | 0 | ovs_list_init(&row->reparse_node); |
1626 | | |
1627 | | /* Skip rows that have been deleted in the meantime. */ |
1628 | 0 | if (ovsdb_idl_row_is_orphan(row)) { |
1629 | 0 | continue; |
1630 | 0 | } |
1631 | 0 | ovsdb_idl_row_unparse(row); |
1632 | 0 | ovsdb_idl_row_clear_arcs(row, false); |
1633 | 0 | ovsdb_idl_row_parse(row); |
1634 | 0 | } |
1635 | 0 | } |
1636 | | |
1637 | | static struct ovsdb_idl_row * |
1638 | | ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid) |
1639 | 0 | { |
1640 | 0 | struct ovsdb_idl_row *row; |
1641 | |
|
1642 | 0 | HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) { |
1643 | 0 | if (uuid_equals(&row->uuid, uuid)) { |
1644 | 0 | return row; |
1645 | 0 | } |
1646 | 0 | } |
1647 | 0 | return NULL; |
1648 | 0 | } |
1649 | | |
1650 | | /* Returns OVSDB_IDL_UPDATE_DB_CHANGED if a column with mode |
1651 | | * OVSDB_IDL_MODE_RW changed. |
1652 | | * |
1653 | | * Some IDL inconsistencies can be detected when processing updates: |
1654 | | * - trying to insert an already existing row |
1655 | | * - trying to update a missing row |
1656 | | * - trying to delete a non existent row |
1657 | | * |
1658 | | * In such cases OVSDB_IDL_UPDATE_INCONSISTENT is returned. |
1659 | | * Even though the IDL client could recover, it's best to report the |
1660 | | * inconsistent state because the state the server is in is unknown so the |
1661 | | * safest thing to do is to retry (potentially connecting to a new server). |
1662 | | * |
1663 | | * Returns OVSDB_IDL_UPDATE_NO_CHANGES otherwise. |
1664 | | */ |
1665 | | static enum update_result |
1666 | | ovsdb_idl_process_update(struct ovsdb_idl_table *table, |
1667 | | const struct ovsdb_cs_row_update *ru) |
1668 | 0 | { |
1669 | 0 | const struct uuid *uuid = &ru->row_uuid; |
1670 | 0 | struct ovsdb_idl_row *row = ovsdb_idl_get_row(table, uuid); |
1671 | |
|
1672 | 0 | switch (ru->type) { |
1673 | 0 | case OVSDB_CS_ROW_DELETE: |
1674 | 0 | if (row && !ovsdb_idl_row_is_orphan(row)) { |
1675 | | /* XXX perhaps we should check the 'old' values? */ |
1676 | 0 | ovsdb_idl_delete_row(row); |
1677 | 0 | } else { |
1678 | 0 | VLOG_ERR_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" " |
1679 | 0 | "from table %s", |
1680 | 0 | UUID_ARGS(uuid), table->class_->name); |
1681 | 0 | return OVSDB_IDL_UPDATE_INCONSISTENT; |
1682 | 0 | } |
1683 | 0 | break; |
1684 | | |
1685 | 0 | case OVSDB_CS_ROW_INSERT: |
1686 | 0 | if (!row) { |
1687 | 0 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), |
1688 | 0 | ru->columns); |
1689 | 0 | } else if (ovsdb_idl_row_is_orphan(row)) { |
1690 | 0 | ovsdb_idl_row_untrack_change(row); |
1691 | 0 | ovsdb_idl_row_clear_changeseqno(row); |
1692 | 0 | ovsdb_idl_insert_row(row, ru->columns); |
1693 | 0 | } else { |
1694 | 0 | VLOG_ERR_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to " |
1695 | 0 | "table %s", UUID_ARGS(uuid), table->class_->name); |
1696 | 0 | return OVSDB_IDL_UPDATE_INCONSISTENT; |
1697 | 0 | } |
1698 | 0 | break; |
1699 | | |
1700 | 0 | case OVSDB_CS_ROW_UPDATE: |
1701 | 0 | case OVSDB_CS_ROW_XOR: |
1702 | 0 | if (row) { |
1703 | 0 | if (!ovsdb_idl_row_is_orphan(row)) { |
1704 | 0 | return ovsdb_idl_modify_row(row, ru->columns, |
1705 | 0 | ru->type == OVSDB_CS_ROW_XOR) |
1706 | 0 | ? OVSDB_IDL_UPDATE_DB_CHANGED |
1707 | 0 | : OVSDB_IDL_UPDATE_NO_CHANGES; |
1708 | 0 | } else { |
1709 | 0 | VLOG_ERR_RL(&semantic_rl, "cannot modify missing but " |
1710 | 0 | "referenced row "UUID_FMT" in table %s", |
1711 | 0 | UUID_ARGS(uuid), table->class_->name); |
1712 | 0 | return OVSDB_IDL_UPDATE_INCONSISTENT; |
1713 | 0 | } |
1714 | 0 | } else { |
1715 | 0 | VLOG_ERR_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" " |
1716 | 0 | "in table %s", UUID_ARGS(uuid), table->class_->name); |
1717 | 0 | return OVSDB_IDL_UPDATE_INCONSISTENT; |
1718 | 0 | } |
1719 | 0 | break; |
1720 | | |
1721 | 0 | default: |
1722 | 0 | OVS_NOT_REACHED(); |
1723 | 0 | } |
1724 | | |
1725 | 0 | return OVSDB_IDL_UPDATE_DB_CHANGED; |
1726 | 0 | } |
1727 | | |
1728 | | /* Recursively add rows to tracked change lists for all rows that reference |
1729 | | 'row'. */ |
1730 | | static void |
1731 | | add_tracked_change_for_references(struct ovsdb_idl_row *row) |
1732 | 0 | { |
1733 | 0 | const struct ovsdb_idl_arc *arc; |
1734 | 0 | LIST_FOR_EACH (arc, dst_node, &row->dst_arcs) { |
1735 | 0 | struct ovsdb_idl_row *ref = arc->src; |
1736 | |
|
1737 | 0 | if (ovs_list_is_empty(&ref->track_node) && |
1738 | 0 | ovsdb_idl_track_is_set(ref->table)) { |
1739 | |
|
1740 | 0 | ovsdb_idl_row_track_change(ref, OVSDB_IDL_CHANGE_MODIFY); |
1741 | 0 | add_tracked_change_for_references(ref); |
1742 | 0 | } |
1743 | 0 | } |
1744 | 0 | } |
1745 | | |
1746 | | |
1747 | | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
1748 | | * otherwise. |
1749 | | * |
1750 | | * Change 'row' either with the content of 'row_json' or by apply 'diff'. |
1751 | | * Caller needs to provide either valid 'row_json' or 'diff', but not |
1752 | | * both. */ |
1753 | | static bool |
1754 | | ovsdb_idl_row_change(struct ovsdb_idl_row *row, const struct shash *values, |
1755 | | bool xor, enum ovsdb_idl_change change) |
1756 | 0 | { |
1757 | 0 | struct ovsdb_idl_table *table = row->table; |
1758 | 0 | const struct ovsdb_idl_table_class *class = table->class_; |
1759 | 0 | struct shash_node *node; |
1760 | 0 | bool changed = false; |
1761 | |
|
1762 | 0 | SHASH_FOR_EACH (node, values) { |
1763 | 0 | const char *column_name = node->name; |
1764 | 0 | const struct ovsdb_idl_column *column; |
1765 | 0 | struct ovsdb_error *error; |
1766 | 0 | unsigned int column_idx; |
1767 | 0 | struct ovsdb_datum *old; |
1768 | 0 | bool datum_changed = false; |
1769 | |
|
1770 | 0 | column = shash_find_data(&table->columns, column_name); |
1771 | 0 | if (!column) { |
1772 | 0 | VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT, |
1773 | 0 | column_name, UUID_ARGS(&row->uuid)); |
1774 | 0 | continue; |
1775 | 0 | } |
1776 | | |
1777 | 0 | column_idx = column - table->class_->columns; |
1778 | 0 | old = &row->old_datum[column_idx]; |
1779 | |
|
1780 | 0 | if (xor) { |
1781 | 0 | struct ovsdb_datum diff; |
1782 | |
|
1783 | 0 | error = ovsdb_transient_datum_from_json(&diff, &column->type, |
1784 | 0 | node->data); |
1785 | 0 | if (!error) { |
1786 | 0 | error = ovsdb_datum_apply_diff_in_place(old, &diff, |
1787 | 0 | &column->type); |
1788 | 0 | ovsdb_datum_destroy(&diff, &column->type); |
1789 | 0 | datum_changed = true; |
1790 | 0 | } |
1791 | 0 | } else { |
1792 | 0 | struct ovsdb_datum datum; |
1793 | |
|
1794 | 0 | error = ovsdb_datum_from_json(&datum, &column->type, node->data, |
1795 | 0 | NULL); |
1796 | 0 | if (!error) { |
1797 | 0 | if (!ovsdb_datum_equals(old, &datum, &column->type)) { |
1798 | 0 | ovsdb_datum_swap(old, &datum); |
1799 | 0 | datum_changed = true; |
1800 | 0 | } |
1801 | 0 | ovsdb_datum_destroy(&datum, &column->type); |
1802 | 0 | } |
1803 | 0 | } |
1804 | |
|
1805 | 0 | if (error) { |
1806 | 0 | char *s = ovsdb_error_to_string_free(error); |
1807 | 0 | VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT |
1808 | 0 | " in table %s: %s", column_name, |
1809 | 0 | UUID_ARGS(&row->uuid), table->class_->name, s); |
1810 | 0 | free(s); |
1811 | 0 | continue; |
1812 | 0 | } |
1813 | | |
1814 | 0 | if (datum_changed && table->modes[column_idx] & OVSDB_IDL_ALERT) { |
1815 | 0 | changed = true; |
1816 | 0 | row->change_seqno[change] |
1817 | 0 | = row->table->change_seqno[change] |
1818 | 0 | = row->table->idl->change_seqno + 1; |
1819 | |
|
1820 | 0 | if (table->modes[column_idx] & OVSDB_IDL_TRACK) { |
1821 | 0 | if (ovs_list_is_empty(&row->track_node) && |
1822 | 0 | ovsdb_idl_track_is_set(row->table)) { |
1823 | 0 | ovs_list_push_back(&row->table->track_list, |
1824 | 0 | &row->track_node); |
1825 | 0 | } |
1826 | |
|
1827 | 0 | add_tracked_change_for_references(row); |
1828 | 0 | if (!row->updated) { |
1829 | 0 | row->updated = bitmap_allocate(class->n_columns); |
1830 | 0 | } |
1831 | 0 | bitmap_set1(row->updated, column_idx); |
1832 | 0 | } |
1833 | 0 | } |
1834 | 0 | } |
1835 | 0 | return changed; |
1836 | 0 | } |
1837 | | |
1838 | | /* When a row A refers to row B through a column with a "refTable" constraint, |
1839 | | * but row B does not exist, row B is called an "orphan row". Orphan rows |
1840 | | * should not persist, because the database enforces referential integrity, but |
1841 | | * they can appear transiently as changes from the database are received (the |
1842 | | * database doesn't try to topologically sort them and circular references mean |
1843 | | * it isn't always possible anyhow). |
1844 | | * |
1845 | | * This function returns true if 'row' is an orphan row, otherwise false. |
1846 | | */ |
1847 | | static bool |
1848 | | ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row) |
1849 | 0 | { |
1850 | 0 | return !row->old_datum && !row->new_datum; |
1851 | 0 | } |
1852 | | |
1853 | | /* Returns true if 'row' is conceptually part of the database as modified by |
1854 | | * the current transaction (if any), false otherwise. |
1855 | | * |
1856 | | * This function will return true if 'row' is not an orphan (see the comment on |
1857 | | * ovsdb_idl_row_is_orphan()) and: |
1858 | | * |
1859 | | * - 'row' exists in the database and has not been deleted within the |
1860 | | * current transaction (if any). |
1861 | | * |
1862 | | * - 'row' was inserted within the current transaction and has not been |
1863 | | * deleted. (In the latter case you should not have passed 'row' in at |
1864 | | * all, because ovsdb_idl_txn_delete() freed it.) |
1865 | | * |
1866 | | * This function will return false if 'row' is an orphan or if 'row' was |
1867 | | * deleted within the current transaction. |
1868 | | */ |
1869 | | static bool |
1870 | | ovsdb_idl_row_exists(const struct ovsdb_idl_row *row) |
1871 | 0 | { |
1872 | 0 | return row->new_datum != NULL; |
1873 | 0 | } |
1874 | | |
1875 | | static void |
1876 | | ovsdb_idl_row_parse(struct ovsdb_idl_row *row) |
1877 | 0 | { |
1878 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
1879 | 0 | size_t i; |
1880 | |
|
1881 | 0 | if (row->parsed) { |
1882 | 0 | ovsdb_idl_row_unparse(row); |
1883 | 0 | } |
1884 | 0 | for (i = 0; i < class->n_columns; i++) { |
1885 | 0 | const struct ovsdb_idl_column *c = &class->columns[i]; |
1886 | 0 | (c->parse)(row, &row->old_datum[i]); |
1887 | 0 | } |
1888 | 0 | row->parsed = true; |
1889 | 0 | } |
1890 | | |
1891 | | static void |
1892 | | ovsdb_idl_row_unparse(struct ovsdb_idl_row *row) |
1893 | 0 | { |
1894 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
1895 | 0 | size_t i; |
1896 | |
|
1897 | 0 | if (!row->parsed) { |
1898 | 0 | return; |
1899 | 0 | } |
1900 | 0 | for (i = 0; i < class->n_columns; i++) { |
1901 | 0 | const struct ovsdb_idl_column *c = &class->columns[i]; |
1902 | 0 | (c->unparse)(row); |
1903 | 0 | } |
1904 | 0 | row->parsed = false; |
1905 | 0 | } |
1906 | | |
1907 | | /* The OVSDB-IDL Compound Indexes feature allows for the creation of custom |
1908 | | * table indexes over one or more columns in the IDL. These indexes provide |
1909 | | * the ability to retrieve rows matching a particular search criteria and to |
1910 | | * iterate over a subset of rows in a defined order. |
1911 | | */ |
1912 | | |
1913 | | /* Generic comparator that can compare each index, using the custom |
1914 | | * configuration (an struct ovsdb_idl_index) passed to it. |
1915 | | * Not intended for direct usage. |
1916 | | */ |
1917 | | static int |
1918 | | ovsdb_idl_index_generic_comparer(const void *a, |
1919 | | const void *b, const void *conf) |
1920 | 0 | { |
1921 | 0 | const struct ovsdb_idl_column *column; |
1922 | 0 | const struct ovsdb_idl_index *index; |
1923 | 0 | size_t i; |
1924 | |
|
1925 | 0 | index = CONST_CAST(struct ovsdb_idl_index *, conf); |
1926 | |
|
1927 | 0 | if (a == b) { |
1928 | 0 | return 0; |
1929 | 0 | } |
1930 | | |
1931 | 0 | for (i = 0; i < index->n_columns; i++) { |
1932 | 0 | int val; |
1933 | 0 | if (index->columns[i].comparer) { |
1934 | 0 | val = index->columns[i].comparer(a, b); |
1935 | 0 | } else { |
1936 | 0 | column = index->columns[i].column; |
1937 | 0 | const struct ovsdb_idl_row *row_a, *row_b; |
1938 | 0 | row_a = CONST_CAST(struct ovsdb_idl_row *, a); |
1939 | 0 | row_b = CONST_CAST(struct ovsdb_idl_row *, b); |
1940 | 0 | const struct ovsdb_datum *datum_a, *datum_b; |
1941 | 0 | datum_a = ovsdb_idl_read(row_a, column); |
1942 | 0 | datum_b = ovsdb_idl_read(row_b, column); |
1943 | 0 | val = ovsdb_datum_compare_3way(datum_a, datum_b, &column->type); |
1944 | 0 | } |
1945 | |
|
1946 | 0 | if (val) { |
1947 | 0 | return index->columns[i].order == OVSDB_INDEX_ASC ? val : -val; |
1948 | 0 | } |
1949 | 0 | } |
1950 | | |
1951 | | /* If ins_del is true then a row is being inserted into or deleted from |
1952 | | * the index list. In this case, we augment the search key with |
1953 | | * additional values (row UUID and memory address) to create a unique |
1954 | | * search key in order to locate the correct entry efficiently and to |
1955 | | * ensure that the correct entry is deleted in the case of a "delete" |
1956 | | * operation. |
1957 | | */ |
1958 | 0 | if (index->ins_del) { |
1959 | 0 | const struct ovsdb_idl_row *row_a, *row_b; |
1960 | |
|
1961 | 0 | row_a = (const struct ovsdb_idl_row *) a; |
1962 | 0 | row_b = (const struct ovsdb_idl_row *) b; |
1963 | 0 | int value = uuid_compare_3way(&row_a->uuid, &row_b->uuid); |
1964 | |
|
1965 | 0 | return value ? value : (a < b) - (a > b); |
1966 | 0 | } else { |
1967 | 0 | return 0; |
1968 | 0 | } |
1969 | 0 | } |
1970 | | |
1971 | | /* Creates a new index for the given 'idl' and with the 'n' specified |
1972 | | * 'columns'. |
1973 | | * |
1974 | | * All indexes must be created before the first call to ovsdb_idl_run(). */ |
1975 | | struct ovsdb_idl_index * |
1976 | | ovsdb_idl_index_create(struct ovsdb_idl *idl, |
1977 | | const struct ovsdb_idl_index_column *columns, |
1978 | | size_t n) |
1979 | 0 | { |
1980 | 0 | ovs_assert(n > 0); |
1981 | |
|
1982 | 0 | struct ovsdb_idl_index *index = xzalloc(sizeof *index); |
1983 | |
|
1984 | 0 | index->table = ovsdb_idl_table_from_column(idl, columns[0].column); |
1985 | 0 | for (size_t i = 0; i < n; i++) { |
1986 | 0 | const struct ovsdb_idl_index_column *c = &columns[i]; |
1987 | 0 | ovs_assert(ovsdb_idl_table_from_column(idl, |
1988 | 0 | c->column) == index->table); |
1989 | 0 | ovs_assert(*ovsdb_idl_get_mode(idl, c->column) & OVSDB_IDL_MONITOR); |
1990 | 0 | } |
1991 | |
|
1992 | 0 | index->columns = xmemdup(columns, n * sizeof *columns); |
1993 | 0 | index->n_columns = n; |
1994 | 0 | index->skiplist = skiplist_create(ovsdb_idl_index_generic_comparer, index); |
1995 | |
|
1996 | 0 | ovs_list_push_back(&index->table->indexes, &index->node); |
1997 | |
|
1998 | 0 | return index; |
1999 | 0 | } |
2000 | | |
2001 | | struct ovsdb_idl_index * |
2002 | | ovsdb_idl_index_create1(struct ovsdb_idl *idl, |
2003 | | const struct ovsdb_idl_column *column1) |
2004 | 0 | { |
2005 | 0 | const struct ovsdb_idl_index_column columns[] = { |
2006 | 0 | { .column = column1 }, |
2007 | 0 | }; |
2008 | 0 | return ovsdb_idl_index_create(idl, columns, ARRAY_SIZE(columns)); |
2009 | 0 | } |
2010 | | |
2011 | | struct ovsdb_idl_index * |
2012 | | ovsdb_idl_index_create2(struct ovsdb_idl *idl, |
2013 | | const struct ovsdb_idl_column *column1, |
2014 | | const struct ovsdb_idl_column *column2) |
2015 | 0 | { |
2016 | 0 | const struct ovsdb_idl_index_column columns[] = { |
2017 | 0 | { .column = column1 }, |
2018 | 0 | { .column = column2 }, |
2019 | 0 | }; |
2020 | 0 | return ovsdb_idl_index_create(idl, columns, ARRAY_SIZE(columns)); |
2021 | 0 | } |
2022 | | |
2023 | | static void |
2024 | | ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *table) |
2025 | 0 | { |
2026 | 0 | struct ovsdb_idl_index *index; |
2027 | 0 | LIST_FOR_EACH_SAFE (index, node, &table->indexes) { |
2028 | 0 | skiplist_destroy(index->skiplist, NULL); |
2029 | 0 | free(index->columns); |
2030 | 0 | free(index); |
2031 | 0 | } |
2032 | 0 | } |
2033 | | |
2034 | | static void |
2035 | | ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *row) |
2036 | 0 | { |
2037 | 0 | struct ovsdb_idl_table *table = row->table; |
2038 | 0 | struct ovsdb_idl_index *index; |
2039 | 0 | LIST_FOR_EACH (index, node, &table->indexes) { |
2040 | 0 | index->ins_del = true; |
2041 | 0 | skiplist_insert(index->skiplist, row); |
2042 | 0 | index->ins_del = false; |
2043 | 0 | } |
2044 | 0 | } |
2045 | | |
2046 | | static void |
2047 | | ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *row) |
2048 | 0 | { |
2049 | 0 | struct ovsdb_idl_table *table = row->table; |
2050 | 0 | struct ovsdb_idl_index *index; |
2051 | 0 | LIST_FOR_EACH (index, node, &table->indexes) { |
2052 | 0 | index->ins_del = true; |
2053 | 0 | skiplist_delete(index->skiplist, row); |
2054 | 0 | index->ins_del = false; |
2055 | 0 | } |
2056 | 0 | } |
2057 | | |
2058 | | /* Writes a datum in an ovsdb_idl_row, and updates the corresponding field in |
2059 | | * the table record. Not intended for direct usage. */ |
2060 | | void |
2061 | | ovsdb_idl_index_write(struct ovsdb_idl_row *const_row, |
2062 | | const struct ovsdb_idl_column *column, |
2063 | | struct ovsdb_datum *datum, |
2064 | | const struct ovsdb_idl_table_class *class) |
2065 | 0 | { |
2066 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, const_row); |
2067 | 0 | size_t column_idx = column - class->columns; |
2068 | |
|
2069 | 0 | if (bitmap_is_set(row->written, column_idx)) { |
2070 | 0 | free(row->new_datum[column_idx].values); |
2071 | 0 | free(row->new_datum[column_idx].keys); |
2072 | 0 | } else { |
2073 | 0 | bitmap_set1(row->written, column_idx); |
2074 | 0 | } |
2075 | 0 | row->new_datum[column_idx] = *datum; |
2076 | 0 | (column->unparse)(row); |
2077 | 0 | (column->parse)(row, &row->new_datum[column_idx]); |
2078 | 0 | } |
2079 | | |
2080 | | /* Magic UUID for index rows */ |
2081 | | static const struct uuid index_row_uuid = { |
2082 | | .parts = {0xdeadbeef, |
2083 | | 0xdeadbeef, |
2084 | | 0xdeadbeef, |
2085 | | 0xdeadbeef}}; |
2086 | | |
2087 | | /* Check if a row is an index row */ |
2088 | | static bool |
2089 | | is_index_row(const struct ovsdb_idl_row *row) |
2090 | 0 | { |
2091 | 0 | return uuid_equals(&row->uuid, &index_row_uuid); |
2092 | 0 | } |
2093 | | |
2094 | | /* Initializes a row for use in an indexed query. |
2095 | | * Not intended for direct usage. |
2096 | | */ |
2097 | | struct ovsdb_idl_row * |
2098 | | ovsdb_idl_index_init_row(struct ovsdb_idl_index *index) |
2099 | 0 | { |
2100 | 0 | const struct ovsdb_idl_table_class *class = index->table->class_; |
2101 | 0 | struct ovsdb_idl_row *row = xzalloc(class->allocation_size); |
2102 | 0 | class->row_init(row); |
2103 | 0 | row->uuid = index_row_uuid; |
2104 | 0 | row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum); |
2105 | 0 | row->written = bitmap_allocate(class->n_columns); |
2106 | 0 | row->table = index->table; |
2107 | | /* arcs are not used for index row, but it doesn't harm to initialize */ |
2108 | 0 | ovs_list_init(&row->src_arcs); |
2109 | 0 | ovs_list_init(&row->dst_arcs); |
2110 | 0 | return row; |
2111 | 0 | } |
2112 | | |
2113 | | /* Destroys 'row_' and frees all associated memory. This function is intended |
2114 | | * to be used indirectly through one of the "index_destroy_row" functions |
2115 | | * generated by ovsdb-idlc. |
2116 | | */ |
2117 | | void |
2118 | | ovsdb_idl_index_destroy_row(const struct ovsdb_idl_row *row_) |
2119 | 0 | { |
2120 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
2121 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
2122 | 0 | const struct ovsdb_idl_column *c; |
2123 | 0 | size_t i; |
2124 | |
|
2125 | 0 | ovs_assert(is_index_row(row_)); |
2126 | 0 | ovs_assert(ovs_list_is_empty(&row_->src_arcs)); |
2127 | 0 | ovs_assert(ovs_list_is_empty(&row_->dst_arcs)); |
2128 | 0 | BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { |
2129 | 0 | c = &class->columns[i]; |
2130 | 0 | (c->unparse) (row); |
2131 | 0 | ovsdb_datum_destroy(&row->new_datum[i], &c->type); |
2132 | 0 | } |
2133 | 0 | free(row->new_datum); |
2134 | 0 | free(row->written); |
2135 | 0 | free(row); |
2136 | 0 | } |
2137 | | |
2138 | | struct ovsdb_idl_row * |
2139 | | ovsdb_idl_index_find(struct ovsdb_idl_index *index, |
2140 | | const struct ovsdb_idl_row *target) |
2141 | 0 | { |
2142 | 0 | return skiplist_get_data(skiplist_find(index->skiplist, target)); |
2143 | 0 | } |
2144 | | |
2145 | | struct ovsdb_idl_cursor |
2146 | | ovsdb_idl_cursor_first(struct ovsdb_idl_index *index) |
2147 | 0 | { |
2148 | 0 | struct skiplist_node *node = skiplist_first(index->skiplist); |
2149 | 0 | return (struct ovsdb_idl_cursor) { index, node }; |
2150 | 0 | } |
2151 | | |
2152 | | struct ovsdb_idl_cursor |
2153 | | ovsdb_idl_cursor_first_eq(struct ovsdb_idl_index *index, |
2154 | | const struct ovsdb_idl_row *target) |
2155 | 0 | { |
2156 | 0 | struct skiplist_node *node = skiplist_find(index->skiplist, target); |
2157 | 0 | return (struct ovsdb_idl_cursor) { index, node }; |
2158 | 0 | } |
2159 | | |
2160 | | struct ovsdb_idl_cursor |
2161 | | ovsdb_idl_cursor_first_ge(struct ovsdb_idl_index *index, |
2162 | | const struct ovsdb_idl_row *target) |
2163 | 0 | { |
2164 | 0 | struct skiplist_node *node = (target |
2165 | 0 | ? skiplist_forward_to(index->skiplist, |
2166 | 0 | target) |
2167 | 0 | : skiplist_first(index->skiplist)); |
2168 | 0 | return (struct ovsdb_idl_cursor) { index, node }; |
2169 | 0 | } |
2170 | | |
2171 | | void |
2172 | | ovsdb_idl_cursor_next(struct ovsdb_idl_cursor *cursor) |
2173 | 0 | { |
2174 | 0 | cursor->position = skiplist_next(cursor->position); |
2175 | 0 | } |
2176 | | |
2177 | | void |
2178 | | ovsdb_idl_cursor_next_eq(struct ovsdb_idl_cursor *cursor) |
2179 | 0 | { |
2180 | 0 | struct ovsdb_idl_row *data = skiplist_get_data(cursor->position); |
2181 | 0 | struct skiplist_node *next_position = skiplist_next(cursor->position); |
2182 | 0 | struct ovsdb_idl_row *next_data = skiplist_get_data(next_position); |
2183 | 0 | cursor->position = (!ovsdb_idl_index_compare(cursor->index, |
2184 | 0 | data, next_data) |
2185 | 0 | ? next_position : NULL); |
2186 | 0 | } |
2187 | | |
2188 | | struct ovsdb_idl_row * |
2189 | | ovsdb_idl_cursor_data(struct ovsdb_idl_cursor *cursor) |
2190 | 0 | { |
2191 | 0 | return skiplist_get_data(cursor->position); |
2192 | 0 | } |
2193 | | |
2194 | | /* Returns the result of comparing two rows using the comparison function |
2195 | | * for this index. |
2196 | | * Returns: |
2197 | | * < 0 if a < b |
2198 | | * 0 if a == b |
2199 | | * > 0 if a > b |
2200 | | * When the pointer to either row is NULL, this function considers NULL to be |
2201 | | * greater than any other value, and NULL == NULL. |
2202 | | */ |
2203 | | int |
2204 | | ovsdb_idl_index_compare(struct ovsdb_idl_index *index, |
2205 | | const struct ovsdb_idl_row *a, |
2206 | | const struct ovsdb_idl_row *b) |
2207 | 0 | { |
2208 | 0 | if (a && b) { |
2209 | 0 | return ovsdb_idl_index_generic_comparer(a, b, index); |
2210 | 0 | } else if (!a && !b) { |
2211 | 0 | return 0; |
2212 | 0 | } else if (a) { |
2213 | 0 | return -1; |
2214 | 0 | } else { |
2215 | 0 | return 1; |
2216 | 0 | } |
2217 | 0 | } |
2218 | | |
2219 | | static void |
2220 | | ovsdb_idl_row_clear_old(struct ovsdb_idl_row *row) |
2221 | 0 | { |
2222 | 0 | ovs_assert(row->old_datum == row->new_datum); |
2223 | 0 | if (!ovsdb_idl_row_is_orphan(row)) { |
2224 | 0 | if (ovsdb_idl_track_is_set(row->table) && !row->tracked_old_datum) { |
2225 | 0 | row->tracked_old_datum = row->old_datum; |
2226 | 0 | } else { |
2227 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
2228 | 0 | size_t i; |
2229 | |
|
2230 | 0 | for (i = 0; i < class->n_columns; i++) { |
2231 | 0 | ovsdb_datum_destroy(&row->old_datum[i], |
2232 | 0 | &class->columns[i].type); |
2233 | 0 | } |
2234 | 0 | free(row->old_datum); |
2235 | 0 | } |
2236 | 0 | row->old_datum = row->new_datum = NULL; |
2237 | 0 | } |
2238 | 0 | } |
2239 | | |
2240 | | static void |
2241 | | ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row) |
2242 | 0 | { |
2243 | 0 | if (row->old_datum != row->new_datum) { |
2244 | 0 | if (row->new_datum) { |
2245 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
2246 | 0 | size_t i; |
2247 | |
|
2248 | 0 | if (row->written) { |
2249 | 0 | BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { |
2250 | 0 | ovsdb_datum_destroy(&row->new_datum[i], |
2251 | 0 | &class->columns[i].type); |
2252 | 0 | } |
2253 | 0 | } |
2254 | 0 | free(row->new_datum); |
2255 | 0 | free(row->written); |
2256 | 0 | row->written = NULL; |
2257 | 0 | } |
2258 | 0 | row->new_datum = row->old_datum; |
2259 | 0 | } |
2260 | 0 | } |
2261 | | |
2262 | | static void |
2263 | | ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts) |
2264 | 0 | { |
2265 | 0 | struct ovsdb_idl_arc *arc; |
2266 | | |
2267 | | /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows |
2268 | | * that this causes to be unreferenced. |
2269 | | */ |
2270 | 0 | LIST_FOR_EACH_SAFE (arc, src_node, &row->src_arcs) { |
2271 | 0 | ovs_list_remove(&arc->dst_node); |
2272 | 0 | if (destroy_dsts |
2273 | 0 | && ovsdb_idl_row_is_orphan(arc->dst) |
2274 | 0 | && ovs_list_is_empty(&arc->dst->dst_arcs)) { |
2275 | 0 | ovsdb_idl_row_destroy(arc->dst); |
2276 | 0 | } |
2277 | 0 | free(arc); |
2278 | 0 | } |
2279 | 0 | ovs_list_init(&row->src_arcs); |
2280 | 0 | } |
2281 | | |
2282 | | /* Force nodes that reference 'row' to reparse. */ |
2283 | | static void |
2284 | | ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row) |
2285 | 0 | { |
2286 | 0 | struct ovsdb_idl_arc *arc; |
2287 | | |
2288 | | /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy |
2289 | | * 'arc', so we need to use the "safe" variant of list traversal. However, |
2290 | | * calling an ovsdb_idl_column's 'parse' function will add an arc |
2291 | | * equivalent to 'arc' to row->arcs. That could be a problem for |
2292 | | * traversal, but it adds it at the beginning of the list to prevent us |
2293 | | * from stumbling upon it again. |
2294 | | * |
2295 | | * (If duplicate arcs were possible then we would need to make sure that |
2296 | | * 'next' didn't also point into 'arc''s destination, but we forbid |
2297 | | * duplicate arcs.) */ |
2298 | 0 | LIST_FOR_EACH_SAFE (arc, dst_node, &row->dst_arcs) { |
2299 | 0 | struct ovsdb_idl_row *ref = arc->src; |
2300 | |
|
2301 | 0 | ovsdb_idl_row_unparse(ref); |
2302 | 0 | ovsdb_idl_row_clear_arcs(ref, false); |
2303 | 0 | ovsdb_idl_row_parse(ref); |
2304 | 0 | } |
2305 | 0 | } |
2306 | | |
2307 | | /* Add all backrefs of a row to the 'rows_to_reparse' list, so they can be |
2308 | | * re-parsed later. */ |
2309 | | static void |
2310 | | ovsdb_idl_row_mark_backrefs_for_reparsing(struct ovsdb_idl_row *row) |
2311 | 0 | { |
2312 | 0 | struct ovsdb_idl_arc *arc; |
2313 | |
|
2314 | 0 | LIST_FOR_EACH (arc, dst_node, &row->dst_arcs) { |
2315 | 0 | struct ovsdb_idl_row *ref = arc->src; |
2316 | |
|
2317 | 0 | if (ovs_list_is_empty(&ref->reparse_node)) { |
2318 | 0 | ovs_list_push_back(&ref->table->idl->rows_to_reparse, |
2319 | 0 | &ref->reparse_node); |
2320 | 0 | } |
2321 | 0 | } |
2322 | 0 | } |
2323 | | |
2324 | | static void |
2325 | | ovsdb_idl_row_track_change(struct ovsdb_idl_row *row, |
2326 | | enum ovsdb_idl_change change) |
2327 | 0 | { |
2328 | 0 | row->change_seqno[change] |
2329 | 0 | = row->table->change_seqno[change] |
2330 | 0 | = row->table->idl->change_seqno + 1; |
2331 | 0 | if (ovs_list_is_empty(&row->track_node)) { |
2332 | 0 | ovs_list_push_back(&row->table->track_list, &row->track_node); |
2333 | 0 | } |
2334 | 0 | } |
2335 | | |
2336 | | static void |
2337 | | ovsdb_idl_row_untrack_change(struct ovsdb_idl_row *row) |
2338 | 0 | { |
2339 | 0 | if (ovs_list_is_empty(&row->track_node)) { |
2340 | 0 | return; |
2341 | 0 | } |
2342 | | |
2343 | 0 | ovs_list_remove(&row->track_node); |
2344 | 0 | ovs_list_init(&row->track_node); |
2345 | 0 | } |
2346 | | |
2347 | | static void ovsdb_idl_row_clear_changeseqno(struct ovsdb_idl_row *row) |
2348 | 0 | { |
2349 | 0 | row->change_seqno[OVSDB_IDL_CHANGE_INSERT] = |
2350 | 0 | row->change_seqno[OVSDB_IDL_CHANGE_MODIFY] = |
2351 | 0 | row->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0; |
2352 | 0 | } |
2353 | | |
2354 | | static struct ovsdb_idl_row * |
2355 | | ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class) |
2356 | 0 | { |
2357 | 0 | struct ovsdb_idl_row *row = xzalloc(class->allocation_size); |
2358 | 0 | class->row_init(row); |
2359 | 0 | ovs_list_init(&row->src_arcs); |
2360 | 0 | ovs_list_init(&row->dst_arcs); |
2361 | 0 | ovs_list_init(&row->reparse_node); |
2362 | 0 | hmap_node_nullify(&row->txn_node); |
2363 | 0 | ovs_list_init(&row->track_node); |
2364 | 0 | return row; |
2365 | 0 | } |
2366 | | |
2367 | | static struct ovsdb_idl_row * |
2368 | | ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid) |
2369 | 0 | { |
2370 | 0 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(table->class_); |
2371 | 0 | hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid)); |
2372 | 0 | row->uuid = *uuid; |
2373 | 0 | row->table = table; |
2374 | 0 | row->map_op_written = NULL; |
2375 | 0 | row->map_op_lists = NULL; |
2376 | 0 | row->set_op_written = NULL; |
2377 | 0 | row->set_op_lists = NULL; |
2378 | 0 | return row; |
2379 | 0 | } |
2380 | | |
2381 | | /* If 'row' is not referenced anymore, removes 'row' from the table hmap, |
2382 | | * clears the old datum and adds 'row' to the table's track_list. |
2383 | | * |
2384 | | * If 'row' is still referenced, i.e., became "orphan", queues 'row' for |
2385 | | * reparsing after all updates have been processed by adding it to the |
2386 | | * 'deleted_untracked_rows' list. |
2387 | | */ |
2388 | | static void |
2389 | | ovsdb_idl_row_destroy(struct ovsdb_idl_row *row) |
2390 | 0 | { |
2391 | 0 | ovsdb_idl_row_clear_old(row); |
2392 | 0 | if (ovs_list_is_empty(&row->dst_arcs)) { |
2393 | 0 | hmap_remove(&row->table->rows, &row->hmap_node); |
2394 | 0 | ovsdb_idl_destroy_all_map_op_lists(row); |
2395 | 0 | ovsdb_idl_destroy_all_set_op_lists(row); |
2396 | 0 | ovsdb_idl_row_track_change(row, OVSDB_IDL_CHANGE_DELETE); |
2397 | 0 | } else { |
2398 | 0 | ovsdb_idl_row_untrack_change(row); |
2399 | 0 | ovs_list_push_back(&row->table->idl->deleted_untracked_rows, |
2400 | 0 | &row->track_node); |
2401 | 0 | } |
2402 | 0 | } |
2403 | | |
2404 | | static void |
2405 | | ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row *row) |
2406 | 0 | { |
2407 | 0 | if (row->map_op_written) { |
2408 | | /* Clear Map Operation Lists */ |
2409 | 0 | size_t idx, n_columns; |
2410 | 0 | const struct ovsdb_idl_column *columns; |
2411 | 0 | const struct ovsdb_type *type; |
2412 | 0 | n_columns = row->table->class_->n_columns; |
2413 | 0 | columns = row->table->class_->columns; |
2414 | 0 | BITMAP_FOR_EACH_1 (idx, n_columns, row->map_op_written) { |
2415 | 0 | type = &columns[idx].type; |
2416 | 0 | map_op_list_destroy(row->map_op_lists[idx], type); |
2417 | 0 | } |
2418 | 0 | free(row->map_op_lists); |
2419 | 0 | bitmap_free(row->map_op_written); |
2420 | 0 | row->map_op_lists = NULL; |
2421 | 0 | row->map_op_written = NULL; |
2422 | 0 | } |
2423 | 0 | } |
2424 | | |
2425 | | static void |
2426 | | ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row *row) |
2427 | 0 | { |
2428 | 0 | if (row->set_op_written) { |
2429 | | /* Clear Set Operation Lists */ |
2430 | 0 | size_t idx, n_columns; |
2431 | 0 | const struct ovsdb_idl_column *columns; |
2432 | 0 | const struct ovsdb_type *type; |
2433 | 0 | n_columns = row->table->class_->n_columns; |
2434 | 0 | columns = row->table->class_->columns; |
2435 | 0 | BITMAP_FOR_EACH_1 (idx, n_columns, row->set_op_written) { |
2436 | 0 | type = &columns[idx].type; |
2437 | 0 | set_op_list_destroy(row->set_op_lists[idx], type); |
2438 | 0 | } |
2439 | 0 | free(row->set_op_lists); |
2440 | 0 | bitmap_free(row->set_op_written); |
2441 | 0 | row->set_op_lists = NULL; |
2442 | 0 | row->set_op_written = NULL; |
2443 | 0 | } |
2444 | 0 | } |
2445 | | |
2446 | | static void |
2447 | | ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *idl) |
2448 | 0 | { |
2449 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
2450 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
2451 | |
|
2452 | 0 | if (!ovs_list_is_empty(&table->track_list)) { |
2453 | 0 | struct ovsdb_idl_row *row; |
2454 | |
|
2455 | 0 | LIST_FOR_EACH_SAFE (row, track_node, &table->track_list) { |
2456 | 0 | if (!ovsdb_idl_track_is_set(row->table)) { |
2457 | 0 | ovs_list_remove(&row->track_node); |
2458 | 0 | ovsdb_idl_row_unparse(row); |
2459 | 0 | free(row); |
2460 | 0 | } |
2461 | 0 | } |
2462 | 0 | } |
2463 | 0 | } |
2464 | 0 | } |
2465 | | |
2466 | | static void |
2467 | | ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct shash *data) |
2468 | 0 | { |
2469 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
2470 | 0 | size_t i, datum_size; |
2471 | |
|
2472 | 0 | ovs_assert(!row->old_datum && !row->new_datum); |
2473 | 0 | datum_size = class->n_columns * sizeof *row->old_datum; |
2474 | 0 | row->old_datum = row->new_datum = xmalloc(datum_size); |
2475 | 0 | for (i = 0; i < class->n_columns; i++) { |
2476 | 0 | ovsdb_datum_init_default(&row->old_datum[i], &class->columns[i].type); |
2477 | 0 | } |
2478 | 0 | ovsdb_idl_row_change(row, data, false, OVSDB_IDL_CHANGE_INSERT); |
2479 | 0 | ovsdb_idl_row_parse(row); |
2480 | | |
2481 | | /* Backrefs will be re-parsed after all updates processed to avoid |
2482 | | * re-parsing same rows more than once if they are referencing more |
2483 | | * than one inserted row. */ |
2484 | 0 | ovsdb_idl_row_mark_backrefs_for_reparsing(row); |
2485 | 0 | ovsdb_idl_add_to_indexes(row); |
2486 | 0 | } |
2487 | | |
2488 | | static void |
2489 | | ovsdb_idl_delete_row(struct ovsdb_idl_row *row) |
2490 | 0 | { |
2491 | | /* If row has to be reparsed, reparse it before it's deleted. */ |
2492 | 0 | if (!ovs_list_is_empty(&row->reparse_node)) { |
2493 | 0 | ovsdb_idl_row_parse(row); |
2494 | 0 | } |
2495 | 0 | ovsdb_idl_remove_from_indexes(row); |
2496 | 0 | ovsdb_idl_row_clear_arcs(row, true); |
2497 | 0 | ovsdb_idl_row_destroy(row); |
2498 | 0 | } |
2499 | | |
2500 | | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
2501 | | * otherwise. */ |
2502 | | static bool |
2503 | | ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct shash *values, |
2504 | | bool xor) |
2505 | 0 | { |
2506 | 0 | ovsdb_idl_remove_from_indexes(row); |
2507 | 0 | ovsdb_idl_row_unparse(row); |
2508 | 0 | ovsdb_idl_row_clear_arcs(row, true); |
2509 | 0 | bool changed = ovsdb_idl_row_change(row, values, xor, |
2510 | 0 | OVSDB_IDL_CHANGE_MODIFY); |
2511 | 0 | ovsdb_idl_row_parse(row); |
2512 | 0 | ovsdb_idl_add_to_indexes(row); |
2513 | |
|
2514 | 0 | return changed; |
2515 | 0 | } |
2516 | | |
2517 | | static bool |
2518 | | may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst) |
2519 | 0 | { |
2520 | 0 | const struct ovsdb_idl_arc *arc; |
2521 | | |
2522 | | /* No self-arcs. */ |
2523 | 0 | if (src == dst) { |
2524 | 0 | return false; |
2525 | 0 | } |
2526 | | |
2527 | | /* No duplicate arcs. |
2528 | | * |
2529 | | * We only need to test whether the first arc in dst->dst_arcs originates |
2530 | | * at 'src', since we add all of the arcs from a given source in a clump |
2531 | | * (in a single call to ovsdb_idl_row_parse()) and new arcs are always |
2532 | | * added at the front of the dst_arcs list. */ |
2533 | 0 | if (ovs_list_is_empty(&dst->dst_arcs)) { |
2534 | 0 | return true; |
2535 | 0 | } |
2536 | 0 | arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node); |
2537 | 0 | return arc->src != src; |
2538 | 0 | } |
2539 | | |
2540 | | static struct ovsdb_idl_table * |
2541 | | ovsdb_idl_table_from_class(const struct ovsdb_idl *idl, |
2542 | | const struct ovsdb_idl_table_class *table_class) |
2543 | 0 | { |
2544 | 0 | ptrdiff_t idx = table_class - idl->class_->tables; |
2545 | 0 | return idx >= 0 && idx < idl->class_->n_tables ? &idl->tables[idx] : NULL; |
2546 | 0 | } |
2547 | | |
2548 | | /* Called by ovsdb-idlc generated code. */ |
2549 | | struct ovsdb_idl_row * |
2550 | | ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src, |
2551 | | const struct ovsdb_idl_table_class *dst_table_class, |
2552 | | const struct uuid *dst_uuid) |
2553 | 0 | { |
2554 | 0 | struct ovsdb_idl *idl = src->table->idl; |
2555 | 0 | struct ovsdb_idl_table *dst_table; |
2556 | 0 | struct ovsdb_idl_arc *arc; |
2557 | 0 | struct ovsdb_idl_row *dst; |
2558 | |
|
2559 | 0 | dst_table = ovsdb_idl_table_from_class(idl, dst_table_class); |
2560 | 0 | dst = ovsdb_idl_get_row(dst_table, dst_uuid); |
2561 | 0 | if (idl->txn || is_index_row(src)) { |
2562 | | /* There are two cases we should not update any arcs: |
2563 | | * |
2564 | | * 1. We're being called from ovsdb_idl_txn_write(). We must not update |
2565 | | * any arcs, because the transaction will be backed out at commit or |
2566 | | * abort time and we don't want our graph screwed up. |
2567 | | * |
2568 | | * 2. The row is used as an index for querying purpose only. |
2569 | | * |
2570 | | * In these cases, just return the destination row, if there is one and |
2571 | | * it has not been deleted. */ |
2572 | 0 | if (dst && (hmap_node_is_null(&dst->txn_node) || dst->new_datum)) { |
2573 | 0 | return dst; |
2574 | 0 | } |
2575 | 0 | return NULL; |
2576 | 0 | } else { |
2577 | | /* We're being called from some other context. Update the graph. */ |
2578 | 0 | if (!dst) { |
2579 | 0 | dst = ovsdb_idl_row_create(dst_table, dst_uuid); |
2580 | 0 | } |
2581 | | |
2582 | | /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */ |
2583 | 0 | if (may_add_arc(src, dst)) { |
2584 | | /* The arc *must* be added at the front of the dst_arcs list. See |
2585 | | * ovsdb_idl_row_reparse_backrefs() for details. */ |
2586 | 0 | arc = xmalloc(sizeof *arc); |
2587 | 0 | ovs_list_push_front(&src->src_arcs, &arc->src_node); |
2588 | 0 | ovs_list_push_front(&dst->dst_arcs, &arc->dst_node); |
2589 | 0 | arc->src = src; |
2590 | 0 | arc->dst = dst; |
2591 | 0 | } |
2592 | |
|
2593 | 0 | return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL; |
2594 | 0 | } |
2595 | 0 | } |
2596 | | |
2597 | | /* Searches 'tc''s table in 'idl' for a row with UUID 'uuid'. Returns a |
2598 | | * pointer to the row if there is one, otherwise a null pointer. */ |
2599 | | const struct ovsdb_idl_row * |
2600 | | ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl *idl, |
2601 | | const struct ovsdb_idl_table_class *tc, |
2602 | | const struct uuid *uuid) |
2603 | 0 | { |
2604 | 0 | return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl, tc), uuid); |
2605 | 0 | } |
2606 | | |
2607 | | static struct ovsdb_idl_row * |
2608 | | next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node) |
2609 | 0 | { |
2610 | 0 | for (; node; node = hmap_next(&table->rows, node)) { |
2611 | 0 | struct ovsdb_idl_row *row; |
2612 | |
|
2613 | 0 | row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node); |
2614 | 0 | if (ovsdb_idl_row_exists(row)) { |
2615 | 0 | return row; |
2616 | 0 | } |
2617 | 0 | } |
2618 | 0 | return NULL; |
2619 | 0 | } |
2620 | | |
2621 | | /* Returns a row in 'table_class''s table in 'idl', or a null pointer if that |
2622 | | * table is empty. |
2623 | | * |
2624 | | * Database tables are internally maintained as hash tables, so adding or |
2625 | | * removing rows while traversing the same table can cause some rows to be |
2626 | | * visited twice or not at apply. */ |
2627 | | const struct ovsdb_idl_row * |
2628 | | ovsdb_idl_first_row(const struct ovsdb_idl *idl, |
2629 | | const struct ovsdb_idl_table_class *table_class) |
2630 | 0 | { |
2631 | 0 | struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, |
2632 | 0 | table_class); |
2633 | 0 | return next_real_row(table, hmap_first(&table->rows)); |
2634 | 0 | } |
2635 | | |
2636 | | /* Returns a row following 'row' within its table, or a null pointer if 'row' |
2637 | | * is the last row in its table. */ |
2638 | | const struct ovsdb_idl_row * |
2639 | | ovsdb_idl_next_row(const struct ovsdb_idl_row *row) |
2640 | 0 | { |
2641 | 0 | struct ovsdb_idl_table *table = row->table; |
2642 | |
|
2643 | 0 | return next_real_row(table, hmap_next(&table->rows, &row->hmap_node)); |
2644 | 0 | } |
2645 | | |
2646 | | /* Reads and returns the value of 'column' within 'row'. If an ongoing |
2647 | | * transaction has changed 'column''s value, the modified value is returned. |
2648 | | * |
2649 | | * The caller must not modify or free the returned value. |
2650 | | * |
2651 | | * Various kinds of changes can invalidate the returned value: writing to the |
2652 | | * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row' |
2653 | | * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction |
2654 | | * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the |
2655 | | * returned value is needed for a long time, it is best to make a copy of it |
2656 | | * with ovsdb_datum_clone(). */ |
2657 | | const struct ovsdb_datum * |
2658 | | ovsdb_idl_read(const struct ovsdb_idl_row *row, |
2659 | | const struct ovsdb_idl_column *column) |
2660 | 0 | { |
2661 | 0 | const struct ovsdb_idl_table_class *class; |
2662 | 0 | size_t column_idx; |
2663 | |
|
2664 | 0 | ovs_assert(!ovsdb_idl_row_is_synthetic(row)); |
2665 | |
|
2666 | 0 | class = row->table->class_; |
2667 | 0 | column_idx = column - class->columns; |
2668 | |
|
2669 | 0 | ovs_assert(row->new_datum != NULL); |
2670 | 0 | ovs_assert(column_idx < class->n_columns); |
2671 | |
|
2672 | 0 | if (row->written && bitmap_is_set(row->written, column_idx)) { |
2673 | 0 | return &row->new_datum[column_idx]; |
2674 | 0 | } else if (row->old_datum) { |
2675 | 0 | return &row->old_datum[column_idx]; |
2676 | 0 | } else { |
2677 | 0 | return ovsdb_datum_default(&column->type); |
2678 | 0 | } |
2679 | 0 | } |
2680 | | |
2681 | | /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key |
2682 | | * type 'key_type' and value type 'value_type'. (Scalar and set types will |
2683 | | * have a value type of OVSDB_TYPE_VOID.) |
2684 | | * |
2685 | | * This is useful in code that "knows" that a particular column has a given |
2686 | | * type, so that it will abort if someone changes the column's type without |
2687 | | * updating the code that uses it. */ |
2688 | | const struct ovsdb_datum * |
2689 | | ovsdb_idl_get(const struct ovsdb_idl_row *row, |
2690 | | const struct ovsdb_idl_column *column, |
2691 | | enum ovsdb_atomic_type key_type OVS_UNUSED, |
2692 | | enum ovsdb_atomic_type value_type OVS_UNUSED) |
2693 | 0 | { |
2694 | 0 | ovs_assert(column->type.key.type == key_type); |
2695 | 0 | ovs_assert(column->type.value.type == value_type); |
2696 | |
|
2697 | 0 | return ovsdb_idl_read(row, column); |
2698 | 0 | } |
2699 | | |
2700 | | /* Returns true if the field represented by 'column' in 'row' may be modified, |
2701 | | * false if it is immutable. |
2702 | | * |
2703 | | * Normally, whether a field is mutable is controlled by its column's schema. |
2704 | | * However, an immutable column can be set to any initial value at the time of |
2705 | | * insertion, so if 'row' is a new row (one that is being added as part of the |
2706 | | * current transaction, supposing that a transaction is in progress) then even |
2707 | | * its "immutable" fields are actually mutable. */ |
2708 | | bool |
2709 | | ovsdb_idl_is_mutable(const struct ovsdb_idl_row *row, |
2710 | | const struct ovsdb_idl_column *column) |
2711 | 0 | { |
2712 | 0 | return column->is_mutable || (row->new_datum && !row->old_datum); |
2713 | 0 | } |
2714 | | |
2715 | | /* Returns false if 'row' was obtained from the IDL, true if it was initialized |
2716 | | * to all-zero-bits by some other entity. If 'row' was set up some other way |
2717 | | * then the return value is indeterminate. */ |
2718 | | bool |
2719 | | ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row *row) |
2720 | 0 | { |
2721 | 0 | return row->table == NULL; |
2722 | 0 | } |
2723 | | |
2724 | | /* Transactions. */ |
2725 | | |
2726 | | static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, |
2727 | | enum ovsdb_idl_txn_status); |
2728 | | |
2729 | | /* Returns a string representation of 'status'. The caller must not modify or |
2730 | | * free the returned string. |
2731 | | * |
2732 | | * The return value is probably useful only for debug log messages and unit |
2733 | | * tests. */ |
2734 | | const char * |
2735 | | ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status) |
2736 | 0 | { |
2737 | 0 | switch (status) { |
2738 | 0 | case TXN_UNCOMMITTED: |
2739 | 0 | return "uncommitted"; |
2740 | 0 | case TXN_UNCHANGED: |
2741 | 0 | return "unchanged"; |
2742 | 0 | case TXN_INCOMPLETE: |
2743 | 0 | return "incomplete"; |
2744 | 0 | case TXN_ABORTED: |
2745 | 0 | return "aborted"; |
2746 | 0 | case TXN_SUCCESS: |
2747 | 0 | return "success"; |
2748 | 0 | case TXN_TRY_AGAIN: |
2749 | 0 | return "try again"; |
2750 | 0 | case TXN_NOT_LOCKED: |
2751 | 0 | return "not locked"; |
2752 | 0 | case TXN_ERROR: |
2753 | 0 | return "error"; |
2754 | 0 | } |
2755 | 0 | return "<unknown>"; |
2756 | 0 | } |
2757 | | |
2758 | | /* Starts a new transaction on 'idl'. A given ovsdb_idl may only have a single |
2759 | | * active transaction at a time. See the large comment in ovsdb-idl.h for |
2760 | | * general information on transactions. */ |
2761 | | struct ovsdb_idl_txn * |
2762 | | ovsdb_idl_txn_create(struct ovsdb_idl *idl) |
2763 | 0 | { |
2764 | 0 | struct ovsdb_idl_txn *txn; |
2765 | |
|
2766 | 0 | ovs_assert(!idl->txn); |
2767 | 0 | idl->txn = txn = xmalloc(sizeof *txn); |
2768 | 0 | txn->request_id = NULL; |
2769 | 0 | txn->idl = idl; |
2770 | 0 | hmap_init(&txn->txn_rows); |
2771 | 0 | txn->status = TXN_UNCOMMITTED; |
2772 | 0 | txn->error = NULL; |
2773 | 0 | txn->dry_run = false; |
2774 | 0 | ds_init(&txn->comment); |
2775 | |
|
2776 | 0 | txn->inc_table = NULL; |
2777 | 0 | txn->inc_column = NULL; |
2778 | |
|
2779 | 0 | hmap_init(&txn->inserted_rows); |
2780 | |
|
2781 | 0 | return txn; |
2782 | 0 | } |
2783 | | |
2784 | | /* Appends 's', which is treated as a printf()-type format string, to the |
2785 | | * comments that will be passed to the OVSDB server when 'txn' is committed. |
2786 | | * (The comment will be committed to the OVSDB log, which "ovsdb-tool |
2787 | | * show-log" can print in a relatively human-readable form.) */ |
2788 | | void |
2789 | | ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...) |
2790 | 0 | { |
2791 | 0 | va_list args; |
2792 | |
|
2793 | 0 | if (txn->comment.length) { |
2794 | 0 | ds_put_char(&txn->comment, '\n'); |
2795 | 0 | } |
2796 | |
|
2797 | 0 | va_start(args, s); |
2798 | 0 | ds_put_format_valist(&txn->comment, s, args); |
2799 | 0 | va_end(args); |
2800 | 0 | } |
2801 | | |
2802 | | /* Marks 'txn' as a transaction that will not actually modify the database. In |
2803 | | * almost every way, the transaction is treated like other transactions. It |
2804 | | * must be committed or aborted like other transactions, it will be sent to the |
2805 | | * database server like other transactions, and so on. The only difference is |
2806 | | * that the operations sent to the database server will include, as the last |
2807 | | * step, an "abort" operation, so that any changes made by the transaction will |
2808 | | * not actually take effect. */ |
2809 | | void |
2810 | | ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn *txn) |
2811 | 0 | { |
2812 | 0 | txn->dry_run = true; |
2813 | 0 | } |
2814 | | |
2815 | | /* Causes 'txn', when committed, to increment the value of 'column' within |
2816 | | * 'row' by 1. 'column' must have an integer type. After 'txn' commits |
2817 | | * successfully, the client may retrieve the final (incremented) value of |
2818 | | * 'column' with ovsdb_idl_txn_get_increment_new_value(). |
2819 | | * |
2820 | | * If at time of commit the transaction is otherwise empty, that is, it doesn't |
2821 | | * change the database, then 'force' is important. If 'force' is false in this |
2822 | | * case, the IDL suppresses the increment and skips a round trip to the |
2823 | | * database server. If 'force' is true, the IDL will still increment the |
2824 | | * column. |
2825 | | * |
2826 | | * The client could accomplish something similar with ovsdb_idl_read(), |
2827 | | * ovsdb_idl_txn_verify() and ovsdb_idl_txn_write(), or with ovsdb-idlc |
2828 | | * generated wrappers for these functions. However, ovsdb_idl_txn_increment() |
2829 | | * will never (by itself) fail because of a verify error. |
2830 | | * |
2831 | | * The intended use is for incrementing the "next_cfg" column in the |
2832 | | * Open_vSwitch table. */ |
2833 | | void |
2834 | | ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn, |
2835 | | const struct ovsdb_idl_row *row, |
2836 | | const struct ovsdb_idl_column *column, |
2837 | | bool force) |
2838 | 0 | { |
2839 | 0 | ovs_assert(!txn->inc_table); |
2840 | 0 | ovs_assert(column->type.key.type == OVSDB_TYPE_INTEGER); |
2841 | 0 | ovs_assert(column->type.value.type == OVSDB_TYPE_VOID); |
2842 | |
|
2843 | 0 | txn->inc_table = row->table->class_->name; |
2844 | 0 | txn->inc_column = column->name; |
2845 | 0 | txn->inc_row = row->uuid; |
2846 | 0 | txn->inc_force = force; |
2847 | 0 | } |
2848 | | |
2849 | | /* Destroys 'txn' and frees all associated memory. If ovsdb_idl_txn_commit() |
2850 | | * has been called for 'txn' but the commit is still incomplete (that is, the |
2851 | | * last call returned TXN_INCOMPLETE) then the transaction may or may not still |
2852 | | * end up committing at the database server, but the client will not be able to |
2853 | | * get any further status information back. */ |
2854 | | void |
2855 | | ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn) |
2856 | 0 | { |
2857 | 0 | struct ovsdb_idl_txn_insert *insert; |
2858 | |
|
2859 | 0 | if (txn->status == TXN_INCOMPLETE) { |
2860 | 0 | ovsdb_cs_forget_transaction(txn->idl->cs, txn->request_id); |
2861 | 0 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); |
2862 | 0 | } |
2863 | 0 | json_destroy(txn->request_id); |
2864 | 0 | ovsdb_idl_txn_abort(txn); |
2865 | 0 | ds_destroy(&txn->comment); |
2866 | 0 | free(txn->error); |
2867 | 0 | HMAP_FOR_EACH_SAFE (insert, hmap_node, &txn->inserted_rows) { |
2868 | 0 | free(insert); |
2869 | 0 | } |
2870 | 0 | hmap_destroy(&txn->inserted_rows); |
2871 | 0 | free(txn); |
2872 | 0 | } |
2873 | | |
2874 | | /* Causes poll_block() to wake up if 'txn' has completed committing. */ |
2875 | | void |
2876 | | ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *txn) |
2877 | 0 | { |
2878 | 0 | if (txn->status != TXN_UNCOMMITTED && txn->status != TXN_INCOMPLETE) { |
2879 | 0 | poll_immediate_wake(); |
2880 | 0 | } |
2881 | 0 | } |
2882 | | |
2883 | | static struct json * |
2884 | | where_uuid_equals(const struct uuid *uuid) |
2885 | 0 | { |
2886 | 0 | return |
2887 | 0 | json_array_create_1( |
2888 | 0 | json_array_create_3( |
2889 | 0 | json_string_create("_uuid"), |
2890 | 0 | json_string_create("=="), |
2891 | 0 | json_array_create_2( |
2892 | 0 | json_string_create("uuid"), |
2893 | 0 | json_string_create_uuid(uuid)))); |
2894 | 0 | } |
2895 | | |
2896 | | static const struct ovsdb_idl_row * |
2897 | | ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid) |
2898 | 0 | { |
2899 | 0 | const struct ovsdb_idl_row *row; |
2900 | |
|
2901 | 0 | HMAP_FOR_EACH_WITH_HASH (row, txn_node, uuid_hash(uuid), &txn->txn_rows) { |
2902 | 0 | if (uuid_equals(&row->uuid, uuid)) { |
2903 | 0 | return row; |
2904 | 0 | } |
2905 | 0 | } |
2906 | 0 | return NULL; |
2907 | 0 | } |
2908 | | |
2909 | | /* XXX there must be a cleaner way to do this */ |
2910 | | static struct json * |
2911 | | substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn) |
2912 | 0 | { |
2913 | 0 | if (json->type == JSON_ARRAY) { |
2914 | 0 | struct uuid uuid; |
2915 | 0 | size_t i; |
2916 | |
|
2917 | 0 | if (json_array_size(json) == 2 |
2918 | 0 | && json_array_at(json, 0)->type == JSON_STRING |
2919 | 0 | && json_array_at(json, 1)->type == JSON_STRING |
2920 | 0 | && !strcmp(json_string(json_array_at(json, 0)), "uuid") |
2921 | 0 | && uuid_from_string(&uuid, json_string(json_array_at(json, 1)))) { |
2922 | 0 | const struct ovsdb_idl_row *row; |
2923 | |
|
2924 | 0 | row = ovsdb_idl_txn_get_row(txn, &uuid); |
2925 | 0 | if (row && !row->old_datum && row->new_datum) { |
2926 | 0 | if (row->persist_uuid) { |
2927 | 0 | return json; |
2928 | 0 | } else { |
2929 | 0 | json_destroy(json); |
2930 | 0 | return json_array_create_2( |
2931 | 0 | json_string_create("named-uuid"), |
2932 | 0 | json_string_create_nocopy(ovsdb_data_row_name(&uuid))); |
2933 | 0 | } |
2934 | 0 | } |
2935 | 0 | } |
2936 | | |
2937 | 0 | for (i = 0; i < json_array_size(json); i++) { |
2938 | 0 | json_array_set( |
2939 | 0 | json, i, |
2940 | 0 | substitute_uuids( |
2941 | 0 | CONST_CAST(struct json *, json_array_at(json, i)), txn)); |
2942 | 0 | } |
2943 | 0 | } else if (json->type == JSON_OBJECT) { |
2944 | 0 | struct shash_node *node; |
2945 | |
|
2946 | 0 | SHASH_FOR_EACH (node, json_object(json)) { |
2947 | 0 | node->data = substitute_uuids(node->data, txn); |
2948 | 0 | } |
2949 | 0 | } |
2950 | 0 | return json; |
2951 | 0 | } |
2952 | | |
2953 | | static void |
2954 | | ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn) |
2955 | 0 | { |
2956 | 0 | struct ovsdb_idl_row *row; |
2957 | | |
2958 | | /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an |
2959 | | * ovsdb_idl_column's 'parse' function, which will call |
2960 | | * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a |
2961 | | * transaction and fail to update the graph. */ |
2962 | 0 | txn->idl->txn = NULL; |
2963 | |
|
2964 | 0 | HMAP_FOR_EACH_SAFE (row, txn_node, &txn->txn_rows) { |
2965 | 0 | enum { INSERTED, MODIFIED, DELETED } op |
2966 | 0 | = (!row->new_datum ? DELETED |
2967 | 0 | : !row->old_datum ? INSERTED |
2968 | 0 | : MODIFIED); |
2969 | |
|
2970 | 0 | if (op != DELETED) { |
2971 | 0 | ovsdb_idl_remove_from_indexes(row); |
2972 | 0 | } |
2973 | |
|
2974 | 0 | ovsdb_idl_destroy_all_map_op_lists(row); |
2975 | 0 | ovsdb_idl_destroy_all_set_op_lists(row); |
2976 | 0 | if (op != INSERTED) { |
2977 | 0 | if (row->written) { |
2978 | 0 | ovsdb_idl_row_unparse(row); |
2979 | 0 | ovsdb_idl_row_clear_arcs(row, false); |
2980 | 0 | ovsdb_idl_row_parse(row); |
2981 | 0 | } |
2982 | 0 | } else { |
2983 | 0 | ovsdb_idl_row_unparse(row); |
2984 | 0 | } |
2985 | 0 | ovsdb_idl_row_clear_new(row); |
2986 | |
|
2987 | 0 | free(row->prereqs); |
2988 | 0 | row->prereqs = NULL; |
2989 | |
|
2990 | 0 | free(row->written); |
2991 | 0 | row->written = NULL; |
2992 | |
|
2993 | 0 | hmap_remove(&txn->txn_rows, &row->txn_node); |
2994 | 0 | hmap_node_nullify(&row->txn_node); |
2995 | 0 | if (op != INSERTED) { |
2996 | 0 | ovsdb_idl_add_to_indexes(row); |
2997 | 0 | } else { |
2998 | 0 | hmap_remove(&row->table->rows, &row->hmap_node); |
2999 | 0 | free(row); |
3000 | 0 | } |
3001 | 0 | } |
3002 | 0 | hmap_destroy(&txn->txn_rows); |
3003 | 0 | hmap_init(&txn->txn_rows); |
3004 | 0 | } |
3005 | | |
3006 | | static bool |
3007 | | ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row, |
3008 | | struct json *mutations) |
3009 | 0 | { |
3010 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
3011 | 0 | size_t idx; |
3012 | 0 | bool any_mutations = false; |
3013 | |
|
3014 | 0 | if (row->map_op_written) { |
3015 | 0 | BITMAP_FOR_EACH_1(idx, class->n_columns, row->map_op_written) { |
3016 | 0 | struct map_op_list *map_op_list; |
3017 | 0 | const struct ovsdb_idl_column *column; |
3018 | 0 | const struct ovsdb_datum *old_datum; |
3019 | 0 | enum ovsdb_atomic_type key_type, value_type; |
3020 | 0 | struct json *mutation, *map, *col_name, *mutator; |
3021 | 0 | struct json *del_set, *ins_map; |
3022 | 0 | bool any_del, any_ins; |
3023 | |
|
3024 | 0 | map_op_list = row->map_op_lists[idx]; |
3025 | 0 | column = &class->columns[idx]; |
3026 | 0 | key_type = column->type.key.type; |
3027 | 0 | value_type = column->type.value.type; |
3028 | | |
3029 | | /* Get the value to be changed */ |
3030 | 0 | if (row->new_datum && row->written |
3031 | 0 | && bitmap_is_set(row->written,idx)) { |
3032 | 0 | old_datum = &row->new_datum[idx]; |
3033 | 0 | } else if (row->old_datum != NULL) { |
3034 | 0 | old_datum = &row->old_datum[idx]; |
3035 | 0 | } else { |
3036 | 0 | old_datum = ovsdb_datum_default(&column->type); |
3037 | 0 | } |
3038 | |
|
3039 | 0 | del_set = json_array_create_empty(); |
3040 | 0 | ins_map = json_array_create_empty(); |
3041 | 0 | any_del = false; |
3042 | 0 | any_ins = false; |
3043 | |
|
3044 | 0 | for (struct map_op *map_op = map_op_list_first(map_op_list); map_op; |
3045 | 0 | map_op = map_op_list_next(map_op_list, map_op)) { |
3046 | |
|
3047 | 0 | if (map_op_type(map_op) == MAP_OP_UPDATE) { |
3048 | | /* Find out if value really changed. */ |
3049 | 0 | struct ovsdb_datum *new_datum; |
3050 | 0 | unsigned int pos; |
3051 | 0 | new_datum = map_op_datum(map_op); |
3052 | 0 | ovsdb_datum_find_key(old_datum, &new_datum->keys[0], |
3053 | 0 | key_type, &pos); |
3054 | 0 | if (ovsdb_atom_equals(&new_datum->values[0], |
3055 | 0 | &old_datum->values[pos], |
3056 | 0 | value_type)) { |
3057 | | /* No change in value. Move on to next update. */ |
3058 | 0 | continue; |
3059 | 0 | } |
3060 | 0 | } else if (map_op_type(map_op) == MAP_OP_DELETE){ |
3061 | | /* Verify that there is a key to delete. */ |
3062 | 0 | if (!ovsdb_datum_find_key(old_datum, |
3063 | 0 | &map_op_datum(map_op)->keys[0], |
3064 | 0 | key_type, NULL)) { |
3065 | | /* No key to delete. Move on to next update. */ |
3066 | 0 | VLOG_WARN("Trying to delete a key that doesn't " |
3067 | 0 | "exist in the map."); |
3068 | 0 | continue; |
3069 | 0 | } |
3070 | 0 | } |
3071 | | |
3072 | 0 | if (map_op_type(map_op) == MAP_OP_INSERT) { |
3073 | 0 | map = json_array_create_2( |
3074 | 0 | ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0], |
3075 | 0 | key_type), |
3076 | 0 | ovsdb_atom_to_json(&map_op_datum(map_op)->values[0], |
3077 | 0 | value_type)); |
3078 | 0 | json_array_add(ins_map, map); |
3079 | 0 | any_ins = true; |
3080 | 0 | } else { /* MAP_OP_UPDATE or MAP_OP_DELETE */ |
3081 | 0 | map = ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0], |
3082 | 0 | key_type); |
3083 | 0 | json_array_add(del_set, map); |
3084 | 0 | any_del = true; |
3085 | 0 | } |
3086 | | |
3087 | | /* Generate an additional insert mutate for updates. */ |
3088 | 0 | if (map_op_type(map_op) == MAP_OP_UPDATE) { |
3089 | 0 | map = json_array_create_2( |
3090 | 0 | ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0], |
3091 | 0 | key_type), |
3092 | 0 | ovsdb_atom_to_json(&map_op_datum(map_op)->values[0], |
3093 | 0 | value_type)); |
3094 | 0 | json_array_add(ins_map, map); |
3095 | 0 | any_ins = true; |
3096 | 0 | } |
3097 | 0 | } |
3098 | |
|
3099 | 0 | if (any_del) { |
3100 | 0 | col_name = json_string_create(column->name); |
3101 | 0 | mutator = json_string_create("delete"); |
3102 | 0 | map = json_array_create_2(json_string_create("set"), del_set); |
3103 | 0 | mutation = json_array_create_3(col_name, mutator, map); |
3104 | 0 | json_array_add(mutations, mutation); |
3105 | 0 | any_mutations = true; |
3106 | 0 | } else { |
3107 | 0 | json_destroy(del_set); |
3108 | 0 | } |
3109 | 0 | if (any_ins) { |
3110 | 0 | col_name = json_string_create(column->name); |
3111 | 0 | mutator = json_string_create("insert"); |
3112 | 0 | map = json_array_create_2(json_string_create("map"), ins_map); |
3113 | 0 | mutation = json_array_create_3(col_name, mutator, map); |
3114 | 0 | json_array_add(mutations, mutation); |
3115 | 0 | any_mutations = true; |
3116 | 0 | } else { |
3117 | 0 | json_destroy(ins_map); |
3118 | 0 | } |
3119 | 0 | } |
3120 | 0 | } |
3121 | 0 | if (row->set_op_written) { |
3122 | 0 | BITMAP_FOR_EACH_1(idx, class->n_columns, row->set_op_written) { |
3123 | 0 | struct set_op_list *set_op_list; |
3124 | 0 | const struct ovsdb_idl_column *column; |
3125 | 0 | const struct ovsdb_datum *old_datum; |
3126 | 0 | enum ovsdb_atomic_type key_type; |
3127 | 0 | struct json *mutation, *set, *col_name, *mutator; |
3128 | 0 | struct json *del_set, *ins_set; |
3129 | 0 | bool any_del, any_ins; |
3130 | |
|
3131 | 0 | set_op_list = row->set_op_lists[idx]; |
3132 | 0 | column = &class->columns[idx]; |
3133 | 0 | key_type = column->type.key.type; |
3134 | | |
3135 | | /* Get the value to be changed */ |
3136 | 0 | if (row->new_datum && row->written |
3137 | 0 | && bitmap_is_set(row->written,idx)) { |
3138 | 0 | old_datum = &row->new_datum[idx]; |
3139 | 0 | } else if (row->old_datum != NULL) { |
3140 | 0 | old_datum = &row->old_datum[idx]; |
3141 | 0 | } else { |
3142 | 0 | old_datum = ovsdb_datum_default(&column->type); |
3143 | 0 | } |
3144 | |
|
3145 | 0 | del_set = json_array_create_empty(); |
3146 | 0 | ins_set = json_array_create_empty(); |
3147 | 0 | any_del = false; |
3148 | 0 | any_ins = false; |
3149 | |
|
3150 | 0 | for (struct set_op *set_op = set_op_list_first(set_op_list); set_op; |
3151 | 0 | set_op = set_op_list_next(set_op_list, set_op)) { |
3152 | 0 | if (set_op_type(set_op) == SET_OP_INSERT) { |
3153 | 0 | set = ovsdb_atom_to_json(&set_op_datum(set_op)->keys[0], |
3154 | 0 | key_type); |
3155 | 0 | json_array_add(ins_set, set); |
3156 | 0 | any_ins = true; |
3157 | 0 | } else { /* SETP_OP_DELETE */ |
3158 | | /* Verify that there is a key to delete. */ |
3159 | 0 | if (!ovsdb_datum_find_key(old_datum, |
3160 | 0 | &set_op_datum(set_op)->keys[0], |
3161 | 0 | key_type, NULL)) { |
3162 | | /* No key to delete. Move on to next update. */ |
3163 | 0 | VLOG_WARN("Trying to delete a key that doesn't " |
3164 | 0 | "exist in the set."); |
3165 | 0 | continue; |
3166 | 0 | } |
3167 | 0 | set = ovsdb_atom_to_json(&set_op_datum(set_op)->keys[0], |
3168 | 0 | key_type); |
3169 | 0 | json_array_add(del_set, set); |
3170 | 0 | any_del = true; |
3171 | 0 | } |
3172 | 0 | } |
3173 | 0 | if (any_del) { |
3174 | 0 | col_name = json_string_create(column->name); |
3175 | 0 | mutator = json_string_create("delete"); |
3176 | 0 | set = json_array_create_2(json_string_create("set"), del_set); |
3177 | 0 | mutation = json_array_create_3(col_name, mutator, set); |
3178 | 0 | json_array_add(mutations, mutation); |
3179 | 0 | any_mutations = true; |
3180 | 0 | } else { |
3181 | 0 | json_destroy(del_set); |
3182 | 0 | } |
3183 | 0 | if (any_ins) { |
3184 | 0 | col_name = json_string_create(column->name); |
3185 | 0 | mutator = json_string_create("insert"); |
3186 | 0 | set = json_array_create_2(json_string_create("set"), ins_set); |
3187 | 0 | mutation = json_array_create_3(col_name, mutator, set); |
3188 | 0 | json_array_add(mutations, mutation); |
3189 | 0 | any_mutations = true; |
3190 | 0 | } else { |
3191 | 0 | json_destroy(ins_set); |
3192 | 0 | } |
3193 | 0 | } |
3194 | 0 | } |
3195 | 0 | return any_mutations; |
3196 | 0 | } |
3197 | | |
3198 | | /* Attempts to commit 'txn'. Returns the status of the commit operation, one |
3199 | | * of the following TXN_* constants: |
3200 | | * |
3201 | | * TXN_INCOMPLETE: |
3202 | | * |
3203 | | * The transaction is in progress, but not yet complete. The caller |
3204 | | * should call again later, after calling ovsdb_idl_run() to let the IDL |
3205 | | * do OVSDB protocol processing. |
3206 | | * |
3207 | | * TXN_UNCHANGED: |
3208 | | * |
3209 | | * The transaction is complete. (It didn't actually change the database, |
3210 | | * so the IDL didn't send any request to the database server.) |
3211 | | * |
3212 | | * TXN_ABORTED: |
3213 | | * |
3214 | | * The caller previously called ovsdb_idl_txn_abort(). |
3215 | | * |
3216 | | * TXN_SUCCESS: |
3217 | | * |
3218 | | * The transaction was successful. The update made by the transaction |
3219 | | * (and possibly other changes made by other database clients) should |
3220 | | * already be visible in the IDL. |
3221 | | * |
3222 | | * TXN_TRY_AGAIN: |
3223 | | * |
3224 | | * The transaction failed for some transient reason, e.g. because a |
3225 | | * "verify" operation reported an inconsistency or due to a network |
3226 | | * problem. The caller should wait for a change to the database, then |
3227 | | * compose a new transaction, and commit the new transaction. |
3228 | | * |
3229 | | * Use the return value of ovsdb_idl_get_seqno() to wait for a change in |
3230 | | * the database. It is important to use its return value *before* the |
3231 | | * initial call to ovsdb_idl_txn_commit() as the baseline for this |
3232 | | * purpose, because the change that one should wait for can happen after |
3233 | | * the initial call but before the call that returns TXN_TRY_AGAIN, and |
3234 | | * using some other baseline value in that situation could cause an |
3235 | | * indefinite wait if the database rarely changes. |
3236 | | * |
3237 | | * TXN_NOT_LOCKED: |
3238 | | * |
3239 | | * The transaction failed because the IDL has been configured to require |
3240 | | * a database lock (with ovsdb_idl_set_lock()) but didn't get it yet or |
3241 | | * has already lost it. |
3242 | | * |
3243 | | * Committing a transaction rolls back all of the changes that it made to the |
3244 | | * IDL's copy of the database. If the transaction commits successfully, then |
3245 | | * the database server will send an update and, thus, the IDL will be updated |
3246 | | * with the committed changes. */ |
3247 | | enum ovsdb_idl_txn_status |
3248 | | ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) |
3249 | 0 | { |
3250 | 0 | struct ovsdb_idl *idl = txn->idl; |
3251 | 0 | if (txn != idl->txn) { |
3252 | 0 | goto coverage_out; |
3253 | 0 | } else if (!ovsdb_cs_may_send_transaction(idl->cs)) { |
3254 | 0 | txn->status = TXN_TRY_AGAIN; |
3255 | 0 | goto disassemble_out; |
3256 | 0 | } else if (ovsdb_cs_get_lock(idl->cs) && !ovsdb_cs_has_lock(idl->cs)) { |
3257 | 0 | txn->status = TXN_NOT_LOCKED; |
3258 | 0 | goto disassemble_out; |
3259 | 0 | } |
3260 | | |
3261 | 0 | struct json *operations = json_array_create_1( |
3262 | 0 | json_string_create(idl->class_->database)); |
3263 | | |
3264 | | /* Add prerequisites and declarations of new rows. */ |
3265 | 0 | struct ovsdb_idl_row *row; |
3266 | 0 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
3267 | | /* XXX check that deleted rows exist even if no prereqs? */ |
3268 | 0 | if (row->prereqs) { |
3269 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
3270 | 0 | size_t n_columns = class->n_columns; |
3271 | 0 | struct json *op, *columns, *row_json; |
3272 | 0 | size_t idx; |
3273 | |
|
3274 | 0 | op = json_object_create(); |
3275 | 0 | json_array_add(operations, op); |
3276 | 0 | json_object_put_string(op, "op", "wait"); |
3277 | 0 | json_object_put_string(op, "table", class->name); |
3278 | 0 | json_object_put(op, "timeout", json_integer_create(0)); |
3279 | 0 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); |
3280 | 0 | json_object_put_string(op, "until", "=="); |
3281 | 0 | columns = json_array_create_empty(); |
3282 | 0 | json_object_put(op, "columns", columns); |
3283 | 0 | row_json = json_object_create(); |
3284 | 0 | json_object_put(op, "rows", json_array_create_1(row_json)); |
3285 | |
|
3286 | 0 | BITMAP_FOR_EACH_1 (idx, n_columns, row->prereqs) { |
3287 | 0 | const struct ovsdb_idl_column *column = &class->columns[idx]; |
3288 | 0 | json_array_add(columns, json_string_create(column->name)); |
3289 | 0 | json_object_put(row_json, column->name, |
3290 | 0 | ovsdb_datum_to_json(&row->old_datum[idx], |
3291 | 0 | &column->type)); |
3292 | 0 | } |
3293 | 0 | } |
3294 | 0 | } |
3295 | | |
3296 | | /* Add updates. */ |
3297 | 0 | bool any_updates = false; |
3298 | | |
3299 | | /* For tables constrained to have only a single row (a fairly common OVSDB |
3300 | | * pattern for storing global data), identify whether we're inserting a |
3301 | | * row. If so, then verify that the table is empty before inserting the |
3302 | | * row. This gives us a clear verification-related failure if there was an |
3303 | | * insertion race with another client. */ |
3304 | 0 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
3305 | 0 | struct ovsdb_idl_table *table = &idl->tables[i]; |
3306 | 0 | if (table->class_->is_singleton) { |
3307 | | /* Count the number of rows in the table before and after our |
3308 | | * transaction commits. This is O(n) in the number of rows in the |
3309 | | * table, but that's OK since we know that the table should only |
3310 | | * have one row. */ |
3311 | 0 | size_t initial_rows = 0; |
3312 | 0 | size_t final_rows = 0; |
3313 | 0 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { |
3314 | 0 | initial_rows += row->old_datum != NULL; |
3315 | 0 | final_rows += row->new_datum != NULL; |
3316 | 0 | } |
3317 | |
|
3318 | 0 | if (initial_rows == 0 && final_rows == 1) { |
3319 | 0 | struct json *op = json_object_create(); |
3320 | 0 | json_array_add(operations, op); |
3321 | 0 | json_object_put_string(op, "op", "wait"); |
3322 | 0 | json_object_put_string(op, "table", table->class_->name); |
3323 | 0 | json_object_put(op, "where", json_array_create_empty()); |
3324 | 0 | json_object_put(op, "timeout", json_integer_create(0)); |
3325 | 0 | json_object_put_string(op, "until", "=="); |
3326 | 0 | json_object_put(op, "rows", json_array_create_empty()); |
3327 | 0 | } |
3328 | 0 | } |
3329 | 0 | } |
3330 | |
|
3331 | 0 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
3332 | 0 | const struct ovsdb_idl_table_class *class = row->table->class_; |
3333 | |
|
3334 | 0 | if (!row->new_datum) { |
3335 | 0 | if (class->is_root) { |
3336 | 0 | struct json *op = json_object_create(); |
3337 | 0 | json_object_put_string(op, "op", "delete"); |
3338 | 0 | json_object_put_string(op, "table", class->name); |
3339 | 0 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); |
3340 | 0 | json_array_add(operations, op); |
3341 | 0 | any_updates = true; |
3342 | 0 | } else { |
3343 | | /* Let ovsdb-server decide whether to really delete it. */ |
3344 | 0 | } |
3345 | 0 | } else if (row->old_datum != row->new_datum) { |
3346 | 0 | struct json *row_json; |
3347 | 0 | size_t idx; |
3348 | |
|
3349 | 0 | struct json *op = json_object_create(); |
3350 | 0 | json_object_put_string(op, "op", |
3351 | 0 | row->old_datum ? "update" : "insert"); |
3352 | 0 | json_object_put_string(op, "table", class->name); |
3353 | 0 | if (row->old_datum) { |
3354 | 0 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); |
3355 | 0 | } else { |
3356 | 0 | struct ovsdb_idl_txn_insert *insert; |
3357 | |
|
3358 | 0 | any_updates = true; |
3359 | |
|
3360 | 0 | char *uuid_json; |
3361 | 0 | struct json *value; |
3362 | 0 | if (row->persist_uuid) { |
3363 | 0 | uuid_json = "uuid"; |
3364 | 0 | value = json_string_create_uuid(&row->uuid); |
3365 | 0 | } else { |
3366 | 0 | uuid_json = "uuid-name"; |
3367 | 0 | value = json_string_create_nocopy( |
3368 | 0 | ovsdb_data_row_name(&row->uuid)); |
3369 | 0 | } |
3370 | |
|
3371 | 0 | json_object_put(op, uuid_json, value); |
3372 | |
|
3373 | 0 | insert = xmalloc(sizeof *insert); |
3374 | 0 | insert->dummy = row->uuid; |
3375 | 0 | insert->op_index = json_array_size(operations) - 1; |
3376 | 0 | uuid_zero(&insert->real); |
3377 | 0 | hmap_insert(&txn->inserted_rows, &insert->hmap_node, |
3378 | 0 | uuid_hash(&insert->dummy)); |
3379 | 0 | } |
3380 | 0 | row_json = json_object_create(); |
3381 | 0 | json_object_put(op, "row", row_json); |
3382 | |
|
3383 | 0 | if (row->written) { |
3384 | 0 | BITMAP_FOR_EACH_1 (idx, class->n_columns, row->written) { |
3385 | 0 | const struct ovsdb_idl_column *column = |
3386 | 0 | &class->columns[idx]; |
3387 | |
|
3388 | 0 | if (row->old_datum |
3389 | 0 | || !ovsdb_datum_is_default(&row->new_datum[idx], |
3390 | 0 | &column->type)) { |
3391 | 0 | struct json *value; |
3392 | |
|
3393 | 0 | value = ovsdb_datum_to_json(&row->new_datum[idx], |
3394 | 0 | &column->type); |
3395 | 0 | json_object_put(row_json, column->name, |
3396 | 0 | substitute_uuids(value, txn)); |
3397 | | |
3398 | | /* If anything really changed, consider it an update. |
3399 | | * We can't suppress not-really-changed values earlier |
3400 | | * or transactions would become nonatomic (see the big |
3401 | | * comment inside ovsdb_idl_txn_write()). */ |
3402 | 0 | if (!any_updates && row->old_datum && |
3403 | 0 | !ovsdb_datum_equals(&row->old_datum[idx], |
3404 | 0 | &row->new_datum[idx], |
3405 | 0 | &column->type)) { |
3406 | 0 | any_updates = true; |
3407 | 0 | } |
3408 | 0 | } |
3409 | 0 | } |
3410 | 0 | } |
3411 | |
|
3412 | 0 | if (!row->old_datum || !shash_is_empty(json_object(row_json))) { |
3413 | 0 | json_array_add(operations, op); |
3414 | 0 | } else { |
3415 | 0 | json_destroy(op); |
3416 | 0 | } |
3417 | 0 | } |
3418 | | |
3419 | | /* Add mutate operation, for partial map or partial set updates. */ |
3420 | 0 | if (row->map_op_written || row->set_op_written) { |
3421 | 0 | struct json *op, *mutations; |
3422 | 0 | bool any_mutations; |
3423 | |
|
3424 | 0 | op = json_object_create(); |
3425 | 0 | json_object_put_string(op, "op", "mutate"); |
3426 | 0 | json_object_put_string(op, "table", class->name); |
3427 | 0 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); |
3428 | 0 | mutations = json_array_create_empty(); |
3429 | 0 | any_mutations = ovsdb_idl_txn_extract_mutations(row, mutations); |
3430 | 0 | json_object_put(op, "mutations", mutations); |
3431 | |
|
3432 | 0 | if (any_mutations) { |
3433 | 0 | op = substitute_uuids(op, txn); |
3434 | 0 | json_array_add(operations, op); |
3435 | 0 | any_updates = true; |
3436 | 0 | } else { |
3437 | 0 | json_destroy(op); |
3438 | 0 | } |
3439 | 0 | } |
3440 | 0 | } |
3441 | | |
3442 | | /* Add increment. */ |
3443 | 0 | if (txn->inc_table && (any_updates || txn->inc_force)) { |
3444 | 0 | any_updates = true; |
3445 | 0 | txn->inc_index = json_array_size(operations) - 1; |
3446 | |
|
3447 | 0 | struct json *op = json_object_create(); |
3448 | 0 | json_object_put_string(op, "op", "mutate"); |
3449 | 0 | json_object_put_string(op, "table", txn->inc_table); |
3450 | 0 | json_object_put(op, "where", |
3451 | 0 | substitute_uuids(where_uuid_equals(&txn->inc_row), |
3452 | 0 | txn)); |
3453 | 0 | json_object_put(op, "mutations", |
3454 | 0 | json_array_create_1( |
3455 | 0 | json_array_create_3( |
3456 | 0 | json_string_create(txn->inc_column), |
3457 | 0 | json_string_create("+="), |
3458 | 0 | json_integer_create(1)))); |
3459 | 0 | json_array_add(operations, op); |
3460 | |
|
3461 | 0 | op = json_object_create(); |
3462 | 0 | json_object_put_string(op, "op", "select"); |
3463 | 0 | json_object_put_string(op, "table", txn->inc_table); |
3464 | 0 | json_object_put(op, "where", |
3465 | 0 | substitute_uuids(where_uuid_equals(&txn->inc_row), |
3466 | 0 | txn)); |
3467 | 0 | json_object_put(op, "columns", |
3468 | 0 | json_array_create_1(json_string_create( |
3469 | 0 | txn->inc_column))); |
3470 | 0 | json_array_add(operations, op); |
3471 | 0 | } |
3472 | |
|
3473 | 0 | if (txn->comment.length) { |
3474 | 0 | struct json *op = json_object_create(); |
3475 | 0 | json_object_put_string(op, "op", "comment"); |
3476 | 0 | json_object_put_string(op, "comment", ds_cstr(&txn->comment)); |
3477 | 0 | json_array_add(operations, op); |
3478 | 0 | } |
3479 | |
|
3480 | 0 | if (txn->dry_run) { |
3481 | 0 | struct json *op = json_object_create(); |
3482 | 0 | json_object_put_string(op, "op", "abort"); |
3483 | 0 | json_array_add(operations, op); |
3484 | 0 | } |
3485 | |
|
3486 | 0 | if (!any_updates) { |
3487 | 0 | txn->status = TXN_UNCHANGED; |
3488 | 0 | json_destroy(operations); |
3489 | 0 | } else { |
3490 | 0 | txn->request_id = ovsdb_cs_send_transaction(idl->cs, operations); |
3491 | 0 | if (txn->request_id) { |
3492 | 0 | hmap_insert(&idl->outstanding_txns, &txn->hmap_node, |
3493 | 0 | json_hash(txn->request_id, 0)); |
3494 | 0 | txn->status = TXN_INCOMPLETE; |
3495 | 0 | } else { |
3496 | 0 | txn->status = TXN_TRY_AGAIN; |
3497 | 0 | } |
3498 | 0 | } |
3499 | |
|
3500 | 0 | disassemble_out: |
3501 | 0 | ovsdb_idl_txn_disassemble(txn); |
3502 | 0 | coverage_out: |
3503 | 0 | switch (txn->status) { |
3504 | 0 | case TXN_UNCOMMITTED: COVERAGE_INC(txn_uncommitted); break; |
3505 | 0 | case TXN_UNCHANGED: COVERAGE_INC(txn_unchanged); break; |
3506 | 0 | case TXN_INCOMPLETE: COVERAGE_INC(txn_incomplete); break; |
3507 | 0 | case TXN_ABORTED: COVERAGE_INC(txn_aborted); break; |
3508 | 0 | case TXN_SUCCESS: COVERAGE_INC(txn_success); break; |
3509 | 0 | case TXN_TRY_AGAIN: COVERAGE_INC(txn_try_again); break; |
3510 | 0 | case TXN_NOT_LOCKED: COVERAGE_INC(txn_not_locked); break; |
3511 | 0 | case TXN_ERROR: COVERAGE_INC(txn_error); break; |
3512 | 0 | } |
3513 | | |
3514 | 0 | return txn->status; |
3515 | 0 | } |
3516 | | |
3517 | | /* Attempts to commit 'txn', blocking until the commit either succeeds or |
3518 | | * fails. Returns the final commit status, which may be any TXN_* value other |
3519 | | * than TXN_INCOMPLETE. |
3520 | | * |
3521 | | * This function calls ovsdb_idl_run() on 'txn''s IDL, so it may cause the |
3522 | | * return value of ovsdb_idl_get_seqno() to change. */ |
3523 | | enum ovsdb_idl_txn_status |
3524 | | ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn) |
3525 | 0 | { |
3526 | 0 | enum ovsdb_idl_txn_status status; |
3527 | |
|
3528 | 0 | fatal_signal_run(); |
3529 | 0 | while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { |
3530 | 0 | ovsdb_idl_run(txn->idl); |
3531 | 0 | ovsdb_idl_wait(txn->idl); |
3532 | 0 | ovsdb_idl_txn_wait(txn); |
3533 | 0 | poll_block(); |
3534 | 0 | } |
3535 | 0 | return status; |
3536 | 0 | } |
3537 | | |
3538 | | /* Returns the final (incremented) value of the column in 'txn' that was set to |
3539 | | * be incremented by ovsdb_idl_txn_increment(). 'txn' must have committed |
3540 | | * successfully. */ |
3541 | | int64_t |
3542 | | ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn) |
3543 | 0 | { |
3544 | 0 | ovs_assert(txn->status == TXN_SUCCESS); |
3545 | 0 | return txn->inc_new_value; |
3546 | 0 | } |
3547 | | |
3548 | | /* Aborts 'txn' without sending it to the database server. This is effective |
3549 | | * only if ovsdb_idl_txn_commit() has not yet been called for 'txn'. |
3550 | | * Otherwise, it has no effect. |
3551 | | * |
3552 | | * Aborting a transaction doesn't free its memory. Use |
3553 | | * ovsdb_idl_txn_destroy() to do that. */ |
3554 | | void |
3555 | | ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn) |
3556 | 0 | { |
3557 | 0 | ovsdb_idl_txn_disassemble(txn); |
3558 | 0 | if (txn->status == TXN_UNCOMMITTED || txn->status == TXN_INCOMPLETE) { |
3559 | 0 | txn->status = TXN_ABORTED; |
3560 | 0 | } |
3561 | 0 | } |
3562 | | |
3563 | | /* Returns a string that reports the error status for 'txn'. The caller must |
3564 | | * not modify or free the returned string. A call to ovsdb_idl_txn_destroy() |
3565 | | * for 'txn' may free the returned string. |
3566 | | * |
3567 | | * The return value is ordinarily one of the strings that |
3568 | | * ovsdb_idl_txn_status_to_string() would return, but if the transaction failed |
3569 | | * due to an error reported by the database server, the return value is that |
3570 | | * error. */ |
3571 | | const char * |
3572 | | ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn) |
3573 | 0 | { |
3574 | 0 | if (txn->status != TXN_ERROR) { |
3575 | 0 | return ovsdb_idl_txn_status_to_string(txn->status); |
3576 | 0 | } else if (txn->error) { |
3577 | 0 | return txn->error; |
3578 | 0 | } else { |
3579 | 0 | return "no error details available"; |
3580 | 0 | } |
3581 | 0 | } |
3582 | | |
3583 | | static void |
3584 | | ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn, |
3585 | | const struct json *json) |
3586 | 0 | { |
3587 | 0 | if (json && txn->error == NULL) { |
3588 | 0 | txn->error = json_to_string(json, JSSF_SORT); |
3589 | 0 | } |
3590 | 0 | } |
3591 | | |
3592 | | /* For transaction 'txn' that completed successfully, finds and returns the |
3593 | | * permanent UUID that the database assigned to a newly inserted row, given the |
3594 | | * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row. |
3595 | | * |
3596 | | * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or |
3597 | | * if it was assigned by that function and then deleted by |
3598 | | * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted |
3599 | | * and then deleted within a single transaction are never sent to the database |
3600 | | * server, so it never assigns them a permanent UUID.) */ |
3601 | | const struct uuid * |
3602 | | ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn *txn, |
3603 | | const struct uuid *uuid) |
3604 | 0 | { |
3605 | 0 | const struct ovsdb_idl_txn_insert *insert; |
3606 | |
|
3607 | 0 | ovs_assert(txn->status == TXN_SUCCESS || txn->status == TXN_UNCHANGED); |
3608 | 0 | HMAP_FOR_EACH_IN_BUCKET (insert, hmap_node, |
3609 | 0 | uuid_hash(uuid), &txn->inserted_rows) { |
3610 | 0 | if (uuid_equals(uuid, &insert->dummy)) { |
3611 | 0 | return &insert->real; |
3612 | 0 | } |
3613 | 0 | } |
3614 | 0 | return NULL; |
3615 | 0 | } |
3616 | | |
3617 | | static void |
3618 | | ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, |
3619 | | enum ovsdb_idl_txn_status status) |
3620 | 0 | { |
3621 | 0 | txn->status = status; |
3622 | 0 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); |
3623 | 0 | } |
3624 | | |
3625 | | static void |
3626 | | ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_, |
3627 | | const struct ovsdb_idl_column *column, |
3628 | | struct ovsdb_datum *datum, bool owns_datum) |
3629 | 0 | { |
3630 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
3631 | 0 | const struct ovsdb_idl_table_class *class; |
3632 | 0 | unsigned char column_mode; |
3633 | 0 | bool optimize_rewritten; |
3634 | 0 | size_t column_idx; |
3635 | 0 | bool write_only; |
3636 | |
|
3637 | 0 | ovs_assert(!column->is_synthetic); |
3638 | 0 | if (ovsdb_idl_row_is_synthetic(row)) { |
3639 | 0 | goto discard_datum; |
3640 | 0 | } |
3641 | | |
3642 | 0 | class = row->table->class_; |
3643 | 0 | column_idx = column - class->columns; |
3644 | 0 | column_mode = row->table->modes[column_idx]; |
3645 | 0 | write_only = column_mode == OVSDB_IDL_MONITOR; |
3646 | 0 | optimize_rewritten = |
3647 | 0 | write_only || (column_mode & OVSDB_IDL_WRITE_CHANGED_ONLY); |
3648 | | |
3649 | |
|
3650 | 0 | ovs_assert(row->new_datum != NULL); |
3651 | 0 | ovs_assert(column_idx < class->n_columns); |
3652 | 0 | ovs_assert(row->old_datum == NULL || column_mode & OVSDB_IDL_MONITOR); |
3653 | |
|
3654 | 0 | if (row->table->idl->verify_write_only && !write_only) { |
3655 | 0 | VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when" |
3656 | 0 | " explicitly configured not to.", class->name, column->name); |
3657 | 0 | goto discard_datum; |
3658 | 0 | } |
3659 | | |
3660 | | /* If this is a write-only column and the datum being written is the same |
3661 | | * as the one already there, just skip the update entirely. This is worth |
3662 | | * optimizing because we have a lot of columns that get periodically |
3663 | | * refreshed into the database but don't actually change that often. |
3664 | | * |
3665 | | * We don't do this for read/write columns because that would break |
3666 | | * atomicity of transactions--some other client might have written a |
3667 | | * different value in that column since we read it. (But if a whole |
3668 | | * transaction only does writes of existing values, without making any real |
3669 | | * changes, we will drop the whole transaction later in |
3670 | | * ovsdb_idl_txn_commit().) |
3671 | | * |
3672 | | * The application may choose to bypass this restriction and always |
3673 | | * optimize by setting OVSDB_IDL_WRITE_CHANGED_ONLY. |
3674 | | */ |
3675 | 0 | if (optimize_rewritten && ovsdb_datum_equals(ovsdb_idl_read(row, column), |
3676 | 0 | datum, &column->type)) { |
3677 | 0 | goto discard_datum; |
3678 | 0 | } |
3679 | | |
3680 | 0 | bool index_row = is_index_row(row); |
3681 | 0 | if (!index_row) { |
3682 | 0 | ovsdb_idl_remove_from_indexes(row); |
3683 | 0 | } |
3684 | 0 | if (hmap_node_is_null(&row->txn_node)) { |
3685 | 0 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, |
3686 | 0 | uuid_hash(&row->uuid)); |
3687 | 0 | } |
3688 | 0 | if (row->old_datum == row->new_datum) { |
3689 | 0 | row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum); |
3690 | 0 | } |
3691 | 0 | if (!row->written) { |
3692 | 0 | row->written = bitmap_allocate(class->n_columns); |
3693 | 0 | } |
3694 | 0 | if (bitmap_is_set(row->written, column_idx)) { |
3695 | 0 | ovsdb_datum_destroy(&row->new_datum[column_idx], &column->type); |
3696 | 0 | } else { |
3697 | 0 | bitmap_set1(row->written, column_idx); |
3698 | 0 | } |
3699 | 0 | if (owns_datum) { |
3700 | 0 | row->new_datum[column_idx] = *datum; |
3701 | 0 | } else { |
3702 | 0 | ovsdb_datum_clone(&row->new_datum[column_idx], datum); |
3703 | 0 | } |
3704 | 0 | (column->unparse)(row); |
3705 | 0 | (column->parse)(row, &row->new_datum[column_idx]); |
3706 | 0 | row->parsed = true; |
3707 | 0 | if (!index_row) { |
3708 | 0 | ovsdb_idl_add_to_indexes(row); |
3709 | 0 | } |
3710 | 0 | return; |
3711 | | |
3712 | 0 | discard_datum: |
3713 | 0 | if (owns_datum) { |
3714 | 0 | ovsdb_datum_destroy(datum, &column->type); |
3715 | 0 | } |
3716 | 0 | } |
3717 | | |
3718 | | /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_' |
3719 | | * itself and the structs derived from it (e.g. the "struct ovsrec_*", for |
3720 | | * ovs-vswitchd). |
3721 | | * |
3722 | | * 'datum' must have the correct type for its column, but it needs not be |
3723 | | * sorted or unique because this function will take care of that. The IDL does |
3724 | | * not check that it meets schema constraints, but ovsdb-server will do so at |
3725 | | * commit time so it had better be correct. |
3726 | | * |
3727 | | * A transaction must be in progress. Replication of 'column' must not have |
3728 | | * been disabled (by calling ovsdb_idl_omit()). |
3729 | | * |
3730 | | * Usually this function is used indirectly through one of the "set" functions |
3731 | | * generated by ovsdb-idlc. |
3732 | | * |
3733 | | * Takes ownership of what 'datum' points to (and in some cases destroys that |
3734 | | * data before returning) but makes a copy of 'datum' itself. (Commonly |
3735 | | * 'datum' is on the caller's stack.) */ |
3736 | | void |
3737 | | ovsdb_idl_txn_write(const struct ovsdb_idl_row *row, |
3738 | | const struct ovsdb_idl_column *column, |
3739 | | struct ovsdb_datum *datum) |
3740 | 0 | { |
3741 | 0 | ovsdb_datum_sort_unique(datum, &column->type); |
3742 | 0 | ovsdb_idl_txn_write__(row, column, datum, true); |
3743 | 0 | } |
3744 | | |
3745 | | /* Similar to ovsdb_idl_txn_write(), except: |
3746 | | * |
3747 | | * - The caller retains ownership of 'datum' and what it points to. |
3748 | | * |
3749 | | * - The caller must ensure that 'datum' is sorted and unique (e.g. via |
3750 | | * ovsdb_datum_sort_unique().) */ |
3751 | | void |
3752 | | ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *row, |
3753 | | const struct ovsdb_idl_column *column, |
3754 | | const struct ovsdb_datum *datum) |
3755 | 0 | { |
3756 | 0 | ovsdb_idl_txn_write__(row, column, |
3757 | 0 | CONST_CAST(struct ovsdb_datum *, datum), false); |
3758 | 0 | } |
3759 | | |
3760 | | /* Causes the original contents of 'column' in 'row_' to be verified as a |
3761 | | * prerequisite to completing the transaction. That is, if 'column' in 'row_' |
3762 | | * changed (or if 'row_' was deleted) between the time that the IDL originally |
3763 | | * read its contents and the time that the transaction commits, then the |
3764 | | * transaction aborts and ovsdb_idl_txn_commit() returns TXN_TRY_AGAIN. |
3765 | | * |
3766 | | * The intention is that, to ensure that no transaction commits based on dirty |
3767 | | * reads, an application should call ovsdb_idl_txn_verify() on each data item |
3768 | | * read as part of a read-modify-write operation. |
3769 | | * |
3770 | | * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current |
3771 | | * value of 'column' is already known: |
3772 | | * |
3773 | | * - If 'row_' is a row created by the current transaction (returned by |
3774 | | * ovsdb_idl_txn_insert()). |
3775 | | * |
3776 | | * - If 'column' has already been modified (with ovsdb_idl_txn_write()) |
3777 | | * within the current transaction. |
3778 | | * |
3779 | | * Because of the latter property, always call ovsdb_idl_txn_verify() *before* |
3780 | | * ovsdb_idl_txn_write() for a given read-modify-write. |
3781 | | * |
3782 | | * A transaction must be in progress. |
3783 | | * |
3784 | | * Usually this function is used indirectly through one of the "verify" |
3785 | | * functions generated by ovsdb-idlc. */ |
3786 | | void |
3787 | | ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_, |
3788 | | const struct ovsdb_idl_column *column) |
3789 | 0 | { |
3790 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
3791 | 0 | const struct ovsdb_idl_table_class *class; |
3792 | 0 | size_t column_idx; |
3793 | |
|
3794 | 0 | if (ovsdb_idl_row_is_synthetic(row)) { |
3795 | 0 | return; |
3796 | 0 | } |
3797 | | |
3798 | 0 | class = row->table->class_; |
3799 | 0 | column_idx = column - class->columns; |
3800 | |
|
3801 | 0 | ovs_assert(row->new_datum != NULL); |
3802 | 0 | ovs_assert(row->old_datum == NULL || |
3803 | 0 | row->table->modes[column_idx] & OVSDB_IDL_MONITOR); |
3804 | 0 | if (!row->old_datum |
3805 | 0 | || (row->written && bitmap_is_set(row->written, column_idx))) { |
3806 | 0 | return; |
3807 | 0 | } |
3808 | | |
3809 | 0 | if (hmap_node_is_null(&row->txn_node)) { |
3810 | 0 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, |
3811 | 0 | uuid_hash(&row->uuid)); |
3812 | 0 | } |
3813 | 0 | if (!row->prereqs) { |
3814 | 0 | row->prereqs = bitmap_allocate(class->n_columns); |
3815 | 0 | } |
3816 | 0 | bitmap_set1(row->prereqs, column_idx); |
3817 | 0 | } |
3818 | | |
3819 | | /* Deletes 'row_' from its table. May free 'row_', so it must not be |
3820 | | * accessed afterward. |
3821 | | * |
3822 | | * A transaction must be in progress. |
3823 | | * |
3824 | | * Usually this function is used indirectly through one of the "delete" |
3825 | | * functions generated by ovsdb-idlc. */ |
3826 | | void |
3827 | | ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_) |
3828 | 0 | { |
3829 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
3830 | |
|
3831 | 0 | if (ovsdb_idl_row_is_synthetic(row)) { |
3832 | 0 | return; |
3833 | 0 | } |
3834 | | |
3835 | 0 | ovs_assert(row->new_datum != NULL); |
3836 | 0 | ovs_assert(!is_index_row(row_)); |
3837 | 0 | ovsdb_idl_remove_from_indexes(row_); |
3838 | 0 | if (!row->old_datum) { |
3839 | 0 | ovsdb_idl_row_unparse(row); |
3840 | 0 | ovsdb_idl_destroy_all_map_op_lists(row); |
3841 | 0 | ovsdb_idl_destroy_all_set_op_lists(row); |
3842 | 0 | ovsdb_idl_row_clear_new(row); |
3843 | 0 | ovs_assert(!row->prereqs); |
3844 | 0 | hmap_remove(&row->table->rows, &row->hmap_node); |
3845 | 0 | hmap_remove(&row->table->idl->txn->txn_rows, &row->txn_node); |
3846 | 0 | free(row); |
3847 | 0 | return; |
3848 | 0 | } |
3849 | 0 | if (hmap_node_is_null(&row->txn_node)) { |
3850 | 0 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, |
3851 | 0 | uuid_hash(&row->uuid)); |
3852 | 0 | } |
3853 | 0 | ovsdb_idl_row_clear_new(row); |
3854 | 0 | row->new_datum = NULL; |
3855 | 0 | } |
3856 | | |
3857 | | static const struct ovsdb_idl_row * |
3858 | | ovsdb_idl_txn_insert__(struct ovsdb_idl_txn *txn, |
3859 | | const struct ovsdb_idl_table_class *class, |
3860 | | const struct uuid *uuid, |
3861 | | bool persist_uuid) |
3862 | 0 | { |
3863 | 0 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class); |
3864 | |
|
3865 | 0 | ovs_assert(uuid || !persist_uuid); |
3866 | 0 | if (uuid) { |
3867 | 0 | ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid)); |
3868 | 0 | row->uuid = *uuid; |
3869 | 0 | } else { |
3870 | 0 | uuid_generate(&row->uuid); |
3871 | 0 | } |
3872 | 0 | row->persist_uuid = persist_uuid; |
3873 | 0 | row->table = ovsdb_idl_table_from_class(txn->idl, class); |
3874 | 0 | row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum); |
3875 | 0 | hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid)); |
3876 | 0 | hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); |
3877 | 0 | ovsdb_idl_add_to_indexes(row); |
3878 | |
|
3879 | 0 | return row; |
3880 | 0 | } |
3881 | | |
3882 | | /* Inserts and returns a new row in the table with the specified 'class' in the |
3883 | | * database with open transaction 'txn'. |
3884 | | * |
3885 | | * The new row is assigned a provisional UUID. If 'uuid' is null then one is |
3886 | | * randomly generated; otherwise 'uuid' should specify a randomly generated |
3887 | | * UUID not otherwise in use. ovsdb-server will assign a different UUID when |
3888 | | * 'txn' is committed, but the IDL will replace any uses of the provisional |
3889 | | * UUID in the data to be to be committed by the UUID assigned by |
3890 | | * ovsdb-server. |
3891 | | * |
3892 | | * Usually this function is used indirectly through one of the "insert" |
3893 | | * functions generated by ovsdb-idlc. */ |
3894 | | const struct ovsdb_idl_row * |
3895 | | ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn, |
3896 | | const struct ovsdb_idl_table_class *class, |
3897 | | const struct uuid *uuid) |
3898 | 0 | { |
3899 | 0 | return ovsdb_idl_txn_insert__(txn, class, uuid, false); |
3900 | 0 | } |
3901 | | |
3902 | | /* Inserts and returns a new row in the table with the specified 'class' in the |
3903 | | * database with open transaction 'txn'. |
3904 | | * |
3905 | | * The new row is assigned the specified UUID (which cannot be null). |
3906 | | * |
3907 | | * Usually this function is used indirectly through one of the |
3908 | | * "insert_persist_uuid" functions generated by ovsdb-idlc. */ |
3909 | | const struct ovsdb_idl_row * |
3910 | | ovsdb_idl_txn_insert_persist_uuid(struct ovsdb_idl_txn *txn, |
3911 | | const struct ovsdb_idl_table_class *class, |
3912 | | const struct uuid *uuid) |
3913 | 0 | { |
3914 | 0 | ovs_assert(uuid); |
3915 | 0 | return ovsdb_idl_txn_insert__(txn, class, uuid, true); |
3916 | 0 | } |
3917 | | |
3918 | | static void |
3919 | | ovsdb_idl_txn_abort_all(struct ovsdb_idl *idl) |
3920 | 0 | { |
3921 | 0 | struct ovsdb_idl_txn *txn; |
3922 | |
|
3923 | 0 | HMAP_FOR_EACH (txn, hmap_node, &idl->outstanding_txns) { |
3924 | 0 | ovsdb_idl_txn_complete(txn, TXN_TRY_AGAIN); |
3925 | 0 | } |
3926 | 0 | } |
3927 | | |
3928 | | static struct ovsdb_idl_txn * |
3929 | | ovsdb_idl_txn_find(struct ovsdb_idl *idl, const struct json *id) |
3930 | 0 | { |
3931 | 0 | struct ovsdb_idl_txn *txn; |
3932 | |
|
3933 | 0 | HMAP_FOR_EACH_WITH_HASH (txn, hmap_node, |
3934 | 0 | json_hash(id, 0), &idl->outstanding_txns) { |
3935 | 0 | if (json_equal(id, txn->request_id)) { |
3936 | 0 | return txn; |
3937 | 0 | } |
3938 | 0 | } |
3939 | 0 | return NULL; |
3940 | 0 | } |
3941 | | |
3942 | | static bool |
3943 | | check_json_type(const struct json *json, enum json_type type, const char *name) |
3944 | 0 | { |
3945 | 0 | if (!json) { |
3946 | 0 | VLOG_WARN_RL(&syntax_rl, "%s is missing", name); |
3947 | 0 | return false; |
3948 | 0 | } else if (json->type != type) { |
3949 | 0 | VLOG_WARN_RL(&syntax_rl, "%s is %s instead of %s", |
3950 | 0 | name, json_type_to_string(json->type), |
3951 | 0 | json_type_to_string(type)); |
3952 | 0 | return false; |
3953 | 0 | } else { |
3954 | 0 | return true; |
3955 | 0 | } |
3956 | 0 | } |
3957 | | |
3958 | | static bool |
3959 | | ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn, |
3960 | | const struct json *results) |
3961 | 0 | { |
3962 | 0 | const struct json *count, *rows, *row, *column; |
3963 | 0 | struct shash *mutate, *select; |
3964 | |
|
3965 | 0 | if (txn->inc_index + 2 > json_array_size(results)) { |
3966 | 0 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " |
3967 | 0 | "for increment (has %"PRIuSIZE", needs %u)", |
3968 | 0 | json_array_size(results), txn->inc_index + 2); |
3969 | 0 | return false; |
3970 | 0 | } |
3971 | | |
3972 | | /* We know that this is a JSON object because the loop in |
3973 | | * ovsdb_idl_txn_process_reply() checked. */ |
3974 | 0 | mutate = json_object(json_array_at(results, txn->inc_index)); |
3975 | 0 | count = shash_find_data(mutate, "count"); |
3976 | 0 | if (!check_json_type(count, JSON_INTEGER, "\"mutate\" reply \"count\"")) { |
3977 | 0 | return false; |
3978 | 0 | } |
3979 | 0 | if (count->integer != 1) { |
3980 | 0 | VLOG_WARN_RL(&syntax_rl, |
3981 | 0 | "\"mutate\" reply \"count\" is %lld instead of 1", |
3982 | 0 | count->integer); |
3983 | 0 | return false; |
3984 | 0 | } |
3985 | | |
3986 | 0 | select = json_object(json_array_at(results, txn->inc_index + 1)); |
3987 | 0 | rows = shash_find_data(select, "rows"); |
3988 | 0 | if (!check_json_type(rows, JSON_ARRAY, "\"select\" reply \"rows\"")) { |
3989 | 0 | return false; |
3990 | 0 | } |
3991 | 0 | if (json_array_size(rows) != 1) { |
3992 | 0 | VLOG_WARN_RL(&syntax_rl, "\"select\" reply \"rows\" has %"PRIuSIZE" elements " |
3993 | 0 | "instead of 1", |
3994 | 0 | json_array_size(rows)); |
3995 | 0 | return false; |
3996 | 0 | } |
3997 | 0 | row = json_array_at(rows, 0); |
3998 | 0 | if (!check_json_type(row, JSON_OBJECT, "\"select\" reply row")) { |
3999 | 0 | return false; |
4000 | 0 | } |
4001 | 0 | column = shash_find_data(json_object(row), txn->inc_column); |
4002 | 0 | if (!check_json_type(column, JSON_INTEGER, |
4003 | 0 | "\"select\" reply inc column")) { |
4004 | 0 | return false; |
4005 | 0 | } |
4006 | 0 | txn->inc_new_value = column->integer; |
4007 | 0 | return true; |
4008 | 0 | } |
4009 | | |
4010 | | static bool |
4011 | | ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert, |
4012 | | const struct json *results) |
4013 | 0 | { |
4014 | 0 | static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT; |
4015 | 0 | struct ovsdb_error *error; |
4016 | 0 | struct json *json_uuid; |
4017 | 0 | union ovsdb_atom uuid; |
4018 | 0 | struct shash *reply; |
4019 | |
|
4020 | 0 | if (insert->op_index >= json_array_size(results)) { |
4021 | 0 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " |
4022 | 0 | "for insert (has %"PRIuSIZE", needs %u)", |
4023 | 0 | json_array_size(results), insert->op_index); |
4024 | 0 | return false; |
4025 | 0 | } |
4026 | | |
4027 | | /* We know that this is a JSON object because the loop in |
4028 | | * ovsdb_idl_txn_process_reply() checked. */ |
4029 | 0 | reply = json_object(json_array_at(results, insert->op_index)); |
4030 | 0 | json_uuid = shash_find_data(reply, "uuid"); |
4031 | 0 | if (!check_json_type(json_uuid, JSON_ARRAY, "\"insert\" reply \"uuid\"")) { |
4032 | 0 | return false; |
4033 | 0 | } |
4034 | | |
4035 | 0 | error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL); |
4036 | 0 | if (error) { |
4037 | 0 | char *s = ovsdb_error_to_string_free(error); |
4038 | 0 | VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON " |
4039 | 0 | "UUID: %s", s); |
4040 | 0 | free(s); |
4041 | 0 | return false; |
4042 | 0 | } |
4043 | | |
4044 | 0 | insert->real = uuid.uuid; |
4045 | |
|
4046 | 0 | return true; |
4047 | 0 | } |
4048 | | |
4049 | | static void |
4050 | | ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl, |
4051 | | const struct jsonrpc_msg *msg) |
4052 | 0 | { |
4053 | 0 | struct ovsdb_idl_txn *txn = ovsdb_idl_txn_find(idl, msg->id); |
4054 | 0 | if (!txn) { |
4055 | 0 | return; |
4056 | 0 | } |
4057 | | |
4058 | 0 | enum ovsdb_idl_txn_status status; |
4059 | 0 | if (msg->type == JSONRPC_ERROR) { |
4060 | 0 | if (msg->error |
4061 | 0 | && msg->error->type == JSON_STRING |
4062 | 0 | && !strcmp(json_string(msg->error), "canceled")) { |
4063 | | /* ovsdb-server uses this error message to indicate that the |
4064 | | * transaction was canceled because the database in question was |
4065 | | * removed, converted, etc. */ |
4066 | 0 | status = TXN_TRY_AGAIN; |
4067 | 0 | } else { |
4068 | 0 | status = TXN_ERROR; |
4069 | 0 | ovsdb_idl_txn_set_error_json(txn, msg->error); |
4070 | 0 | } |
4071 | 0 | } else if (msg->result->type != JSON_ARRAY) { |
4072 | 0 | VLOG_WARN_RL(&syntax_rl, "reply to \"transact\" is not JSON array"); |
4073 | 0 | status = TXN_ERROR; |
4074 | 0 | ovsdb_idl_txn_set_error_json(txn, msg->result); |
4075 | 0 | } else { |
4076 | 0 | const struct json *ops = msg->result; |
4077 | 0 | int hard_errors = 0; |
4078 | 0 | int soft_errors = 0; |
4079 | 0 | int lock_errors = 0; |
4080 | 0 | size_t i, n; |
4081 | |
|
4082 | 0 | n = json_array_size(ops); |
4083 | 0 | for (i = 0; i < n; i++) { |
4084 | 0 | const struct json *op = json_array_at(ops, i); |
4085 | |
|
4086 | 0 | if (op->type == JSON_NULL) { |
4087 | | /* This isn't an error in itself but indicates that some prior |
4088 | | * operation failed, so make sure that we know about it. */ |
4089 | 0 | soft_errors++; |
4090 | 0 | } else if (op->type == JSON_OBJECT) { |
4091 | 0 | struct json *error; |
4092 | |
|
4093 | 0 | error = shash_find_data(json_object(op), "error"); |
4094 | 0 | if (error) { |
4095 | 0 | if (error->type == JSON_STRING) { |
4096 | 0 | const char *error_string = json_string(error); |
4097 | |
|
4098 | 0 | if (!strcmp(error_string, "timed out")) { |
4099 | 0 | soft_errors++; |
4100 | 0 | } else if (!strcmp(error_string, |
4101 | 0 | "unknown database")) { |
4102 | 0 | ovsdb_cs_flag_inconsistency(idl->cs); |
4103 | 0 | soft_errors++; |
4104 | 0 | } else if (!strcmp(error_string, "not owner")) { |
4105 | 0 | lock_errors++; |
4106 | 0 | } else if (!strcmp(error_string, "not allowed")) { |
4107 | 0 | hard_errors++; |
4108 | 0 | ovsdb_idl_txn_set_error_json(txn, op); |
4109 | 0 | } else if (strcmp(error_string, "aborted")) { |
4110 | 0 | hard_errors++; |
4111 | 0 | ovsdb_idl_txn_set_error_json(txn, op); |
4112 | 0 | VLOG_WARN_RL(&other_rl, |
4113 | 0 | "transaction error: %s", txn->error); |
4114 | 0 | } |
4115 | 0 | } else { |
4116 | 0 | hard_errors++; |
4117 | 0 | ovsdb_idl_txn_set_error_json(txn, op); |
4118 | 0 | VLOG_WARN_RL(&syntax_rl, |
4119 | 0 | "\"error\" in reply is not JSON string"); |
4120 | 0 | } |
4121 | 0 | } |
4122 | 0 | } else { |
4123 | 0 | hard_errors++; |
4124 | 0 | ovsdb_idl_txn_set_error_json(txn, op); |
4125 | 0 | VLOG_WARN_RL(&syntax_rl, |
4126 | 0 | "operation reply is not JSON null or object"); |
4127 | 0 | } |
4128 | 0 | } |
4129 | |
|
4130 | 0 | if (!soft_errors && !hard_errors && !lock_errors) { |
4131 | 0 | struct ovsdb_idl_txn_insert *insert; |
4132 | |
|
4133 | 0 | if (txn->inc_table && !ovsdb_idl_txn_process_inc_reply(txn, ops)) { |
4134 | 0 | hard_errors++; |
4135 | 0 | } |
4136 | |
|
4137 | 0 | HMAP_FOR_EACH (insert, hmap_node, &txn->inserted_rows) { |
4138 | 0 | if (!ovsdb_idl_txn_process_insert_reply(insert, ops)) { |
4139 | 0 | hard_errors++; |
4140 | 0 | } |
4141 | 0 | } |
4142 | 0 | } |
4143 | |
|
4144 | 0 | status = (hard_errors ? TXN_ERROR |
4145 | 0 | : lock_errors ? TXN_NOT_LOCKED |
4146 | 0 | : soft_errors ? TXN_TRY_AGAIN |
4147 | 0 | : TXN_SUCCESS); |
4148 | 0 | } |
4149 | |
|
4150 | 0 | ovsdb_idl_txn_complete(txn, status); |
4151 | 0 | } |
4152 | | |
4153 | | /* Returns the transaction currently active for 'row''s IDL. A transaction |
4154 | | * must currently be active. */ |
4155 | | struct ovsdb_idl_txn * |
4156 | | ovsdb_idl_txn_get(const struct ovsdb_idl_row *row) |
4157 | 0 | { |
4158 | 0 | struct ovsdb_idl_txn *txn = row->table->idl->txn; |
4159 | 0 | ovs_assert(txn != NULL); |
4160 | 0 | return txn; |
4161 | 0 | } |
4162 | | |
4163 | | /* Returns the IDL on which 'txn' acts. */ |
4164 | | struct ovsdb_idl * |
4165 | | ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn) |
4166 | 0 | { |
4167 | 0 | return txn->idl; |
4168 | 0 | } |
4169 | | |
4170 | | /* Blocks until 'idl' successfully connects to the remote database and |
4171 | | * retrieves its contents. */ |
4172 | | void |
4173 | | ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *idl) |
4174 | 0 | { |
4175 | 0 | while (1) { |
4176 | 0 | ovsdb_idl_run(idl); |
4177 | 0 | if (ovsdb_idl_has_ever_connected(idl)) { |
4178 | 0 | return; |
4179 | 0 | } |
4180 | 0 | ovsdb_idl_wait(idl); |
4181 | 0 | poll_block(); |
4182 | 0 | } |
4183 | 0 | } |
4184 | | |
4185 | | /* If 'lock_name' is nonnull, configures 'idl' to obtain the named lock from |
4186 | | * the database server and to avoid modifying the database when the lock cannot |
4187 | | * be acquired (that is, when another client has the same lock). |
4188 | | * |
4189 | | * If 'lock_name' is NULL, drops the locking requirement and releases the |
4190 | | * lock. */ |
4191 | | void |
4192 | | ovsdb_idl_set_lock(struct ovsdb_idl *idl, const char *lock_name) |
4193 | 0 | { |
4194 | 0 | ovsdb_cs_set_lock(idl->cs, lock_name); |
4195 | 0 | } |
4196 | | |
4197 | | /* Returns true if 'idl' is configured to obtain a lock and owns that lock. |
4198 | | * |
4199 | | * Locking and unlocking happens asynchronously from the database client's |
4200 | | * point of view, so the information is only useful for optimization (e.g. if |
4201 | | * the client doesn't have the lock then there's no point in trying to write to |
4202 | | * the database). */ |
4203 | | bool |
4204 | | ovsdb_idl_has_lock(const struct ovsdb_idl *idl) |
4205 | 0 | { |
4206 | 0 | return ovsdb_cs_has_lock(idl->cs); |
4207 | 0 | } |
4208 | | |
4209 | | /* Returns true if 'idl' is configured to obtain a lock but the database server |
4210 | | * has indicated that some other client already owns the requested lock. */ |
4211 | | bool |
4212 | | ovsdb_idl_is_lock_contended(const struct ovsdb_idl *idl) |
4213 | 0 | { |
4214 | 0 | return ovsdb_cs_is_lock_contended(idl->cs); |
4215 | 0 | } |
4216 | | |
4217 | | /* Inserts a new Map Operation into current transaction. */ |
4218 | | static void |
4219 | | ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row *row, |
4220 | | const struct ovsdb_idl_column *column, |
4221 | | struct ovsdb_datum *datum, |
4222 | | enum map_op_type op_type) |
4223 | 0 | { |
4224 | 0 | const struct ovsdb_idl_table_class *class; |
4225 | 0 | size_t column_idx; |
4226 | 0 | struct map_op *map_op; |
4227 | |
|
4228 | 0 | class = row->table->class_; |
4229 | 0 | column_idx = column - class->columns; |
4230 | | |
4231 | | /* Check if a map operation list exists for this column. */ |
4232 | 0 | if (!row->map_op_written) { |
4233 | 0 | row->map_op_written = bitmap_allocate(class->n_columns); |
4234 | 0 | row->map_op_lists = xzalloc(class->n_columns * |
4235 | 0 | sizeof *row->map_op_lists); |
4236 | 0 | } |
4237 | 0 | if (!row->map_op_lists[column_idx]) { |
4238 | 0 | row->map_op_lists[column_idx] = map_op_list_create(); |
4239 | 0 | } |
4240 | | |
4241 | | /* Add a map operation to the corresponding list. */ |
4242 | 0 | map_op = map_op_create(datum, op_type); |
4243 | 0 | bitmap_set1(row->map_op_written, column_idx); |
4244 | 0 | map_op_list_add(row->map_op_lists[column_idx], map_op, &column->type); |
4245 | | |
4246 | | /* Add this row to transaction's list of rows. */ |
4247 | 0 | if (hmap_node_is_null(&row->txn_node)) { |
4248 | 0 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, |
4249 | 0 | uuid_hash(&row->uuid)); |
4250 | 0 | } |
4251 | 0 | } |
4252 | | |
4253 | | /* Inserts a new Set Operation into current transaction. */ |
4254 | | static void |
4255 | | ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row *row, |
4256 | | const struct ovsdb_idl_column *column, |
4257 | | struct ovsdb_datum *datum, |
4258 | | enum set_op_type op_type) |
4259 | 0 | { |
4260 | 0 | const struct ovsdb_idl_table_class *class; |
4261 | 0 | size_t column_idx; |
4262 | 0 | struct set_op *set_op; |
4263 | |
|
4264 | 0 | class = row->table->class_; |
4265 | 0 | column_idx = column - class->columns; |
4266 | | |
4267 | | /* Check if a set operation list exists for this column. */ |
4268 | 0 | if (!row->set_op_written) { |
4269 | 0 | row->set_op_written = bitmap_allocate(class->n_columns); |
4270 | 0 | row->set_op_lists = xzalloc(class->n_columns * |
4271 | 0 | sizeof *row->set_op_lists); |
4272 | 0 | } |
4273 | 0 | if (!row->set_op_lists[column_idx]) { |
4274 | 0 | row->set_op_lists[column_idx] = set_op_list_create(); |
4275 | 0 | } |
4276 | | |
4277 | | /* Add a set operation to the corresponding list. */ |
4278 | 0 | set_op = set_op_create(datum, op_type); |
4279 | 0 | bitmap_set1(row->set_op_written, column_idx); |
4280 | 0 | set_op_list_add(row->set_op_lists[column_idx], set_op, &column->type); |
4281 | | |
4282 | | /* Add this row to the transactions's list of rows. */ |
4283 | 0 | if (hmap_node_is_null(&row->txn_node)) { |
4284 | 0 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, |
4285 | 0 | uuid_hash(&row->uuid)); |
4286 | 0 | } |
4287 | 0 | } |
4288 | | |
4289 | | static bool |
4290 | | is_valid_partial_update(const struct ovsdb_idl_row *row, |
4291 | | const struct ovsdb_idl_column *column, |
4292 | | struct ovsdb_datum *datum) |
4293 | 0 | { |
4294 | | /* Verify that this column is being monitored. */ |
4295 | 0 | unsigned int column_idx = column - row->table->class_->columns; |
4296 | 0 | if (!(row->table->modes[column_idx] & OVSDB_IDL_MONITOR)) { |
4297 | 0 | VLOG_WARN("cannot partially update non-monitored column"); |
4298 | 0 | return false; |
4299 | 0 | } |
4300 | | |
4301 | | /* Verify that the update affects a single element. */ |
4302 | 0 | if (datum->n != 1) { |
4303 | 0 | VLOG_WARN("invalid datum for partial update"); |
4304 | 0 | return false; |
4305 | 0 | } |
4306 | | |
4307 | 0 | return true; |
4308 | 0 | } |
4309 | | |
4310 | | /* Inserts the value described in 'datum' into the map in 'column' in |
4311 | | * 'row_'. If the value doesn't already exist in 'column' then it's value |
4312 | | * is added. The value in 'datum' must be of the same type as the values |
4313 | | * in 'column'. This function takes ownership of 'datum'. |
4314 | | * |
4315 | | * Usually this function is used indirectly through one of the "update" |
4316 | | * functions generated by vswitch-idl. */ |
4317 | | void |
4318 | | ovsdb_idl_txn_write_partial_set(const struct ovsdb_idl_row *row_, |
4319 | | const struct ovsdb_idl_column *column, |
4320 | | struct ovsdb_datum *datum) |
4321 | 0 | { |
4322 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
4323 | 0 | enum set_op_type op_type; |
4324 | |
|
4325 | 0 | if (!is_valid_partial_update(row, column, datum)) { |
4326 | 0 | ovsdb_datum_destroy(datum, &column->type); |
4327 | 0 | free(datum); |
4328 | 0 | return; |
4329 | 0 | } |
4330 | | |
4331 | 0 | op_type = SET_OP_INSERT; |
4332 | |
|
4333 | 0 | ovsdb_idl_txn_add_set_op(row, column, datum, op_type); |
4334 | 0 | } |
4335 | | |
4336 | | /* Deletes the value specified in 'datum' from the set in 'column' in 'row_'. |
4337 | | * The value in 'datum' must be of the same type as the keys in 'column'. |
4338 | | * This function takes ownership of 'datum'. |
4339 | | * |
4340 | | * Usually this function is used indirectly through one of the "update" |
4341 | | * functions generated by vswitch-idl. */ |
4342 | | void |
4343 | | ovsdb_idl_txn_delete_partial_set(const struct ovsdb_idl_row *row_, |
4344 | | const struct ovsdb_idl_column *column, |
4345 | | struct ovsdb_datum *datum) |
4346 | 0 | { |
4347 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
4348 | |
|
4349 | 0 | if (!is_valid_partial_update(row, column, datum)) { |
4350 | 0 | struct ovsdb_type type_ = column->type; |
4351 | 0 | type_.value.type = OVSDB_TYPE_VOID; |
4352 | 0 | ovsdb_datum_destroy(datum, &type_); |
4353 | 0 | free(datum); |
4354 | 0 | return; |
4355 | 0 | } |
4356 | 0 | ovsdb_idl_txn_add_set_op(row, column, datum, SET_OP_DELETE); |
4357 | 0 | } |
4358 | | |
4359 | | /* Inserts the key-value specified in 'datum' into the map in 'column' in |
4360 | | * 'row_'. If the key already exist in 'column', then it's value is updated |
4361 | | * with the value in 'datum'. The key-value in 'datum' must be of the same type |
4362 | | * as the keys-values in 'column'. This function takes ownership of 'datum'. |
4363 | | * |
4364 | | * Usually this function is used indirectly through one of the "update" |
4365 | | * functions generated by vswitch-idl. */ |
4366 | | void |
4367 | | ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row *row_, |
4368 | | const struct ovsdb_idl_column *column, |
4369 | | struct ovsdb_datum *datum) |
4370 | 0 | { |
4371 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
4372 | 0 | enum ovsdb_atomic_type key_type; |
4373 | 0 | enum map_op_type op_type; |
4374 | 0 | const struct ovsdb_datum *old_datum; |
4375 | |
|
4376 | 0 | if (!is_valid_partial_update(row, column, datum)) { |
4377 | 0 | ovsdb_datum_destroy(datum, &column->type); |
4378 | 0 | free(datum); |
4379 | 0 | return; |
4380 | 0 | } |
4381 | | |
4382 | | /* Find out if this is an insert or an update. */ |
4383 | 0 | key_type = column->type.key.type; |
4384 | 0 | old_datum = ovsdb_idl_read(row, column); |
4385 | 0 | if (ovsdb_datum_find_key(old_datum, &datum->keys[0], key_type, NULL)) { |
4386 | 0 | op_type = MAP_OP_UPDATE; |
4387 | 0 | } else { |
4388 | 0 | op_type = MAP_OP_INSERT; |
4389 | 0 | } |
4390 | |
|
4391 | 0 | ovsdb_idl_txn_add_map_op(row, column, datum, op_type); |
4392 | 0 | } |
4393 | | |
4394 | | /* Deletes the key specified in 'datum' from the map in 'column' in 'row_'. |
4395 | | * The key in 'datum' must be of the same type as the keys in 'column'. |
4396 | | * The value in 'datum' must be NULL. This function takes ownership of |
4397 | | * 'datum'. |
4398 | | * |
4399 | | * Usually this function is used indirectly through one of the "update" |
4400 | | * functions generated by vswitch-idl. */ |
4401 | | void |
4402 | | ovsdb_idl_txn_delete_partial_map(const struct ovsdb_idl_row *row_, |
4403 | | const struct ovsdb_idl_column *column, |
4404 | | struct ovsdb_datum *datum) |
4405 | 0 | { |
4406 | 0 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
4407 | |
|
4408 | 0 | if (!is_valid_partial_update(row, column, datum)) { |
4409 | 0 | struct ovsdb_type type_ = column->type; |
4410 | 0 | type_.value.type = OVSDB_TYPE_VOID; |
4411 | 0 | ovsdb_datum_destroy(datum, &type_); |
4412 | 0 | free(datum); |
4413 | 0 | return; |
4414 | 0 | } |
4415 | 0 | ovsdb_idl_txn_add_map_op(row, column, datum, MAP_OP_DELETE); |
4416 | 0 | } |
4417 | | |
4418 | | void |
4419 | | ovsdb_idl_loop_destroy(struct ovsdb_idl_loop *loop) |
4420 | 0 | { |
4421 | 0 | if (loop) { |
4422 | 0 | if (loop->committing_txn) { |
4423 | 0 | ovsdb_idl_txn_destroy(loop->committing_txn); |
4424 | 0 | } |
4425 | 0 | ovsdb_idl_destroy(loop->idl); |
4426 | 0 | } |
4427 | 0 | } |
4428 | | |
4429 | | struct ovsdb_idl_txn * |
4430 | | ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) |
4431 | 0 | { |
4432 | 0 | ovsdb_idl_run(loop->idl); |
4433 | | |
4434 | | /* See if the 'committing_txn' succeeded in the meantime. */ |
4435 | 0 | if (loop->committing_txn && loop->committing_txn->status == TXN_SUCCESS) { |
4436 | 0 | ovsdb_idl_try_commit_loop_txn(loop, NULL); |
4437 | 0 | } |
4438 | |
|
4439 | 0 | loop->open_txn = (loop->committing_txn |
4440 | 0 | || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno |
4441 | 0 | ? NULL |
4442 | 0 | : ovsdb_idl_txn_create(loop->idl)); |
4443 | 0 | if (loop->open_txn) { |
4444 | 0 | ovsdb_idl_txn_add_comment(loop->open_txn, "%s", program_name); |
4445 | 0 | } |
4446 | 0 | return loop->open_txn; |
4447 | 0 | } |
4448 | | |
4449 | | /* Attempts to commit the current transaction, if one is open. |
4450 | | * |
4451 | | * If a transaction was open, in this or a previous iteration of the main loop, |
4452 | | * and had not before finished committing (successfully or unsuccessfully), the |
4453 | | * return value is one of: |
4454 | | * |
4455 | | * 1: The transaction committed successfully (or it did not change anything in |
4456 | | * the database). |
4457 | | * 0: The transaction failed. |
4458 | | * -1: The commit is still in progress. |
4459 | | * |
4460 | | * Thus, the return value is -1 if the transaction is in progress and otherwise |
4461 | | * true for success, false for failure. |
4462 | | * |
4463 | | * (In the corner case where the IDL sends a transaction to the database and |
4464 | | * the database commits it, and the connection between the IDL and the database |
4465 | | * drops before the IDL receives the message confirming the commit, this |
4466 | | * function can return 0 even though the transaction succeeded.) |
4467 | | */ |
4468 | | static int |
4469 | | ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop, |
4470 | | bool *may_need_wakeup) |
4471 | 0 | { |
4472 | 0 | if (!loop->committing_txn) { |
4473 | | /* Not a meaningful return value: no transaction was in progress. */ |
4474 | 0 | return 1; |
4475 | 0 | } |
4476 | | |
4477 | 0 | int retval; |
4478 | 0 | struct ovsdb_idl_txn *txn = loop->committing_txn; |
4479 | |
|
4480 | 0 | enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); |
4481 | 0 | if (status != TXN_INCOMPLETE) { |
4482 | 0 | switch (status) { |
4483 | 0 | case TXN_TRY_AGAIN: |
4484 | | /* We want to re-evaluate the database when it's changed from |
4485 | | * the contents that it had when we started the commit. (That |
4486 | | * might have already happened.) */ |
4487 | 0 | loop->skip_seqno = loop->precommit_seqno; |
4488 | 0 | if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno |
4489 | 0 | && may_need_wakeup) { |
4490 | 0 | *may_need_wakeup = true; |
4491 | 0 | } |
4492 | 0 | retval = 0; |
4493 | 0 | break; |
4494 | | |
4495 | 0 | case TXN_SUCCESS: |
4496 | | /* Possibly some work on the database was deferred because no |
4497 | | * further transaction could proceed. Wake up again. */ |
4498 | 0 | retval = 1; |
4499 | 0 | loop->cur_cfg = loop->next_cfg; |
4500 | 0 | if (may_need_wakeup) { |
4501 | 0 | *may_need_wakeup = true; |
4502 | 0 | } |
4503 | 0 | break; |
4504 | | |
4505 | 0 | case TXN_UNCHANGED: |
4506 | 0 | retval = 1; |
4507 | 0 | loop->cur_cfg = loop->next_cfg; |
4508 | 0 | break; |
4509 | | |
4510 | 0 | case TXN_ABORTED: |
4511 | 0 | case TXN_NOT_LOCKED: |
4512 | 0 | case TXN_ERROR: |
4513 | 0 | retval = 0; |
4514 | 0 | break; |
4515 | | |
4516 | 0 | case TXN_UNCOMMITTED: |
4517 | 0 | case TXN_INCOMPLETE: |
4518 | 0 | default: |
4519 | 0 | OVS_NOT_REACHED(); |
4520 | 0 | } |
4521 | 0 | ovsdb_idl_txn_destroy(txn); |
4522 | 0 | loop->committing_txn = NULL; |
4523 | 0 | } else { |
4524 | 0 | retval = -1; |
4525 | 0 | } |
4526 | | |
4527 | 0 | return retval; |
4528 | 0 | } |
4529 | | |
4530 | | /* Attempts to commit the current transaction, if one is open, and sets up the |
4531 | | * poll loop to wake up when some more work might be needed. |
4532 | | * |
4533 | | * If a transaction was open, in this or a previous iteration of the main loop, |
4534 | | * and had not before finished committing (successfully or unsuccessfully), the |
4535 | | * return value is one of: |
4536 | | * |
4537 | | * 1: The transaction committed successfully (or it did not change anything in |
4538 | | * the database). |
4539 | | * 0: The transaction failed. |
4540 | | * -1: The commit is still in progress. |
4541 | | * |
4542 | | * Thus, the return value is -1 if the transaction is in progress and otherwise |
4543 | | * true for success, false for failure. |
4544 | | * |
4545 | | * (In the corner case where the IDL sends a transaction to the database and |
4546 | | * the database commits it, and the connection between the IDL and the database |
4547 | | * drops before the IDL receives the message confirming the commit, this |
4548 | | * function can return 0 even though the transaction succeeded.) |
4549 | | */ |
4550 | | int |
4551 | | ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) |
4552 | 0 | { |
4553 | 0 | if (loop->open_txn) { |
4554 | 0 | loop->committing_txn = loop->open_txn; |
4555 | 0 | loop->open_txn = NULL; |
4556 | |
|
4557 | 0 | loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl); |
4558 | 0 | } |
4559 | |
|
4560 | 0 | bool may_need_wakeup = false; |
4561 | 0 | int retval = ovsdb_idl_try_commit_loop_txn(loop, &may_need_wakeup); |
4562 | 0 | if (may_need_wakeup) { |
4563 | 0 | poll_immediate_wake(); |
4564 | 0 | } |
4565 | 0 | ovsdb_idl_wait(loop->idl); |
4566 | |
|
4567 | 0 | return retval; |
4568 | 0 | } |