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