]>
Commit | Line | Data |
---|---|---|
6aa09313 | 1 | /* Copyright (c) 2009, 2010, 2011 Nicira Networks |
f85f8ebb 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.h" | |
19 | ||
0d0f05b9 | 20 | #include "column.h" |
f85f8ebb BP |
21 | #include "json.h" |
22 | #include "ovsdb-error.h" | |
23 | #include "ovsdb-parser.h" | |
0d0f05b9 | 24 | #include "ovsdb-types.h" |
f85f8ebb BP |
25 | #include "table.h" |
26 | #include "transaction.h" | |
27 | ||
f85f8ebb | 28 | struct ovsdb_schema * |
6aa09313 | 29 | ovsdb_schema_create(const char *name, const char *version, const char *cksum) |
f85f8ebb BP |
30 | { |
31 | struct ovsdb_schema *schema; | |
32 | ||
33 | schema = xzalloc(sizeof *schema); | |
34 | schema->name = xstrdup(name); | |
8159b984 | 35 | schema->version = xstrdup(version); |
6aa09313 | 36 | schema->cksum = xstrdup(cksum); |
f85f8ebb BP |
37 | shash_init(&schema->tables); |
38 | ||
39 | return schema; | |
40 | } | |
41 | ||
58985e09 BP |
42 | struct ovsdb_schema * |
43 | ovsdb_schema_clone(const struct ovsdb_schema *old) | |
44 | { | |
45 | struct ovsdb_schema *new; | |
46 | struct shash_node *node; | |
47 | ||
6aa09313 | 48 | new = ovsdb_schema_create(old->name, old->version, old->cksum); |
58985e09 BP |
49 | SHASH_FOR_EACH (node, &old->tables) { |
50 | const struct ovsdb_table_schema *ts = node->data; | |
51 | ||
52 | shash_add(&new->tables, node->name, ovsdb_table_schema_clone(ts)); | |
53 | } | |
54 | return new; | |
55 | } | |
56 | ||
f85f8ebb BP |
57 | void |
58 | ovsdb_schema_destroy(struct ovsdb_schema *schema) | |
59 | { | |
60 | struct shash_node *node; | |
61 | ||
271915d3 BP |
62 | if (!schema) { |
63 | return; | |
64 | } | |
65 | ||
f85f8ebb BP |
66 | SHASH_FOR_EACH (node, &schema->tables) { |
67 | ovsdb_table_schema_destroy(node->data); | |
68 | } | |
69 | shash_destroy(&schema->tables); | |
f85f8ebb | 70 | free(schema->name); |
8159b984 | 71 | free(schema->version); |
6aa09313 | 72 | free(schema->cksum); |
f85f8ebb BP |
73 | free(schema); |
74 | } | |
75 | ||
76 | struct ovsdb_error * | |
77 | ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap) | |
78 | { | |
79 | struct ovsdb_schema *schema; | |
80 | struct ovsdb_error *error; | |
81 | struct json *json; | |
82 | ||
83 | *schemap = NULL; | |
84 | json = json_from_file(file_name); | |
85 | if (json->type == JSON_STRING) { | |
86 | error = ovsdb_error("failed to read schema", | |
87 | "\"%s\" could not be read as JSON (%s)", | |
88 | file_name, json_string(json)); | |
89 | json_destroy(json); | |
90 | return error; | |
91 | } | |
92 | ||
93 | error = ovsdb_schema_from_json(json, &schema); | |
e084f690 | 94 | json_destroy(json); |
f85f8ebb | 95 | if (error) { |
f85f8ebb BP |
96 | return ovsdb_wrap_error(error, |
97 | "failed to parse \"%s\" as ovsdb schema", | |
98 | file_name); | |
99 | } | |
100 | ||
101 | *schemap = schema; | |
102 | return NULL; | |
103 | } | |
104 | ||
0d0f05b9 BP |
105 | static struct ovsdb_error * WARN_UNUSED_RESULT |
106 | ovsdb_schema_check_ref_table(const struct ovsdb_column *column, | |
107 | const struct shash *tables, | |
108 | const struct ovsdb_base_type *base, | |
109 | const char *base_name) | |
110 | { | |
111 | if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName | |
112 | && !shash_find(tables, base->u.uuid.refTableName)) { | |
113 | return ovsdb_syntax_error(NULL, NULL, | |
114 | "column %s %s refers to undefined table %s", | |
115 | column->name, base_name, | |
116 | base->u.uuid.refTableName); | |
117 | } else { | |
118 | return NULL; | |
119 | } | |
120 | } | |
121 | ||
8159b984 BP |
122 | static bool |
123 | is_valid_version(const char *s) | |
124 | { | |
125 | int n = -1; | |
1883ed0f | 126 | ignore(sscanf(s, "%*[0-9].%*[0-9].%*[0-9]%n", &n)); |
8159b984 BP |
127 | return n != -1 && s[n] == '\0'; |
128 | } | |
129 | ||
c5f341ab BP |
130 | /* Returns the number of tables in 'schema''s root set. */ |
131 | static size_t | |
132 | root_set_size(const struct ovsdb_schema *schema) | |
133 | { | |
134 | struct shash_node *node; | |
135 | size_t n_root; | |
136 | ||
137 | SHASH_FOR_EACH (node, &schema->tables) { | |
138 | struct ovsdb_table_schema *table = node->data; | |
139 | ||
140 | n_root += table->is_root; | |
141 | } | |
142 | return n_root; | |
143 | } | |
144 | ||
f85f8ebb BP |
145 | struct ovsdb_error * |
146 | ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap) | |
147 | { | |
148 | struct ovsdb_schema *schema; | |
6aa09313 | 149 | const struct json *name, *tables, *version_json, *cksum; |
f85f8ebb BP |
150 | struct ovsdb_error *error; |
151 | struct shash_node *node; | |
152 | struct ovsdb_parser parser; | |
8159b984 | 153 | const char *version; |
f85f8ebb BP |
154 | |
155 | *schemap = NULL; | |
156 | ||
157 | ovsdb_parser_init(&parser, json, "database schema"); | |
158 | name = ovsdb_parser_member(&parser, "name", OP_ID); | |
8159b984 BP |
159 | version_json = ovsdb_parser_member(&parser, "version", |
160 | OP_STRING | OP_OPTIONAL); | |
6aa09313 | 161 | cksum = ovsdb_parser_member(&parser, "cksum", OP_STRING | OP_OPTIONAL); |
f85f8ebb BP |
162 | tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT); |
163 | error = ovsdb_parser_finish(&parser); | |
164 | if (error) { | |
165 | return error; | |
166 | } | |
167 | ||
8159b984 BP |
168 | if (version_json) { |
169 | version = json_string(version_json); | |
170 | if (!is_valid_version(version)) { | |
171 | return ovsdb_syntax_error(json, NULL, "schema version \"%s\" not " | |
172 | "in format x.y.z", version); | |
173 | } | |
174 | } else { | |
175 | /* Backward compatibility with old databases. */ | |
176 | version = ""; | |
177 | } | |
178 | ||
6aa09313 BP |
179 | schema = ovsdb_schema_create(json_string(name), version, |
180 | cksum ? json_string(cksum) : ""); | |
f85f8ebb BP |
181 | SHASH_FOR_EACH (node, json_object(tables)) { |
182 | struct ovsdb_table_schema *table; | |
183 | ||
184 | if (node->name[0] == '_') { | |
185 | error = ovsdb_syntax_error(json, NULL, "names beginning with " | |
186 | "\"_\" are reserved"); | |
b966380b BP |
187 | } else if (!ovsdb_parser_is_id(node->name)) { |
188 | error = ovsdb_syntax_error(json, NULL, "name must be a valid id"); | |
f85f8ebb BP |
189 | } else { |
190 | error = ovsdb_table_schema_from_json(node->data, node->name, | |
191 | &table); | |
192 | } | |
193 | if (error) { | |
194 | ovsdb_schema_destroy(schema); | |
195 | return error; | |
196 | } | |
197 | ||
198 | shash_add(&schema->tables, table->name, table); | |
199 | } | |
0d0f05b9 BP |
200 | |
201 | /* Validate that all refTables refer to the names of tables that exist. */ | |
202 | SHASH_FOR_EACH (node, &schema->tables) { | |
203 | struct ovsdb_table_schema *table = node->data; | |
204 | struct shash_node *node2; | |
205 | ||
206 | SHASH_FOR_EACH (node2, &table->columns) { | |
207 | struct ovsdb_column *column = node2->data; | |
208 | ||
209 | error = ovsdb_schema_check_ref_table(column, &schema->tables, | |
210 | &column->type.key, "key"); | |
211 | if (!error) { | |
212 | error = ovsdb_schema_check_ref_table(column, &schema->tables, | |
213 | &column->type.value, | |
214 | "value"); | |
215 | } | |
216 | if (error) { | |
217 | ovsdb_schema_destroy(schema); | |
218 | return error; | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
c5f341ab BP |
223 | /* "isRoot" was not part of the original schema definition. Before it was |
224 | * added, there was no support for garbage collection. So, for backward | |
225 | * compatibility, if the root set is empty then assume that every table is | |
226 | * in the root set. */ | |
227 | if (root_set_size(schema) == 0) { | |
228 | SHASH_FOR_EACH (node, &schema->tables) { | |
229 | struct ovsdb_table_schema *table = node->data; | |
230 | ||
231 | table->is_root = true; | |
232 | } | |
233 | } | |
234 | ||
f85f8ebb BP |
235 | *schemap = schema; |
236 | return 0; | |
237 | } | |
238 | ||
239 | struct json * | |
240 | ovsdb_schema_to_json(const struct ovsdb_schema *schema) | |
241 | { | |
242 | struct json *json, *tables; | |
243 | struct shash_node *node; | |
c5f341ab | 244 | bool default_is_root; |
f85f8ebb BP |
245 | |
246 | json = json_object_create(); | |
247 | json_object_put_string(json, "name", schema->name); | |
8159b984 BP |
248 | if (schema->version[0]) { |
249 | json_object_put_string(json, "version", schema->version); | |
250 | } | |
6aa09313 BP |
251 | if (schema->cksum[0]) { |
252 | json_object_put_string(json, "cksum", schema->cksum); | |
253 | } | |
f85f8ebb | 254 | |
c5f341ab BP |
255 | /* "isRoot" was not part of the original schema definition. Before it was |
256 | * added, there was no support for garbage collection. So, for backward | |
257 | * compatibility, if every table is in the root set then do not output | |
258 | * "isRoot" in table schemas. */ | |
259 | default_is_root = root_set_size(schema) == shash_count(&schema->tables); | |
260 | ||
f85f8ebb BP |
261 | tables = json_object_create(); |
262 | ||
263 | SHASH_FOR_EACH (node, &schema->tables) { | |
264 | struct ovsdb_table_schema *table = node->data; | |
265 | json_object_put(tables, table->name, | |
c5f341ab | 266 | ovsdb_table_schema_to_json(table, default_is_root)); |
f85f8ebb BP |
267 | } |
268 | json_object_put(json, "tables", tables); | |
269 | ||
270 | return json; | |
271 | } | |
403e3a25 BP |
272 | |
273 | /* Returns true if 'a' and 'b' specify equivalent schemas, false if they | |
274 | * differ. */ | |
275 | bool | |
276 | ovsdb_schema_equal(const struct ovsdb_schema *a, | |
277 | const struct ovsdb_schema *b) | |
278 | { | |
279 | /* This implementation is simple, stupid, and slow, but I doubt that it | |
280 | * will ever require much maintenance. */ | |
281 | struct json *ja = ovsdb_schema_to_json(a); | |
282 | struct json *jb = ovsdb_schema_to_json(b); | |
283 | bool equals = json_equal(ja, jb); | |
284 | json_destroy(ja); | |
285 | json_destroy(jb); | |
286 | ||
287 | return equals; | |
288 | } | |
f85f8ebb | 289 | \f |
0d0f05b9 BP |
290 | static void |
291 | ovsdb_set_ref_table(const struct shash *tables, | |
292 | struct ovsdb_base_type *base) | |
293 | { | |
294 | if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { | |
295 | struct ovsdb_table *table; | |
296 | ||
297 | table = shash_find_data(tables, base->u.uuid.refTableName); | |
298 | base->u.uuid.refTable = table; | |
299 | } | |
300 | } | |
301 | ||
f85f8ebb | 302 | struct ovsdb * |
bd06962a | 303 | ovsdb_create(struct ovsdb_schema *schema) |
f85f8ebb BP |
304 | { |
305 | struct shash_node *node; | |
306 | struct ovsdb *db; | |
307 | ||
308 | db = xmalloc(sizeof *db); | |
309 | db->schema = schema; | |
bd06962a | 310 | list_init(&db->replicas); |
f85f8ebb BP |
311 | list_init(&db->triggers); |
312 | db->run_triggers = false; | |
313 | ||
314 | shash_init(&db->tables); | |
315 | SHASH_FOR_EACH (node, &schema->tables) { | |
316 | struct ovsdb_table_schema *ts = node->data; | |
317 | shash_add(&db->tables, node->name, ovsdb_table_create(ts)); | |
318 | } | |
319 | ||
0d0f05b9 BP |
320 | /* Set all the refTables. */ |
321 | SHASH_FOR_EACH (node, &schema->tables) { | |
322 | struct ovsdb_table_schema *table = node->data; | |
323 | struct shash_node *node2; | |
324 | ||
325 | SHASH_FOR_EACH (node2, &table->columns) { | |
326 | struct ovsdb_column *column = node2->data; | |
327 | ||
328 | ovsdb_set_ref_table(&db->tables, &column->type.key); | |
329 | ovsdb_set_ref_table(&db->tables, &column->type.value); | |
330 | } | |
331 | } | |
332 | ||
f85f8ebb BP |
333 | return db; |
334 | } | |
335 | ||
f85f8ebb BP |
336 | void |
337 | ovsdb_destroy(struct ovsdb *db) | |
338 | { | |
339 | if (db) { | |
340 | struct shash_node *node; | |
341 | ||
bd06962a BP |
342 | /* Remove all the replicas. */ |
343 | while (!list_is_empty(&db->replicas)) { | |
344 | struct ovsdb_replica *r | |
345 | = CONTAINER_OF(list_pop_back(&db->replicas), | |
346 | struct ovsdb_replica, node); | |
347 | ovsdb_remove_replica(db, r); | |
348 | } | |
349 | ||
f85f8ebb BP |
350 | /* Delete all the tables. This also deletes their schemas. */ |
351 | SHASH_FOR_EACH (node, &db->tables) { | |
352 | struct ovsdb_table *table = node->data; | |
353 | ovsdb_table_destroy(table); | |
354 | } | |
355 | shash_destroy(&db->tables); | |
356 | ||
1248fcd2 BP |
357 | /* The schemas, but not the table that points to them, were deleted in |
358 | * the previous step, so we need to clear out the table. We can't | |
359 | * destroy the table, because ovsdb_schema_destroy() will do that. */ | |
360 | shash_clear(&db->schema->tables); | |
f85f8ebb BP |
361 | |
362 | ovsdb_schema_destroy(db->schema); | |
f85f8ebb BP |
363 | free(db); |
364 | } | |
365 | } | |
366 | ||
367 | struct ovsdb_table * | |
368 | ovsdb_get_table(const struct ovsdb *db, const char *name) | |
369 | { | |
370 | return shash_find_data(&db->tables, name); | |
371 | } | |
bd06962a BP |
372 | \f |
373 | void | |
374 | ovsdb_replica_init(struct ovsdb_replica *r, | |
375 | const struct ovsdb_replica_class *class) | |
376 | { | |
377 | r->class = class; | |
378 | } | |
379 | ||
380 | void | |
381 | ovsdb_add_replica(struct ovsdb *db, struct ovsdb_replica *r) | |
382 | { | |
383 | list_push_back(&db->replicas, &r->node); | |
384 | } | |
385 | ||
386 | void | |
c69ee87c | 387 | ovsdb_remove_replica(struct ovsdb *db OVS_UNUSED, struct ovsdb_replica *r) |
bd06962a BP |
388 | { |
389 | list_remove(&r->node); | |
390 | (r->class->destroy)(r); | |
391 | } |