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:
6 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include "command-line.h"
24 #include "fatal-signal.h"
25 #include "ovn/ovn-nb-idl.h"
26 #include "poll-loop.h"
29 #include "stream-ssl.h"
31 #include "openvswitch/vlog.h"
33 VLOG_DEFINE_THIS_MODULE(ovn_nbctl);
35 struct nbctl_context {
36 struct ovsdb_idl *idl;
37 struct ovsdb_idl_txn *txn;
40 static const char *db;
42 static const char *default_db(void);
48 %s: OVN northbound DB management utility\n\
49 usage: %s [OPTIONS] COMMAND [ARG...]\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\
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\
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\
80 --db=DATABASE connect to DATABASE\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());
87 stream_usage("database", true, true, false);
90 static const struct nbrec_logical_switch *
91 lswitch_by_name_or_uuid(struct nbctl_context *nb_ctx, const char *id)
93 const struct nbrec_logical_switch *lswitch = NULL;
95 bool duplicate = false;
96 struct uuid lswitch_uuid;
98 if (uuid_from_string(&lswitch_uuid, id)) {
100 lswitch = nbrec_logical_switch_get_for_uuid(nb_ctx->idl,
105 const struct nbrec_logical_switch *iter;
107 NBREC_LOGICAL_SWITCH_FOR_EACH(iter, nb_ctx->idl) {
108 if (strcmp(iter->name, id)) {
112 VLOG_WARN("There is more than one logical switch named '%s'. "
122 if (!lswitch && !duplicate) {
123 VLOG_WARN("lswitch not found for %s: '%s'",
124 is_uuid ? "UUID" : "name", id);
131 do_lswitch_add(struct ovs_cmdl_context *ctx)
133 struct nbctl_context *nb_ctx = ctx->pvt;
134 struct nbrec_logical_switch *lswitch;
136 lswitch = nbrec_logical_switch_insert(nb_ctx->txn);
137 if (ctx->argc == 2) {
138 nbrec_logical_switch_set_name(lswitch, ctx->argv[1]);
143 do_lswitch_del(struct ovs_cmdl_context *ctx)
145 struct nbctl_context *nb_ctx = ctx->pvt;
146 const char *id = ctx->argv[1];
147 const struct nbrec_logical_switch *lswitch;
149 lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
154 nbrec_logical_switch_delete(lswitch);
158 do_lswitch_list(struct ovs_cmdl_context *ctx)
160 struct nbctl_context *nb_ctx = ctx->pvt;
161 const struct nbrec_logical_switch *lswitch;
163 NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, nb_ctx->idl) {
164 printf(UUID_FMT " (%s)\n",
165 UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
170 do_lswitch_set_external_id(struct ovs_cmdl_context *ctx)
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;
177 lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
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]);
187 smap_remove(&new_external_ids, ctx->argv[2]);
189 nbrec_logical_switch_set_external_ids(lswitch, &new_external_ids);
190 smap_destroy(&new_external_ids);
194 do_lswitch_get_external_id(struct ovs_cmdl_context *ctx)
196 struct nbctl_context *nb_ctx = ctx->pvt;
197 const char *id = ctx->argv[1];
198 const struct nbrec_logical_switch *lswitch;
200 lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
205 if (ctx->argc == 3) {
206 const char *key = ctx->argv[2];
209 /* List one external ID */
211 value = smap_get(&lswitch->external_ids, key);
213 printf("%s\n", value);
216 struct smap_node *node;
218 /* List all external IDs */
220 SMAP_FOR_EACH(node, &lswitch->external_ids) {
221 printf("%s=%s\n", node->key, node->value);
226 static const struct nbrec_logical_port *
227 lport_by_name_or_uuid(struct nbctl_context *nb_ctx, const char *id)
229 const struct nbrec_logical_port *lport = NULL;
230 bool is_uuid = false;
231 struct uuid lport_uuid;
233 if (uuid_from_string(&lport_uuid, id)) {
235 lport = nbrec_logical_port_get_for_uuid(nb_ctx->idl, &lport_uuid);
239 NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
240 if (!strcmp(lport->name, id)) {
247 VLOG_WARN("lport not found for %s: '%s'",
248 is_uuid ? "UUID" : "name", id);
255 do_lport_add(struct ovs_cmdl_context *ctx)
257 struct nbctl_context *nb_ctx = ctx->pvt;
258 struct nbrec_logical_port *lport;
259 const struct nbrec_logical_switch *lswitch;
262 lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
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.");
273 if (ctx->argc == 5) {
275 if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag) || tag < 0 || tag > 4095) {
276 VLOG_WARN("Invalid tag '%s'", ctx->argv[4]);
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);
292 do_lport_del(struct ovs_cmdl_context *ctx)
294 struct nbctl_context *nb_ctx = ctx->pvt;
295 const struct nbrec_logical_port *lport;
297 lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
302 nbrec_logical_port_delete(lport);
306 is_lswitch(const struct nbrec_logical_switch *lswitch,
307 struct uuid *lswitch_uuid, const char *name)
310 return uuid_equals(lswitch_uuid, &lswitch->header_.uuid);
312 return !strcmp(lswitch->name, name);
318 do_lport_list(struct ovs_cmdl_context *ctx)
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;
326 if (uuid_from_string(&lswitch_uuid, id)) {
330 NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
333 match = is_lswitch(lport->lswitch, &lswitch_uuid, NULL);
335 match = is_lswitch(lport->lswitch, NULL, id);
340 printf(UUID_FMT " (%s)\n",
341 UUID_ARGS(&lport->header_.uuid), lport->name);
346 do_lport_get_parent(struct ovs_cmdl_context *ctx)
348 struct nbctl_context *nb_ctx = ctx->pvt;
349 const struct nbrec_logical_port *lport;
351 lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
356 if (lport->parent_name) {
357 printf("%s\n", lport->parent_name);
362 do_lport_get_tag(struct ovs_cmdl_context *ctx)
364 struct nbctl_context *nb_ctx = ctx->pvt;
365 const struct nbrec_logical_port *lport;
367 lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
372 if (lport->n_tag > 0) {
373 printf("%"PRId64"\n", lport->tag[0]);
378 do_lport_set_external_id(struct ovs_cmdl_context *ctx)
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;
385 lport = lport_by_name_or_uuid(nb_ctx, id);
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]);
395 smap_remove(&new_external_ids, ctx->argv[2]);
397 nbrec_logical_port_set_external_ids(lport, &new_external_ids);
398 smap_destroy(&new_external_ids);
402 do_lport_get_external_id(struct ovs_cmdl_context *ctx)
404 struct nbctl_context *nb_ctx = ctx->pvt;
405 const char *id = ctx->argv[1];
406 const struct nbrec_logical_port *lport;
408 lport = lport_by_name_or_uuid(nb_ctx, id);
413 if (ctx->argc == 3) {
414 const char *key = ctx->argv[2];
417 /* List one external ID */
419 value = smap_get(&lport->external_ids, key);
421 printf("%s\n", value);
424 struct smap_node *node;
426 /* List all external IDs */
428 SMAP_FOR_EACH(node, &lport->external_ids) {
429 printf("%s=%s\n", node->key, node->value);
435 do_lport_set_macs(struct ovs_cmdl_context *ctx)
437 struct nbctl_context *nb_ctx = ctx->pvt;
438 const char *id = ctx->argv[1];
439 const struct nbrec_logical_port *lport;
441 lport = lport_by_name_or_uuid(nb_ctx, id);
446 nbrec_logical_port_set_macs(lport,
447 (const char **) ctx->argv + 2, ctx->argc - 2);
451 do_lport_get_macs(struct ovs_cmdl_context *ctx)
453 struct nbctl_context *nb_ctx = ctx->pvt;
454 const char *id = ctx->argv[1];
455 const struct nbrec_logical_port *lport;
458 lport = lport_by_name_or_uuid(nb_ctx, id);
463 for (i = 0; i < lport->n_macs; i++) {
464 printf("%s\n", lport->macs[i]);
469 do_lport_get_up(struct ovs_cmdl_context *ctx)
471 struct nbctl_context *nb_ctx = ctx->pvt;
472 const char *id = ctx->argv[1];
473 const struct nbrec_logical_port *lport;
475 lport = lport_by_name_or_uuid(nb_ctx, id);
480 printf("%s\n", (lport->up && *lport->up) ? "up" : "down");
484 parse_options(int argc, char *argv[])
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'},
495 STREAM_SSL_LONG_OPTIONS,
498 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
503 c = getopt_long(argc, argv, short_options, long_options, NULL);
509 VLOG_OPTION_HANDLERS;
510 STREAM_SSL_OPTION_HANDLERS;
521 ovs_cmdl_print_options(long_options);
525 ovs_print_version(0, 0);
540 static const struct ovs_cmdl_command all_commands[] = {
542 .name = "lswitch-add",
543 .usage = "[LSWITCH]",
546 .handler = do_lswitch_add,
549 .name = "lswitch-del",
553 .handler = do_lswitch_del,
556 .name = "lswitch-list",
560 .handler = do_lswitch_list,
563 .name = "lswitch-set-external-id",
564 .usage = "LSWITCH KEY [VALUE]",
567 .handler = do_lswitch_set_external_id,
570 .name = "lswitch-get-external-id",
571 .usage = "LSWITCH [KEY]",
574 .handler = do_lswitch_get_external_id,
578 .usage = "LSWITCH LPORT [PARENT] [TAG]",
581 .handler = do_lport_add,
588 .handler = do_lport_del,
591 .name = "lport-list",
595 .handler = do_lport_list,
598 .name = "lport-get-parent",
602 .handler = do_lport_get_parent,
605 .name = "lport-get-tag",
609 .handler = do_lport_get_tag,
612 .name = "lport-set-external-id",
613 .usage = "LPORT KEY [VALUE]",
616 .handler = do_lport_set_external_id,
619 .name = "lport-get-external-id",
620 .usage = "LPORT [KEY]",
623 .handler = do_lport_get_external_id,
626 .name = "lport-set-macs",
627 .usage = "LPORT [MAC] [MAC] [...]",
629 /* Accept however many arguments the system will allow. */
631 .handler = do_lport_set_macs,
634 .name = "lport-get-macs",
638 .handler = do_lport_get_macs,
641 .name = "lport-get-up",
645 .handler = do_lport_get_up,
654 static const struct ovs_cmdl_command *
655 get_all_commands(void)
665 def = xasprintf("unix:%s/db.sock", ovs_rundir());
671 main(int argc, char *argv[])
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;
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);
688 args = process_escape_args(argv);
690 nb_ctx.idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false);
692 ctx.argc = argc - optind;
693 ctx.argv = argv + optind;
695 seqno = ovsdb_idl_get_seqno(nb_ctx.idl);
697 ovsdb_idl_run(nb_ctx.idl);
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));
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);
721 if (seqno == ovsdb_idl_get_seqno(nb_ctx.idl)) {
722 ovsdb_idl_wait(nb_ctx.idl);
728 ovsdb_idl_txn_destroy(nb_ctx.txn);
730 ovsdb_idl_destroy(nb_ctx.idl);