ovsdb-client: Make "server" and "database" arguments optional.
[openvswitch] / ovsdb / ovsdb-client.c
1 /*
2  * Copyright (c) 2009, 2010, 2011 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 <assert.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <getopt.h>
23 #include <limits.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "command-line.h"
30 #include "column.h"
31 #include "compiler.h"
32 #include "daemon.h"
33 #include "dirs.h"
34 #include "dynamic-string.h"
35 #include "json.h"
36 #include "jsonrpc.h"
37 #include "lib/table.h"
38 #include "ovsdb.h"
39 #include "ovsdb-data.h"
40 #include "ovsdb-error.h"
41 #include "sort.h"
42 #include "sset.h"
43 #include "stream.h"
44 #include "stream-ssl.h"
45 #include "table.h"
46 #include "timeval.h"
47 #include "util.h"
48 #include "vlog.h"
49
50 VLOG_DEFINE_THIS_MODULE(ovsdb_client);
51
52 enum args_needed {
53     NEED_NONE,            /* No JSON-RPC connection or database name needed. */
54     NEED_RPC,             /* JSON-RPC connection needed. */
55     NEED_DATABASE         /* JSON-RPC connection and database name needed. */
56 };
57
58 struct ovsdb_client_command {
59     const char *name;
60     enum args_needed need;
61     int min_args;
62     int max_args;
63     void (*handler)(struct jsonrpc *rpc, const char *database,
64                     int argc, char *argv[]);
65 };
66
67 /* Format for table output. */
68 static struct table_style table_style = TABLE_STYLE_DEFAULT;
69
70 static const struct ovsdb_client_command all_commands[];
71
72 static void usage(void) NO_RETURN;
73 static void parse_options(int argc, char *argv[]);
74 static struct jsonrpc *open_jsonrpc(const char *server);
75 static void fetch_dbs(struct jsonrpc *, struct sset *dbs);
76
77 int
78 main(int argc, char *argv[])
79 {
80     const struct ovsdb_client_command *command;
81     const char *database;
82     struct jsonrpc *rpc;
83
84     proctitle_init(argc, argv);
85     set_program_name(argv[0]);
86     parse_options(argc, argv);
87     signal(SIGPIPE, SIG_IGN);
88
89     if (optind >= argc) {
90         ovs_fatal(0, "missing command name; use --help for help");
91     }
92
93     for (command = all_commands; ; command++) {
94         if (!command->name) {
95             VLOG_FATAL("unknown command '%s'; use --help for help",
96                        argv[optind]);
97         } else if (!strcmp(command->name, argv[optind])) {
98             break;
99         }
100     }
101     optind++;
102
103     if (command->need != NEED_NONE) {
104         if (argc - optind > command->min_args
105             && (isalpha((unsigned char) argv[optind][0])
106                 && strchr(argv[optind], ':'))) {
107             rpc = open_jsonrpc(argv[optind++]);
108         } else {
109             char *sock = xasprintf("unix:%s/db.sock", ovs_rundir());
110             rpc = open_jsonrpc(sock);
111             free(sock);
112         }
113     } else {
114         rpc = NULL;
115     }
116
117     if (command->need == NEED_DATABASE) {
118         struct sset dbs;
119
120         sset_init(&dbs);
121         fetch_dbs(rpc, &dbs);
122         if (argc - optind > command->min_args
123             && sset_contains(&dbs, argv[optind])) {
124             database = argv[optind++];
125         } else if (sset_count(&dbs) == 1) {
126             database = xstrdup(SSET_FIRST(&dbs));
127         } else if (sset_contains(&dbs, "Open_vSwitch")) {
128             database = "Open_vSwitch";
129         } else {
130             ovs_fatal(0, "no default database for `%s' command, please "
131                       "specify a database name", command->name);
132         }
133         sset_destroy(&dbs);
134     } else {
135         database = NULL;
136     }
137
138     if (argc - optind < command->min_args ||
139         argc - optind > command->max_args) {
140         VLOG_FATAL("invalid syntax for '%s' (use --help for help)",
141                     command->name);
142     }
143
144     command->handler(rpc, database, argc - optind, argv + optind);
145
146     jsonrpc_close(rpc);
147
148     if (ferror(stdout)) {
149         VLOG_FATAL("write to stdout failed");
150     }
151     if (ferror(stderr)) {
152         VLOG_FATAL("write to stderr failed");
153     }
154
155     return 0;
156 }
157
158 static void
159 parse_options(int argc, char *argv[])
160 {
161     enum {
162         OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,
163         DAEMON_OPTION_ENUMS,
164         TABLE_OPTION_ENUMS
165     };
166     static struct option long_options[] = {
167         {"verbose", optional_argument, NULL, 'v'},
168         {"help", no_argument, NULL, 'h'},
169         {"version", no_argument, NULL, 'V'},
170         DAEMON_LONG_OPTIONS,
171 #ifdef HAVE_OPENSSL
172         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
173         STREAM_SSL_LONG_OPTIONS,
174 #endif
175         TABLE_LONG_OPTIONS,
176         {NULL, 0, NULL, 0},
177     };
178     char *short_options = long_options_to_short_options(long_options);
179
180     for (;;) {
181         int c;
182
183         c = getopt_long(argc, argv, short_options, long_options, NULL);
184         if (c == -1) {
185             break;
186         }
187
188         switch (c) {
189         case 'h':
190             usage();
191
192         case 'V':
193             OVS_PRINT_VERSION(0, 0);
194             exit(EXIT_SUCCESS);
195
196         case 'v':
197             vlog_set_verbosity(optarg);
198             break;
199
200         DAEMON_OPTION_HANDLERS
201
202         TABLE_OPTION_HANDLERS(&table_style)
203
204         STREAM_SSL_OPTION_HANDLERS
205
206         case OPT_BOOTSTRAP_CA_CERT:
207             stream_ssl_set_ca_cert_file(optarg, true);
208             break;
209
210         case '?':
211             exit(EXIT_FAILURE);
212
213         case 0:
214             /* getopt_long() already set the value for us. */
215             break;
216
217         default:
218             abort();
219         }
220     }
221     free(short_options);
222 }
223
224 static void
225 usage(void)
226 {
227     printf("%s: Open vSwitch database JSON-RPC client\n"
228            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
229            "\nValid commands are:\n"
230            "\n  list-dbs [SERVER]\n"
231            "    list databases available on SERVER\n"
232            "\n  get-schema [SERVER] [DATABASE]\n"
233            "    retrieve schema for DATABASE from SERVER\n"
234            "\n  get-schema-version [SERVER] [DATABASE]\n"
235            "    retrieve schema for DATABASE from SERVER and report only its\n"
236            "    version number on stdout\n"
237            "\n  list-tables [SERVER] [DATABASE]\n"
238            "    list tables for DATABASE on SERVER\n"
239            "\n  list-columns [SERVER] [DATABASE] [TABLE]\n"
240            "    list columns in TABLE (or all tables) in DATABASE on SERVER\n"
241            "\n  transact [SERVER] TRANSACTION\n"
242            "    run TRANSACTION (a JSON array of operations) on SERVER\n"
243            "    and print the results as JSON on stdout\n"
244            "\n  monitor [SERVER] [DATABASE] TABLE [COLUMN,...]...\n"
245            "    monitor contents of COLUMNs in TABLE in DATABASE on SERVER.\n"
246            "    COLUMNs may include !initial, !insert, !delete, !modify\n"
247            "    to avoid seeing the specified kinds of changes.\n"
248            "\n  dump [SERVER] [DATABASE]\n"
249            "    dump contents of DATABASE on SERVER to stdout\n"
250            "\nThe default SERVER is unix:%s/db.sock.\n"
251            "The default DATABASE is Open_vSwitch.\n",
252            program_name, program_name, ovs_rundir());
253     stream_usage("SERVER", true, true, true);
254     printf("\nOutput formatting options:\n"
255            "  -f, --format=FORMAT         set output formatting to FORMAT\n"
256            "                              (\"table\", \"html\", \"csv\", "
257            "or \"json\")\n"
258            "  --no-headings               omit table heading row\n"
259            "  --pretty                    pretty-print JSON in output");
260     daemon_usage();
261     vlog_usage();
262     printf("\nOther options:\n"
263            "  -h, --help                  display this help message\n"
264            "  -V, --version               display version information\n");
265     exit(EXIT_SUCCESS);
266 }
267 \f
268 static struct json *
269 parse_json(const char *s)
270 {
271     struct json *json = json_from_string(s);
272     if (json->type == JSON_STRING) {
273         ovs_fatal(0, "\"%s\": %s", s, json->u.string);
274     }
275     return json;
276 }
277
278 static struct jsonrpc *
279 open_jsonrpc(const char *server)
280 {
281     struct stream *stream;
282     int error;
283
284     error = stream_open_block(jsonrpc_stream_open(server, &stream), &stream);
285     if (error == EAFNOSUPPORT) {
286         struct pstream *pstream;
287
288         error = jsonrpc_pstream_open(server, &pstream);
289         if (error) {
290             ovs_fatal(error, "failed to connect or listen to \"%s\"", server);
291         }
292
293         VLOG_INFO("%s: waiting for connection...", server);
294         error = pstream_accept_block(pstream, &stream);
295         if (error) {
296             ovs_fatal(error, "failed to accept connection on \"%s\"", server);
297         }
298
299         pstream_close(pstream);
300     } else if (error) {
301         ovs_fatal(error, "failed to connect to \"%s\"", server);
302     }
303
304     return jsonrpc_open(stream);
305 }
306
307 static void
308 print_json(struct json *json)
309 {
310     char *string = json_to_string(json, table_style.json_flags);
311     fputs(string, stdout);
312     free(string);
313 }
314
315 static void
316 print_and_free_json(struct json *json)
317 {
318     print_json(json);
319     json_destroy(json);
320 }
321
322 static void
323 check_ovsdb_error(struct ovsdb_error *error)
324 {
325     if (error) {
326         ovs_fatal(0, "%s", ovsdb_error_to_string(error));
327     }
328 }
329
330 static struct ovsdb_schema *
331 fetch_schema(struct jsonrpc *rpc, const char *database)
332 {
333     struct jsonrpc_msg *request, *reply;
334     struct ovsdb_schema *schema;
335     int error;
336
337     request = jsonrpc_create_request("get_schema",
338                                      json_array_create_1(
339                                          json_string_create(database)),
340                                      NULL);
341     error = jsonrpc_transact_block(rpc, request, &reply);
342     if (error) {
343         ovs_fatal(error, "transaction failed");
344     }
345     check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema));
346     jsonrpc_msg_destroy(reply);
347
348     return schema;
349 }
350
351 static void
352 fetch_dbs(struct jsonrpc *rpc, struct sset *dbs)
353 {
354     struct jsonrpc_msg *request, *reply;
355     int error;
356     size_t i;
357
358     request = jsonrpc_create_request("list_dbs", json_array_create_empty(),
359                                      NULL);
360     error = jsonrpc_transact_block(rpc, request, &reply);
361     if (error) {
362         ovs_fatal(error, "transaction failed");
363     }
364
365     if (reply->result->type != JSON_ARRAY) {
366         ovs_fatal(0, "list_dbs response is not array");
367     }
368
369     for (i = 0; i < reply->result->u.array.n; i++) {
370         const struct json *name = reply->result->u.array.elems[i];
371
372         if (name->type != JSON_STRING) {
373             ovs_fatal(0, "list_dbs response %zu is not string", i);
374         }
375         sset_add(dbs, name->u.string);
376     }
377     jsonrpc_msg_destroy(reply);
378 }
379 \f
380 static void
381 do_list_dbs(struct jsonrpc *rpc, const char *database OVS_UNUSED,
382             int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
383 {
384     const char *db_name;
385     struct sset dbs;
386
387     sset_init(&dbs);
388     fetch_dbs(rpc, &dbs);
389     SSET_FOR_EACH (db_name, &dbs) {
390         puts(db_name);
391     }
392     sset_destroy(&dbs);
393 }
394
395 static void
396 do_get_schema(struct jsonrpc *rpc, const char *database,
397               int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
398 {
399     struct ovsdb_schema *schema = fetch_schema(rpc, database);
400     print_and_free_json(ovsdb_schema_to_json(schema));
401     ovsdb_schema_destroy(schema);
402 }
403
404 static void
405 do_get_schema_version(struct jsonrpc *rpc, const char *database,
406                       int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
407 {
408     struct ovsdb_schema *schema = fetch_schema(rpc, database);
409     puts(schema->version);
410     ovsdb_schema_destroy(schema);
411 }
412
413 static void
414 do_list_tables(struct jsonrpc *rpc, const char *database,
415                int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
416 {
417     struct ovsdb_schema *schema;
418     struct shash_node *node;
419     struct table t;
420
421     schema = fetch_schema(rpc, database);
422     table_init(&t);
423     table_add_column(&t, "Table");
424     SHASH_FOR_EACH (node, &schema->tables) {
425         struct ovsdb_table_schema *ts = node->data;
426
427         table_add_row(&t);
428         table_add_cell(&t)->text = xstrdup(ts->name);
429     }
430     ovsdb_schema_destroy(schema);
431     table_print(&t, &table_style);
432 }
433
434 static void
435 do_list_columns(struct jsonrpc *rpc, const char *database,
436                 int argc OVS_UNUSED, char *argv[])
437 {
438     const char *table_name = argv[0];
439     struct ovsdb_schema *schema;
440     struct shash_node *table_node;
441     struct table t;
442
443     schema = fetch_schema(rpc, database);
444     table_init(&t);
445     if (!table_name) {
446         table_add_column(&t, "Table");
447     }
448     table_add_column(&t, "Column");
449     table_add_column(&t, "Type");
450     SHASH_FOR_EACH (table_node, &schema->tables) {
451         struct ovsdb_table_schema *ts = table_node->data;
452
453         if (!table_name || !strcmp(table_name, ts->name)) {
454             struct shash_node *column_node;
455
456             SHASH_FOR_EACH (column_node, &ts->columns) {
457                 const struct ovsdb_column *column = column_node->data;
458
459                 table_add_row(&t);
460                 if (!table_name) {
461                     table_add_cell(&t)->text = xstrdup(ts->name);
462                 }
463                 table_add_cell(&t)->text = xstrdup(column->name);
464                 table_add_cell(&t)->json = ovsdb_type_to_json(&column->type);
465             }
466         }
467     }
468     ovsdb_schema_destroy(schema);
469     table_print(&t, &table_style);
470 }
471
472 static void
473 do_transact(struct jsonrpc *rpc, const char *database OVS_UNUSED,
474             int argc OVS_UNUSED, char *argv[])
475 {
476     struct jsonrpc_msg *request, *reply;
477     struct json *transaction;
478     int error;
479
480     transaction = parse_json(argv[0]);
481
482     request = jsonrpc_create_request("transact", transaction, NULL);
483     error = jsonrpc_transact_block(rpc, request, &reply);
484     if (error) {
485         ovs_fatal(error, "transaction failed");
486     }
487     if (reply->error) {
488         ovs_fatal(error, "transaction returned error: %s",
489                   json_to_string(reply->error, table_style.json_flags));
490     }
491     print_json(reply->result);
492     putchar('\n');
493     jsonrpc_msg_destroy(reply);
494 }
495
496 static void
497 monitor_print_row(struct json *row, const char *type, const char *uuid,
498                   const struct ovsdb_column_set *columns, struct table *t)
499 {
500     size_t i;
501
502     if (!row) {
503         ovs_error(0, "missing %s row", type);
504         return;
505     } else if (row->type != JSON_OBJECT) {
506         ovs_error(0, "<row> is not object");
507         return;
508     }
509
510     table_add_row(t);
511     table_add_cell(t)->text = xstrdup(uuid);
512     table_add_cell(t)->text = xstrdup(type);
513     for (i = 0; i < columns->n_columns; i++) {
514         const struct ovsdb_column *column = columns->columns[i];
515         struct json *value = shash_find_data(json_object(row), column->name);
516         struct cell *cell = table_add_cell(t);
517         if (value) {
518             cell->json = json_clone(value);
519             cell->type = &column->type;
520         }
521     }
522 }
523
524 static void
525 monitor_print(struct json *table_updates,
526               const struct ovsdb_table_schema *table,
527               const struct ovsdb_column_set *columns, bool initial)
528 {
529     struct json *table_update;
530     struct shash_node *node;
531     struct table t;
532     size_t i;
533
534     table_init(&t);
535
536     if (table_updates->type != JSON_OBJECT) {
537         ovs_error(0, "<table-updates> is not object");
538         return;
539     }
540     table_update = shash_find_data(json_object(table_updates), table->name);
541     if (!table_update) {
542         return;
543     }
544     if (table_update->type != JSON_OBJECT) {
545         ovs_error(0, "<table-update> is not object");
546         return;
547     }
548
549     table_add_column(&t, "row");
550     table_add_column(&t, "action");
551     for (i = 0; i < columns->n_columns; i++) {
552         table_add_column(&t, "%s", columns->columns[i]->name);
553     }
554     SHASH_FOR_EACH (node, json_object(table_update)) {
555         struct json *row_update = node->data;
556         struct json *old, *new;
557
558         if (row_update->type != JSON_OBJECT) {
559             ovs_error(0, "<row-update> is not object");
560             continue;
561         }
562         old = shash_find_data(json_object(row_update), "old");
563         new = shash_find_data(json_object(row_update), "new");
564         if (initial) {
565             monitor_print_row(new, "initial", node->name, columns, &t);
566         } else if (!old) {
567             monitor_print_row(new, "insert", node->name, columns, &t);
568         } else if (!new) {
569             monitor_print_row(old, "delete", node->name, columns, &t);
570         } else {
571             monitor_print_row(old, "old", node->name, columns, &t);
572             monitor_print_row(new, "new", "", columns, &t);
573         }
574     }
575     table_print(&t, &table_style);
576     table_destroy(&t);
577 }
578
579 static void
580 add_column(const char *server, const struct ovsdb_column *column,
581            struct ovsdb_column_set *columns, struct json *columns_json)
582 {
583     if (ovsdb_column_set_contains(columns, column->index)) {
584         ovs_fatal(0, "%s: column \"%s\" mentioned multiple times",
585                   server, column->name);
586     }
587     ovsdb_column_set_add(columns, column);
588     json_array_add(columns_json, json_string_create(column->name));
589 }
590
591 static struct json *
592 parse_monitor_columns(char *arg, const char *server, const char *database,
593                       const struct ovsdb_table_schema *table,
594                       struct ovsdb_column_set *columns)
595 {
596     bool initial, insert, delete, modify;
597     struct json *mr, *columns_json;
598     char *save_ptr = NULL;
599     char *token;
600
601     mr = json_object_create();
602     columns_json = json_array_create_empty();
603     json_object_put(mr, "columns", columns_json);
604
605     initial = insert = delete = modify = true;
606     for (token = strtok_r(arg, ",", &save_ptr); token != NULL;
607          token = strtok_r(NULL, ",", &save_ptr)) {
608         if (!strcmp(token, "!initial")) {
609             initial = false;
610         } else if (!strcmp(token, "!insert")) {
611             insert = false;
612         } else if (!strcmp(token, "!delete")) {
613             delete = false;
614         } else if (!strcmp(token, "!modify")) {
615             modify = false;
616         } else {
617             const struct ovsdb_column *column;
618
619             column = ovsdb_table_schema_get_column(table, token);
620             if (!column) {
621                 ovs_fatal(0, "%s: table \"%s\" in %s does not have a "
622                           "column named \"%s\"",
623                           server, table->name, database, token);
624             }
625             add_column(server, column, columns, columns_json);
626         }
627     }
628
629     if (columns_json->u.array.n == 0) {
630         const struct shash_node **nodes;
631         size_t i, n;
632
633         n = shash_count(&table->columns);
634         nodes = shash_sort(&table->columns);
635         for (i = 0; i < n; i++) {
636             const struct ovsdb_column *column = nodes[i]->data;
637             if (column->index != OVSDB_COL_UUID
638                 && column->index != OVSDB_COL_VERSION) {
639                 add_column(server, column, columns, columns_json);
640             }
641         }
642         free(nodes);
643
644         add_column(server, ovsdb_table_schema_get_column(table,"_version"),
645                    columns, columns_json);
646     }
647
648     if (!initial || !insert || !delete || !modify) {
649         struct json *select = json_object_create();
650         json_object_put(select, "initial", json_boolean_create(initial));
651         json_object_put(select, "insert", json_boolean_create(insert));
652         json_object_put(select, "delete", json_boolean_create(delete));
653         json_object_put(select, "modify", json_boolean_create(modify));
654         json_object_put(mr, "select", select);
655     }
656
657     return mr;
658 }
659
660 static void
661 do_monitor(struct jsonrpc *rpc, const char *database,
662            int argc, char *argv[])
663 {
664     const char *server = jsonrpc_get_name(rpc);
665     const char *table_name = argv[0];
666     struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
667     struct ovsdb_table_schema *table;
668     struct ovsdb_schema *schema;
669     struct jsonrpc_msg *request;
670     struct json *monitor, *monitor_request_array,
671         *monitor_requests, *request_id;
672
673     schema = fetch_schema(rpc, database);
674     table = shash_find_data(&schema->tables, table_name);
675     if (!table) {
676         ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
677                   server, database, table_name);
678     }
679
680     monitor_request_array = json_array_create_empty();
681     if (argc > 1) {
682         int i;
683
684         for (i = 1; i < argc; i++) {
685             json_array_add(
686                 monitor_request_array,
687                 parse_monitor_columns(argv[i], server, database, table,
688                                       &columns));
689         }
690     } else {
691         /* Allocate a writable empty string since parse_monitor_columns() is
692          * going to strtok() it and that's risky with literal "". */
693         char empty[] = "";
694         json_array_add(
695             monitor_request_array,
696             parse_monitor_columns(empty, server, database, table, &columns));
697     }
698
699     monitor_requests = json_object_create();
700     json_object_put(monitor_requests, table_name, monitor_request_array);
701
702     monitor = json_array_create_3(json_string_create(database),
703                                   json_null_create(), monitor_requests);
704     request = jsonrpc_create_request("monitor", monitor, NULL);
705     request_id = json_clone(request->id);
706     jsonrpc_send(rpc, request);
707     for (;;) {
708         struct jsonrpc_msg *msg;
709         int error;
710
711         error = jsonrpc_recv_block(rpc, &msg);
712         if (error) {
713             ovsdb_schema_destroy(schema);
714             ovs_fatal(error, "%s: receive failed", server);
715         }
716
717         if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
718             jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
719                                                    msg->id));
720         } else if (msg->type == JSONRPC_REPLY
721                    && json_equal(msg->id, request_id)) {
722             monitor_print(msg->result, table, &columns, true);
723             fflush(stdout);
724             if (get_detach()) {
725                 /* daemonize() closes the standard file descriptors.  We output
726                  * to stdout, so we need to save and restore STDOUT_FILENO. */
727                 int fd = dup(STDOUT_FILENO);
728                 daemonize();
729                 dup2(fd, STDOUT_FILENO);
730                 close(fd);
731             }
732         } else if (msg->type == JSONRPC_NOTIFY
733                    && !strcmp(msg->method, "update")) {
734             struct json *params = msg->params;
735             if (params->type == JSON_ARRAY
736                 && params->u.array.n == 2
737                 && params->u.array.elems[0]->type == JSON_NULL) {
738                 monitor_print(params->u.array.elems[1],
739                               table, &columns, false);
740                 fflush(stdout);
741             }
742         }
743         jsonrpc_msg_destroy(msg);
744     }
745 }
746
747 struct dump_table_aux {
748     struct ovsdb_datum **data;
749     const struct ovsdb_column **columns;
750     size_t n_columns;
751 };
752
753 static int
754 compare_data(size_t a_y, size_t b_y, size_t x,
755              const struct dump_table_aux *aux)
756 {
757     return ovsdb_datum_compare_3way(&aux->data[a_y][x],
758                                     &aux->data[b_y][x],
759                                     &aux->columns[x]->type);
760 }
761
762 static int
763 compare_rows(size_t a_y, size_t b_y, void *aux_)
764 {
765     struct dump_table_aux *aux = aux_;
766     size_t x;
767
768     /* Skip UUID columns on the first pass, since their values tend to be
769      * random and make our results less reproducible. */
770     for (x = 0; x < aux->n_columns; x++) {
771         if (aux->columns[x]->type.key.type != OVSDB_TYPE_UUID) {
772             int cmp = compare_data(a_y, b_y, x, aux);
773             if (cmp) {
774                 return cmp;
775             }
776         }
777     }
778
779     /* Use UUID columns as tie-breakers. */
780     for (x = 0; x < aux->n_columns; x++) {
781         if (aux->columns[x]->type.key.type == OVSDB_TYPE_UUID) {
782             int cmp = compare_data(a_y, b_y, x, aux);
783             if (cmp) {
784                 return cmp;
785             }
786         }
787     }
788
789     return 0;
790 }
791
792 static void
793 swap_rows(size_t a_y, size_t b_y, void *aux_)
794 {
795     struct dump_table_aux *aux = aux_;
796     struct ovsdb_datum *tmp = aux->data[a_y];
797     aux->data[a_y] = aux->data[b_y];
798     aux->data[b_y] = tmp;
799 }
800
801 static int
802 compare_columns(const void *a_, const void *b_)
803 {
804     const struct ovsdb_column *const *ap = a_;
805     const struct ovsdb_column *const *bp = b_;
806     const struct ovsdb_column *a = *ap;
807     const struct ovsdb_column *b = *bp;
808
809     return strcmp(a->name, b->name);
810 }
811
812 static void
813 dump_table(const struct ovsdb_table_schema *ts, struct json_array *rows)
814 {
815     const struct ovsdb_column **columns;
816     size_t n_columns;
817
818     struct ovsdb_datum **data;
819
820     struct dump_table_aux aux;
821     struct shash_node *node;
822     struct table t;
823     size_t x, y;
824
825     /* Sort columns by name, for reproducibility. */
826     columns = xmalloc(shash_count(&ts->columns) * sizeof *columns);
827     n_columns = 0;
828     SHASH_FOR_EACH (node, &ts->columns) {
829         struct ovsdb_column *column = node->data;
830         if (strcmp(column->name, "_version")) {
831             columns[n_columns++] = column;
832         }
833     }
834     qsort(columns, n_columns, sizeof *columns, compare_columns);
835
836     /* Extract data from table. */
837     data = xmalloc(rows->n * sizeof *data);
838     for (y = 0; y < rows->n; y++) {
839         struct shash *row;
840
841         if (rows->elems[y]->type != JSON_OBJECT) {
842             ovs_fatal(0,  "row %zu in table %s response is not a JSON object: "
843                       "%s", y, ts->name, json_to_string(rows->elems[y], 0));
844         }
845         row = json_object(rows->elems[y]);
846
847         data[y] = xmalloc(n_columns * sizeof **data);
848         for (x = 0; x < n_columns; x++) {
849             const struct json *json = shash_find_data(row, columns[x]->name);
850             if (!json) {
851                 ovs_fatal(0, "row %zu in table %s response lacks %s column",
852                           y, ts->name, columns[x]->name);
853             }
854
855             check_ovsdb_error(ovsdb_datum_from_json(&data[y][x],
856                                                     &columns[x]->type,
857                                                     json, NULL));
858         }
859     }
860
861     /* Sort rows by column values, for reproducibility. */
862     aux.data = data;
863     aux.columns = columns;
864     aux.n_columns = n_columns;
865     sort(rows->n, compare_rows, swap_rows, &aux);
866
867     /* Add column headings. */
868     table_init(&t);
869     table_set_caption(&t, xasprintf("%s table", ts->name));
870     for (x = 0; x < n_columns; x++) {
871         table_add_column(&t, "%s", columns[x]->name);
872     }
873
874     /* Print rows. */
875     for (y = 0; y < rows->n; y++) {
876         table_add_row(&t);
877         for (x = 0; x < n_columns; x++) {
878             struct cell *cell = table_add_cell(&t);
879             cell->json = ovsdb_datum_to_json(&data[y][x], &columns[x]->type);
880             cell->type = &columns[x]->type;
881         }
882     }
883     table_print(&t, &table_style);
884     table_destroy(&t);
885 }
886
887 static void
888 do_dump(struct jsonrpc *rpc, const char *database,
889         int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
890 {
891     struct jsonrpc_msg *request, *reply;
892     struct ovsdb_schema *schema;
893     struct json *transaction;
894     int error;
895
896     const struct shash_node **tables;
897     size_t n_tables;
898
899     size_t i;
900
901     schema = fetch_schema(rpc, database);
902     tables = shash_sort(&schema->tables);
903     n_tables = shash_count(&schema->tables);
904
905     /* Construct transaction to retrieve entire database. */
906     transaction = json_array_create_1(json_string_create(database));
907     for (i = 0; i < n_tables; i++) {
908         const struct ovsdb_table_schema *ts = tables[i]->data;
909         struct json *op, *columns;
910         struct shash_node *node;
911
912         columns = json_array_create_empty();
913         SHASH_FOR_EACH (node, &ts->columns) {
914             const struct ovsdb_column *column = node->data;
915
916             if (strcmp(column->name, "_version")) {
917                 json_array_add(columns, json_string_create(column->name));
918             }
919         }
920
921         op = json_object_create();
922         json_object_put_string(op, "op", "select");
923         json_object_put_string(op, "table", tables[i]->name);
924         json_object_put(op, "where", json_array_create_empty());
925         json_object_put(op, "columns", columns);
926         json_array_add(transaction, op);
927     }
928
929     /* Send request, get reply. */
930     request = jsonrpc_create_request("transact", transaction, NULL);
931     error = jsonrpc_transact_block(rpc, request, &reply);
932     if (error) {
933         ovs_fatal(error, "transaction failed");
934     }
935
936     /* Print database contents. */
937     if (reply->result->type != JSON_ARRAY
938         || reply->result->u.array.n != n_tables) {
939         ovs_fatal(0, "reply is not array of %zu elements: %s",
940                   n_tables, json_to_string(reply->result, 0));
941     }
942     for (i = 0; i < n_tables; i++) {
943         const struct ovsdb_table_schema *ts = tables[i]->data;
944         const struct json *op_result = reply->result->u.array.elems[i];
945         struct json *rows;
946
947         if (op_result->type != JSON_OBJECT
948             || !(rows = shash_find_data(json_object(op_result), "rows"))
949             || rows->type != JSON_ARRAY) {
950             ovs_fatal(0, "%s table reply is not an object with a \"rows\" "
951                       "member array: %s",
952                       ts->name, json_to_string(op_result, 0));
953         }
954
955         dump_table(ts, &rows->u.array);
956     }
957 }
958
959 static void
960 do_help(struct jsonrpc *rpc OVS_UNUSED, const char *database OVS_UNUSED,
961         int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
962 {
963     usage();
964 }
965
966 /* All command handlers (except for "help") are expected to take an optional
967  * server socket name (e.g. "unix:...") as their first argument.  The socket
968  * name argument must be included in max_args (but left out of min_args).  The
969  * command name and socket name are not included in the arguments passed to the
970  * handler: the argv[0] passed to the handler is the first argument after the
971  * optional server socket name.  The connection to the server is available as
972  * global variable 'rpc'. */
973 static const struct ovsdb_client_command all_commands[] = {
974     { "list-dbs",           NEED_RPC,      0, 0,       do_list_dbs },
975     { "get-schema",         NEED_DATABASE, 0, 0,       do_get_schema },
976     { "get-schema-version", NEED_DATABASE, 0, 0,       do_get_schema_version },
977     { "list-tables",        NEED_DATABASE, 0, 0,       do_list_tables },
978     { "list-columns",       NEED_DATABASE, 0, 1,       do_list_columns },
979     { "transact",           NEED_RPC,      1, 1,       do_transact },
980     { "monitor",            NEED_DATABASE, 1, INT_MAX, do_monitor },
981     { "dump",               NEED_DATABASE, 0, 0,       do_dump },
982
983     { "help",               NEED_NONE,     0, INT_MAX, do_help },
984
985     { NULL,                 0,             0, 0,       NULL },
986 };