Implement database schema versioning.
[cascardo/ovs.git] / ovsdb / ovsdb.c
1 /* Copyright (c) 2009, 2010 Nicira Networks
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 "json.h"
22 #include "ovsdb-error.h"
23 #include "ovsdb-parser.h"
24 #include "ovsdb-types.h"
25 #include "table.h"
26 #include "transaction.h"
27
28 struct ovsdb_schema *
29 ovsdb_schema_create(const char *name, const char *version)
30 {
31     struct ovsdb_schema *schema;
32
33     schema = xzalloc(sizeof *schema);
34     schema->name = xstrdup(name);
35     schema->version = xstrdup(version);
36     shash_init(&schema->tables);
37
38     return schema;
39 }
40
41 struct ovsdb_schema *
42 ovsdb_schema_clone(const struct ovsdb_schema *old)
43 {
44     struct ovsdb_schema *new;
45     struct shash_node *node;
46
47     new = ovsdb_schema_create(old->name, old->version);
48     SHASH_FOR_EACH (node, &old->tables) {
49         const struct ovsdb_table_schema *ts = node->data;
50
51         shash_add(&new->tables, node->name, ovsdb_table_schema_clone(ts));
52     }
53     return new;
54 }
55
56 void
57 ovsdb_schema_destroy(struct ovsdb_schema *schema)
58 {
59     struct shash_node *node;
60
61     if (!schema) {
62         return;
63     }
64
65     SHASH_FOR_EACH (node, &schema->tables) {
66         ovsdb_table_schema_destroy(node->data);
67     }
68     shash_destroy(&schema->tables);
69     free(schema->name);
70     free(schema->version);
71     free(schema);
72 }
73
74 struct ovsdb_error *
75 ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap)
76 {
77     struct ovsdb_schema *schema;
78     struct ovsdb_error *error;
79     struct json *json;
80
81     *schemap = NULL;
82     json = json_from_file(file_name);
83     if (json->type == JSON_STRING) {
84         error = ovsdb_error("failed to read schema",
85                            "\"%s\" could not be read as JSON (%s)",
86                            file_name, json_string(json));
87         json_destroy(json);
88         return error;
89     }
90
91     error = ovsdb_schema_from_json(json, &schema);
92     json_destroy(json);
93     if (error) {
94         return ovsdb_wrap_error(error,
95                                 "failed to parse \"%s\" as ovsdb schema",
96                                 file_name);
97     }
98
99     *schemap = schema;
100     return NULL;
101 }
102
103 static struct ovsdb_error * WARN_UNUSED_RESULT
104 ovsdb_schema_check_ref_table(const struct ovsdb_column *column,
105                              const struct shash *tables,
106                              const struct ovsdb_base_type *base,
107                              const char *base_name)
108 {
109     if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName
110         && !shash_find(tables, base->u.uuid.refTableName)) {
111         return ovsdb_syntax_error(NULL, NULL,
112                                   "column %s %s refers to undefined table %s",
113                                   column->name, base_name,
114                                   base->u.uuid.refTableName);
115     } else {
116         return NULL;
117     }
118 }
119
120 static bool
121 is_valid_version(const char *s)
122 {
123     int n = -1;
124     sscanf(s, "%*[0-9].%*[0-9].%*[0-9]%n", &n);
125     return n != -1 && s[n] == '\0';
126 }
127
128 struct ovsdb_error *
129 ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
130 {
131     struct ovsdb_schema *schema;
132     const struct json *name, *tables, *version_json;
133     struct ovsdb_error *error;
134     struct shash_node *node;
135     struct ovsdb_parser parser;
136     const char *version;
137
138     *schemap = NULL;
139
140     ovsdb_parser_init(&parser, json, "database schema");
141     name = ovsdb_parser_member(&parser, "name", OP_ID);
142     version_json = ovsdb_parser_member(&parser, "version",
143                                        OP_STRING | OP_OPTIONAL);
144     ovsdb_parser_member(&parser, "cksum", OP_STRING | OP_OPTIONAL);
145     tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
146     error = ovsdb_parser_finish(&parser);
147     if (error) {
148         return error;
149     }
150
151     if (version_json) {
152         version = json_string(version_json);
153         if (!is_valid_version(version)) {
154             return ovsdb_syntax_error(json, NULL, "schema version \"%s\" not "
155                                       "in format x.y.z", version);
156         }
157     } else {
158         /* Backward compatibility with old databases. */
159         version = "";
160     }
161
162     schema = ovsdb_schema_create(json_string(name), version);
163     SHASH_FOR_EACH (node, json_object(tables)) {
164         struct ovsdb_table_schema *table;
165
166         if (node->name[0] == '_') {
167             error = ovsdb_syntax_error(json, NULL, "names beginning with "
168                                        "\"_\" are reserved");
169         } else if (!ovsdb_parser_is_id(node->name)) {
170             error = ovsdb_syntax_error(json, NULL, "name must be a valid id");
171         } else {
172             error = ovsdb_table_schema_from_json(node->data, node->name,
173                                                  &table);
174         }
175         if (error) {
176             ovsdb_schema_destroy(schema);
177             return error;
178         }
179
180         shash_add(&schema->tables, table->name, table);
181     }
182
183     /* Validate that all refTables refer to the names of tables that exist. */
184     SHASH_FOR_EACH (node, &schema->tables) {
185         struct ovsdb_table_schema *table = node->data;
186         struct shash_node *node2;
187
188         SHASH_FOR_EACH (node2, &table->columns) {
189             struct ovsdb_column *column = node2->data;
190
191             error = ovsdb_schema_check_ref_table(column, &schema->tables,
192                                                  &column->type.key, "key");
193             if (!error) {
194                 error = ovsdb_schema_check_ref_table(column, &schema->tables,
195                                                      &column->type.value,
196                                                      "value");
197             }
198             if (error) {
199                 ovsdb_schema_destroy(schema);
200                 return error;
201             }
202         }
203     }
204
205     *schemap = schema;
206     return 0;
207 }
208
209 struct json *
210 ovsdb_schema_to_json(const struct ovsdb_schema *schema)
211 {
212     struct json *json, *tables;
213     struct shash_node *node;
214
215     json = json_object_create();
216     json_object_put_string(json, "name", schema->name);
217     if (schema->version[0]) {
218         json_object_put_string(json, "version", schema->version);
219     }
220
221     tables = json_object_create();
222
223     SHASH_FOR_EACH (node, &schema->tables) {
224         struct ovsdb_table_schema *table = node->data;
225         json_object_put(tables, table->name,
226                         ovsdb_table_schema_to_json(table));
227     }
228     json_object_put(json, "tables", tables);
229
230     return json;
231 }
232 \f
233 static void
234 ovsdb_set_ref_table(const struct shash *tables,
235                     struct ovsdb_base_type *base)
236 {
237     if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) {
238         struct ovsdb_table *table;
239
240         table = shash_find_data(tables, base->u.uuid.refTableName);
241         base->u.uuid.refTable = table;
242     }
243 }
244
245 struct ovsdb *
246 ovsdb_create(struct ovsdb_schema *schema)
247 {
248     struct shash_node *node;
249     struct ovsdb *db;
250
251     db = xmalloc(sizeof *db);
252     db->schema = schema;
253     list_init(&db->replicas);
254     list_init(&db->triggers);
255     db->run_triggers = false;
256
257     shash_init(&db->tables);
258     SHASH_FOR_EACH (node, &schema->tables) {
259         struct ovsdb_table_schema *ts = node->data;
260         shash_add(&db->tables, node->name, ovsdb_table_create(ts));
261     }
262
263     /* Set all the refTables. */
264     SHASH_FOR_EACH (node, &schema->tables) {
265         struct ovsdb_table_schema *table = node->data;
266         struct shash_node *node2;
267
268         SHASH_FOR_EACH (node2, &table->columns) {
269             struct ovsdb_column *column = node2->data;
270
271             ovsdb_set_ref_table(&db->tables, &column->type.key);
272             ovsdb_set_ref_table(&db->tables, &column->type.value);
273         }
274     }
275
276     return db;
277 }
278
279 void
280 ovsdb_destroy(struct ovsdb *db)
281 {
282     if (db) {
283         struct shash_node *node;
284
285         /* Remove all the replicas. */
286         while (!list_is_empty(&db->replicas)) {
287             struct ovsdb_replica *r
288                 = CONTAINER_OF(list_pop_back(&db->replicas),
289                                struct ovsdb_replica, node);
290             ovsdb_remove_replica(db, r);
291         }
292
293         /* Delete all the tables.  This also deletes their schemas. */
294         SHASH_FOR_EACH (node, &db->tables) {
295             struct ovsdb_table *table = node->data;
296             ovsdb_table_destroy(table);
297         }
298         shash_destroy(&db->tables);
299
300         /* The schemas, but not the table that points to them, were deleted in
301          * the previous step, so we need to clear out the table.  We can't
302          * destroy the table, because ovsdb_schema_destroy() will do that. */
303         shash_clear(&db->schema->tables);
304
305         ovsdb_schema_destroy(db->schema);
306         free(db);
307     }
308 }
309
310 struct ovsdb_table *
311 ovsdb_get_table(const struct ovsdb *db, const char *name)
312 {
313     return shash_find_data(&db->tables, name);
314 }
315 \f
316 void
317 ovsdb_replica_init(struct ovsdb_replica *r,
318                    const struct ovsdb_replica_class *class)
319 {
320     r->class = class;
321 }
322
323 void
324 ovsdb_add_replica(struct ovsdb *db, struct ovsdb_replica *r)
325 {
326     list_push_back(&db->replicas, &r->node);
327 }
328
329 void
330 ovsdb_remove_replica(struct ovsdb *db OVS_UNUSED, struct ovsdb_replica *r)
331 {
332     list_remove(&r->node);
333     (r->class->destroy)(r);
334 }