]>
git.proxmox.com Git - mirror_ovs.git/blob - ovsdb/file.c
1 /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2016, 2017 Nicira, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
27 #include "openvswitch/json.h"
30 #include "ovsdb-error.h"
32 #include "socket-util.h"
36 #include "transaction.h"
39 #include "openvswitch/vlog.h"
41 VLOG_DEFINE_THIS_MODULE(ovsdb_file
);
43 /* A transaction being converted to JSON for writing to a file. */
44 struct ovsdb_file_txn
{
45 struct json
*json
; /* JSON for the whole transaction. */
46 struct json
*table_json
; /* JSON for 'table''s transaction. */
47 struct ovsdb_table
*table
; /* Table described in 'table_json'. */
50 static void ovsdb_file_txn_init(struct ovsdb_file_txn
*);
51 static void ovsdb_file_txn_add_row(struct ovsdb_file_txn
*,
52 const struct ovsdb_row
*old
,
53 const struct ovsdb_row
*new,
54 const unsigned long int *changed
);
56 static struct ovsdb_error
*
57 ovsdb_file_update_row_from_json(struct ovsdb_row
*row
, bool converting
,
58 const struct json
*json
)
60 struct ovsdb_table_schema
*schema
= row
->table
->schema
;
61 struct ovsdb_error
*error
;
62 struct shash_node
*node
;
64 if (json
->type
!= JSON_OBJECT
) {
65 return ovsdb_syntax_error(json
, NULL
, "row must be JSON object");
68 SHASH_FOR_EACH (node
, json_object(json
)) {
69 const char *column_name
= node
->name
;
70 const struct ovsdb_column
*column
;
71 struct ovsdb_datum datum
;
73 column
= ovsdb_table_schema_get_column(schema
, column_name
);
78 return ovsdb_syntax_error(json
, "unknown column",
79 "No column %s in table %s.",
80 column_name
, schema
->name
);
83 error
= ovsdb_datum_from_json(&datum
, &column
->type
, node
->data
, NULL
);
87 ovsdb_datum_swap(&row
->fields
[column
->index
], &datum
);
88 ovsdb_datum_destroy(&datum
, &column
->type
);
94 static struct ovsdb_error
*
95 ovsdb_file_txn_row_from_json(struct ovsdb_txn
*txn
, struct ovsdb_table
*table
,
97 const struct uuid
*row_uuid
, struct json
*json
)
99 const struct ovsdb_row
*row
= ovsdb_table_get_row(table
, row_uuid
);
100 if (json
->type
== JSON_NULL
) {
102 return ovsdb_syntax_error(NULL
, NULL
, "transaction deletes "
103 "row "UUID_FMT
" that does not exist",
104 UUID_ARGS(row_uuid
));
106 ovsdb_txn_row_delete(txn
, row
);
109 return ovsdb_file_update_row_from_json(ovsdb_txn_row_modify(txn
, row
),
112 struct ovsdb_error
*error
;
113 struct ovsdb_row
*new;
115 new = ovsdb_row_create(table
);
116 *ovsdb_row_get_uuid_rw(new) = *row_uuid
;
117 error
= ovsdb_file_update_row_from_json(new, converting
, json
);
119 ovsdb_row_destroy(new);
121 ovsdb_txn_row_insert(txn
, new);
127 static struct ovsdb_error
*
128 ovsdb_file_txn_table_from_json(struct ovsdb_txn
*txn
,
129 struct ovsdb_table
*table
,
130 bool converting
, struct json
*json
)
132 struct shash_node
*node
;
134 if (json
->type
!= JSON_OBJECT
) {
135 return ovsdb_syntax_error(json
, NULL
, "object expected");
138 SHASH_FOR_EACH (node
, json
->object
) {
139 const char *uuid_string
= node
->name
;
140 struct json
*txn_row_json
= node
->data
;
141 struct ovsdb_error
*error
;
142 struct uuid row_uuid
;
144 if (!uuid_from_string(&row_uuid
, uuid_string
)) {
145 return ovsdb_syntax_error(json
, NULL
, "\"%s\" is not a valid UUID",
149 error
= ovsdb_file_txn_row_from_json(txn
, table
, converting
,
150 &row_uuid
, txn_row_json
);
159 /* Converts 'json' to an ovsdb_txn for 'db', storing the new transaction in
160 * '*txnp'. Returns NULL if successful, otherwise an error.
162 * If 'converting' is true, then unknown table and column names are ignored
163 * (which can ease upgrading and downgrading schemas); otherwise, they are
164 * treated as errors. */
166 ovsdb_file_txn_from_json(struct ovsdb
*db
, const struct json
*json
,
167 bool converting
, struct ovsdb_txn
**txnp
)
169 struct ovsdb_error
*error
;
170 struct shash_node
*node
;
171 struct ovsdb_txn
*txn
;
175 if (json
->type
!= JSON_OBJECT
) {
176 return ovsdb_syntax_error(json
, NULL
, "object expected");
179 txn
= ovsdb_txn_create(db
);
180 SHASH_FOR_EACH (node
, json
->object
) {
181 const char *table_name
= node
->name
;
182 struct json
*node_json
= node
->data
;
183 struct ovsdb_table
*table
;
185 table
= shash_find_data(&db
->tables
, table_name
);
187 if (!strcmp(table_name
, "_date")
188 && node_json
->type
== JSON_INTEGER
) {
190 } else if (!strcmp(table_name
, "_comment") || converting
) {
194 error
= ovsdb_syntax_error(json
, "unknown table",
195 "No table named %s.", table_name
);
199 error
= ovsdb_file_txn_table_from_json(txn
, table
, converting
,
209 ovsdb_txn_abort(txn
);
213 static struct ovsdb_error
* OVS_WARN_UNUSED_RESULT
214 ovsdb_convert_table(struct ovsdb_txn
*txn
,
215 const struct ovsdb_table
*src_table
,
216 struct ovsdb_table
*dst_table
)
218 const struct ovsdb_row
*src_row
;
219 HMAP_FOR_EACH (src_row
, hmap_node
, &src_table
->rows
) {
220 struct ovsdb_row
*dst_row
= ovsdb_row_create(dst_table
);
221 *ovsdb_row_get_uuid_rw(dst_row
) = *ovsdb_row_get_uuid(src_row
);
223 struct shash_node
*node
;
224 SHASH_FOR_EACH (node
, &src_table
->schema
->columns
) {
225 const struct ovsdb_column
*src_column
= node
->data
;
226 if (src_column
->index
== OVSDB_COL_UUID
||
227 src_column
->index
== OVSDB_COL_VERSION
) {
231 const struct ovsdb_column
*dst_column
232 = shash_find_data(&dst_table
->schema
->columns
,
238 struct ovsdb_error
*error
= ovsdb_datum_convert(
239 &dst_row
->fields
[dst_column
->index
], &dst_column
->type
,
240 &src_row
->fields
[src_column
->index
], &src_column
->type
);
242 ovsdb_row_destroy(dst_row
);
247 ovsdb_txn_row_insert(txn
, dst_row
);
252 /* Copies the data in 'src', converts it into the schema specified in
253 * 'new_schema', and puts it into a newly created, unbacked database, and
254 * stores a pointer to the new database in '*dstp'. Returns null if
255 * successful, otherwise an error; on error, stores NULL in '*dstp'. */
256 struct ovsdb_error
* OVS_WARN_UNUSED_RESULT
257 ovsdb_convert(const struct ovsdb
*src
, const struct ovsdb_schema
*new_schema
,
260 struct ovsdb
*dst
= ovsdb_create(ovsdb_schema_clone(new_schema
),
261 ovsdb_storage_create_unbacked());
262 struct ovsdb_txn
*txn
= ovsdb_txn_create(dst
);
263 struct ovsdb_error
*error
= NULL
;
265 struct shash_node
*node
;
266 SHASH_FOR_EACH (node
, &src
->tables
) {
267 const char *table_name
= node
->name
;
268 struct ovsdb_table
*src_table
= node
->data
;
269 struct ovsdb_table
*dst_table
= shash_find_data(&dst
->tables
,
275 error
= ovsdb_convert_table(txn
, src_table
, dst_table
);
281 error
= ovsdb_txn_replay_commit(txn
);
283 txn
= NULL
; /* ovsdb_txn_replay_commit() already aborted. */
293 ovsdb_txn_abort(txn
);
300 ovsdb_file_change_cb(const struct ovsdb_row
*old
,
301 const struct ovsdb_row
*new,
302 const unsigned long int *changed
,
305 struct ovsdb_file_txn
*ftxn
= ftxn_
;
306 ovsdb_file_txn_add_row(ftxn
, old
, new, changed
);
311 ovsdb_to_txn_json(const struct ovsdb
*db
, const char *comment
)
313 struct ovsdb_file_txn ftxn
;
315 ovsdb_file_txn_init(&ftxn
);
317 struct shash_node
*node
;
318 SHASH_FOR_EACH (node
, &db
->tables
) {
319 const struct ovsdb_table
*table
= node
->data
;
320 const struct ovsdb_row
*row
;
322 HMAP_FOR_EACH (row
, hmap_node
, &table
->rows
) {
323 ovsdb_file_txn_add_row(&ftxn
, NULL
, row
, NULL
);
327 return ovsdb_file_txn_annotate(ftxn
.json
, comment
);
330 /* Returns 'txn' transformed into the JSON format that is used in OVSDB files.
331 * (But the caller must use ovsdb_file_txn_annotate() to add the _comment and
332 * _date members.) If 'txn' doesn't actually change anything, returns NULL */
334 ovsdb_file_txn_to_json(const struct ovsdb_txn
*txn
)
336 struct ovsdb_file_txn ftxn
;
338 ovsdb_file_txn_init(&ftxn
);
339 ovsdb_txn_for_each_change(txn
, ovsdb_file_change_cb
, &ftxn
);
344 ovsdb_file_txn_annotate(struct json
*json
, const char *comment
)
347 json
= json_object_create();
350 json_object_put_string(json
, "_comment", comment
);
352 json_object_put(json
, "_date", json_integer_create(time_wall_msec()));
357 ovsdb_file_txn_init(struct ovsdb_file_txn
*ftxn
)
360 ftxn
->table_json
= NULL
;
365 ovsdb_file_txn_add_row(struct ovsdb_file_txn
*ftxn
,
366 const struct ovsdb_row
*old
,
367 const struct ovsdb_row
*new,
368 const unsigned long int *changed
)
373 row
= json_null_create();
375 struct shash_node
*node
;
377 row
= old
? NULL
: json_object_create();
378 SHASH_FOR_EACH (node
, &new->table
->schema
->columns
) {
379 const struct ovsdb_column
*column
= node
->data
;
380 const struct ovsdb_type
*type
= &column
->type
;
381 unsigned int idx
= column
->index
;
383 if (idx
!= OVSDB_COL_UUID
&& column
->persistent
385 ? bitmap_is_set(changed
, idx
)
386 : !ovsdb_datum_is_default(&new->fields
[idx
], type
)))
389 row
= json_object_create();
391 json_object_put(row
, column
->name
,
392 ovsdb_datum_to_json(&new->fields
[idx
], type
));
398 struct ovsdb_table
*table
= new ? new->table
: old
->table
;
399 char uuid
[UUID_LEN
+ 1];
401 if (table
!= ftxn
->table
) {
402 /* Create JSON object for transaction overall. */
404 ftxn
->json
= json_object_create();
407 /* Create JSON object for transaction on this table. */
408 ftxn
->table_json
= json_object_create();
410 json_object_put(ftxn
->json
, table
->schema
->name
, ftxn
->table_json
);
413 /* Add row to transaction for this table. */
414 snprintf(uuid
, sizeof uuid
,
415 UUID_FMT
, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old
)));
416 json_object_put(ftxn
->table_json
, uuid
, row
);
420 static struct ovsdb
*
421 ovsdb_file_read__(const char *filename
, bool rw
,
422 struct ovsdb_schema
*new_schema
)
424 struct ovsdb_storage
*storage
= ovsdb_storage_open_standalone(filename
,
426 struct ovsdb_schema
*schema
= ovsdb_storage_read_schema(storage
);
428 ovsdb_schema_destroy(schema
);
431 struct ovsdb
*ovsdb
= ovsdb_create(schema
, storage
);
433 /* Read a transaction. Bail if end-of-file. */
434 struct json
*txn_json
;
435 struct ovsdb_schema
*schema2
;
436 struct ovsdb_error
*error
= ovsdb_storage_read(storage
, &schema2
,
439 ovs_fatal(0, "%s", ovsdb_error_to_string_free(error
));
441 ovs_assert(!schema2
);
446 /* Apply transaction to database. */
447 struct ovsdb_txn
*txn
;
448 error
= ovsdb_file_txn_from_json(ovsdb
, txn_json
, new_schema
!= NULL
,
451 ovs_fatal(0, "%s", ovsdb_error_to_string_free(error
));
453 json_destroy(txn_json
);
455 error
= ovsdb_txn_replay_commit(txn
);
457 ovsdb_storage_unread(storage
);
464 /* Reads 'filename' as a standalone database. Returns the new database. On
465 * error, prints a message on stderr and terminates the process.
467 * If 'rw' is true, opens the database for read/write access, otherwise
470 * Consumes 'schema'. */
472 ovsdb_file_read(const char *filename
, bool rw
)
474 return ovsdb_file_read__(filename
, rw
, NULL
);
477 /* Reads 'filename' as a standalone database, using 'schema' in place of the
478 * schema embedded in the file. Returns the new database. On error,
479 * prints a message on stderr and terminates the process.
481 * Consumes 'schema'. */
483 ovsdb_file_read_as_schema(const char *filename
, struct ovsdb_schema
*schema
)
485 return ovsdb_file_read__(filename
, false, schema
);