]>
Commit | Line | Data |
---|---|---|
2fa1df7b AZ |
1 | /* |
2 | * Copyright (c) 2015 Nicira, Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | ||
19 | #include <errno.h> | |
20 | ||
21 | #include "bitmap.h" | |
22 | #include "column.h" | |
23 | #include "dynamic-string.h" | |
24 | #include "json.h" | |
25 | #include "jsonrpc.h" | |
26 | #include "ovsdb-error.h" | |
27 | #include "ovsdb-parser.h" | |
28 | #include "ovsdb.h" | |
29 | #include "row.h" | |
30 | #include "simap.h" | |
7e911055 | 31 | #include "hash.h" |
2fa1df7b | 32 | #include "table.h" |
6e5a9216 | 33 | #include "hash.h" |
2fa1df7b AZ |
34 | #include "timeval.h" |
35 | #include "transaction.h" | |
36 | #include "jsonrpc-server.h" | |
37 | #include "monitor.h" | |
38 | #include "openvswitch/vlog.h" | |
39 | ||
40 | ||
41 | static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class; | |
6e5a9216 | 42 | static struct hmap ovsdb_monitors = HMAP_INITIALIZER(&ovsdb_monitors); |
2fa1df7b AZ |
43 | |
44 | /* Backend monitor. | |
45 | * | |
46 | * ovsdb_monitor keep track of the ovsdb changes. | |
47 | */ | |
48 | ||
49 | /* A collection of tables being monitored. */ | |
50 | struct ovsdb_monitor { | |
51 | struct ovsdb_replica replica; | |
52 | struct shash tables; /* Holds "struct ovsdb_monitor_table"s. */ | |
d9412837 | 53 | struct ovs_list jsonrpc_monitors; /* Contains "jsonrpc_monitor_node"s. */ |
2fa1df7b | 54 | struct ovsdb *db; |
59c35e11 | 55 | uint64_t n_transactions; /* Count number of committed transactions. */ |
6e5a9216 | 56 | struct hmap_node hmap_node; /* Elements within ovsdb_monitors. */ |
4c280978 AZ |
57 | struct hmap json_cache; /* Contains "ovsdb_monitor_json_cache_node"s.*/ |
58 | }; | |
59 | ||
60 | /* A json object of updates between 'from_txn' and 'dbmon->n_transactions' | |
61 | * inclusive. */ | |
62 | struct ovsdb_monitor_json_cache_node { | |
63 | struct hmap_node hmap_node; /* Elements in json cache. */ | |
64 | uint64_t from_txn; | |
65 | struct json *json; /* Null, or a cloned of json */ | |
2fa1df7b AZ |
66 | }; |
67 | ||
d9412837 AZ |
68 | struct jsonrpc_monitor_node { |
69 | struct ovsdb_jsonrpc_monitor *jsonrpc_monitor; | |
70 | struct ovs_list node; | |
71 | }; | |
72 | ||
2fa1df7b AZ |
73 | /* A particular column being monitored. */ |
74 | struct ovsdb_monitor_column { | |
75 | const struct ovsdb_column *column; | |
76 | enum ovsdb_monitor_selection select; | |
77 | }; | |
78 | ||
79 | /* A row that has changed in a monitored table. */ | |
80 | struct ovsdb_monitor_row { | |
81 | struct hmap_node hmap_node; /* In ovsdb_jsonrpc_monitor_table.changes. */ | |
82 | struct uuid uuid; /* UUID of row that changed. */ | |
83 | struct ovsdb_datum *old; /* Old data, NULL for an inserted row. */ | |
84 | struct ovsdb_datum *new; /* New data, NULL for a deleted row. */ | |
85 | }; | |
86 | ||
1158f320 AZ |
87 | /* Contains 'struct ovsdb_monitor_row's for rows that have been |
88 | * updated but not yet flushed to all the jsonrpc connection. | |
89 | * | |
90 | * 'n_refs' represent the number of jsonrpc connections that have | |
91 | * not received updates. Generate the update for the last jsonprc | |
92 | * connection will also destroy the whole "struct ovsdb_monitor_changes" | |
93 | * object. | |
94 | * | |
95 | * 'transaction' stores the first update's transaction id. | |
96 | * */ | |
97 | struct ovsdb_monitor_changes { | |
98 | struct ovsdb_monitor_table *mt; | |
99 | struct hmap rows; | |
100 | int n_refs; | |
101 | uint64_t transaction; | |
7e911055 AZ |
102 | struct hmap_node hmap_node; /* Element in ovsdb_monitor_tables' changes |
103 | hmap. */ | |
1158f320 AZ |
104 | }; |
105 | ||
2fa1df7b AZ |
106 | /* A particular table being monitored. */ |
107 | struct ovsdb_monitor_table { | |
108 | const struct ovsdb_table *table; | |
109 | ||
110 | /* This is the union (bitwise-OR) of the 'select' values in all of the | |
111 | * members of 'columns' below. */ | |
112 | enum ovsdb_monitor_selection select; | |
113 | ||
114 | /* Columns being monitored. */ | |
115 | struct ovsdb_monitor_column *columns; | |
116 | size_t n_columns; | |
117 | ||
7e911055 AZ |
118 | /* Contains 'ovsdb_monitor_changes' indexed by 'transaction'. */ |
119 | struct hmap changes; | |
2fa1df7b AZ |
120 | }; |
121 | ||
d9412837 | 122 | static void ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon); |
6e5a9216 AZ |
123 | static struct ovsdb_monitor_changes * ovsdb_monitor_table_add_changes( |
124 | struct ovsdb_monitor_table *mt, uint64_t next_txn); | |
7e911055 AZ |
125 | static struct ovsdb_monitor_changes *ovsdb_monitor_table_find_changes( |
126 | struct ovsdb_monitor_table *mt, uint64_t unflushed); | |
1158f320 AZ |
127 | static void ovsdb_monitor_changes_destroy( |
128 | struct ovsdb_monitor_changes *changes); | |
129 | static void ovsdb_monitor_table_track_changes(struct ovsdb_monitor_table *mt, | |
7e911055 | 130 | uint64_t unflushed); |
d9412837 | 131 | |
4c280978 AZ |
132 | static struct ovsdb_monitor_json_cache_node * |
133 | ovsdb_monitor_json_cache_search(const struct ovsdb_monitor *dbmon, | |
134 | uint64_t from_txn) | |
135 | { | |
136 | struct ovsdb_monitor_json_cache_node *node; | |
137 | uint32_t hash = hash_uint64(from_txn); | |
138 | ||
139 | HMAP_FOR_EACH_WITH_HASH(node, hmap_node, hash, &dbmon->json_cache) { | |
140 | if (node->from_txn == from_txn) { | |
141 | return node; | |
142 | } | |
143 | } | |
144 | ||
145 | return NULL; | |
146 | } | |
147 | ||
148 | static void | |
149 | ovsdb_monitor_json_cache_insert(struct ovsdb_monitor *dbmon, | |
150 | uint64_t from_txn, struct json *json) | |
151 | { | |
152 | struct ovsdb_monitor_json_cache_node *node; | |
153 | uint32_t hash; | |
154 | ||
155 | node = xmalloc(sizeof *node); | |
156 | ||
157 | hash = hash_uint64(from_txn); | |
158 | node->from_txn = from_txn; | |
159 | node->json = json ? json_clone(json) : NULL; | |
160 | ||
161 | hmap_insert(&dbmon->json_cache, &node->hmap_node, hash); | |
162 | } | |
163 | ||
164 | static void | |
165 | ovsdb_monitor_json_cache_flush(struct ovsdb_monitor *dbmon) | |
166 | { | |
167 | struct ovsdb_monitor_json_cache_node *node, *next; | |
168 | ||
169 | HMAP_FOR_EACH_SAFE(node, next, hmap_node, &dbmon->json_cache) { | |
170 | hmap_remove(&dbmon->json_cache, &node->hmap_node); | |
171 | json_destroy(node->json); | |
172 | free(node); | |
173 | } | |
174 | } | |
175 | ||
2fa1df7b AZ |
176 | static int |
177 | compare_ovsdb_monitor_column(const void *a_, const void *b_) | |
178 | { | |
179 | const struct ovsdb_monitor_column *a = a_; | |
180 | const struct ovsdb_monitor_column *b = b_; | |
181 | ||
182 | return a->column < b->column ? -1 : a->column > b->column; | |
183 | } | |
184 | ||
185 | static struct ovsdb_monitor * | |
186 | ovsdb_monitor_cast(struct ovsdb_replica *replica) | |
187 | { | |
188 | ovs_assert(replica->class == &ovsdb_jsonrpc_replica_class); | |
189 | return CONTAINER_OF(replica, struct ovsdb_monitor, replica); | |
190 | } | |
191 | ||
1158f320 | 192 | /* Finds and returns the ovsdb_monitor_row in 'mt->changes->rows' for the |
2fa1df7b AZ |
193 | * given 'uuid', or NULL if there is no such row. */ |
194 | static struct ovsdb_monitor_row * | |
7e911055 AZ |
195 | ovsdb_monitor_changes_row_find(const struct ovsdb_monitor_changes *changes, |
196 | const struct uuid *uuid) | |
2fa1df7b AZ |
197 | { |
198 | struct ovsdb_monitor_row *row; | |
199 | ||
1158f320 | 200 | HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), |
7e911055 | 201 | &changes->rows) { |
2fa1df7b AZ |
202 | if (uuid_equals(uuid, &row->uuid)) { |
203 | return row; | |
204 | } | |
205 | } | |
206 | return NULL; | |
207 | } | |
208 | ||
209 | /* Allocates an array of 'mt->n_columns' ovsdb_datums and initializes them as | |
210 | * copies of the data in 'row' drawn from the columns represented by | |
211 | * mt->columns[]. Returns the array. | |
212 | * | |
213 | * If 'row' is NULL, returns NULL. */ | |
214 | static struct ovsdb_datum * | |
215 | clone_monitor_row_data(const struct ovsdb_monitor_table *mt, | |
216 | const struct ovsdb_row *row) | |
217 | { | |
218 | struct ovsdb_datum *data; | |
219 | size_t i; | |
220 | ||
221 | if (!row) { | |
222 | return NULL; | |
223 | } | |
224 | ||
225 | data = xmalloc(mt->n_columns * sizeof *data); | |
226 | for (i = 0; i < mt->n_columns; i++) { | |
227 | const struct ovsdb_column *c = mt->columns[i].column; | |
228 | const struct ovsdb_datum *src = &row->fields[c->index]; | |
229 | struct ovsdb_datum *dst = &data[i]; | |
230 | const struct ovsdb_type *type = &c->type; | |
231 | ||
232 | ovsdb_datum_clone(dst, src, type); | |
233 | } | |
234 | return data; | |
235 | } | |
236 | ||
237 | /* Replaces the mt->n_columns ovsdb_datums in row[] by copies of the data from | |
238 | * in 'row' drawn from the columns represented by mt->columns[]. */ | |
239 | static void | |
240 | update_monitor_row_data(const struct ovsdb_monitor_table *mt, | |
241 | const struct ovsdb_row *row, | |
242 | struct ovsdb_datum *data) | |
243 | { | |
244 | size_t i; | |
245 | ||
246 | for (i = 0; i < mt->n_columns; i++) { | |
247 | const struct ovsdb_column *c = mt->columns[i].column; | |
248 | const struct ovsdb_datum *src = &row->fields[c->index]; | |
249 | struct ovsdb_datum *dst = &data[i]; | |
250 | const struct ovsdb_type *type = &c->type; | |
251 | ||
252 | if (!ovsdb_datum_equals(src, dst, type)) { | |
253 | ovsdb_datum_destroy(dst, type); | |
254 | ovsdb_datum_clone(dst, src, type); | |
255 | } | |
256 | } | |
257 | } | |
258 | ||
259 | /* Frees all of the mt->n_columns ovsdb_datums in data[], using the types taken | |
260 | * from mt->columns[], plus 'data' itself. */ | |
261 | static void | |
262 | free_monitor_row_data(const struct ovsdb_monitor_table *mt, | |
263 | struct ovsdb_datum *data) | |
264 | { | |
265 | if (data) { | |
266 | size_t i; | |
267 | ||
268 | for (i = 0; i < mt->n_columns; i++) { | |
269 | const struct ovsdb_column *c = mt->columns[i].column; | |
270 | ||
271 | ovsdb_datum_destroy(&data[i], &c->type); | |
272 | } | |
273 | free(data); | |
274 | } | |
275 | } | |
276 | ||
277 | /* Frees 'row', which must have been created from 'mt'. */ | |
278 | static void | |
279 | ovsdb_monitor_row_destroy(const struct ovsdb_monitor_table *mt, | |
280 | struct ovsdb_monitor_row *row) | |
281 | { | |
282 | if (row) { | |
283 | free_monitor_row_data(mt, row->old); | |
284 | free_monitor_row_data(mt, row->new); | |
285 | free(row); | |
286 | } | |
287 | } | |
288 | ||
6e5a9216 | 289 | void |
36247a75 AZ |
290 | ovsdb_monitor_add_jsonrpc_monitor(struct ovsdb_monitor *dbmon, |
291 | struct ovsdb_jsonrpc_monitor *jsonrpc_monitor) | |
292 | { | |
293 | struct jsonrpc_monitor_node *jm; | |
294 | ||
295 | jm = xzalloc(sizeof *jm); | |
296 | jm->jsonrpc_monitor = jsonrpc_monitor; | |
297 | list_push_back(&dbmon->jsonrpc_monitors, &jm->node); | |
298 | } | |
299 | ||
2fa1df7b AZ |
300 | struct ovsdb_monitor * |
301 | ovsdb_monitor_create(struct ovsdb *db, | |
302 | struct ovsdb_jsonrpc_monitor *jsonrpc_monitor) | |
303 | { | |
304 | struct ovsdb_monitor *dbmon; | |
305 | ||
306 | dbmon = xzalloc(sizeof *dbmon); | |
307 | ||
308 | ovsdb_replica_init(&dbmon->replica, &ovsdb_jsonrpc_replica_class); | |
309 | ovsdb_add_replica(db, &dbmon->replica); | |
d9412837 | 310 | list_init(&dbmon->jsonrpc_monitors); |
2fa1df7b | 311 | dbmon->db = db; |
59c35e11 | 312 | dbmon->n_transactions = 0; |
2fa1df7b | 313 | shash_init(&dbmon->tables); |
6e5a9216 | 314 | hmap_node_nullify(&dbmon->hmap_node); |
4c280978 | 315 | hmap_init(&dbmon->json_cache); |
2fa1df7b | 316 | |
36247a75 | 317 | ovsdb_monitor_add_jsonrpc_monitor(dbmon, jsonrpc_monitor); |
2fa1df7b AZ |
318 | return dbmon; |
319 | } | |
320 | ||
321 | void | |
322 | ovsdb_monitor_add_table(struct ovsdb_monitor *m, | |
323 | const struct ovsdb_table *table) | |
324 | { | |
325 | struct ovsdb_monitor_table *mt; | |
326 | ||
327 | mt = xzalloc(sizeof *mt); | |
328 | mt->table = table; | |
2fa1df7b | 329 | shash_add(&m->tables, table->schema->name, mt); |
7e911055 | 330 | hmap_init(&mt->changes); |
2fa1df7b AZ |
331 | } |
332 | ||
333 | void | |
334 | ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon, | |
335 | const struct ovsdb_table *table, | |
336 | const struct ovsdb_column *column, | |
337 | enum ovsdb_monitor_selection select, | |
338 | size_t *allocated_columns) | |
339 | { | |
340 | struct ovsdb_monitor_table *mt; | |
341 | struct ovsdb_monitor_column *c; | |
342 | ||
343 | mt = shash_find_data(&dbmon->tables, table->schema->name); | |
344 | ||
345 | if (mt->n_columns >= *allocated_columns) { | |
346 | mt->columns = x2nrealloc(mt->columns, allocated_columns, | |
347 | sizeof *mt->columns); | |
348 | } | |
349 | ||
350 | mt->select |= select; | |
351 | c = &mt->columns[mt->n_columns++]; | |
352 | c->column = column; | |
353 | c->select = select; | |
354 | } | |
355 | ||
356 | /* Check for duplicated column names. Return the first | |
357 | * duplicated column's name if found. Otherwise return | |
358 | * NULL. */ | |
359 | const char * OVS_WARN_UNUSED_RESULT | |
360 | ovsdb_monitor_table_check_duplicates(struct ovsdb_monitor *m, | |
361 | const struct ovsdb_table *table) | |
362 | { | |
363 | struct ovsdb_monitor_table *mt; | |
364 | int i; | |
365 | ||
366 | mt = shash_find_data(&m->tables, table->schema->name); | |
367 | ||
368 | if (mt) { | |
369 | /* Check for duplicate columns. */ | |
370 | qsort(mt->columns, mt->n_columns, sizeof *mt->columns, | |
371 | compare_ovsdb_monitor_column); | |
372 | for (i = 1; i < mt->n_columns; i++) { | |
373 | if (mt->columns[i].column == mt->columns[i - 1].column) { | |
374 | return mt->columns[i].column->name; | |
375 | } | |
376 | } | |
377 | } | |
378 | ||
379 | return NULL; | |
380 | } | |
381 | ||
6e5a9216 | 382 | static struct ovsdb_monitor_changes * |
1158f320 AZ |
383 | ovsdb_monitor_table_add_changes(struct ovsdb_monitor_table *mt, |
384 | uint64_t next_txn) | |
385 | { | |
386 | struct ovsdb_monitor_changes *changes; | |
387 | ||
388 | changes = xzalloc(sizeof *changes); | |
389 | ||
390 | changes->transaction = next_txn; | |
391 | changes->mt = mt; | |
392 | changes->n_refs = 1; | |
393 | hmap_init(&changes->rows); | |
7e911055 | 394 | hmap_insert(&mt->changes, &changes->hmap_node, hash_uint64(next_txn)); |
6e5a9216 AZ |
395 | |
396 | return changes; | |
7e911055 AZ |
397 | }; |
398 | ||
399 | static struct ovsdb_monitor_changes * | |
400 | ovsdb_monitor_table_find_changes(struct ovsdb_monitor_table *mt, | |
401 | uint64_t transaction) | |
402 | { | |
403 | struct ovsdb_monitor_changes *changes; | |
404 | size_t hash = hash_uint64(transaction); | |
405 | ||
406 | HMAP_FOR_EACH_WITH_HASH(changes, hmap_node, hash, &mt->changes) { | |
407 | if (changes->transaction == transaction) { | |
408 | return changes; | |
409 | } | |
410 | } | |
411 | ||
412 | return NULL; | |
1158f320 AZ |
413 | } |
414 | ||
415 | /* Stop currently tracking changes to table 'mt' since 'transaction'. | |
416 | * | |
417 | * Return 'true' if the 'transaction' is being tracked. 'false' otherwise. */ | |
418 | static void | |
419 | ovsdb_monitor_table_untrack_changes(struct ovsdb_monitor_table *mt, | |
420 | uint64_t transaction) | |
421 | { | |
7e911055 AZ |
422 | struct ovsdb_monitor_changes *changes = |
423 | ovsdb_monitor_table_find_changes(mt, transaction); | |
1158f320 | 424 | if (changes) { |
1158f320 | 425 | if (--changes->n_refs == 0) { |
7e911055 | 426 | hmap_remove(&mt->changes, &changes->hmap_node); |
1158f320 | 427 | ovsdb_monitor_changes_destroy(changes); |
1158f320 AZ |
428 | } |
429 | } | |
430 | } | |
431 | ||
432 | /* Start tracking changes to table 'mt' begins from 'transaction' inclusive. | |
433 | */ | |
434 | static void | |
435 | ovsdb_monitor_table_track_changes(struct ovsdb_monitor_table *mt, | |
436 | uint64_t transaction) | |
437 | { | |
7e911055 AZ |
438 | struct ovsdb_monitor_changes *changes; |
439 | ||
440 | changes = ovsdb_monitor_table_find_changes(mt, transaction); | |
441 | if (changes) { | |
442 | changes->n_refs++; | |
443 | } else { | |
444 | ovsdb_monitor_table_add_changes(mt, transaction); | |
445 | } | |
1158f320 AZ |
446 | } |
447 | ||
448 | static void | |
449 | ovsdb_monitor_changes_destroy(struct ovsdb_monitor_changes *changes) | |
450 | { | |
451 | struct ovsdb_monitor_row *row, *next; | |
452 | ||
453 | HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) { | |
454 | hmap_remove(&changes->rows, &row->hmap_node); | |
455 | ovsdb_monitor_row_destroy(changes->mt, row); | |
456 | } | |
457 | hmap_destroy(&changes->rows); | |
458 | free(changes); | |
459 | } | |
460 | ||
23cbedb7 AZ |
461 | static enum ovsdb_monitor_selection |
462 | ovsdb_monitor_row_update_type(bool initial, const bool old, const bool new) | |
463 | { | |
464 | return initial ? OJMS_INITIAL | |
465 | : !old ? OJMS_INSERT | |
466 | : !new ? OJMS_DELETE | |
467 | : OJMS_MODIFY; | |
468 | } | |
469 | ||
2fa1df7b AZ |
470 | /* Returns JSON for a <row-update> (as described in RFC 7047) for 'row' within |
471 | * 'mt', or NULL if no row update should be sent. | |
472 | * | |
473 | * The caller should specify 'initial' as true if the returned JSON is going to | |
474 | * be used as part of the initial reply to a "monitor" request, false if it is | |
475 | * going to be used as part of an "update" notification. | |
476 | * | |
477 | * 'changed' must be a scratch buffer for internal use that is at least | |
478 | * bitmap_n_bytes(mt->n_columns) bytes long. */ | |
479 | static struct json * | |
480 | ovsdb_monitor_compose_row_update( | |
481 | const struct ovsdb_monitor_table *mt, | |
482 | const struct ovsdb_monitor_row *row, | |
483 | bool initial, unsigned long int *changed) | |
484 | { | |
485 | enum ovsdb_monitor_selection type; | |
486 | struct json *old_json, *new_json; | |
487 | struct json *row_json; | |
488 | size_t i; | |
489 | ||
23cbedb7 | 490 | type = ovsdb_monitor_row_update_type(initial, row->old, row->new); |
2fa1df7b AZ |
491 | if (!(mt->select & type)) { |
492 | return NULL; | |
493 | } | |
494 | ||
495 | if (type == OJMS_MODIFY) { | |
496 | size_t n_changes; | |
497 | ||
498 | n_changes = 0; | |
499 | memset(changed, 0, bitmap_n_bytes(mt->n_columns)); | |
500 | for (i = 0; i < mt->n_columns; i++) { | |
501 | const struct ovsdb_column *c = mt->columns[i].column; | |
502 | if (!ovsdb_datum_equals(&row->old[i], &row->new[i], &c->type)) { | |
503 | bitmap_set1(changed, i); | |
504 | n_changes++; | |
505 | } | |
506 | } | |
507 | if (!n_changes) { | |
508 | /* No actual changes: presumably a row changed and then | |
509 | * changed back later. */ | |
510 | return NULL; | |
511 | } | |
512 | } | |
513 | ||
514 | row_json = json_object_create(); | |
515 | old_json = new_json = NULL; | |
516 | if (type & (OJMS_DELETE | OJMS_MODIFY)) { | |
517 | old_json = json_object_create(); | |
518 | json_object_put(row_json, "old", old_json); | |
519 | } | |
520 | if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { | |
521 | new_json = json_object_create(); | |
522 | json_object_put(row_json, "new", new_json); | |
523 | } | |
524 | for (i = 0; i < mt->n_columns; i++) { | |
525 | const struct ovsdb_monitor_column *c = &mt->columns[i]; | |
526 | ||
527 | if (!(type & c->select)) { | |
528 | /* We don't care about this type of change for this | |
529 | * particular column (but we will care about it for some | |
530 | * other column). */ | |
531 | continue; | |
532 | } | |
533 | ||
534 | if ((type == OJMS_MODIFY && bitmap_is_set(changed, i)) | |
535 | || type == OJMS_DELETE) { | |
536 | json_object_put(old_json, c->column->name, | |
537 | ovsdb_datum_to_json(&row->old[i], | |
538 | &c->column->type)); | |
539 | } | |
540 | if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { | |
541 | json_object_put(new_json, c->column->name, | |
542 | ovsdb_datum_to_json(&row->new[i], | |
543 | &c->column->type)); | |
544 | } | |
545 | } | |
546 | ||
547 | return row_json; | |
548 | } | |
549 | ||
550 | /* Constructs and returns JSON for a <table-updates> object (as described in | |
4c280978 AZ |
551 | * RFC 7047) for all the outstanding changes within 'monitor', starting from |
552 | * 'transaction'. */ | |
553 | static struct json* | |
554 | ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon, | |
555 | bool initial, uint64_t transaction) | |
2fa1df7b AZ |
556 | { |
557 | struct shash_node *node; | |
558 | unsigned long int *changed; | |
559 | struct json *json; | |
560 | size_t max_columns; | |
59c35e11 | 561 | |
2fa1df7b AZ |
562 | max_columns = 0; |
563 | SHASH_FOR_EACH (node, &dbmon->tables) { | |
564 | struct ovsdb_monitor_table *mt = node->data; | |
565 | ||
566 | max_columns = MAX(max_columns, mt->n_columns); | |
567 | } | |
568 | changed = xmalloc(bitmap_n_bytes(max_columns)); | |
569 | ||
570 | json = NULL; | |
571 | SHASH_FOR_EACH (node, &dbmon->tables) { | |
572 | struct ovsdb_monitor_table *mt = node->data; | |
573 | struct ovsdb_monitor_row *row, *next; | |
7e911055 | 574 | struct ovsdb_monitor_changes *changes; |
2fa1df7b AZ |
575 | struct json *table_json = NULL; |
576 | ||
4c280978 | 577 | changes = ovsdb_monitor_table_find_changes(mt, transaction); |
7e911055 | 578 | if (!changes) { |
1158f320 AZ |
579 | continue; |
580 | } | |
581 | ||
7e911055 | 582 | HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) { |
2fa1df7b AZ |
583 | struct json *row_json; |
584 | ||
585 | row_json = ovsdb_monitor_compose_row_update( | |
586 | mt, row, initial, changed); | |
587 | if (row_json) { | |
588 | char uuid[UUID_LEN + 1]; | |
589 | ||
590 | /* Create JSON object for transaction overall. */ | |
591 | if (!json) { | |
592 | json = json_object_create(); | |
593 | } | |
594 | ||
595 | /* Create JSON object for transaction on this table. */ | |
596 | if (!table_json) { | |
597 | table_json = json_object_create(); | |
598 | json_object_put(json, mt->table->schema->name, table_json); | |
599 | } | |
600 | ||
601 | /* Add JSON row to JSON table. */ | |
602 | snprintf(uuid, sizeof uuid, UUID_FMT, UUID_ARGS(&row->uuid)); | |
603 | json_object_put(table_json, uuid, row_json); | |
604 | } | |
2fa1df7b | 605 | } |
4c280978 AZ |
606 | } |
607 | free(changed); | |
608 | ||
609 | return json; | |
610 | } | |
611 | ||
612 | /* Returns JSON for a <table-updates> object (as described in RFC 7047) | |
613 | * for all the outstanding changes within 'monitor' that starts from | |
614 | * '*unflushed' transaction id. | |
615 | * | |
616 | * The caller should specify 'initial' as true if the returned JSON is going to | |
617 | * be used as part of the initial reply to a "monitor" request, false if it is | |
618 | * going to be used as part of an "update" notification. */ | |
619 | struct json * | |
620 | ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon, | |
621 | bool initial, uint64_t *unflushed) | |
622 | { | |
623 | struct ovsdb_monitor_json_cache_node *cache_node; | |
624 | struct shash_node *node; | |
625 | struct json *json; | |
626 | uint64_t prev_txn = *unflushed; | |
627 | uint64_t next_txn = dbmon->n_transactions + 1; | |
628 | ||
629 | /* Return a clone of cached json if one exists. Otherwise, | |
630 | * generate a new one and add it to the cache. */ | |
631 | cache_node = ovsdb_monitor_json_cache_search(dbmon, prev_txn); | |
632 | if (cache_node) { | |
633 | json = cache_node->json ? json_clone(cache_node->json) : NULL; | |
634 | } else { | |
635 | json = ovsdb_monitor_compose_update(dbmon, initial, prev_txn); | |
636 | ovsdb_monitor_json_cache_insert(dbmon, prev_txn, json); | |
637 | } | |
638 | ||
639 | /* Maintain transaction id of 'changes'. */ | |
640 | SHASH_FOR_EACH (node, &dbmon->tables) { | |
641 | struct ovsdb_monitor_table *mt = node->data; | |
1158f320 AZ |
642 | |
643 | ovsdb_monitor_table_untrack_changes(mt, prev_txn); | |
644 | ovsdb_monitor_table_track_changes(mt, next_txn); | |
2fa1df7b | 645 | } |
1158f320 | 646 | *unflushed = next_txn; |
4c280978 | 647 | |
2fa1df7b AZ |
648 | return json; |
649 | } | |
650 | ||
651 | bool | |
59c35e11 AZ |
652 | ovsdb_monitor_needs_flush(struct ovsdb_monitor *dbmon, |
653 | uint64_t next_transaction) | |
2fa1df7b | 654 | { |
59c35e11 AZ |
655 | ovs_assert(next_transaction <= dbmon->n_transactions + 1); |
656 | return (next_transaction <= dbmon->n_transactions); | |
2fa1df7b AZ |
657 | } |
658 | ||
659 | void | |
660 | ovsdb_monitor_table_add_select(struct ovsdb_monitor *dbmon, | |
661 | const struct ovsdb_table *table, | |
662 | enum ovsdb_monitor_selection select) | |
663 | { | |
664 | struct ovsdb_monitor_table * mt; | |
665 | ||
666 | mt = shash_find_data(&dbmon->tables, table->schema->name); | |
667 | mt->select |= select; | |
668 | } | |
669 | ||
58de87cc AZ |
670 | /* |
671 | * If a row's change type (insert, delete or modify) matches that of | |
672 | * the monitor, they should be sent to the monitor's clients as updates. | |
673 | * Of cause, the monitor should also internally update with this change. | |
674 | * | |
675 | * When a change type does not require client side update, the monitor | |
676 | * may still need to keep track of certain changes in order to generate | |
677 | * correct future updates. For example, the monitor internal state should | |
678 | * be updated whenever a new row is inserted, in order to generate the | |
679 | * correct initial state, regardless if a insert change type is being | |
680 | * monitored. | |
681 | * | |
682 | * On the other hand, if a transaction only contains changes to columns | |
683 | * that are not monitored, this transaction can be safely ignored by the | |
684 | * monitor. | |
685 | * | |
686 | * Thus, the order of the declaration is important: | |
687 | * 'OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE' always implies | |
688 | * 'OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE', but not vice versa. */ | |
689 | enum ovsdb_monitor_changes_efficacy { | |
690 | OVSDB_CHANGES_NO_EFFECT, /* Monitor does not care about this | |
691 | change. */ | |
692 | OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE, /* Monitor internal updates. */ | |
693 | OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE, /* Client needs to be updated. */ | |
694 | }; | |
695 | ||
2fa1df7b AZ |
696 | struct ovsdb_monitor_aux { |
697 | const struct ovsdb_monitor *monitor; | |
698 | struct ovsdb_monitor_table *mt; | |
699 | }; | |
700 | ||
701 | static void | |
702 | ovsdb_monitor_init_aux(struct ovsdb_monitor_aux *aux, | |
703 | const struct ovsdb_monitor *m) | |
704 | { | |
705 | aux->monitor = m; | |
706 | aux->mt = NULL; | |
707 | } | |
708 | ||
7e911055 AZ |
709 | static void |
710 | ovsdb_monitor_changes_update(const struct ovsdb_row *old, | |
711 | const struct ovsdb_row *new, | |
712 | const struct ovsdb_monitor_table *mt, | |
713 | struct ovsdb_monitor_changes *changes) | |
2fa1df7b | 714 | { |
2fa1df7b AZ |
715 | const struct uuid *uuid = ovsdb_row_get_uuid(new ? new : old); |
716 | struct ovsdb_monitor_row *change; | |
2fa1df7b | 717 | |
7e911055 | 718 | change = ovsdb_monitor_changes_row_find(changes, uuid); |
2fa1df7b | 719 | if (!change) { |
1158f320 | 720 | change = xzalloc(sizeof *change); |
7e911055 | 721 | hmap_insert(&changes->rows, &change->hmap_node, uuid_hash(uuid)); |
2fa1df7b AZ |
722 | change->uuid = *uuid; |
723 | change->old = clone_monitor_row_data(mt, old); | |
724 | change->new = clone_monitor_row_data(mt, new); | |
725 | } else { | |
726 | if (new) { | |
727 | update_monitor_row_data(mt, new, change->new); | |
728 | } else { | |
729 | free_monitor_row_data(mt, change->new); | |
730 | change->new = NULL; | |
731 | ||
732 | if (!change->old) { | |
733 | /* This row was added then deleted. Forget about it. */ | |
7e911055 | 734 | hmap_remove(&changes->rows, &change->hmap_node); |
2fa1df7b AZ |
735 | free(change); |
736 | } | |
737 | } | |
738 | } | |
7e911055 AZ |
739 | } |
740 | ||
58de87cc AZ |
741 | static bool |
742 | ovsdb_monitor_columns_changed(const struct ovsdb_monitor_table *mt, | |
743 | const unsigned long int *changed) | |
744 | { | |
745 | size_t i; | |
746 | ||
747 | for (i = 0; i < mt->n_columns; i++) { | |
748 | size_t column_index = mt->columns[i].column->index; | |
749 | ||
750 | if (bitmap_is_set(changed, column_index)) { | |
751 | return true; | |
752 | } | |
753 | } | |
754 | ||
755 | return false; | |
756 | } | |
757 | ||
758 | /* Return the efficacy of a row's change to a monitor table. | |
759 | * | |
760 | * Please see the block comment above 'ovsdb_monitor_changes_efficacy' | |
761 | * definition form more information. */ | |
762 | static enum ovsdb_monitor_changes_efficacy | |
763 | ovsdb_monitor_changes_classify(enum ovsdb_monitor_selection type, | |
764 | const struct ovsdb_monitor_table *mt, | |
765 | const unsigned long int *changed) | |
766 | { | |
767 | if (type == OJMS_MODIFY && | |
768 | !ovsdb_monitor_columns_changed(mt, changed)) { | |
769 | return OVSDB_CHANGES_NO_EFFECT; | |
770 | } | |
771 | ||
772 | return (mt->select & type) | |
773 | ? OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE | |
774 | : OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE; | |
775 | } | |
776 | ||
7e911055 AZ |
777 | static bool |
778 | ovsdb_monitor_change_cb(const struct ovsdb_row *old, | |
779 | const struct ovsdb_row *new, | |
780 | const unsigned long int *changed OVS_UNUSED, | |
781 | void *aux_) | |
782 | { | |
783 | struct ovsdb_monitor_aux *aux = aux_; | |
784 | const struct ovsdb_monitor *m = aux->monitor; | |
785 | struct ovsdb_table *table = new ? new->table : old->table; | |
786 | struct ovsdb_monitor_table *mt; | |
787 | struct ovsdb_monitor_changes *changes; | |
788 | ||
789 | if (!aux->mt || table != aux->mt->table) { | |
790 | aux->mt = shash_find_data(&m->tables, table->schema->name); | |
791 | if (!aux->mt) { | |
792 | /* We don't care about rows in this table at all. Tell the caller | |
793 | * to skip it. */ | |
794 | return false; | |
795 | } | |
796 | } | |
797 | mt = aux->mt; | |
798 | ||
799 | HMAP_FOR_EACH(changes, hmap_node, &mt->changes) { | |
58de87cc AZ |
800 | enum ovsdb_monitor_changes_efficacy efficacy; |
801 | enum ovsdb_monitor_selection type; | |
802 | ||
803 | type = ovsdb_monitor_row_update_type(false, old, new); | |
804 | efficacy = ovsdb_monitor_changes_classify(type, mt, changed); | |
805 | if (efficacy > OVSDB_CHANGES_NO_EFFECT) { | |
806 | ovsdb_monitor_changes_update(old, new, mt, changes); | |
807 | } | |
7e911055 | 808 | } |
58de87cc | 809 | |
2fa1df7b AZ |
810 | return true; |
811 | } | |
812 | ||
61b63013 | 813 | void |
2fa1df7b AZ |
814 | ovsdb_monitor_get_initial(const struct ovsdb_monitor *dbmon) |
815 | { | |
816 | struct ovsdb_monitor_aux aux; | |
817 | struct shash_node *node; | |
2fa1df7b AZ |
818 | |
819 | ovsdb_monitor_init_aux(&aux, dbmon); | |
820 | SHASH_FOR_EACH (node, &dbmon->tables) { | |
821 | struct ovsdb_monitor_table *mt = node->data; | |
822 | ||
823 | if (mt->select & OJMS_INITIAL) { | |
824 | struct ovsdb_row *row; | |
6e5a9216 | 825 | struct ovsdb_monitor_changes *changes; |
2fa1df7b | 826 | |
6e5a9216 AZ |
827 | changes = ovsdb_monitor_table_find_changes(mt, 0); |
828 | if (!changes) { | |
829 | changes = ovsdb_monitor_table_add_changes(mt, 0); | |
830 | HMAP_FOR_EACH (row, hmap_node, &mt->table->rows) { | |
831 | ovsdb_monitor_changes_update(NULL, row, mt, changes); | |
832 | } | |
833 | } else { | |
834 | changes->n_refs++; | |
2fa1df7b AZ |
835 | } |
836 | } | |
837 | } | |
2fa1df7b AZ |
838 | } |
839 | ||
840 | void | |
d9412837 AZ |
841 | ovsdb_monitor_remove_jsonrpc_monitor(struct ovsdb_monitor *dbmon, |
842 | struct ovsdb_jsonrpc_monitor *jsonrpc_monitor) | |
843 | { | |
844 | struct jsonrpc_monitor_node *jm; | |
845 | ||
6e5a9216 AZ |
846 | if (list_is_empty(&dbmon->jsonrpc_monitors)) { |
847 | ovsdb_monitor_destroy(dbmon); | |
848 | return; | |
849 | } | |
850 | ||
d9412837 AZ |
851 | /* Find and remove the jsonrpc monitor from the list. */ |
852 | LIST_FOR_EACH(jm, node, &dbmon->jsonrpc_monitors) { | |
853 | if (jm->jsonrpc_monitor == jsonrpc_monitor) { | |
854 | list_remove(&jm->node); | |
855 | free(jm); | |
856 | ||
857 | /* Destroy ovsdb monitor if this is the last user. */ | |
858 | if (list_is_empty(&dbmon->jsonrpc_monitors)) { | |
859 | ovsdb_monitor_destroy(dbmon); | |
860 | } | |
861 | ||
862 | return; | |
863 | }; | |
864 | } | |
865 | ||
866 | /* Should never reach here. jsonrpc_monitor should be on the list. */ | |
867 | OVS_NOT_REACHED(); | |
868 | } | |
869 | ||
6e5a9216 AZ |
870 | static bool |
871 | ovsdb_monitor_table_equal(const struct ovsdb_monitor_table *a, | |
872 | const struct ovsdb_monitor_table *b) | |
873 | { | |
874 | size_t i; | |
875 | ||
876 | if ((a->table != b->table) || | |
877 | (a->select != b->select) || | |
878 | (a->n_columns != b->n_columns)) { | |
879 | return false; | |
880 | } | |
881 | ||
882 | for (i = 0; i < a->n_columns; i++) { | |
883 | if ((a->columns[i].column != b->columns[i].column) || | |
884 | (a->columns[i].select != b->columns[i].select)) { | |
885 | return false; | |
886 | } | |
887 | } | |
888 | ||
889 | return true; | |
890 | } | |
891 | ||
892 | static bool | |
893 | ovsdb_monitor_equal(const struct ovsdb_monitor *a, | |
894 | const struct ovsdb_monitor *b) | |
895 | { | |
896 | struct shash_node *node; | |
897 | ||
898 | if (shash_count(&a->tables) != shash_count(&b->tables)) { | |
899 | return false; | |
900 | } | |
901 | ||
902 | SHASH_FOR_EACH(node, &a->tables) { | |
903 | const struct ovsdb_monitor_table *mta = node->data; | |
904 | const struct ovsdb_monitor_table *mtb; | |
905 | ||
906 | mtb = shash_find_data(&b->tables, node->name); | |
907 | if (!mtb) { | |
908 | return false; | |
909 | } | |
910 | ||
911 | if (!ovsdb_monitor_table_equal(mta, mtb)) { | |
912 | return false; | |
913 | } | |
914 | } | |
915 | ||
916 | return true; | |
917 | } | |
918 | ||
919 | static size_t | |
920 | ovsdb_monitor_hash(const struct ovsdb_monitor *dbmon, size_t basis) | |
921 | { | |
922 | const struct shash_node **nodes; | |
923 | size_t i, j, n; | |
924 | ||
925 | nodes = shash_sort(&dbmon->tables); | |
926 | n = shash_count(&dbmon->tables); | |
927 | ||
928 | for (i = 0; i < n; i++) { | |
929 | struct ovsdb_monitor_table *mt = nodes[i]->data; | |
930 | ||
931 | basis = hash_pointer(mt->table, basis); | |
932 | basis = hash_3words(mt->select, mt->n_columns, basis); | |
933 | ||
934 | for (j = 0; j < mt->n_columns; j++) { | |
935 | basis = hash_pointer(mt->columns[j].column, basis); | |
936 | basis = hash_2words(mt->columns[j].select, basis); | |
937 | } | |
938 | } | |
939 | free(nodes); | |
940 | ||
941 | return basis; | |
942 | } | |
943 | ||
944 | struct ovsdb_monitor * | |
945 | ovsdb_monitor_add(struct ovsdb_monitor *new_dbmon) | |
946 | { | |
947 | struct ovsdb_monitor *dbmon; | |
948 | size_t hash; | |
949 | ||
950 | /* New_dbmon should be associated with only one jsonrpc | |
951 | * connections. */ | |
952 | ovs_assert(list_is_singleton(&new_dbmon->jsonrpc_monitors)); | |
953 | ||
954 | hash = ovsdb_monitor_hash(new_dbmon, 0); | |
955 | HMAP_FOR_EACH_WITH_HASH(dbmon, hmap_node, hash, &ovsdb_monitors) { | |
956 | if (ovsdb_monitor_equal(dbmon, new_dbmon)) { | |
957 | return dbmon; | |
958 | } | |
959 | } | |
960 | ||
961 | hmap_insert(&ovsdb_monitors, &new_dbmon->hmap_node, hash); | |
962 | return new_dbmon; | |
963 | } | |
964 | ||
d9412837 | 965 | static void |
2fa1df7b AZ |
966 | ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon) |
967 | { | |
968 | struct shash_node *node; | |
969 | ||
970 | list_remove(&dbmon->replica.node); | |
971 | ||
6e5a9216 AZ |
972 | if (!hmap_node_is_null(&dbmon->hmap_node)) { |
973 | hmap_remove(&ovsdb_monitors, &dbmon->hmap_node); | |
974 | } | |
975 | ||
4c280978 AZ |
976 | ovsdb_monitor_json_cache_flush(dbmon); |
977 | hmap_destroy(&dbmon->json_cache); | |
978 | ||
2fa1df7b AZ |
979 | SHASH_FOR_EACH (node, &dbmon->tables) { |
980 | struct ovsdb_monitor_table *mt = node->data; | |
7e911055 | 981 | struct ovsdb_monitor_changes *changes, *next; |
2fa1df7b | 982 | |
7e911055 AZ |
983 | HMAP_FOR_EACH_SAFE (changes, next, hmap_node, &mt->changes) { |
984 | hmap_remove(&mt->changes, &changes->hmap_node); | |
985 | ovsdb_monitor_changes_destroy(changes); | |
986 | } | |
2fa1df7b AZ |
987 | free(mt->columns); |
988 | free(mt); | |
989 | } | |
990 | shash_destroy(&dbmon->tables); | |
991 | free(dbmon); | |
992 | } | |
993 | ||
994 | static struct ovsdb_error * | |
995 | ovsdb_monitor_commit(struct ovsdb_replica *replica, | |
996 | const struct ovsdb_txn *txn, | |
997 | bool durable OVS_UNUSED) | |
998 | { | |
999 | struct ovsdb_monitor *m = ovsdb_monitor_cast(replica); | |
1000 | struct ovsdb_monitor_aux aux; | |
1001 | ||
4c280978 | 1002 | ovsdb_monitor_json_cache_flush(m); |
2fa1df7b AZ |
1003 | ovsdb_monitor_init_aux(&aux, m); |
1004 | ovsdb_txn_for_each_change(txn, ovsdb_monitor_change_cb, &aux); | |
59c35e11 | 1005 | m->n_transactions++; |
2fa1df7b AZ |
1006 | |
1007 | return NULL; | |
1008 | } | |
1009 | ||
1010 | static void | |
1011 | ovsdb_monitor_destroy_callback(struct ovsdb_replica *replica) | |
1012 | { | |
1013 | struct ovsdb_monitor *dbmon = ovsdb_monitor_cast(replica); | |
d9412837 | 1014 | struct jsonrpc_monitor_node *jm, *next; |
2fa1df7b | 1015 | |
d9412837 AZ |
1016 | /* Delete all front end monitors. Removing the last front |
1017 | * end monitor will also destroy the corresponding 'ovsdb_monitor'. | |
1018 | * ovsdb monitor will also be destroied. */ | |
1019 | LIST_FOR_EACH_SAFE(jm, next, node, &dbmon->jsonrpc_monitors) { | |
1020 | ovsdb_jsonrpc_monitor_destroy(jm->jsonrpc_monitor); | |
1021 | } | |
2fa1df7b AZ |
1022 | } |
1023 | ||
1024 | static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class = { | |
1025 | ovsdb_monitor_commit, | |
1026 | ovsdb_monitor_destroy_callback, | |
1027 | }; |