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