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