ovsdb-client: New command "transact".
[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            "\n  transact SERVER TRANSACTION\n"
151            "    run TRANSACTION (a JSON array of operations) on SERVER\n"
152            "    and print the results as JSON on stdout\n",
153            program_name, program_name);
154     stream_usage("SERVER", true, true);
155     printf("\nOutput formatting options:\n"
156            "  -f, --format=FORMAT         set output formatting to FORMAT\n"
157            "                              (\"table\", \"html\", or \"csv\"\n"
158            "  --wide                      don't limit TTY lines to 79 bytes\n"
159            "  --no-headings               omit table heading row\n");
160     vlog_usage();
161     printf("\nOther options:\n"
162            "  -h, --help                  display this help message\n"
163            "  -V, --version               display version information\n");
164     exit(EXIT_SUCCESS);
165 }
166 \f
167 static struct json *
168 parse_json(const char *s)
169 {
170     struct json *json = json_from_string(s);
171     if (json->type == JSON_STRING) {
172         ovs_fatal(0, "\"%s\": %s", s, json->u.string);
173     }
174     return json;
175 }
176
177 static struct jsonrpc *
178 open_jsonrpc(const char *server)
179 {
180     struct stream *stream;
181     int error;
182
183     error = stream_open_block(server, &stream);
184     if (error == EAFNOSUPPORT) {
185         struct pstream *pstream;
186
187         error = pstream_open(server, &pstream);
188         if (error) {
189             ovs_fatal(error, "failed to connect or listen to \"%s\"", server);
190         }
191
192         VLOG_INFO("%s: waiting for connection...", server);
193         error = pstream_accept_block(pstream, &stream);
194         if (error) {
195             ovs_fatal(error, "failed to accept connection on \"%s\"", server);
196         }
197
198         pstream_close(pstream);
199     } else if (error) {
200         ovs_fatal(error, "failed to connect to \"%s\"", server);
201     }
202
203     return jsonrpc_open(stream);
204 }
205
206 static void
207 print_json(struct json *json)
208 {
209     char *string = json_to_string(json, JSSF_SORT);
210     fputs(string, stdout);
211     free(string);
212 }
213
214 static void
215 print_and_free_json(struct json *json)
216 {
217     print_json(json);
218     json_destroy(json);
219 }
220
221 static void
222 check_ovsdb_error(struct ovsdb_error *error)
223 {
224     if (error) {
225         ovs_fatal(0, "%s", ovsdb_error_to_string(error));
226     }
227 }
228
229 static struct ovsdb_schema *
230 fetch_schema(const char *server)
231 {
232     struct jsonrpc_msg *request, *reply;
233     struct ovsdb_schema *schema;
234     struct jsonrpc *rpc;
235     int error;
236
237     rpc = open_jsonrpc(server);
238     request = jsonrpc_create_request("get_schema", json_array_create_empty());
239     error = jsonrpc_transact_block(rpc, request, &reply);
240     if (error) {
241         ovs_fatal(error, "transaction failed");
242     }
243     check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema));
244     jsonrpc_msg_destroy(reply);
245     jsonrpc_close(rpc);
246
247     return schema;
248 }
249 \f
250 struct column {
251     char *heading;
252     int width;
253 };
254
255 struct table {
256     char **cells;
257     struct column *columns;
258     size_t n_columns, allocated_columns;
259     size_t n_rows, allocated_rows;
260     size_t current_column;
261 };
262
263 static void
264 table_init(struct table *table)
265 {
266     memset(table, 0, sizeof *table);
267 }
268
269 static void
270 table_add_column(struct table *table, const char *heading, ...)
271     PRINTF_FORMAT(2, 3);
272
273 static void
274 table_add_column(struct table *table, const char *heading, ...)
275 {
276     struct column *column;
277     va_list args;
278
279     assert(!table->n_rows);
280     if (table->n_columns >= table->allocated_columns) {
281         table->columns = x2nrealloc(table->columns, &table->allocated_columns,
282                                     sizeof *table->columns);
283     }
284     column = &table->columns[table->n_columns++];
285
286     va_start(args, heading);
287     column->heading = xvasprintf(heading, args);
288     column->width = strlen(column->heading);
289     va_end(args);
290 }
291
292 static char **
293 table_cell__(const struct table *table, size_t row, size_t column)
294 {
295     return &table->cells[column + row * table->n_columns];
296 }
297
298 static void
299 table_add_row(struct table *table)
300 {
301     size_t x, y;
302
303     if (table->n_rows >= table->allocated_rows) {
304         table->cells = x2nrealloc(table->cells, &table->allocated_rows,
305                                   table->n_columns * sizeof *table->cells);
306     }
307
308     y = table->n_rows++;
309     table->current_column = 0;
310     for (x = 0; x < table->n_columns; x++) {
311         *table_cell__(table, y, x) = NULL;
312     }
313 }
314
315 static void
316 table_add_cell_nocopy(struct table *table, char *s)
317 {
318     size_t x, y;
319     int length;
320
321     assert(table->n_rows > 0);
322     assert(table->current_column < table->n_columns);
323
324     x = table->current_column++;
325     y = table->n_rows - 1;
326     *table_cell__(table, y, x) = s;
327
328     length = strlen(s);
329     if (length > table->columns[x].width) {
330         table->columns[x].width = length;
331     }
332 }
333
334 static void
335 table_add_cell(struct table *table, const char *format, ...)
336 {
337     va_list args;
338
339     va_start(args, format);
340     table_add_cell_nocopy(table, xvasprintf(format, args));
341     va_end(args);
342 }
343
344 static void
345 table_print_table_line__(struct ds *line, size_t max_width)
346 {
347     ds_truncate(line, max_width);
348     puts(ds_cstr(line));
349     ds_clear(line);
350 }
351
352 static void
353 table_print_table__(const struct table *table)
354 {
355     struct ds line = DS_EMPTY_INITIALIZER;
356     size_t x, y;
357
358     if (output_headings) {
359         for (x = 0; x < table->n_columns; x++) {
360             const struct column *column = &table->columns[x];
361             if (x) {
362                 ds_put_char(&line, ' ');
363             }
364             ds_put_format(&line, "%-*s", column->width, column->heading);
365         }
366         table_print_table_line__(&line, output_width);
367
368         for (x = 0; x < table->n_columns; x++) {
369             const struct column *column = &table->columns[x];
370             int i;
371
372             if (x) {
373                 ds_put_char(&line, ' ');
374             }
375             for (i = 0; i < column->width; i++) {
376                 ds_put_char(&line, '-');
377             }
378         }
379         table_print_table_line__(&line, output_width);
380     }
381
382     for (y = 0; y < table->n_rows; y++) {
383         for (x = 0; x < table->n_columns; x++) {
384             const char *cell = *table_cell__(table, y, x);
385             if (x) {
386                 ds_put_char(&line, ' ');
387             }
388             ds_put_format(&line, "%-*s", table->columns[x].width, cell);
389         }
390         table_print_table_line__(&line, output_width);
391     }
392
393     ds_destroy(&line);
394 }
395
396 static void
397 table_print_html_cell__(const char *element, const char *content)
398 {
399     const char *p;
400
401     printf("    <%s>", element);
402     for (p = content; *p != '\0'; p++) {
403         switch (*p) {
404         case '&':
405             fputs("&amp;", stdout);
406             break;
407         case '<':
408             fputs("&lt;", stdout);
409             break;
410         case '>':
411             fputs("&gt;", stdout);
412             break;
413         default:
414             putchar(*p);
415             break;
416         }
417     }
418     printf("</%s>\n", element);
419 }
420
421 static void
422 table_print_html__(const struct table *table)
423 {
424     size_t x, y;
425
426     fputs("<table>\n", stdout);
427
428     if (output_headings) {
429         fputs("  <tr>\n", stdout);
430         for (x = 0; x < table->n_columns; x++) {
431             const struct column *column = &table->columns[x];
432             table_print_html_cell__("th", column->heading);
433         }
434         fputs("  </tr>\n", stdout);
435     }
436
437     for (y = 0; y < table->n_rows; y++) {
438         fputs("  <tr>\n", stdout);
439         for (x = 0; x < table->n_columns; x++) {
440             table_print_html_cell__("td", *table_cell__(table, y, x));
441         }
442         fputs("  </tr>\n", stdout);
443     }
444
445     fputs("</table>\n", stdout);
446 }
447
448 static void
449 table_print_csv_cell__(const char *content)
450 {
451     const char *p;
452
453     if (!strpbrk(content, "\n\",")) {
454         fputs(content, stdout);
455     } else {
456         putchar('"');
457         for (p = content; *p != '\0'; p++) {
458             switch (*p) {
459             case '"':
460                 fputs("\"\"", stdout);
461                 break;
462             default:
463                 putchar(*p);
464                 break;
465             }
466         }
467         putchar('"');
468     }
469 }
470
471 static void
472 table_print_csv__(const struct table *table)
473 {
474     size_t x, y;
475
476     if (output_headings) {
477         for (x = 0; x < table->n_columns; x++) {
478             const struct column *column = &table->columns[x];
479             if (x) {
480                 putchar(',');
481             }
482             table_print_csv_cell__(column->heading);
483         }
484         putchar('\n');
485     }
486
487     for (y = 0; y < table->n_rows; y++) {
488         for (x = 0; x < table->n_columns; x++) {
489             if (x) {
490                 putchar(',');
491             }
492             table_print_csv_cell__(*table_cell__(table, y, x));
493         }
494         putchar('\n');
495     }
496 }
497
498 static void
499 table_print(const struct table *table)
500 {
501     switch (output_format) {
502     case FMT_TABLE:
503         table_print_table__(table);
504         break;
505
506     case FMT_HTML:
507         table_print_html__(table);
508         break;
509
510     case FMT_CSV:
511         table_print_csv__(table);
512         break;
513     }
514 }
515 \f
516 static void
517 do_get_schema(int argc UNUSED, char *argv[])
518 {
519     struct ovsdb_schema *schema = fetch_schema(argv[1]);
520     print_and_free_json(ovsdb_schema_to_json(schema));
521     ovsdb_schema_destroy(schema);
522 }
523
524 static void
525 do_list_tables(int argc UNUSED, char *argv[])
526 {
527     struct ovsdb_schema *schema;
528     struct shash_node *node;
529     struct table t;
530
531     schema = fetch_schema(argv[1]);
532     table_init(&t);
533     table_add_column(&t, "Table");
534     table_add_column(&t, "Comment");
535     SHASH_FOR_EACH (node, &schema->tables) {
536         struct ovsdb_table_schema *ts = node->data;
537
538         table_add_row(&t);
539         table_add_cell(&t, ts->name);
540         if (ts->comment) {
541             table_add_cell(&t, ts->comment);
542         }
543     }
544     ovsdb_schema_destroy(schema);
545     table_print(&t);
546 }
547
548 static void
549 do_list_columns(int argc UNUSED, char *argv[])
550 {
551     const char *table_name = argv[2];
552     struct ovsdb_schema *schema;
553     struct shash_node *table_node;
554     struct table t;
555
556     schema = fetch_schema(argv[1]);
557     table_init(&t);
558     if (!table_name) {
559         table_add_column(&t, "Table");
560     }
561     table_add_column(&t, "Column");
562     table_add_column(&t, "Type");
563     table_add_column(&t, "Comment");
564     SHASH_FOR_EACH (table_node, &schema->tables) {
565         struct ovsdb_table_schema *ts = table_node->data;
566
567         if (!table_name || !strcmp(table_name, ts->name)) {
568             struct shash_node *column_node;
569
570             SHASH_FOR_EACH (column_node, &ts->columns) {
571                 struct ovsdb_column *column = column_node->data;
572                 struct json *type = ovsdb_type_to_json(&column->type);
573
574                 table_add_row(&t);
575                 if (!table_name) {
576                     table_add_cell(&t, ts->name);
577                 }
578                 table_add_cell(&t, column->name);
579                 table_add_cell_nocopy(&t, json_to_string(type, JSSF_SORT));
580                 if (column->comment) {
581                     table_add_cell(&t, column->comment);
582                 }
583
584                 json_destroy(type);
585             }
586         }
587     }
588     ovsdb_schema_destroy(schema);
589     table_print(&t);
590 }
591
592 static void
593 do_transact(int argc UNUSED, char *argv[] UNUSED)
594 {
595     struct jsonrpc_msg *request, *reply;
596     struct json *transaction;
597     struct jsonrpc *rpc;
598     int error;
599
600     transaction = parse_json(argv[2]);
601
602     rpc = open_jsonrpc(argv[1]);
603     request = jsonrpc_create_request("transact", transaction);
604     error = jsonrpc_transact_block(rpc, request, &reply);
605     if (error) {
606         ovs_fatal(error, "transaction failed");
607     }
608     if (reply->error) {
609         ovs_fatal(error, "transaction returned error: %s",
610                   json_to_string(reply->error, JSSF_SORT));
611     }
612     print_json(reply->result);
613     putchar('\n');
614     jsonrpc_msg_destroy(reply);
615     jsonrpc_close(rpc);
616 }
617
618 static void
619 do_help(int argc UNUSED, char *argv[] UNUSED)
620 {
621     usage();
622 }
623
624 static const struct command all_commands[] = {
625     { "get-schema", 1, 1, do_get_schema },
626     { "list-tables", 1, 1, do_list_tables },
627     { "list-columns", 1, 2, do_list_columns },
628     { "transact", 2, 2, do_transact },
629     { "help", 0, INT_MAX, do_help },
630     { NULL, 0, 0, NULL },
631 };