ovsdb-tool: Do not lock source db for compacting or converting to new db.
[cascardo/ovs.git] / ovsdb / ovsdb-tool.c
1 /*
2  * Copyright (c) 2009, 2010 Nicira Networks.
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"
27 #include "file.h"
28 #include "lockfile.h"
29 #include "log.h"
30 #include "json.h"
31 #include "ovsdb.h"
32 #include "ovsdb-error.h"
33 #include "socket-util.h"
34 #include "table.h"
35 #include "timeval.h"
36 #include "util.h"
37
38 #include "vlog.h"
39 #define THIS_MODULE VLM_ovsdb_tool
40
41 /* -m, --more: Verbosity level for "show-log" command output. */
42 static int show_log_verbosity;
43
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]);
53     time_init();
54     vlog_init();
55     parse_options(argc, argv);
56     signal(SIGPIPE, SIG_IGN);
57     run_command(argc - optind, argv + optind, all_commands);
58     return 0;
59 }
60
61 static void
62 parse_options(int argc, char *argv[])
63 {
64     static struct option long_options[] = {
65         {"more", no_argument, 0, 'm'},
66         {"verbose", optional_argument, 0, 'v'},
67         {"help", no_argument, 0, 'h'},
68         {"version", no_argument, 0, 'V'},
69         {0, 0, 0, 0},
70     };
71     char *short_options = long_options_to_short_options(long_options);
72
73     for (;;) {
74         int c;
75
76         c = getopt_long(argc, argv, short_options, long_options, NULL);
77         if (c == -1) {
78             break;
79         }
80
81         switch (c) {
82         case 'm':
83             show_log_verbosity++;
84             break;
85
86         case 'h':
87             usage();
88
89         case 'V':
90             OVS_PRINT_VERSION(0, 0);
91             exit(EXIT_SUCCESS);
92
93         case 'v':
94             vlog_set_verbosity(optarg);
95             break;
96
97         case '?':
98             exit(EXIT_FAILURE);
99
100         default:
101             abort();
102         }
103     }
104     free(short_options);
105 }
106
107 static void
108 usage(void)
109 {
110     printf("%s: Open vSwitch database management utility\n"
111            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
112            "  create DB SCHEMA   create DB with the given SCHEMA\n"
113            "  compact DB [DST]   compact DB in-place (or to DST)\n"
114            "  convert DB SCHEMA [DST]   convert DB to SCHEMA (to DST)\n"
115            "  extract-schema DB  print DB's schema on stdout\n"
116            "  query DB TRNS      execute read-only transaction on DB\n"
117            "  transact DB TRNS   execute read/write transaction on DB\n"
118            "  show-log DB        prints information about DB's log entries\n",
119            program_name, program_name);
120     vlog_usage();
121     printf("\nOther options:\n"
122            "  -m, --more                  increase show-log verbosity\n"
123            "  -h, --help                  display this help message\n"
124            "  -V, --version               display version information\n");
125     exit(EXIT_SUCCESS);
126 }
127 \f
128 static struct json *
129 parse_json(const char *s)
130 {
131     struct json *json = json_from_string(s);
132     if (json->type == JSON_STRING) {
133         ovs_fatal(0, "\"%s\": %s", s, json->u.string);
134     }
135     return json;
136 }
137
138 static void
139 print_and_free_json(struct json *json)
140 {
141     char *string = json_to_string(json, JSSF_SORT);
142     json_destroy(json);
143     puts(string);
144     free(string);
145 }
146
147 static void
148 check_ovsdb_error(struct ovsdb_error *error)
149 {
150     if (error) {
151         ovs_fatal(0, "%s", ovsdb_error_to_string(error));
152     }
153 }
154 \f
155 static void
156 do_create(int argc OVS_UNUSED, char *argv[])
157 {
158     const char *db_file_name = argv[1];
159     const char *schema_file_name = argv[2];
160     struct ovsdb_schema *schema;
161     struct ovsdb_log *log;
162     struct json *json;
163
164     /* Read schema from file and convert to JSON. */
165     check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema));
166     json = ovsdb_schema_to_json(schema);
167     ovsdb_schema_destroy(schema);
168
169     /* Create database file. */
170     check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_LOG_CREATE,
171                                      -1, &log));
172     check_ovsdb_error(ovsdb_log_write(log, json));
173     check_ovsdb_error(ovsdb_log_commit(log));
174     ovsdb_log_close(log);
175
176     json_destroy(json);
177 }
178
179 static void
180 compact_or_convert(const char *src_name, const char *dst_name,
181                    const struct ovsdb_schema *new_schema,
182                    const char *comment)
183 {
184     struct lockfile *src_lock;
185     struct lockfile *dst_lock;
186     bool in_place = dst_name == NULL;
187     struct ovsdb *db;
188     int retval;
189
190     /* Lock the source, if we will be replacing it. */
191     if (in_place) {
192         retval = lockfile_lock(src_name, INT_MAX, &src_lock);
193         if (retval) {
194             ovs_fatal(retval, "%s: failed to lock lockfile", src_name);
195         }
196     }
197
198     /* Get (temporary) destination and lock it. */
199     if (in_place) {
200         dst_name = xasprintf("%s.tmp", src_name);
201     }
202     retval = lockfile_lock(dst_name, INT_MAX, &dst_lock);
203     if (retval) {
204         ovs_fatal(retval, "%s: failed to lock lockfile", dst_name);
205     }
206
207     /* Save a copy. */
208     check_ovsdb_error(new_schema
209                       ? ovsdb_file_open_as_schema(src_name, new_schema, &db)
210                       : ovsdb_file_open(src_name, true, &db));
211     check_ovsdb_error(ovsdb_file_save_copy(dst_name, false, comment, db));
212     ovsdb_destroy(db);
213
214     /* Replace source. */
215     if (in_place) {
216         if (rename(dst_name, src_name)) {
217             ovs_fatal(errno, "failed to rename \"%s\" to \"%s\"",
218                       dst_name, src_name);
219         }
220         fsync_parent_dir(dst_name);
221         lockfile_unlock(src_lock);
222     }
223
224     lockfile_unlock(dst_lock);
225 }
226
227 static void
228 do_compact(int argc OVS_UNUSED, char *argv[])
229 {
230     compact_or_convert(argv[1], argv[2], NULL, "compacted by ovsdb-tool");
231 }
232
233 static void
234 do_convert(int argc OVS_UNUSED, char *argv[])
235 {
236     const char *schema_file_name = argv[2];
237     struct ovsdb_schema *new_schema;
238
239     check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &new_schema));
240     compact_or_convert(argv[1], argv[3], new_schema,
241                        "converted by ovsdb-tool");
242     ovsdb_schema_destroy(new_schema);
243 }
244
245 static void
246 transact(bool read_only, const char *db_file_name, const char *transaction)
247 {
248     struct json *request, *result;
249     struct ovsdb *db;
250
251     check_ovsdb_error(ovsdb_file_open(db_file_name, read_only, &db));
252
253     request = parse_json(transaction);
254     result = ovsdb_execute(db, request, 0, NULL);
255     json_destroy(request);
256
257     print_and_free_json(result);
258     ovsdb_destroy(db);
259 }
260
261 static void
262 do_query(int argc OVS_UNUSED, char *argv[])
263 {
264     transact(true, argv[1], argv[2]);
265 }
266
267 static void
268 do_transact(int argc OVS_UNUSED, char *argv[])
269 {
270     transact(false, argv[1], argv[2]);
271 }
272
273 static void
274 print_db_changes(struct shash *tables, struct shash *names)
275 {
276     struct shash_node *n1;
277
278     SHASH_FOR_EACH (n1, tables) {
279         const char *table = n1->name;
280         struct json *rows = n1->data;
281         struct shash_node *n2;
282
283         if (n1->name[0] == '_' || rows->type != JSON_OBJECT) {
284             continue;
285         }
286
287         SHASH_FOR_EACH (n2, json_object(rows)) {
288             const char *row_uuid = n2->name;
289             struct json *columns = n2->data;
290             struct shash_node *n3;
291             char *old_name, *new_name;
292             bool free_new_name = false;
293
294             old_name = new_name = shash_find_data(names, row_uuid);
295             if (columns->type == JSON_OBJECT) {
296                 struct json *new_name_json;
297
298                 new_name_json = shash_find_data(json_object(columns), "name");
299                 if (new_name_json) {
300                     new_name = json_to_string(new_name_json, JSSF_SORT);
301                     free_new_name = true;
302                 }
303             }
304
305             printf("\ttable %s", table);
306
307             if (!old_name) {
308                 if (new_name) {
309                     printf(" insert row %s:\n", new_name);
310                 } else {
311                     printf(" insert row %.8s:\n", row_uuid);
312                 }
313             } else {
314                 printf(" row %s:\n", old_name);
315             }
316
317             if (columns->type == JSON_OBJECT) {
318                 if (show_log_verbosity > 1) {
319                     SHASH_FOR_EACH (n3, json_object(columns)) {
320                         const char *column = n3->name;
321                         struct json *value = n3->data;
322                         char *value_string;
323
324                         value_string = json_to_string(value, JSSF_SORT);
325                         printf("\t\t%s=%s\n", column, value_string);
326                         free(value_string);
327                     }
328                 }
329                 if (!old_name
330                     || (new_name != old_name && strcmp(old_name, new_name))) {
331                     if (old_name) {
332                         shash_delete(names, shash_find(names, row_uuid));
333                         free(old_name);
334                     }
335                     shash_add(names, row_uuid, (new_name
336                                                 ? xstrdup(new_name)
337                                                 : xmemdup0(row_uuid, 8)));
338                 }
339             } else if (columns->type == JSON_NULL) {
340                 printf("\t\tdelete row\n");
341                 shash_delete(names, shash_find(names, row_uuid));
342                 free(old_name);
343             }
344
345             if (free_new_name) {
346                 free(new_name);
347             }
348         }
349     }
350 }
351
352 static void
353 do_show_log(int argc OVS_UNUSED, char *argv[])
354 {
355     const char *db_file_name = argv[1];
356     struct shash names;
357     struct ovsdb_log *log;
358     unsigned int i;
359
360     check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_LOG_READ_ONLY,
361                                      -1, &log));
362     shash_init(&names);
363     for (i = 0; ; i++) {
364         struct json *json;
365
366         check_ovsdb_error(ovsdb_log_read(log, &json));
367         if (!json) {
368             break;
369         }
370
371         printf("record %u:", i);
372         if (json->type == JSON_OBJECT) {
373             struct json *date, *comment;
374
375             date = shash_find_data(json_object(json), "_date");
376             if (date && date->type == JSON_INTEGER) {
377                 time_t t = json_integer(date);
378                 char s[128];
379
380                 strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", localtime(&t));
381                 printf(" %s", s);
382             }
383
384             comment = shash_find_data(json_object(json), "_comment");
385             if (comment && comment->type == JSON_STRING) {
386                 printf(" \"%s\"", json_string(comment));
387             }
388
389             if (i > 0 && show_log_verbosity > 0) {
390                 putchar('\n');
391                 print_db_changes(json_object(json), &names);
392             }
393         }
394         json_destroy(json);
395         putchar('\n');
396     }
397
398     /* XXX free 'names'. */
399 }
400
401 static void
402 do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
403 {
404     usage();
405 }
406
407 static const struct command all_commands[] = {
408     { "create", 2, 2, do_create },
409     { "compact", 1, 2, do_compact },
410     { "convert", 2, 3, do_convert },
411     { "query", 2, 2, do_query },
412     { "transact", 2, 2, do_transact },
413     { "show-log", 1, 1, do_show_log },
414     { "help", 0, INT_MAX, do_help },
415     { NULL, 0, 0, NULL },
416 };