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"
23 #include "db-ctl-base.h"
25 #include "fatal-signal.h"
26 #include "openvswitch/json.h"
27 #include "ovn/lib/ovn-nb-idl.h"
29 #include "poll-loop.h"
34 #include "stream-ssl.h"
39 #include "openvswitch/vlog.h"
41 VLOG_DEFINE_THIS_MODULE(nbctl);
43 /* --db: The database server to contact. */
44 static const char *db;
46 /* --oneline: Write each command's output as a single line? */
49 /* --dry-run: Do not commit any changes. */
52 /* --timeout: Time to wait for a connection to 'db'. */
55 /* Format for table output. */
56 static struct table_style table_style = TABLE_STYLE_DEFAULT;
58 /* The IDL we're using and the current transaction, if any.
59 * This is for use by nbctl_exit() only, to allow it to clean up.
60 * Other code should use its context arguments. */
61 static struct ovsdb_idl *the_idl;
62 static struct ovsdb_idl_txn *the_idl_txn;
63 OVS_NO_RETURN static void nbctl_exit(int status);
65 static void nbctl_cmd_init(void);
66 OVS_NO_RETURN static void usage(void);
67 static void parse_options(int argc, char *argv[], struct shash *local_options);
68 static const char *nbctl_default_db(void);
69 static void run_prerequisites(struct ctl_command[], size_t n_commands,
71 static bool do_nbctl(const char *args, struct ctl_command *, size_t n,
73 static const struct nbrec_dhcp_options *dhcp_options_get(
74 struct ctl_context *ctx, const char *id, bool must_exist);
77 main(int argc, char *argv[])
79 struct ovsdb_idl *idl;
80 struct ctl_command *commands;
81 struct shash local_options;
86 set_program_name(argv[0]);
87 fatal_ignore_sigpipe();
88 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
89 vlog_set_levels_from_string_assert("reconnect:warn");
94 /* Log our arguments. This is often valuable for debugging systems. */
95 args = process_escape_args(argv);
96 VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG,
97 "Called as %s", args);
99 /* Parse command line. */
100 shash_init(&local_options);
101 parse_options(argc, argv, &local_options);
102 commands = ctl_parse_commands(argc - optind, argv + optind, &local_options,
109 /* Initialize IDL. */
110 idl = the_idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false);
111 run_prerequisites(commands, n_commands, idl);
113 /* Execute the commands.
115 * 'seqno' is the database sequence number for which we last tried to
116 * execute our transaction. There's no point in trying to commit more than
117 * once for any given sequence number, because if the transaction fails
118 * it's because the database changed and we need to obtain an up-to-date
119 * view of the database before we try the transaction again. */
120 seqno = ovsdb_idl_get_seqno(idl);
123 if (!ovsdb_idl_is_alive(idl)) {
124 int retval = ovsdb_idl_get_last_error(idl);
125 ctl_fatal("%s: database connection failed (%s)",
126 db, ovs_retval_to_string(retval));
129 if (seqno != ovsdb_idl_get_seqno(idl)) {
130 seqno = ovsdb_idl_get_seqno(idl);
131 if (do_nbctl(args, commands, n_commands, idl)) {
137 if (seqno == ovsdb_idl_get_seqno(idl)) {
145 nbctl_default_db(void)
149 def = getenv("OVN_NB_DB");
151 def = xasprintf("unix:%s/ovnnb_db.sock", ovs_rundir());
158 parse_options(int argc, char *argv[], struct shash *local_options)
161 OPT_DB = UCHAR_MAX + 1,
171 static const struct option global_long_options[] = {
172 {"db", required_argument, NULL, OPT_DB},
173 {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG},
174 {"dry-run", no_argument, NULL, OPT_DRY_RUN},
175 {"oneline", no_argument, NULL, OPT_ONELINE},
176 {"timeout", required_argument, NULL, 't'},
177 {"help", no_argument, NULL, 'h'},
178 {"commands", no_argument, NULL, OPT_COMMANDS},
179 {"options", no_argument, NULL, OPT_OPTIONS},
180 {"version", no_argument, NULL, 'V'},
182 STREAM_SSL_LONG_OPTIONS,
186 const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
187 char *tmp, *short_options;
189 struct option *options;
190 size_t allocated_options;
194 tmp = ovs_cmdl_long_options_to_short_options(global_long_options);
195 short_options = xasprintf("+%s", tmp);
198 /* We want to parse both global and command-specific options here, but
199 * getopt_long() isn't too convenient for the job. We copy our global
200 * options into a dynamic array, then append all of the command-specific
202 options = xmemdup(global_long_options, sizeof global_long_options);
203 allocated_options = ARRAY_SIZE(global_long_options);
204 n_options = n_global_long_options;
205 ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL);
206 table_style.format = TF_LIST;
212 c = getopt_long(argc, argv, short_options, options, &idx);
227 vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN);
235 if (shash_find(local_options, options[idx].name)) {
236 ctl_fatal("'%s' option specified multiple times",
239 shash_add_nocopy(local_options,
240 xasprintf("--%s", options[idx].name),
241 nullable_xstrdup(optarg));
249 ctl_print_commands();
252 ctl_print_options(global_long_options);
255 ovs_print_version(0, 0);
256 printf("DB Schema %s\n", nbrec_get_db_version());
260 timeout = strtoul(optarg, NULL, 10);
262 ctl_fatal("value %s on -t or --timeout is invalid", optarg);
267 TABLE_OPTION_HANDLERS(&table_style)
268 STREAM_SSL_OPTION_HANDLERS
280 db = nbctl_default_db();
283 for (i = n_global_long_options; options[i].name; i++) {
284 free(CONST_CAST(char *, options[i].name));
293 %s: OVN northbound DB management utility\n\
294 usage: %s [OPTIONS] COMMAND [ARG...]\n\
297 show print overview of database contents\n\
298 show SWITCH print overview of database contents for SWITCH\n\
299 show ROUTER print overview of database contents for ROUTER\n\
301 Logical switch commands:\n\
302 ls-add [SWITCH] create a logical switch named SWITCH\n\
303 ls-del SWITCH delete SWITCH and all its ports\n\
304 ls-list print the names of all logical switches\n\
307 acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\
308 add an ACL to SWITCH\n\
309 acl-del SWITCH [DIRECTION [PRIORITY MATCH]]\n\
310 remove ACLs from SWITCH\n\
311 acl-list SWITCH print ACLs for SWITCH\n\
313 Logical switch port commands:\n\
314 lsp-add SWITCH PORT add logical port PORT on SWITCH\n\
315 lsp-add SWITCH PORT PARENT TAG\n\
316 add logical port PORT on SWITCH with PARENT\n\
318 lsp-del PORT delete PORT from its attached switch\n\
319 lsp-list SWITCH print the names of all logical ports on SWITCH\n\
320 lsp-get-parent PORT get the parent of PORT if set\n\
321 lsp-get-tag PORT get the PORT's tag if set\n\
322 lsp-set-addresses PORT [ADDRESS]...\n\
323 set MAC or MAC+IP addresses for PORT.\n\
324 lsp-get-addresses PORT get a list of MAC addresses on PORT\n\
325 lsp-set-port-security PORT [ADDRS]...\n\
326 set port security addresses for PORT.\n\
327 lsp-get-port-security PORT get PORT's port security addresses\n\
328 lsp-get-up PORT get state of PORT ('up' or 'down')\n\
329 lsp-set-enabled PORT STATE\n\
330 set administrative state PORT\n\
331 ('enabled' or 'disabled')\n\
332 lsp-get-enabled PORT get administrative state PORT\n\
333 ('enabled' or 'disabled')\n\
334 lsp-set-type PORT TYPE set the type for PORT\n\
335 lsp-get-type PORT get the type for PORT\n\
336 lsp-set-options PORT KEY=VALUE [KEY=VALUE]...\n\
337 set options related to the type of PORT\n\
338 lsp-get-options PORT get the type specific options for PORT\n\
339 lsp-set-dhcpv4-options PORT [DHCP_OPTIONS_UUID]\n\
340 set dhcpv4 options for PORT\n\
341 lsp-get-dhcpv4-options PORT get the dhcpv4 options for PORT\n\
343 Logical router commands:\n\
344 lr-add [ROUTER] create a logical router named ROUTER\n\
345 lr-del ROUTER delete ROUTER and all its ports\n\
346 lr-list print the names of all logical routers\n\
348 Logical router port commands:\n\
349 lrp-add ROUTER PORT MAC NETWORK... [peer=PEER]\n\
350 add logical port PORT on ROUTER\n\
351 lrp-del PORT delete PORT from its attached router\n\
352 lrp-list ROUTER print the names of all ports on ROUTER\n\
353 lrp-set-enabled PORT STATE\n\
354 set administrative state PORT\n\
355 ('enabled' or 'disabled')\n\
356 lrp-get-enabled PORT get administrative state PORT\n\
357 ('enabled' or 'disabled')\n\
360 lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\
361 add a route to ROUTER\n\
362 lr-route-del ROUTER [PREFIX]\n\
363 remove routes from ROUTER\n\
364 lr-route-list ROUTER print routes for ROUTER\n\
367 DHCP Options commands:\n\
368 dhcp-options-create CIDR [EXTERNAL_IDS]\n\
369 create a DHCP options row with CIDR\n\
370 dhcp-options-del DHCP_OPTIONS_UUID\n\
371 delete DHCP_OPTIONS_UUID\n\
372 dhcp-options-list \n\
373 lists the DHCP_Options rows\n\
374 dhcp-options-set-options DHCP_OPTIONS_UUID KEY=VALUE [KEY=VALUE]...\n\
375 set DHCP options to the DHCP_OPTIONS_UUID\n\
376 dhcp-options-get-options DHCO_OPTIONS_UUID \n\
377 displays the DHCP options of th DHCP_OPTIONS_UUID\n\
382 --db=DATABASE connect to DATABASE\n\
384 -t, --timeout=SECS wait at most SECS seconds\n\
385 --dry-run do not commit changes to database\n\
386 --oneline print exactly one line of output per command\n",
387 program_name, program_name, ctl_get_db_cmd_usage(), nbctl_default_db());
390 --no-syslog equivalent to --verbose=nbctl:syslog:warn\n");
393 -h, --help display this help message\n\
394 -V, --version display version information\n");
399 /* Find a logical router given its id. */
400 static const struct nbrec_logical_router *
401 lr_by_name_or_uuid(struct ctl_context *ctx, const char *id,
404 const struct nbrec_logical_router *lr = NULL;
405 bool is_uuid = false;
408 if (uuid_from_string(&lr_uuid, id)) {
410 lr = nbrec_logical_router_get_for_uuid(ctx->idl, &lr_uuid);
414 const struct nbrec_logical_router *iter;
416 NBREC_LOGICAL_ROUTER_FOR_EACH(iter, ctx->idl) {
417 if (strcmp(iter->name, id)) {
421 ctl_fatal("Multiple logical routers named '%s'. "
428 if (!lr && must_exist) {
429 ctl_fatal("%s: router %s not found", id, is_uuid ? "UUID" : "name");
435 static const struct nbrec_logical_switch *
436 ls_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist)
438 const struct nbrec_logical_switch *ls = NULL;
441 bool is_uuid = uuid_from_string(&ls_uuid, id);
443 ls = nbrec_logical_switch_get_for_uuid(ctx->idl, &ls_uuid);
447 const struct nbrec_logical_switch *iter;
449 NBREC_LOGICAL_SWITCH_FOR_EACH(iter, ctx->idl) {
450 if (strcmp(iter->name, id)) {
454 ctl_fatal("Multiple logical switches named '%s'. "
461 if (!ls && must_exist) {
462 ctl_fatal("%s: switch %s not found", id, is_uuid ? "UUID" : "name");
468 /* Given pointer to logical router, this routine prints the router
471 print_lr(const struct nbrec_logical_router *lr, struct ds *s)
473 ds_put_format(s, " router "UUID_FMT" (%s)\n",
474 UUID_ARGS(&lr->header_.uuid), lr->name);
476 for (size_t i = 0; i < lr->n_ports; i++) {
477 const struct nbrec_logical_router_port *lrp = lr->ports[i];
478 ds_put_format(s, " port %s\n", lrp->name);
480 ds_put_cstr(s, " mac: ");
481 ds_put_format(s, "\"%s\"\n", lrp->mac);
483 if (lrp->n_networks) {
484 ds_put_cstr(s, " networks: [");
485 for (size_t j = 0; j < lrp->n_networks; j++) {
486 ds_put_format(s, "%s\"%s\"",
490 ds_put_cstr(s, "]\n");
496 print_ls(const struct nbrec_logical_switch *ls, struct ds *s)
498 ds_put_format(s, " switch "UUID_FMT" (%s)\n",
499 UUID_ARGS(&ls->header_.uuid), ls->name);
501 for (size_t i = 0; i < ls->n_ports; i++) {
502 const struct nbrec_logical_switch_port *lsp = ls->ports[i];
504 ds_put_format(s, " port %s\n", lsp->name);
505 if (lsp->parent_name) {
506 ds_put_format(s, " parent: %s\n", lsp->parent_name);
509 ds_put_format(s, " tag: %"PRIu64"\n", lsp->tag[0]);
511 if (lsp->n_addresses) {
512 ds_put_cstr(s, " addresses: [");
513 for (size_t j = 0; j < lsp->n_addresses; j++) {
514 ds_put_format(s, "%s\"%s\"",
518 ds_put_cstr(s, "]\n");
524 nbctl_show(struct ctl_context *ctx)
526 const struct nbrec_logical_switch *ls;
528 if (ctx->argc == 2) {
529 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], false);
531 print_ls(ls, &ctx->output);
534 NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) {
535 print_ls(ls, &ctx->output);
538 const struct nbrec_logical_router *lr;
540 if (ctx->argc == 2) {
541 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], false);
543 print_lr(lr, &ctx->output);
546 NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) {
547 print_lr(lr, &ctx->output);
553 nbctl_ls_add(struct ctl_context *ctx)
555 const char *ls_name = ctx->argc == 2 ? ctx->argv[1] : NULL;
557 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
558 bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL;
559 if (may_exist && add_duplicate) {
560 ctl_fatal("--may-exist and --add-duplicate may not be used together");
564 if (!add_duplicate) {
565 const struct nbrec_logical_switch *ls;
566 NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) {
567 if (!strcmp(ls->name, ls_name)) {
571 ctl_fatal("%s: a switch with this name already exists",
576 } else if (may_exist) {
577 ctl_fatal("--may-exist requires specifying a name");
578 } else if (add_duplicate) {
579 ctl_fatal("--add-duplicate requires specifying a name");
582 struct nbrec_logical_switch *ls;
583 ls = nbrec_logical_switch_insert(ctx->txn);
585 nbrec_logical_switch_set_name(ls, ls_name);
590 nbctl_ls_del(struct ctl_context *ctx)
592 bool must_exist = !shash_find(&ctx->options, "--if-exists");
593 const char *id = ctx->argv[1];
594 const struct nbrec_logical_switch *ls;
596 ls = ls_by_name_or_uuid(ctx, id, must_exist);
601 nbrec_logical_switch_delete(ls);
605 nbctl_ls_list(struct ctl_context *ctx)
607 const struct nbrec_logical_switch *ls;
608 struct smap lswitches;
610 smap_init(&lswitches);
611 NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) {
612 smap_add_format(&lswitches, ls->name, UUID_FMT " (%s)",
613 UUID_ARGS(&ls->header_.uuid), ls->name);
615 const struct smap_node **nodes = smap_sort(&lswitches);
616 for (size_t i = 0; i < smap_count(&lswitches); i++) {
617 const struct smap_node *node = nodes[i];
618 ds_put_format(&ctx->output, "%s\n", node->value);
620 smap_destroy(&lswitches);
624 static const struct nbrec_logical_switch_port *
625 lsp_by_name_or_uuid(struct ctl_context *ctx, const char *id,
628 const struct nbrec_logical_switch_port *lsp = NULL;
630 struct uuid lsp_uuid;
631 bool is_uuid = uuid_from_string(&lsp_uuid, id);
633 lsp = nbrec_logical_switch_port_get_for_uuid(ctx->idl, &lsp_uuid);
637 NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(lsp, ctx->idl) {
638 if (!strcmp(lsp->name, id)) {
644 if (!lsp && must_exist) {
645 ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name");
651 /* Returns the logical switch that contains 'lsp'. */
652 static const struct nbrec_logical_switch *
653 lsp_to_ls(const struct ovsdb_idl *idl,
654 const struct nbrec_logical_switch_port *lsp)
656 const struct nbrec_logical_switch *ls;
657 NBREC_LOGICAL_SWITCH_FOR_EACH (ls, idl) {
658 for (size_t i = 0; i < ls->n_ports; i++) {
659 if (ls->ports[i] == lsp) {
665 /* Can't happen because of the database schema */
666 ctl_fatal("logical port %s is not part of any logical switch",
671 ls_get_name(const struct nbrec_logical_switch *ls,
672 char uuid_s[UUID_LEN + 1], size_t uuid_s_size)
677 snprintf(uuid_s, uuid_s_size, UUID_FMT, UUID_ARGS(&ls->header_.uuid));
682 nbctl_lsp_add(struct ctl_context *ctx)
684 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
686 const struct nbrec_logical_switch *ls;
687 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
689 const char *parent_name;
691 if (ctx->argc == 3) {
694 } else if (ctx->argc == 5) {
696 parent_name = ctx->argv[3];
697 if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag)
698 || tag < 0 || tag > 4095) {
699 ctl_fatal("%s: invalid tag", ctx->argv[4]);
702 ctl_fatal("lsp-add with parent must also specify a tag");
705 const char *lsp_name = ctx->argv[2];
706 const struct nbrec_logical_switch_port *lsp;
707 lsp = lsp_by_name_or_uuid(ctx, lsp_name, false);
710 ctl_fatal("%s: a port with this name already exists",
714 const struct nbrec_logical_switch *lsw;
715 lsw = lsp_to_ls(ctx->idl, lsp);
717 char uuid_s[UUID_LEN + 1];
718 ctl_fatal("%s: port already exists but in switch %s", lsp_name,
719 ls_get_name(lsw, uuid_s, sizeof uuid_s));
723 if (!lsp->parent_name) {
724 ctl_fatal("%s: port already exists but has no parent",
726 } else if (strcmp(parent_name, lsp->parent_name)) {
727 ctl_fatal("%s: port already exists with different parent %s",
728 lsp_name, lsp->parent_name);
732 ctl_fatal("%s: port already exists but has no tag",
734 } else if (lsp->tag[0] != tag) {
735 ctl_fatal("%s: port already exists with different "
736 "tag %"PRId64, lsp_name, lsp->tag[0]);
739 if (lsp->parent_name) {
740 ctl_fatal("%s: port already exists but has parent %s",
741 lsp_name, lsp->parent_name);
748 /* Create the logical port. */
749 lsp = nbrec_logical_switch_port_insert(ctx->txn);
750 nbrec_logical_switch_port_set_name(lsp, lsp_name);
752 nbrec_logical_switch_port_set_parent_name(lsp, parent_name);
753 nbrec_logical_switch_port_set_tag(lsp, &tag, 1);
756 /* Insert the logical port into the logical switch. */
757 nbrec_logical_switch_verify_ports(ls);
758 struct nbrec_logical_switch_port **new_ports = xmalloc(sizeof *new_ports *
760 memcpy(new_ports, ls->ports, sizeof *new_ports * ls->n_ports);
761 new_ports[ls->n_ports] = CONST_CAST(struct nbrec_logical_switch_port *,
763 nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports + 1);
767 /* Removes logical switch port 'ls->ports[idx]'. */
769 remove_lsp(const struct nbrec_logical_switch *ls, size_t idx)
771 const struct nbrec_logical_switch_port *lsp = ls->ports[idx];
773 /* First remove 'lsp' from the array of ports. This is what will
774 * actually cause the logical port to be deleted when the transaction is
775 * sent to the database server (due to garbage collection). */
776 struct nbrec_logical_switch_port **new_ports
777 = xmemdup(ls->ports, sizeof *new_ports * ls->n_ports);
778 new_ports[idx] = new_ports[ls->n_ports - 1];
779 nbrec_logical_switch_verify_ports(ls);
780 nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports - 1);
783 /* Delete 'lsp' from the IDL. This won't have a real effect on the
784 * database server (the IDL will suppress it in fact) but it means that it
785 * won't show up when we iterate with NBREC_LOGICAL_SWITCH_PORT_FOR_EACH
787 nbrec_logical_switch_port_delete(lsp);
791 nbctl_lsp_del(struct ctl_context *ctx)
793 bool must_exist = !shash_find(&ctx->options, "--if-exists");
794 const struct nbrec_logical_switch_port *lsp;
796 lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], must_exist);
801 /* Find the switch that contains 'lsp', then delete it. */
802 const struct nbrec_logical_switch *ls;
803 NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) {
804 for (size_t i = 0; i < ls->n_ports; i++) {
805 if (ls->ports[i] == lsp) {
812 /* Can't happen because of the database schema. */
813 ctl_fatal("logical port %s is not part of any logical switch",
818 nbctl_lsp_list(struct ctl_context *ctx)
820 const char *id = ctx->argv[1];
821 const struct nbrec_logical_switch *ls;
825 ls = ls_by_name_or_uuid(ctx, id, true);
828 for (i = 0; i < ls->n_ports; i++) {
829 const struct nbrec_logical_switch_port *lsp = ls->ports[i];
830 smap_add_format(&lsps, lsp->name, UUID_FMT " (%s)",
831 UUID_ARGS(&lsp->header_.uuid), lsp->name);
833 const struct smap_node **nodes = smap_sort(&lsps);
834 for (i = 0; i < smap_count(&lsps); i++) {
835 const struct smap_node *node = nodes[i];
836 ds_put_format(&ctx->output, "%s\n", node->value);
843 nbctl_lsp_get_parent(struct ctl_context *ctx)
845 const struct nbrec_logical_switch_port *lsp;
847 lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], true);
848 if (lsp->parent_name) {
849 ds_put_format(&ctx->output, "%s\n", lsp->parent_name);
854 nbctl_lsp_get_tag(struct ctl_context *ctx)
856 const struct nbrec_logical_switch_port *lsp;
858 lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], true);
859 if (lsp->n_tag > 0) {
860 ds_put_format(&ctx->output, "%"PRId64"\n", lsp->tag[0]);
865 nbctl_lsp_set_addresses(struct ctl_context *ctx)
867 const char *id = ctx->argv[1];
868 const struct nbrec_logical_switch_port *lsp;
870 lsp = lsp_by_name_or_uuid(ctx, id, true);
873 for (i = 2; i < ctx->argc; i++) {
876 if (strcmp(ctx->argv[i], "unknown")
877 && !ovs_scan(ctx->argv[i], ETH_ADDR_SCAN_FMT,
878 ETH_ADDR_SCAN_ARGS(ea))) {
879 ctl_fatal("%s: Invalid address format. See ovn-nb(5). "
880 "Hint: An Ethernet address must be "
881 "listed before an IP address, together as a single "
882 "argument.", ctx->argv[i]);
886 nbrec_logical_switch_port_set_addresses(lsp,
887 (const char **) ctx->argv + 2, ctx->argc - 2);
891 nbctl_lsp_get_addresses(struct ctl_context *ctx)
893 const char *id = ctx->argv[1];
894 const struct nbrec_logical_switch_port *lsp;
895 struct svec addresses;
899 lsp = lsp_by_name_or_uuid(ctx, id, true);
901 svec_init(&addresses);
902 for (i = 0; i < lsp->n_addresses; i++) {
903 svec_add(&addresses, lsp->addresses[i]);
905 svec_sort(&addresses);
906 SVEC_FOR_EACH(i, mac, &addresses) {
907 ds_put_format(&ctx->output, "%s\n", mac);
909 svec_destroy(&addresses);
913 nbctl_lsp_set_port_security(struct ctl_context *ctx)
915 const char *id = ctx->argv[1];
916 const struct nbrec_logical_switch_port *lsp;
918 lsp = lsp_by_name_or_uuid(ctx, id, true);
919 nbrec_logical_switch_port_set_port_security(lsp,
920 (const char **) ctx->argv + 2, ctx->argc - 2);
924 nbctl_lsp_get_port_security(struct ctl_context *ctx)
926 const char *id = ctx->argv[1];
927 const struct nbrec_logical_switch_port *lsp;
932 lsp = lsp_by_name_or_uuid(ctx, id, true);
934 for (i = 0; i < lsp->n_port_security; i++) {
935 svec_add(&addrs, lsp->port_security[i]);
938 SVEC_FOR_EACH(i, addr, &addrs) {
939 ds_put_format(&ctx->output, "%s\n", addr);
941 svec_destroy(&addrs);
945 nbctl_lsp_get_up(struct ctl_context *ctx)
947 const char *id = ctx->argv[1];
948 const struct nbrec_logical_switch_port *lsp;
950 lsp = lsp_by_name_or_uuid(ctx, id, true);
951 ds_put_format(&ctx->output,
952 "%s\n", (lsp->up && *lsp->up) ? "up" : "down");
956 parse_enabled(const char *state)
958 if (!strcasecmp(state, "enabled")) {
960 } else if (!strcasecmp(state, "disabled")) {
963 ctl_fatal("%s: state must be \"enabled\" or \"disabled\"", state);
968 nbctl_lsp_set_enabled(struct ctl_context *ctx)
970 const char *id = ctx->argv[1];
971 const char *state = ctx->argv[2];
972 const struct nbrec_logical_switch_port *lsp;
974 lsp = lsp_by_name_or_uuid(ctx, id, true);
975 bool enabled = parse_enabled(state);
976 nbrec_logical_switch_port_set_enabled(lsp, &enabled, 1);
980 nbctl_lsp_get_enabled(struct ctl_context *ctx)
982 const char *id = ctx->argv[1];
983 const struct nbrec_logical_switch_port *lsp;
985 lsp = lsp_by_name_or_uuid(ctx, id, true);
986 ds_put_format(&ctx->output, "%s\n",
987 !lsp->enabled || *lsp->enabled ? "enabled" : "disabled");
991 nbctl_lsp_set_type(struct ctl_context *ctx)
993 const char *id = ctx->argv[1];
994 const char *type = ctx->argv[2];
995 const struct nbrec_logical_switch_port *lsp;
997 lsp = lsp_by_name_or_uuid(ctx, id, true);
998 nbrec_logical_switch_port_set_type(lsp, type);
1002 nbctl_lsp_get_type(struct ctl_context *ctx)
1004 const char *id = ctx->argv[1];
1005 const struct nbrec_logical_switch_port *lsp;
1007 lsp = lsp_by_name_or_uuid(ctx, id, true);
1008 ds_put_format(&ctx->output, "%s\n", lsp->type);
1012 nbctl_lsp_set_options(struct ctl_context *ctx)
1014 const char *id = ctx->argv[1];
1015 const struct nbrec_logical_switch_port *lsp;
1017 struct smap options = SMAP_INITIALIZER(&options);
1019 lsp = lsp_by_name_or_uuid(ctx, id, true);
1020 for (i = 2; i < ctx->argc; i++) {
1022 value = xstrdup(ctx->argv[i]);
1023 key = strsep(&value, "=");
1025 smap_add(&options, key, value);
1030 nbrec_logical_switch_port_set_options(lsp, &options);
1032 smap_destroy(&options);
1036 nbctl_lsp_get_options(struct ctl_context *ctx)
1038 const char *id = ctx->argv[1];
1039 const struct nbrec_logical_switch_port *lsp;
1040 struct smap_node *node;
1042 lsp = lsp_by_name_or_uuid(ctx, id, true);
1043 SMAP_FOR_EACH(node, &lsp->options) {
1044 ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
1049 nbctl_lsp_set_dhcpv4_options(struct ctl_context *ctx)
1051 const char *id = ctx->argv[1];
1052 const struct nbrec_logical_switch_port *lsp;
1054 lsp = lsp_by_name_or_uuid(ctx, id, true);
1055 const struct nbrec_dhcp_options *dhcp_opt = NULL;
1056 if (ctx->argc == 3 ) {
1057 dhcp_opt = dhcp_options_get(ctx, ctx->argv[2], true);
1063 char *error = ip_parse_cidr(dhcp_opt->cidr, &ip, &plen);
1066 ctl_fatal("DHCP options cidr '%s' is not IPv4", dhcp_opt->cidr);
1070 nbrec_logical_switch_port_set_dhcpv4_options(lsp, dhcp_opt);
1074 nbctl_lsp_get_dhcpv4_options(struct ctl_context *ctx)
1076 const char *id = ctx->argv[1];
1077 const struct nbrec_logical_switch_port *lsp;
1079 lsp = lsp_by_name_or_uuid(ctx, id, true);
1080 if (lsp->dhcpv4_options) {
1081 ds_put_format(&ctx->output, UUID_FMT " (%s)\n",
1082 UUID_ARGS(&lsp->dhcpv4_options->header_.uuid),
1083 lsp->dhcpv4_options->cidr);
1093 dir_encode(const char *dir)
1095 if (!strcmp(dir, "from-lport")) {
1096 return DIR_FROM_LPORT;
1097 } else if (!strcmp(dir, "to-lport")) {
1098 return DIR_TO_LPORT;
1105 acl_cmp(const void *acl1_, const void *acl2_)
1107 const struct nbrec_acl *const *acl1p = acl1_;
1108 const struct nbrec_acl *const *acl2p = acl2_;
1109 const struct nbrec_acl *acl1 = *acl1p;
1110 const struct nbrec_acl *acl2 = *acl2p;
1112 int dir1 = dir_encode(acl1->direction);
1113 int dir2 = dir_encode(acl2->direction);
1116 return dir1 < dir2 ? -1 : 1;
1117 } else if (acl1->priority != acl2->priority) {
1118 return acl1->priority > acl2->priority ? -1 : 1;
1120 return strcmp(acl1->match, acl2->match);
1125 nbctl_acl_list(struct ctl_context *ctx)
1127 const struct nbrec_logical_switch *ls;
1128 const struct nbrec_acl **acls;
1131 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
1133 acls = xmalloc(sizeof *acls * ls->n_acls);
1134 for (i = 0; i < ls->n_acls; i++) {
1135 acls[i] = ls->acls[i];
1138 qsort(acls, ls->n_acls, sizeof *acls, acl_cmp);
1140 for (i = 0; i < ls->n_acls; i++) {
1141 const struct nbrec_acl *acl = acls[i];
1142 ds_put_format(&ctx->output, "%10s %5"PRId64" (%s) %s%s\n",
1143 acl->direction, acl->priority,
1144 acl->match, acl->action, acl->log ? " log" : "");
1151 parse_direction(const char *arg)
1153 /* Validate direction. Only require the first letter. */
1154 if (arg[0] == 't') {
1156 } else if (arg[0] == 'f') {
1157 return "from-lport";
1159 ctl_fatal("%s: direction must be \"to-lport\" or \"from-lport\"", arg);
1164 parse_priority(const char *arg)
1166 /* Validate priority. */
1168 if (!ovs_scan(arg, "%"SCNd64, &priority)
1169 || priority < 0 || priority > 32767) {
1170 ctl_fatal("%s: priority must in range 0...32767", arg);
1176 nbctl_acl_add(struct ctl_context *ctx)
1178 const struct nbrec_logical_switch *ls;
1179 const char *action = ctx->argv[5];
1181 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
1183 const char *direction = parse_direction(ctx->argv[2]);
1184 int64_t priority = parse_priority(ctx->argv[3]);
1186 /* Validate action. */
1187 if (strcmp(action, "allow") && strcmp(action, "allow-related")
1188 && strcmp(action, "drop") && strcmp(action, "reject")) {
1189 ctl_fatal("%s: action must be one of \"allow\", \"allow-related\", "
1190 "\"drop\", and \"reject\"", action);
1194 /* Create the acl. */
1195 struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn);
1196 nbrec_acl_set_priority(acl, priority);
1197 nbrec_acl_set_direction(acl, direction);
1198 nbrec_acl_set_match(acl, ctx->argv[4]);
1199 nbrec_acl_set_action(acl, action);
1200 if (shash_find(&ctx->options, "--log") != NULL) {
1201 nbrec_acl_set_log(acl, true);
1204 /* Insert the acl into the logical switch. */
1205 nbrec_logical_switch_verify_acls(ls);
1206 struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * (ls->n_acls + 1));
1207 memcpy(new_acls, ls->acls, sizeof *new_acls * ls->n_acls);
1208 new_acls[ls->n_acls] = acl;
1209 nbrec_logical_switch_set_acls(ls, new_acls, ls->n_acls + 1);
1214 nbctl_acl_del(struct ctl_context *ctx)
1216 const struct nbrec_logical_switch *ls;
1217 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
1219 if (ctx->argc != 2 && ctx->argc != 3 && ctx->argc != 5) {
1220 ctl_fatal("cannot specify priority without match");
1223 if (ctx->argc == 2) {
1224 /* If direction, priority, and match are not specified, delete
1226 nbrec_logical_switch_verify_acls(ls);
1227 nbrec_logical_switch_set_acls(ls, NULL, 0);
1231 const char *direction = parse_direction(ctx->argv[2]);
1233 /* If priority and match are not specified, delete all ACLs with the
1234 * specified direction. */
1235 if (ctx->argc == 3) {
1236 struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * ls->n_acls);
1239 for (size_t i = 0; i < ls->n_acls; i++) {
1240 if (strcmp(direction, ls->acls[i]->direction)) {
1241 new_acls[n_acls++] = ls->acls[i];
1245 nbrec_logical_switch_verify_acls(ls);
1246 nbrec_logical_switch_set_acls(ls, new_acls, n_acls);
1251 int64_t priority = parse_priority(ctx->argv[3]);
1253 /* Remove the matching rule. */
1254 for (size_t i = 0; i < ls->n_acls; i++) {
1255 struct nbrec_acl *acl = ls->acls[i];
1257 if (priority == acl->priority && !strcmp(ctx->argv[4], acl->match) &&
1258 !strcmp(direction, acl->direction)) {
1259 struct nbrec_acl **new_acls
1260 = xmemdup(ls->acls, sizeof *new_acls * ls->n_acls);
1261 new_acls[i] = ls->acls[ls->n_acls - 1];
1262 nbrec_logical_switch_verify_acls(ls);
1263 nbrec_logical_switch_set_acls(ls, new_acls,
1272 nbctl_lr_add(struct ctl_context *ctx)
1274 const char *lr_name = ctx->argc == 2 ? ctx->argv[1] : NULL;
1276 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
1277 bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL;
1278 if (may_exist && add_duplicate) {
1279 ctl_fatal("--may-exist and --add-duplicate may not be used together");
1283 if (!add_duplicate) {
1284 const struct nbrec_logical_router *lr;
1285 NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->idl) {
1286 if (!strcmp(lr->name, lr_name)) {
1290 ctl_fatal("%s: a router with this name already exists",
1295 } else if (may_exist) {
1296 ctl_fatal("--may-exist requires specifying a name");
1297 } else if (add_duplicate) {
1298 ctl_fatal("--add-duplicate requires specifying a name");
1301 struct nbrec_logical_router *lr;
1302 lr = nbrec_logical_router_insert(ctx->txn);
1304 nbrec_logical_router_set_name(lr, lr_name);
1309 nbctl_lr_del(struct ctl_context *ctx)
1311 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1312 const char *id = ctx->argv[1];
1313 const struct nbrec_logical_router *lr;
1315 lr = lr_by_name_or_uuid(ctx, id, must_exist);
1320 nbrec_logical_router_delete(lr);
1324 nbctl_lr_list(struct ctl_context *ctx)
1326 const struct nbrec_logical_router *lr;
1330 NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) {
1331 smap_add_format(&lrs, lr->name, UUID_FMT " (%s)",
1332 UUID_ARGS(&lr->header_.uuid), lr->name);
1334 const struct smap_node **nodes = smap_sort(&lrs);
1335 for (size_t i = 0; i < smap_count(&lrs); i++) {
1336 const struct smap_node *node = nodes[i];
1337 ds_put_format(&ctx->output, "%s\n", node->value);
1343 static const struct nbrec_dhcp_options *
1344 dhcp_options_get(struct ctl_context *ctx, const char *id, bool must_exist)
1346 struct uuid dhcp_opts_uuid;
1347 const struct nbrec_dhcp_options *dhcp_opts = NULL;
1348 if (uuid_from_string(&dhcp_opts_uuid, id)) {
1349 dhcp_opts = nbrec_dhcp_options_get_for_uuid(ctx->idl, &dhcp_opts_uuid);
1352 if (!dhcp_opts && must_exist) {
1353 ctl_fatal("%s: dhcp options UUID not found", id);
1359 nbctl_dhcp_options_create(struct ctl_context *ctx)
1361 /* Validate the cidr */
1364 char *error = ip_parse_cidr(ctx->argv[1], &ip, &plen);
1366 /* check if its IPv6 cidr */
1368 struct in6_addr ipv6;
1369 error = ipv6_parse_cidr(ctx->argv[1], &ipv6, &plen);
1372 ctl_fatal("Invalid cidr format '%s'", ctx->argv[1]);
1377 struct nbrec_dhcp_options *dhcp_opts = nbrec_dhcp_options_insert(ctx->txn);
1378 nbrec_dhcp_options_set_cidr(dhcp_opts, ctx->argv[1]);
1380 struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
1381 for (size_t i = 2; i < ctx->argc; i++) {
1383 value = xstrdup(ctx->argv[i]);
1384 key = strsep(&value, "=");
1386 smap_add(&ext_ids, key, value);
1391 nbrec_dhcp_options_set_external_ids(dhcp_opts, &ext_ids);
1392 smap_destroy(&ext_ids);
1396 nbctl_dhcp_options_set_options(struct ctl_context *ctx)
1398 const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get(
1399 ctx, ctx->argv[1], true);
1401 struct smap dhcp_options = SMAP_INITIALIZER(&dhcp_options);
1402 for (size_t i = 2; i < ctx->argc; i++) {
1404 value = xstrdup(ctx->argv[i]);
1405 key = strsep(&value, "=");
1407 smap_add(&dhcp_options, key, value);
1412 nbrec_dhcp_options_set_options(dhcp_opts, &dhcp_options);
1413 smap_destroy(&dhcp_options);
1417 nbctl_dhcp_options_get_options(struct ctl_context *ctx)
1419 const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get(
1420 ctx, ctx->argv[1], true);
1422 struct smap_node *node;
1423 SMAP_FOR_EACH(node, &dhcp_opts->options) {
1424 ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
1429 nbctl_dhcp_options_del(struct ctl_context *ctx)
1431 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1432 const char *id = ctx->argv[1];
1433 const struct nbrec_dhcp_options *dhcp_opts;
1435 dhcp_opts = dhcp_options_get(ctx, id, must_exist);
1440 nbrec_dhcp_options_delete(dhcp_opts);
1444 nbctl_dhcp_options_list(struct ctl_context *ctx)
1446 const struct nbrec_dhcp_options *dhcp_opts;
1447 struct smap dhcp_options;
1449 smap_init(&dhcp_options);
1450 NBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opts, ctx->idl) {
1451 smap_add_format(&dhcp_options, dhcp_opts->cidr, UUID_FMT,
1452 UUID_ARGS(&dhcp_opts->header_.uuid));
1454 const struct smap_node **nodes = smap_sort(&dhcp_options);
1455 for (size_t i = 0; i < smap_count(&dhcp_options); i++) {
1456 const struct smap_node *node = nodes[i];
1457 ds_put_format(&ctx->output, "%s\n", node->value);
1459 smap_destroy(&dhcp_options);
1463 /* The caller must free the returned string. */
1465 normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen)
1467 ovs_be32 network = ipv4 & be32_prefix_mask(plen);
1469 return xasprintf(IP_FMT, IP_ARGS(network));
1471 return xasprintf(IP_FMT"/%d", IP_ARGS(network), plen);
1475 /* The caller must free the returned string. */
1477 normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen)
1479 char network_s[INET6_ADDRSTRLEN];
1481 struct in6_addr mask = ipv6_create_mask(plen);
1482 struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask);
1484 inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN);
1486 return xasprintf("%s", network_s);
1488 return xasprintf("%s/%d", network_s, plen);
1492 /* The caller must free the returned string. */
1494 normalize_prefix_str(const char *orig_prefix)
1500 error = ip_parse_cidr(orig_prefix, &ipv4, &plen);
1502 return normalize_ipv4_prefix(ipv4, plen);
1504 struct in6_addr ipv6;
1507 error = ipv6_parse_cidr(orig_prefix, &ipv6, &plen);
1512 return normalize_ipv6_prefix(ipv6, plen);
1517 nbctl_lr_route_add(struct ctl_context *ctx)
1519 const struct nbrec_logical_router *lr;
1520 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
1521 char *prefix, *next_hop;
1523 prefix = normalize_prefix_str(ctx->argv[2]);
1525 ctl_fatal("bad prefix argument: %s", ctx->argv[2]);
1528 next_hop = normalize_prefix_str(ctx->argv[3]);
1530 ctl_fatal("bad next hop argument: %s", ctx->argv[3]);
1533 if (strchr(prefix, '.')) {
1535 if (!ip_parse(ctx->argv[3], &hop_ipv4)) {
1536 ctl_fatal("bad IPv4 nexthop argument: %s", ctx->argv[3]);
1539 struct in6_addr hop_ipv6;
1540 if (!ipv6_parse(ctx->argv[3], &hop_ipv6)) {
1541 ctl_fatal("bad IPv6 nexthop argument: %s", ctx->argv[3]);
1545 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
1546 for (int i = 0; i < lr->n_static_routes; i++) {
1547 const struct nbrec_logical_router_static_route *route
1548 = lr->static_routes[i];
1551 rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix);
1553 /* Ignore existing prefix we couldn't parse. */
1557 if (strcmp(rt_prefix, prefix)) {
1563 ctl_fatal("duplicate prefix: %s", prefix);
1566 /* Update the next hop for an existing route. */
1567 nbrec_logical_router_verify_static_routes(lr);
1568 nbrec_logical_router_static_route_verify_ip_prefix(route);
1569 nbrec_logical_router_static_route_verify_nexthop(route);
1570 nbrec_logical_router_static_route_set_ip_prefix(route, prefix);
1571 nbrec_logical_router_static_route_set_nexthop(route, next_hop);
1572 if (ctx->argc == 5) {
1573 nbrec_logical_router_static_route_set_output_port(route,
1582 struct nbrec_logical_router_static_route *route;
1583 route = nbrec_logical_router_static_route_insert(ctx->txn);
1584 nbrec_logical_router_static_route_set_ip_prefix(route, prefix);
1585 nbrec_logical_router_static_route_set_nexthop(route, next_hop);
1586 if (ctx->argc == 5) {
1587 nbrec_logical_router_static_route_set_output_port(route, ctx->argv[4]);
1590 nbrec_logical_router_verify_static_routes(lr);
1591 struct nbrec_logical_router_static_route **new_routes
1592 = xmalloc(sizeof *new_routes * (lr->n_static_routes + 1));
1593 memcpy(new_routes, lr->static_routes,
1594 sizeof *new_routes * lr->n_static_routes);
1595 new_routes[lr->n_static_routes] = route;
1596 nbrec_logical_router_set_static_routes(lr, new_routes,
1597 lr->n_static_routes + 1);
1604 nbctl_lr_route_del(struct ctl_context *ctx)
1606 const struct nbrec_logical_router *lr;
1607 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
1609 if (ctx->argc == 2) {
1610 /* If a prefix is not specified, delete all routes. */
1611 nbrec_logical_router_set_static_routes(lr, NULL, 0);
1615 char *prefix = normalize_prefix_str(ctx->argv[2]);
1617 ctl_fatal("bad prefix argument: %s", ctx->argv[2]);
1620 for (int i = 0; i < lr->n_static_routes; i++) {
1621 char *rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix);
1623 /* Ignore existing prefix we couldn't parse. */
1627 if (!strcmp(prefix, rt_prefix)) {
1628 struct nbrec_logical_router_static_route **new_routes
1629 = xmemdup(lr->static_routes,
1630 sizeof *new_routes * lr->n_static_routes);
1632 new_routes[i] = lr->static_routes[lr->n_static_routes - 1];
1633 nbrec_logical_router_verify_static_routes(lr);
1634 nbrec_logical_router_set_static_routes(lr, new_routes,
1635 lr->n_static_routes - 1);
1644 if (!shash_find(&ctx->options, "--if-exists")) {
1645 ctl_fatal("no matching prefix: %s", prefix);
1650 static const struct nbrec_logical_router_port *
1651 lrp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist)
1653 const struct nbrec_logical_router_port *lrp = NULL;
1655 struct uuid lrp_uuid;
1656 bool is_uuid = uuid_from_string(&lrp_uuid, id);
1658 lrp = nbrec_logical_router_port_get_for_uuid(ctx->idl, &lrp_uuid);
1662 NBREC_LOGICAL_ROUTER_PORT_FOR_EACH(lrp, ctx->idl) {
1663 if (!strcmp(lrp->name, id)) {
1669 if (!lrp && must_exist) {
1670 ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name");
1676 /* Returns the logical router that contains 'lrp'. */
1677 static const struct nbrec_logical_router *
1678 lrp_to_lr(const struct ovsdb_idl *idl,
1679 const struct nbrec_logical_router_port *lrp)
1681 const struct nbrec_logical_router *lr;
1682 NBREC_LOGICAL_ROUTER_FOR_EACH (lr, idl) {
1683 for (size_t i = 0; i < lr->n_ports; i++) {
1684 if (lr->ports[i] == lrp) {
1690 /* Can't happen because of the database schema */
1691 ctl_fatal("port %s is not part of any logical router",
1696 lr_get_name(const struct nbrec_logical_router *lr, char uuid_s[UUID_LEN + 1],
1702 snprintf(uuid_s, uuid_s_size, UUID_FMT, UUID_ARGS(&lr->header_.uuid));
1707 nbctl_lrp_add(struct ctl_context *ctx)
1709 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
1711 const struct nbrec_logical_router *lr;
1712 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
1714 const char *lrp_name = ctx->argv[2];
1715 const char *mac = ctx->argv[3];
1716 const char **networks = (const char **) &ctx->argv[4];
1718 int n_networks = ctx->argc - 4;
1719 for (int i = 4; i < ctx->argc; i++) {
1720 if (strchr(ctx->argv[i], '=')) {
1727 ctl_fatal("%s: router port requires specifying a network", lrp_name);
1730 char **settings = (char **) &ctx->argv[n_networks + 4];
1731 int n_settings = ctx->argc - 4 - n_networks;
1733 const struct nbrec_logical_router_port *lrp;
1734 lrp = lrp_by_name_or_uuid(ctx, lrp_name, false);
1737 ctl_fatal("%s: a port with this name already exists",
1741 const struct nbrec_logical_router *bound_lr;
1742 bound_lr = lrp_to_lr(ctx->idl, lrp);
1743 if (bound_lr != lr) {
1744 char uuid_s[UUID_LEN + 1];
1745 ctl_fatal("%s: port already exists but in router %s", lrp_name,
1746 lr_get_name(bound_lr, uuid_s, sizeof uuid_s));
1749 if (strcmp(mac, lrp->mac)) {
1750 ctl_fatal("%s: port already exists with mac %s", lrp_name,
1754 struct sset new_networks = SSET_INITIALIZER(&new_networks);
1755 for (int i = 0; i < n_networks; i++) {
1756 sset_add(&new_networks, networks[i]);
1759 struct sset orig_networks = SSET_INITIALIZER(&orig_networks);
1760 sset_add_array(&orig_networks, lrp->networks, lrp->n_networks);
1762 if (!sset_equals(&orig_networks, &new_networks)) {
1763 ctl_fatal("%s: port already exists with different network",
1767 sset_destroy(&orig_networks);
1768 sset_destroy(&new_networks);
1770 /* Special-case sanity-check of peer ports. */
1771 const char *peer = NULL;
1772 for (int i = 0; i < n_settings; i++) {
1773 if (!strncmp(settings[i], "peer=", 5)) {
1774 peer = settings[i] + 5;
1779 if ((!peer != !lrp->peer) ||
1780 (lrp->peer && strcmp(peer, lrp->peer))) {
1781 ctl_fatal("%s: port already exists with mismatching peer",
1789 if (!ovs_scan(mac, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) {
1790 ctl_fatal("%s: invalid mac address %s", lrp_name, mac);
1793 for (int i = 0; i < n_networks; i++) {
1796 char *error = ip_parse_cidr(networks[i], &ipv4, &plen);
1799 struct in6_addr ipv6;
1800 error = ipv6_parse_cidr(networks[i], &ipv6, &plen);
1803 ctl_fatal("%s: invalid network address: %s", lrp_name,
1809 /* Create the logical port. */
1810 lrp = nbrec_logical_router_port_insert(ctx->txn);
1811 nbrec_logical_router_port_set_name(lrp, lrp_name);
1812 nbrec_logical_router_port_set_mac(lrp, mac);
1813 nbrec_logical_router_port_set_networks(lrp, networks, n_networks);
1815 for (int i = 0; i < n_settings; i++) {
1816 ctl_set_column("Logical_Router_Port", &lrp->header_, settings[i],
1820 /* Insert the logical port into the logical router. */
1821 nbrec_logical_router_verify_ports(lr);
1822 struct nbrec_logical_router_port **new_ports = xmalloc(sizeof *new_ports *
1824 memcpy(new_ports, lr->ports, sizeof *new_ports * lr->n_ports);
1825 new_ports[lr->n_ports] = CONST_CAST(struct nbrec_logical_router_port *,
1827 nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports + 1);
1831 /* Removes logical router port 'lr->ports[idx]'. */
1833 remove_lrp(const struct nbrec_logical_router *lr, size_t idx)
1835 const struct nbrec_logical_router_port *lrp = lr->ports[idx];
1837 /* First remove 'lrp' from the array of ports. This is what will
1838 * actually cause the logical port to be deleted when the transaction is
1839 * sent to the database server (due to garbage collection). */
1840 struct nbrec_logical_router_port **new_ports
1841 = xmemdup(lr->ports, sizeof *new_ports * lr->n_ports);
1842 new_ports[idx] = new_ports[lr->n_ports - 1];
1843 nbrec_logical_router_verify_ports(lr);
1844 nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports - 1);
1847 /* Delete 'lrp' from the IDL. This won't have a real effect on
1848 * the database server (the IDL will suppress it in fact) but it
1849 * means that it won't show up when we iterate with
1850 * NBREC_LOGICAL_ROUTER_PORT_FOR_EACH later. */
1851 nbrec_logical_router_port_delete(lrp);
1855 nbctl_lrp_del(struct ctl_context *ctx)
1857 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1858 const struct nbrec_logical_router_port *lrp;
1860 lrp = lrp_by_name_or_uuid(ctx, ctx->argv[1], must_exist);
1865 /* Find the router that contains 'lrp', then delete it. */
1866 const struct nbrec_logical_router *lr;
1867 NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->idl) {
1868 for (size_t i = 0; i < lr->n_ports; i++) {
1869 if (lr->ports[i] == lrp) {
1876 /* Can't happen because of the database schema. */
1877 ctl_fatal("logical port %s is not part of any logical router",
1881 /* Print a list of logical router ports. */
1883 nbctl_lrp_list(struct ctl_context *ctx)
1885 const char *id = ctx->argv[1];
1886 const struct nbrec_logical_router *lr;
1890 lr = lr_by_name_or_uuid(ctx, id, true);
1893 for (i = 0; i < lr->n_ports; i++) {
1894 const struct nbrec_logical_router_port *lrp = lr->ports[i];
1895 smap_add_format(&lrps, lrp->name, UUID_FMT " (%s)",
1896 UUID_ARGS(&lrp->header_.uuid), lrp->name);
1898 const struct smap_node **nodes = smap_sort(&lrps);
1899 for (i = 0; i < smap_count(&lrps); i++) {
1900 const struct smap_node *node = nodes[i];
1901 ds_put_format(&ctx->output, "%s\n", node->value);
1903 smap_destroy(&lrps);
1907 /* Set the logical router port admin-enabled state. */
1909 nbctl_lrp_set_enabled(struct ctl_context *ctx)
1911 const char *id = ctx->argv[1];
1912 const char *state = ctx->argv[2];
1913 const struct nbrec_logical_router_port *lrp;
1915 lrp = lrp_by_name_or_uuid(ctx, id, true);
1920 bool enabled = parse_enabled(state);
1921 nbrec_logical_router_port_set_enabled(lrp, &enabled, 1);
1924 /* Print admin-enabled state for logical router port. */
1926 nbctl_lrp_get_enabled(struct ctl_context *ctx)
1928 const char *id = ctx->argv[1];
1929 const struct nbrec_logical_router_port *lrp;
1931 lrp = lrp_by_name_or_uuid(ctx, id, true);
1936 ds_put_format(&ctx->output, "%s\n",
1938 *lrp->enabled ? "enabled" : "disabled");
1944 const struct nbrec_logical_router_static_route *route;
1948 ipv4_route_cmp(const void *route1_, const void *route2_)
1950 const struct ipv4_route *route1p = route1_;
1951 const struct ipv4_route *route2p = route2_;
1953 if (route1p->plen != route2p->plen) {
1954 return route1p->plen > route2p->plen ? -1 : 1;
1955 } else if (route1p->addr != route2p->addr) {
1956 return ntohl(route1p->addr) < ntohl(route2p->addr) ? -1 : 1;
1964 struct in6_addr addr;
1965 const struct nbrec_logical_router_static_route *route;
1969 ipv6_route_cmp(const void *route1_, const void *route2_)
1971 const struct ipv6_route *route1p = route1_;
1972 const struct ipv6_route *route2p = route2_;
1974 if (route1p->plen != route2p->plen) {
1975 return route1p->plen > route2p->plen ? -1 : 1;
1977 return memcmp(&route1p->addr, &route2p->addr, sizeof(route1p->addr));
1981 print_route(const struct nbrec_logical_router_static_route *route, struct ds *s)
1984 char *prefix = normalize_prefix_str(route->ip_prefix);
1985 char *next_hop = normalize_prefix_str(route->nexthop);
1986 ds_put_format(s, "%25s %25s", prefix, next_hop);
1990 if (route->output_port) {
1991 ds_put_format(s, " %s", route->output_port);
1993 ds_put_char(s, '\n');
1997 nbctl_lr_route_list(struct ctl_context *ctx)
1999 const struct nbrec_logical_router *lr;
2000 struct ipv4_route *ipv4_routes;
2001 struct ipv6_route *ipv6_routes;
2002 size_t n_ipv4_routes = 0;
2003 size_t n_ipv6_routes = 0;
2005 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
2007 ipv4_routes = xmalloc(sizeof *ipv4_routes * lr->n_static_routes);
2008 ipv6_routes = xmalloc(sizeof *ipv6_routes * lr->n_static_routes);
2010 for (int i = 0; i < lr->n_static_routes; i++) {
2011 const struct nbrec_logical_router_static_route *route
2012 = lr->static_routes[i];
2017 error = ip_parse_cidr(route->ip_prefix, &ipv4, &plen);
2019 ipv4_routes[n_ipv4_routes].plen = plen;
2020 ipv4_routes[n_ipv4_routes].addr = ipv4;
2021 ipv4_routes[n_ipv4_routes].route = route;
2026 struct in6_addr ipv6;
2027 error = ipv6_parse_cidr(route->ip_prefix, &ipv6, &plen);
2029 ipv6_routes[n_ipv6_routes].plen = plen;
2030 ipv6_routes[n_ipv6_routes].addr = ipv6;
2031 ipv6_routes[n_ipv6_routes].route = route;
2034 /* Invalid prefix. */
2035 VLOG_WARN("router "UUID_FMT" (%s) has invalid prefix: %s",
2036 UUID_ARGS(&lr->header_.uuid), lr->name,
2044 qsort(ipv4_routes, n_ipv4_routes, sizeof *ipv4_routes, ipv4_route_cmp);
2045 qsort(ipv6_routes, n_ipv6_routes, sizeof *ipv6_routes, ipv6_route_cmp);
2047 if (n_ipv4_routes) {
2048 ds_put_cstr(&ctx->output, "IPv4 Routes\n");
2050 for (int i = 0; i < n_ipv4_routes; i++) {
2051 print_route(ipv4_routes[i].route, &ctx->output);
2054 if (n_ipv6_routes) {
2055 ds_put_format(&ctx->output, "%sIPv6 Routes\n",
2056 n_ipv4_routes ? "\n" : "");
2058 for (int i = 0; i < n_ipv6_routes; i++) {
2059 print_route(ipv6_routes[i].route, &ctx->output);
2066 static const struct ctl_table_class tables[] = {
2067 {&nbrec_table_logical_switch,
2068 {{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL},
2069 {NULL, NULL, NULL}}},
2071 {&nbrec_table_logical_switch_port,
2072 {{&nbrec_table_logical_switch_port, &nbrec_logical_switch_port_col_name,
2074 {NULL, NULL, NULL}}},
2077 {{NULL, NULL, NULL},
2078 {NULL, NULL, NULL}}},
2080 {&nbrec_table_load_balancer,
2081 {{NULL, NULL, NULL},
2082 {NULL, NULL, NULL}}},
2084 {&nbrec_table_logical_router,
2085 {{&nbrec_table_logical_router, &nbrec_logical_router_col_name, NULL},
2086 {NULL, NULL, NULL}}},
2088 {&nbrec_table_logical_router_port,
2089 {{&nbrec_table_logical_router_port, &nbrec_logical_router_port_col_name,
2091 {NULL, NULL, NULL}}},
2093 {&nbrec_table_logical_router_static_route,
2094 {{&nbrec_table_logical_router_static_route, NULL,
2096 {NULL, NULL, NULL}}},
2099 {{&nbrec_table_nat, NULL,
2101 {NULL, NULL, NULL}}},
2103 {&nbrec_table_address_set,
2104 {{&nbrec_table_address_set, &nbrec_address_set_col_name, NULL},
2105 {NULL, NULL, NULL}}},
2107 {&nbrec_table_dhcp_options,
2108 {{&nbrec_table_dhcp_options, NULL,
2110 {NULL, NULL, NULL}}},
2112 {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
2116 run_prerequisites(struct ctl_command *commands, size_t n_commands,
2117 struct ovsdb_idl *idl)
2119 struct ctl_command *c;
2121 for (c = commands; c < &commands[n_commands]; c++) {
2122 if (c->syntax->prerequisites) {
2123 struct ctl_context ctx;
2125 ds_init(&c->output);
2128 ctl_context_init(&ctx, c, idl, NULL, NULL, NULL);
2129 (c->syntax->prerequisites)(&ctx);
2130 ctl_context_done(&ctx, c);
2132 ovs_assert(!c->output.string);
2133 ovs_assert(!c->table);
2139 do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands,
2140 struct ovsdb_idl *idl)
2142 struct ovsdb_idl_txn *txn;
2143 enum ovsdb_idl_txn_status status;
2144 struct ovsdb_symbol_table *symtab;
2145 struct ctl_context ctx;
2146 struct ctl_command *c;
2147 struct shash_node *node;
2150 txn = the_idl_txn = ovsdb_idl_txn_create(idl);
2152 ovsdb_idl_txn_set_dry_run(txn);
2155 ovsdb_idl_txn_add_comment(txn, "ovs-nbctl: %s", args);
2157 symtab = ovsdb_symbol_table_create();
2158 for (c = commands; c < &commands[n_commands]; c++) {
2159 ds_init(&c->output);
2162 ctl_context_init(&ctx, NULL, idl, txn, symtab, NULL);
2163 for (c = commands; c < &commands[n_commands]; c++) {
2164 ctl_context_init_command(&ctx, c);
2165 if (c->syntax->run) {
2166 (c->syntax->run)(&ctx);
2168 ctl_context_done_command(&ctx, c);
2170 if (ctx.try_again) {
2171 ctl_context_done(&ctx, NULL);
2175 ctl_context_done(&ctx, NULL);
2177 SHASH_FOR_EACH (node, &symtab->sh) {
2178 struct ovsdb_symbol *symbol = node->data;
2179 if (!symbol->created) {
2180 ctl_fatal("row id \"%s\" is referenced but never created (e.g. "
2181 "with \"-- --id=%s create ...\")",
2182 node->name, node->name);
2184 if (!symbol->strong_ref) {
2185 if (!symbol->weak_ref) {
2186 VLOG_WARN("row id \"%s\" was created but no reference to it "
2187 "was inserted, so it will not actually appear in "
2188 "the database", node->name);
2190 VLOG_WARN("row id \"%s\" was created but only a weak "
2191 "reference to it was inserted, so it will not "
2192 "actually appear in the database", node->name);
2197 status = ovsdb_idl_txn_commit_block(txn);
2198 if (status == TXN_UNCHANGED || status == TXN_SUCCESS) {
2199 for (c = commands; c < &commands[n_commands]; c++) {
2200 if (c->syntax->postprocess) {
2201 ctl_context_init(&ctx, c, idl, txn, symtab, NULL);
2202 (c->syntax->postprocess)(&ctx);
2203 ctl_context_done(&ctx, c);
2207 error = xstrdup(ovsdb_idl_txn_get_error(txn));
2210 case TXN_UNCOMMITTED:
2211 case TXN_INCOMPLETE:
2215 /* Should not happen--we never call ovsdb_idl_txn_abort(). */
2216 ctl_fatal("transaction aborted");
2226 ctl_fatal("transaction error: %s", error);
2228 case TXN_NOT_LOCKED:
2229 /* Should not happen--we never call ovsdb_idl_set_lock(). */
2230 ctl_fatal("database not locked");
2237 ovsdb_symbol_table_destroy(symtab);
2239 for (c = commands; c < &commands[n_commands]; c++) {
2240 struct ds *ds = &c->output;
2243 table_print(c->table, &table_style);
2244 } else if (oneline) {
2248 for (j = 0; j < ds->length; j++) {
2249 int ch = ds->string[j];
2252 fputs("\\n", stdout);
2256 fputs("\\\\", stdout);
2265 fputs(ds_cstr(ds), stdout);
2267 ds_destroy(&c->output);
2268 table_destroy(c->table);
2271 shash_destroy_free_data(&c->options);
2274 ovsdb_idl_txn_destroy(txn);
2275 ovsdb_idl_destroy(idl);
2280 /* Our transaction needs to be rerun, or a prerequisite was not met. Free
2281 * resources and return so that the caller can try again. */
2283 ovsdb_idl_txn_abort(txn);
2284 ovsdb_idl_txn_destroy(txn);
2287 ovsdb_symbol_table_destroy(symtab);
2288 for (c = commands; c < &commands[n_commands]; c++) {
2289 ds_destroy(&c->output);
2290 table_destroy(c->table);
2297 /* Frees the current transaction and the underlying IDL and then calls
2300 * Freeing the transaction and the IDL is not strictly necessary, but it makes
2301 * for a clean memory leak report from valgrind in the normal case. That makes
2302 * it easier to notice real memory leaks. */
2304 nbctl_exit(int status)
2307 ovsdb_idl_txn_abort(the_idl_txn);
2308 ovsdb_idl_txn_destroy(the_idl_txn);
2310 ovsdb_idl_destroy(the_idl);
2314 static const struct ctl_command_syntax nbctl_commands[] = {
2315 { "show", 0, 1, "[SWITCH]", NULL, nbctl_show, NULL, "", RO },
2317 /* logical switch commands. */
2318 { "ls-add", 0, 1, "[SWITCH]", NULL, nbctl_ls_add, NULL,
2319 "--may-exist,--add-duplicate", RW },
2320 { "ls-del", 1, 1, "SWITCH", NULL, nbctl_ls_del, NULL, "--if-exists", RW },
2321 { "ls-list", 0, 0, "", NULL, nbctl_ls_list, NULL, "", RO },
2324 { "acl-add", 5, 5, "SWITCH DIRECTION PRIORITY MATCH ACTION", NULL,
2325 nbctl_acl_add, NULL, "--log", RW },
2326 { "acl-del", 1, 4, "SWITCH [DIRECTION [PRIORITY MATCH]]", NULL,
2327 nbctl_acl_del, NULL, "", RW },
2328 { "acl-list", 1, 1, "SWITCH", NULL, nbctl_acl_list, NULL, "", RO },
2330 /* logical switch port commands. */
2331 { "lsp-add", 2, 4, "SWITCH PORT [PARENT] [TAG]", NULL, nbctl_lsp_add,
2332 NULL, "--may-exist", RW },
2333 { "lsp-del", 1, 1, "PORT", NULL, nbctl_lsp_del, NULL, "--if-exists", RW },
2334 { "lsp-list", 1, 1, "SWITCH", NULL, nbctl_lsp_list, NULL, "", RO },
2335 { "lsp-get-parent", 1, 1, "PORT", NULL, nbctl_lsp_get_parent, NULL,
2337 { "lsp-get-tag", 1, 1, "PORT", NULL, nbctl_lsp_get_tag, NULL, "", RO },
2338 { "lsp-set-addresses", 1, INT_MAX, "PORT [ADDRESS]...", NULL,
2339 nbctl_lsp_set_addresses, NULL, "", RW },
2340 { "lsp-get-addresses", 1, 1, "PORT", NULL, nbctl_lsp_get_addresses, NULL,
2342 { "lsp-set-port-security", 0, INT_MAX, "PORT [ADDRS]...", NULL,
2343 nbctl_lsp_set_port_security, NULL, "", RW },
2344 { "lsp-get-port-security", 1, 1, "PORT", NULL,
2345 nbctl_lsp_get_port_security, NULL, "", RO },
2346 { "lsp-get-up", 1, 1, "PORT", NULL, nbctl_lsp_get_up, NULL, "", RO },
2347 { "lsp-set-enabled", 2, 2, "PORT STATE", NULL, nbctl_lsp_set_enabled,
2349 { "lsp-get-enabled", 1, 1, "PORT", NULL, nbctl_lsp_get_enabled, NULL,
2351 { "lsp-set-type", 2, 2, "PORT TYPE", NULL, nbctl_lsp_set_type, NULL,
2353 { "lsp-get-type", 1, 1, "PORT", NULL, nbctl_lsp_get_type, NULL, "", RO },
2354 { "lsp-set-options", 1, INT_MAX, "PORT KEY=VALUE [KEY=VALUE]...", NULL,
2355 nbctl_lsp_set_options, NULL, "", RW },
2356 { "lsp-get-options", 1, 1, "PORT", NULL, nbctl_lsp_get_options, NULL,
2358 { "lsp-set-dhcpv4-options", 1, 2, "PORT [DHCP_OPT_UUID]", NULL,
2359 nbctl_lsp_set_dhcpv4_options, NULL, "", RW },
2360 { "lsp-get-dhcpv4-options", 1, 1, "PORT", NULL,
2361 nbctl_lsp_get_dhcpv4_options, NULL, "", RO },
2363 /* logical router commands. */
2364 { "lr-add", 0, 1, "[ROUTER]", NULL, nbctl_lr_add, NULL,
2365 "--may-exist,--add-duplicate", RW },
2366 { "lr-del", 1, 1, "ROUTER", NULL, nbctl_lr_del, NULL, "--if-exists", RW },
2367 { "lr-list", 0, 0, "", NULL, nbctl_lr_list, NULL, "", RO },
2369 /* logical router port commands. */
2370 { "lrp-add", 4, INT_MAX,
2371 "ROUTER PORT MAC NETWORK... [COLUMN[:KEY]=VALUE]...",
2372 NULL, nbctl_lrp_add, NULL, "--may-exist", RW },
2373 { "lrp-del", 1, 1, "PORT", NULL, nbctl_lrp_del, NULL, "--if-exists", RW },
2374 { "lrp-list", 1, 1, "ROUTER", NULL, nbctl_lrp_list, NULL, "", RO },
2375 { "lrp-set-enabled", 2, 2, "PORT STATE", NULL, nbctl_lrp_set_enabled,
2377 { "lrp-get-enabled", 1, 1, "PORT", NULL, nbctl_lrp_get_enabled,
2380 /* logical router route commands. */
2381 { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL,
2382 nbctl_lr_route_add, NULL, "--may-exist", RW },
2383 { "lr-route-del", 1, 2, "ROUTER [PREFIX]", NULL, nbctl_lr_route_del,
2384 NULL, "--if-exists", RW },
2385 { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL,
2388 /* DHCP_Options commands */
2389 {"dhcp-options-create", 1, INT_MAX, "CIDR [EXTERNAL:IDS]", NULL,
2390 nbctl_dhcp_options_create, NULL, "", RW },
2391 {"dhcp-options-del", 1, 1, "DHCP_OPT_UUID", NULL,
2392 nbctl_dhcp_options_del, NULL, "", RW},
2393 {"dhcp-options-list", 0, 0, "", NULL, nbctl_dhcp_options_list, NULL, "", RO},
2394 {"dhcp-options-set-options", 1, INT_MAX, "DHCP_OPT_UUID KEY=VALUE [KEY=VALUE]...",
2395 NULL, nbctl_dhcp_options_set_options, NULL, "", RW },
2396 {"dhcp-options-get-options", 1, 1, "DHCP_OPT_UUID", NULL,
2397 nbctl_dhcp_options_get_options, NULL, "", RO },
2399 {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO},
2402 /* Registers nbctl and common db commands. */
2404 nbctl_cmd_init(void)
2406 ctl_init(tables, NULL, nbctl_exit);
2407 ctl_register_commands(nbctl_commands);