]> git.proxmox.com Git - mirror_ovs.git/blame - ovn/utilities/ovn-nbctl.c
ovn: Add a case of policy based routing.
[mirror_ovs.git] / ovn / utilities / ovn-nbctl.c
CommitLineData
a416ff28
RB
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at:
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15#include <config.h>
16
17#include <getopt.h>
bf5fa52a 18#include <inttypes.h>
a416ff28
RB
19#include <stdlib.h>
20#include <stdio.h>
21
22#include "command-line.h"
70799ae8 23#include "db-ctl-base.h"
a416ff28
RB
24#include "dirs.h"
25#include "fatal-signal.h"
ee89ea7b 26#include "openvswitch/json.h"
e3df8838 27#include "ovn/lib/ovn-nb-idl.h"
8ad609c0 28#include "ovn/lib/ovn-util.h"
69e73316 29#include "packets.h"
a416ff28 30#include "poll-loop.h"
afc9da0b 31#include "process.h"
2a547e72 32#include "smap.h"
4685e523 33#include "sset.h"
a416ff28
RB
34#include "stream.h"
35#include "stream-ssl.h"
2a547e72 36#include "svec.h"
70799ae8
BP
37#include "table.h"
38#include "timeval.h"
a416ff28
RB
39#include "util.h"
40#include "openvswitch/vlog.h"
41
70799ae8 42VLOG_DEFINE_THIS_MODULE(nbctl);
a416ff28 43
70799ae8
BP
44/* --db: The database server to contact. */
45static const char *db;
46
47/* --oneline: Write each command's output as a single line? */
48static bool oneline;
49
50/* --dry-run: Do not commit any changes. */
51static bool dry_run;
52
fa183acc
BP
53/* --wait=TYPE: Wait for configuration change to take effect? */
54enum 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. */
58};
59static enum nbctl_wait_type wait_type = NBCTL_WAIT_NONE;
60
de32cec7
BP
61/* Should we wait (if specified by 'wait_type') even if the commands don't
62 * change the database at all? */
63static bool force_wait = false;
64
70799ae8
BP
65/* --timeout: Time to wait for a connection to 'db'. */
66static int timeout;
67
68/* Format for table output. */
69static struct table_style table_style = TABLE_STYLE_DEFAULT;
70
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. */
74static struct ovsdb_idl *the_idl;
75static struct ovsdb_idl_txn *the_idl_txn;
76OVS_NO_RETURN static void nbctl_exit(int status);
77
78static void nbctl_cmd_init(void);
79OVS_NO_RETURN static void usage(void);
80static void parse_options(int argc, char *argv[], struct shash *local_options);
70799ae8
BP
81static void run_prerequisites(struct ctl_command[], size_t n_commands,
82 struct ovsdb_idl *);
8ee574c3 83static bool do_nbctl(const char *args, struct ctl_command *, size_t n,
70799ae8 84 struct ovsdb_idl *);
281977f7
NS
85static const struct nbrec_dhcp_options *dhcp_options_get(
86 struct ctl_context *ctx, const char *id, bool must_exist);
70799ae8
BP
87
88int
89main(int argc, char *argv[])
90{
a416ff28 91 struct ovsdb_idl *idl;
70799ae8
BP
92 struct ctl_command *commands;
93 struct shash local_options;
94 unsigned int seqno;
95 size_t n_commands;
96 char *args;
a416ff28 97
70799ae8
BP
98 set_program_name(argv[0]);
99 fatal_ignore_sigpipe();
100 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
45863ce5 101 vlog_set_levels_from_string_assert("reconnect:warn");
70799ae8
BP
102
103 nbctl_cmd_init();
104
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);
109
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,
114 &n_commands);
115
116 if (timeout) {
117 time_alarm(timeout);
118 }
119
120 /* Initialize IDL. */
121 idl = the_idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false);
122 run_prerequisites(commands, n_commands, idl);
123
124 /* Execute the commands.
125 *
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);
132 for (;;) {
133 ovsdb_idl_run(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));
138 }
139
140 if (seqno != ovsdb_idl_get_seqno(idl)) {
141 seqno = ovsdb_idl_get_seqno(idl);
8ee574c3
WT
142 if (do_nbctl(args, commands, n_commands, idl)) {
143 free(args);
144 exit(EXIT_SUCCESS);
145 }
70799ae8 146 }
a416ff28 147
70799ae8
BP
148 if (seqno == ovsdb_idl_get_seqno(idl)) {
149 ovsdb_idl_wait(idl);
150 poll_block();
151 }
152 }
153}
154
70799ae8
BP
155static void
156parse_options(int argc, char *argv[], struct shash *local_options)
157{
158 enum {
159 OPT_DB = UCHAR_MAX + 1,
160 OPT_NO_SYSLOG,
fa183acc
BP
161 OPT_NO_WAIT,
162 OPT_WAIT,
70799ae8
BP
163 OPT_DRY_RUN,
164 OPT_ONELINE,
165 OPT_LOCAL,
166 OPT_COMMANDS,
167 OPT_OPTIONS,
168 VLOG_OPTION_ENUMS,
169 TABLE_OPTION_ENUMS
170 };
171 static const struct option global_long_options[] = {
172 {"db", required_argument, NULL, OPT_DB},
173 {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG},
fa183acc
BP
174 {"no-wait", no_argument, NULL, OPT_NO_WAIT},
175 {"wait", required_argument, NULL, OPT_WAIT},
70799ae8
BP
176 {"dry-run", no_argument, NULL, OPT_DRY_RUN},
177 {"oneline", no_argument, NULL, OPT_ONELINE},
178 {"timeout", required_argument, NULL, 't'},
179 {"help", no_argument, NULL, 'h'},
180 {"commands", no_argument, NULL, OPT_COMMANDS},
181 {"options", no_argument, NULL, OPT_OPTIONS},
182 {"version", no_argument, NULL, 'V'},
183 VLOG_LONG_OPTIONS,
184 STREAM_SSL_LONG_OPTIONS,
185 TABLE_LONG_OPTIONS,
186 {NULL, 0, NULL, 0},
187 };
188 const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
189 char *tmp, *short_options;
190
191 struct option *options;
192 size_t allocated_options;
193 size_t n_options;
194 size_t i;
195
196 tmp = ovs_cmdl_long_options_to_short_options(global_long_options);
197 short_options = xasprintf("+%s", tmp);
198 free(tmp);
199
200 /* We want to parse both global and command-specific options here, but
201 * getopt_long() isn't too convenient for the job. We copy our global
202 * options into a dynamic array, then append all of the command-specific
203 * options. */
204 options = xmemdup(global_long_options, sizeof global_long_options);
205 allocated_options = ARRAY_SIZE(global_long_options);
206 n_options = n_global_long_options;
207 ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL);
208 table_style.format = TF_LIST;
209
210 for (;;) {
211 int idx;
212 int c;
213
214 c = getopt_long(argc, argv, short_options, options, &idx);
215 if (c == -1) {
216 break;
217 }
218
219 switch (c) {
220 case OPT_DB:
221 db = optarg;
222 break;
223
224 case OPT_ONELINE:
225 oneline = true;
226 break;
227
228 case OPT_NO_SYSLOG:
922fed06 229 vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN);
70799ae8
BP
230 break;
231
fa183acc
BP
232 case OPT_NO_WAIT:
233 wait_type = NBCTL_WAIT_NONE;
234 break;
235
236 case OPT_WAIT:
237 if (!strcmp(optarg, "none")) {
238 wait_type = NBCTL_WAIT_NONE;
239 } else if (!strcmp(optarg, "sb")) {
240 wait_type = NBCTL_WAIT_SB;
241 } else if (!strcmp(optarg, "hv")) {
242 wait_type = NBCTL_WAIT_HV;
243 } else {
244 ctl_fatal("argument to --wait must be "
245 "\"none\", \"sb\", or \"hv\"");
246 }
247 break;
248
70799ae8
BP
249 case OPT_DRY_RUN:
250 dry_run = true;
251 break;
252
253 case OPT_LOCAL:
254 if (shash_find(local_options, options[idx].name)) {
255 ctl_fatal("'%s' option specified multiple times",
256 options[idx].name);
257 }
258 shash_add_nocopy(local_options,
259 xasprintf("--%s", options[idx].name),
2225c0b9 260 nullable_xstrdup(optarg));
70799ae8
BP
261 break;
262
263 case 'h':
264 usage();
265 exit(EXIT_SUCCESS);
266
267 case OPT_COMMANDS:
268 ctl_print_commands();
269
270 case OPT_OPTIONS:
271 ctl_print_options(global_long_options);
272
273 case 'V':
274 ovs_print_version(0, 0);
275 printf("DB Schema %s\n", nbrec_get_db_version());
276 exit(EXIT_SUCCESS);
277
278 case 't':
279 timeout = strtoul(optarg, NULL, 10);
280 if (timeout < 0) {
281 ctl_fatal("value %s on -t or --timeout is invalid", optarg);
282 }
283 break;
284
285 VLOG_OPTION_HANDLERS
286 TABLE_OPTION_HANDLERS(&table_style)
287 STREAM_SSL_OPTION_HANDLERS
288
289 case '?':
290 exit(EXIT_FAILURE);
291
292 default:
293 abort();
294 }
295 }
241d9625 296 free(short_options);
70799ae8
BP
297
298 if (!db) {
8ad609c0 299 db = default_nb_db();
70799ae8
BP
300 }
301
302 for (i = n_global_long_options; options[i].name; i++) {
303 free(CONST_CAST(char *, options[i].name));
304 }
305 free(options);
306}
a416ff28
RB
307
308static void
309usage(void)
310{
311 printf("\
312%s: OVN northbound DB management utility\n\
313usage: %s [OPTIONS] COMMAND [ARG...]\n\
314\n\
907a0edf 315General commands:\n\
fa183acc 316 init initialize the database\n\
907a0edf 317 show print overview of database contents\n\
ea46a4e9 318 show SWITCH print overview of database contents for SWITCH\n\
fa2a27b2 319 show ROUTER print overview of database contents for ROUTER\n\
907a0edf 320\n\
a367622a 321Logical switch commands:\n\
ea46a4e9
JP
322 ls-add [SWITCH] create a logical switch named SWITCH\n\
323 ls-del SWITCH delete SWITCH and all its ports\n\
324 ls-list print the names of all logical switches\n\
a416ff28 325\n\
8f96455d 326ACL commands:\n\
ea46a4e9
JP
327 acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\
328 add an ACL to SWITCH\n\
329 acl-del SWITCH [DIRECTION [PRIORITY MATCH]]\n\
330 remove ACLs from SWITCH\n\
331 acl-list SWITCH print ACLs for SWITCH\n\
8f96455d 332\n\
80f408f4 333Logical switch port commands:\n\
ea46a4e9
JP
334 lsp-add SWITCH PORT add logical port PORT on SWITCH\n\
335 lsp-add SWITCH PORT PARENT TAG\n\
336 add logical port PORT on SWITCH with PARENT\n\
bf5fa52a 337 on TAG\n\
31ed1192 338 lsp-del PORT delete PORT from its attached switch\n\
ea46a4e9 339 lsp-list SWITCH print the names of all logical ports on SWITCH\n\
31ed1192
JP
340 lsp-get-parent PORT get the parent of PORT if set\n\
341 lsp-get-tag PORT get the PORT's tag if set\n\
342 lsp-set-addresses PORT [ADDRESS]...\n\
343 set MAC or MAC+IP addresses for PORT.\n\
dd34473c 344 lsp-get-addresses PORT get a list of MAC or MAC+IP addresses on PORT\n\
31ed1192
JP
345 lsp-set-port-security PORT [ADDRS]...\n\
346 set port security addresses for PORT.\n\
347 lsp-get-port-security PORT get PORT's port security addresses\n\
348 lsp-get-up PORT get state of PORT ('up' or 'down')\n\
349 lsp-set-enabled PORT STATE\n\
350 set administrative state PORT\n\
e9e8bcdf 351 ('enabled' or 'disabled')\n\
31ed1192 352 lsp-get-enabled PORT get administrative state PORT\n\
e9e8bcdf 353 ('enabled' or 'disabled')\n\
31ed1192
JP
354 lsp-set-type PORT TYPE set the type for PORT\n\
355 lsp-get-type PORT get the type for PORT\n\
356 lsp-set-options PORT KEY=VALUE [KEY=VALUE]...\n\
357 set options related to the type of PORT\n\
358 lsp-get-options PORT get the type specific options for PORT\n\
281977f7
NS
359 lsp-set-dhcpv4-options PORT [DHCP_OPTIONS_UUID]\n\
360 set dhcpv4 options for PORT\n\
361 lsp-get-dhcpv4-options PORT get the dhcpv4 options for PORT\n\
a416ff28 362\n\
fa2a27b2
JP
363Logical router commands:\n\
364 lr-add [ROUTER] create a logical router named ROUTER\n\
365 lr-del ROUTER delete ROUTER and all its ports\n\
366 lr-list print the names of all logical routers\n\
367\n\
31114af7 368Logical router port commands:\n\
4685e523 369 lrp-add ROUTER PORT MAC NETWORK... [peer=PEER]\n\
31114af7
JP
370 add logical port PORT on ROUTER\n\
371 lrp-del PORT delete PORT from its attached router\n\
372 lrp-list ROUTER print the names of all ports on ROUTER\n\
373 lrp-set-enabled PORT STATE\n\
374 set administrative state PORT\n\
375 ('enabled' or 'disabled')\n\
376 lrp-get-enabled PORT get administrative state PORT\n\
377 ('enabled' or 'disabled')\n\
378\n\
e48ccf3c 379Route commands:\n\
440a9f4b 380 [--policy=POLICY] lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\
e48ccf3c
JP
381 add a route to ROUTER\n\
382 lr-route-del ROUTER [PREFIX]\n\
383 remove routes from ROUTER\n\
384 lr-route-list ROUTER print routes for ROUTER\n\
385\n\
e2bfcad6 386LB commands:\n\
387 lb-add LB VIP[:PORT] IP[:PORT]... [PROTOCOL]\n\
388 create a load-balancer or add a VIP to an\n\
389 existing load balancer\n\
390 lb-del LB [VIP] remove a load-balancer or just the VIP from\n\
391 the load balancer\n\
392 lb-list [LB] print load-balancers\n\
393 lr-lb-add ROUTER LB add a load-balancer to ROUTER\n\
394 lr-lb-del ROUTER [LB] remove load-balancers from ROUTER\n\
395 lr-lb-list ROUTER print load-balancers\n\
396 ls-lb-add SWITCH LB add a load-balancer to SWITCH\n\
397 ls-lb-del SWITCH [LB] remove load-balancers from SWITCH\n\
398 ls-lb-list SWITCH print load-balancers\n\
281977f7
NS
399\n\
400DHCP Options commands:\n\
401 dhcp-options-create CIDR [EXTERNAL_IDS]\n\
402 create a DHCP options row with CIDR\n\
403 dhcp-options-del DHCP_OPTIONS_UUID\n\
404 delete DHCP_OPTIONS_UUID\n\
405 dhcp-options-list \n\
406 lists the DHCP_Options rows\n\
407 dhcp-options-set-options DHCP_OPTIONS_UUID KEY=VALUE [KEY=VALUE]...\n\
b4ec31a9 408 set DHCP options for DHCP_OPTIONS_UUID\n\
281977f7 409 dhcp-options-get-options DHCO_OPTIONS_UUID \n\
b4ec31a9 410 displays the DHCP options for DHCP_OPTIONS_UUID\n\
281977f7 411\n\
7ba0c32f
WL
412%s\
413\n\
de32cec7
BP
414Synchronization command (use with --wait=sb|hv):\n\
415 sync wait even for earlier changes to take effect\n\
416\n\
a416ff28 417Options:\n\
70799ae8
BP
418 --db=DATABASE connect to DATABASE\n\
419 (default: %s)\n\
fa183acc
BP
420 --no-wait, --wait=none do not wait for OVN reconfiguration (default)\n\
421 --wait=sb wait for southbound database update\n\
422 --wait=hv wait for all chassis to catch up\n\
70799ae8
BP
423 -t, --timeout=SECS wait at most SECS seconds\n\
424 --dry-run do not commit changes to database\n\
425 --oneline print exactly one line of output per command\n",
8ad609c0
BP
426 program_name, program_name, ctl_get_db_cmd_usage(),
427 default_nb_db());
a416ff28 428 vlog_usage();
70799ae8
BP
429 printf("\
430 --no-syslog equivalent to --verbose=nbctl:syslog:warn\n");
431 printf("\n\
432Other options:\n\
433 -h, --help display this help message\n\
434 -V, --version display version information\n");
435 exit(EXIT_SUCCESS);
a416ff28
RB
436}
437\f
7127583d 438
fa2a27b2 439/* Find a logical router given its id. */
7127583d 440static const struct nbrec_logical_router *
fa2a27b2 441lr_by_name_or_uuid(struct ctl_context *ctx, const char *id,
7127583d
NG
442 bool must_exist)
443{
fa2a27b2 444 const struct nbrec_logical_router *lr = NULL;
7127583d 445 bool is_uuid = false;
fa2a27b2 446 struct uuid lr_uuid;
7127583d 447
fa2a27b2 448 if (uuid_from_string(&lr_uuid, id)) {
7127583d 449 is_uuid = true;
fa2a27b2 450 lr = nbrec_logical_router_get_for_uuid(ctx->idl, &lr_uuid);
7127583d
NG
451 }
452
fa2a27b2 453 if (!lr) {
7127583d
NG
454 const struct nbrec_logical_router *iter;
455
456 NBREC_LOGICAL_ROUTER_FOR_EACH(iter, ctx->idl) {
457 if (strcmp(iter->name, id)) {
458 continue;
459 }
fa2a27b2 460 if (lr) {
7127583d
NG
461 ctl_fatal("Multiple logical routers named '%s'. "
462 "Use a UUID.", id);
463 }
fa2a27b2 464 lr = iter;
7127583d
NG
465 }
466 }
467
fa2a27b2
JP
468 if (!lr && must_exist) {
469 ctl_fatal("%s: router %s not found", id, is_uuid ? "UUID" : "name");
7127583d
NG
470 }
471
fa2a27b2 472 return lr;
7127583d
NG
473}
474
a416ff28 475static const struct nbrec_logical_switch *
ea46a4e9 476ls_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist)
a416ff28 477{
ea46a4e9 478 const struct nbrec_logical_switch *ls = NULL;
a416ff28 479
ea46a4e9
JP
480 struct uuid ls_uuid;
481 bool is_uuid = uuid_from_string(&ls_uuid, id);
d24d3372 482 if (is_uuid) {
ea46a4e9 483 ls = nbrec_logical_switch_get_for_uuid(ctx->idl, &ls_uuid);
c1f4da63
RB
484 }
485
ea46a4e9 486 if (!ls) {
a416ff28
RB
487 const struct nbrec_logical_switch *iter;
488
70799ae8 489 NBREC_LOGICAL_SWITCH_FOR_EACH(iter, ctx->idl) {
a416ff28
RB
490 if (strcmp(iter->name, id)) {
491 continue;
492 }
ea46a4e9 493 if (ls) {
d24d3372
BP
494 ctl_fatal("Multiple logical switches named '%s'. "
495 "Use a UUID.", id);
a416ff28 496 }
ea46a4e9 497 ls = iter;
a416ff28
RB
498 }
499 }
500
ea46a4e9
JP
501 if (!ls && must_exist) {
502 ctl_fatal("%s: switch %s not found", id, is_uuid ? "UUID" : "name");
a416ff28
RB
503 }
504
ea46a4e9 505 return ls;
a416ff28
RB
506}
507
e2bfcad6 508static const struct nbrec_load_balancer *
509lb_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist)
510{
511 const struct nbrec_load_balancer *lb = NULL;
512
513 struct uuid lb_uuid;
514 bool is_uuid = uuid_from_string(&lb_uuid, id);
515 if (is_uuid) {
516 lb = nbrec_load_balancer_get_for_uuid(ctx->idl, &lb_uuid);
517 }
518
519 if (!lb) {
520 const struct nbrec_load_balancer *iter;
521
522 NBREC_LOAD_BALANCER_FOR_EACH(iter, ctx->idl) {
523 if (strcmp(iter->name, id)) {
524 continue;
525 }
526 if (lb) {
527 ctl_fatal("Multiple load balancers named '%s'. "
528 "Use a UUID.", id);
529 }
530 lb = iter;
531 }
532 }
533
534 if (!lb && must_exist) {
535 ctl_fatal("%s: load balancer %s not found", id,
536 is_uuid ? "UUID" : "name");
537 }
538
539 return lb;
540}
541
fa2a27b2
JP
542/* Given pointer to logical router, this routine prints the router
543 * information. */
7127583d 544static void
fa2a27b2 545print_lr(const struct nbrec_logical_router *lr, struct ds *s)
7127583d 546{
fa2a27b2
JP
547 ds_put_format(s, " router "UUID_FMT" (%s)\n",
548 UUID_ARGS(&lr->header_.uuid), lr->name);
7127583d 549
fa2a27b2 550 for (size_t i = 0; i < lr->n_ports; i++) {
31114af7
JP
551 const struct nbrec_logical_router_port *lrp = lr->ports[i];
552 ds_put_format(s, " port %s\n", lrp->name);
553 if (lrp->mac) {
7127583d 554 ds_put_cstr(s, " mac: ");
34a88a45
JP
555 ds_put_format(s, "\"%s\"\n", lrp->mac);
556 }
557 if (lrp->n_networks) {
558 ds_put_cstr(s, " networks: [");
559 for (size_t j = 0; j < lrp->n_networks; j++) {
560 ds_put_format(s, "%s\"%s\"",
561 j == 0 ? "" : ", ",
562 lrp->networks[j]);
563 }
564 ds_put_cstr(s, "]\n");
7127583d 565 }
7127583d
NG
566 }
567}
568
907a0edf 569static void
ea46a4e9 570print_ls(const struct nbrec_logical_switch *ls, struct ds *s)
907a0edf 571{
ea46a4e9
JP
572 ds_put_format(s, " switch "UUID_FMT" (%s)\n",
573 UUID_ARGS(&ls->header_.uuid), ls->name);
907a0edf 574
ea46a4e9
JP
575 for (size_t i = 0; i < ls->n_ports; i++) {
576 const struct nbrec_logical_switch_port *lsp = ls->ports[i];
907a0edf 577
80f408f4
JP
578 ds_put_format(s, " port %s\n", lsp->name);
579 if (lsp->parent_name) {
580 ds_put_format(s, " parent: %s\n", lsp->parent_name);
bb4c7107 581 }
80f408f4
JP
582 if (lsp->n_tag) {
583 ds_put_format(s, " tag: %"PRIu64"\n", lsp->tag[0]);
445a266a 584 }
80f408f4 585 if (lsp->n_addresses) {
ebd3ce14 586 ds_put_cstr(s, " addresses: [");
80f408f4 587 for (size_t j = 0; j < lsp->n_addresses; j++) {
ebd3ce14
RB
588 ds_put_format(s, "%s\"%s\"",
589 j == 0 ? "" : ", ",
80f408f4 590 lsp->addresses[j]);
907a0edf 591 }
ebd3ce14 592 ds_put_cstr(s, "]\n");
907a0edf
JP
593 }
594 }
595}
596
fa183acc
BP
597static void
598nbctl_init(struct ctl_context *ctx OVS_UNUSED)
599{
600}
601
de32cec7
BP
602static void
603nbctl_pre_sync(struct ctl_context *ctx OVS_UNUSED)
604{
605 if (wait_type != NBCTL_WAIT_NONE) {
606 force_wait = true;
607 } else {
608 VLOG_INFO("\"sync\" command has no effect without --wait");
609 }
610}
611
612static void
613nbctl_sync(struct ctl_context *ctx OVS_UNUSED)
614{
615}
616
907a0edf 617static void
70799ae8 618nbctl_show(struct ctl_context *ctx)
907a0edf 619{
ea46a4e9 620 const struct nbrec_logical_switch *ls;
907a0edf
JP
621
622 if (ctx->argc == 2) {
ea46a4e9
JP
623 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], false);
624 if (ls) {
625 print_ls(ls, &ctx->output);
907a0edf
JP
626 }
627 } else {
ea46a4e9
JP
628 NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) {
629 print_ls(ls, &ctx->output);
907a0edf
JP
630 }
631 }
fa2a27b2 632 const struct nbrec_logical_router *lr;
7127583d
NG
633
634 if (ctx->argc == 2) {
fa2a27b2
JP
635 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], false);
636 if (lr) {
637 print_lr(lr, &ctx->output);
7127583d
NG
638 }
639 } else {
fa2a27b2
JP
640 NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) {
641 print_lr(lr, &ctx->output);
7127583d
NG
642 }
643 }
644}
645
a416ff28 646static void
ea46a4e9 647nbctl_ls_add(struct ctl_context *ctx)
a416ff28 648{
ea46a4e9 649 const char *ls_name = ctx->argc == 2 ? ctx->argv[1] : NULL;
4ff6642f
BP
650
651 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
652 bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL;
653 if (may_exist && add_duplicate) {
654 ctl_fatal("--may-exist and --add-duplicate may not be used together");
655 }
656
ea46a4e9 657 if (ls_name) {
4ff6642f 658 if (!add_duplicate) {
ea46a4e9
JP
659 const struct nbrec_logical_switch *ls;
660 NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) {
661 if (!strcmp(ls->name, ls_name)) {
4ff6642f
BP
662 if (may_exist) {
663 return;
664 }
ea46a4e9
JP
665 ctl_fatal("%s: a switch with this name already exists",
666 ls_name);
4ff6642f
BP
667 }
668 }
669 }
670 } else if (may_exist) {
671 ctl_fatal("--may-exist requires specifying a name");
672 } else if (add_duplicate) {
673 ctl_fatal("--add-duplicate requires specifying a name");
674 }
675
ea46a4e9
JP
676 struct nbrec_logical_switch *ls;
677 ls = nbrec_logical_switch_insert(ctx->txn);
678 if (ls_name) {
679 nbrec_logical_switch_set_name(ls, ls_name);
a416ff28
RB
680 }
681}
682
683static void
ea46a4e9 684nbctl_ls_del(struct ctl_context *ctx)
a416ff28 685{
d24d3372 686 bool must_exist = !shash_find(&ctx->options, "--if-exists");
a416ff28 687 const char *id = ctx->argv[1];
ea46a4e9 688 const struct nbrec_logical_switch *ls;
a416ff28 689
ea46a4e9
JP
690 ls = ls_by_name_or_uuid(ctx, id, must_exist);
691 if (!ls) {
a416ff28
RB
692 return;
693 }
694
ea46a4e9 695 nbrec_logical_switch_delete(ls);
a416ff28
RB
696}
697
698static void
ea46a4e9 699nbctl_ls_list(struct ctl_context *ctx)
a416ff28 700{
ea46a4e9 701 const struct nbrec_logical_switch *ls;
2a547e72 702 struct smap lswitches;
a416ff28 703
2a547e72 704 smap_init(&lswitches);
ea46a4e9
JP
705 NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) {
706 smap_add_format(&lswitches, ls->name, UUID_FMT " (%s)",
707 UUID_ARGS(&ls->header_.uuid), ls->name);
a416ff28 708 }
2a547e72
JP
709 const struct smap_node **nodes = smap_sort(&lswitches);
710 for (size_t i = 0; i < smap_count(&lswitches); i++) {
711 const struct smap_node *node = nodes[i];
70799ae8 712 ds_put_format(&ctx->output, "%s\n", node->value);
2a547e72
JP
713 }
714 smap_destroy(&lswitches);
715 free(nodes);
a416ff28 716}
a416ff28 717\f
80f408f4
JP
718static const struct nbrec_logical_switch_port *
719lsp_by_name_or_uuid(struct ctl_context *ctx, const char *id,
720 bool must_exist)
a416ff28 721{
80f408f4 722 const struct nbrec_logical_switch_port *lsp = NULL;
a416ff28 723
80f408f4
JP
724 struct uuid lsp_uuid;
725 bool is_uuid = uuid_from_string(&lsp_uuid, id);
d24d3372 726 if (is_uuid) {
80f408f4 727 lsp = nbrec_logical_switch_port_get_for_uuid(ctx->idl, &lsp_uuid);
c1f4da63
RB
728 }
729
80f408f4
JP
730 if (!lsp) {
731 NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(lsp, ctx->idl) {
732 if (!strcmp(lsp->name, id)) {
a416ff28
RB
733 break;
734 }
735 }
736 }
737
80f408f4
JP
738 if (!lsp && must_exist) {
739 ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name");
a416ff28
RB
740 }
741
80f408f4 742 return lsp;
a416ff28
RB
743}
744
ea46a4e9 745/* Returns the logical switch that contains 'lsp'. */
d24d3372 746static const struct nbrec_logical_switch *
ea46a4e9 747lsp_to_ls(const struct ovsdb_idl *idl,
80f408f4 748 const struct nbrec_logical_switch_port *lsp)
d24d3372 749{
ea46a4e9
JP
750 const struct nbrec_logical_switch *ls;
751 NBREC_LOGICAL_SWITCH_FOR_EACH (ls, idl) {
752 for (size_t i = 0; i < ls->n_ports; i++) {
753 if (ls->ports[i] == lsp) {
754 return ls;
d24d3372
BP
755 }
756 }
757 }
758
759 /* Can't happen because of the database schema */
760 ctl_fatal("logical port %s is not part of any logical switch",
80f408f4 761 lsp->name);
d24d3372
BP
762}
763
764static const char *
ea46a4e9 765ls_get_name(const struct nbrec_logical_switch *ls,
d24d3372
BP
766 char uuid_s[UUID_LEN + 1], size_t uuid_s_size)
767{
ea46a4e9
JP
768 if (ls->name[0]) {
769 return ls->name;
d24d3372 770 }
ea46a4e9 771 snprintf(uuid_s, uuid_s_size, UUID_FMT, UUID_ARGS(&ls->header_.uuid));
d24d3372
BP
772 return uuid_s;
773}
774
a416ff28 775static void
80f408f4 776nbctl_lsp_add(struct ctl_context *ctx)
a416ff28 777{
d24d3372
BP
778 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
779
ea46a4e9
JP
780 const struct nbrec_logical_switch *ls;
781 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
a416ff28 782
d24d3372
BP
783 const char *parent_name;
784 int64_t tag;
785 if (ctx->argc == 3) {
786 parent_name = NULL;
787 tag = -1;
788 } else if (ctx->argc == 5) {
789 /* Validate tag. */
790 parent_name = ctx->argv[3];
791 if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag)
792 || tag < 0 || tag > 4095) {
793 ctl_fatal("%s: invalid tag", ctx->argv[4]);
794 }
795 } else {
31ed1192 796 ctl_fatal("lsp-add with parent must also specify a tag");
a416ff28
RB
797 }
798
80f408f4
JP
799 const char *lsp_name = ctx->argv[2];
800 const struct nbrec_logical_switch_port *lsp;
801 lsp = lsp_by_name_or_uuid(ctx, lsp_name, false);
802 if (lsp) {
d24d3372 803 if (!may_exist) {
31ed1192 804 ctl_fatal("%s: a port with this name already exists",
80f408f4 805 lsp_name);
d24d3372 806 }
bf5fa52a 807
d24d3372 808 const struct nbrec_logical_switch *lsw;
ea46a4e9
JP
809 lsw = lsp_to_ls(ctx->idl, lsp);
810 if (lsw != ls) {
d24d3372 811 char uuid_s[UUID_LEN + 1];
ea46a4e9
JP
812 ctl_fatal("%s: port already exists but in switch %s", lsp_name,
813 ls_get_name(lsw, uuid_s, sizeof uuid_s));
d24d3372
BP
814 }
815
816 if (parent_name) {
80f408f4 817 if (!lsp->parent_name) {
31ed1192 818 ctl_fatal("%s: port already exists but has no parent",
80f408f4
JP
819 lsp_name);
820 } else if (strcmp(parent_name, lsp->parent_name)) {
31ed1192 821 ctl_fatal("%s: port already exists with different parent %s",
80f408f4 822 lsp_name, lsp->parent_name);
d24d3372
BP
823 }
824
b511690b
GS
825 if (!lsp->n_tag_request) {
826 ctl_fatal("%s: port already exists but has no tag_request",
80f408f4 827 lsp_name);
b511690b 828 } else if (lsp->tag_request[0] != tag) {
31ed1192 829 ctl_fatal("%s: port already exists with different "
b511690b
GS
830 "tag_request %"PRId64, lsp_name,
831 lsp->tag_request[0]);
d24d3372
BP
832 }
833 } else {
80f408f4 834 if (lsp->parent_name) {
31ed1192 835 ctl_fatal("%s: port already exists but has parent %s",
80f408f4 836 lsp_name, lsp->parent_name);
d24d3372 837 }
bf5fa52a 838 }
d24d3372
BP
839
840 return;
bf5fa52a
RB
841 }
842
445a266a 843 /* Create the logical port. */
80f408f4
JP
844 lsp = nbrec_logical_switch_port_insert(ctx->txn);
845 nbrec_logical_switch_port_set_name(lsp, lsp_name);
d24d3372 846 if (tag >= 0) {
80f408f4 847 nbrec_logical_switch_port_set_parent_name(lsp, parent_name);
b511690b 848 nbrec_logical_switch_port_set_tag_request(lsp, &tag, 1);
bf5fa52a 849 }
445a266a
BP
850
851 /* Insert the logical port into the logical switch. */
ea46a4e9 852 nbrec_logical_switch_verify_ports(ls);
80f408f4 853 struct nbrec_logical_switch_port **new_ports = xmalloc(sizeof *new_ports *
ea46a4e9
JP
854 (ls->n_ports + 1));
855 memcpy(new_ports, ls->ports, sizeof *new_ports * ls->n_ports);
856 new_ports[ls->n_ports] = CONST_CAST(struct nbrec_logical_switch_port *,
80f408f4 857 lsp);
ea46a4e9 858 nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports + 1);
445a266a
BP
859 free(new_ports);
860}
861
ea46a4e9 862/* Removes logical switch port 'ls->ports[idx]'. */
445a266a 863static void
ea46a4e9 864remove_lsp(const struct nbrec_logical_switch *ls, size_t idx)
445a266a 865{
ea46a4e9 866 const struct nbrec_logical_switch_port *lsp = ls->ports[idx];
445a266a 867
80f408f4 868 /* First remove 'lsp' from the array of ports. This is what will
445a266a
BP
869 * actually cause the logical port to be deleted when the transaction is
870 * sent to the database server (due to garbage collection). */
80f408f4 871 struct nbrec_logical_switch_port **new_ports
ea46a4e9
JP
872 = xmemdup(ls->ports, sizeof *new_ports * ls->n_ports);
873 new_ports[idx] = new_ports[ls->n_ports - 1];
874 nbrec_logical_switch_verify_ports(ls);
875 nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports - 1);
445a266a
BP
876 free(new_ports);
877
80f408f4 878 /* Delete 'lsp' from the IDL. This won't have a real effect on the
445a266a 879 * database server (the IDL will suppress it in fact) but it means that it
80f408f4
JP
880 * won't show up when we iterate with NBREC_LOGICAL_SWITCH_PORT_FOR_EACH
881 * later. */
882 nbrec_logical_switch_port_delete(lsp);
a416ff28
RB
883}
884
885static void
80f408f4 886nbctl_lsp_del(struct ctl_context *ctx)
a416ff28 887{
d24d3372 888 bool must_exist = !shash_find(&ctx->options, "--if-exists");
80f408f4 889 const struct nbrec_logical_switch_port *lsp;
a416ff28 890
80f408f4
JP
891 lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], must_exist);
892 if (!lsp) {
a416ff28
RB
893 return;
894 }
895
80f408f4 896 /* Find the switch that contains 'lsp', then delete it. */
ea46a4e9
JP
897 const struct nbrec_logical_switch *ls;
898 NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) {
899 for (size_t i = 0; i < ls->n_ports; i++) {
900 if (ls->ports[i] == lsp) {
901 remove_lsp(ls, i);
445a266a
BP
902 return;
903 }
904 }
a416ff28 905 }
a416ff28 906
d24d3372
BP
907 /* Can't happen because of the database schema. */
908 ctl_fatal("logical port %s is not part of any logical switch",
445a266a
BP
909 ctx->argv[1]);
910}
a416ff28
RB
911
912static void
80f408f4 913nbctl_lsp_list(struct ctl_context *ctx)
a416ff28 914{
a416ff28 915 const char *id = ctx->argv[1];
ea46a4e9 916 const struct nbrec_logical_switch *ls;
80f408f4 917 struct smap lsps;
2a547e72 918 size_t i;
a416ff28 919
ea46a4e9 920 ls = ls_by_name_or_uuid(ctx, id, true);
a416ff28 921
80f408f4 922 smap_init(&lsps);
ea46a4e9
JP
923 for (i = 0; i < ls->n_ports; i++) {
924 const struct nbrec_logical_switch_port *lsp = ls->ports[i];
80f408f4
JP
925 smap_add_format(&lsps, lsp->name, UUID_FMT " (%s)",
926 UUID_ARGS(&lsp->header_.uuid), lsp->name);
a416ff28 927 }
80f408f4
JP
928 const struct smap_node **nodes = smap_sort(&lsps);
929 for (i = 0; i < smap_count(&lsps); i++) {
2a547e72 930 const struct smap_node *node = nodes[i];
70799ae8 931 ds_put_format(&ctx->output, "%s\n", node->value);
2a547e72 932 }
80f408f4 933 smap_destroy(&lsps);
2a547e72 934 free(nodes);
a416ff28
RB
935}
936
bf5fa52a 937static void
80f408f4 938nbctl_lsp_get_parent(struct ctl_context *ctx)
bf5fa52a 939{
80f408f4 940 const struct nbrec_logical_switch_port *lsp;
bf5fa52a 941
80f408f4
JP
942 lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], true);
943 if (lsp->parent_name) {
944 ds_put_format(&ctx->output, "%s\n", lsp->parent_name);
bf5fa52a
RB
945 }
946}
947
948static void
80f408f4 949nbctl_lsp_get_tag(struct ctl_context *ctx)
bf5fa52a 950{
80f408f4 951 const struct nbrec_logical_switch_port *lsp;
bf5fa52a 952
80f408f4
JP
953 lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], true);
954 if (lsp->n_tag > 0) {
955 ds_put_format(&ctx->output, "%"PRId64"\n", lsp->tag[0]);
bf5fa52a
RB
956 }
957}
958
a416ff28 959static void
80f408f4 960nbctl_lsp_set_addresses(struct ctl_context *ctx)
a416ff28 961{
a416ff28 962 const char *id = ctx->argv[1];
80f408f4 963 const struct nbrec_logical_switch_port *lsp;
a416ff28 964
80f408f4 965 lsp = lsp_by_name_or_uuid(ctx, id, true);
a416ff28 966
69e73316
RB
967 int i;
968 for (i = 2; i < ctx->argc; i++) {
969 struct eth_addr ea;
970
8639f9be 971 if (strcmp(ctx->argv[i], "unknown") && strcmp(ctx->argv[i], "dynamic")
69e73316
RB
972 && !ovs_scan(ctx->argv[i], ETH_ADDR_SCAN_FMT,
973 ETH_ADDR_SCAN_ARGS(ea))) {
d24d3372
BP
974 ctl_fatal("%s: Invalid address format. See ovn-nb(5). "
975 "Hint: An Ethernet address must be "
976 "listed before an IP address, together as a single "
977 "argument.", ctx->argv[i]);
69e73316
RB
978 }
979 }
980
80f408f4 981 nbrec_logical_switch_port_set_addresses(lsp,
a416ff28
RB
982 (const char **) ctx->argv + 2, ctx->argc - 2);
983}
984
985static void
80f408f4 986nbctl_lsp_get_addresses(struct ctl_context *ctx)
a416ff28 987{
a416ff28 988 const char *id = ctx->argv[1];
80f408f4 989 const struct nbrec_logical_switch_port *lsp;
2fa326a3 990 struct svec addresses;
2a547e72 991 const char *mac;
a416ff28
RB
992 size_t i;
993
80f408f4 994 lsp = lsp_by_name_or_uuid(ctx, id, true);
a416ff28 995
2fa326a3 996 svec_init(&addresses);
80f408f4
JP
997 for (i = 0; i < lsp->n_addresses; i++) {
998 svec_add(&addresses, lsp->addresses[i]);
2a547e72 999 }
2fa326a3
BP
1000 svec_sort(&addresses);
1001 SVEC_FOR_EACH(i, mac, &addresses) {
70799ae8 1002 ds_put_format(&ctx->output, "%s\n", mac);
a416ff28 1003 }
2fa326a3 1004 svec_destroy(&addresses);
a416ff28 1005}
65fb6826 1006
92207865 1007static void
80f408f4 1008nbctl_lsp_set_port_security(struct ctl_context *ctx)
92207865 1009{
92207865 1010 const char *id = ctx->argv[1];
80f408f4 1011 const struct nbrec_logical_switch_port *lsp;
92207865 1012
80f408f4
JP
1013 lsp = lsp_by_name_or_uuid(ctx, id, true);
1014 nbrec_logical_switch_port_set_port_security(lsp,
92207865
BP
1015 (const char **) ctx->argv + 2, ctx->argc - 2);
1016}
1017
1018static void
80f408f4 1019nbctl_lsp_get_port_security(struct ctl_context *ctx)
92207865 1020{
92207865 1021 const char *id = ctx->argv[1];
80f408f4 1022 const struct nbrec_logical_switch_port *lsp;
2a547e72
JP
1023 struct svec addrs;
1024 const char *addr;
92207865
BP
1025 size_t i;
1026
80f408f4 1027 lsp = lsp_by_name_or_uuid(ctx, id, true);
2a547e72 1028 svec_init(&addrs);
80f408f4
JP
1029 for (i = 0; i < lsp->n_port_security; i++) {
1030 svec_add(&addrs, lsp->port_security[i]);
2a547e72
JP
1031 }
1032 svec_sort(&addrs);
1033 SVEC_FOR_EACH(i, addr, &addrs) {
70799ae8 1034 ds_put_format(&ctx->output, "%s\n", addr);
92207865 1035 }
2a547e72 1036 svec_destroy(&addrs);
92207865
BP
1037}
1038
65fb6826 1039static void
80f408f4 1040nbctl_lsp_get_up(struct ctl_context *ctx)
65fb6826 1041{
65fb6826 1042 const char *id = ctx->argv[1];
80f408f4 1043 const struct nbrec_logical_switch_port *lsp;
65fb6826 1044
80f408f4 1045 lsp = lsp_by_name_or_uuid(ctx, id, true);
70799ae8 1046 ds_put_format(&ctx->output,
80f408f4 1047 "%s\n", (lsp->up && *lsp->up) ? "up" : "down");
65fb6826 1048}
e9e8bcdf 1049
7127583d
NG
1050static bool
1051parse_enabled(const char *state)
1052{
1053 if (!strcasecmp(state, "enabled")) {
1054 return true;
1055 } else if (!strcasecmp(state, "disabled")) {
1056 return false;
1057 } else {
1058 ctl_fatal("%s: state must be \"enabled\" or \"disabled\"", state);
1059 }
1060}
1061
e9e8bcdf 1062static void
80f408f4 1063nbctl_lsp_set_enabled(struct ctl_context *ctx)
e9e8bcdf 1064{
e9e8bcdf
RB
1065 const char *id = ctx->argv[1];
1066 const char *state = ctx->argv[2];
80f408f4 1067 const struct nbrec_logical_switch_port *lsp;
e9e8bcdf 1068
80f408f4 1069 lsp = lsp_by_name_or_uuid(ctx, id, true);
7127583d 1070 bool enabled = parse_enabled(state);
80f408f4 1071 nbrec_logical_switch_port_set_enabled(lsp, &enabled, 1);
7127583d
NG
1072}
1073
e9e8bcdf 1074static void
80f408f4 1075nbctl_lsp_get_enabled(struct ctl_context *ctx)
e9e8bcdf 1076{
e9e8bcdf 1077 const char *id = ctx->argv[1];
80f408f4 1078 const struct nbrec_logical_switch_port *lsp;
e9e8bcdf 1079
80f408f4 1080 lsp = lsp_by_name_or_uuid(ctx, id, true);
70799ae8 1081 ds_put_format(&ctx->output, "%s\n",
80f408f4 1082 !lsp->enabled || *lsp->enabled ? "enabled" : "disabled");
e9e8bcdf 1083}
f2903c22
RB
1084
1085static void
80f408f4 1086nbctl_lsp_set_type(struct ctl_context *ctx)
f2903c22 1087{
f2903c22
RB
1088 const char *id = ctx->argv[1];
1089 const char *type = ctx->argv[2];
80f408f4 1090 const struct nbrec_logical_switch_port *lsp;
f2903c22 1091
80f408f4
JP
1092 lsp = lsp_by_name_or_uuid(ctx, id, true);
1093 nbrec_logical_switch_port_set_type(lsp, type);
f2903c22
RB
1094}
1095
1096static void
80f408f4 1097nbctl_lsp_get_type(struct ctl_context *ctx)
f2903c22 1098{
f2903c22 1099 const char *id = ctx->argv[1];
80f408f4 1100 const struct nbrec_logical_switch_port *lsp;
f2903c22 1101
80f408f4
JP
1102 lsp = lsp_by_name_or_uuid(ctx, id, true);
1103 ds_put_format(&ctx->output, "%s\n", lsp->type);
f2903c22
RB
1104}
1105
1106static void
80f408f4 1107nbctl_lsp_set_options(struct ctl_context *ctx)
f2903c22 1108{
f2903c22 1109 const char *id = ctx->argv[1];
80f408f4 1110 const struct nbrec_logical_switch_port *lsp;
f2903c22
RB
1111 size_t i;
1112 struct smap options = SMAP_INITIALIZER(&options);
1113
80f408f4 1114 lsp = lsp_by_name_or_uuid(ctx, id, true);
f2903c22
RB
1115 for (i = 2; i < ctx->argc; i++) {
1116 char *key, *value;
1117 value = xstrdup(ctx->argv[i]);
1118 key = strsep(&value, "=");
1119 if (value) {
1120 smap_add(&options, key, value);
1121 }
1122 free(key);
1123 }
1124
80f408f4 1125 nbrec_logical_switch_port_set_options(lsp, &options);
f2903c22
RB
1126
1127 smap_destroy(&options);
1128}
1129
1130static void
80f408f4 1131nbctl_lsp_get_options(struct ctl_context *ctx)
f2903c22 1132{
f2903c22 1133 const char *id = ctx->argv[1];
80f408f4 1134 const struct nbrec_logical_switch_port *lsp;
f2903c22
RB
1135 struct smap_node *node;
1136
80f408f4
JP
1137 lsp = lsp_by_name_or_uuid(ctx, id, true);
1138 SMAP_FOR_EACH(node, &lsp->options) {
70799ae8 1139 ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
f2903c22
RB
1140 }
1141}
8f96455d 1142
281977f7
NS
1143static void
1144nbctl_lsp_set_dhcpv4_options(struct ctl_context *ctx)
1145{
1146 const char *id = ctx->argv[1];
1147 const struct nbrec_logical_switch_port *lsp;
1148
1149 lsp = lsp_by_name_or_uuid(ctx, id, true);
1150 const struct nbrec_dhcp_options *dhcp_opt = NULL;
1151 if (ctx->argc == 3 ) {
1152 dhcp_opt = dhcp_options_get(ctx, ctx->argv[2], true);
1153 }
1154
1155 if (dhcp_opt) {
1156 ovs_be32 ip;
1157 unsigned int plen;
1158 char *error = ip_parse_cidr(dhcp_opt->cidr, &ip, &plen);
1159 if (error){
1160 free(error);
1161 ctl_fatal("DHCP options cidr '%s' is not IPv4", dhcp_opt->cidr);
281977f7
NS
1162 }
1163 }
1164 nbrec_logical_switch_port_set_dhcpv4_options(lsp, dhcp_opt);
1165}
1166
1167static void
1168nbctl_lsp_get_dhcpv4_options(struct ctl_context *ctx)
1169{
1170 const char *id = ctx->argv[1];
1171 const struct nbrec_logical_switch_port *lsp;
1172
1173 lsp = lsp_by_name_or_uuid(ctx, id, true);
1174 if (lsp->dhcpv4_options) {
1175 ds_put_format(&ctx->output, UUID_FMT " (%s)\n",
1176 UUID_ARGS(&lsp->dhcpv4_options->header_.uuid),
1177 lsp->dhcpv4_options->cidr);
1178 }
1179}
1180
8f96455d
JP
1181enum {
1182 DIR_FROM_LPORT,
1183 DIR_TO_LPORT
1184};
1185
1186static int
1187dir_encode(const char *dir)
1188{
1189 if (!strcmp(dir, "from-lport")) {
1190 return DIR_FROM_LPORT;
1191 } else if (!strcmp(dir, "to-lport")) {
1192 return DIR_TO_LPORT;
1193 }
1194
1195 OVS_NOT_REACHED();
1196}
1197
1198static int
1199acl_cmp(const void *acl1_, const void *acl2_)
1200{
1201 const struct nbrec_acl *const *acl1p = acl1_;
1202 const struct nbrec_acl *const *acl2p = acl2_;
1203 const struct nbrec_acl *acl1 = *acl1p;
1204 const struct nbrec_acl *acl2 = *acl2p;
1205
1206 int dir1 = dir_encode(acl1->direction);
1207 int dir2 = dir_encode(acl2->direction);
1208
1209 if (dir1 != dir2) {
1210 return dir1 < dir2 ? -1 : 1;
1211 } else if (acl1->priority != acl2->priority) {
1212 return acl1->priority > acl2->priority ? -1 : 1;
1213 } else {
1214 return strcmp(acl1->match, acl2->match);
1215 }
1216}
1217
1218static void
70799ae8 1219nbctl_acl_list(struct ctl_context *ctx)
8f96455d 1220{
ea46a4e9 1221 const struct nbrec_logical_switch *ls;
8f96455d
JP
1222 const struct nbrec_acl **acls;
1223 size_t i;
1224
ea46a4e9 1225 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
8f96455d 1226
ea46a4e9
JP
1227 acls = xmalloc(sizeof *acls * ls->n_acls);
1228 for (i = 0; i < ls->n_acls; i++) {
1229 acls[i] = ls->acls[i];
8f96455d
JP
1230 }
1231
ea46a4e9 1232 qsort(acls, ls->n_acls, sizeof *acls, acl_cmp);
8f96455d 1233
ea46a4e9 1234 for (i = 0; i < ls->n_acls; i++) {
8f96455d 1235 const struct nbrec_acl *acl = acls[i];
9e891740
JP
1236 ds_put_format(&ctx->output, "%10s %5"PRId64" (%s) %s%s\n",
1237 acl->direction, acl->priority,
1238 acl->match, acl->action, acl->log ? " log" : "");
8f96455d
JP
1239 }
1240
1241 free(acls);
1242}
1243
d24d3372
BP
1244static const char *
1245parse_direction(const char *arg)
8f96455d 1246{
8f96455d 1247 /* Validate direction. Only require the first letter. */
d24d3372
BP
1248 if (arg[0] == 't') {
1249 return "to-lport";
1250 } else if (arg[0] == 'f') {
1251 return "from-lport";
8f96455d 1252 } else {
d24d3372 1253 ctl_fatal("%s: direction must be \"to-lport\" or \"from-lport\"", arg);
8f96455d 1254 }
d24d3372 1255}
8f96455d 1256
d24d3372
BP
1257static int
1258parse_priority(const char *arg)
1259{
8f96455d 1260 /* Validate priority. */
d24d3372
BP
1261 int64_t priority;
1262 if (!ovs_scan(arg, "%"SCNd64, &priority)
1263 || priority < 0 || priority > 32767) {
1264 ctl_fatal("%s: priority must in range 0...32767", arg);
8f96455d 1265 }
d24d3372
BP
1266 return priority;
1267}
1268
1269static void
1270nbctl_acl_add(struct ctl_context *ctx)
1271{
ea46a4e9 1272 const struct nbrec_logical_switch *ls;
d24d3372
BP
1273 const char *action = ctx->argv[5];
1274
ea46a4e9 1275 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
d24d3372
BP
1276
1277 const char *direction = parse_direction(ctx->argv[2]);
1278 int64_t priority = parse_priority(ctx->argv[3]);
8f96455d
JP
1279
1280 /* Validate action. */
1281 if (strcmp(action, "allow") && strcmp(action, "allow-related")
1282 && strcmp(action, "drop") && strcmp(action, "reject")) {
d24d3372
BP
1283 ctl_fatal("%s: action must be one of \"allow\", \"allow-related\", "
1284 "\"drop\", and \"reject\"", action);
8f96455d
JP
1285 return;
1286 }
1287
1288 /* Create the acl. */
70799ae8 1289 struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn);
8f96455d
JP
1290 nbrec_acl_set_priority(acl, priority);
1291 nbrec_acl_set_direction(acl, direction);
1292 nbrec_acl_set_match(acl, ctx->argv[4]);
1293 nbrec_acl_set_action(acl, action);
70799ae8 1294 if (shash_find(&ctx->options, "--log") != NULL) {
8f96455d
JP
1295 nbrec_acl_set_log(acl, true);
1296 }
1297
1298 /* Insert the acl into the logical switch. */
ea46a4e9
JP
1299 nbrec_logical_switch_verify_acls(ls);
1300 struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * (ls->n_acls + 1));
1301 memcpy(new_acls, ls->acls, sizeof *new_acls * ls->n_acls);
1302 new_acls[ls->n_acls] = acl;
1303 nbrec_logical_switch_set_acls(ls, new_acls, ls->n_acls + 1);
8f96455d
JP
1304 free(new_acls);
1305}
1306
1307static void
70799ae8 1308nbctl_acl_del(struct ctl_context *ctx)
8f96455d 1309{
ea46a4e9
JP
1310 const struct nbrec_logical_switch *ls;
1311 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
8f96455d
JP
1312
1313 if (ctx->argc != 2 && ctx->argc != 3 && ctx->argc != 5) {
d24d3372 1314 ctl_fatal("cannot specify priority without match");
8f96455d
JP
1315 }
1316
1317 if (ctx->argc == 2) {
1318 /* If direction, priority, and match are not specified, delete
1319 * all ACLs. */
ea46a4e9
JP
1320 nbrec_logical_switch_verify_acls(ls);
1321 nbrec_logical_switch_set_acls(ls, NULL, 0);
8f96455d
JP
1322 return;
1323 }
1324
d24d3372 1325 const char *direction = parse_direction(ctx->argv[2]);
8f96455d
JP
1326
1327 /* If priority and match are not specified, delete all ACLs with the
1328 * specified direction. */
1329 if (ctx->argc == 3) {
ea46a4e9 1330 struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * ls->n_acls);
8f96455d
JP
1331
1332 int n_acls = 0;
ea46a4e9
JP
1333 for (size_t i = 0; i < ls->n_acls; i++) {
1334 if (strcmp(direction, ls->acls[i]->direction)) {
1335 new_acls[n_acls++] = ls->acls[i];
8f96455d
JP
1336 }
1337 }
1338
ea46a4e9
JP
1339 nbrec_logical_switch_verify_acls(ls);
1340 nbrec_logical_switch_set_acls(ls, new_acls, n_acls);
8f96455d
JP
1341 free(new_acls);
1342 return;
1343 }
1344
d24d3372 1345 int64_t priority = parse_priority(ctx->argv[3]);
8f96455d
JP
1346
1347 /* Remove the matching rule. */
ea46a4e9
JP
1348 for (size_t i = 0; i < ls->n_acls; i++) {
1349 struct nbrec_acl *acl = ls->acls[i];
8f96455d
JP
1350
1351 if (priority == acl->priority && !strcmp(ctx->argv[4], acl->match) &&
1352 !strcmp(direction, acl->direction)) {
1353 struct nbrec_acl **new_acls
ea46a4e9
JP
1354 = xmemdup(ls->acls, sizeof *new_acls * ls->n_acls);
1355 new_acls[i] = ls->acls[ls->n_acls - 1];
1356 nbrec_logical_switch_verify_acls(ls);
1357 nbrec_logical_switch_set_acls(ls, new_acls,
1358 ls->n_acls - 1);
8f96455d
JP
1359 free(new_acls);
1360 return;
1361 }
1362 }
1363}
e2bfcad6 1364
1365static void
1366nbctl_lb_add(struct ctl_context *ctx)
1367{
1368 const char *lb_name = ctx->argv[1];
1369 const char *lb_vip = ctx->argv[2];
1370 char *lb_ips = ctx->argv[3];
1371
1372 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
1373 bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL;
1374
1375 const char *lb_proto;
1376 bool is_update_proto = false;
1377 bool is_vip_with_port = true;
1378
1379 if (ctx->argc == 4) {
1380 /* Default protocol. */
1381 lb_proto = "tcp";
1382 } else {
1383 /* Validate protocol. */
1384 lb_proto = ctx->argv[4];
1385 is_update_proto = true;
1386 if (strcmp(lb_proto, "tcp") && strcmp(lb_proto, "udp")) {
1387 ctl_fatal("%s: protocol must be one of \"tcp\", \"udp\".",
1388 lb_proto);
1389 }
1390 }
1391
1392 ovs_be32 ipv4 = 0;
1393 ovs_be16 port = 0;
1394 char *error = ip_parse_port(lb_vip, &ipv4, &port);
1395 if (error) {
1396 free(error);
1397 if (!ip_parse(lb_vip, &ipv4)) {
1398 ctl_fatal("%s: should be an IPv4 address (or an IPv4 address "
1399 "and a port number with : as a separator).", lb_vip);
1400 }
1401
1402 if (is_update_proto) {
1403 ctl_fatal("Protocol is unnecessary when no port of vip "
1404 "is given.");
1405 }
1406 is_vip_with_port = false;
1407 }
1408
1409 char *token = NULL, *save_ptr = NULL;
1410 struct ds lb_ips_new = DS_EMPTY_INITIALIZER;
1411 for (token = strtok_r(lb_ips, ",", &save_ptr);
1412 token != NULL; token = strtok_r(NULL, ",", &save_ptr)) {
1413 if (is_vip_with_port) {
1414 error = ip_parse_port(token, &ipv4, &port);
1415 if (error) {
1416 free(error);
1417 ds_destroy(&lb_ips_new);
1418 ctl_fatal("%s: should be an IPv4 address and a port "
1419 "number with : as a separator.", token);
1420 }
1421 } else {
1422 if (!ip_parse(token, &ipv4)) {
1423 ds_destroy(&lb_ips_new);
1424 ctl_fatal("%s: should be an IPv4 address.", token);
1425 }
1426 }
1427 ds_put_format(&lb_ips_new, "%s%s",
1428 lb_ips_new.length ? "," : "", token);
1429 }
1430
1431 const struct nbrec_load_balancer *lb = NULL;
1432 if (!add_duplicate) {
1433 lb = lb_by_name_or_uuid(ctx, lb_name, false);
1434 if (lb) {
1435 if (smap_get(&lb->vips, lb_vip)) {
1436 if (!may_exist) {
1437 ds_destroy(&lb_ips_new);
1438 ctl_fatal("%s: a load balancer with this vip (%s) "
1439 "already exists", lb_name, lb_vip);
1440 }
1441 /* Update the vips. */
1442 smap_replace(CONST_CAST(struct smap *, &lb->vips),
1443 lb_vip, ds_cstr(&lb_ips_new));
1444 } else {
1445 /* Add the new vips. */
1446 smap_add(CONST_CAST(struct smap *, &lb->vips),
1447 lb_vip, ds_cstr(&lb_ips_new));
1448 }
1449
1450 /* Update the load balancer. */
1451 if (is_update_proto) {
1452 nbrec_load_balancer_verify_protocol(lb);
1453 nbrec_load_balancer_set_protocol(lb, lb_proto);
1454 }
1455 nbrec_load_balancer_verify_vips(lb);
1456 nbrec_load_balancer_set_vips(lb, &lb->vips);
1457 ds_destroy(&lb_ips_new);
1458 return;
1459 }
1460 }
1461
1462 /* Create the load balancer. */
1463 lb = nbrec_load_balancer_insert(ctx->txn);
1464 nbrec_load_balancer_set_name(lb, lb_name);
1465 nbrec_load_balancer_set_protocol(lb, lb_proto);
1466 smap_add(CONST_CAST(struct smap *, &lb->vips),
1467 lb_vip, ds_cstr(&lb_ips_new));
1468 nbrec_load_balancer_set_vips(lb, &lb->vips);
1469 ds_destroy(&lb_ips_new);
1470}
1471
1472static void
1473nbctl_lb_del(struct ctl_context *ctx)
1474{
1475 const char *id = ctx->argv[1];
1476 const struct nbrec_load_balancer *lb = NULL;
1477 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1478
1479 lb = lb_by_name_or_uuid(ctx, id, false);
1480 if (!lb) {
1481 return;
1482 }
1483
1484 if (ctx->argc == 3) {
1485 const char *lb_vip = ctx->argv[2];
1486 if (smap_get(&lb->vips, lb_vip)) {
1487 smap_remove(CONST_CAST(struct smap *, &lb->vips), lb_vip);
1488 if (smap_is_empty(&lb->vips)) {
1489 nbrec_load_balancer_delete(lb);
1490 return;
1491 }
1492
1493 /* Delete the vip of the load balancer. */
1494 nbrec_load_balancer_verify_vips(lb);
1495 nbrec_load_balancer_set_vips(lb, &lb->vips);
1496 return;
1497 }
1498 if (must_exist) {
1499 ctl_fatal("vip %s is not part of the load balancer.",
1500 lb_vip);
1501 }
1502 return;
1503 }
1504 nbrec_load_balancer_delete(lb);
1505}
1506
1507static void
1508lb_info_add_smap(const struct nbrec_load_balancer *lb,
1509 struct smap *lbs)
1510{
1511 struct ds key = DS_EMPTY_INITIALIZER;
1512 struct ds val = DS_EMPTY_INITIALIZER;
1513 char *error, *protocol;
1514 ovs_be32 ipv4 = 0;
1515 ovs_be16 port = 0;
1516
1517 const struct smap_node **nodes = smap_sort(&lb->vips);
1518 if (nodes) {
1519 for (int i = 0; i < smap_count(&lb->vips); i++) {
1520 const struct smap_node *node = nodes[i];
1521 protocol = lb->protocol;
1522 error = ip_parse_port(node->key, &ipv4, &port);
1523 if (error) {
1524 free(error);
1525 protocol = "tcp/udp";
1526 }
1527
1528 i == 0 ? ds_put_format(&val,
1529 UUID_FMT " %-20.16s%-11.7s%-25.21s%s",
1530 UUID_ARGS(&lb->header_.uuid),
1531 lb->name, protocol,
1532 node->key, node->value)
1533 : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
1534 "", protocol,
1535 node->key, node->value);
1536 }
1537
1538 ds_put_format(&key, "%-20.16s", lb->name);
1539 smap_add(lbs, ds_cstr(&key), ds_cstr(&val));
1540
1541 ds_destroy(&key);
1542 ds_destroy(&val);
1543 free(nodes);
1544 }
1545}
1546
1547static void
1548lb_info_print(struct ctl_context *ctx, struct smap *lbs)
1549{
1550 const struct smap_node **nodes = smap_sort(lbs);
1551 if (nodes) {
1552 ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.21s%s\n",
1553 "UUID", "LB", "PROTO", "VIP", "IPs");
1554 for (size_t i = 0; i < smap_count(lbs); i++) {
1555 const struct smap_node *node = nodes[i];
1556 ds_put_format(&ctx->output, "%s\n", node->value);
1557 }
1558
1559 free(nodes);
1560 }
1561}
1562
1563static void
1564lb_info_list_all(struct ctl_context *ctx,
1565 const char *lb_name, bool lb_check)
1566{
1567 const struct nbrec_load_balancer *lb;
1568 struct smap lbs = SMAP_INITIALIZER(&lbs);
1569
1570 NBREC_LOAD_BALANCER_FOR_EACH(lb, ctx->idl) {
1571 if (lb_check && strcmp(lb->name, lb_name)) {
1572 continue;
1573 }
1574 lb_info_add_smap(lb, &lbs);
1575 }
1576
1577 lb_info_print(ctx, &lbs);
1578 smap_destroy(&lbs);
1579}
1580
1581static void
1582nbctl_lb_list(struct ctl_context *ctx)
1583{
1584 if (ctx->argc == 1) {
1585 lb_info_list_all(ctx, NULL, false);
1586 } else if (ctx->argc == 2) {
1587 lb_info_list_all(ctx, ctx->argv[1], true);
1588 }
1589}
1590
1591static void
1592nbctl_lr_lb_add(struct ctl_context *ctx)
1593{
1594 const struct nbrec_logical_router *lr;
1595 const struct nbrec_load_balancer *new_lb;
1596
1597 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
1598 new_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
1599
1600 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
1601 for (int i = 0; i < lr->n_load_balancer; i++) {
1602 const struct nbrec_load_balancer *lb
1603 = lr->load_balancer[i];
1604
1605 if (uuid_equals(&new_lb->header_.uuid, &lb->header_.uuid)) {
1606 if (may_exist) {
1607 return;
1608 }
1609 ctl_fatal(UUID_FMT " : a load balancer with this UUID already "
1610 "exists", UUID_ARGS(&lb->header_.uuid));
1611 }
1612 }
1613
1614 /* Insert the load balancer into the logical router. */
1615 nbrec_logical_router_verify_load_balancer(lr);
1616 struct nbrec_load_balancer **new_lbs
1617 = xmalloc(sizeof *new_lbs * (lr->n_load_balancer + 1));
1618
1619 memcpy(new_lbs, lr->load_balancer, sizeof *new_lbs * lr->n_load_balancer);
1620 new_lbs[lr->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer *,
1621 new_lb);
1622 nbrec_logical_router_set_load_balancer(lr, new_lbs,
1623 lr->n_load_balancer + 1);
1624 free(new_lbs);
1625}
1626
1627static void
1628nbctl_lr_lb_del(struct ctl_context *ctx)
1629{
1630 const struct nbrec_logical_router *lr;
1631 const struct nbrec_load_balancer *del_lb;
1632 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
1633
1634 if (ctx->argc == 2) {
1635 /* If load-balancer is not specified, remove
1636 * all load-balancers from the logical router. */
1637 nbrec_logical_router_verify_load_balancer(lr);
1638 nbrec_logical_router_set_load_balancer(lr, NULL, 0);
1639 return;
1640 }
1641
1642 del_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
1643 for (size_t i = 0; i < lr->n_load_balancer; i++) {
1644 const struct nbrec_load_balancer *lb
1645 = lr->load_balancer[i];
1646
1647 if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) {
1648 /* Remove the matching rule. */
1649 nbrec_logical_router_verify_load_balancer(lr);
1650
1651 struct nbrec_load_balancer **new_lbs
1652 = xmemdup(lr->load_balancer,
1653 sizeof *new_lbs * lr->n_load_balancer);
1654 new_lbs[i] = lr->load_balancer[lr->n_load_balancer - 1];
1655 nbrec_logical_router_set_load_balancer(lr, new_lbs,
1656 lr->n_load_balancer - 1);
1657 free(new_lbs);
1658 return;
1659 }
1660 }
1661
1662 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1663 if (must_exist) {
1664 ctl_fatal("load balancer %s is not part of any logical router.",
1665 del_lb->name);
1666 }
1667}
1668
1669static void
1670nbctl_lr_lb_list(struct ctl_context *ctx)
1671{
1672 const char *lr_name = ctx->argv[1];
1673 const struct nbrec_logical_router *lr;
1674 struct smap lbs = SMAP_INITIALIZER(&lbs);
1675
1676 lr = lr_by_name_or_uuid(ctx, lr_name, true);
1677 for (int i = 0; i < lr->n_load_balancer; i++) {
1678 const struct nbrec_load_balancer *lb
1679 = lr->load_balancer[i];
1680 lb_info_add_smap(lb, &lbs);
1681 }
1682
1683 lb_info_print(ctx, &lbs);
1684 smap_destroy(&lbs);
1685}
1686
1687static void
1688nbctl_ls_lb_add(struct ctl_context *ctx)
1689{
1690 const struct nbrec_logical_switch *ls;
1691 const struct nbrec_load_balancer *new_lb;
1692
1693 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
1694 new_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
1695
1696 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
1697 for (int i = 0; i < ls->n_load_balancer; i++) {
1698 const struct nbrec_load_balancer *lb
1699 = ls->load_balancer[i];
1700
1701 if (uuid_equals(&new_lb->header_.uuid, &lb->header_.uuid)) {
1702 if (may_exist) {
1703 return;
1704 }
1705 ctl_fatal(UUID_FMT " : a load balancer with this UUID already "
1706 "exists", UUID_ARGS(&lb->header_.uuid));
1707 }
1708 }
1709
1710 /* Insert the load balancer into the logical switch. */
1711 nbrec_logical_switch_verify_load_balancer(ls);
1712 struct nbrec_load_balancer **new_lbs
1713 = xmalloc(sizeof *new_lbs * (ls->n_load_balancer + 1));
1714
1715 memcpy(new_lbs, ls->load_balancer, sizeof *new_lbs * ls->n_load_balancer);
1716 new_lbs[ls->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer *,
1717 new_lb);
1718 nbrec_logical_switch_set_load_balancer(ls, new_lbs,
1719 ls->n_load_balancer + 1);
1720 free(new_lbs);
1721}
1722
1723static void
1724nbctl_ls_lb_del(struct ctl_context *ctx)
1725{
1726 const struct nbrec_logical_switch *ls;
1727 const struct nbrec_load_balancer *del_lb;
1728 ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
1729
1730 if (ctx->argc == 2) {
1731 /* If load-balancer is not specified, remove
1732 * all load-balancers from the logical switch. */
1733 nbrec_logical_switch_verify_load_balancer(ls);
1734 nbrec_logical_switch_set_load_balancer(ls, NULL, 0);
1735 return;
1736 }
1737
1738 del_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
1739 for (size_t i = 0; i < ls->n_load_balancer; i++) {
1740 const struct nbrec_load_balancer *lb
1741 = ls->load_balancer[i];
1742
1743 if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) {
1744 /* Remove the matching rule. */
1745 nbrec_logical_switch_verify_load_balancer(ls);
1746
1747 struct nbrec_load_balancer **new_lbs
1748 = xmemdup(ls->load_balancer,
1749 sizeof *new_lbs * ls->n_load_balancer);
1750 new_lbs[i] = ls->load_balancer[ls->n_load_balancer - 1];
1751 nbrec_logical_switch_set_load_balancer(ls, new_lbs,
1752 ls->n_load_balancer - 1);
1753 free(new_lbs);
1754 return;
1755 }
1756 }
1757
1758 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1759 if (must_exist) {
1760 ctl_fatal("load balancer %s is not part of any logical switch.",
1761 del_lb->name);
1762 }
1763}
1764
1765static void
1766nbctl_ls_lb_list(struct ctl_context *ctx)
1767{
1768 const char *ls_name = ctx->argv[1];
1769 const struct nbrec_logical_switch *ls;
1770 struct smap lbs = SMAP_INITIALIZER(&lbs);
1771
1772 ls = ls_by_name_or_uuid(ctx, ls_name, true);
1773 for (int i = 0; i < ls->n_load_balancer; i++) {
1774 const struct nbrec_load_balancer *lb
1775 = ls->load_balancer[i];
1776 lb_info_add_smap(lb, &lbs);
1777 }
1778
1779 lb_info_print(ctx, &lbs);
1780 smap_destroy(&lbs);
1781}
1782
fa2a27b2
JP
1783static void
1784nbctl_lr_add(struct ctl_context *ctx)
1785{
1786 const char *lr_name = ctx->argc == 2 ? ctx->argv[1] : NULL;
1787
1788 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
1789 bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL;
1790 if (may_exist && add_duplicate) {
1791 ctl_fatal("--may-exist and --add-duplicate may not be used together");
1792 }
1793
1794 if (lr_name) {
1795 if (!add_duplicate) {
1796 const struct nbrec_logical_router *lr;
1797 NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->idl) {
1798 if (!strcmp(lr->name, lr_name)) {
1799 if (may_exist) {
1800 return;
1801 }
1802 ctl_fatal("%s: a router with this name already exists",
1803 lr_name);
1804 }
1805 }
1806 }
1807 } else if (may_exist) {
1808 ctl_fatal("--may-exist requires specifying a name");
1809 } else if (add_duplicate) {
1810 ctl_fatal("--add-duplicate requires specifying a name");
1811 }
1812
1813 struct nbrec_logical_router *lr;
1814 lr = nbrec_logical_router_insert(ctx->txn);
1815 if (lr_name) {
1816 nbrec_logical_router_set_name(lr, lr_name);
1817 }
1818}
1819
1820static void
1821nbctl_lr_del(struct ctl_context *ctx)
1822{
1823 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1824 const char *id = ctx->argv[1];
1825 const struct nbrec_logical_router *lr;
1826
1827 lr = lr_by_name_or_uuid(ctx, id, must_exist);
1828 if (!lr) {
1829 return;
1830 }
1831
1832 nbrec_logical_router_delete(lr);
1833}
1834
1835static void
1836nbctl_lr_list(struct ctl_context *ctx)
1837{
1838 const struct nbrec_logical_router *lr;
1839 struct smap lrs;
1840
1841 smap_init(&lrs);
1842 NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) {
1843 smap_add_format(&lrs, lr->name, UUID_FMT " (%s)",
1844 UUID_ARGS(&lr->header_.uuid), lr->name);
1845 }
1846 const struct smap_node **nodes = smap_sort(&lrs);
1847 for (size_t i = 0; i < smap_count(&lrs); i++) {
1848 const struct smap_node *node = nodes[i];
1849 ds_put_format(&ctx->output, "%s\n", node->value);
1850 }
1851 smap_destroy(&lrs);
1852 free(nodes);
1853}
e48ccf3c 1854
281977f7
NS
1855static const struct nbrec_dhcp_options *
1856dhcp_options_get(struct ctl_context *ctx, const char *id, bool must_exist)
1857{
1858 struct uuid dhcp_opts_uuid;
1859 const struct nbrec_dhcp_options *dhcp_opts = NULL;
1860 if (uuid_from_string(&dhcp_opts_uuid, id)) {
1861 dhcp_opts = nbrec_dhcp_options_get_for_uuid(ctx->idl, &dhcp_opts_uuid);
1862 }
1863
1864 if (!dhcp_opts && must_exist) {
1865 ctl_fatal("%s: dhcp options UUID not found", id);
1866 }
1867 return dhcp_opts;
1868}
1869
1870static void
1871nbctl_dhcp_options_create(struct ctl_context *ctx)
1872{
1873 /* Validate the cidr */
1874 ovs_be32 ip;
1875 unsigned int plen;
1876 char *error = ip_parse_cidr(ctx->argv[1], &ip, &plen);
1877 if (error){
1878 /* check if its IPv6 cidr */
1879 free(error);
1880 struct in6_addr ipv6;
1881 error = ipv6_parse_cidr(ctx->argv[1], &ipv6, &plen);
1882 if (error) {
1883 free(error);
1884 ctl_fatal("Invalid cidr format '%s'", ctx->argv[1]);
281977f7
NS
1885 }
1886 }
1887
1888 struct nbrec_dhcp_options *dhcp_opts = nbrec_dhcp_options_insert(ctx->txn);
1889 nbrec_dhcp_options_set_cidr(dhcp_opts, ctx->argv[1]);
1890
1891 struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
1892 for (size_t i = 2; i < ctx->argc; i++) {
1893 char *key, *value;
1894 value = xstrdup(ctx->argv[i]);
1895 key = strsep(&value, "=");
1896 if (value) {
1897 smap_add(&ext_ids, key, value);
1898 }
1899 free(key);
1900 }
1901
1902 nbrec_dhcp_options_set_external_ids(dhcp_opts, &ext_ids);
1903 smap_destroy(&ext_ids);
1904}
1905
1906static void
1907nbctl_dhcp_options_set_options(struct ctl_context *ctx)
1908{
1909 const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get(
1910 ctx, ctx->argv[1], true);
1911
1912 struct smap dhcp_options = SMAP_INITIALIZER(&dhcp_options);
1913 for (size_t i = 2; i < ctx->argc; i++) {
1914 char *key, *value;
1915 value = xstrdup(ctx->argv[i]);
1916 key = strsep(&value, "=");
1917 if (value) {
1918 smap_add(&dhcp_options, key, value);
1919 }
1920 free(key);
1921 }
1922
1923 nbrec_dhcp_options_set_options(dhcp_opts, &dhcp_options);
1924 smap_destroy(&dhcp_options);
1925}
1926
1927static void
1928nbctl_dhcp_options_get_options(struct ctl_context *ctx)
1929{
1930 const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get(
1931 ctx, ctx->argv[1], true);
1932
1933 struct smap_node *node;
1934 SMAP_FOR_EACH(node, &dhcp_opts->options) {
1935 ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
1936 }
1937}
1938
1939static void
1940nbctl_dhcp_options_del(struct ctl_context *ctx)
1941{
1942 bool must_exist = !shash_find(&ctx->options, "--if-exists");
1943 const char *id = ctx->argv[1];
1944 const struct nbrec_dhcp_options *dhcp_opts;
1945
1946 dhcp_opts = dhcp_options_get(ctx, id, must_exist);
1947 if (!dhcp_opts) {
1948 return;
1949 }
1950
1951 nbrec_dhcp_options_delete(dhcp_opts);
1952}
1953
1954static void
1955nbctl_dhcp_options_list(struct ctl_context *ctx)
1956{
1957 const struct nbrec_dhcp_options *dhcp_opts;
1958 struct smap dhcp_options;
1959
1960 smap_init(&dhcp_options);
1961 NBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opts, ctx->idl) {
1962 smap_add_format(&dhcp_options, dhcp_opts->cidr, UUID_FMT,
1963 UUID_ARGS(&dhcp_opts->header_.uuid));
1964 }
1965 const struct smap_node **nodes = smap_sort(&dhcp_options);
1966 for (size_t i = 0; i < smap_count(&dhcp_options); i++) {
1967 const struct smap_node *node = nodes[i];
1968 ds_put_format(&ctx->output, "%s\n", node->value);
1969 }
1970 smap_destroy(&dhcp_options);
1971 free(nodes);
1972}
1973
e48ccf3c
JP
1974/* The caller must free the returned string. */
1975static char *
1976normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen)
1977{
1978 ovs_be32 network = ipv4 & be32_prefix_mask(plen);
1979 if (plen == 32) {
1980 return xasprintf(IP_FMT, IP_ARGS(network));
1981 } else {
1982 return xasprintf(IP_FMT"/%d", IP_ARGS(network), plen);
1983 }
1984}
1985
1986/* The caller must free the returned string. */
1987static char *
1988normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen)
1989{
1990 char network_s[INET6_ADDRSTRLEN];
1991
1992 struct in6_addr mask = ipv6_create_mask(plen);
1993 struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask);
1994
1995 inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN);
1996 if (plen == 128) {
1997 return xasprintf("%s", network_s);
1998 } else {
1999 return xasprintf("%s/%d", network_s, plen);
2000 }
2001}
2002
2003/* The caller must free the returned string. */
2004static char *
2005normalize_prefix_str(const char *orig_prefix)
2006{
2007 unsigned int plen;
2008 ovs_be32 ipv4;
2009 char *error;
2010
2011 error = ip_parse_cidr(orig_prefix, &ipv4, &plen);
2012 if (!error) {
2013 return normalize_ipv4_prefix(ipv4, plen);
2014 } else {
2015 struct in6_addr ipv6;
2016 free(error);
2017
2018 error = ipv6_parse_cidr(orig_prefix, &ipv6, &plen);
2019 if (error) {
2020 free(error);
2021 return NULL;
2022 }
2023 return normalize_ipv6_prefix(ipv6, plen);
2024 }
2025}
2026\f
2027static void
2028nbctl_lr_route_add(struct ctl_context *ctx)
2029{
2030 const struct nbrec_logical_router *lr;
2031 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
2032 char *prefix, *next_hop;
2033
440a9f4b
GS
2034 const char *policy = shash_find_data(&ctx->options, "--policy");
2035 if (policy && strcmp(policy, "src-ip") && strcmp(policy, "dst-ip")) {
2036 ctl_fatal("bad policy: %s", policy);
2037 }
2038
e48ccf3c
JP
2039 prefix = normalize_prefix_str(ctx->argv[2]);
2040 if (!prefix) {
2041 ctl_fatal("bad prefix argument: %s", ctx->argv[2]);
2042 }
2043
2044 next_hop = normalize_prefix_str(ctx->argv[3]);
2045 if (!next_hop) {
b712dea0 2046 free(prefix);
e48ccf3c
JP
2047 ctl_fatal("bad next hop argument: %s", ctx->argv[3]);
2048 }
2049
2050 if (strchr(prefix, '.')) {
2051 ovs_be32 hop_ipv4;
2052 if (!ip_parse(ctx->argv[3], &hop_ipv4)) {
b712dea0 2053 free(prefix);
2054 free(next_hop);
e48ccf3c
JP
2055 ctl_fatal("bad IPv4 nexthop argument: %s", ctx->argv[3]);
2056 }
2057 } else {
2058 struct in6_addr hop_ipv6;
2059 if (!ipv6_parse(ctx->argv[3], &hop_ipv6)) {
b712dea0 2060 free(prefix);
2061 free(next_hop);
e48ccf3c
JP
2062 ctl_fatal("bad IPv6 nexthop argument: %s", ctx->argv[3]);
2063 }
2064 }
2065
2066 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
2067 for (int i = 0; i < lr->n_static_routes; i++) {
2068 const struct nbrec_logical_router_static_route *route
2069 = lr->static_routes[i];
2070 char *rt_prefix;
2071
2072 rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix);
2073 if (!rt_prefix) {
2074 /* Ignore existing prefix we couldn't parse. */
2075 continue;
2076 }
2077
2078 if (strcmp(rt_prefix, prefix)) {
2079 free(rt_prefix);
2080 continue;
2081 }
2082
2083 if (!may_exist) {
b712dea0 2084 free(next_hop);
85d87d16 2085 free(rt_prefix);
e48ccf3c
JP
2086 ctl_fatal("duplicate prefix: %s", prefix);
2087 }
2088
2089 /* Update the next hop for an existing route. */
2090 nbrec_logical_router_verify_static_routes(lr);
2091 nbrec_logical_router_static_route_verify_ip_prefix(route);
2092 nbrec_logical_router_static_route_verify_nexthop(route);
2093 nbrec_logical_router_static_route_set_ip_prefix(route, prefix);
2094 nbrec_logical_router_static_route_set_nexthop(route, next_hop);
01a528f3
GS
2095 if (ctx->argc == 5) {
2096 nbrec_logical_router_static_route_set_output_port(route,
2097 ctx->argv[4]);
2098 }
440a9f4b
GS
2099 if (policy) {
2100 nbrec_logical_router_static_route_set_policy(route, policy);
2101 }
e48ccf3c
JP
2102 free(rt_prefix);
2103 free(next_hop);
2104 free(prefix);
2105 return;
2106 }
2107
2108 struct nbrec_logical_router_static_route *route;
2109 route = nbrec_logical_router_static_route_insert(ctx->txn);
2110 nbrec_logical_router_static_route_set_ip_prefix(route, prefix);
2111 nbrec_logical_router_static_route_set_nexthop(route, next_hop);
2112 if (ctx->argc == 5) {
2113 nbrec_logical_router_static_route_set_output_port(route, ctx->argv[4]);
2114 }
440a9f4b
GS
2115 if (policy) {
2116 nbrec_logical_router_static_route_set_policy(route, policy);
2117 }
e48ccf3c
JP
2118
2119 nbrec_logical_router_verify_static_routes(lr);
2120 struct nbrec_logical_router_static_route **new_routes
2121 = xmalloc(sizeof *new_routes * (lr->n_static_routes + 1));
2122 memcpy(new_routes, lr->static_routes,
2123 sizeof *new_routes * lr->n_static_routes);
2124 new_routes[lr->n_static_routes] = route;
2125 nbrec_logical_router_set_static_routes(lr, new_routes,
2126 lr->n_static_routes + 1);
2127 free(new_routes);
2128 free(next_hop);
2129 free(prefix);
2130}
2131
2132static void
2133nbctl_lr_route_del(struct ctl_context *ctx)
2134{
2135 const struct nbrec_logical_router *lr;
2136 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
2137
2138 if (ctx->argc == 2) {
2139 /* If a prefix is not specified, delete all routes. */
2140 nbrec_logical_router_set_static_routes(lr, NULL, 0);
2141 return;
2142 }
2143
2144 char *prefix = normalize_prefix_str(ctx->argv[2]);
2145 if (!prefix) {
2146 ctl_fatal("bad prefix argument: %s", ctx->argv[2]);
2147 }
2148
2149 for (int i = 0; i < lr->n_static_routes; i++) {
2150 char *rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix);
2151 if (!rt_prefix) {
2152 /* Ignore existing prefix we couldn't parse. */
2153 continue;
2154 }
2155
2156 if (!strcmp(prefix, rt_prefix)) {
2157 struct nbrec_logical_router_static_route **new_routes
2158 = xmemdup(lr->static_routes,
2159 sizeof *new_routes * lr->n_static_routes);
2160
2161 new_routes[i] = lr->static_routes[lr->n_static_routes - 1];
2162 nbrec_logical_router_verify_static_routes(lr);
2163 nbrec_logical_router_set_static_routes(lr, new_routes,
2164 lr->n_static_routes - 1);
2165 free(new_routes);
2166 free(rt_prefix);
2167 free(prefix);
2168 return;
2169 }
2170 free(rt_prefix);
2171 }
2172
2173 if (!shash_find(&ctx->options, "--if-exists")) {
2174 ctl_fatal("no matching prefix: %s", prefix);
2175 }
2176 free(prefix);
2177}
fa2a27b2 2178\f
31114af7
JP
2179static const struct nbrec_logical_router_port *
2180lrp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist)
2181{
2182 const struct nbrec_logical_router_port *lrp = NULL;
2183
2184 struct uuid lrp_uuid;
2185 bool is_uuid = uuid_from_string(&lrp_uuid, id);
2186 if (is_uuid) {
2187 lrp = nbrec_logical_router_port_get_for_uuid(ctx->idl, &lrp_uuid);
2188 }
2189
2190 if (!lrp) {
2191 NBREC_LOGICAL_ROUTER_PORT_FOR_EACH(lrp, ctx->idl) {
2192 if (!strcmp(lrp->name, id)) {
2193 break;
2194 }
2195 }
2196 }
2197
2198 if (!lrp && must_exist) {
2199 ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name");
2200 }
2201
2202 return lrp;
2203}
2204
2205/* Returns the logical router that contains 'lrp'. */
2206static const struct nbrec_logical_router *
2207lrp_to_lr(const struct ovsdb_idl *idl,
2208 const struct nbrec_logical_router_port *lrp)
2209{
2210 const struct nbrec_logical_router *lr;
2211 NBREC_LOGICAL_ROUTER_FOR_EACH (lr, idl) {
2212 for (size_t i = 0; i < lr->n_ports; i++) {
2213 if (lr->ports[i] == lrp) {
2214 return lr;
2215 }
2216 }
2217 }
2218
2219 /* Can't happen because of the database schema */
2220 ctl_fatal("port %s is not part of any logical router",
2221 lrp->name);
2222}
2223
2224static const char *
2225lr_get_name(const struct nbrec_logical_router *lr, char uuid_s[UUID_LEN + 1],
2226 size_t uuid_s_size)
2227{
2228 if (lr->name[0]) {
2229 return lr->name;
2230 }
2231 snprintf(uuid_s, uuid_s_size, UUID_FMT, UUID_ARGS(&lr->header_.uuid));
2232 return uuid_s;
2233}
2234
2235static void
2236nbctl_lrp_add(struct ctl_context *ctx)
2237{
2238 bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
2239
2240 const struct nbrec_logical_router *lr;
2241 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
2242
2243 const char *lrp_name = ctx->argv[2];
2244 const char *mac = ctx->argv[3];
4685e523
JP
2245 const char **networks = (const char **) &ctx->argv[4];
2246
2247 int n_networks = ctx->argc - 4;
2248 for (int i = 4; i < ctx->argc; i++) {
2249 if (strchr(ctx->argv[i], '=')) {
2250 n_networks = i - 4;
2251 break;
2252 }
2253 }
2254
2255 if (!n_networks) {
2256 ctl_fatal("%s: router port requires specifying a network", lrp_name);
2257 }
2258
2259 char **settings = (char **) &ctx->argv[n_networks + 4];
2260 int n_settings = ctx->argc - 4 - n_networks;
31114af7
JP
2261
2262 const struct nbrec_logical_router_port *lrp;
2263 lrp = lrp_by_name_or_uuid(ctx, lrp_name, false);
2264 if (lrp) {
2265 if (!may_exist) {
2266 ctl_fatal("%s: a port with this name already exists",
2267 lrp_name);
2268 }
2269
2270 const struct nbrec_logical_router *bound_lr;
2271 bound_lr = lrp_to_lr(ctx->idl, lrp);
2272 if (bound_lr != lr) {
2273 char uuid_s[UUID_LEN + 1];
2274 ctl_fatal("%s: port already exists but in router %s", lrp_name,
2275 lr_get_name(bound_lr, uuid_s, sizeof uuid_s));
2276 }
2277
2278 if (strcmp(mac, lrp->mac)) {
2279 ctl_fatal("%s: port already exists with mac %s", lrp_name,
2280 lrp->mac);
2281 }
2282
4685e523
JP
2283 struct sset new_networks = SSET_INITIALIZER(&new_networks);
2284 for (int i = 0; i < n_networks; i++) {
2285 sset_add(&new_networks, networks[i]);
2286 }
2287
2288 struct sset orig_networks = SSET_INITIALIZER(&orig_networks);
2289 sset_add_array(&orig_networks, lrp->networks, lrp->n_networks);
2290
88e5c2a4 2291 bool same_networks = sset_equals(&orig_networks, &new_networks);
2292 sset_destroy(&orig_networks);
2293 sset_destroy(&new_networks);
2294 if (!same_networks) {
4685e523
JP
2295 ctl_fatal("%s: port already exists with different network",
2296 lrp_name);
2297 }
2298
4685e523
JP
2299 /* Special-case sanity-check of peer ports. */
2300 const char *peer = NULL;
2301 for (int i = 0; i < n_settings; i++) {
2302 if (!strncmp(settings[i], "peer=", 5)) {
2303 peer = settings[i] + 5;
2304 break;
2305 }
31114af7
JP
2306 }
2307
2308 if ((!peer != !lrp->peer) ||
2309 (lrp->peer && strcmp(peer, lrp->peer))) {
2310 ctl_fatal("%s: port already exists with mismatching peer",
2311 lrp_name);
2312 }
2313
2314 return;
2315 }
2316
2317 struct eth_addr ea;
10c3fcdf 2318 if (!eth_addr_from_string(mac, &ea)) {
4685e523 2319 ctl_fatal("%s: invalid mac address %s", lrp_name, mac);
31114af7
JP
2320 }
2321
4685e523
JP
2322 for (int i = 0; i < n_networks; i++) {
2323 ovs_be32 ipv4;
2324 unsigned int plen;
2325 char *error = ip_parse_cidr(networks[i], &ipv4, &plen);
31114af7
JP
2326 if (error) {
2327 free(error);
4685e523
JP
2328 struct in6_addr ipv6;
2329 error = ipv6_parse_cidr(networks[i], &ipv6, &plen);
2330 if (error) {
2331 free(error);
2332 ctl_fatal("%s: invalid network address: %s", lrp_name,
2333 networks[i]);
2334 }
31114af7
JP
2335 }
2336 }
2337
2338 /* Create the logical port. */
2339 lrp = nbrec_logical_router_port_insert(ctx->txn);
2340 nbrec_logical_router_port_set_name(lrp, lrp_name);
2341 nbrec_logical_router_port_set_mac(lrp, mac);
4685e523
JP
2342 nbrec_logical_router_port_set_networks(lrp, networks, n_networks);
2343
2344 for (int i = 0; i < n_settings; i++) {
2345 ctl_set_column("Logical_Router_Port", &lrp->header_, settings[i],
2346 ctx->symtab);
31114af7
JP
2347 }
2348
2349 /* Insert the logical port into the logical router. */
2350 nbrec_logical_router_verify_ports(lr);
2351 struct nbrec_logical_router_port **new_ports = xmalloc(sizeof *new_ports *
2352 (lr->n_ports + 1));
2353 memcpy(new_ports, lr->ports, sizeof *new_ports * lr->n_ports);
2354 new_ports[lr->n_ports] = CONST_CAST(struct nbrec_logical_router_port *,
2355 lrp);
2356 nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports + 1);
2357 free(new_ports);
2358}
2359
2360/* Removes logical router port 'lr->ports[idx]'. */
2361static void
2362remove_lrp(const struct nbrec_logical_router *lr, size_t idx)
2363{
2364 const struct nbrec_logical_router_port *lrp = lr->ports[idx];
2365
2366 /* First remove 'lrp' from the array of ports. This is what will
2367 * actually cause the logical port to be deleted when the transaction is
2368 * sent to the database server (due to garbage collection). */
2369 struct nbrec_logical_router_port **new_ports
2370 = xmemdup(lr->ports, sizeof *new_ports * lr->n_ports);
2371 new_ports[idx] = new_ports[lr->n_ports - 1];
2372 nbrec_logical_router_verify_ports(lr);
2373 nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports - 1);
2374 free(new_ports);
2375
2376 /* Delete 'lrp' from the IDL. This won't have a real effect on
2377 * the database server (the IDL will suppress it in fact) but it
2378 * means that it won't show up when we iterate with
2379 * NBREC_LOGICAL_ROUTER_PORT_FOR_EACH later. */
2380 nbrec_logical_router_port_delete(lrp);
2381}
2382
2383static void
2384nbctl_lrp_del(struct ctl_context *ctx)
2385{
2386 bool must_exist = !shash_find(&ctx->options, "--if-exists");
2387 const struct nbrec_logical_router_port *lrp;
2388
2389 lrp = lrp_by_name_or_uuid(ctx, ctx->argv[1], must_exist);
2390 if (!lrp) {
2391 return;
2392 }
2393
2394 /* Find the router that contains 'lrp', then delete it. */
2395 const struct nbrec_logical_router *lr;
2396 NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->idl) {
2397 for (size_t i = 0; i < lr->n_ports; i++) {
2398 if (lr->ports[i] == lrp) {
2399 remove_lrp(lr, i);
2400 return;
2401 }
2402 }
2403 }
2404
2405 /* Can't happen because of the database schema. */
2406 ctl_fatal("logical port %s is not part of any logical router",
2407 ctx->argv[1]);
2408}
2409
2410/* Print a list of logical router ports. */
2411static void
2412nbctl_lrp_list(struct ctl_context *ctx)
2413{
2414 const char *id = ctx->argv[1];
2415 const struct nbrec_logical_router *lr;
2416 struct smap lrps;
2417 size_t i;
2418
2419 lr = lr_by_name_or_uuid(ctx, id, true);
2420
2421 smap_init(&lrps);
2422 for (i = 0; i < lr->n_ports; i++) {
2423 const struct nbrec_logical_router_port *lrp = lr->ports[i];
2424 smap_add_format(&lrps, lrp->name, UUID_FMT " (%s)",
2425 UUID_ARGS(&lrp->header_.uuid), lrp->name);
2426 }
2427 const struct smap_node **nodes = smap_sort(&lrps);
2428 for (i = 0; i < smap_count(&lrps); i++) {
2429 const struct smap_node *node = nodes[i];
2430 ds_put_format(&ctx->output, "%s\n", node->value);
2431 }
2432 smap_destroy(&lrps);
2433 free(nodes);
2434}
2435
2436/* Set the logical router port admin-enabled state. */
2437static void
2438nbctl_lrp_set_enabled(struct ctl_context *ctx)
2439{
2440 const char *id = ctx->argv[1];
2441 const char *state = ctx->argv[2];
2442 const struct nbrec_logical_router_port *lrp;
2443
2444 lrp = lrp_by_name_or_uuid(ctx, id, true);
2445 if (!lrp) {
2446 return;
2447 }
2448
2449 bool enabled = parse_enabled(state);
2450 nbrec_logical_router_port_set_enabled(lrp, &enabled, 1);
2451}
2452
2453/* Print admin-enabled state for logical router port. */
2454static void
2455nbctl_lrp_get_enabled(struct ctl_context *ctx)
2456{
2457 const char *id = ctx->argv[1];
2458 const struct nbrec_logical_router_port *lrp;
2459
2460 lrp = lrp_by_name_or_uuid(ctx, id, true);
2461 if (!lrp) {
2462 return;
2463 }
2464
2465 ds_put_format(&ctx->output, "%s\n",
2466 !lrp->enabled ||
2467 *lrp->enabled ? "enabled" : "disabled");
2468}
2469\f
e48ccf3c 2470struct ipv4_route {
440a9f4b 2471 int priority;
e48ccf3c
JP
2472 ovs_be32 addr;
2473 const struct nbrec_logical_router_static_route *route;
2474};
2475
2476static int
2477ipv4_route_cmp(const void *route1_, const void *route2_)
2478{
2479 const struct ipv4_route *route1p = route1_;
2480 const struct ipv4_route *route2p = route2_;
2481
440a9f4b
GS
2482 if (route1p->priority != route2p->priority) {
2483 return route1p->priority > route2p->priority ? -1 : 1;
e48ccf3c
JP
2484 } else if (route1p->addr != route2p->addr) {
2485 return ntohl(route1p->addr) < ntohl(route2p->addr) ? -1 : 1;
2486 } else {
2487 return 0;
2488 }
2489}
2490
2491struct ipv6_route {
440a9f4b 2492 int priority;
e48ccf3c
JP
2493 struct in6_addr addr;
2494 const struct nbrec_logical_router_static_route *route;
2495};
2496
2497static int
2498ipv6_route_cmp(const void *route1_, const void *route2_)
2499{
2500 const struct ipv6_route *route1p = route1_;
2501 const struct ipv6_route *route2p = route2_;
2502
440a9f4b
GS
2503 if (route1p->priority != route2p->priority) {
2504 return route1p->priority > route2p->priority ? -1 : 1;
e48ccf3c
JP
2505 }
2506 return memcmp(&route1p->addr, &route2p->addr, sizeof(route1p->addr));
2507}
2508
2509static void
2510print_route(const struct nbrec_logical_router_static_route *route, struct ds *s)
2511{
2512
2513 char *prefix = normalize_prefix_str(route->ip_prefix);
2514 char *next_hop = normalize_prefix_str(route->nexthop);
2515 ds_put_format(s, "%25s %25s", prefix, next_hop);
2516 free(prefix);
2517 free(next_hop);
2518
440a9f4b
GS
2519 if (route->policy) {
2520 ds_put_format(s, " %s", route->policy);
2521 } else {
2522 ds_put_format(s, " %s", "dst-ip");
2523 }
2524
e48ccf3c
JP
2525 if (route->output_port) {
2526 ds_put_format(s, " %s", route->output_port);
2527 }
2528 ds_put_char(s, '\n');
2529}
2530
2531static void
2532nbctl_lr_route_list(struct ctl_context *ctx)
2533{
2534 const struct nbrec_logical_router *lr;
2535 struct ipv4_route *ipv4_routes;
2536 struct ipv6_route *ipv6_routes;
2537 size_t n_ipv4_routes = 0;
2538 size_t n_ipv6_routes = 0;
2539
2540 lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
2541
2542 ipv4_routes = xmalloc(sizeof *ipv4_routes * lr->n_static_routes);
2543 ipv6_routes = xmalloc(sizeof *ipv6_routes * lr->n_static_routes);
2544
2545 for (int i = 0; i < lr->n_static_routes; i++) {
2546 const struct nbrec_logical_router_static_route *route
2547 = lr->static_routes[i];
2548 unsigned int plen;
2549 ovs_be32 ipv4;
440a9f4b 2550 const char *policy = route->policy ? route->policy : "dst-ip";
e48ccf3c 2551 char *error;
e48ccf3c
JP
2552 error = ip_parse_cidr(route->ip_prefix, &ipv4, &plen);
2553 if (!error) {
440a9f4b
GS
2554 ipv4_routes[n_ipv4_routes].priority = !strcmp(policy, "dst-ip")
2555 ? (2 * plen) + 1
2556 : 2 * plen;
e48ccf3c
JP
2557 ipv4_routes[n_ipv4_routes].addr = ipv4;
2558 ipv4_routes[n_ipv4_routes].route = route;
2559 n_ipv4_routes++;
2560 } else {
2561 free(error);
2562
2563 struct in6_addr ipv6;
ed46c522
WT
2564 error = ipv6_parse_cidr(route->ip_prefix, &ipv6, &plen);
2565 if (!error) {
440a9f4b
GS
2566 ipv6_routes[n_ipv6_routes].priority = !strcmp(policy, "dst-ip")
2567 ? (2 * plen) + 1
2568 : 2 * plen;
e48ccf3c
JP
2569 ipv6_routes[n_ipv6_routes].addr = ipv6;
2570 ipv6_routes[n_ipv6_routes].route = route;
2571 n_ipv6_routes++;
2572 } else {
2573 /* Invalid prefix. */
2574 VLOG_WARN("router "UUID_FMT" (%s) has invalid prefix: %s",
2575 UUID_ARGS(&lr->header_.uuid), lr->name,
2576 route->ip_prefix);
2577 free(error);
2578 continue;
2579 }
2580 }
2581 }
2582
2583 qsort(ipv4_routes, n_ipv4_routes, sizeof *ipv4_routes, ipv4_route_cmp);
2584 qsort(ipv6_routes, n_ipv6_routes, sizeof *ipv6_routes, ipv6_route_cmp);
2585
2586 if (n_ipv4_routes) {
2587 ds_put_cstr(&ctx->output, "IPv4 Routes\n");
2588 }
2589 for (int i = 0; i < n_ipv4_routes; i++) {
2590 print_route(ipv4_routes[i].route, &ctx->output);
2591 }
2592
2593 if (n_ipv6_routes) {
2594 ds_put_format(&ctx->output, "%sIPv6 Routes\n",
2595 n_ipv4_routes ? "\n" : "");
2596 }
2597 for (int i = 0; i < n_ipv6_routes; i++) {
2598 print_route(ipv6_routes[i].route, &ctx->output);
2599 }
2600
2601 free(ipv4_routes);
2602 free(ipv6_routes);
2603}
2604
70799ae8 2605static const struct ctl_table_class tables[] = {
fa183acc
BP
2606 {&nbrec_table_nb_global,
2607 {{&nbrec_table_nb_global, NULL, NULL},
2608 {NULL, NULL, NULL}}},
2609
70799ae8
BP
2610 {&nbrec_table_logical_switch,
2611 {{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL},
2612 {NULL, NULL, NULL}}},
2613
80f408f4
JP
2614 {&nbrec_table_logical_switch_port,
2615 {{&nbrec_table_logical_switch_port, &nbrec_logical_switch_port_col_name,
2616 NULL},
70799ae8
BP
2617 {NULL, NULL, NULL}}},
2618
2619 {&nbrec_table_acl,
2620 {{NULL, NULL, NULL},
2621 {NULL, NULL, NULL}}},
2622
7a15be69
GS
2623 {&nbrec_table_load_balancer,
2624 {{NULL, NULL, NULL},
2625 {NULL, NULL, NULL}}},
2626
70799ae8
BP
2627 {&nbrec_table_logical_router,
2628 {{&nbrec_table_logical_router, &nbrec_logical_router_col_name, NULL},
2629 {NULL, NULL, NULL}}},
2630
2631 {&nbrec_table_logical_router_port,
2632 {{&nbrec_table_logical_router_port, &nbrec_logical_router_port_col_name,
2633 NULL},
2634 {NULL, NULL, NULL}}},
2635
28dc3fe9
SR
2636 {&nbrec_table_logical_router_static_route,
2637 {{&nbrec_table_logical_router_static_route, NULL,
2638 NULL},
2639 {NULL, NULL, NULL}}},
2640
de297547
GS
2641 {&nbrec_table_nat,
2642 {{&nbrec_table_nat, NULL,
2643 NULL},
2644 {NULL, NULL, NULL}}},
2645
ea382567
RB
2646 {&nbrec_table_address_set,
2647 {{&nbrec_table_address_set, &nbrec_address_set_col_name, NULL},
2648 {NULL, NULL, NULL}}},
2649
281977f7
NS
2650 {&nbrec_table_dhcp_options,
2651 {{&nbrec_table_dhcp_options, NULL,
2652 NULL},
2653 {NULL, NULL, NULL}}},
2654
1a03fc7d
BS
2655 {&nbrec_table_qos,
2656 {{&nbrec_table_qos, NULL,
2657 NULL},
2658 {NULL, NULL, NULL}}},
2659
70799ae8
BP
2660 {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
2661};
2662\f
a416ff28 2663static void
70799ae8
BP
2664run_prerequisites(struct ctl_command *commands, size_t n_commands,
2665 struct ovsdb_idl *idl)
a416ff28 2666{
fa183acc
BP
2667 ovsdb_idl_add_table(idl, &nbrec_table_nb_global);
2668 if (wait_type == NBCTL_WAIT_SB) {
2669 ovsdb_idl_add_column(idl, &nbrec_nb_global_col_sb_cfg);
2670 } else if (wait_type == NBCTL_WAIT_HV) {
2671 ovsdb_idl_add_column(idl, &nbrec_nb_global_col_hv_cfg);
2672 }
a416ff28 2673
fa183acc 2674 for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) {
70799ae8
BP
2675 if (c->syntax->prerequisites) {
2676 struct ctl_context ctx;
a416ff28 2677
70799ae8
BP
2678 ds_init(&c->output);
2679 c->table = NULL;
a416ff28 2680
70799ae8
BP
2681 ctl_context_init(&ctx, c, idl, NULL, NULL, NULL);
2682 (c->syntax->prerequisites)(&ctx);
2683 ctl_context_done(&ctx, c);
a416ff28 2684
70799ae8
BP
2685 ovs_assert(!c->output.string);
2686 ovs_assert(!c->table);
2687 }
2688 }
2689}
a416ff28 2690
8ee574c3 2691static bool
70799ae8
BP
2692do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands,
2693 struct ovsdb_idl *idl)
2694{
2695 struct ovsdb_idl_txn *txn;
2696 enum ovsdb_idl_txn_status status;
2697 struct ovsdb_symbol_table *symtab;
2698 struct ctl_context ctx;
2699 struct ctl_command *c;
2700 struct shash_node *node;
fa183acc 2701 int64_t next_cfg = 0;
70799ae8
BP
2702 char *error = NULL;
2703
2704 txn = the_idl_txn = ovsdb_idl_txn_create(idl);
2705 if (dry_run) {
2706 ovsdb_idl_txn_set_dry_run(txn);
2707 }
2708
2709 ovsdb_idl_txn_add_comment(txn, "ovs-nbctl: %s", args);
2710
fa183acc
BP
2711 const struct nbrec_nb_global *nb = nbrec_nb_global_first(idl);
2712 if (!nb) {
2713 /* XXX add verification that table is empty */
2714 nb = nbrec_nb_global_insert(txn);
2715 }
2716
2717 if (wait_type != NBCTL_WAIT_NONE) {
de32cec7
BP
2718 ovsdb_idl_txn_increment(txn, &nb->header_, &nbrec_nb_global_col_nb_cfg,
2719 force_wait);
fa183acc
BP
2720 }
2721
70799ae8
BP
2722 symtab = ovsdb_symbol_table_create();
2723 for (c = commands; c < &commands[n_commands]; c++) {
2724 ds_init(&c->output);
2725 c->table = NULL;
2726 }
2727 ctl_context_init(&ctx, NULL, idl, txn, symtab, NULL);
2728 for (c = commands; c < &commands[n_commands]; c++) {
2729 ctl_context_init_command(&ctx, c);
2730 if (c->syntax->run) {
2731 (c->syntax->run)(&ctx);
2732 }
2733 ctl_context_done_command(&ctx, c);
a416ff28 2734
70799ae8
BP
2735 if (ctx.try_again) {
2736 ctl_context_done(&ctx, NULL);
2737 goto try_again;
2738 }
2739 }
2740 ctl_context_done(&ctx, NULL);
a416ff28 2741
70799ae8
BP
2742 SHASH_FOR_EACH (node, &symtab->sh) {
2743 struct ovsdb_symbol *symbol = node->data;
2744 if (!symbol->created) {
2745 ctl_fatal("row id \"%s\" is referenced but never created (e.g. "
2746 "with \"-- --id=%s create ...\")",
2747 node->name, node->name);
2748 }
2749 if (!symbol->strong_ref) {
2750 if (!symbol->weak_ref) {
2751 VLOG_WARN("row id \"%s\" was created but no reference to it "
2752 "was inserted, so it will not actually appear in "
2753 "the database", node->name);
2754 } else {
2755 VLOG_WARN("row id \"%s\" was created but only a weak "
2756 "reference to it was inserted, so it will not "
2757 "actually appear in the database", node->name);
2758 }
a416ff28
RB
2759 }
2760 }
2761
70799ae8 2762 status = ovsdb_idl_txn_commit_block(txn);
fa183acc
BP
2763 if (wait_type != NBCTL_WAIT_NONE && status == TXN_SUCCESS) {
2764 next_cfg = ovsdb_idl_txn_get_increment_new_value(txn);
2765 }
70799ae8
BP
2766 if (status == TXN_UNCHANGED || status == TXN_SUCCESS) {
2767 for (c = commands; c < &commands[n_commands]; c++) {
2768 if (c->syntax->postprocess) {
2769 ctl_context_init(&ctx, c, idl, txn, symtab, NULL);
2770 (c->syntax->postprocess)(&ctx);
2771 ctl_context_done(&ctx, c);
2772 }
2773 }
a416ff28 2774 }
70799ae8 2775 error = xstrdup(ovsdb_idl_txn_get_error(txn));
a416ff28 2776
70799ae8
BP
2777 switch (status) {
2778 case TXN_UNCOMMITTED:
2779 case TXN_INCOMPLETE:
2780 OVS_NOT_REACHED();
a416ff28 2781
70799ae8
BP
2782 case TXN_ABORTED:
2783 /* Should not happen--we never call ovsdb_idl_txn_abort(). */
2784 ctl_fatal("transaction aborted");
a416ff28 2785
70799ae8
BP
2786 case TXN_UNCHANGED:
2787 case TXN_SUCCESS:
2788 break;
a416ff28 2789
70799ae8
BP
2790 case TXN_TRY_AGAIN:
2791 goto try_again;
a416ff28 2792
70799ae8
BP
2793 case TXN_ERROR:
2794 ctl_fatal("transaction error: %s", error);
a416ff28 2795
70799ae8
BP
2796 case TXN_NOT_LOCKED:
2797 /* Should not happen--we never call ovsdb_idl_set_lock(). */
2798 ctl_fatal("database not locked");
a416ff28 2799
70799ae8
BP
2800 default:
2801 OVS_NOT_REACHED();
2802 }
2803 free(error);
afc9da0b 2804
70799ae8 2805 ovsdb_symbol_table_destroy(symtab);
a416ff28 2806
70799ae8
BP
2807 for (c = commands; c < &commands[n_commands]; c++) {
2808 struct ds *ds = &c->output;
a416ff28 2809
70799ae8
BP
2810 if (c->table) {
2811 table_print(c->table, &table_style);
2812 } else if (oneline) {
2813 size_t j;
a416ff28 2814
70799ae8
BP
2815 ds_chomp(ds, '\n');
2816 for (j = 0; j < ds->length; j++) {
2817 int ch = ds->string[j];
2818 switch (ch) {
2819 case '\n':
2820 fputs("\\n", stdout);
2821 break;
2822
2823 case '\\':
2824 fputs("\\\\", stdout);
2825 break;
2826
2827 default:
2828 putchar(ch);
2829 }
a416ff28 2830 }
70799ae8
BP
2831 putchar('\n');
2832 } else {
2833 fputs(ds_cstr(ds), stdout);
a416ff28 2834 }
70799ae8
BP
2835 ds_destroy(&c->output);
2836 table_destroy(c->table);
2837 free(c->table);
a416ff28 2838
70799ae8 2839 shash_destroy_free_data(&c->options);
a416ff28 2840 }
70799ae8 2841 free(commands);
fa183acc
BP
2842
2843 if (wait_type != NBCTL_WAIT_NONE && status != TXN_UNCHANGED) {
2844 ovsdb_idl_enable_reconnect(idl);
2845 for (;;) {
2846 ovsdb_idl_run(idl);
2847 NBREC_NB_GLOBAL_FOR_EACH (nb, idl) {
2848 int64_t cur_cfg = (wait_type == NBCTL_WAIT_SB
2849 ? nb->sb_cfg
2850 : nb->hv_cfg);
2851 if (cur_cfg >= next_cfg) {
2852 goto done;
2853 }
2854 }
2855 ovsdb_idl_wait(idl);
2856 poll_block();
2857 }
2858 done: ;
2859 }
2860
70799ae8
BP
2861 ovsdb_idl_txn_destroy(txn);
2862 ovsdb_idl_destroy(idl);
2863
8ee574c3 2864 return true;
a416ff28 2865
70799ae8
BP
2866try_again:
2867 /* Our transaction needs to be rerun, or a prerequisite was not met. Free
2868 * resources and return so that the caller can try again. */
2869 if (txn) {
2870 ovsdb_idl_txn_abort(txn);
2871 ovsdb_idl_txn_destroy(txn);
2872 the_idl_txn = NULL;
a416ff28 2873 }
70799ae8
BP
2874 ovsdb_symbol_table_destroy(symtab);
2875 for (c = commands; c < &commands[n_commands]; c++) {
2876 ds_destroy(&c->output);
2877 table_destroy(c->table);
2878 free(c->table);
2879 }
2880 free(error);
8ee574c3 2881 return false;
70799ae8
BP
2882}
2883
2884/* Frees the current transaction and the underlying IDL and then calls
2885 * exit(status).
2886 *
2887 * Freeing the transaction and the IDL is not strictly necessary, but it makes
2888 * for a clean memory leak report from valgrind in the normal case. That makes
2889 * it easier to notice real memory leaks. */
2890static void
2891nbctl_exit(int status)
2892{
2893 if (the_idl_txn) {
2894 ovsdb_idl_txn_abort(the_idl_txn);
2895 ovsdb_idl_txn_destroy(the_idl_txn);
2896 }
2897 ovsdb_idl_destroy(the_idl);
2898 exit(status);
2899}
2900
2901static const struct ctl_command_syntax nbctl_commands[] = {
fa183acc 2902 { "init", 0, 0, "", NULL, nbctl_init, NULL, "", RW },
de32cec7 2903 { "sync", 0, 0, "", nbctl_pre_sync, nbctl_sync, NULL, "", RO },
ea46a4e9 2904 { "show", 0, 1, "[SWITCH]", NULL, nbctl_show, NULL, "", RO },
70799ae8 2905
ea46a4e9
JP
2906 /* logical switch commands. */
2907 { "ls-add", 0, 1, "[SWITCH]", NULL, nbctl_ls_add, NULL,
2908 "--may-exist,--add-duplicate", RW },
2909 { "ls-del", 1, 1, "SWITCH", NULL, nbctl_ls_del, NULL, "--if-exists", RW },
2910 { "ls-list", 0, 0, "", NULL, nbctl_ls_list, NULL, "", RO },
70799ae8
BP
2911
2912 /* acl commands. */
ea46a4e9 2913 { "acl-add", 5, 5, "SWITCH DIRECTION PRIORITY MATCH ACTION", NULL,
70799ae8 2914 nbctl_acl_add, NULL, "--log", RW },
ea46a4e9 2915 { "acl-del", 1, 4, "SWITCH [DIRECTION [PRIORITY MATCH]]", NULL,
70799ae8 2916 nbctl_acl_del, NULL, "", RW },
ea46a4e9 2917 { "acl-list", 1, 1, "SWITCH", NULL, nbctl_acl_list, NULL, "", RO },
70799ae8 2918
80f408f4 2919 /* logical switch port commands. */
ea46a4e9 2920 { "lsp-add", 2, 4, "SWITCH PORT [PARENT] [TAG]", NULL, nbctl_lsp_add,
d24d3372 2921 NULL, "--may-exist", RW },
31ed1192 2922 { "lsp-del", 1, 1, "PORT", NULL, nbctl_lsp_del, NULL, "--if-exists", RW },
ea46a4e9 2923 { "lsp-list", 1, 1, "SWITCH", NULL, nbctl_lsp_list, NULL, "", RO },
31ed1192 2924 { "lsp-get-parent", 1, 1, "PORT", NULL, nbctl_lsp_get_parent, NULL,
70799ae8 2925 "", RO },
31ed1192
JP
2926 { "lsp-get-tag", 1, 1, "PORT", NULL, nbctl_lsp_get_tag, NULL, "", RO },
2927 { "lsp-set-addresses", 1, INT_MAX, "PORT [ADDRESS]...", NULL,
80f408f4 2928 nbctl_lsp_set_addresses, NULL, "", RW },
31ed1192 2929 { "lsp-get-addresses", 1, 1, "PORT", NULL, nbctl_lsp_get_addresses, NULL,
70799ae8 2930 "", RO },
31ed1192 2931 { "lsp-set-port-security", 0, INT_MAX, "PORT [ADDRS]...", NULL,
80f408f4 2932 nbctl_lsp_set_port_security, NULL, "", RW },
31ed1192 2933 { "lsp-get-port-security", 1, 1, "PORT", NULL,
80f408f4 2934 nbctl_lsp_get_port_security, NULL, "", RO },
31ed1192
JP
2935 { "lsp-get-up", 1, 1, "PORT", NULL, nbctl_lsp_get_up, NULL, "", RO },
2936 { "lsp-set-enabled", 2, 2, "PORT STATE", NULL, nbctl_lsp_set_enabled,
70799ae8 2937 NULL, "", RW },
31ed1192 2938 { "lsp-get-enabled", 1, 1, "PORT", NULL, nbctl_lsp_get_enabled, NULL,
70799ae8 2939 "", RO },
31ed1192 2940 { "lsp-set-type", 2, 2, "PORT TYPE", NULL, nbctl_lsp_set_type, NULL,
70799ae8 2941 "", RW },
31ed1192
JP
2942 { "lsp-get-type", 1, 1, "PORT", NULL, nbctl_lsp_get_type, NULL, "", RO },
2943 { "lsp-set-options", 1, INT_MAX, "PORT KEY=VALUE [KEY=VALUE]...", NULL,
80f408f4 2944 nbctl_lsp_set_options, NULL, "", RW },
31ed1192 2945 { "lsp-get-options", 1, 1, "PORT", NULL, nbctl_lsp_get_options, NULL,
70799ae8 2946 "", RO },
281977f7
NS
2947 { "lsp-set-dhcpv4-options", 1, 2, "PORT [DHCP_OPT_UUID]", NULL,
2948 nbctl_lsp_set_dhcpv4_options, NULL, "", RW },
2949 { "lsp-get-dhcpv4-options", 1, 1, "PORT", NULL,
2950 nbctl_lsp_get_dhcpv4_options, NULL, "", RO },
70799ae8 2951
fa2a27b2
JP
2952 /* logical router commands. */
2953 { "lr-add", 0, 1, "[ROUTER]", NULL, nbctl_lr_add, NULL,
2954 "--may-exist,--add-duplicate", RW },
2955 { "lr-del", 1, 1, "ROUTER", NULL, nbctl_lr_del, NULL, "--if-exists", RW },
2956 { "lr-list", 0, 0, "", NULL, nbctl_lr_list, NULL, "", RO },
2957
31114af7 2958 /* logical router port commands. */
4685e523
JP
2959 { "lrp-add", 4, INT_MAX,
2960 "ROUTER PORT MAC NETWORK... [COLUMN[:KEY]=VALUE]...",
2961 NULL, nbctl_lrp_add, NULL, "--may-exist", RW },
2962 { "lrp-del", 1, 1, "PORT", NULL, nbctl_lrp_del, NULL, "--if-exists", RW },
31114af7
JP
2963 { "lrp-list", 1, 1, "ROUTER", NULL, nbctl_lrp_list, NULL, "", RO },
2964 { "lrp-set-enabled", 2, 2, "PORT STATE", NULL, nbctl_lrp_set_enabled,
2965 NULL, "", RW },
2966 { "lrp-get-enabled", 1, 1, "PORT", NULL, nbctl_lrp_get_enabled,
2967 NULL, "", RO },
2968
e48ccf3c
JP
2969 /* logical router route commands. */
2970 { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL,
440a9f4b 2971 nbctl_lr_route_add, NULL, "--may-exist,--policy=", RW },
e48ccf3c
JP
2972 { "lr-route-del", 1, 2, "ROUTER [PREFIX]", NULL, nbctl_lr_route_del,
2973 NULL, "--if-exists", RW },
2974 { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL,
2975 "", RO },
2976
e2bfcad6 2977 /* load balancer commands. */
2978 { "lb-add", 3, 4, "LB VIP[:PORT] IP[:PORT]... [PROTOCOL]", NULL,
2979 nbctl_lb_add, NULL, "--may-exist,--add-duplicate", RW },
2980 { "lb-del", 1, 2, "LB [VIP]", NULL, nbctl_lb_del, NULL,
2981 "--if-exists", RW },
2982 { "lb-list", 0, 1, "[LB]", NULL, nbctl_lb_list, NULL, "", RO },
2983 { "lr-lb-add", 2, 2, "ROUTER LB", NULL, nbctl_lr_lb_add, NULL,
2984 "--may-exist", RW },
2985 { "lr-lb-del", 1, 2, "ROUTER [LB]", NULL, nbctl_lr_lb_del, NULL,
2986 "--if-exists", RW },
2987 { "lr-lb-list", 1, 1, "ROUTER", NULL, nbctl_lr_lb_list, NULL,
2988 "", RO },
2989 { "ls-lb-add", 2, 2, "SWITCH LB", NULL, nbctl_ls_lb_add, NULL,
2990 "--may-exist", RW },
2991 { "ls-lb-del", 1, 2, "SWITCH [LB]", NULL, nbctl_ls_lb_del, NULL,
2992 "--if-exists", RW },
2993 { "ls-lb-list", 1, 1, "SWITCH", NULL, nbctl_ls_lb_list, NULL,
2994 "", RO },
2995
281977f7
NS
2996 /* DHCP_Options commands */
2997 {"dhcp-options-create", 1, INT_MAX, "CIDR [EXTERNAL:IDS]", NULL,
2998 nbctl_dhcp_options_create, NULL, "", RW },
2999 {"dhcp-options-del", 1, 1, "DHCP_OPT_UUID", NULL,
3000 nbctl_dhcp_options_del, NULL, "", RW},
3001 {"dhcp-options-list", 0, 0, "", NULL, nbctl_dhcp_options_list, NULL, "", RO},
3002 {"dhcp-options-set-options", 1, INT_MAX, "DHCP_OPT_UUID KEY=VALUE [KEY=VALUE]...",
3003 NULL, nbctl_dhcp_options_set_options, NULL, "", RW },
3004 {"dhcp-options-get-options", 1, 1, "DHCP_OPT_UUID", NULL,
3005 nbctl_dhcp_options_get_options, NULL, "", RO },
3006
70799ae8
BP
3007 {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO},
3008};
a416ff28 3009
70799ae8
BP
3010/* Registers nbctl and common db commands. */
3011static void
3012nbctl_cmd_init(void)
3013{
3014 ctl_init(tables, NULL, nbctl_exit);
3015 ctl_register_commands(nbctl_commands);
a416ff28 3016}