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