]>
Commit | Line | Data |
---|---|---|
f85f8ebb | 1 | /* |
e1ebc8ce | 2 | * Copyright (c) 2009, 2010, 2011 Nicira Networks. |
f85f8ebb BP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | #include <errno.h> | |
19 | #include <fcntl.h> | |
20 | #include <getopt.h> | |
21 | #include <signal.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
24 | ||
25 | #include "command-line.h" | |
26 | #include "compiler.h" | |
bd06962a | 27 | #include "file.h" |
1e19e50e | 28 | #include "lockfile.h" |
41709ccc | 29 | #include "log.h" |
f85f8ebb BP |
30 | #include "json.h" |
31 | #include "ovsdb.h" | |
32 | #include "ovsdb-error.h" | |
1e19e50e | 33 | #include "socket-util.h" |
f85f8ebb BP |
34 | #include "table.h" |
35 | #include "timeval.h" | |
36 | #include "util.h" | |
f85f8ebb | 37 | #include "vlog.h" |
5136ce49 | 38 | |
d98e6007 | 39 | VLOG_DEFINE_THIS_MODULE(ovsdb_tool); |
f85f8ebb | 40 | |
c6782bb0 BP |
41 | /* -m, --more: Verbosity level for "show-log" command output. */ |
42 | static int show_log_verbosity; | |
43 | ||
f85f8ebb BP |
44 | static const struct command all_commands[]; |
45 | ||
46 | static void usage(void) NO_RETURN; | |
47 | static void parse_options(int argc, char *argv[]); | |
48 | ||
49 | int | |
50 | main(int argc, char *argv[]) | |
51 | { | |
52 | set_program_name(argv[0]); | |
f85f8ebb BP |
53 | parse_options(argc, argv); |
54 | signal(SIGPIPE, SIG_IGN); | |
55 | run_command(argc - optind, argv + optind, all_commands); | |
56 | return 0; | |
57 | } | |
58 | ||
59 | static void | |
60 | parse_options(int argc, char *argv[]) | |
61 | { | |
62 | static struct option long_options[] = { | |
e3c17733 BP |
63 | {"more", no_argument, NULL, 'm'}, |
64 | {"verbose", optional_argument, NULL, 'v'}, | |
65 | {"help", no_argument, NULL, 'h'}, | |
66 | {"version", no_argument, NULL, 'V'}, | |
67 | {NULL, 0, NULL, 0}, | |
f85f8ebb BP |
68 | }; |
69 | char *short_options = long_options_to_short_options(long_options); | |
70 | ||
71 | for (;;) { | |
72 | int c; | |
73 | ||
74 | c = getopt_long(argc, argv, short_options, long_options, NULL); | |
75 | if (c == -1) { | |
76 | break; | |
77 | } | |
78 | ||
79 | switch (c) { | |
c6782bb0 BP |
80 | case 'm': |
81 | show_log_verbosity++; | |
82 | break; | |
83 | ||
f85f8ebb BP |
84 | case 'h': |
85 | usage(); | |
86 | ||
87 | case 'V': | |
88 | OVS_PRINT_VERSION(0, 0); | |
89 | exit(EXIT_SUCCESS); | |
90 | ||
91 | case 'v': | |
92 | vlog_set_verbosity(optarg); | |
93 | break; | |
94 | ||
95 | case '?': | |
96 | exit(EXIT_FAILURE); | |
97 | ||
98 | default: | |
99 | abort(); | |
100 | } | |
101 | } | |
102 | free(short_options); | |
103 | } | |
104 | ||
105 | static void | |
106 | usage(void) | |
107 | { | |
108 | printf("%s: Open vSwitch database management utility\n" | |
109 | "usage: %s [OPTIONS] COMMAND [ARG...]\n" | |
110 | " create DB SCHEMA create DB with the given SCHEMA\n" | |
111 | " compact DB [DST] compact DB in-place (or to DST)\n" | |
1e19e50e | 112 | " convert DB SCHEMA [DST] convert DB to SCHEMA (to DST)\n" |
8159b984 | 113 | " db-version DB report version of schema used by DB\n" |
6aa09313 | 114 | " db-cksum DB report checksum of schema used by DB\n" |
8159b984 | 115 | " schema-version SCHEMA report SCHEMA's schema version\n" |
6aa09313 | 116 | " schema-cksum SCHEMA report SCHEMA's checksum\n" |
f85f8ebb | 117 | " query DB TRNS execute read-only transaction on DB\n" |
722f6301 BP |
118 | " transact DB TRNS execute read/write transaction on DB\n" |
119 | " show-log DB prints information about DB's log entries\n", | |
f85f8ebb BP |
120 | program_name, program_name); |
121 | vlog_usage(); | |
122 | printf("\nOther options:\n" | |
c6782bb0 | 123 | " -m, --more increase show-log verbosity\n" |
f85f8ebb BP |
124 | " -h, --help display this help message\n" |
125 | " -V, --version display version information\n"); | |
126 | exit(EXIT_SUCCESS); | |
127 | } | |
128 | \f | |
129 | static struct json * | |
130 | parse_json(const char *s) | |
131 | { | |
132 | struct json *json = json_from_string(s); | |
133 | if (json->type == JSON_STRING) { | |
134 | ovs_fatal(0, "\"%s\": %s", s, json->u.string); | |
135 | } | |
136 | return json; | |
137 | } | |
138 | ||
139 | static void | |
140 | print_and_free_json(struct json *json) | |
141 | { | |
142 | char *string = json_to_string(json, JSSF_SORT); | |
143 | json_destroy(json); | |
144 | puts(string); | |
145 | free(string); | |
146 | } | |
147 | ||
148 | static void | |
149 | check_ovsdb_error(struct ovsdb_error *error) | |
150 | { | |
151 | if (error) { | |
152 | ovs_fatal(0, "%s", ovsdb_error_to_string(error)); | |
153 | } | |
154 | } | |
155 | \f | |
156 | static void | |
c69ee87c | 157 | do_create(int argc OVS_UNUSED, char *argv[]) |
f85f8ebb BP |
158 | { |
159 | const char *db_file_name = argv[1]; | |
160 | const char *schema_file_name = argv[2]; | |
161 | struct ovsdb_schema *schema; | |
41709ccc | 162 | struct ovsdb_log *log; |
f85f8ebb BP |
163 | struct json *json; |
164 | ||
165 | /* Read schema from file and convert to JSON. */ | |
166 | check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema)); | |
167 | json = ovsdb_schema_to_json(schema); | |
90cc4071 | 168 | ovsdb_schema_destroy(schema); |
f85f8ebb BP |
169 | |
170 | /* Create database file. */ | |
7446f148 BP |
171 | check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_LOG_CREATE, |
172 | -1, &log)); | |
41709ccc BP |
173 | check_ovsdb_error(ovsdb_log_write(log, json)); |
174 | check_ovsdb_error(ovsdb_log_commit(log)); | |
175 | ovsdb_log_close(log); | |
f85f8ebb BP |
176 | |
177 | json_destroy(json); | |
178 | } | |
179 | ||
1e19e50e BP |
180 | static void |
181 | compact_or_convert(const char *src_name, const char *dst_name, | |
182 | const struct ovsdb_schema *new_schema, | |
183 | const char *comment) | |
184 | { | |
185 | struct lockfile *src_lock; | |
186 | struct lockfile *dst_lock; | |
187 | bool in_place = dst_name == NULL; | |
188 | struct ovsdb *db; | |
189 | int retval; | |
190 | ||
aebfc869 | 191 | /* Lock the source, if we will be replacing it. */ |
1e19e50e | 192 | if (in_place) { |
7a6ec19d | 193 | retval = lockfile_lock(src_name, 0, &src_lock); |
aebfc869 BP |
194 | if (retval) { |
195 | ovs_fatal(retval, "%s: failed to lock lockfile", src_name); | |
196 | } | |
1e19e50e BP |
197 | } |
198 | ||
aebfc869 BP |
199 | /* Get (temporary) destination and lock it. */ |
200 | if (in_place) { | |
201 | dst_name = xasprintf("%s.tmp", src_name); | |
1e19e50e | 202 | } |
7a6ec19d | 203 | retval = lockfile_lock(dst_name, 0, &dst_lock); |
1e19e50e BP |
204 | if (retval) { |
205 | ovs_fatal(retval, "%s: failed to lock lockfile", dst_name); | |
206 | } | |
207 | ||
208 | /* Save a copy. */ | |
209 | check_ovsdb_error(new_schema | |
210 | ? ovsdb_file_open_as_schema(src_name, new_schema, &db) | |
ada496b5 | 211 | : ovsdb_file_open(src_name, true, &db, NULL)); |
1e19e50e BP |
212 | check_ovsdb_error(ovsdb_file_save_copy(dst_name, false, comment, db)); |
213 | ovsdb_destroy(db); | |
214 | ||
215 | /* Replace source. */ | |
216 | if (in_place) { | |
217 | if (rename(dst_name, src_name)) { | |
218 | ovs_fatal(errno, "failed to rename \"%s\" to \"%s\"", | |
219 | dst_name, src_name); | |
220 | } | |
221 | fsync_parent_dir(dst_name); | |
1e19e50e BP |
222 | lockfile_unlock(src_lock); |
223 | } | |
224 | ||
225 | lockfile_unlock(dst_lock); | |
226 | } | |
227 | ||
228 | static void | |
229 | do_compact(int argc OVS_UNUSED, char *argv[]) | |
230 | { | |
9bdea45b BP |
231 | compact_or_convert(argv[1], argv[2], NULL, |
232 | "compacted by ovsdb-tool "VERSION BUILDNR); | |
1e19e50e BP |
233 | } |
234 | ||
235 | static void | |
236 | do_convert(int argc OVS_UNUSED, char *argv[]) | |
237 | { | |
238 | const char *schema_file_name = argv[2]; | |
239 | struct ovsdb_schema *new_schema; | |
240 | ||
241 | check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &new_schema)); | |
242 | compact_or_convert(argv[1], argv[3], new_schema, | |
9bdea45b | 243 | "converted by ovsdb-tool "VERSION BUILDNR); |
1e19e50e BP |
244 | ovsdb_schema_destroy(new_schema); |
245 | } | |
246 | ||
403e3a25 BP |
247 | static void |
248 | do_needs_conversion(int argc OVS_UNUSED, char *argv[]) | |
249 | { | |
250 | const char *db_file_name = argv[1]; | |
251 | const char *schema_file_name = argv[2]; | |
252 | struct ovsdb_schema *schema1, *schema2; | |
253 | ||
254 | check_ovsdb_error(ovsdb_file_read_schema(db_file_name, &schema1)); | |
255 | check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema2)); | |
256 | puts(ovsdb_schema_equal(schema1, schema2) ? "no" : "yes"); | |
257 | ovsdb_schema_destroy(schema1); | |
258 | ovsdb_schema_destroy(schema2); | |
259 | } | |
260 | ||
8159b984 BP |
261 | static void |
262 | do_db_version(int argc OVS_UNUSED, char *argv[]) | |
263 | { | |
264 | const char *db_file_name = argv[1]; | |
e1ebc8ce | 265 | struct ovsdb_schema *schema; |
8159b984 | 266 | |
e1ebc8ce BP |
267 | check_ovsdb_error(ovsdb_file_read_schema(db_file_name, &schema)); |
268 | puts(schema->version); | |
269 | ovsdb_schema_destroy(schema); | |
8159b984 BP |
270 | } |
271 | ||
6aa09313 BP |
272 | static void |
273 | do_db_cksum(int argc OVS_UNUSED, char *argv[]) | |
274 | { | |
275 | const char *db_file_name = argv[1]; | |
276 | struct ovsdb_schema *schema; | |
277 | ||
278 | check_ovsdb_error(ovsdb_file_read_schema(db_file_name, &schema)); | |
279 | puts(schema->cksum); | |
280 | ovsdb_schema_destroy(schema); | |
281 | } | |
282 | ||
8159b984 BP |
283 | static void |
284 | do_schema_version(int argc OVS_UNUSED, char *argv[]) | |
285 | { | |
286 | const char *schema_file_name = argv[1]; | |
287 | struct ovsdb_schema *schema; | |
288 | ||
289 | check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema)); | |
290 | puts(schema->version); | |
291 | ovsdb_schema_destroy(schema); | |
292 | } | |
293 | ||
6aa09313 BP |
294 | static void |
295 | do_schema_cksum(int argc OVS_UNUSED, char *argv[]) | |
296 | { | |
297 | const char *schema_file_name = argv[1]; | |
298 | struct ovsdb_schema *schema; | |
299 | ||
300 | check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema)); | |
301 | puts(schema->cksum); | |
302 | ovsdb_schema_destroy(schema); | |
303 | } | |
304 | ||
f85f8ebb | 305 | static void |
8b681e6f | 306 | transact(bool read_only, const char *db_file_name, const char *transaction) |
f85f8ebb BP |
307 | { |
308 | struct json *request, *result; | |
309 | struct ovsdb *db; | |
310 | ||
ada496b5 | 311 | check_ovsdb_error(ovsdb_file_open(db_file_name, read_only, &db, NULL)); |
f85f8ebb BP |
312 | |
313 | request = parse_json(transaction); | |
314 | result = ovsdb_execute(db, request, 0, NULL); | |
315 | json_destroy(request); | |
316 | ||
317 | print_and_free_json(result); | |
318 | ovsdb_destroy(db); | |
319 | } | |
320 | ||
321 | static void | |
c69ee87c | 322 | do_query(int argc OVS_UNUSED, char *argv[]) |
f85f8ebb | 323 | { |
8b681e6f | 324 | transact(true, argv[1], argv[2]); |
f85f8ebb BP |
325 | } |
326 | ||
327 | static void | |
c69ee87c | 328 | do_transact(int argc OVS_UNUSED, char *argv[]) |
f85f8ebb | 329 | { |
8b681e6f | 330 | transact(false, argv[1], argv[2]); |
f85f8ebb BP |
331 | } |
332 | ||
c6782bb0 BP |
333 | static void |
334 | print_db_changes(struct shash *tables, struct shash *names) | |
335 | { | |
336 | struct shash_node *n1; | |
337 | ||
338 | SHASH_FOR_EACH (n1, tables) { | |
339 | const char *table = n1->name; | |
340 | struct json *rows = n1->data; | |
341 | struct shash_node *n2; | |
342 | ||
343 | if (n1->name[0] == '_' || rows->type != JSON_OBJECT) { | |
344 | continue; | |
345 | } | |
346 | ||
347 | SHASH_FOR_EACH (n2, json_object(rows)) { | |
348 | const char *row_uuid = n2->name; | |
349 | struct json *columns = n2->data; | |
350 | struct shash_node *n3; | |
351 | char *old_name, *new_name; | |
352 | bool free_new_name = false; | |
353 | ||
354 | old_name = new_name = shash_find_data(names, row_uuid); | |
355 | if (columns->type == JSON_OBJECT) { | |
356 | struct json *new_name_json; | |
357 | ||
358 | new_name_json = shash_find_data(json_object(columns), "name"); | |
359 | if (new_name_json) { | |
360 | new_name = json_to_string(new_name_json, JSSF_SORT); | |
361 | free_new_name = true; | |
362 | } | |
363 | } | |
364 | ||
365 | printf("\ttable %s", table); | |
366 | ||
367 | if (!old_name) { | |
368 | if (new_name) { | |
369 | printf(" insert row %s:\n", new_name); | |
370 | } else { | |
371 | printf(" insert row %.8s:\n", row_uuid); | |
372 | } | |
373 | } else { | |
374 | printf(" row %s:\n", old_name); | |
375 | } | |
376 | ||
377 | if (columns->type == JSON_OBJECT) { | |
378 | if (show_log_verbosity > 1) { | |
379 | SHASH_FOR_EACH (n3, json_object(columns)) { | |
380 | const char *column = n3->name; | |
381 | struct json *value = n3->data; | |
382 | char *value_string; | |
383 | ||
384 | value_string = json_to_string(value, JSSF_SORT); | |
385 | printf("\t\t%s=%s\n", column, value_string); | |
386 | free(value_string); | |
387 | } | |
388 | } | |
389 | if (!old_name | |
390 | || (new_name != old_name && strcmp(old_name, new_name))) { | |
391 | if (old_name) { | |
392 | shash_delete(names, shash_find(names, row_uuid)); | |
393 | free(old_name); | |
394 | } | |
395 | shash_add(names, row_uuid, (new_name | |
396 | ? xstrdup(new_name) | |
397 | : xmemdup0(row_uuid, 8))); | |
398 | } | |
399 | } else if (columns->type == JSON_NULL) { | |
b932d88b BP |
400 | struct shash_node *node; |
401 | ||
c6782bb0 | 402 | printf("\t\tdelete row\n"); |
b932d88b BP |
403 | node = shash_find(names, row_uuid); |
404 | if (node) { | |
405 | shash_delete(names, node); | |
406 | } | |
c6782bb0 BP |
407 | free(old_name); |
408 | } | |
409 | ||
410 | if (free_new_name) { | |
411 | free(new_name); | |
412 | } | |
413 | } | |
414 | } | |
415 | } | |
416 | ||
722f6301 | 417 | static void |
c69ee87c | 418 | do_show_log(int argc OVS_UNUSED, char *argv[]) |
722f6301 BP |
419 | { |
420 | const char *db_file_name = argv[1]; | |
c6782bb0 | 421 | struct shash names; |
722f6301 BP |
422 | struct ovsdb_log *log; |
423 | unsigned int i; | |
424 | ||
7446f148 BP |
425 | check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_LOG_READ_ONLY, |
426 | -1, &log)); | |
c6782bb0 | 427 | shash_init(&names); |
722f6301 BP |
428 | for (i = 0; ; i++) { |
429 | struct json *json; | |
430 | ||
431 | check_ovsdb_error(ovsdb_log_read(log, &json)); | |
432 | if (!json) { | |
433 | break; | |
434 | } | |
435 | ||
436 | printf("record %u:", i); | |
437 | if (json->type == JSON_OBJECT) { | |
438 | struct json *date, *comment; | |
439 | ||
440 | date = shash_find_data(json_object(json), "_date"); | |
441 | if (date && date->type == JSON_INTEGER) { | |
442 | time_t t = json_integer(date); | |
443 | char s[128]; | |
444 | ||
445 | strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", localtime(&t)); | |
446 | printf(" %s", s); | |
447 | } | |
448 | ||
449 | comment = shash_find_data(json_object(json), "_comment"); | |
450 | if (comment && comment->type == JSON_STRING) { | |
451 | printf(" \"%s\"", json_string(comment)); | |
452 | } | |
c6782bb0 BP |
453 | |
454 | if (i > 0 && show_log_verbosity > 0) { | |
455 | putchar('\n'); | |
456 | print_db_changes(json_object(json), &names); | |
457 | } | |
722f6301 BP |
458 | } |
459 | json_destroy(json); | |
460 | putchar('\n'); | |
461 | } | |
c6782bb0 | 462 | |
400eb935 | 463 | ovsdb_log_close(log); |
c6782bb0 | 464 | /* XXX free 'names'. */ |
722f6301 BP |
465 | } |
466 | ||
f85f8ebb | 467 | static void |
c69ee87c | 468 | do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) |
f85f8ebb BP |
469 | { |
470 | usage(); | |
471 | } | |
472 | ||
473 | static const struct command all_commands[] = { | |
474 | { "create", 2, 2, do_create }, | |
1e19e50e BP |
475 | { "compact", 1, 2, do_compact }, |
476 | { "convert", 2, 3, do_convert }, | |
403e3a25 | 477 | { "needs-conversion", 2, 2, do_needs_conversion }, |
8159b984 | 478 | { "db-version", 1, 1, do_db_version }, |
6aa09313 | 479 | { "db-cksum", 1, 1, do_db_cksum }, |
8159b984 | 480 | { "schema-version", 1, 1, do_schema_version }, |
6aa09313 | 481 | { "schema-cksum", 1, 1, do_schema_cksum }, |
f85f8ebb BP |
482 | { "query", 2, 2, do_query }, |
483 | { "transact", 2, 2, do_transact }, | |
722f6301 | 484 | { "show-log", 1, 1, do_show_log }, |
f85f8ebb BP |
485 | { "help", 0, INT_MAX, do_help }, |
486 | { NULL, 0, 0, NULL }, | |
487 | }; |