]>
Commit | Line | Data |
---|---|---|
bd06962a BP |
1 | /* Copyright (c) 2009 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 "file.h" | |
19 | ||
20 | #include <assert.h> | |
21 | #include <fcntl.h> | |
22 | ||
23 | #include "column.h" | |
24 | #include "log.h" | |
25 | #include "json.h" | |
26 | #include "ovsdb.h" | |
27 | #include "ovsdb-error.h" | |
28 | #include "row.h" | |
29 | #include "table.h" | |
d171b584 | 30 | #include "timeval.h" |
bd06962a BP |
31 | #include "transaction.h" |
32 | #include "uuid.h" | |
33 | #include "util.h" | |
34 | ||
35 | #define THIS_MODULE VLM_ovsdb_file | |
36 | #include "vlog.h" | |
37 | ||
38 | static struct ovsdb_error *ovsdb_file_txn_from_json(struct ovsdb *, | |
39 | const struct json *, | |
40 | struct ovsdb_txn **); | |
41 | static void ovsdb_file_replica_create(struct ovsdb *, struct ovsdb_log *); | |
42 | ||
43 | struct ovsdb_error * | |
44 | ovsdb_file_open(const char *file_name, bool read_only, struct ovsdb **dbp) | |
45 | { | |
46 | struct ovsdb_schema *schema; | |
47 | struct ovsdb_error *error; | |
48 | struct ovsdb_log *log; | |
49 | struct json *json; | |
50 | struct ovsdb *db; | |
51 | ||
52 | error = ovsdb_log_open(file_name, read_only ? O_RDONLY : O_RDWR, &log); | |
53 | if (error) { | |
54 | return error; | |
55 | } | |
56 | ||
57 | error = ovsdb_log_read(log, &json); | |
58 | if (error) { | |
59 | return error; | |
60 | } else if (!json) { | |
61 | return ovsdb_io_error(EOF, "%s: database file contains no schema", | |
62 | file_name); | |
63 | } | |
64 | ||
65 | error = ovsdb_schema_from_json(json, &schema); | |
66 | if (error) { | |
67 | json_destroy(json); | |
68 | return ovsdb_wrap_error(error, | |
69 | "failed to parse \"%s\" as ovsdb schema", | |
70 | file_name); | |
71 | } | |
72 | json_destroy(json); | |
73 | ||
74 | db = ovsdb_create(schema); | |
75 | while ((error = ovsdb_log_read(log, &json)) == NULL && json) { | |
76 | struct ovsdb_txn *txn; | |
77 | ||
78 | error = ovsdb_file_txn_from_json(db, json, &txn); | |
79 | json_destroy(json); | |
80 | if (error) { | |
81 | break; | |
82 | } | |
83 | ||
84 | ovsdb_txn_commit(txn, false); | |
85 | } | |
86 | if (error) { | |
87 | char *msg = ovsdb_error_to_string(error); | |
88 | VLOG_WARN("%s", msg); | |
89 | free(msg); | |
90 | ||
91 | ovsdb_error_destroy(error); | |
92 | } | |
93 | ||
94 | if (!read_only) { | |
95 | ovsdb_file_replica_create(db, log); | |
96 | } else { | |
97 | ovsdb_log_close(log); | |
98 | } | |
99 | ||
100 | *dbp = db; | |
101 | return NULL; | |
102 | } | |
103 | ||
104 | static struct ovsdb_error * | |
105 | ovsdb_file_txn_row_from_json(struct ovsdb_txn *txn, struct ovsdb_table *table, | |
106 | const struct uuid *row_uuid, struct json *json) | |
107 | { | |
108 | const struct ovsdb_row *row = ovsdb_table_get_row(table, row_uuid); | |
109 | if (json->type == JSON_NULL) { | |
110 | if (!row) { | |
111 | return ovsdb_syntax_error(NULL, NULL, "transaction deletes " | |
112 | "row "UUID_FMT" that does not exist", | |
113 | UUID_ARGS(row_uuid)); | |
114 | } | |
115 | ovsdb_txn_row_delete(txn, row); | |
116 | return NULL; | |
117 | } else if (row) { | |
118 | return ovsdb_row_from_json(ovsdb_txn_row_modify(txn, row), | |
119 | json, NULL, NULL); | |
120 | } else { | |
121 | struct ovsdb_error *error; | |
122 | struct ovsdb_row *new; | |
123 | ||
124 | new = ovsdb_row_create(table); | |
125 | *ovsdb_row_get_uuid_rw(new) = *row_uuid; | |
126 | error = ovsdb_row_from_json(new, json, NULL, NULL); | |
127 | if (error) { | |
128 | ovsdb_row_destroy(new); | |
129 | } | |
130 | ||
131 | ovsdb_txn_row_insert(txn, new); | |
132 | ||
133 | return error; | |
134 | } | |
135 | } | |
136 | ||
137 | static struct ovsdb_error * | |
138 | ovsdb_file_txn_table_from_json(struct ovsdb_txn *txn, | |
139 | struct ovsdb_table *table, struct json *json) | |
140 | { | |
141 | struct shash_node *node; | |
142 | ||
143 | if (json->type != JSON_OBJECT) { | |
144 | return ovsdb_syntax_error(json, NULL, "object expected"); | |
145 | } | |
146 | ||
147 | SHASH_FOR_EACH (node, json->u.object) { | |
148 | const char *uuid_string = node->name; | |
149 | struct json *txn_row_json = node->data; | |
150 | struct ovsdb_error *error; | |
151 | struct uuid row_uuid; | |
152 | ||
153 | if (!uuid_from_string(&row_uuid, uuid_string)) { | |
154 | return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID", | |
155 | uuid_string); | |
156 | } | |
157 | ||
158 | error = ovsdb_file_txn_row_from_json(txn, table, &row_uuid, | |
159 | txn_row_json); | |
160 | if (error) { | |
161 | return error; | |
162 | } | |
163 | } | |
164 | ||
165 | return NULL; | |
166 | } | |
167 | ||
168 | static struct ovsdb_error * | |
169 | ovsdb_file_txn_from_json(struct ovsdb *db, const struct json *json, | |
170 | struct ovsdb_txn **txnp) | |
171 | { | |
172 | struct ovsdb_error *error; | |
173 | struct shash_node *node; | |
174 | struct ovsdb_txn *txn; | |
175 | ||
176 | *txnp = NULL; | |
177 | if (json->type != JSON_OBJECT) { | |
178 | return ovsdb_syntax_error(json, NULL, "object expected"); | |
179 | } | |
180 | ||
181 | txn = ovsdb_txn_create(db); | |
182 | SHASH_FOR_EACH (node, json->u.object) { | |
183 | const char *table_name = node->name; | |
184 | struct json *txn_table_json = node->data; | |
185 | struct ovsdb_table *table; | |
186 | ||
187 | table = shash_find_data(&db->tables, table_name); | |
188 | if (!table) { | |
d171b584 BP |
189 | if (!strcmp(table_name, "_date") |
190 | || !strcmp(table_name, "_comment")) { | |
191 | continue; | |
192 | } | |
193 | ||
bd06962a BP |
194 | error = ovsdb_syntax_error(json, "unknown table", |
195 | "No table named %s.", table_name); | |
196 | goto error; | |
197 | } | |
198 | ||
199 | error = ovsdb_file_txn_table_from_json(txn, table, txn_table_json); | |
200 | if (error) { | |
201 | goto error; | |
202 | } | |
203 | } | |
204 | *txnp = txn; | |
205 | return NULL; | |
206 | ||
207 | error: | |
208 | ovsdb_txn_abort(txn); | |
209 | return error; | |
210 | } | |
211 | \f | |
212 | /* Replica implementation. */ | |
213 | ||
214 | struct ovsdb_file_replica { | |
215 | struct ovsdb_replica replica; | |
216 | struct ovsdb_log *log; | |
217 | }; | |
218 | ||
219 | static const struct ovsdb_replica_class ovsdb_file_replica_class; | |
220 | ||
221 | static void | |
222 | ovsdb_file_replica_create(struct ovsdb *db, struct ovsdb_log *log) | |
223 | { | |
224 | struct ovsdb_file_replica *r = xmalloc(sizeof *r); | |
225 | ovsdb_replica_init(&r->replica, &ovsdb_file_replica_class); | |
226 | r->log = log; | |
227 | ovsdb_add_replica(db, &r->replica); | |
228 | ||
229 | } | |
230 | ||
231 | static struct ovsdb_file_replica * | |
232 | ovsdb_file_replica_cast(struct ovsdb_replica *replica) | |
233 | { | |
234 | assert(replica->class == &ovsdb_file_replica_class); | |
235 | return CONTAINER_OF(replica, struct ovsdb_file_replica, replica); | |
236 | } | |
237 | ||
238 | struct ovsdb_file_replica_aux { | |
239 | struct json *json; /* JSON for the whole transaction. */ | |
240 | struct json *table_json; /* JSON for 'table''s transaction. */ | |
241 | struct ovsdb_table *table; /* Table described in 'table_json'. */ | |
242 | }; | |
243 | ||
244 | static bool | |
245 | ovsdb_file_replica_change_cb(const struct ovsdb_row *old, | |
246 | const struct ovsdb_row *new, | |
247 | void *aux_) | |
248 | { | |
249 | struct ovsdb_file_replica_aux *aux = aux_; | |
250 | struct json *row; | |
251 | ||
252 | if (!new) { | |
253 | row = json_null_create(); | |
254 | } else { | |
255 | struct shash_node *node; | |
256 | ||
257 | row = NULL; | |
258 | SHASH_FOR_EACH (node, &new->table->schema->columns) { | |
259 | const struct ovsdb_column *column = node->data; | |
260 | const struct ovsdb_type *type = &column->type; | |
261 | unsigned int idx = column->index; | |
262 | ||
263 | if (idx != OVSDB_COL_UUID && column->persistent | |
264 | && (!old || !ovsdb_datum_equals(&old->fields[idx], | |
265 | &new->fields[idx], type))) | |
266 | { | |
267 | if (!row) { | |
268 | row = json_object_create(); | |
269 | } | |
270 | json_object_put(row, column->name, | |
271 | ovsdb_datum_to_json(&new->fields[idx], type)); | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | if (row) { | |
277 | struct ovsdb_table *table = new ? new->table : old->table; | |
278 | char uuid[UUID_LEN + 1]; | |
279 | ||
280 | if (table != aux->table) { | |
281 | /* Create JSON object for transaction overall. */ | |
282 | if (!aux->json) { | |
283 | aux->json = json_object_create(); | |
284 | } | |
285 | ||
286 | /* Create JSON object for transaction on this table. */ | |
287 | aux->table_json = json_object_create(); | |
288 | aux->table = table; | |
289 | json_object_put(aux->json, table->schema->name, aux->table_json); | |
290 | } | |
291 | ||
292 | /* Add row to transaction for this table. */ | |
293 | snprintf(uuid, sizeof uuid, | |
294 | UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old))); | |
295 | json_object_put(aux->table_json, uuid, row); | |
296 | } | |
297 | ||
298 | return true; | |
299 | } | |
300 | ||
301 | static struct ovsdb_error * | |
302 | ovsdb_file_replica_commit(struct ovsdb_replica *r_, | |
303 | const struct ovsdb_txn *txn, bool durable) | |
304 | { | |
305 | struct ovsdb_file_replica *r = ovsdb_file_replica_cast(r_); | |
306 | struct ovsdb_file_replica_aux aux; | |
307 | struct ovsdb_error *error; | |
d171b584 | 308 | const char *comment; |
bd06962a BP |
309 | |
310 | aux.json = NULL; | |
311 | aux.table_json = NULL; | |
312 | aux.table = NULL; | |
313 | ovsdb_txn_for_each_change(txn, ovsdb_file_replica_change_cb, &aux); | |
314 | ||
315 | if (!aux.json) { | |
316 | /* Nothing to commit. */ | |
317 | return NULL; | |
318 | } | |
319 | ||
d171b584 BP |
320 | comment = ovsdb_txn_get_comment(txn); |
321 | if (comment) { | |
322 | json_object_put_string(aux.json, "_comment", comment); | |
323 | } | |
324 | ||
325 | json_object_put(aux.json, "_date", json_integer_create(time_now())); | |
326 | ||
bd06962a BP |
327 | error = ovsdb_log_write(r->log, aux.json); |
328 | json_destroy(aux.json); | |
329 | if (error) { | |
330 | return ovsdb_wrap_error(error, "writing transaction failed"); | |
331 | } | |
332 | ||
333 | if (durable) { | |
334 | error = ovsdb_log_commit(r->log); | |
335 | if (error) { | |
336 | return ovsdb_wrap_error(error, "committing transaction failed"); | |
337 | } | |
338 | } | |
339 | ||
340 | return NULL; | |
341 | } | |
342 | ||
343 | static void | |
344 | ovsdb_file_replica_destroy(struct ovsdb_replica *r_) | |
345 | { | |
346 | struct ovsdb_file_replica *r = ovsdb_file_replica_cast(r_); | |
347 | ||
348 | ovsdb_log_close(r->log); | |
349 | free(r); | |
350 | } | |
351 | ||
352 | static const struct ovsdb_replica_class ovsdb_file_replica_class = { | |
353 | ovsdb_file_replica_commit, | |
354 | ovsdb_file_replica_destroy | |
355 | }; |