ovsdb: Add new ovsdb-client program.
[sliver-openvswitch.git] / ovsdb / ovsdb-client.c
1 /*
2  * Copyright (c) 2009 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
19 #include <errno.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "command-line.h"
28 #include "column.h"
29 #include "compiler.h"
30 #include "dynamic-string.h"
31 #include "json.h"
32 #include "jsonrpc.h"
33 #include "ovsdb.h"
34 #include "ovsdb-error.h"
35 #include "stream.h"
36 #include "table.h"
37 #include "timeval.h"
38 #include "util.h"
39
40 #include "vlog.h"
41 #define THIS_MODULE VLM_ovsdb_client
42
43 /* --format: Output formatting. */
44 static enum {
45     FMT_TABLE,                  /* Textual table. */
46     FMT_HTML,                   /* HTML table. */
47     FMT_CSV                     /* Comma-separated lines. */
48 } output_format;
49
50 /* --wide: For --format=table, the maximum output width. */
51 static int output_width;
52
53 /* --no-headings: Whether table output should include headings. */
54 static int output_headings = true;
55
56 static const struct command all_commands[];
57
58 static void usage(void) NO_RETURN;
59 static void parse_options(int argc, char *argv[]);
60
61 int
62 main(int argc, char *argv[])
63 {
64     set_program_name(argv[0]);
65     time_init();
66     vlog_init();
67     parse_options(argc, argv);
68     signal(SIGPIPE, SIG_IGN);
69     run_command(argc - optind, argv + optind, all_commands);
70     return 0;
71 }
72
73 static void
74 parse_options(int argc, char *argv[])
75 {
76     static struct option long_options[] = {
77         {"wide", no_argument, &output_width, INT_MAX},
78         {"format", required_argument, 0, 'f'},
79             {"no-headings", no_argument, &output_headings, 0},
80         {"verbose", optional_argument, 0, 'v'},
81         {"help", no_argument, 0, 'h'},
82         {"version", no_argument, 0, 'V'},
83         {0, 0, 0, 0},
84     };
85     char *short_options = long_options_to_short_options(long_options);
86
87     output_width = isatty(fileno(stdout)) ? 79 : INT_MAX;
88     for (;;) {
89         int c;
90
91         c = getopt_long(argc, argv, short_options, long_options, NULL);
92         if (c == -1) {
93             break;
94         }
95
96         switch (c) {
97         case 'f':
98             if (!strcmp(optarg, "table")) {
99                 output_format = FMT_TABLE;
100             } else if (!strcmp(optarg, "html")) {
101                 output_format = FMT_HTML;
102             } else if (!strcmp(optarg, "csv")) {
103                 output_format = FMT_CSV;
104             } else {
105                 ovs_fatal(0, "unknown output format \"%s\"", optarg);
106             }
107             break;
108
109         case 'w':
110             output_width = INT_MAX;
111             break;
112
113         case 'h':
114             usage();
115
116         case 'V':
117             OVS_PRINT_VERSION(0, 0);
118             exit(EXIT_SUCCESS);
119
120         case 'v':
121             vlog_set_verbosity(optarg);
122             break;
123
124         case '?':
125             exit(EXIT_FAILURE);
126
127         case 0:
128             /* getopt_long() already set the value for us. */
129             break;
130
131         default:
132             abort();
133         }
134     }
135     free(short_options);
136 }
137
138 static void
139 usage(void)
140 {
141     printf("%s: Open vSwitch database JSON-RPC client\n"
142            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
143            "\nValid commands are:\n"
144            "\n  get-schema SERVER\n"
145            "    retrieve schema from SERVER\n"
146            "\n  list-tables SERVER\n"
147            "    list SERVER's tables\n"
148            "\n  list-columns SERVER [TABLE]\n"
149            "    list columns in TABLE (or all tables) on SERVER\n",
150            program_name, program_name);
151     stream_usage("SERVER", true, false);
152     printf("\nOutput formatting options:\n"
153            "  -f, --format=FORMAT         set output formatting to FORMAT\n"
154            "                              (\"table\", \"html\", or \"csv\"\n"
155            "  --wide                      don't limit TTY lines to 79 bytes\n"
156            "  --no-headings               omit table heading row\n");
157     vlog_usage();
158     printf("\nOther options:\n"
159            "  -h, --help                  display this help message\n"
160            "  -V, --version               display version information\n");
161     exit(EXIT_SUCCESS);
162 }
163 \f
164 static struct jsonrpc *
165 open_jsonrpc(const char *server)
166 {
167     struct stream *stream;
168     int error;
169
170     error = stream_open_block(server, &stream);
171     if (error) {
172         ovs_fatal(error, "failed to connect to \"%s\"", server);
173     }
174
175     return jsonrpc_open(stream);
176 }
177
178 static void
179 print_json(struct json *json)
180 {
181     char *string = json_to_string(json, JSSF_SORT);
182     fputs(string, stdout);
183     free(string);
184 }
185
186 static void
187 print_and_free_json(struct json *json)
188 {
189     print_json(json);
190     json_destroy(json);
191 }
192
193 static void
194 check_ovsdb_error(struct ovsdb_error *error)
195 {
196     if (error) {
197         ovs_fatal(0, "%s", ovsdb_error_to_string(error));
198     }
199 }
200
201 static struct ovsdb_schema *
202 fetch_schema(const char *server)
203 {
204     struct jsonrpc_msg *request, *reply;
205     struct ovsdb_schema *schema;
206     struct jsonrpc *rpc;
207     int error;
208
209     rpc = open_jsonrpc(server);
210     request = jsonrpc_create_request("get_schema", json_array_create_empty());
211     error = jsonrpc_transact_block(rpc, request, &reply);
212     if (error) {
213         ovs_fatal(error, "transaction failed");
214     }
215     check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema));
216     jsonrpc_msg_destroy(reply);
217     jsonrpc_close(rpc);
218
219     return schema;
220 }
221 \f
222 struct column {
223     char *heading;
224     int width;
225 };
226
227 struct table {
228     char **cells;
229     struct column *columns;
230     size_t n_columns, allocated_columns;
231     size_t n_rows, allocated_rows;
232     size_t current_column;
233 };
234
235 static void
236 table_init(struct table *table)
237 {
238     memset(table, 0, sizeof *table);
239 }
240
241 static void
242 table_add_column(struct table *table, const char *heading, ...)
243     PRINTF_FORMAT(2, 3);
244
245 static void
246 table_add_column(struct table *table, const char *heading, ...)
247 {
248     struct column *column;
249     va_list args;
250
251     assert(!table->n_rows);
252     if (table->n_columns >= table->allocated_columns) {
253         table->columns = x2nrealloc(table->columns, &table->allocated_columns,
254                                     sizeof *table->columns);
255     }
256     column = &table->columns[table->n_columns++];
257
258     va_start(args, heading);
259     column->heading = xvasprintf(heading, args);
260     column->width = strlen(column->heading);
261     va_end(args);
262 }
263
264 static char **
265 table_cell__(const struct table *table, size_t row, size_t column)
266 {
267     return &table->cells[column + row * table->n_columns];
268 }
269
270 static void
271 table_add_row(struct table *table)
272 {
273     size_t x, y;
274
275     if (table->n_rows >= table->allocated_rows) {
276         table->cells = x2nrealloc(table->cells, &table->allocated_rows,
277                                   table->n_columns * sizeof *table->cells);
278     }
279
280     y = table->n_rows++;
281     table->current_column = 0;
282     for (x = 0; x < table->n_columns; x++) {
283         *table_cell__(table, y, x) = NULL;
284     }
285 }
286
287 static void
288 table_add_cell_nocopy(struct table *table, char *s)
289 {
290     size_t x, y;
291     int length;
292
293     assert(table->n_rows > 0);
294     assert(table->current_column < table->n_columns);
295
296     x = table->current_column++;
297     y = table->n_rows - 1;
298     *table_cell__(table, y, x) = s;
299
300     length = strlen(s);
301     if (length > table->columns[x].width) {
302         table->columns[x].width = length;
303     }
304 }
305
306 static void
307 table_add_cell(struct table *table, const char *format, ...)
308 {
309     va_list args;
310
311     va_start(args, format);
312     table_add_cell_nocopy(table, xvasprintf(format, args));
313     va_end(args);
314 }
315
316 static void
317 table_print_table_line__(struct ds *line, size_t max_width)
318 {
319     ds_truncate(line, max_width);
320     puts(ds_cstr(line));
321     ds_clear(line);
322 }
323
324 static void
325 table_print_table__(const struct table *table)
326 {
327     struct ds line = DS_EMPTY_INITIALIZER;
328     size_t x, y;
329
330     if (output_headings) {
331         for (x = 0; x < table->n_columns; x++) {
332             const struct column *column = &table->columns[x];
333             if (x) {
334                 ds_put_char(&line, ' ');
335             }
336             ds_put_format(&line, "%-*s", column->width, column->heading);
337         }
338         table_print_table_line__(&line, output_width);
339
340         for (x = 0; x < table->n_columns; x++) {
341             const struct column *column = &table->columns[x];
342             int i;
343
344             if (x) {
345                 ds_put_char(&line, ' ');
346             }
347             for (i = 0; i < column->width; i++) {
348                 ds_put_char(&line, '-');
349             }
350         }
351         table_print_table_line__(&line, output_width);
352     }
353
354     for (y = 0; y < table->n_rows; y++) {
355         for (x = 0; x < table->n_columns; x++) {
356             const char *cell = *table_cell__(table, y, x);
357             if (x) {
358                 ds_put_char(&line, ' ');
359             }
360             ds_put_format(&line, "%-*s", table->columns[x].width, cell);
361         }
362         table_print_table_line__(&line, output_width);
363     }
364
365     ds_destroy(&line);
366 }
367
368 static void
369 table_print_html_cell__(const char *element, const char *content)
370 {
371     const char *p;
372
373     printf("    <%s>", element);
374     for (p = content; *p != '\0'; p++) {
375         switch (*p) {
376         case '&':
377             fputs("&amp;", stdout);
378             break;
379         case '<':
380             fputs("&lt;", stdout);
381             break;
382         case '>':
383             fputs("&gt;", stdout);
384             break;
385         default:
386             putchar(*p);
387             break;
388         }
389     }
390     printf("</%s>\n", element);
391 }
392
393 static void
394 table_print_html__(const struct table *table)
395 {
396     size_t x, y;
397
398     fputs("<table>\n", stdout);
399
400     if (output_headings) {
401         fputs("  <tr>\n", stdout);
402         for (x = 0; x < table->n_columns; x++) {
403             const struct column *column = &table->columns[x];
404             table_print_html_cell__("th", column->heading);
405         }
406         fputs("  </tr>\n", stdout);
407     }
408
409     for (y = 0; y < table->n_rows; y++) {
410         fputs("  <tr>\n", stdout);
411         for (x = 0; x < table->n_columns; x++) {
412             table_print_html_cell__("td", *table_cell__(table, y, x));
413         }
414         fputs("  </tr>\n", stdout);
415     }
416
417     fputs("</table>\n", stdout);
418 }
419
420 static void
421 table_print_csv_cell__(const char *content)
422 {
423     const char *p;
424
425     if (!strpbrk(content, "\n\",")) {
426         fputs(content, stdout);
427     } else {
428         putchar('"');
429         for (p = content; *p != '\0'; p++) {
430             switch (*p) {
431             case '"':
432                 fputs("\"\"", stdout);
433                 break;
434             default:
435                 putchar(*p);
436                 break;
437             }
438         }
439         putchar('"');
440     }
441 }
442
443 static void
444 table_print_csv__(const struct table *table)
445 {
446     size_t x, y;
447
448     if (output_headings) {
449         for (x = 0; x < table->n_columns; x++) {
450             const struct column *column = &table->columns[x];
451             if (x) {
452                 putchar(',');
453             }
454             table_print_csv_cell__(column->heading);
455         }
456         putchar('\n');
457     }
458
459     for (y = 0; y < table->n_rows; y++) {
460         for (x = 0; x < table->n_columns; x++) {
461             if (x) {
462                 putchar(',');
463             }
464             table_print_csv_cell__(*table_cell__(table, y, x));
465         }
466         putchar('\n');
467     }
468 }
469
470 static void
471 table_print(const struct table *table)
472 {
473     switch (output_format) {
474     case FMT_TABLE:
475         table_print_table__(table);
476         break;
477
478     case FMT_HTML:
479         table_print_html__(table);
480         break;
481
482     case FMT_CSV:
483         table_print_csv__(table);
484         break;
485     }
486 }
487 \f
488 static void
489 do_get_schema(int argc UNUSED, char *argv[])
490 {
491     struct ovsdb_schema *schema = fetch_schema(argv[1]);
492     print_and_free_json(ovsdb_schema_to_json(schema));
493     ovsdb_schema_destroy(schema);
494 }
495
496 static void
497 do_list_tables(int argc UNUSED, char *argv[])
498 {
499     struct ovsdb_schema *schema;
500     struct shash_node *node;
501     struct table t;
502
503     schema = fetch_schema(argv[1]);
504     table_init(&t);
505     table_add_column(&t, "Table");
506     table_add_column(&t, "Comment");
507     SHASH_FOR_EACH (node, &schema->tables) {
508         struct ovsdb_table_schema *ts = node->data;
509
510         table_add_row(&t);
511         table_add_cell(&t, ts->name);
512         if (ts->comment) {
513             table_add_cell(&t, ts->comment);
514         }
515     }
516     ovsdb_schema_destroy(schema);
517     table_print(&t);
518 }
519
520 static void
521 do_list_columns(int argc UNUSED, char *argv[])
522 {
523     const char *table_name = argv[2];
524     struct ovsdb_schema *schema;
525     struct shash_node *table_node;
526     struct table t;
527
528     schema = fetch_schema(argv[1]);
529     table_init(&t);
530     if (!table_name) {
531         table_add_column(&t, "Table");
532     }
533     table_add_column(&t, "Column");
534     table_add_column(&t, "Type");
535     table_add_column(&t, "Comment");
536     SHASH_FOR_EACH (table_node, &schema->tables) {
537         struct ovsdb_table_schema *ts = table_node->data;
538
539         if (!table_name || !strcmp(table_name, ts->name)) {
540             struct shash_node *column_node;
541
542             SHASH_FOR_EACH (column_node, &ts->columns) {
543                 struct ovsdb_column *column = column_node->data;
544                 struct json *type = ovsdb_type_to_json(&column->type);
545
546                 table_add_row(&t);
547                 if (!table_name) {
548                     table_add_cell(&t, ts->name);
549                 }
550                 table_add_cell(&t, column->name);
551                 table_add_cell_nocopy(&t, json_to_string(type, JSSF_SORT));
552                 if (column->comment) {
553                     table_add_cell(&t, column->comment);
554                 }
555
556                 json_destroy(type);
557             }
558         }
559     }
560     ovsdb_schema_destroy(schema);
561     table_print(&t);
562 }
563
564 static void
565 do_help(int argc UNUSED, char *argv[] UNUSED)
566 {
567     usage();
568 }
569
570 static const struct command all_commands[] = {
571     { "get-schema", 1, 1, do_get_schema },
572     { "list-tables", 1, 1, do_list_tables },
573     { "list-columns", 1, 2, do_list_columns },
574     { "help", 0, INT_MAX, do_help },
575     { NULL, 0, 0, NULL },
576 };