]>
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 ovsdb_datum_destroy(&dst_row
->fields
[dst_column
->index
],
241 struct ovsdb_error
*error
= ovsdb_datum_convert(
242 &dst_row
->fields
[dst_column
->index
], &dst_column
->type
,
243 &src_row
->fields
[src_column
->index
], &src_column
->type
);
245 ovsdb_datum_init_empty(&dst_row
->fields
[dst_column
->index
]);
246 ovsdb_row_destroy(dst_row
);
251 ovsdb_txn_row_insert(txn
, dst_row
);
256 /* Copies the data in 'src', converts it into the schema specified in
257 * 'new_schema', and puts it into a newly created, unbacked database, and
258 * stores a pointer to the new database in '*dstp'. Returns null if
259 * successful, otherwise an error; on error, stores NULL in '*dstp'. */
260 struct ovsdb_error
* OVS_WARN_UNUSED_RESULT
261 ovsdb_convert(const struct ovsdb
*src
, const struct ovsdb_schema
*new_schema
,
264 struct ovsdb
*dst
= ovsdb_create(ovsdb_schema_clone(new_schema
),
265 ovsdb_storage_create_unbacked());
266 struct ovsdb_txn
*txn
= ovsdb_txn_create(dst
);
267 struct ovsdb_error
*error
= NULL
;
269 struct shash_node
*node
;
270 SHASH_FOR_EACH (node
, &src
->tables
) {
271 const char *table_name
= node
->name
;
272 struct ovsdb_table
*src_table
= node
->data
;
273 struct ovsdb_table
*dst_table
= shash_find_data(&dst
->tables
,
279 error
= ovsdb_convert_table(txn
, src_table
, dst_table
);
285 error
= ovsdb_txn_replay_commit(txn
);
287 txn
= NULL
; /* ovsdb_txn_replay_commit() already aborted. */
297 ovsdb_txn_abort(txn
);
304 ovsdb_file_change_cb(const struct ovsdb_row
*old
,
305 const struct ovsdb_row
*new,
306 const unsigned long int *changed
,
309 struct ovsdb_file_txn
*ftxn
= ftxn_
;
310 ovsdb_file_txn_add_row(ftxn
, old
, new, changed
);
315 ovsdb_to_txn_json(const struct ovsdb
*db
, const char *comment
)
317 struct ovsdb_file_txn ftxn
;
319 ovsdb_file_txn_init(&ftxn
);
321 struct shash_node
*node
;
322 SHASH_FOR_EACH (node
, &db
->tables
) {
323 const struct ovsdb_table
*table
= node
->data
;
324 const struct ovsdb_row
*row
;
326 HMAP_FOR_EACH (row
, hmap_node
, &table
->rows
) {
327 ovsdb_file_txn_add_row(&ftxn
, NULL
, row
, NULL
);
331 return ovsdb_file_txn_annotate(ftxn
.json
, comment
);
334 /* Returns 'txn' transformed into the JSON format that is used in OVSDB files.
335 * (But the caller must use ovsdb_file_txn_annotate() to add the _comment and
336 * _date members.) If 'txn' doesn't actually change anything, returns NULL */
338 ovsdb_file_txn_to_json(const struct ovsdb_txn
*txn
)
340 struct ovsdb_file_txn ftxn
;
342 ovsdb_file_txn_init(&ftxn
);
343 ovsdb_txn_for_each_change(txn
, ovsdb_file_change_cb
, &ftxn
);
348 ovsdb_file_txn_annotate(struct json
*json
, const char *comment
)
351 json
= json_object_create();
354 json_object_put_string(json
, "_comment", comment
);
356 json_object_put(json
, "_date", json_integer_create(time_wall_msec()));
361 ovsdb_file_txn_init(struct ovsdb_file_txn
*ftxn
)
364 ftxn
->table_json
= NULL
;
369 ovsdb_file_txn_add_row(struct ovsdb_file_txn
*ftxn
,
370 const struct ovsdb_row
*old
,
371 const struct ovsdb_row
*new,
372 const unsigned long int *changed
)
377 row
= json_null_create();
379 struct shash_node
*node
;
381 row
= old
? NULL
: json_object_create();
382 SHASH_FOR_EACH (node
, &new->table
->schema
->columns
) {
383 const struct ovsdb_column
*column
= node
->data
;
384 const struct ovsdb_type
*type
= &column
->type
;
385 unsigned int idx
= column
->index
;
387 if (idx
!= OVSDB_COL_UUID
&& column
->persistent
389 ? bitmap_is_set(changed
, idx
)
390 : !ovsdb_datum_is_default(&new->fields
[idx
], type
)))
393 row
= json_object_create();
395 json_object_put(row
, column
->name
,
396 ovsdb_datum_to_json(&new->fields
[idx
], type
));
402 struct ovsdb_table
*table
= new ? new->table
: old
->table
;
403 char uuid
[UUID_LEN
+ 1];
405 if (table
!= ftxn
->table
) {
406 /* Create JSON object for transaction overall. */
408 ftxn
->json
= json_object_create();
411 /* Create JSON object for transaction on this table. */
412 ftxn
->table_json
= json_object_create();
414 json_object_put(ftxn
->json
, table
->schema
->name
, ftxn
->table_json
);
417 /* Add row to transaction for this table. */
418 snprintf(uuid
, sizeof uuid
,
419 UUID_FMT
, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old
)));
420 json_object_put(ftxn
->table_json
, uuid
, row
);
424 static struct ovsdb
*
425 ovsdb_file_read__(const char *filename
, bool rw
,
426 struct ovsdb_schema
*new_schema
)
428 struct ovsdb_storage
*storage
= ovsdb_storage_open_standalone(filename
,
430 struct ovsdb_schema
*schema
= ovsdb_storage_read_schema(storage
);
432 ovsdb_schema_destroy(schema
);
435 struct ovsdb
*ovsdb
= ovsdb_create(schema
, storage
);
437 /* Read a transaction. Bail if end-of-file. */
438 struct json
*txn_json
;
439 struct ovsdb_schema
*schema2
;
440 struct ovsdb_error
*error
= ovsdb_storage_read(storage
, &schema2
,
443 ovs_fatal(0, "%s", ovsdb_error_to_string_free(error
));
445 ovs_assert(!schema2
);
450 /* Apply transaction to database. */
451 struct ovsdb_txn
*txn
;
452 error
= ovsdb_file_txn_from_json(ovsdb
, txn_json
, new_schema
!= NULL
,
455 ovs_fatal(0, "%s", ovsdb_error_to_string_free(error
));
457 json_destroy(txn_json
);
459 error
= ovsdb_txn_replay_commit(txn
);
461 ovsdb_storage_unread(storage
);
468 /* Reads 'filename' as a standalone database. Returns the new database. On
469 * error, prints a message on stderr and terminates the process.
471 * If 'rw' is true, opens the database for read/write access, otherwise
474 * Consumes 'schema'. */
476 ovsdb_file_read(const char *filename
, bool rw
)
478 return ovsdb_file_read__(filename
, rw
, NULL
);
481 /* Reads 'filename' as a standalone database, using 'schema' in place of the
482 * schema embedded in the file. Returns the new database. On error,
483 * prints a message on stderr and terminates the process.
485 * Consumes 'schema'. */
487 ovsdb_file_read_as_schema(const char *filename
, struct ovsdb_schema
*schema
)
489 return ovsdb_file_read__(filename
, false, schema
);