Merge remote-tracking branch 'origin/master' into ovn
[cascardo/ovs.git] / ovn / ovn-nbctl.c
1 /*
2  * Licensed under the Apache License, Version 2.0 (the "License");
3  * you may not use this file except in compliance with the License.
4  * You may obtain a copy of the License at:
5  *
6  *     http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14
15 #include <config.h>
16
17 #include <getopt.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20
21 #include "command-line.h"
22 #include "dirs.h"
23 #include "fatal-signal.h"
24 #include "ovn/ovn-nb-idl.h"
25 #include "poll-loop.h"
26 #include "process.h"
27 #include "stream.h"
28 #include "stream-ssl.h"
29 #include "util.h"
30 #include "openvswitch/vlog.h"
31
32 VLOG_DEFINE_THIS_MODULE(ovn_nbctl);
33
34 struct nbctl_context {
35     struct ovsdb_idl *idl;
36     struct ovsdb_idl_txn *txn;
37 };
38
39 static const char *db;
40
41 static const char *default_db(void);
42
43 static void
44 usage(void)
45 {
46     printf("\
47 %s: OVN northbound DB management utility\n\
48 usage: %s [OPTIONS] COMMAND [ARG...]\n\
49 \n\
50 Logical Switch Commands:\n\
51   lswitch-add [name]        Create a logical switch\n\
52   lswitch-del <lswitch>     Delete a logical switch\n\
53   lswitch-list              List configured logical switches\n\
54   lswitch-set-external-id <lswitch> <key> [value]\n\
55                             Set or delete an external:id on a logical switch\n\
56   lswitch-get-external-id <lswitch> [key]\n\
57                             List one or all external:ids set on a switch\n\
58 \n\
59 Logical Port Commands:\n\
60   lport-add <name> <lswitch> Create a logical port on a logical switch\n\
61   lport-del <lport>         Delete a logical port (by name or UUID)\n\
62   lport-list <lswitch>      List ports on a logical switch\n\
63   lport-set-external-id <lport> <key> [value]\n\
64                             Set or delete an external:id on a logical port\n\
65   lport-get-external-id <lport> [key]\n\
66                             List one or all external:ids set on a port\n\
67   lport-set-macs <lport> [MAC] [MAC] [...]\n\
68                             Set MAC addresses for the logical port. Specify\n\
69                             more than one using additional arguments.\n\
70   lport-get-macs <lport>    Get a list of MAC addresses on the port.\n\
71   lport-get-up <lport>      Get state of the port ('up' or 'down').\n\
72 \n\
73 Options:\n\
74   --db=DATABASE             connect to DATABASE\n\
75                             (default: %s)\n\
76   -h, --help                display this help message\n\
77   -o, --options             list available options\n\
78   -V, --version             display version information\n\
79 ", program_name, program_name, default_db());
80     vlog_usage();
81     stream_usage("database", true, true, false);
82 }
83 \f
84 static const struct nbrec_logical_switch *
85 lswitch_by_name_or_uuid(struct nbctl_context *nb_ctx, const char *id)
86 {
87     const struct nbrec_logical_switch *lswitch = NULL;
88     bool is_uuid = false;
89     bool duplicate = false;
90     struct uuid lswitch_uuid;
91
92     if (uuid_from_string(&lswitch_uuid, id)) {
93         is_uuid = true;
94         lswitch = nbrec_logical_switch_get_for_uuid(nb_ctx->idl,
95                                                     &lswitch_uuid);
96     }
97
98     if (!lswitch) {
99         const struct nbrec_logical_switch *iter;
100
101         NBREC_LOGICAL_SWITCH_FOR_EACH(iter, nb_ctx->idl) {
102             if (strcmp(iter->name, id)) {
103                 continue;
104             }
105             if (lswitch) {
106                 VLOG_WARN("There is more than one logical switch named '%s'. "
107                         "Use a UUID.", id);
108                 lswitch = NULL;
109                 duplicate = true;
110                 break;
111             }
112             lswitch = iter;
113         }
114     }
115
116     if (!lswitch && !duplicate) {
117         VLOG_WARN("lswitch not found for %s: '%s'",
118                 is_uuid ? "UUID" : "name", id);
119     }
120
121     return lswitch;
122 }
123
124 static void
125 do_lswitch_add(struct ovs_cmdl_context *ctx)
126 {
127     struct nbctl_context *nb_ctx = ctx->pvt;
128     struct nbrec_logical_switch *lswitch;
129
130     lswitch = nbrec_logical_switch_insert(nb_ctx->txn);
131     if (ctx->argc == 2) {
132         nbrec_logical_switch_set_name(lswitch, ctx->argv[1]);
133     }
134 }
135
136 static void
137 do_lswitch_del(struct ovs_cmdl_context *ctx)
138 {
139     struct nbctl_context *nb_ctx = ctx->pvt;
140     const char *id = ctx->argv[1];
141     const struct nbrec_logical_switch *lswitch;
142
143     lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
144     if (!lswitch) {
145         return;
146     }
147
148     nbrec_logical_switch_delete(lswitch);
149 }
150
151 static void
152 do_lswitch_list(struct ovs_cmdl_context *ctx)
153 {
154     struct nbctl_context *nb_ctx = ctx->pvt;
155     const struct nbrec_logical_switch *lswitch;
156
157     NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, nb_ctx->idl) {
158         printf(UUID_FMT " (%s)\n",
159                UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
160     }
161 }
162
163 static void
164 do_lswitch_set_external_id(struct ovs_cmdl_context *ctx)
165 {
166     struct nbctl_context *nb_ctx = ctx->pvt;
167     const char *id = ctx->argv[1];
168     const struct nbrec_logical_switch *lswitch;
169     struct smap new_external_ids;
170
171     lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
172     if (!lswitch) {
173         return;
174     }
175
176     smap_init(&new_external_ids);
177     smap_clone(&new_external_ids, &lswitch->external_ids);
178     if (ctx->argc == 4) {
179         smap_replace(&new_external_ids, ctx->argv[2], ctx->argv[3]);
180     } else {
181         smap_remove(&new_external_ids, ctx->argv[2]);
182     }
183     nbrec_logical_switch_set_external_ids(lswitch, &new_external_ids);
184     smap_destroy(&new_external_ids);
185 }
186
187 static void
188 do_lswitch_get_external_id(struct ovs_cmdl_context *ctx)
189 {
190     struct nbctl_context *nb_ctx = ctx->pvt;
191     const char *id = ctx->argv[1];
192     const struct nbrec_logical_switch *lswitch;
193
194     lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
195     if (!lswitch) {
196         return;
197     }
198
199     if (ctx->argc == 3) {
200         const char *key = ctx->argv[2];
201         const char *value;
202
203         /* List one external ID */
204
205         value = smap_get(&lswitch->external_ids, key);
206         if (value && *value) {
207             printf("%s\n", value);
208         } else {
209             printf("external-id '%s' is not set.\n", key);
210         }
211     } else {
212         struct smap_node *node;
213
214         /* List all external IDs */
215
216         SMAP_FOR_EACH(node, &lswitch->external_ids) {
217             printf("%s=%s\n", node->key, node->value);
218         }
219     }
220 }
221 \f
222 static const struct nbrec_logical_port *
223 lport_by_name_or_uuid(struct nbctl_context *nb_ctx, const char *id)
224 {
225     const struct nbrec_logical_port *lport = NULL;
226     bool is_uuid = false;
227     struct uuid lport_uuid;
228
229     if (uuid_from_string(&lport_uuid, id)) {
230         is_uuid = true;
231         lport = nbrec_logical_port_get_for_uuid(nb_ctx->idl, &lport_uuid);
232     }
233
234     if (!lport) {
235         NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
236             if (!strcmp(lport->name, id)) {
237                 break;
238             }
239         }
240     }
241
242     if (!lport) {
243         VLOG_WARN("lport not found for %s: '%s'",
244                 is_uuid ? "UUID" : "name", id);
245     }
246
247     return lport;
248 }
249
250 static void
251 do_lport_add(struct ovs_cmdl_context *ctx)
252 {
253     struct nbctl_context *nb_ctx = ctx->pvt;
254     struct nbrec_logical_port *lport;
255     const struct nbrec_logical_switch *lswitch;
256
257     lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[2]);
258     if (!lswitch) {
259         return;
260     }
261
262     lport = nbrec_logical_port_insert(nb_ctx->txn);
263     nbrec_logical_port_set_name(lport, ctx->argv[1]);
264     nbrec_logical_port_set_lswitch(lport, lswitch);
265 }
266
267 static void
268 do_lport_del(struct ovs_cmdl_context *ctx)
269 {
270     struct nbctl_context *nb_ctx = ctx->pvt;
271     const struct nbrec_logical_port *lport;
272
273     lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
274     if (!lport) {
275         return;
276     }
277
278     nbrec_logical_port_delete(lport);
279 }
280
281 static bool
282 is_lswitch(const struct nbrec_logical_switch *lswitch,
283         struct uuid *lswitch_uuid, const char *name)
284 {
285     if (lswitch_uuid) {
286         return uuid_equals(lswitch_uuid, &lswitch->header_.uuid);
287     } else {
288         return !strcmp(lswitch->name, name);
289     }
290 }
291
292
293 static void
294 do_lport_list(struct ovs_cmdl_context *ctx)
295 {
296     struct nbctl_context *nb_ctx = ctx->pvt;
297     const char *id = ctx->argv[1];
298     const struct nbrec_logical_port *lport;
299     bool is_uuid = false;
300     struct uuid lswitch_uuid;
301
302     if (uuid_from_string(&lswitch_uuid, id)) {
303         is_uuid = true;
304     }
305
306     NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
307         bool match;
308         if (is_uuid) {
309             match = is_lswitch(lport->lswitch, &lswitch_uuid, NULL);
310         } else {
311             match = is_lswitch(lport->lswitch, NULL, id);
312         }
313         if (!match) {
314             continue;
315         }
316         printf(UUID_FMT " (%s)\n",
317                UUID_ARGS(&lport->header_.uuid), lport->name);
318     }
319 }
320
321 static void
322 do_lport_set_external_id(struct ovs_cmdl_context *ctx)
323 {
324     struct nbctl_context *nb_ctx = ctx->pvt;
325     const char *id = ctx->argv[1];
326     const struct nbrec_logical_port *lport;
327     struct smap new_external_ids;
328
329     lport = lport_by_name_or_uuid(nb_ctx, id);
330     if (!lport) {
331         return;
332     }
333
334     smap_init(&new_external_ids);
335     smap_clone(&new_external_ids, &lport->external_ids);
336     if (ctx->argc == 4) {
337         smap_replace(&new_external_ids, ctx->argv[2], ctx->argv[3]);
338     } else {
339         smap_remove(&new_external_ids, ctx->argv[2]);
340     }
341     nbrec_logical_port_set_external_ids(lport, &new_external_ids);
342     smap_destroy(&new_external_ids);
343 }
344
345 static void
346 do_lport_get_external_id(struct ovs_cmdl_context *ctx)
347 {
348     struct nbctl_context *nb_ctx = ctx->pvt;
349     const char *id = ctx->argv[1];
350     const struct nbrec_logical_port *lport;
351
352     lport = lport_by_name_or_uuid(nb_ctx, id);
353     if (!lport) {
354         return;
355     }
356
357     if (ctx->argc == 3) {
358         const char *key = ctx->argv[2];
359         const char *value;
360
361         /* List one external ID */
362
363         value = smap_get(&lport->external_ids, key);
364         if (value && *value) {
365             printf("%s\n", value);
366         } else {
367             printf("external-id '%s' is not set.\n", key);
368         }
369     } else {
370         struct smap_node *node;
371
372         /* List all external IDs */
373
374         SMAP_FOR_EACH(node, &lport->external_ids) {
375             printf("%s=%s\n", node->key, node->value);
376         }
377     }
378 }
379
380 static void
381 do_lport_set_macs(struct ovs_cmdl_context *ctx)
382 {
383     struct nbctl_context *nb_ctx = ctx->pvt;
384     const char *id = ctx->argv[1];
385     const struct nbrec_logical_port *lport;
386
387     lport = lport_by_name_or_uuid(nb_ctx, id);
388     if (!lport) {
389         return;
390     }
391
392     nbrec_logical_port_set_macs(lport,
393             (const char **) ctx->argv + 2, ctx->argc - 2);
394 }
395
396 static void
397 do_lport_get_macs(struct ovs_cmdl_context *ctx)
398 {
399     struct nbctl_context *nb_ctx = ctx->pvt;
400     const char *id = ctx->argv[1];
401     const struct nbrec_logical_port *lport;
402     size_t i;
403
404     lport = lport_by_name_or_uuid(nb_ctx, id);
405     if (!lport) {
406         return;
407     }
408
409     for (i = 0; i < lport->n_macs; i++) {
410         printf("%s\n", lport->macs[i]);
411     }
412 }
413
414 static void
415 do_lport_get_up(struct ovs_cmdl_context *ctx)
416 {
417     struct nbctl_context *nb_ctx = ctx->pvt;
418     const char *id = ctx->argv[1];
419     const struct nbrec_logical_port *lport;
420
421     lport = lport_by_name_or_uuid(nb_ctx, id);
422     if (!lport) {
423         return;
424     }
425
426     printf("%s\n", (lport->up && *lport->up) ? "up" : "down");
427 }
428 \f
429 static void
430 parse_options(int argc, char *argv[])
431 {
432     enum {
433         VLOG_OPTION_ENUMS,
434     };
435     static const struct option long_options[] = {
436         {"db", required_argument, NULL, 'd'},
437         {"help", no_argument, NULL, 'h'},
438         {"options", no_argument, NULL, 'o'},
439         {"version", no_argument, NULL, 'V'},
440         VLOG_LONG_OPTIONS,
441         STREAM_SSL_LONG_OPTIONS,
442         {NULL, 0, NULL, 0},
443     };
444     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
445
446     for (;;) {
447         int c;
448
449         c = getopt_long(argc, argv, short_options, long_options, NULL);
450         if (c == -1) {
451             break;
452         }
453
454         switch (c) {
455         VLOG_OPTION_HANDLERS;
456         STREAM_SSL_OPTION_HANDLERS;
457
458         case 'd':
459             db = optarg;
460             break;
461
462         case 'h':
463             usage();
464             exit(EXIT_SUCCESS);
465
466         case 'o':
467             ovs_cmdl_print_options(long_options);
468             exit(EXIT_SUCCESS);
469
470         case 'V':
471             ovs_print_version(0, 0);
472             exit(EXIT_SUCCESS);
473
474         default:
475             break;
476         }
477     }
478
479     if (!db) {
480         db = default_db();
481     }
482
483     free(short_options);
484 }
485
486 static const struct ovs_cmdl_command all_commands[] = {
487     {
488         .name = "lswitch-add",
489         .usage = "[lswitch]",
490         .min_args = 0,
491         .max_args = 1,
492         .handler = do_lswitch_add,
493     },
494     {
495         .name = "lswitch-del",
496         .usage = "<lswitch>",
497         .min_args = 1,
498         .max_args = 1,
499         .handler = do_lswitch_del,
500     },
501     {
502         .name = "lswitch-list",
503         .usage = "",
504         .min_args = 0,
505         .max_args = 0,
506         .handler = do_lswitch_list,
507     },
508     {
509         .name = "lswitch-set-external-id",
510         .usage = "<lswitch> <key> [value]",
511         .min_args = 2,
512         .max_args = 3,
513         .handler = do_lswitch_set_external_id,
514     },
515     {
516         .name = "lswitch-get-external-id",
517         .usage = "<lswitch> [key]",
518         .min_args = 1,
519         .max_args = 2,
520         .handler = do_lswitch_get_external_id,
521     },
522     {
523         .name = "lport-add",
524         .usage = "<name> <lswitch>",
525         .min_args = 2,
526         .max_args = 2,
527         .handler = do_lport_add,
528     },
529     {
530         .name = "lport-del",
531         .usage = "<lport>",
532         .min_args = 1,
533         .max_args = 1,
534         .handler = do_lport_del,
535     },
536     {
537         .name = "lport-list",
538         .usage = "<lswitch>",
539         .min_args = 1,
540         .max_args = 1,
541         .handler = do_lport_list,
542     },
543     {
544         .name = "lport-set-external-id",
545         .usage = "<lport> <key> [value]",
546         .min_args = 2,
547         .max_args = 3,
548         .handler = do_lport_set_external_id,
549     },
550     {
551         .name = "lport-get-external-id",
552         .usage = "<lport> [key]",
553         .min_args = 1,
554         .max_args = 2,
555         .handler = do_lport_get_external_id,
556     },
557     {
558         .name = "lport-set-macs",
559         .usage = "<lport> [MAC] [MAC] [...]",
560         .min_args = 1,
561         /* Accept however many arguments the system will allow. */
562         .max_args = INT_MAX,
563         .handler = do_lport_set_macs,
564     },
565     {
566         .name = "lport-get-macs",
567         .usage = "<lport>",
568         .min_args = 1,
569         .max_args = 1,
570         .handler = do_lport_get_macs,
571     },
572     {
573         .name = "lport-get-up",
574         .usage = "<lport>",
575         .min_args = 1,
576         .max_args = 1,
577         .handler = do_lport_get_up,
578     },
579
580     {
581         /* sentinel */
582         .name = NULL,
583     },
584 };
585
586 static const struct ovs_cmdl_command *
587 get_all_commands(void)
588 {
589     return all_commands;
590 }
591
592 static const char *
593 default_db(void)
594 {
595     static char *def;
596     if (!def) {
597         def = xasprintf("unix:%s/db.sock", ovs_rundir());
598     }
599     return def;
600 }
601
602 int
603 main(int argc, char *argv[])
604 {
605     extern struct vlog_module VLM_reconnect;
606     struct ovs_cmdl_context ctx;
607     struct nbctl_context nb_ctx = { .idl = NULL, };
608     enum ovsdb_idl_txn_status txn_status;
609     unsigned int seqno;
610     int res = 0;
611     char *args;
612
613     fatal_ignore_sigpipe();
614     set_program_name(argv[0]);
615     vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
616     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
617     parse_options(argc, argv);
618     nbrec_init();
619
620     args = process_escape_args(argv);
621
622     nb_ctx.idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false);
623     ctx.pvt = &nb_ctx;
624     ctx.argc = argc - optind;
625     ctx.argv = argv + optind;
626
627     seqno = ovsdb_idl_get_seqno(nb_ctx.idl);
628     for (;;) {
629         ovsdb_idl_run(nb_ctx.idl);
630
631         if (!ovsdb_idl_is_alive(nb_ctx.idl)) {
632             int retval = ovsdb_idl_get_last_error(nb_ctx.idl);
633             VLOG_ERR("%s: database connection failed (%s)",
634                     db, ovs_retval_to_string(retval));
635             res = 1;
636             break;
637         }
638
639         if (seqno != ovsdb_idl_get_seqno(nb_ctx.idl)) {
640             nb_ctx.txn = ovsdb_idl_txn_create(nb_ctx.idl);
641             ovsdb_idl_txn_add_comment(nb_ctx.txn, "ovn-nbctl: %s", args);
642             ovs_cmdl_run_command(&ctx, get_all_commands());
643             txn_status = ovsdb_idl_txn_commit_block(nb_ctx.txn);
644             if (txn_status == TXN_TRY_AGAIN) {
645                 ovsdb_idl_txn_destroy(nb_ctx.txn);
646                 nb_ctx.txn = NULL;
647                 continue;
648             } else {
649                 break;
650             }
651         }
652
653         if (seqno == ovsdb_idl_get_seqno(nb_ctx.idl)) {
654             ovsdb_idl_wait(nb_ctx.idl);
655             poll_block();
656         }
657     }
658
659     if (nb_ctx.txn) {
660         ovsdb_idl_txn_destroy(nb_ctx.txn);
661     }
662     ovsdb_idl_destroy(nb_ctx.idl);
663     free(args);
664
665     exit(res);
666 }