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