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"
28 #include "ovn/lib/ovn-util.h"
30 #include "poll-loop.h"
35 #include "stream-ssl.h"
40 #include "openvswitch/vlog.h"
42 VLOG_DEFINE_THIS_MODULE(nbctl
);
44 /* --db: The database server to contact. */
45 static const char *db
;
47 /* --oneline: Write each command's output as a single line? */
50 /* --dry-run: Do not commit any changes. */
53 /* --wait=TYPE: Wait for configuration change to take effect? */
54 enum nbctl_wait_type
{
55 NBCTL_WAIT_NONE
, /* Do not wait. */
56 NBCTL_WAIT_SB
, /* Wait for southbound database updates. */
57 NBCTL_WAIT_HV
/* Wait for hypervisors to catch up. */
59 static enum nbctl_wait_type wait_type
= NBCTL_WAIT_NONE
;
61 /* Should we wait (if specified by 'wait_type') even if the commands don't
62 * change the database at all? */
63 static bool force_wait
= false;
65 /* --timeout: Time to wait for a connection to 'db'. */
68 /* Format for table output. */
69 static struct table_style table_style
= TABLE_STYLE_DEFAULT
;
71 /* The IDL we're using and the current transaction, if any.
72 * This is for use by nbctl_exit() only, to allow it to clean up.
73 * Other code should use its context arguments. */
74 static struct ovsdb_idl
*the_idl
;
75 static struct ovsdb_idl_txn
*the_idl_txn
;
76 OVS_NO_RETURN
static void nbctl_exit(int status
);
78 static void nbctl_cmd_init(void);
79 OVS_NO_RETURN
static void usage(void);
80 static void parse_options(int argc
, char *argv
[], struct shash
*local_options
);
81 static void run_prerequisites(struct ctl_command
[], size_t n_commands
,
83 static bool do_nbctl(const char *args
, struct ctl_command
*, size_t n
,
85 static const struct nbrec_dhcp_options
*dhcp_options_get(
86 struct ctl_context
*ctx
, const char *id
, bool must_exist
);
89 main(int argc
, char *argv
[])
91 struct ovsdb_idl
*idl
;
92 struct ctl_command
*commands
;
93 struct shash local_options
;
98 set_program_name(argv
[0]);
99 fatal_ignore_sigpipe();
100 vlog_set_levels(NULL
, VLF_CONSOLE
, VLL_WARN
);
101 vlog_set_levels_from_string_assert("reconnect:warn");
105 /* Log our arguments. This is often valuable for debugging systems. */
106 args
= process_escape_args(argv
);
107 VLOG(ctl_might_write_to_db(argv
) ? VLL_INFO
: VLL_DBG
,
108 "Called as %s", args
);
110 /* Parse command line. */
111 shash_init(&local_options
);
112 parse_options(argc
, argv
, &local_options
);
113 commands
= ctl_parse_commands(argc
- optind
, argv
+ optind
, &local_options
,
120 /* Initialize IDL. */
121 idl
= the_idl
= ovsdb_idl_create(db
, &nbrec_idl_class
, true, false);
122 run_prerequisites(commands
, n_commands
, idl
);
124 /* Execute the commands.
126 * 'seqno' is the database sequence number for which we last tried to
127 * execute our transaction. There's no point in trying to commit more than
128 * once for any given sequence number, because if the transaction fails
129 * it's because the database changed and we need to obtain an up-to-date
130 * view of the database before we try the transaction again. */
131 seqno
= ovsdb_idl_get_seqno(idl
);
134 if (!ovsdb_idl_is_alive(idl
)) {
135 int retval
= ovsdb_idl_get_last_error(idl
);
136 ctl_fatal("%s: database connection failed (%s)",
137 db
, ovs_retval_to_string(retval
));
140 if (seqno
!= ovsdb_idl_get_seqno(idl
)) {
141 seqno
= ovsdb_idl_get_seqno(idl
);
142 if (do_nbctl(args
, commands
, n_commands
, idl
)) {
148 if (seqno
== ovsdb_idl_get_seqno(idl
)) {
156 parse_options(int argc
, char *argv
[], struct shash
*local_options
)
159 OPT_DB
= UCHAR_MAX
+ 1,
168 OPT_BOOTSTRAP_CA_CERT
,
173 static const struct option global_long_options
[] = {
174 {"db", required_argument
, NULL
, OPT_DB
},
175 {"no-syslog", no_argument
, NULL
, OPT_NO_SYSLOG
},
176 {"no-wait", no_argument
, NULL
, OPT_NO_WAIT
},
177 {"wait", required_argument
, NULL
, OPT_WAIT
},
178 {"dry-run", no_argument
, NULL
, OPT_DRY_RUN
},
179 {"oneline", no_argument
, NULL
, OPT_ONELINE
},
180 {"timeout", required_argument
, NULL
, 't'},
181 {"help", no_argument
, NULL
, 'h'},
182 {"commands", no_argument
, NULL
, OPT_COMMANDS
},
183 {"options", no_argument
, NULL
, OPT_OPTIONS
},
184 {"version", no_argument
, NULL
, 'V'},
186 STREAM_SSL_LONG_OPTIONS
,
187 {"bootstrap-ca-cert", required_argument
, NULL
, OPT_BOOTSTRAP_CA_CERT
},
191 const int n_global_long_options
= ARRAY_SIZE(global_long_options
) - 1;
192 char *tmp
, *short_options
;
194 struct option
*options
;
195 size_t allocated_options
;
199 tmp
= ovs_cmdl_long_options_to_short_options(global_long_options
);
200 short_options
= xasprintf("+%s", tmp
);
203 /* We want to parse both global and command-specific options here, but
204 * getopt_long() isn't too convenient for the job. We copy our global
205 * options into a dynamic array, then append all of the command-specific
207 options
= xmemdup(global_long_options
, sizeof global_long_options
);
208 allocated_options
= ARRAY_SIZE(global_long_options
);
209 n_options
= n_global_long_options
;
210 ctl_add_cmd_options(&options
, &n_options
, &allocated_options
, OPT_LOCAL
);
216 c
= getopt_long(argc
, argv
, short_options
, options
, &idx
);
231 vlog_set_levels(&this_module
, VLF_SYSLOG
, VLL_WARN
);
235 wait_type
= NBCTL_WAIT_NONE
;
239 if (!strcmp(optarg
, "none")) {
240 wait_type
= NBCTL_WAIT_NONE
;
241 } else if (!strcmp(optarg
, "sb")) {
242 wait_type
= NBCTL_WAIT_SB
;
243 } else if (!strcmp(optarg
, "hv")) {
244 wait_type
= NBCTL_WAIT_HV
;
246 ctl_fatal("argument to --wait must be "
247 "\"none\", \"sb\", or \"hv\"");
256 if (shash_find(local_options
, options
[idx
].name
)) {
257 ctl_fatal("'%s' option specified multiple times",
260 shash_add_nocopy(local_options
,
261 xasprintf("--%s", options
[idx
].name
),
262 nullable_xstrdup(optarg
));
270 ctl_print_commands();
273 ctl_print_options(global_long_options
);
276 ovs_print_version(0, 0);
277 printf("DB Schema %s\n", nbrec_get_db_version());
281 timeout
= strtoul(optarg
, NULL
, 10);
283 ctl_fatal("value %s on -t or --timeout is invalid", optarg
);
288 TABLE_OPTION_HANDLERS(&table_style
)
289 STREAM_SSL_OPTION_HANDLERS
291 case OPT_BOOTSTRAP_CA_CERT
:
292 stream_ssl_set_ca_cert_file(optarg
, true);
305 db
= default_nb_db();
308 for (i
= n_global_long_options
; options
[i
].name
; i
++) {
309 free(CONST_CAST(char *, options
[i
].name
));
318 %s: OVN northbound DB management utility\n\
319 usage: %s [OPTIONS] COMMAND [ARG...]\n\
322 init initialize the database\n\
323 show print overview of database contents\n\
324 show SWITCH print overview of database contents for SWITCH\n\
325 show ROUTER print overview of database contents for ROUTER\n\
327 Logical switch commands:\n\
328 ls-add [SWITCH] create a logical switch named SWITCH\n\
329 ls-del SWITCH delete SWITCH and all its ports\n\
330 ls-list print the names of all logical switches\n\
333 acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\
334 add an ACL to SWITCH\n\
335 acl-del SWITCH [DIRECTION [PRIORITY MATCH]]\n\
336 remove ACLs from SWITCH\n\
337 acl-list SWITCH print ACLs for SWITCH\n\
339 Logical switch port commands:\n\
340 lsp-add SWITCH PORT add logical port PORT on SWITCH\n\
341 lsp-add SWITCH PORT PARENT TAG\n\
342 add logical port PORT on SWITCH with PARENT\n\
344 lsp-del PORT delete PORT from its attached switch\n\
345 lsp-list SWITCH print the names of all logical ports on SWITCH\n\
346 lsp-get-parent PORT get the parent of PORT if set\n\
347 lsp-get-tag PORT get the PORT's tag if set\n\
348 lsp-set-addresses PORT [ADDRESS]...\n\
349 set MAC or MAC+IP addresses for PORT.\n\
350 lsp-get-addresses PORT get a list of MAC or MAC+IP addresses on PORT\n\
351 lsp-set-port-security PORT [ADDRS]...\n\
352 set port security addresses for PORT.\n\
353 lsp-get-port-security PORT get PORT's port security addresses\n\
354 lsp-get-up PORT get state of PORT ('up' or 'down')\n\
355 lsp-set-enabled PORT STATE\n\
356 set administrative state PORT\n\
357 ('enabled' or 'disabled')\n\
358 lsp-get-enabled PORT get administrative state PORT\n\
359 ('enabled' or 'disabled')\n\
360 lsp-set-type PORT TYPE set the type for PORT\n\
361 lsp-get-type PORT get the type for PORT\n\
362 lsp-set-options PORT KEY=VALUE [KEY=VALUE]...\n\
363 set options related to the type of PORT\n\
364 lsp-get-options PORT get the type specific options for PORT\n\
365 lsp-set-dhcpv4-options PORT [DHCP_OPTIONS_UUID]\n\
366 set dhcpv4 options for PORT\n\
367 lsp-get-dhcpv4-options PORT get the dhcpv4 options for PORT\n\
369 Logical router commands:\n\
370 lr-add [ROUTER] create a logical router named ROUTER\n\
371 lr-del ROUTER delete ROUTER and all its ports\n\
372 lr-list print the names of all logical routers\n\
374 Logical router port commands:\n\
375 lrp-add ROUTER PORT MAC NETWORK... [peer=PEER]\n\
376 add logical port PORT on ROUTER\n\
377 lrp-del PORT delete PORT from its attached router\n\
378 lrp-list ROUTER print the names of all ports on ROUTER\n\
379 lrp-set-enabled PORT STATE\n\
380 set administrative state PORT\n\
381 ('enabled' or 'disabled')\n\
382 lrp-get-enabled PORT get administrative state PORT\n\
383 ('enabled' or 'disabled')\n\
386 [--policy=POLICY] lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\
387 add a route to ROUTER\n\
388 lr-route-del ROUTER [PREFIX]\n\
389 remove routes from ROUTER\n\
390 lr-route-list ROUTER print routes for ROUTER\n\
393 lr-nat-add ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]\n\
394 add a NAT to ROUTER\n\
395 lr-nat-del ROUTER [TYPE [IP]]\n\
396 remove NATs from ROUTER\n\
397 lr-nat-list ROUTER print NATs for ROUTER\n\
400 lb-add LB VIP[:PORT] IP[:PORT]... [PROTOCOL]\n\
401 create a load-balancer or add a VIP to an\n\
402 existing load balancer\n\
403 lb-del LB [VIP] remove a load-balancer or just the VIP from\n\
405 lb-list [LB] print load-balancers\n\
406 lr-lb-add ROUTER LB add a load-balancer to ROUTER\n\
407 lr-lb-del ROUTER [LB] remove load-balancers from ROUTER\n\
408 lr-lb-list ROUTER print load-balancers\n\
409 ls-lb-add SWITCH LB add a load-balancer to SWITCH\n\
410 ls-lb-del SWITCH [LB] remove load-balancers from SWITCH\n\
411 ls-lb-list SWITCH print load-balancers\n\
413 DHCP Options commands:\n\
414 dhcp-options-create CIDR [EXTERNAL_IDS]\n\
415 create a DHCP options row with CIDR\n\
416 dhcp-options-del DHCP_OPTIONS_UUID\n\
417 delete DHCP_OPTIONS_UUID\n\
418 dhcp-options-list \n\
419 lists the DHCP_Options rows\n\
420 dhcp-options-set-options DHCP_OPTIONS_UUID KEY=VALUE [KEY=VALUE]...\n\
421 set DHCP options for DHCP_OPTIONS_UUID\n\
422 dhcp-options-get-options DHCO_OPTIONS_UUID \n\
423 displays the DHCP options for DHCP_OPTIONS_UUID\n\
425 Connection commands:\n\
426 get-connection print the connections\n\
427 del-connection delete the connections\n\
428 set-connection TARGET... set the list of connections to TARGET...\n\
431 get-ssl print the SSL configuration\n\
432 del-ssl delete the SSL configuration\n\
433 set-ssl PRIV-KEY CERT CA-CERT set the SSL configuration\n\
437 Synchronization command (use with --wait=sb|hv):\n\
438 sync wait even for earlier changes to take effect\n\
441 --db=DATABASE connect to DATABASE\n\
443 --no-wait, --wait=none do not wait for OVN reconfiguration (default)\n\
444 --wait=sb wait for southbound database update\n\
445 --wait=hv wait for all chassis to catch up\n\
446 -t, --timeout=SECS wait at most SECS seconds\n\
447 --dry-run do not commit changes to database\n\
448 --oneline print exactly one line of output per command\n",
449 program_name
, program_name
, ctl_get_db_cmd_usage(),
454 --no-syslog equivalent to --verbose=nbctl:syslog:warn\n");
457 -h, --help display this help message\n\
458 -V, --version display version information\n");
459 stream_usage("database", true, true, false);
464 /* Find a logical router given its id. */
465 static const struct nbrec_logical_router
*
466 lr_by_name_or_uuid(struct ctl_context
*ctx
, const char *id
,
469 const struct nbrec_logical_router
*lr
= NULL
;
470 bool is_uuid
= false;
473 if (uuid_from_string(&lr_uuid
, id
)) {
475 lr
= nbrec_logical_router_get_for_uuid(ctx
->idl
, &lr_uuid
);
479 const struct nbrec_logical_router
*iter
;
481 NBREC_LOGICAL_ROUTER_FOR_EACH(iter
, ctx
->idl
) {
482 if (strcmp(iter
->name
, id
)) {
486 ctl_fatal("Multiple logical routers named '%s'. "
493 if (!lr
&& must_exist
) {
494 ctl_fatal("%s: router %s not found", id
, is_uuid
? "UUID" : "name");
500 static const struct nbrec_logical_switch
*
501 ls_by_name_or_uuid(struct ctl_context
*ctx
, const char *id
, bool must_exist
)
503 const struct nbrec_logical_switch
*ls
= NULL
;
506 bool is_uuid
= uuid_from_string(&ls_uuid
, id
);
508 ls
= nbrec_logical_switch_get_for_uuid(ctx
->idl
, &ls_uuid
);
512 const struct nbrec_logical_switch
*iter
;
514 NBREC_LOGICAL_SWITCH_FOR_EACH(iter
, ctx
->idl
) {
515 if (strcmp(iter
->name
, id
)) {
519 ctl_fatal("Multiple logical switches named '%s'. "
526 if (!ls
&& must_exist
) {
527 ctl_fatal("%s: switch %s not found", id
, is_uuid
? "UUID" : "name");
533 static const struct nbrec_load_balancer
*
534 lb_by_name_or_uuid(struct ctl_context
*ctx
, const char *id
, bool must_exist
)
536 const struct nbrec_load_balancer
*lb
= NULL
;
539 bool is_uuid
= uuid_from_string(&lb_uuid
, id
);
541 lb
= nbrec_load_balancer_get_for_uuid(ctx
->idl
, &lb_uuid
);
545 const struct nbrec_load_balancer
*iter
;
547 NBREC_LOAD_BALANCER_FOR_EACH(iter
, ctx
->idl
) {
548 if (strcmp(iter
->name
, id
)) {
552 ctl_fatal("Multiple load balancers named '%s'. "
559 if (!lb
&& must_exist
) {
560 ctl_fatal("%s: load balancer %s not found", id
,
561 is_uuid
? "UUID" : "name");
568 print_alias(const struct smap
*external_ids
, const char *key
, struct ds
*s
)
570 const char *alias
= smap_get(external_ids
, key
);
571 if (alias
&& alias
[0]) {
572 ds_put_format(s
, " (aka %s)", alias
);
576 /* Given pointer to logical router, this routine prints the router
579 print_lr(const struct nbrec_logical_router
*lr
, struct ds
*s
)
581 ds_put_format(s
, "router "UUID_FMT
" (%s)",
582 UUID_ARGS(&lr
->header_
.uuid
), lr
->name
);
583 print_alias(&lr
->external_ids
, "neutron:router_name", s
);
584 ds_put_char(s
, '\n');
586 for (size_t i
= 0; i
< lr
->n_ports
; i
++) {
587 const struct nbrec_logical_router_port
*lrp
= lr
->ports
[i
];
588 ds_put_format(s
, " port %s\n", lrp
->name
);
590 ds_put_cstr(s
, " mac: ");
591 ds_put_format(s
, "\"%s\"\n", lrp
->mac
);
593 if (lrp
->n_networks
) {
594 ds_put_cstr(s
, " networks: [");
595 for (size_t j
= 0; j
< lrp
->n_networks
; j
++) {
596 ds_put_format(s
, "%s\"%s\"",
600 ds_put_cstr(s
, "]\n");
604 for (size_t i
= 0; i
< lr
->n_nat
; i
++) {
605 const struct nbrec_nat
*nat
= lr
->nat
[i
];
606 ds_put_format(s
, " nat "UUID_FMT
"\n",
607 UUID_ARGS(&nat
->header_
.uuid
));
608 ds_put_cstr(s
, " external ip: ");
609 ds_put_format(s
, "\"%s\"\n", nat
->external_ip
);
610 ds_put_cstr(s
, " logical ip: ");
611 ds_put_format(s
, "\"%s\"\n", nat
->logical_ip
);
612 ds_put_cstr(s
, " type: ");
613 ds_put_format(s
, "\"%s\"\n", nat
->type
);
618 print_ls(const struct nbrec_logical_switch
*ls
, struct ds
*s
)
620 ds_put_format(s
, "switch "UUID_FMT
" (%s)",
621 UUID_ARGS(&ls
->header_
.uuid
), ls
->name
);
622 print_alias(&ls
->external_ids
, "neutron:network_name", s
);
623 ds_put_char(s
, '\n');
625 for (size_t i
= 0; i
< ls
->n_ports
; i
++) {
626 const struct nbrec_logical_switch_port
*lsp
= ls
->ports
[i
];
628 ds_put_format(s
, " port %s", lsp
->name
);
629 print_alias(&lsp
->external_ids
, "neutron:port_name", s
);
630 ds_put_char(s
, '\n');
633 ds_put_format(s
, " type: %s\n", lsp
->type
);
635 if (lsp
->parent_name
) {
636 ds_put_format(s
, " parent: %s\n", lsp
->parent_name
);
639 ds_put_format(s
, " tag: %"PRIu64
"\n", lsp
->tag
[0]);
642 /* Print the addresses, but not if there's just a single "router"
643 * address because that's just clutter. */
645 && !(lsp
->n_addresses
== 1
646 && !strcmp(lsp
->addresses
[0], "router"))) {
647 ds_put_cstr(s
, " addresses: [");
648 for (size_t j
= 0; j
< lsp
->n_addresses
; j
++) {
649 ds_put_format(s
, "%s\"%s\"",
653 ds_put_cstr(s
, "]\n");
656 const char *router_port
= smap_get(&lsp
->options
, "router-port");
658 ds_put_format(s
, " router-port: %s\n", router_port
);
664 nbctl_init(struct ctl_context
*ctx OVS_UNUSED
)
669 nbctl_pre_sync(struct ctl_context
*ctx OVS_UNUSED
)
671 if (wait_type
!= NBCTL_WAIT_NONE
) {
674 VLOG_INFO("\"sync\" command has no effect without --wait");
679 nbctl_sync(struct ctl_context
*ctx OVS_UNUSED
)
684 nbctl_show(struct ctl_context
*ctx
)
686 const struct nbrec_logical_switch
*ls
;
688 if (ctx
->argc
== 2) {
689 ls
= ls_by_name_or_uuid(ctx
, ctx
->argv
[1], false);
691 print_ls(ls
, &ctx
->output
);
694 NBREC_LOGICAL_SWITCH_FOR_EACH(ls
, ctx
->idl
) {
695 print_ls(ls
, &ctx
->output
);
698 const struct nbrec_logical_router
*lr
;
700 if (ctx
->argc
== 2) {
701 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], false);
703 print_lr(lr
, &ctx
->output
);
706 NBREC_LOGICAL_ROUTER_FOR_EACH(lr
, ctx
->idl
) {
707 print_lr(lr
, &ctx
->output
);
713 nbctl_ls_add(struct ctl_context
*ctx
)
715 const char *ls_name
= ctx
->argc
== 2 ? ctx
->argv
[1] : NULL
;
717 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
718 bool add_duplicate
= shash_find(&ctx
->options
, "--add-duplicate") != NULL
;
719 if (may_exist
&& add_duplicate
) {
720 ctl_fatal("--may-exist and --add-duplicate may not be used together");
724 if (!add_duplicate
) {
725 const struct nbrec_logical_switch
*ls
;
726 NBREC_LOGICAL_SWITCH_FOR_EACH (ls
, ctx
->idl
) {
727 if (!strcmp(ls
->name
, ls_name
)) {
731 ctl_fatal("%s: a switch with this name already exists",
736 } else if (may_exist
) {
737 ctl_fatal("--may-exist requires specifying a name");
738 } else if (add_duplicate
) {
739 ctl_fatal("--add-duplicate requires specifying a name");
742 struct nbrec_logical_switch
*ls
;
743 ls
= nbrec_logical_switch_insert(ctx
->txn
);
745 nbrec_logical_switch_set_name(ls
, ls_name
);
750 nbctl_ls_del(struct ctl_context
*ctx
)
752 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
753 const char *id
= ctx
->argv
[1];
754 const struct nbrec_logical_switch
*ls
;
756 ls
= ls_by_name_or_uuid(ctx
, id
, must_exist
);
761 nbrec_logical_switch_delete(ls
);
765 nbctl_ls_list(struct ctl_context
*ctx
)
767 const struct nbrec_logical_switch
*ls
;
768 struct smap lswitches
;
770 smap_init(&lswitches
);
771 NBREC_LOGICAL_SWITCH_FOR_EACH(ls
, ctx
->idl
) {
772 smap_add_format(&lswitches
, ls
->name
, UUID_FMT
" (%s)",
773 UUID_ARGS(&ls
->header_
.uuid
), ls
->name
);
775 const struct smap_node
**nodes
= smap_sort(&lswitches
);
776 for (size_t i
= 0; i
< smap_count(&lswitches
); i
++) {
777 const struct smap_node
*node
= nodes
[i
];
778 ds_put_format(&ctx
->output
, "%s\n", node
->value
);
780 smap_destroy(&lswitches
);
784 static const struct nbrec_logical_switch_port
*
785 lsp_by_name_or_uuid(struct ctl_context
*ctx
, const char *id
,
788 const struct nbrec_logical_switch_port
*lsp
= NULL
;
790 struct uuid lsp_uuid
;
791 bool is_uuid
= uuid_from_string(&lsp_uuid
, id
);
793 lsp
= nbrec_logical_switch_port_get_for_uuid(ctx
->idl
, &lsp_uuid
);
797 NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(lsp
, ctx
->idl
) {
798 if (!strcmp(lsp
->name
, id
)) {
804 if (!lsp
&& must_exist
) {
805 ctl_fatal("%s: port %s not found", id
, is_uuid
? "UUID" : "name");
811 /* Returns the logical switch that contains 'lsp'. */
812 static const struct nbrec_logical_switch
*
813 lsp_to_ls(const struct ovsdb_idl
*idl
,
814 const struct nbrec_logical_switch_port
*lsp
)
816 const struct nbrec_logical_switch
*ls
;
817 NBREC_LOGICAL_SWITCH_FOR_EACH (ls
, idl
) {
818 for (size_t i
= 0; i
< ls
->n_ports
; i
++) {
819 if (ls
->ports
[i
] == lsp
) {
825 /* Can't happen because of the database schema */
826 ctl_fatal("logical port %s is not part of any logical switch",
831 ls_get_name(const struct nbrec_logical_switch
*ls
,
832 char uuid_s
[UUID_LEN
+ 1], size_t uuid_s_size
)
837 snprintf(uuid_s
, uuid_s_size
, UUID_FMT
, UUID_ARGS(&ls
->header_
.uuid
));
842 nbctl_lsp_add(struct ctl_context
*ctx
)
844 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
846 const struct nbrec_logical_switch
*ls
;
847 ls
= ls_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
849 const char *parent_name
;
851 if (ctx
->argc
== 3) {
854 } else if (ctx
->argc
== 5) {
856 parent_name
= ctx
->argv
[3];
857 if (!ovs_scan(ctx
->argv
[4], "%"SCNd64
, &tag
)
858 || tag
< 0 || tag
> 4095) {
859 ctl_fatal("%s: invalid tag (must be in range 0 to 4095)",
863 ctl_fatal("lsp-add with parent must also specify a tag");
866 const char *lsp_name
= ctx
->argv
[2];
867 const struct nbrec_logical_switch_port
*lsp
;
868 lsp
= lsp_by_name_or_uuid(ctx
, lsp_name
, false);
871 ctl_fatal("%s: a port with this name already exists",
875 const struct nbrec_logical_switch
*lsw
;
876 lsw
= lsp_to_ls(ctx
->idl
, lsp
);
878 char uuid_s
[UUID_LEN
+ 1];
879 ctl_fatal("%s: port already exists but in switch %s", lsp_name
,
880 ls_get_name(lsw
, uuid_s
, sizeof uuid_s
));
884 if (!lsp
->parent_name
) {
885 ctl_fatal("%s: port already exists but has no parent",
887 } else if (strcmp(parent_name
, lsp
->parent_name
)) {
888 ctl_fatal("%s: port already exists with different parent %s",
889 lsp_name
, lsp
->parent_name
);
892 if (!lsp
->n_tag_request
) {
893 ctl_fatal("%s: port already exists but has no tag_request",
895 } else if (lsp
->tag_request
[0] != tag
) {
896 ctl_fatal("%s: port already exists with different "
897 "tag_request %"PRId64
, lsp_name
,
898 lsp
->tag_request
[0]);
901 if (lsp
->parent_name
) {
902 ctl_fatal("%s: port already exists but has parent %s",
903 lsp_name
, lsp
->parent_name
);
910 /* Create the logical port. */
911 lsp
= nbrec_logical_switch_port_insert(ctx
->txn
);
912 nbrec_logical_switch_port_set_name(lsp
, lsp_name
);
914 nbrec_logical_switch_port_set_parent_name(lsp
, parent_name
);
915 nbrec_logical_switch_port_set_tag_request(lsp
, &tag
, 1);
918 /* Insert the logical port into the logical switch. */
919 nbrec_logical_switch_verify_ports(ls
);
920 struct nbrec_logical_switch_port
**new_ports
= xmalloc(sizeof *new_ports
*
922 memcpy(new_ports
, ls
->ports
, sizeof *new_ports
* ls
->n_ports
);
923 new_ports
[ls
->n_ports
] = CONST_CAST(struct nbrec_logical_switch_port
*,
925 nbrec_logical_switch_set_ports(ls
, new_ports
, ls
->n_ports
+ 1);
929 /* Removes logical switch port 'ls->ports[idx]'. */
931 remove_lsp(const struct nbrec_logical_switch
*ls
, size_t idx
)
933 const struct nbrec_logical_switch_port
*lsp
= ls
->ports
[idx
];
935 /* First remove 'lsp' from the array of ports. This is what will
936 * actually cause the logical port to be deleted when the transaction is
937 * sent to the database server (due to garbage collection). */
938 struct nbrec_logical_switch_port
**new_ports
939 = xmemdup(ls
->ports
, sizeof *new_ports
* ls
->n_ports
);
940 new_ports
[idx
] = new_ports
[ls
->n_ports
- 1];
941 nbrec_logical_switch_verify_ports(ls
);
942 nbrec_logical_switch_set_ports(ls
, new_ports
, ls
->n_ports
- 1);
945 /* Delete 'lsp' from the IDL. This won't have a real effect on the
946 * database server (the IDL will suppress it in fact) but it means that it
947 * won't show up when we iterate with NBREC_LOGICAL_SWITCH_PORT_FOR_EACH
949 nbrec_logical_switch_port_delete(lsp
);
953 nbctl_lsp_del(struct ctl_context
*ctx
)
955 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
956 const struct nbrec_logical_switch_port
*lsp
;
958 lsp
= lsp_by_name_or_uuid(ctx
, ctx
->argv
[1], must_exist
);
963 /* Find the switch that contains 'lsp', then delete it. */
964 const struct nbrec_logical_switch
*ls
;
965 NBREC_LOGICAL_SWITCH_FOR_EACH (ls
, ctx
->idl
) {
966 for (size_t i
= 0; i
< ls
->n_ports
; i
++) {
967 if (ls
->ports
[i
] == lsp
) {
974 /* Can't happen because of the database schema. */
975 ctl_fatal("logical port %s is not part of any logical switch",
980 nbctl_lsp_list(struct ctl_context
*ctx
)
982 const char *id
= ctx
->argv
[1];
983 const struct nbrec_logical_switch
*ls
;
987 ls
= ls_by_name_or_uuid(ctx
, id
, true);
990 for (i
= 0; i
< ls
->n_ports
; i
++) {
991 const struct nbrec_logical_switch_port
*lsp
= ls
->ports
[i
];
992 smap_add_format(&lsps
, lsp
->name
, UUID_FMT
" (%s)",
993 UUID_ARGS(&lsp
->header_
.uuid
), lsp
->name
);
995 const struct smap_node
**nodes
= smap_sort(&lsps
);
996 for (i
= 0; i
< smap_count(&lsps
); i
++) {
997 const struct smap_node
*node
= nodes
[i
];
998 ds_put_format(&ctx
->output
, "%s\n", node
->value
);
1000 smap_destroy(&lsps
);
1005 nbctl_lsp_get_parent(struct ctl_context
*ctx
)
1007 const struct nbrec_logical_switch_port
*lsp
;
1009 lsp
= lsp_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1010 if (lsp
->parent_name
) {
1011 ds_put_format(&ctx
->output
, "%s\n", lsp
->parent_name
);
1016 nbctl_lsp_get_tag(struct ctl_context
*ctx
)
1018 const struct nbrec_logical_switch_port
*lsp
;
1020 lsp
= lsp_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1021 if (lsp
->n_tag
> 0) {
1022 ds_put_format(&ctx
->output
, "%"PRId64
"\n", lsp
->tag
[0]);
1027 nbctl_lsp_set_addresses(struct ctl_context
*ctx
)
1029 const char *id
= ctx
->argv
[1];
1030 const struct nbrec_logical_switch_port
*lsp
;
1032 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1035 for (i
= 2; i
< ctx
->argc
; i
++) {
1038 if (strcmp(ctx
->argv
[i
], "unknown") && strcmp(ctx
->argv
[i
], "dynamic")
1039 && strcmp(ctx
->argv
[i
], "router")
1040 && !ovs_scan(ctx
->argv
[i
], ETH_ADDR_SCAN_FMT
,
1041 ETH_ADDR_SCAN_ARGS(ea
))) {
1042 ctl_fatal("%s: Invalid address format. See ovn-nb(5). "
1043 "Hint: An Ethernet address must be "
1044 "listed before an IP address, together as a single "
1045 "argument.", ctx
->argv
[i
]);
1049 nbrec_logical_switch_port_set_addresses(lsp
,
1050 (const char **) ctx
->argv
+ 2, ctx
->argc
- 2);
1054 nbctl_lsp_get_addresses(struct ctl_context
*ctx
)
1056 const char *id
= ctx
->argv
[1];
1057 const struct nbrec_logical_switch_port
*lsp
;
1058 struct svec addresses
;
1062 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1064 svec_init(&addresses
);
1065 for (i
= 0; i
< lsp
->n_addresses
; i
++) {
1066 svec_add(&addresses
, lsp
->addresses
[i
]);
1068 svec_sort(&addresses
);
1069 SVEC_FOR_EACH(i
, mac
, &addresses
) {
1070 ds_put_format(&ctx
->output
, "%s\n", mac
);
1072 svec_destroy(&addresses
);
1076 nbctl_lsp_set_port_security(struct ctl_context
*ctx
)
1078 const char *id
= ctx
->argv
[1];
1079 const struct nbrec_logical_switch_port
*lsp
;
1081 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1082 nbrec_logical_switch_port_set_port_security(lsp
,
1083 (const char **) ctx
->argv
+ 2, ctx
->argc
- 2);
1087 nbctl_lsp_get_port_security(struct ctl_context
*ctx
)
1089 const char *id
= ctx
->argv
[1];
1090 const struct nbrec_logical_switch_port
*lsp
;
1095 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1097 for (i
= 0; i
< lsp
->n_port_security
; i
++) {
1098 svec_add(&addrs
, lsp
->port_security
[i
]);
1101 SVEC_FOR_EACH(i
, addr
, &addrs
) {
1102 ds_put_format(&ctx
->output
, "%s\n", addr
);
1104 svec_destroy(&addrs
);
1108 nbctl_lsp_get_up(struct ctl_context
*ctx
)
1110 const char *id
= ctx
->argv
[1];
1111 const struct nbrec_logical_switch_port
*lsp
;
1113 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1114 ds_put_format(&ctx
->output
,
1115 "%s\n", (lsp
->up
&& *lsp
->up
) ? "up" : "down");
1119 parse_enabled(const char *state
)
1121 if (!strcasecmp(state
, "enabled")) {
1123 } else if (!strcasecmp(state
, "disabled")) {
1126 ctl_fatal("%s: state must be \"enabled\" or \"disabled\"", state
);
1131 nbctl_lsp_set_enabled(struct ctl_context
*ctx
)
1133 const char *id
= ctx
->argv
[1];
1134 const char *state
= ctx
->argv
[2];
1135 const struct nbrec_logical_switch_port
*lsp
;
1137 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1138 bool enabled
= parse_enabled(state
);
1139 nbrec_logical_switch_port_set_enabled(lsp
, &enabled
, 1);
1143 nbctl_lsp_get_enabled(struct ctl_context
*ctx
)
1145 const char *id
= ctx
->argv
[1];
1146 const struct nbrec_logical_switch_port
*lsp
;
1148 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1149 ds_put_format(&ctx
->output
, "%s\n",
1150 !lsp
->enabled
|| *lsp
->enabled
? "enabled" : "disabled");
1154 nbctl_lsp_set_type(struct ctl_context
*ctx
)
1156 const char *id
= ctx
->argv
[1];
1157 const char *type
= ctx
->argv
[2];
1158 const struct nbrec_logical_switch_port
*lsp
;
1160 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1161 nbrec_logical_switch_port_set_type(lsp
, type
);
1165 nbctl_lsp_get_type(struct ctl_context
*ctx
)
1167 const char *id
= ctx
->argv
[1];
1168 const struct nbrec_logical_switch_port
*lsp
;
1170 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1171 ds_put_format(&ctx
->output
, "%s\n", lsp
->type
);
1175 nbctl_lsp_set_options(struct ctl_context
*ctx
)
1177 const char *id
= ctx
->argv
[1];
1178 const struct nbrec_logical_switch_port
*lsp
;
1180 struct smap options
= SMAP_INITIALIZER(&options
);
1182 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1183 for (i
= 2; i
< ctx
->argc
; i
++) {
1185 value
= xstrdup(ctx
->argv
[i
]);
1186 key
= strsep(&value
, "=");
1188 smap_add(&options
, key
, value
);
1193 nbrec_logical_switch_port_set_options(lsp
, &options
);
1195 smap_destroy(&options
);
1199 nbctl_lsp_get_options(struct ctl_context
*ctx
)
1201 const char *id
= ctx
->argv
[1];
1202 const struct nbrec_logical_switch_port
*lsp
;
1203 struct smap_node
*node
;
1205 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1206 SMAP_FOR_EACH(node
, &lsp
->options
) {
1207 ds_put_format(&ctx
->output
, "%s=%s\n", node
->key
, node
->value
);
1212 nbctl_lsp_set_dhcpv4_options(struct ctl_context
*ctx
)
1214 const char *id
= ctx
->argv
[1];
1215 const struct nbrec_logical_switch_port
*lsp
;
1217 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1218 const struct nbrec_dhcp_options
*dhcp_opt
= NULL
;
1219 if (ctx
->argc
== 3 ) {
1220 dhcp_opt
= dhcp_options_get(ctx
, ctx
->argv
[2], true);
1226 char *error
= ip_parse_cidr(dhcp_opt
->cidr
, &ip
, &plen
);
1229 ctl_fatal("DHCP options cidr '%s' is not IPv4", dhcp_opt
->cidr
);
1232 nbrec_logical_switch_port_set_dhcpv4_options(lsp
, dhcp_opt
);
1236 nbctl_lsp_get_dhcpv4_options(struct ctl_context
*ctx
)
1238 const char *id
= ctx
->argv
[1];
1239 const struct nbrec_logical_switch_port
*lsp
;
1241 lsp
= lsp_by_name_or_uuid(ctx
, id
, true);
1242 if (lsp
->dhcpv4_options
) {
1243 ds_put_format(&ctx
->output
, UUID_FMT
" (%s)\n",
1244 UUID_ARGS(&lsp
->dhcpv4_options
->header_
.uuid
),
1245 lsp
->dhcpv4_options
->cidr
);
1255 dir_encode(const char *dir
)
1257 if (!strcmp(dir
, "from-lport")) {
1258 return DIR_FROM_LPORT
;
1259 } else if (!strcmp(dir
, "to-lport")) {
1260 return DIR_TO_LPORT
;
1267 acl_cmp(const void *acl1_
, const void *acl2_
)
1269 const struct nbrec_acl
*const *acl1p
= acl1_
;
1270 const struct nbrec_acl
*const *acl2p
= acl2_
;
1271 const struct nbrec_acl
*acl1
= *acl1p
;
1272 const struct nbrec_acl
*acl2
= *acl2p
;
1274 int dir1
= dir_encode(acl1
->direction
);
1275 int dir2
= dir_encode(acl2
->direction
);
1278 return dir1
< dir2
? -1 : 1;
1279 } else if (acl1
->priority
!= acl2
->priority
) {
1280 return acl1
->priority
> acl2
->priority
? -1 : 1;
1282 return strcmp(acl1
->match
, acl2
->match
);
1287 nbctl_acl_list(struct ctl_context
*ctx
)
1289 const struct nbrec_logical_switch
*ls
;
1290 const struct nbrec_acl
**acls
;
1293 ls
= ls_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1295 acls
= xmalloc(sizeof *acls
* ls
->n_acls
);
1296 for (i
= 0; i
< ls
->n_acls
; i
++) {
1297 acls
[i
] = ls
->acls
[i
];
1300 qsort(acls
, ls
->n_acls
, sizeof *acls
, acl_cmp
);
1302 for (i
= 0; i
< ls
->n_acls
; i
++) {
1303 const struct nbrec_acl
*acl
= acls
[i
];
1304 ds_put_format(&ctx
->output
, "%10s %5"PRId64
" (%s) %s%s\n",
1305 acl
->direction
, acl
->priority
,
1306 acl
->match
, acl
->action
, acl
->log
? " log" : "");
1313 parse_direction(const char *arg
)
1315 /* Validate direction. Only require the first letter. */
1316 if (arg
[0] == 't') {
1318 } else if (arg
[0] == 'f') {
1319 return "from-lport";
1321 ctl_fatal("%s: direction must be \"to-lport\" or \"from-lport\"", arg
);
1326 parse_priority(const char *arg
)
1328 /* Validate priority. */
1330 if (!ovs_scan(arg
, "%"SCNd64
, &priority
)
1331 || priority
< 0 || priority
> 32767) {
1332 ctl_fatal("%s: priority must in range 0...32767", arg
);
1338 nbctl_acl_add(struct ctl_context
*ctx
)
1340 const struct nbrec_logical_switch
*ls
;
1341 const char *action
= ctx
->argv
[5];
1343 ls
= ls_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1345 const char *direction
= parse_direction(ctx
->argv
[2]);
1346 int64_t priority
= parse_priority(ctx
->argv
[3]);
1348 /* Validate action. */
1349 if (strcmp(action
, "allow") && strcmp(action
, "allow-related")
1350 && strcmp(action
, "drop") && strcmp(action
, "reject")) {
1351 ctl_fatal("%s: action must be one of \"allow\", \"allow-related\", "
1352 "\"drop\", and \"reject\"", action
);
1356 /* Create the acl. */
1357 struct nbrec_acl
*acl
= nbrec_acl_insert(ctx
->txn
);
1358 nbrec_acl_set_priority(acl
, priority
);
1359 nbrec_acl_set_direction(acl
, direction
);
1360 nbrec_acl_set_match(acl
, ctx
->argv
[4]);
1361 nbrec_acl_set_action(acl
, action
);
1362 if (shash_find(&ctx
->options
, "--log") != NULL
) {
1363 nbrec_acl_set_log(acl
, true);
1366 /* Check if same acl already exists for the ls */
1367 for (size_t i
= 0; i
< ls
->n_acls
; i
++) {
1368 if (!acl_cmp(&ls
->acls
[i
], &acl
)) {
1369 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
1371 ctl_fatal("Same ACL already existed on the ls %s.",
1378 /* Insert the acl into the logical switch. */
1379 nbrec_logical_switch_verify_acls(ls
);
1380 struct nbrec_acl
**new_acls
= xmalloc(sizeof *new_acls
* (ls
->n_acls
+ 1));
1381 memcpy(new_acls
, ls
->acls
, sizeof *new_acls
* ls
->n_acls
);
1382 new_acls
[ls
->n_acls
] = acl
;
1383 nbrec_logical_switch_set_acls(ls
, new_acls
, ls
->n_acls
+ 1);
1388 nbctl_acl_del(struct ctl_context
*ctx
)
1390 const struct nbrec_logical_switch
*ls
;
1391 ls
= ls_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1393 if (ctx
->argc
!= 2 && ctx
->argc
!= 3 && ctx
->argc
!= 5) {
1394 ctl_fatal("cannot specify priority without match");
1397 if (ctx
->argc
== 2) {
1398 /* If direction, priority, and match are not specified, delete
1400 nbrec_logical_switch_verify_acls(ls
);
1401 nbrec_logical_switch_set_acls(ls
, NULL
, 0);
1405 const char *direction
= parse_direction(ctx
->argv
[2]);
1407 /* If priority and match are not specified, delete all ACLs with the
1408 * specified direction. */
1409 if (ctx
->argc
== 3) {
1410 struct nbrec_acl
**new_acls
= xmalloc(sizeof *new_acls
* ls
->n_acls
);
1413 for (size_t i
= 0; i
< ls
->n_acls
; i
++) {
1414 if (strcmp(direction
, ls
->acls
[i
]->direction
)) {
1415 new_acls
[n_acls
++] = ls
->acls
[i
];
1419 nbrec_logical_switch_verify_acls(ls
);
1420 nbrec_logical_switch_set_acls(ls
, new_acls
, n_acls
);
1425 int64_t priority
= parse_priority(ctx
->argv
[3]);
1427 /* Remove the matching rule. */
1428 for (size_t i
= 0; i
< ls
->n_acls
; i
++) {
1429 struct nbrec_acl
*acl
= ls
->acls
[i
];
1431 if (priority
== acl
->priority
&& !strcmp(ctx
->argv
[4], acl
->match
) &&
1432 !strcmp(direction
, acl
->direction
)) {
1433 struct nbrec_acl
**new_acls
1434 = xmemdup(ls
->acls
, sizeof *new_acls
* ls
->n_acls
);
1435 new_acls
[i
] = ls
->acls
[ls
->n_acls
- 1];
1436 nbrec_logical_switch_verify_acls(ls
);
1437 nbrec_logical_switch_set_acls(ls
, new_acls
,
1446 nbctl_lb_add(struct ctl_context
*ctx
)
1448 const char *lb_name
= ctx
->argv
[1];
1449 const char *lb_vip
= ctx
->argv
[2];
1450 char *lb_ips
= ctx
->argv
[3];
1452 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
1453 bool add_duplicate
= shash_find(&ctx
->options
, "--add-duplicate") != NULL
;
1455 const char *lb_proto
;
1456 bool is_update_proto
= false;
1457 bool is_vip_with_port
= true;
1459 if (ctx
->argc
== 4) {
1460 /* Default protocol. */
1463 /* Validate protocol. */
1464 lb_proto
= ctx
->argv
[4];
1465 is_update_proto
= true;
1466 if (strcmp(lb_proto
, "tcp") && strcmp(lb_proto
, "udp")) {
1467 ctl_fatal("%s: protocol must be one of \"tcp\", \"udp\".",
1474 char *error
= ip_parse_port(lb_vip
, &ipv4
, &port
);
1477 if (!ip_parse(lb_vip
, &ipv4
)) {
1478 ctl_fatal("%s: should be an IPv4 address (or an IPv4 address "
1479 "and a port number with : as a separator).", lb_vip
);
1482 if (is_update_proto
) {
1483 ctl_fatal("Protocol is unnecessary when no port of vip "
1486 is_vip_with_port
= false;
1489 char *token
= NULL
, *save_ptr
= NULL
;
1490 struct ds lb_ips_new
= DS_EMPTY_INITIALIZER
;
1491 for (token
= strtok_r(lb_ips
, ",", &save_ptr
);
1492 token
!= NULL
; token
= strtok_r(NULL
, ",", &save_ptr
)) {
1493 if (is_vip_with_port
) {
1494 error
= ip_parse_port(token
, &ipv4
, &port
);
1497 ds_destroy(&lb_ips_new
);
1498 ctl_fatal("%s: should be an IPv4 address and a port "
1499 "number with : as a separator.", token
);
1502 if (!ip_parse(token
, &ipv4
)) {
1503 ds_destroy(&lb_ips_new
);
1504 ctl_fatal("%s: should be an IPv4 address.", token
);
1507 ds_put_format(&lb_ips_new
, "%s%s",
1508 lb_ips_new
.length
? "," : "", token
);
1511 const struct nbrec_load_balancer
*lb
= NULL
;
1512 if (!add_duplicate
) {
1513 lb
= lb_by_name_or_uuid(ctx
, lb_name
, false);
1515 if (smap_get(&lb
->vips
, lb_vip
)) {
1517 ds_destroy(&lb_ips_new
);
1518 ctl_fatal("%s: a load balancer with this vip (%s) "
1519 "already exists", lb_name
, lb_vip
);
1521 /* Update the vips. */
1522 smap_replace(CONST_CAST(struct smap
*, &lb
->vips
),
1523 lb_vip
, ds_cstr(&lb_ips_new
));
1525 /* Add the new vips. */
1526 smap_add(CONST_CAST(struct smap
*, &lb
->vips
),
1527 lb_vip
, ds_cstr(&lb_ips_new
));
1530 /* Update the load balancer. */
1531 if (is_update_proto
) {
1532 nbrec_load_balancer_verify_protocol(lb
);
1533 nbrec_load_balancer_set_protocol(lb
, lb_proto
);
1535 nbrec_load_balancer_verify_vips(lb
);
1536 nbrec_load_balancer_set_vips(lb
, &lb
->vips
);
1537 ds_destroy(&lb_ips_new
);
1542 /* Create the load balancer. */
1543 lb
= nbrec_load_balancer_insert(ctx
->txn
);
1544 nbrec_load_balancer_set_name(lb
, lb_name
);
1545 nbrec_load_balancer_set_protocol(lb
, lb_proto
);
1546 smap_add(CONST_CAST(struct smap
*, &lb
->vips
),
1547 lb_vip
, ds_cstr(&lb_ips_new
));
1548 nbrec_load_balancer_set_vips(lb
, &lb
->vips
);
1549 ds_destroy(&lb_ips_new
);
1553 nbctl_lb_del(struct ctl_context
*ctx
)
1555 const char *id
= ctx
->argv
[1];
1556 const struct nbrec_load_balancer
*lb
= NULL
;
1557 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
1559 lb
= lb_by_name_or_uuid(ctx
, id
, false);
1564 if (ctx
->argc
== 3) {
1565 const char *lb_vip
= ctx
->argv
[2];
1566 if (smap_get(&lb
->vips
, lb_vip
)) {
1567 smap_remove(CONST_CAST(struct smap
*, &lb
->vips
), lb_vip
);
1568 if (smap_is_empty(&lb
->vips
)) {
1569 nbrec_load_balancer_delete(lb
);
1573 /* Delete the vip of the load balancer. */
1574 nbrec_load_balancer_verify_vips(lb
);
1575 nbrec_load_balancer_set_vips(lb
, &lb
->vips
);
1579 ctl_fatal("vip %s is not part of the load balancer.",
1584 nbrec_load_balancer_delete(lb
);
1588 lb_info_add_smap(const struct nbrec_load_balancer
*lb
,
1591 struct ds key
= DS_EMPTY_INITIALIZER
;
1592 struct ds val
= DS_EMPTY_INITIALIZER
;
1593 char *error
, *protocol
;
1597 const struct smap_node
**nodes
= smap_sort(&lb
->vips
);
1599 for (int i
= 0; i
< smap_count(&lb
->vips
); i
++) {
1600 const struct smap_node
*node
= nodes
[i
];
1601 protocol
= lb
->protocol
;
1602 error
= ip_parse_port(node
->key
, &ipv4
, &port
);
1605 protocol
= "tcp/udp";
1608 i
== 0 ? ds_put_format(&val
,
1609 UUID_FMT
" %-20.16s%-11.7s%-25.21s%s",
1610 UUID_ARGS(&lb
->header_
.uuid
),
1612 node
->key
, node
->value
)
1613 : ds_put_format(&val
, "\n%60s%-11.7s%-25.21s%s",
1615 node
->key
, node
->value
);
1618 ds_put_format(&key
, "%-20.16s", lb
->name
);
1619 smap_add(lbs
, ds_cstr(&key
), ds_cstr(&val
));
1628 lb_info_print(struct ctl_context
*ctx
, struct smap
*lbs
)
1630 const struct smap_node
**nodes
= smap_sort(lbs
);
1632 ds_put_format(&ctx
->output
, "%-40.36s%-20.16s%-11.7s%-25.21s%s\n",
1633 "UUID", "LB", "PROTO", "VIP", "IPs");
1634 for (size_t i
= 0; i
< smap_count(lbs
); i
++) {
1635 const struct smap_node
*node
= nodes
[i
];
1636 ds_put_format(&ctx
->output
, "%s\n", node
->value
);
1644 lb_info_list_all(struct ctl_context
*ctx
,
1645 const char *lb_name
, bool lb_check
)
1647 const struct nbrec_load_balancer
*lb
;
1648 struct smap lbs
= SMAP_INITIALIZER(&lbs
);
1650 NBREC_LOAD_BALANCER_FOR_EACH(lb
, ctx
->idl
) {
1651 if (lb_check
&& strcmp(lb
->name
, lb_name
)) {
1654 lb_info_add_smap(lb
, &lbs
);
1657 lb_info_print(ctx
, &lbs
);
1662 nbctl_lb_list(struct ctl_context
*ctx
)
1664 if (ctx
->argc
== 1) {
1665 lb_info_list_all(ctx
, NULL
, false);
1666 } else if (ctx
->argc
== 2) {
1667 lb_info_list_all(ctx
, ctx
->argv
[1], true);
1672 nbctl_lr_lb_add(struct ctl_context
*ctx
)
1674 const struct nbrec_logical_router
*lr
;
1675 const struct nbrec_load_balancer
*new_lb
;
1677 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1678 new_lb
= lb_by_name_or_uuid(ctx
, ctx
->argv
[2], true);
1680 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
1681 for (int i
= 0; i
< lr
->n_load_balancer
; i
++) {
1682 const struct nbrec_load_balancer
*lb
1683 = lr
->load_balancer
[i
];
1685 if (uuid_equals(&new_lb
->header_
.uuid
, &lb
->header_
.uuid
)) {
1689 ctl_fatal(UUID_FMT
" : a load balancer with this UUID already "
1690 "exists", UUID_ARGS(&lb
->header_
.uuid
));
1694 /* Insert the load balancer into the logical router. */
1695 nbrec_logical_router_verify_load_balancer(lr
);
1696 struct nbrec_load_balancer
**new_lbs
1697 = xmalloc(sizeof *new_lbs
* (lr
->n_load_balancer
+ 1));
1699 memcpy(new_lbs
, lr
->load_balancer
, sizeof *new_lbs
* lr
->n_load_balancer
);
1700 new_lbs
[lr
->n_load_balancer
] = CONST_CAST(struct nbrec_load_balancer
*,
1702 nbrec_logical_router_set_load_balancer(lr
, new_lbs
,
1703 lr
->n_load_balancer
+ 1);
1708 nbctl_lr_lb_del(struct ctl_context
*ctx
)
1710 const struct nbrec_logical_router
*lr
;
1711 const struct nbrec_load_balancer
*del_lb
;
1712 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1714 if (ctx
->argc
== 2) {
1715 /* If load-balancer is not specified, remove
1716 * all load-balancers from the logical router. */
1717 nbrec_logical_router_verify_load_balancer(lr
);
1718 nbrec_logical_router_set_load_balancer(lr
, NULL
, 0);
1722 del_lb
= lb_by_name_or_uuid(ctx
, ctx
->argv
[2], true);
1723 for (size_t i
= 0; i
< lr
->n_load_balancer
; i
++) {
1724 const struct nbrec_load_balancer
*lb
1725 = lr
->load_balancer
[i
];
1727 if (uuid_equals(&del_lb
->header_
.uuid
, &lb
->header_
.uuid
)) {
1728 /* Remove the matching rule. */
1729 nbrec_logical_router_verify_load_balancer(lr
);
1731 struct nbrec_load_balancer
**new_lbs
1732 = xmemdup(lr
->load_balancer
,
1733 sizeof *new_lbs
* lr
->n_load_balancer
);
1734 new_lbs
[i
] = lr
->load_balancer
[lr
->n_load_balancer
- 1];
1735 nbrec_logical_router_set_load_balancer(lr
, new_lbs
,
1736 lr
->n_load_balancer
- 1);
1742 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
1744 ctl_fatal("load balancer %s is not part of any logical router.",
1750 nbctl_lr_lb_list(struct ctl_context
*ctx
)
1752 const char *lr_name
= ctx
->argv
[1];
1753 const struct nbrec_logical_router
*lr
;
1754 struct smap lbs
= SMAP_INITIALIZER(&lbs
);
1756 lr
= lr_by_name_or_uuid(ctx
, lr_name
, true);
1757 for (int i
= 0; i
< lr
->n_load_balancer
; i
++) {
1758 const struct nbrec_load_balancer
*lb
1759 = lr
->load_balancer
[i
];
1760 lb_info_add_smap(lb
, &lbs
);
1763 lb_info_print(ctx
, &lbs
);
1768 nbctl_ls_lb_add(struct ctl_context
*ctx
)
1770 const struct nbrec_logical_switch
*ls
;
1771 const struct nbrec_load_balancer
*new_lb
;
1773 ls
= ls_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1774 new_lb
= lb_by_name_or_uuid(ctx
, ctx
->argv
[2], true);
1776 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
1777 for (int i
= 0; i
< ls
->n_load_balancer
; i
++) {
1778 const struct nbrec_load_balancer
*lb
1779 = ls
->load_balancer
[i
];
1781 if (uuid_equals(&new_lb
->header_
.uuid
, &lb
->header_
.uuid
)) {
1785 ctl_fatal(UUID_FMT
" : a load balancer with this UUID already "
1786 "exists", UUID_ARGS(&lb
->header_
.uuid
));
1790 /* Insert the load balancer into the logical switch. */
1791 nbrec_logical_switch_verify_load_balancer(ls
);
1792 struct nbrec_load_balancer
**new_lbs
1793 = xmalloc(sizeof *new_lbs
* (ls
->n_load_balancer
+ 1));
1795 memcpy(new_lbs
, ls
->load_balancer
, sizeof *new_lbs
* ls
->n_load_balancer
);
1796 new_lbs
[ls
->n_load_balancer
] = CONST_CAST(struct nbrec_load_balancer
*,
1798 nbrec_logical_switch_set_load_balancer(ls
, new_lbs
,
1799 ls
->n_load_balancer
+ 1);
1804 nbctl_ls_lb_del(struct ctl_context
*ctx
)
1806 const struct nbrec_logical_switch
*ls
;
1807 const struct nbrec_load_balancer
*del_lb
;
1808 ls
= ls_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
1810 if (ctx
->argc
== 2) {
1811 /* If load-balancer is not specified, remove
1812 * all load-balancers from the logical switch. */
1813 nbrec_logical_switch_verify_load_balancer(ls
);
1814 nbrec_logical_switch_set_load_balancer(ls
, NULL
, 0);
1818 del_lb
= lb_by_name_or_uuid(ctx
, ctx
->argv
[2], true);
1819 for (size_t i
= 0; i
< ls
->n_load_balancer
; i
++) {
1820 const struct nbrec_load_balancer
*lb
1821 = ls
->load_balancer
[i
];
1823 if (uuid_equals(&del_lb
->header_
.uuid
, &lb
->header_
.uuid
)) {
1824 /* Remove the matching rule. */
1825 nbrec_logical_switch_verify_load_balancer(ls
);
1827 struct nbrec_load_balancer
**new_lbs
1828 = xmemdup(ls
->load_balancer
,
1829 sizeof *new_lbs
* ls
->n_load_balancer
);
1830 new_lbs
[i
] = ls
->load_balancer
[ls
->n_load_balancer
- 1];
1831 nbrec_logical_switch_set_load_balancer(ls
, new_lbs
,
1832 ls
->n_load_balancer
- 1);
1838 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
1840 ctl_fatal("load balancer %s is not part of any logical switch.",
1846 nbctl_ls_lb_list(struct ctl_context
*ctx
)
1848 const char *ls_name
= ctx
->argv
[1];
1849 const struct nbrec_logical_switch
*ls
;
1850 struct smap lbs
= SMAP_INITIALIZER(&lbs
);
1852 ls
= ls_by_name_or_uuid(ctx
, ls_name
, true);
1853 for (int i
= 0; i
< ls
->n_load_balancer
; i
++) {
1854 const struct nbrec_load_balancer
*lb
1855 = ls
->load_balancer
[i
];
1856 lb_info_add_smap(lb
, &lbs
);
1859 lb_info_print(ctx
, &lbs
);
1864 nbctl_lr_add(struct ctl_context
*ctx
)
1866 const char *lr_name
= ctx
->argc
== 2 ? ctx
->argv
[1] : NULL
;
1868 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
1869 bool add_duplicate
= shash_find(&ctx
->options
, "--add-duplicate") != NULL
;
1870 if (may_exist
&& add_duplicate
) {
1871 ctl_fatal("--may-exist and --add-duplicate may not be used together");
1875 if (!add_duplicate
) {
1876 const struct nbrec_logical_router
*lr
;
1877 NBREC_LOGICAL_ROUTER_FOR_EACH (lr
, ctx
->idl
) {
1878 if (!strcmp(lr
->name
, lr_name
)) {
1882 ctl_fatal("%s: a router with this name already exists",
1887 } else if (may_exist
) {
1888 ctl_fatal("--may-exist requires specifying a name");
1889 } else if (add_duplicate
) {
1890 ctl_fatal("--add-duplicate requires specifying a name");
1893 struct nbrec_logical_router
*lr
;
1894 lr
= nbrec_logical_router_insert(ctx
->txn
);
1896 nbrec_logical_router_set_name(lr
, lr_name
);
1901 nbctl_lr_del(struct ctl_context
*ctx
)
1903 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
1904 const char *id
= ctx
->argv
[1];
1905 const struct nbrec_logical_router
*lr
;
1907 lr
= lr_by_name_or_uuid(ctx
, id
, must_exist
);
1912 nbrec_logical_router_delete(lr
);
1916 nbctl_lr_list(struct ctl_context
*ctx
)
1918 const struct nbrec_logical_router
*lr
;
1922 NBREC_LOGICAL_ROUTER_FOR_EACH(lr
, ctx
->idl
) {
1923 smap_add_format(&lrs
, lr
->name
, UUID_FMT
" (%s)",
1924 UUID_ARGS(&lr
->header_
.uuid
), lr
->name
);
1926 const struct smap_node
**nodes
= smap_sort(&lrs
);
1927 for (size_t i
= 0; i
< smap_count(&lrs
); i
++) {
1928 const struct smap_node
*node
= nodes
[i
];
1929 ds_put_format(&ctx
->output
, "%s\n", node
->value
);
1935 static const struct nbrec_dhcp_options
*
1936 dhcp_options_get(struct ctl_context
*ctx
, const char *id
, bool must_exist
)
1938 struct uuid dhcp_opts_uuid
;
1939 const struct nbrec_dhcp_options
*dhcp_opts
= NULL
;
1940 if (uuid_from_string(&dhcp_opts_uuid
, id
)) {
1941 dhcp_opts
= nbrec_dhcp_options_get_for_uuid(ctx
->idl
, &dhcp_opts_uuid
);
1944 if (!dhcp_opts
&& must_exist
) {
1945 ctl_fatal("%s: dhcp options UUID not found", id
);
1951 nbctl_dhcp_options_create(struct ctl_context
*ctx
)
1953 /* Validate the cidr */
1956 char *error
= ip_parse_cidr(ctx
->argv
[1], &ip
, &plen
);
1958 /* check if its IPv6 cidr */
1960 struct in6_addr ipv6
;
1961 error
= ipv6_parse_cidr(ctx
->argv
[1], &ipv6
, &plen
);
1964 ctl_fatal("Invalid cidr format '%s'", ctx
->argv
[1]);
1968 struct nbrec_dhcp_options
*dhcp_opts
= nbrec_dhcp_options_insert(ctx
->txn
);
1969 nbrec_dhcp_options_set_cidr(dhcp_opts
, ctx
->argv
[1]);
1971 struct smap ext_ids
= SMAP_INITIALIZER(&ext_ids
);
1972 for (size_t i
= 2; i
< ctx
->argc
; i
++) {
1974 value
= xstrdup(ctx
->argv
[i
]);
1975 key
= strsep(&value
, "=");
1977 smap_add(&ext_ids
, key
, value
);
1982 nbrec_dhcp_options_set_external_ids(dhcp_opts
, &ext_ids
);
1983 smap_destroy(&ext_ids
);
1987 nbctl_dhcp_options_set_options(struct ctl_context
*ctx
)
1989 const struct nbrec_dhcp_options
*dhcp_opts
= dhcp_options_get(
1990 ctx
, ctx
->argv
[1], true);
1992 struct smap dhcp_options
= SMAP_INITIALIZER(&dhcp_options
);
1993 for (size_t i
= 2; i
< ctx
->argc
; i
++) {
1995 value
= xstrdup(ctx
->argv
[i
]);
1996 key
= strsep(&value
, "=");
1998 smap_add(&dhcp_options
, key
, value
);
2003 nbrec_dhcp_options_set_options(dhcp_opts
, &dhcp_options
);
2004 smap_destroy(&dhcp_options
);
2008 nbctl_dhcp_options_get_options(struct ctl_context
*ctx
)
2010 const struct nbrec_dhcp_options
*dhcp_opts
= dhcp_options_get(
2011 ctx
, ctx
->argv
[1], true);
2013 struct smap_node
*node
;
2014 SMAP_FOR_EACH(node
, &dhcp_opts
->options
) {
2015 ds_put_format(&ctx
->output
, "%s=%s\n", node
->key
, node
->value
);
2020 nbctl_dhcp_options_del(struct ctl_context
*ctx
)
2022 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
2023 const char *id
= ctx
->argv
[1];
2024 const struct nbrec_dhcp_options
*dhcp_opts
;
2026 dhcp_opts
= dhcp_options_get(ctx
, id
, must_exist
);
2031 nbrec_dhcp_options_delete(dhcp_opts
);
2035 nbctl_dhcp_options_list(struct ctl_context
*ctx
)
2037 const struct nbrec_dhcp_options
*dhcp_opts
;
2038 struct smap dhcp_options
;
2040 smap_init(&dhcp_options
);
2041 NBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opts
, ctx
->idl
) {
2042 smap_add_format(&dhcp_options
, dhcp_opts
->cidr
, UUID_FMT
,
2043 UUID_ARGS(&dhcp_opts
->header_
.uuid
));
2045 const struct smap_node
**nodes
= smap_sort(&dhcp_options
);
2046 for (size_t i
= 0; i
< smap_count(&dhcp_options
); i
++) {
2047 const struct smap_node
*node
= nodes
[i
];
2048 ds_put_format(&ctx
->output
, "%s\n", node
->value
);
2050 smap_destroy(&dhcp_options
);
2054 /* The caller must free the returned string. */
2056 normalize_ipv4_prefix(ovs_be32 ipv4
, unsigned int plen
)
2058 ovs_be32 network
= ipv4
& be32_prefix_mask(plen
);
2060 return xasprintf(IP_FMT
, IP_ARGS(network
));
2062 return xasprintf(IP_FMT
"/%d", IP_ARGS(network
), plen
);
2066 /* The caller must free the returned string. */
2068 normalize_ipv6_prefix(struct in6_addr ipv6
, unsigned int plen
)
2070 char network_s
[INET6_ADDRSTRLEN
];
2072 struct in6_addr mask
= ipv6_create_mask(plen
);
2073 struct in6_addr network
= ipv6_addr_bitand(&ipv6
, &mask
);
2075 inet_ntop(AF_INET6
, &network
, network_s
, INET6_ADDRSTRLEN
);
2077 return xasprintf("%s", network_s
);
2079 return xasprintf("%s/%d", network_s
, plen
);
2083 /* The caller must free the returned string. */
2085 normalize_prefix_str(const char *orig_prefix
)
2091 error
= ip_parse_cidr(orig_prefix
, &ipv4
, &plen
);
2093 return normalize_ipv4_prefix(ipv4
, plen
);
2095 struct in6_addr ipv6
;
2098 error
= ipv6_parse_cidr(orig_prefix
, &ipv6
, &plen
);
2103 return normalize_ipv6_prefix(ipv6
, plen
);
2108 nbctl_lr_route_add(struct ctl_context
*ctx
)
2110 const struct nbrec_logical_router
*lr
;
2111 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
2112 char *prefix
, *next_hop
;
2114 const char *policy
= shash_find_data(&ctx
->options
, "--policy");
2115 if (policy
&& strcmp(policy
, "src-ip") && strcmp(policy
, "dst-ip")) {
2116 ctl_fatal("bad policy: %s", policy
);
2119 prefix
= normalize_prefix_str(ctx
->argv
[2]);
2121 ctl_fatal("bad prefix argument: %s", ctx
->argv
[2]);
2124 next_hop
= normalize_prefix_str(ctx
->argv
[3]);
2127 ctl_fatal("bad next hop argument: %s", ctx
->argv
[3]);
2130 if (strchr(prefix
, '.')) {
2132 if (!ip_parse(ctx
->argv
[3], &hop_ipv4
)) {
2135 ctl_fatal("bad IPv4 nexthop argument: %s", ctx
->argv
[3]);
2138 struct in6_addr hop_ipv6
;
2139 if (!ipv6_parse(ctx
->argv
[3], &hop_ipv6
)) {
2142 ctl_fatal("bad IPv6 nexthop argument: %s", ctx
->argv
[3]);
2146 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
2147 for (int i
= 0; i
< lr
->n_static_routes
; i
++) {
2148 const struct nbrec_logical_router_static_route
*route
2149 = lr
->static_routes
[i
];
2152 rt_prefix
= normalize_prefix_str(lr
->static_routes
[i
]->ip_prefix
);
2154 /* Ignore existing prefix we couldn't parse. */
2158 if (strcmp(rt_prefix
, prefix
)) {
2166 ctl_fatal("duplicate prefix: %s", prefix
);
2169 /* Update the next hop for an existing route. */
2170 nbrec_logical_router_verify_static_routes(lr
);
2171 nbrec_logical_router_static_route_verify_ip_prefix(route
);
2172 nbrec_logical_router_static_route_verify_nexthop(route
);
2173 nbrec_logical_router_static_route_set_ip_prefix(route
, prefix
);
2174 nbrec_logical_router_static_route_set_nexthop(route
, next_hop
);
2175 if (ctx
->argc
== 5) {
2176 nbrec_logical_router_static_route_set_output_port(route
,
2180 nbrec_logical_router_static_route_set_policy(route
, policy
);
2188 struct nbrec_logical_router_static_route
*route
;
2189 route
= nbrec_logical_router_static_route_insert(ctx
->txn
);
2190 nbrec_logical_router_static_route_set_ip_prefix(route
, prefix
);
2191 nbrec_logical_router_static_route_set_nexthop(route
, next_hop
);
2192 if (ctx
->argc
== 5) {
2193 nbrec_logical_router_static_route_set_output_port(route
, ctx
->argv
[4]);
2196 nbrec_logical_router_static_route_set_policy(route
, policy
);
2199 nbrec_logical_router_verify_static_routes(lr
);
2200 struct nbrec_logical_router_static_route
**new_routes
2201 = xmalloc(sizeof *new_routes
* (lr
->n_static_routes
+ 1));
2202 memcpy(new_routes
, lr
->static_routes
,
2203 sizeof *new_routes
* lr
->n_static_routes
);
2204 new_routes
[lr
->n_static_routes
] = route
;
2205 nbrec_logical_router_set_static_routes(lr
, new_routes
,
2206 lr
->n_static_routes
+ 1);
2213 nbctl_lr_route_del(struct ctl_context
*ctx
)
2215 const struct nbrec_logical_router
*lr
;
2216 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
2218 if (ctx
->argc
== 2) {
2219 /* If a prefix is not specified, delete all routes. */
2220 nbrec_logical_router_set_static_routes(lr
, NULL
, 0);
2224 char *prefix
= normalize_prefix_str(ctx
->argv
[2]);
2226 ctl_fatal("bad prefix argument: %s", ctx
->argv
[2]);
2229 for (int i
= 0; i
< lr
->n_static_routes
; i
++) {
2230 char *rt_prefix
= normalize_prefix_str(lr
->static_routes
[i
]->ip_prefix
);
2232 /* Ignore existing prefix we couldn't parse. */
2236 if (!strcmp(prefix
, rt_prefix
)) {
2237 struct nbrec_logical_router_static_route
**new_routes
2238 = xmemdup(lr
->static_routes
,
2239 sizeof *new_routes
* lr
->n_static_routes
);
2241 new_routes
[i
] = lr
->static_routes
[lr
->n_static_routes
- 1];
2242 nbrec_logical_router_verify_static_routes(lr
);
2243 nbrec_logical_router_set_static_routes(lr
, new_routes
,
2244 lr
->n_static_routes
- 1);
2253 if (!shash_find(&ctx
->options
, "--if-exists")) {
2254 ctl_fatal("no matching prefix: %s", prefix
);
2260 nbctl_lr_nat_add(struct ctl_context
*ctx
)
2262 const struct nbrec_logical_router
*lr
;
2263 const char *nat_type
= ctx
->argv
[2];
2264 const char *external_ip
= ctx
->argv
[3];
2265 const char *logical_ip
= ctx
->argv
[4];
2266 char *new_logical_ip
= NULL
;
2268 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
2270 if (strcmp(nat_type
, "dnat") && strcmp(nat_type
, "snat")
2271 && strcmp(nat_type
, "dnat_and_snat")) {
2272 ctl_fatal("%s: type must be one of \"dnat\", \"snat\" and "
2273 "\"dnat_and_snat\".", nat_type
);
2278 if (!ip_parse(external_ip
, &ipv4
)) {
2279 ctl_fatal("%s: should be an IPv4 address.", external_ip
);
2282 if (strcmp("snat", nat_type
)) {
2283 if (!ip_parse(logical_ip
, &ipv4
)) {
2284 ctl_fatal("%s: should be an IPv4 address.", logical_ip
);
2286 new_logical_ip
= xstrdup(logical_ip
);
2288 char *error
= ip_parse_cidr(logical_ip
, &ipv4
, &plen
);
2291 ctl_fatal("%s: should be an IPv4 address or network.",
2294 new_logical_ip
= normalize_ipv4_prefix(ipv4
, plen
);
2297 const char *logical_port
;
2298 const char *external_mac
;
2299 if (ctx
->argc
== 6) {
2300 ctl_fatal("lr-nat-add with logical_port "
2301 "must also specify external_mac.");
2302 } else if (ctx
->argc
== 7) {
2303 if (strcmp(nat_type
, "dnat_and_snat")) {
2304 ctl_fatal("logical_port and external_mac are only valid when "
2305 "type is \"dnat_and_snat\".");
2308 logical_port
= ctx
->argv
[5];
2309 lsp_by_name_or_uuid(ctx
, logical_port
, true);
2311 external_mac
= ctx
->argv
[6];
2313 if (!eth_addr_from_string(external_mac
, &ea
)) {
2314 ctl_fatal("invalid mac address %s.", external_mac
);
2317 logical_port
= NULL
;
2318 external_mac
= NULL
;
2321 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
2322 int is_snat
= !strcmp("snat", nat_type
);
2323 for (size_t i
= 0; i
< lr
->n_nat
; i
++) {
2324 const struct nbrec_nat
*nat
= lr
->nat
[i
];
2325 if (!strcmp(nat_type
, nat
->type
)) {
2326 if (!strcmp(is_snat
? new_logical_ip
: external_ip
,
2327 is_snat
? nat
->logical_ip
: nat
->external_ip
)) {
2328 if (!strcmp(is_snat
? external_ip
: new_logical_ip
,
2329 is_snat
? nat
->external_ip
: nat
->logical_ip
)) {
2331 nbrec_nat_verify_logical_port(nat
);
2332 nbrec_nat_verify_external_mac(nat
);
2333 nbrec_nat_set_logical_port(nat
, logical_port
);
2334 nbrec_nat_set_external_mac(nat
, external_mac
);
2335 free(new_logical_ip
);
2338 ctl_fatal("%s, %s: a NAT with this external_ip and "
2339 "logical_ip already exists",
2340 external_ip
, new_logical_ip
);
2342 ctl_fatal("a NAT with this type (%s) and %s (%s) "
2345 is_snat
? "logical_ip" : "external_ip",
2346 is_snat
? new_logical_ip
: external_ip
);
2352 /* Create the NAT. */
2353 struct nbrec_nat
*nat
= nbrec_nat_insert(ctx
->txn
);
2354 nbrec_nat_set_type(nat
, nat_type
);
2355 nbrec_nat_set_external_ip(nat
, external_ip
);
2356 nbrec_nat_set_logical_ip(nat
, new_logical_ip
);
2357 if (logical_port
&& external_mac
) {
2358 nbrec_nat_set_logical_port(nat
, logical_port
);
2359 nbrec_nat_set_external_mac(nat
, external_mac
);
2361 free(new_logical_ip
);
2363 /* Insert the NAT into the logical router. */
2364 nbrec_logical_router_verify_nat(lr
);
2365 struct nbrec_nat
**new_nats
= xmalloc(sizeof *new_nats
* (lr
->n_nat
+ 1));
2366 memcpy(new_nats
, lr
->nat
, sizeof *new_nats
* lr
->n_nat
);
2367 new_nats
[lr
->n_nat
] = nat
;
2368 nbrec_logical_router_set_nat(lr
, new_nats
, lr
->n_nat
+ 1);
2373 nbctl_lr_nat_del(struct ctl_context
*ctx
)
2375 const struct nbrec_logical_router
*lr
;
2376 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
2377 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
2379 if (ctx
->argc
== 2) {
2380 /* If type, external_ip and logical_ip are not specified, delete
2382 nbrec_logical_router_verify_nat(lr
);
2383 nbrec_logical_router_set_nat(lr
, NULL
, 0);
2387 const char *nat_type
= ctx
->argv
[2];
2388 if (strcmp(nat_type
, "dnat") && strcmp(nat_type
, "snat")
2389 && strcmp(nat_type
, "dnat_and_snat")) {
2390 ctl_fatal("%s: type must be one of \"dnat\", \"snat\" and "
2391 "\"dnat_and_snat\".", nat_type
);
2394 if (ctx
->argc
== 3) {
2395 /*Deletes all NATs with the specified type. */
2396 struct nbrec_nat
**new_nats
= xmalloc(sizeof *new_nats
* lr
->n_nat
);
2398 for (size_t i
= 0; i
< lr
->n_nat
; i
++) {
2399 if (strcmp(nat_type
, lr
->nat
[i
]->type
)) {
2400 new_nats
[n_nat
++] = lr
->nat
[i
];
2404 nbrec_logical_router_verify_nat(lr
);
2405 nbrec_logical_router_set_nat(lr
, new_nats
, n_nat
);
2410 const char *nat_ip
= ctx
->argv
[3];
2411 int is_snat
= !strcmp("snat", nat_type
);
2412 /* Remove the matching NAT. */
2413 for (size_t i
= 0; i
< lr
->n_nat
; i
++) {
2414 struct nbrec_nat
*nat
= lr
->nat
[i
];
2415 if (!strcmp(nat_type
, nat
->type
) &&
2416 !strcmp(nat_ip
, is_snat
? nat
->logical_ip
: nat
->external_ip
)) {
2417 struct nbrec_nat
**new_nats
2418 = xmemdup(lr
->nat
, sizeof *new_nats
* lr
->n_nat
);
2419 new_nats
[i
] = lr
->nat
[lr
->n_nat
- 1];
2420 nbrec_logical_router_verify_nat(lr
);
2421 nbrec_logical_router_set_nat(lr
, new_nats
,
2429 ctl_fatal("no matching NAT with the type (%s) and %s (%s)",
2430 nat_type
, is_snat
? "logical_ip" : "external_ip", nat_ip
);
2435 nbctl_lr_nat_list(struct ctl_context
*ctx
)
2437 const struct nbrec_logical_router
*lr
;
2438 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
2440 struct smap lr_nats
= SMAP_INITIALIZER(&lr_nats
);
2441 for (size_t i
= 0; i
< lr
->n_nat
; i
++) {
2442 const struct nbrec_nat
*nat
= lr
->nat
[i
];
2443 char *key
= xasprintf("%-17.13s%s", nat
->type
, nat
->external_ip
);
2444 if (nat
->external_mac
&& nat
->logical_port
) {
2445 smap_add_format(&lr_nats
, key
, "%-22.18s%-21.17s%s",
2446 nat
->logical_ip
, nat
->external_mac
,
2449 smap_add_format(&lr_nats
, key
, "%s", nat
->logical_ip
);
2454 const struct smap_node
**nodes
= smap_sort(&lr_nats
);
2456 ds_put_format(&ctx
->output
, "%-17.13s%-19.15s%-22.18s%-21.17s%s\n",
2457 "TYPE", "EXTERNAL_IP", "LOGICAL_IP", "EXTERNAL_MAC",
2459 for (size_t i
= 0; i
< smap_count(&lr_nats
); i
++) {
2460 const struct smap_node
*node
= nodes
[i
];
2461 ds_put_format(&ctx
->output
, "%-36.32s%s\n",
2462 node
->key
, node
->value
);
2466 smap_destroy(&lr_nats
);
2470 static const struct nbrec_logical_router_port
*
2471 lrp_by_name_or_uuid(struct ctl_context
*ctx
, const char *id
, bool must_exist
)
2473 const struct nbrec_logical_router_port
*lrp
= NULL
;
2475 struct uuid lrp_uuid
;
2476 bool is_uuid
= uuid_from_string(&lrp_uuid
, id
);
2478 lrp
= nbrec_logical_router_port_get_for_uuid(ctx
->idl
, &lrp_uuid
);
2482 NBREC_LOGICAL_ROUTER_PORT_FOR_EACH(lrp
, ctx
->idl
) {
2483 if (!strcmp(lrp
->name
, id
)) {
2489 if (!lrp
&& must_exist
) {
2490 ctl_fatal("%s: port %s not found", id
, is_uuid
? "UUID" : "name");
2496 /* Returns the logical router that contains 'lrp'. */
2497 static const struct nbrec_logical_router
*
2498 lrp_to_lr(const struct ovsdb_idl
*idl
,
2499 const struct nbrec_logical_router_port
*lrp
)
2501 const struct nbrec_logical_router
*lr
;
2502 NBREC_LOGICAL_ROUTER_FOR_EACH (lr
, idl
) {
2503 for (size_t i
= 0; i
< lr
->n_ports
; i
++) {
2504 if (lr
->ports
[i
] == lrp
) {
2510 /* Can't happen because of the database schema */
2511 ctl_fatal("port %s is not part of any logical router",
2516 lr_get_name(const struct nbrec_logical_router
*lr
, char uuid_s
[UUID_LEN
+ 1],
2522 snprintf(uuid_s
, uuid_s_size
, UUID_FMT
, UUID_ARGS(&lr
->header_
.uuid
));
2527 nbctl_lrp_add(struct ctl_context
*ctx
)
2529 bool may_exist
= shash_find(&ctx
->options
, "--may-exist") != NULL
;
2531 const struct nbrec_logical_router
*lr
;
2532 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
2534 const char *lrp_name
= ctx
->argv
[2];
2535 const char *mac
= ctx
->argv
[3];
2536 const char **networks
= (const char **) &ctx
->argv
[4];
2538 int n_networks
= ctx
->argc
- 4;
2539 for (int i
= 4; i
< ctx
->argc
; i
++) {
2540 if (strchr(ctx
->argv
[i
], '=')) {
2547 ctl_fatal("%s: router port requires specifying a network", lrp_name
);
2550 char **settings
= (char **) &ctx
->argv
[n_networks
+ 4];
2551 int n_settings
= ctx
->argc
- 4 - n_networks
;
2553 const struct nbrec_logical_router_port
*lrp
;
2554 lrp
= lrp_by_name_or_uuid(ctx
, lrp_name
, false);
2557 ctl_fatal("%s: a port with this name already exists",
2561 const struct nbrec_logical_router
*bound_lr
;
2562 bound_lr
= lrp_to_lr(ctx
->idl
, lrp
);
2563 if (bound_lr
!= lr
) {
2564 char uuid_s
[UUID_LEN
+ 1];
2565 ctl_fatal("%s: port already exists but in router %s", lrp_name
,
2566 lr_get_name(bound_lr
, uuid_s
, sizeof uuid_s
));
2569 if (strcmp(mac
, lrp
->mac
)) {
2570 ctl_fatal("%s: port already exists with mac %s", lrp_name
,
2574 struct sset new_networks
= SSET_INITIALIZER(&new_networks
);
2575 for (int i
= 0; i
< n_networks
; i
++) {
2576 sset_add(&new_networks
, networks
[i
]);
2579 struct sset orig_networks
= SSET_INITIALIZER(&orig_networks
);
2580 sset_add_array(&orig_networks
, lrp
->networks
, lrp
->n_networks
);
2582 bool same_networks
= sset_equals(&orig_networks
, &new_networks
);
2583 sset_destroy(&orig_networks
);
2584 sset_destroy(&new_networks
);
2585 if (!same_networks
) {
2586 ctl_fatal("%s: port already exists with different network",
2590 /* Special-case sanity-check of peer ports. */
2591 const char *peer
= NULL
;
2592 for (int i
= 0; i
< n_settings
; i
++) {
2593 if (!strncmp(settings
[i
], "peer=", 5)) {
2594 peer
= settings
[i
] + 5;
2599 if ((!peer
!= !lrp
->peer
) ||
2600 (lrp
->peer
&& strcmp(peer
, lrp
->peer
))) {
2601 ctl_fatal("%s: port already exists with mismatching peer",
2609 if (!eth_addr_from_string(mac
, &ea
)) {
2610 ctl_fatal("%s: invalid mac address %s", lrp_name
, mac
);
2613 for (int i
= 0; i
< n_networks
; i
++) {
2616 char *error
= ip_parse_cidr(networks
[i
], &ipv4
, &plen
);
2619 struct in6_addr ipv6
;
2620 error
= ipv6_parse_cidr(networks
[i
], &ipv6
, &plen
);
2623 ctl_fatal("%s: invalid network address: %s", lrp_name
,
2629 /* Create the logical port. */
2630 lrp
= nbrec_logical_router_port_insert(ctx
->txn
);
2631 nbrec_logical_router_port_set_name(lrp
, lrp_name
);
2632 nbrec_logical_router_port_set_mac(lrp
, mac
);
2633 nbrec_logical_router_port_set_networks(lrp
, networks
, n_networks
);
2635 for (int i
= 0; i
< n_settings
; i
++) {
2636 ctl_set_column("Logical_Router_Port", &lrp
->header_
, settings
[i
],
2640 /* Insert the logical port into the logical router. */
2641 nbrec_logical_router_verify_ports(lr
);
2642 struct nbrec_logical_router_port
**new_ports
= xmalloc(sizeof *new_ports
*
2644 memcpy(new_ports
, lr
->ports
, sizeof *new_ports
* lr
->n_ports
);
2645 new_ports
[lr
->n_ports
] = CONST_CAST(struct nbrec_logical_router_port
*,
2647 nbrec_logical_router_set_ports(lr
, new_ports
, lr
->n_ports
+ 1);
2651 /* Removes logical router port 'lr->ports[idx]'. */
2653 remove_lrp(const struct nbrec_logical_router
*lr
, size_t idx
)
2655 const struct nbrec_logical_router_port
*lrp
= lr
->ports
[idx
];
2657 /* First remove 'lrp' from the array of ports. This is what will
2658 * actually cause the logical port to be deleted when the transaction is
2659 * sent to the database server (due to garbage collection). */
2660 struct nbrec_logical_router_port
**new_ports
2661 = xmemdup(lr
->ports
, sizeof *new_ports
* lr
->n_ports
);
2662 new_ports
[idx
] = new_ports
[lr
->n_ports
- 1];
2663 nbrec_logical_router_verify_ports(lr
);
2664 nbrec_logical_router_set_ports(lr
, new_ports
, lr
->n_ports
- 1);
2667 /* Delete 'lrp' from the IDL. This won't have a real effect on
2668 * the database server (the IDL will suppress it in fact) but it
2669 * means that it won't show up when we iterate with
2670 * NBREC_LOGICAL_ROUTER_PORT_FOR_EACH later. */
2671 nbrec_logical_router_port_delete(lrp
);
2675 nbctl_lrp_del(struct ctl_context
*ctx
)
2677 bool must_exist
= !shash_find(&ctx
->options
, "--if-exists");
2678 const struct nbrec_logical_router_port
*lrp
;
2680 lrp
= lrp_by_name_or_uuid(ctx
, ctx
->argv
[1], must_exist
);
2685 /* Find the router that contains 'lrp', then delete it. */
2686 const struct nbrec_logical_router
*lr
;
2687 NBREC_LOGICAL_ROUTER_FOR_EACH (lr
, ctx
->idl
) {
2688 for (size_t i
= 0; i
< lr
->n_ports
; i
++) {
2689 if (lr
->ports
[i
] == lrp
) {
2696 /* Can't happen because of the database schema. */
2697 ctl_fatal("logical port %s is not part of any logical router",
2701 /* Print a list of logical router ports. */
2703 nbctl_lrp_list(struct ctl_context
*ctx
)
2705 const char *id
= ctx
->argv
[1];
2706 const struct nbrec_logical_router
*lr
;
2710 lr
= lr_by_name_or_uuid(ctx
, id
, true);
2713 for (i
= 0; i
< lr
->n_ports
; i
++) {
2714 const struct nbrec_logical_router_port
*lrp
= lr
->ports
[i
];
2715 smap_add_format(&lrps
, lrp
->name
, UUID_FMT
" (%s)",
2716 UUID_ARGS(&lrp
->header_
.uuid
), lrp
->name
);
2718 const struct smap_node
**nodes
= smap_sort(&lrps
);
2719 for (i
= 0; i
< smap_count(&lrps
); i
++) {
2720 const struct smap_node
*node
= nodes
[i
];
2721 ds_put_format(&ctx
->output
, "%s\n", node
->value
);
2723 smap_destroy(&lrps
);
2727 /* Set the logical router port admin-enabled state. */
2729 nbctl_lrp_set_enabled(struct ctl_context
*ctx
)
2731 const char *id
= ctx
->argv
[1];
2732 const char *state
= ctx
->argv
[2];
2733 const struct nbrec_logical_router_port
*lrp
;
2735 lrp
= lrp_by_name_or_uuid(ctx
, id
, true);
2740 bool enabled
= parse_enabled(state
);
2741 nbrec_logical_router_port_set_enabled(lrp
, &enabled
, 1);
2744 /* Print admin-enabled state for logical router port. */
2746 nbctl_lrp_get_enabled(struct ctl_context
*ctx
)
2748 const char *id
= ctx
->argv
[1];
2749 const struct nbrec_logical_router_port
*lrp
;
2751 lrp
= lrp_by_name_or_uuid(ctx
, id
, true);
2756 ds_put_format(&ctx
->output
, "%s\n",
2758 *lrp
->enabled
? "enabled" : "disabled");
2764 const struct nbrec_logical_router_static_route
*route
;
2768 ipv4_route_cmp(const void *route1_
, const void *route2_
)
2770 const struct ipv4_route
*route1p
= route1_
;
2771 const struct ipv4_route
*route2p
= route2_
;
2773 if (route1p
->priority
!= route2p
->priority
) {
2774 return route1p
->priority
> route2p
->priority
? -1 : 1;
2775 } else if (route1p
->addr
!= route2p
->addr
) {
2776 return ntohl(route1p
->addr
) < ntohl(route2p
->addr
) ? -1 : 1;
2784 struct in6_addr addr
;
2785 const struct nbrec_logical_router_static_route
*route
;
2789 ipv6_route_cmp(const void *route1_
, const void *route2_
)
2791 const struct ipv6_route
*route1p
= route1_
;
2792 const struct ipv6_route
*route2p
= route2_
;
2794 if (route1p
->priority
!= route2p
->priority
) {
2795 return route1p
->priority
> route2p
->priority
? -1 : 1;
2797 return memcmp(&route1p
->addr
, &route2p
->addr
, sizeof(route1p
->addr
));
2801 print_route(const struct nbrec_logical_router_static_route
*route
, struct ds
*s
)
2804 char *prefix
= normalize_prefix_str(route
->ip_prefix
);
2805 char *next_hop
= normalize_prefix_str(route
->nexthop
);
2806 ds_put_format(s
, "%25s %25s", prefix
, next_hop
);
2810 if (route
->policy
) {
2811 ds_put_format(s
, " %s", route
->policy
);
2813 ds_put_format(s
, " %s", "dst-ip");
2816 if (route
->output_port
) {
2817 ds_put_format(s
, " %s", route
->output_port
);
2819 ds_put_char(s
, '\n');
2823 nbctl_lr_route_list(struct ctl_context
*ctx
)
2825 const struct nbrec_logical_router
*lr
;
2826 struct ipv4_route
*ipv4_routes
;
2827 struct ipv6_route
*ipv6_routes
;
2828 size_t n_ipv4_routes
= 0;
2829 size_t n_ipv6_routes
= 0;
2831 lr
= lr_by_name_or_uuid(ctx
, ctx
->argv
[1], true);
2833 ipv4_routes
= xmalloc(sizeof *ipv4_routes
* lr
->n_static_routes
);
2834 ipv6_routes
= xmalloc(sizeof *ipv6_routes
* lr
->n_static_routes
);
2836 for (int i
= 0; i
< lr
->n_static_routes
; i
++) {
2837 const struct nbrec_logical_router_static_route
*route
2838 = lr
->static_routes
[i
];
2841 const char *policy
= route
->policy
? route
->policy
: "dst-ip";
2843 error
= ip_parse_cidr(route
->ip_prefix
, &ipv4
, &plen
);
2845 ipv4_routes
[n_ipv4_routes
].priority
= !strcmp(policy
, "dst-ip")
2848 ipv4_routes
[n_ipv4_routes
].addr
= ipv4
;
2849 ipv4_routes
[n_ipv4_routes
].route
= route
;
2854 struct in6_addr ipv6
;
2855 error
= ipv6_parse_cidr(route
->ip_prefix
, &ipv6
, &plen
);
2857 ipv6_routes
[n_ipv6_routes
].priority
= !strcmp(policy
, "dst-ip")
2860 ipv6_routes
[n_ipv6_routes
].addr
= ipv6
;
2861 ipv6_routes
[n_ipv6_routes
].route
= route
;
2864 /* Invalid prefix. */
2865 VLOG_WARN("router "UUID_FMT
" (%s) has invalid prefix: %s",
2866 UUID_ARGS(&lr
->header_
.uuid
), lr
->name
,
2874 qsort(ipv4_routes
, n_ipv4_routes
, sizeof *ipv4_routes
, ipv4_route_cmp
);
2875 qsort(ipv6_routes
, n_ipv6_routes
, sizeof *ipv6_routes
, ipv6_route_cmp
);
2877 if (n_ipv4_routes
) {
2878 ds_put_cstr(&ctx
->output
, "IPv4 Routes\n");
2880 for (int i
= 0; i
< n_ipv4_routes
; i
++) {
2881 print_route(ipv4_routes
[i
].route
, &ctx
->output
);
2884 if (n_ipv6_routes
) {
2885 ds_put_format(&ctx
->output
, "%sIPv6 Routes\n",
2886 n_ipv4_routes
? "\n" : "");
2888 for (int i
= 0; i
< n_ipv6_routes
; i
++) {
2889 print_route(ipv6_routes
[i
].route
, &ctx
->output
);
2897 verify_connections(struct ctl_context
*ctx
)
2899 const struct nbrec_nb_global
*nb_global
= nbrec_nb_global_first(ctx
->idl
);
2900 const struct nbrec_connection
*conn
;
2902 nbrec_nb_global_verify_connections(nb_global
);
2904 NBREC_CONNECTION_FOR_EACH(conn
, ctx
->idl
) {
2905 nbrec_connection_verify_target(conn
);
2910 pre_connection(struct ctl_context
*ctx
)
2912 ovsdb_idl_add_column(ctx
->idl
, &nbrec_nb_global_col_connections
);
2913 ovsdb_idl_add_column(ctx
->idl
, &nbrec_connection_col_target
);
2917 cmd_get_connection(struct ctl_context
*ctx
)
2919 const struct nbrec_connection
*conn
;
2920 struct svec targets
;
2923 verify_connections(ctx
);
2925 /* Print the targets in sorted order for reproducibility. */
2926 svec_init(&targets
);
2928 NBREC_CONNECTION_FOR_EACH(conn
, ctx
->idl
) {
2929 svec_add(&targets
, conn
->target
);
2932 svec_sort_unique(&targets
);
2933 for (i
= 0; i
< targets
.n
; i
++) {
2934 ds_put_format(&ctx
->output
, "%s\n", targets
.names
[i
]);
2936 svec_destroy(&targets
);
2940 delete_connections(struct ctl_context
*ctx
)
2942 const struct nbrec_nb_global
*nb_global
= nbrec_nb_global_first(ctx
->idl
);
2943 const struct nbrec_connection
*conn
, *next
;
2945 /* Delete Manager rows pointed to by 'connection_options' column. */
2946 NBREC_CONNECTION_FOR_EACH_SAFE(conn
, next
, ctx
->idl
) {
2947 nbrec_connection_delete(conn
);
2950 /* Delete 'Manager' row refs in 'manager_options' column. */
2951 nbrec_nb_global_set_connections(nb_global
, NULL
, 0);
2955 cmd_del_connection(struct ctl_context
*ctx
)
2957 verify_connections(ctx
);
2958 delete_connections(ctx
);
2962 insert_connections(struct ctl_context
*ctx
, char *targets
[], size_t n
)
2964 const struct nbrec_nb_global
*nb_global
= nbrec_nb_global_first(ctx
->idl
);
2965 struct nbrec_connection
**connections
;
2968 /* Insert each connection in a new row in Connection table. */
2969 connections
= xmalloc(n
* sizeof *connections
);
2970 for (i
= 0; i
< n
; i
++) {
2971 if (stream_verify_name(targets
[i
]) &&
2972 pstream_verify_name(targets
[i
])) {
2973 VLOG_WARN("target type \"%s\" is possibly erroneous", targets
[i
]);
2976 connections
[conns
] = nbrec_connection_insert(ctx
->txn
);
2977 nbrec_connection_set_target(connections
[conns
], targets
[i
]);
2981 /* Store uuids of new connection rows in 'connection' column. */
2982 nbrec_nb_global_set_connections(nb_global
, connections
, conns
);
2987 cmd_set_connection(struct ctl_context
*ctx
)
2989 const size_t n
= ctx
->argc
- 1;
2991 verify_connections(ctx
);
2992 delete_connections(ctx
);
2993 insert_connections(ctx
, &ctx
->argv
[1], n
);
2997 pre_cmd_get_ssl(struct ctl_context
*ctx
)
2999 ovsdb_idl_add_column(ctx
->idl
, &nbrec_nb_global_col_ssl
);
3001 ovsdb_idl_add_column(ctx
->idl
, &nbrec_ssl_col_private_key
);
3002 ovsdb_idl_add_column(ctx
->idl
, &nbrec_ssl_col_certificate
);
3003 ovsdb_idl_add_column(ctx
->idl
, &nbrec_ssl_col_ca_cert
);
3004 ovsdb_idl_add_column(ctx
->idl
, &nbrec_ssl_col_bootstrap_ca_cert
);
3008 cmd_get_ssl(struct ctl_context
*ctx
)
3010 const struct nbrec_nb_global
*nb_global
= nbrec_nb_global_first(ctx
->idl
);
3011 const struct nbrec_ssl
*ssl
= nbrec_ssl_first(ctx
->idl
);
3013 nbrec_nb_global_verify_ssl(nb_global
);
3015 nbrec_ssl_verify_private_key(ssl
);
3016 nbrec_ssl_verify_certificate(ssl
);
3017 nbrec_ssl_verify_ca_cert(ssl
);
3018 nbrec_ssl_verify_bootstrap_ca_cert(ssl
);
3020 ds_put_format(&ctx
->output
, "Private key: %s\n", ssl
->private_key
);
3021 ds_put_format(&ctx
->output
, "Certificate: %s\n", ssl
->certificate
);
3022 ds_put_format(&ctx
->output
, "CA Certificate: %s\n", ssl
->ca_cert
);
3023 ds_put_format(&ctx
->output
, "Bootstrap: %s\n",
3024 ssl
->bootstrap_ca_cert
? "true" : "false");
3029 pre_cmd_del_ssl(struct ctl_context
*ctx
)
3031 ovsdb_idl_add_column(ctx
->idl
, &nbrec_nb_global_col_ssl
);
3035 cmd_del_ssl(struct ctl_context
*ctx
)
3037 const struct nbrec_nb_global
*nb_global
= nbrec_nb_global_first(ctx
->idl
);
3038 const struct nbrec_ssl
*ssl
= nbrec_ssl_first(ctx
->idl
);
3041 nbrec_nb_global_verify_ssl(nb_global
);
3042 nbrec_ssl_delete(ssl
);
3043 nbrec_nb_global_set_ssl(nb_global
, NULL
);
3048 pre_cmd_set_ssl(struct ctl_context
*ctx
)
3050 ovsdb_idl_add_column(ctx
->idl
, &nbrec_nb_global_col_ssl
);
3054 cmd_set_ssl(struct ctl_context
*ctx
)
3056 bool bootstrap
= shash_find(&ctx
->options
, "--bootstrap");
3057 const struct nbrec_nb_global
*nb_global
= nbrec_nb_global_first(ctx
->idl
);
3058 const struct nbrec_ssl
*ssl
= nbrec_ssl_first(ctx
->idl
);
3060 nbrec_nb_global_verify_ssl(nb_global
);
3062 nbrec_ssl_delete(ssl
);
3064 ssl
= nbrec_ssl_insert(ctx
->txn
);
3066 nbrec_ssl_set_private_key(ssl
, ctx
->argv
[1]);
3067 nbrec_ssl_set_certificate(ssl
, ctx
->argv
[2]);
3068 nbrec_ssl_set_ca_cert(ssl
, ctx
->argv
[3]);
3070 nbrec_ssl_set_bootstrap_ca_cert(ssl
, bootstrap
);
3072 nbrec_nb_global_set_ssl(nb_global
, ssl
);
3075 static const struct ctl_table_class tables
[NBREC_N_TABLES
] = {
3076 [NBREC_TABLE_DHCP_OPTIONS
].row_ids
3077 = {{&nbrec_logical_switch_port_col_name
, NULL
,
3078 &nbrec_logical_switch_port_col_dhcpv4_options
},
3079 {&nbrec_logical_switch_port_col_external_ids
,
3080 "neutron:port_name", &nbrec_logical_switch_port_col_dhcpv4_options
},
3081 {&nbrec_logical_switch_port_col_name
, NULL
,
3082 &nbrec_logical_switch_port_col_dhcpv6_options
},
3083 {&nbrec_logical_switch_port_col_external_ids
,
3084 "neutron:port_name", &nbrec_logical_switch_port_col_dhcpv6_options
}},
3086 [NBREC_TABLE_LOGICAL_SWITCH
].row_ids
3087 = {{&nbrec_logical_switch_col_name
, NULL
, NULL
},
3088 {&nbrec_logical_switch_col_external_ids
, "neutron:network_name", NULL
}},
3090 [NBREC_TABLE_LOGICAL_SWITCH_PORT
].row_ids
3091 = {{&nbrec_logical_switch_port_col_name
, NULL
, NULL
},
3092 {&nbrec_logical_switch_port_col_external_ids
,
3093 "neutron:port_name", NULL
}},
3095 [NBREC_TABLE_LOGICAL_ROUTER
].row_ids
3096 = {{&nbrec_logical_router_col_name
, NULL
, NULL
},
3097 {&nbrec_logical_router_col_external_ids
, "neutron:router_name", NULL
}},
3099 [NBREC_TABLE_LOGICAL_ROUTER_PORT
].row_ids
[0]
3100 = {&nbrec_logical_router_port_col_name
, NULL
, NULL
},
3102 [NBREC_TABLE_ADDRESS_SET
].row_ids
[0]
3103 = {&nbrec_address_set_col_name
, NULL
, NULL
},
3107 run_prerequisites(struct ctl_command
*commands
, size_t n_commands
,
3108 struct ovsdb_idl
*idl
)
3110 ovsdb_idl_add_table(idl
, &nbrec_table_nb_global
);
3111 if (wait_type
== NBCTL_WAIT_SB
) {
3112 ovsdb_idl_add_column(idl
, &nbrec_nb_global_col_sb_cfg
);
3113 } else if (wait_type
== NBCTL_WAIT_HV
) {
3114 ovsdb_idl_add_column(idl
, &nbrec_nb_global_col_hv_cfg
);
3117 for (struct ctl_command
*c
= commands
; c
< &commands
[n_commands
]; c
++) {
3118 if (c
->syntax
->prerequisites
) {
3119 struct ctl_context ctx
;
3121 ds_init(&c
->output
);
3124 ctl_context_init(&ctx
, c
, idl
, NULL
, NULL
, NULL
);
3125 (c
->syntax
->prerequisites
)(&ctx
);
3126 ctl_context_done(&ctx
, c
);
3128 ovs_assert(!c
->output
.string
);
3129 ovs_assert(!c
->table
);
3135 do_nbctl(const char *args
, struct ctl_command
*commands
, size_t n_commands
,
3136 struct ovsdb_idl
*idl
)
3138 struct ovsdb_idl_txn
*txn
;
3139 enum ovsdb_idl_txn_status status
;
3140 struct ovsdb_symbol_table
*symtab
;
3141 struct ctl_context ctx
;
3142 struct ctl_command
*c
;
3143 struct shash_node
*node
;
3144 int64_t next_cfg
= 0;
3147 txn
= the_idl_txn
= ovsdb_idl_txn_create(idl
);
3149 ovsdb_idl_txn_set_dry_run(txn
);
3152 ovsdb_idl_txn_add_comment(txn
, "ovs-nbctl: %s", args
);
3154 const struct nbrec_nb_global
*nb
= nbrec_nb_global_first(idl
);
3156 /* XXX add verification that table is empty */
3157 nb
= nbrec_nb_global_insert(txn
);
3160 if (wait_type
!= NBCTL_WAIT_NONE
) {
3161 ovsdb_idl_txn_increment(txn
, &nb
->header_
, &nbrec_nb_global_col_nb_cfg
,
3165 symtab
= ovsdb_symbol_table_create();
3166 for (c
= commands
; c
< &commands
[n_commands
]; c
++) {
3167 ds_init(&c
->output
);
3170 ctl_context_init(&ctx
, NULL
, idl
, txn
, symtab
, NULL
);
3171 for (c
= commands
; c
< &commands
[n_commands
]; c
++) {
3172 ctl_context_init_command(&ctx
, c
);
3173 if (c
->syntax
->run
) {
3174 (c
->syntax
->run
)(&ctx
);
3176 ctl_context_done_command(&ctx
, c
);
3178 if (ctx
.try_again
) {
3179 ctl_context_done(&ctx
, NULL
);
3183 ctl_context_done(&ctx
, NULL
);
3185 SHASH_FOR_EACH (node
, &symtab
->sh
) {
3186 struct ovsdb_symbol
*symbol
= node
->data
;
3187 if (!symbol
->created
) {
3188 ctl_fatal("row id \"%s\" is referenced but never created (e.g. "
3189 "with \"-- --id=%s create ...\")",
3190 node
->name
, node
->name
);
3192 if (!symbol
->strong_ref
) {
3193 if (!symbol
->weak_ref
) {
3194 VLOG_WARN("row id \"%s\" was created but no reference to it "
3195 "was inserted, so it will not actually appear in "
3196 "the database", node
->name
);
3198 VLOG_WARN("row id \"%s\" was created but only a weak "
3199 "reference to it was inserted, so it will not "
3200 "actually appear in the database", node
->name
);
3205 status
= ovsdb_idl_txn_commit_block(txn
);
3206 if (wait_type
!= NBCTL_WAIT_NONE
&& status
== TXN_SUCCESS
) {
3207 next_cfg
= ovsdb_idl_txn_get_increment_new_value(txn
);
3209 if (status
== TXN_UNCHANGED
|| status
== TXN_SUCCESS
) {
3210 for (c
= commands
; c
< &commands
[n_commands
]; c
++) {
3211 if (c
->syntax
->postprocess
) {
3212 ctl_context_init(&ctx
, c
, idl
, txn
, symtab
, NULL
);
3213 (c
->syntax
->postprocess
)(&ctx
);
3214 ctl_context_done(&ctx
, c
);
3218 error
= xstrdup(ovsdb_idl_txn_get_error(txn
));
3221 case TXN_UNCOMMITTED
:
3222 case TXN_INCOMPLETE
:
3226 /* Should not happen--we never call ovsdb_idl_txn_abort(). */
3227 ctl_fatal("transaction aborted");
3237 ctl_fatal("transaction error: %s", error
);
3239 case TXN_NOT_LOCKED
:
3240 /* Should not happen--we never call ovsdb_idl_set_lock(). */
3241 ctl_fatal("database not locked");
3248 ovsdb_symbol_table_destroy(symtab
);
3250 for (c
= commands
; c
< &commands
[n_commands
]; c
++) {
3251 struct ds
*ds
= &c
->output
;
3254 table_print(c
->table
, &table_style
);
3255 } else if (oneline
) {
3259 for (j
= 0; j
< ds
->length
; j
++) {
3260 int ch
= ds
->string
[j
];
3263 fputs("\\n", stdout
);
3267 fputs("\\\\", stdout
);
3276 fputs(ds_cstr(ds
), stdout
);
3278 ds_destroy(&c
->output
);
3279 table_destroy(c
->table
);
3282 shash_destroy_free_data(&c
->options
);
3286 if (wait_type
!= NBCTL_WAIT_NONE
&& status
!= TXN_UNCHANGED
) {
3287 ovsdb_idl_enable_reconnect(idl
);
3290 NBREC_NB_GLOBAL_FOR_EACH (nb
, idl
) {
3291 int64_t cur_cfg
= (wait_type
== NBCTL_WAIT_SB
3294 if (cur_cfg
>= next_cfg
) {
3298 ovsdb_idl_wait(idl
);
3304 ovsdb_idl_txn_destroy(txn
);
3305 ovsdb_idl_destroy(idl
);
3310 /* Our transaction needs to be rerun, or a prerequisite was not met. Free
3311 * resources and return so that the caller can try again. */
3312 ovsdb_idl_txn_abort(txn
);
3313 ovsdb_idl_txn_destroy(txn
);
3316 ovsdb_symbol_table_destroy(symtab
);
3317 for (c
= commands
; c
< &commands
[n_commands
]; c
++) {
3318 ds_destroy(&c
->output
);
3319 table_destroy(c
->table
);
3326 /* Frees the current transaction and the underlying IDL and then calls
3329 * Freeing the transaction and the IDL is not strictly necessary, but it makes
3330 * for a clean memory leak report from valgrind in the normal case. That makes
3331 * it easier to notice real memory leaks. */
3333 nbctl_exit(int status
)
3336 ovsdb_idl_txn_abort(the_idl_txn
);
3337 ovsdb_idl_txn_destroy(the_idl_txn
);
3339 ovsdb_idl_destroy(the_idl
);
3343 static const struct ctl_command_syntax nbctl_commands
[] = {
3344 { "init", 0, 0, "", NULL
, nbctl_init
, NULL
, "", RW
},
3345 { "sync", 0, 0, "", nbctl_pre_sync
, nbctl_sync
, NULL
, "", RO
},
3346 { "show", 0, 1, "[SWITCH]", NULL
, nbctl_show
, NULL
, "", RO
},
3348 /* logical switch commands. */
3349 { "ls-add", 0, 1, "[SWITCH]", NULL
, nbctl_ls_add
, NULL
,
3350 "--may-exist,--add-duplicate", RW
},
3351 { "ls-del", 1, 1, "SWITCH", NULL
, nbctl_ls_del
, NULL
, "--if-exists", RW
},
3352 { "ls-list", 0, 0, "", NULL
, nbctl_ls_list
, NULL
, "", RO
},
3355 { "acl-add", 5, 5, "SWITCH DIRECTION PRIORITY MATCH ACTION", NULL
,
3356 nbctl_acl_add
, NULL
, "--log,--may-exist", RW
},
3357 { "acl-del", 1, 4, "SWITCH [DIRECTION [PRIORITY MATCH]]", NULL
,
3358 nbctl_acl_del
, NULL
, "", RW
},
3359 { "acl-list", 1, 1, "SWITCH", NULL
, nbctl_acl_list
, NULL
, "", RO
},
3361 /* logical switch port commands. */
3362 { "lsp-add", 2, 4, "SWITCH PORT [PARENT] [TAG]", NULL
, nbctl_lsp_add
,
3363 NULL
, "--may-exist", RW
},
3364 { "lsp-del", 1, 1, "PORT", NULL
, nbctl_lsp_del
, NULL
, "--if-exists", RW
},
3365 { "lsp-list", 1, 1, "SWITCH", NULL
, nbctl_lsp_list
, NULL
, "", RO
},
3366 { "lsp-get-parent", 1, 1, "PORT", NULL
, nbctl_lsp_get_parent
, NULL
,
3368 { "lsp-get-tag", 1, 1, "PORT", NULL
, nbctl_lsp_get_tag
, NULL
, "", RO
},
3369 { "lsp-set-addresses", 1, INT_MAX
, "PORT [ADDRESS]...", NULL
,
3370 nbctl_lsp_set_addresses
, NULL
, "", RW
},
3371 { "lsp-get-addresses", 1, 1, "PORT", NULL
, nbctl_lsp_get_addresses
, NULL
,
3373 { "lsp-set-port-security", 0, INT_MAX
, "PORT [ADDRS]...", NULL
,
3374 nbctl_lsp_set_port_security
, NULL
, "", RW
},
3375 { "lsp-get-port-security", 1, 1, "PORT", NULL
,
3376 nbctl_lsp_get_port_security
, NULL
, "", RO
},
3377 { "lsp-get-up", 1, 1, "PORT", NULL
, nbctl_lsp_get_up
, NULL
, "", RO
},
3378 { "lsp-set-enabled", 2, 2, "PORT STATE", NULL
, nbctl_lsp_set_enabled
,
3380 { "lsp-get-enabled", 1, 1, "PORT", NULL
, nbctl_lsp_get_enabled
, NULL
,
3382 { "lsp-set-type", 2, 2, "PORT TYPE", NULL
, nbctl_lsp_set_type
, NULL
,
3384 { "lsp-get-type", 1, 1, "PORT", NULL
, nbctl_lsp_get_type
, NULL
, "", RO
},
3385 { "lsp-set-options", 1, INT_MAX
, "PORT KEY=VALUE [KEY=VALUE]...", NULL
,
3386 nbctl_lsp_set_options
, NULL
, "", RW
},
3387 { "lsp-get-options", 1, 1, "PORT", NULL
, nbctl_lsp_get_options
, NULL
,
3389 { "lsp-set-dhcpv4-options", 1, 2, "PORT [DHCP_OPT_UUID]", NULL
,
3390 nbctl_lsp_set_dhcpv4_options
, NULL
, "", RW
},
3391 { "lsp-get-dhcpv4-options", 1, 1, "PORT", NULL
,
3392 nbctl_lsp_get_dhcpv4_options
, NULL
, "", RO
},
3394 /* logical router commands. */
3395 { "lr-add", 0, 1, "[ROUTER]", NULL
, nbctl_lr_add
, NULL
,
3396 "--may-exist,--add-duplicate", RW
},
3397 { "lr-del", 1, 1, "ROUTER", NULL
, nbctl_lr_del
, NULL
, "--if-exists", RW
},
3398 { "lr-list", 0, 0, "", NULL
, nbctl_lr_list
, NULL
, "", RO
},
3400 /* logical router port commands. */
3401 { "lrp-add", 4, INT_MAX
,
3402 "ROUTER PORT MAC NETWORK... [COLUMN[:KEY]=VALUE]...",
3403 NULL
, nbctl_lrp_add
, NULL
, "--may-exist", RW
},
3404 { "lrp-del", 1, 1, "PORT", NULL
, nbctl_lrp_del
, NULL
, "--if-exists", RW
},
3405 { "lrp-list", 1, 1, "ROUTER", NULL
, nbctl_lrp_list
, NULL
, "", RO
},
3406 { "lrp-set-enabled", 2, 2, "PORT STATE", NULL
, nbctl_lrp_set_enabled
,
3408 { "lrp-get-enabled", 1, 1, "PORT", NULL
, nbctl_lrp_get_enabled
,
3411 /* logical router route commands. */
3412 { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL
,
3413 nbctl_lr_route_add
, NULL
, "--may-exist,--policy=", RW
},
3414 { "lr-route-del", 1, 2, "ROUTER [PREFIX]", NULL
, nbctl_lr_route_del
,
3415 NULL
, "--if-exists", RW
},
3416 { "lr-route-list", 1, 1, "ROUTER", NULL
, nbctl_lr_route_list
, NULL
,
3420 { "lr-nat-add", 4, 6,
3421 "ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]", NULL
,
3422 nbctl_lr_nat_add
, NULL
, "--may-exist", RW
},
3423 { "lr-nat-del", 1, 3, "ROUTER [TYPE [IP]]", NULL
,
3424 nbctl_lr_nat_del
, NULL
, "--if-exists", RW
},
3425 { "lr-nat-list", 1, 1, "ROUTER", NULL
, nbctl_lr_nat_list
, NULL
, "", RO
},
3427 /* load balancer commands. */
3428 { "lb-add", 3, 4, "LB VIP[:PORT] IP[:PORT]... [PROTOCOL]", NULL
,
3429 nbctl_lb_add
, NULL
, "--may-exist,--add-duplicate", RW
},
3430 { "lb-del", 1, 2, "LB [VIP]", NULL
, nbctl_lb_del
, NULL
,
3431 "--if-exists", RW
},
3432 { "lb-list", 0, 1, "[LB]", NULL
, nbctl_lb_list
, NULL
, "", RO
},
3433 { "lr-lb-add", 2, 2, "ROUTER LB", NULL
, nbctl_lr_lb_add
, NULL
,
3434 "--may-exist", RW
},
3435 { "lr-lb-del", 1, 2, "ROUTER [LB]", NULL
, nbctl_lr_lb_del
, NULL
,
3436 "--if-exists", RW
},
3437 { "lr-lb-list", 1, 1, "ROUTER", NULL
, nbctl_lr_lb_list
, NULL
,
3439 { "ls-lb-add", 2, 2, "SWITCH LB", NULL
, nbctl_ls_lb_add
, NULL
,
3440 "--may-exist", RW
},
3441 { "ls-lb-del", 1, 2, "SWITCH [LB]", NULL
, nbctl_ls_lb_del
, NULL
,
3442 "--if-exists", RW
},
3443 { "ls-lb-list", 1, 1, "SWITCH", NULL
, nbctl_ls_lb_list
, NULL
,
3446 /* DHCP_Options commands */
3447 {"dhcp-options-create", 1, INT_MAX
, "CIDR [EXTERNAL:IDS]", NULL
,
3448 nbctl_dhcp_options_create
, NULL
, "", RW
},
3449 {"dhcp-options-del", 1, 1, "DHCP_OPT_UUID", NULL
,
3450 nbctl_dhcp_options_del
, NULL
, "", RW
},
3451 {"dhcp-options-list", 0, 0, "", NULL
, nbctl_dhcp_options_list
, NULL
, "", RO
},
3452 {"dhcp-options-set-options", 1, INT_MAX
, "DHCP_OPT_UUID KEY=VALUE [KEY=VALUE]...",
3453 NULL
, nbctl_dhcp_options_set_options
, NULL
, "", RW
},
3454 {"dhcp-options-get-options", 1, 1, "DHCP_OPT_UUID", NULL
,
3455 nbctl_dhcp_options_get_options
, NULL
, "", RO
},
3457 /* Connection commands. */
3458 {"get-connection", 0, 0, "", pre_connection
, cmd_get_connection
, NULL
, "", RO
},
3459 {"del-connection", 0, 0, "", pre_connection
, cmd_del_connection
, NULL
, "", RW
},
3460 {"set-connection", 1, INT_MAX
, "TARGET...", pre_connection
, cmd_set_connection
,
3464 {"get-ssl", 0, 0, "", pre_cmd_get_ssl
, cmd_get_ssl
, NULL
, "", RO
},
3465 {"del-ssl", 0, 0, "", pre_cmd_del_ssl
, cmd_del_ssl
, NULL
, "", RW
},
3466 {"set-ssl", 3, 3, "PRIVATE-KEY CERTIFICATE CA-CERT", pre_cmd_set_ssl
,
3467 cmd_set_ssl
, NULL
, "--bootstrap", RW
},
3469 {NULL
, 0, 0, NULL
, NULL
, NULL
, NULL
, "", RO
},
3472 /* Registers nbctl and common db commands. */
3474 nbctl_cmd_init(void)
3476 ctl_init(nbrec_table_classes
, tables
, NULL
, nbctl_exit
);
3477 ctl_register_commands(nbctl_commands
);