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