Branch data Line data Source code
1 : : /* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
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.h"
19 : :
20 : : #include "column.h"
21 : : #include "openvswitch/json.h"
22 : : #include "ovsdb-error.h"
23 : : #include "ovsdb-parser.h"
24 : : #include "ovsdb-types.h"
25 : : #include "simap.h"
26 : : #include "table.h"
27 : : #include "transaction.h"
28 : :
29 : : struct ovsdb_schema *
30 : 3045 : ovsdb_schema_create(const char *name, const char *version, const char *cksum)
31 : : {
32 : : struct ovsdb_schema *schema;
33 : :
34 : 3045 : schema = xzalloc(sizeof *schema);
35 : 3045 : schema->name = xstrdup(name);
36 : 3045 : schema->version = xstrdup(version);
37 : 3045 : schema->cksum = xstrdup(cksum);
38 : 3045 : shash_init(&schema->tables);
39 : :
40 : 3045 : return schema;
41 : : }
42 : :
43 : : struct ovsdb_schema *
44 : 2 : ovsdb_schema_clone(const struct ovsdb_schema *old)
45 : : {
46 : : struct ovsdb_schema *new;
47 : : struct shash_node *node;
48 : :
49 : 2 : new = ovsdb_schema_create(old->name, old->version, old->cksum);
50 [ + + ][ - + ]: 4 : SHASH_FOR_EACH (node, &old->tables) {
51 : 2 : const struct ovsdb_table_schema *ts = node->data;
52 : :
53 : 2 : shash_add(&new->tables, node->name, ovsdb_table_schema_clone(ts));
54 : : }
55 : 2 : return new;
56 : : }
57 : :
58 : : void
59 : 3015 : ovsdb_schema_destroy(struct ovsdb_schema *schema)
60 : : {
61 : : struct shash_node *node;
62 : :
63 [ - + ]: 3015 : if (!schema) {
64 : 0 : return;
65 : : }
66 : :
67 [ + + ][ - + ]: 18185 : SHASH_FOR_EACH (node, &schema->tables) {
68 : 15170 : ovsdb_table_schema_destroy(node->data);
69 : : }
70 : 3015 : shash_destroy(&schema->tables);
71 : 3015 : free(schema->name);
72 : 3015 : free(schema->version);
73 : 3015 : free(schema->cksum);
74 : 3015 : free(schema);
75 : : }
76 : :
77 : : struct ovsdb_error *
78 : 1263 : ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap)
79 : : {
80 : : struct ovsdb_schema *schema;
81 : : struct ovsdb_error *error;
82 : : struct json *json;
83 : :
84 : 1263 : *schemap = NULL;
85 : 1263 : json = json_from_file(file_name);
86 [ - + ]: 1263 : if (json->type == JSON_STRING) {
87 : 0 : error = ovsdb_error("failed to read schema",
88 : : "\"%s\" could not be read as JSON (%s)",
89 : : file_name, json_string(json));
90 : 0 : json_destroy(json);
91 : 0 : return error;
92 : : }
93 : :
94 : 1263 : error = ovsdb_schema_from_json(json, &schema);
95 : 1263 : json_destroy(json);
96 [ - + ]: 1263 : if (error) {
97 : 0 : return ovsdb_wrap_error(error,
98 : : "failed to parse \"%s\" as ovsdb schema",
99 : : file_name);
100 : : }
101 : :
102 : 1263 : *schemap = schema;
103 : 1263 : return NULL;
104 : : }
105 : :
106 : : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
107 : 671625 : ovsdb_schema_check_ref_table(struct ovsdb_column *column,
108 : : const struct shash *tables,
109 : : const struct ovsdb_base_type *base,
110 : : const char *base_name)
111 : : {
112 : : struct ovsdb_table_schema *refTable;
113 : :
114 [ + + ][ + + ]: 671625 : if (base->type != OVSDB_TYPE_UUID || !base->u.uuid.refTableName) {
115 : 637072 : return NULL;
116 : : }
117 : :
118 : 34553 : refTable = shash_find_data(tables, base->u.uuid.refTableName);
119 [ + + ]: 34553 : if (!refTable) {
120 : 1 : return ovsdb_syntax_error(NULL, NULL,
121 : : "column %s %s refers to undefined table %s",
122 : : column->name, base_name,
123 : : base->u.uuid.refTableName);
124 : : }
125 : :
126 [ + + ][ + + ]: 34552 : if (ovsdb_base_type_is_strong_ref(base) && !refTable->is_root) {
127 : : /* We cannot allow a strong reference to a non-root table to be
128 : : * ephemeral: if it is the only reference to a row, then replaying the
129 : : * database log from disk will cause the referenced row to be deleted,
130 : : * even though it did exist in memory. If there are references to that
131 : : * row later in the log (to modify it, to delete it, or just to point
132 : : * to it), then this will yield a transaction error. */
133 : 22754 : column->persistent = true;
134 : : }
135 : :
136 : 34552 : return NULL;
137 : : }
138 : :
139 : : static bool
140 : 2695 : is_valid_version(const char *s)
141 : : {
142 : 2695 : int n = -1;
143 : 2695 : ignore(ovs_scan(s, "%*[0-9].%*[0-9].%*[0-9]%n", &n));
144 [ + + ][ + - ]: 2695 : return n != -1 && s[n] == '\0';
145 : : }
146 : :
147 : : /* Returns the number of tables in 'schema''s root set. */
148 : : static size_t
149 : 11492 : root_set_size(const struct ovsdb_schema *schema)
150 : : {
151 : : struct shash_node *node;
152 : 11492 : size_t n_root = 0;
153 : :
154 [ + + ][ - + ]: 164459 : SHASH_FOR_EACH (node, &schema->tables) {
155 : 152967 : struct ovsdb_table_schema *table = node->data;
156 : :
157 : 152967 : n_root += table->is_root;
158 : : }
159 : 11492 : return n_root;
160 : : }
161 : :
162 : : struct ovsdb_error *
163 : 3044 : ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
164 : : {
165 : : struct ovsdb_schema *schema;
166 : : const struct json *name, *tables, *version_json, *cksum;
167 : : struct ovsdb_error *error;
168 : : struct shash_node *node;
169 : : struct ovsdb_parser parser;
170 : : const char *version;
171 : :
172 : 3044 : *schemap = NULL;
173 : :
174 : 3044 : ovsdb_parser_init(&parser, json, "database schema");
175 : 3044 : name = ovsdb_parser_member(&parser, "name", OP_ID);
176 : 3044 : version_json = ovsdb_parser_member(&parser, "version",
177 : : OP_STRING | OP_OPTIONAL);
178 : 3044 : cksum = ovsdb_parser_member(&parser, "cksum", OP_STRING | OP_OPTIONAL);
179 : 3044 : tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
180 : 3044 : error = ovsdb_parser_finish(&parser);
181 [ - + ]: 3044 : if (error) {
182 : 0 : return error;
183 : : }
184 : :
185 [ + + ]: 3044 : if (version_json) {
186 : 2695 : version = json_string(version_json);
187 [ + + ]: 2695 : if (!is_valid_version(version)) {
188 : 1 : return ovsdb_syntax_error(json, NULL, "schema version \"%s\" not "
189 : : "in format x.y.z", version);
190 : : }
191 : : } else {
192 : : /* Backward compatibility with old databases. */
193 : 349 : version = "";
194 : : }
195 : :
196 [ + + ]: 3043 : schema = ovsdb_schema_create(json_string(name), version,
197 : : cksum ? json_string(cksum) : "");
198 [ + + ][ - + ]: 33928 : SHASH_FOR_EACH (node, json_object(tables)) {
199 : : struct ovsdb_table_schema *table;
200 : :
201 [ - + ]: 30885 : if (node->name[0] == '_') {
202 : 0 : error = ovsdb_syntax_error(json, NULL, "names beginning with "
203 : : "\"_\" are reserved");
204 [ - + ]: 30885 : } else if (!ovsdb_parser_is_id(node->name)) {
205 : 0 : error = ovsdb_syntax_error(json, NULL, "name must be a valid id");
206 : : } else {
207 : 30885 : error = ovsdb_table_schema_from_json(node->data, node->name,
208 : : &table);
209 : : }
210 [ - + ]: 30885 : if (error) {
211 : 0 : ovsdb_schema_destroy(schema);
212 : 0 : return error;
213 : : }
214 : :
215 : 30885 : shash_add(&schema->tables, table->name, table);
216 : : }
217 : :
218 : : /* "isRoot" was not part of the original schema definition. Before it was
219 : : * added, there was no support for garbage collection. So, for backward
220 : : * compatibility, if the root set is empty then assume that every table is
221 : : * in the root set. */
222 [ + + ]: 3043 : if (root_set_size(schema) == 0) {
223 [ + + ][ - + ]: 4654 : SHASH_FOR_EACH (node, &schema->tables) {
224 : 3407 : struct ovsdb_table_schema *table = node->data;
225 : :
226 : 3407 : table->is_root = true;
227 : : }
228 : : }
229 : :
230 : : /* Validate that all refTables refer to the names of tables that exist.
231 : : *
232 : : * Also force certain columns to be persistent, as explained in
233 : : * ovsdb_schema_check_ref_table(). This requires 'is_root' to be known, so
234 : : * this must follow the loop updating 'is_root' above. */
235 [ + + ][ - + ]: 33926 : SHASH_FOR_EACH (node, &schema->tables) {
236 : 30884 : struct ovsdb_table_schema *table = node->data;
237 : : struct shash_node *node2;
238 : :
239 [ + + ][ - + ]: 366696 : SHASH_FOR_EACH (node2, &table->columns) {
240 : 335813 : struct ovsdb_column *column = node2->data;
241 : :
242 : 335813 : error = ovsdb_schema_check_ref_table(column, &schema->tables,
243 : 335813 : &column->type.key, "key");
244 [ + + ]: 335813 : if (!error) {
245 : 335812 : error = ovsdb_schema_check_ref_table(column, &schema->tables,
246 : 335812 : &column->type.value,
247 : : "value");
248 : : }
249 [ + + ]: 335813 : if (error) {
250 : 1 : ovsdb_schema_destroy(schema);
251 : 1 : return error;
252 : : }
253 : : }
254 : : }
255 : :
256 : 3042 : *schemap = schema;
257 : 3044 : return NULL;
258 : : }
259 : :
260 : : struct json *
261 : 8449 : ovsdb_schema_to_json(const struct ovsdb_schema *schema)
262 : : {
263 : : struct json *json, *tables;
264 : : struct shash_node *node;
265 : : bool default_is_root;
266 : :
267 : 8449 : json = json_object_create();
268 : 8449 : json_object_put_string(json, "name", schema->name);
269 [ + + ]: 8449 : if (schema->version[0]) {
270 : 8282 : json_object_put_string(json, "version", schema->version);
271 : : }
272 [ + + ]: 8449 : if (schema->cksum[0]) {
273 : 8073 : json_object_put_string(json, "cksum", schema->cksum);
274 : : }
275 : :
276 : : /* "isRoot" was not part of the original schema definition. Before it was
277 : : * added, there was no support for garbage collection. So, for backward
278 : : * compatibility, if every table is in the root set then do not output
279 : : * "isRoot" in table schemas. */
280 : 8449 : default_is_root = root_set_size(schema) == shash_count(&schema->tables);
281 : :
282 : 8449 : tables = json_object_create();
283 : :
284 [ + + ][ - + ]: 130531 : SHASH_FOR_EACH (node, &schema->tables) {
285 : 122082 : struct ovsdb_table_schema *table = node->data;
286 : 122082 : json_object_put(tables, table->name,
287 : : ovsdb_table_schema_to_json(table, default_is_root));
288 : : }
289 : 8449 : json_object_put(json, "tables", tables);
290 : :
291 : 8449 : return json;
292 : : }
293 : :
294 : : /* Returns true if 'a' and 'b' specify equivalent schemas, false if they
295 : : * differ. */
296 : : bool
297 : 37 : ovsdb_schema_equal(const struct ovsdb_schema *a,
298 : : const struct ovsdb_schema *b)
299 : : {
300 : : /* This implementation is simple, stupid, and slow, but I doubt that it
301 : : * will ever require much maintenance. */
302 : 37 : struct json *ja = ovsdb_schema_to_json(a);
303 : 37 : struct json *jb = ovsdb_schema_to_json(b);
304 : 37 : bool equals = json_equal(ja, jb);
305 : 37 : json_destroy(ja);
306 : 37 : json_destroy(jb);
307 : :
308 : 37 : return equals;
309 : : }
310 : :
311 : : static void
312 : 338204 : ovsdb_set_ref_table(const struct shash *tables,
313 : : struct ovsdb_base_type *base)
314 : : {
315 [ + + ][ + + ]: 338204 : if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) {
316 : : struct ovsdb_table *table;
317 : :
318 : 17744 : table = shash_find_data(tables, base->u.uuid.refTableName);
319 : 17744 : base->u.uuid.refTable = table;
320 : : }
321 : 338204 : }
322 : :
323 : : struct ovsdb *
324 : 1567 : ovsdb_create(struct ovsdb_schema *schema)
325 : : {
326 : : struct shash_node *node;
327 : : struct ovsdb *db;
328 : :
329 : 1567 : db = xmalloc(sizeof *db);
330 : 1567 : db->schema = schema;
331 : 1567 : ovs_list_init(&db->replicas);
332 : 1567 : ovs_list_init(&db->triggers);
333 : 1567 : db->run_triggers = false;
334 : :
335 : 1567 : shash_init(&db->tables);
336 [ + + ][ - + ]: 17255 : SHASH_FOR_EACH (node, &schema->tables) {
337 : 15688 : struct ovsdb_table_schema *ts = node->data;
338 : 15688 : shash_add(&db->tables, node->name, ovsdb_table_create(ts));
339 : : }
340 : :
341 : : /* Set all the refTables. */
342 [ + + ][ - + ]: 17255 : SHASH_FOR_EACH (node, &schema->tables) {
343 : 15688 : struct ovsdb_table_schema *table = node->data;
344 : : struct shash_node *node2;
345 : :
346 [ + + ][ - + ]: 184790 : SHASH_FOR_EACH (node2, &table->columns) {
347 : 169102 : struct ovsdb_column *column = node2->data;
348 : :
349 : 169102 : ovsdb_set_ref_table(&db->tables, &column->type.key);
350 : 169102 : ovsdb_set_ref_table(&db->tables, &column->type.value);
351 : : }
352 : : }
353 : :
354 : 1567 : return db;
355 : : }
356 : :
357 : : void
358 : 1567 : ovsdb_destroy(struct ovsdb *db)
359 : : {
360 [ + + ]: 1567 : if (db) {
361 : : struct shash_node *node;
362 : :
363 : : /* Remove all the replicas. */
364 [ + + ]: 3077 : while (!ovs_list_is_empty(&db->replicas)) {
365 : 1512 : struct ovsdb_replica *r
366 : 1512 : = CONTAINER_OF(ovs_list_pop_back(&db->replicas),
367 : : struct ovsdb_replica, node);
368 : 1512 : ovsdb_remove_replica(db, r);
369 : : }
370 : :
371 : : /* Delete all the tables. This also deletes their schemas. */
372 [ + + ][ - + ]: 17221 : SHASH_FOR_EACH (node, &db->tables) {
373 : 15656 : struct ovsdb_table *table = node->data;
374 : 15656 : ovsdb_table_destroy(table);
375 : : }
376 : 1565 : shash_destroy(&db->tables);
377 : :
378 : : /* The schemas, but not the table that points to them, were deleted in
379 : : * the previous step, so we need to clear out the table. We can't
380 : : * destroy the table, because ovsdb_schema_destroy() will do that. */
381 : 1565 : shash_clear(&db->schema->tables);
382 : :
383 : 1565 : ovsdb_schema_destroy(db->schema);
384 : 1565 : free(db);
385 : : }
386 : 1567 : }
387 : :
388 : : /* Adds some memory usage statistics for 'db' into 'usage', for use with
389 : : * memory_report(). */
390 : : void
391 : 75 : ovsdb_get_memory_usage(const struct ovsdb *db, struct simap *usage)
392 : : {
393 : : const struct shash_node *node;
394 : 75 : unsigned int cells = 0;
395 : :
396 [ + + ][ - + ]: 1191 : SHASH_FOR_EACH (node, &db->tables) {
397 : 1116 : const struct ovsdb_table *table = node->data;
398 : 1116 : unsigned int n_columns = shash_count(&table->schema->columns);
399 : 1116 : unsigned int n_rows = hmap_count(&table->rows);
400 : :
401 : 1116 : cells += n_rows * n_columns;
402 : : }
403 : :
404 : 75 : simap_increase(usage, "cells", cells);
405 : 75 : }
406 : :
407 : : struct ovsdb_table *
408 : 44508 : ovsdb_get_table(const struct ovsdb *db, const char *name)
409 : : {
410 : 44508 : return shash_find_data(&db->tables, name);
411 : : }
412 : :
413 : : void
414 : 8654 : ovsdb_replica_init(struct ovsdb_replica *r,
415 : : const struct ovsdb_replica_class *class)
416 : : {
417 : 8654 : r->class = class;
418 : 8654 : }
419 : :
420 : : void
421 : 8654 : ovsdb_add_replica(struct ovsdb *db, struct ovsdb_replica *r)
422 : : {
423 : 8654 : ovs_list_push_back(&db->replicas, &r->node);
424 : 8654 : }
425 : :
426 : : void
427 : 1512 : ovsdb_remove_replica(struct ovsdb *db OVS_UNUSED, struct ovsdb_replica *r)
428 : : {
429 : 1512 : ovs_list_remove(&r->node);
430 : 1512 : (r->class->destroy)(r);
431 : 1512 : }
|