ovn-nbctl: Enable database commands using db-ctl-base infrastructure.
[cascardo/ovs.git] / ovn / utilities / ovn-nbctl.c
1 /*
2  * Licensed under the Apache License, Version 2.0 (the "License");
3  * you may not use this file except in compliance with the License.
4  * You may obtain a copy of the License at:
5  *
6  *     http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14
15 #include <config.h>
16
17 #include <getopt.h>
18 #include <inttypes.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21
22 #include "command-line.h"
23 #include "db-ctl-base.h"
24 #include "dirs.h"
25 #include "fatal-signal.h"
26 #include "json.h"
27 #include "ovn/lib/ovn-nb-idl.h"
28 #include "poll-loop.h"
29 #include "process.h"
30 #include "smap.h"
31 #include "stream.h"
32 #include "stream-ssl.h"
33 #include "svec.h"
34 #include "table.h"
35 #include "timeval.h"
36 #include "util.h"
37 #include "openvswitch/vlog.h"
38
39 VLOG_DEFINE_THIS_MODULE(nbctl);
40
41 /* --db: The database server to contact. */
42 static const char *db;
43
44 /* --oneline: Write each command's output as a single line? */
45 static bool oneline;
46
47 /* --dry-run: Do not commit any changes. */
48 static bool dry_run;
49
50 /* --timeout: Time to wait for a connection to 'db'. */
51 static int timeout;
52
53 /* Format for table output. */
54 static struct table_style table_style = TABLE_STYLE_DEFAULT;
55
56 /* The IDL we're using and the current transaction, if any.
57  * This is for use by nbctl_exit() only, to allow it to clean up.
58  * Other code should use its context arguments. */
59 static struct ovsdb_idl *the_idl;
60 static struct ovsdb_idl_txn *the_idl_txn;
61 OVS_NO_RETURN static void nbctl_exit(int status);
62
63 static void nbctl_cmd_init(void);
64 OVS_NO_RETURN static void usage(void);
65 static void parse_options(int argc, char *argv[], struct shash *local_options);
66 static const char *nbctl_default_db(void);
67 static void run_prerequisites(struct ctl_command[], size_t n_commands,
68                               struct ovsdb_idl *);
69 static void do_nbctl(const char *args, struct ctl_command *, size_t n,
70                      struct ovsdb_idl *);
71
72 int
73 main(int argc, char *argv[])
74 {
75     extern struct vlog_module VLM_reconnect;
76     struct ovsdb_idl *idl;
77     struct ctl_command *commands;
78     struct shash local_options;
79     unsigned int seqno;
80     size_t n_commands;
81     char *args;
82
83     set_program_name(argv[0]);
84     fatal_ignore_sigpipe();
85     vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
86     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
87     nbrec_init();
88
89     nbctl_cmd_init();
90
91     /* Log our arguments.  This is often valuable for debugging systems. */
92     args = process_escape_args(argv);
93     VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG,
94          "Called as %s", args);
95
96     /* Parse command line. */
97     shash_init(&local_options);
98     parse_options(argc, argv, &local_options);
99     commands = ctl_parse_commands(argc - optind, argv + optind, &local_options,
100                                   &n_commands);
101
102     if (timeout) {
103         time_alarm(timeout);
104     }
105
106     /* Initialize IDL. */
107     idl = the_idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false);
108     run_prerequisites(commands, n_commands, idl);
109
110     /* Execute the commands.
111      *
112      * 'seqno' is the database sequence number for which we last tried to
113      * execute our transaction.  There's no point in trying to commit more than
114      * once for any given sequence number, because if the transaction fails
115      * it's because the database changed and we need to obtain an up-to-date
116      * view of the database before we try the transaction again. */
117     seqno = ovsdb_idl_get_seqno(idl);
118     for (;;) {
119         ovsdb_idl_run(idl);
120         if (!ovsdb_idl_is_alive(idl)) {
121             int retval = ovsdb_idl_get_last_error(idl);
122             ctl_fatal("%s: database connection failed (%s)",
123                         db, ovs_retval_to_string(retval));
124         }
125
126         if (seqno != ovsdb_idl_get_seqno(idl)) {
127             seqno = ovsdb_idl_get_seqno(idl);
128             do_nbctl(args, commands, n_commands, idl);
129         }
130
131         if (seqno == ovsdb_idl_get_seqno(idl)) {
132             ovsdb_idl_wait(idl);
133             poll_block();
134         }
135     }
136 }
137
138 static const char *
139 nbctl_default_db(void)
140 {
141     static char *def;
142     if (!def) {
143         def = getenv("OVN_NB_DB");
144         if (!def) {
145             def = xasprintf("unix:%s/db.sock", ovs_rundir());
146         }
147     }
148     return def;
149 }
150
151 static void
152 parse_options(int argc, char *argv[], struct shash *local_options)
153 {
154     enum {
155         OPT_DB = UCHAR_MAX + 1,
156         OPT_NO_SYSLOG,
157         OPT_DRY_RUN,
158         OPT_ONELINE,
159         OPT_LOCAL,
160         OPT_COMMANDS,
161         OPT_OPTIONS,
162         VLOG_OPTION_ENUMS,
163         TABLE_OPTION_ENUMS
164     };
165     static const struct option global_long_options[] = {
166         {"db", required_argument, NULL, OPT_DB},
167         {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG},
168         {"dry-run", no_argument, NULL, OPT_DRY_RUN},
169         {"oneline", no_argument, NULL, OPT_ONELINE},
170         {"timeout", required_argument, NULL, 't'},
171         {"help", no_argument, NULL, 'h'},
172         {"commands", no_argument, NULL, OPT_COMMANDS},
173         {"options", no_argument, NULL, OPT_OPTIONS},
174         {"version", no_argument, NULL, 'V'},
175         VLOG_LONG_OPTIONS,
176         STREAM_SSL_LONG_OPTIONS,
177         TABLE_LONG_OPTIONS,
178         {NULL, 0, NULL, 0},
179     };
180     const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
181     char *tmp, *short_options;
182
183     struct option *options;
184     size_t allocated_options;
185     size_t n_options;
186     size_t i;
187
188     tmp = ovs_cmdl_long_options_to_short_options(global_long_options);
189     short_options = xasprintf("+%s", tmp);
190     free(tmp);
191
192     /* We want to parse both global and command-specific options here, but
193      * getopt_long() isn't too convenient for the job.  We copy our global
194      * options into a dynamic array, then append all of the command-specific
195      * options. */
196     options = xmemdup(global_long_options, sizeof global_long_options);
197     allocated_options = ARRAY_SIZE(global_long_options);
198     n_options = n_global_long_options;
199     ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL);
200     table_style.format = TF_LIST;
201
202     for (;;) {
203         int idx;
204         int c;
205
206         c = getopt_long(argc, argv, short_options, options, &idx);
207         if (c == -1) {
208             break;
209         }
210
211         switch (c) {
212         case OPT_DB:
213             db = optarg;
214             break;
215
216         case OPT_ONELINE:
217             oneline = true;
218             break;
219
220         case OPT_NO_SYSLOG:
221             vlog_set_levels(&VLM_nbctl, VLF_SYSLOG, VLL_WARN);
222             break;
223
224         case OPT_DRY_RUN:
225             dry_run = true;
226             break;
227
228         case OPT_LOCAL:
229             if (shash_find(local_options, options[idx].name)) {
230                 ctl_fatal("'%s' option specified multiple times",
231                             options[idx].name);
232             }
233             shash_add_nocopy(local_options,
234                              xasprintf("--%s", options[idx].name),
235                              optarg ? xstrdup(optarg) : NULL);
236             break;
237
238         case 'h':
239             usage();
240             exit(EXIT_SUCCESS);
241
242         case OPT_COMMANDS:
243             ctl_print_commands();
244
245         case OPT_OPTIONS:
246             ctl_print_options(global_long_options);
247
248         case 'V':
249             ovs_print_version(0, 0);
250             printf("DB Schema %s\n", nbrec_get_db_version());
251             exit(EXIT_SUCCESS);
252
253         case 't':
254             timeout = strtoul(optarg, NULL, 10);
255             if (timeout < 0) {
256                 ctl_fatal("value %s on -t or --timeout is invalid", optarg);
257             }
258             break;
259
260         VLOG_OPTION_HANDLERS
261         TABLE_OPTION_HANDLERS(&table_style)
262         STREAM_SSL_OPTION_HANDLERS
263
264         case '?':
265             exit(EXIT_FAILURE);
266
267         default:
268             abort();
269         }
270     }
271
272     if (!db) {
273         db = nbctl_default_db();
274     }
275
276     for (i = n_global_long_options; options[i].name; i++) {
277         free(CONST_CAST(char *, options[i].name));
278     }
279     free(options);
280 }
281
282 static void
283 usage(void)
284 {
285     printf("\
286 %s: OVN northbound DB management utility\n\
287 usage: %s [OPTIONS] COMMAND [ARG...]\n\
288 \n\
289 General commands:\n\
290   show                      print overview of database contents\n\
291   show LSWITCH              print overview of database contents for LSWITCH\n\
292 \n\
293 Logical switch commands:\n\
294   lswitch-add [LSWITCH]     create a logical switch named LSWITCH\n\
295   lswitch-del LSWITCH       delete LSWITCH and all its ports\n\
296   lswitch-list              print the names of all logical switches\n\
297   lswitch-set-external-id LSWITCH KEY [VALUE]\n\
298                             set or delete an external-id on LSWITCH\n\
299   lswitch-get-external-id LSWITCH [KEY]\n\
300                             list one or all external-ids on LSWITCH\n\
301 \n\
302 ACL commands:\n\
303   acl-add LSWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\
304                             add an ACL to LSWITCH\n\
305   acl-del LSWITCH [DIRECTION [PRIORITY MATCH]]\n\
306                             remove ACLs from LSWITCH\n\
307   acl-list LSWITCH          print ACLs for LSWITCH\n\
308 \n\
309 Logical port commands:\n\
310   lport-add LSWITCH LPORT   add logical port LPORT on LSWITCH\n\
311   lport-add LSWITCH LPORT PARENT TAG\n\
312                             add logical port LPORT on LSWITCH with PARENT\n\
313                             on TAG\n\
314   lport-del LPORT           delete LPORT from its attached switch\n\
315   lport-list LSWITCH        print the names of all logical ports on LSWITCH\n\
316   lport-get-parent LPORT    get the parent of LPORT if set\n\
317   lport-get-tag LPORT       get the LPORT's tag if set\n\
318   lport-set-external-id LPORT KEY [VALUE]\n\
319                             set or delete an external-id on LPORT\n\
320   lport-get-external-id LPORT [KEY]\n\
321                             list one or all external-ids on LPORT\n\
322   lport-set-macs LPORT [MAC]...\n\
323                             set MAC addresses for LPORT.\n\
324   lport-get-macs LPORT      get a list of MAC addresses on LPORT\n\
325   lport-set-port-security LPORT [ADDRS]...\n\
326                             set port security addresses for LPORT.\n\
327   lport-get-port-security LPORT    get LPORT's port security addresses\n\
328   lport-get-up LPORT        get state of LPORT ('up' or 'down')\n\
329   lport-set-enabled LPORT STATE\n\
330                             set administrative state LPORT\n\
331                             ('enabled' or 'disabled')\n\
332   lport-get-enabled LPORT   get administrative state LPORT\n\
333                             ('enabled' or 'disabled')\n\
334   lport-set-type LPORT TYPE Set the type for LPORT\n\
335   lport-get-type LPORT      Get the type for LPORT\n\
336   lport-set-options LPORT KEY=VALUE [KEY=VALUE]...\n\
337                             Set options related to the type of LPORT\n\
338   lport-get-options LPORT   Get the type specific options for LPORT\n\
339 \n\
340 Options:\n\
341   --db=DATABASE               connect to DATABASE\n\
342                               (default: %s)\n\
343   -t, --timeout=SECS          wait at most SECS seconds\n\
344   --dry-run                   do not commit changes to database\n\
345   --oneline                   print exactly one line of output per command\n",
346            program_name, program_name, nbctl_default_db());
347     vlog_usage();
348     printf("\
349   --no-syslog             equivalent to --verbose=nbctl:syslog:warn\n");
350     printf("\n\
351 Other options:\n\
352   -h, --help                  display this help message\n\
353   -V, --version               display version information\n");
354     exit(EXIT_SUCCESS);
355 }
356 \f
357 static const struct nbrec_logical_switch *
358 lswitch_by_name_or_uuid(struct ctl_context *ctx, const char *id)
359 {
360     const struct nbrec_logical_switch *lswitch = NULL;
361     bool is_uuid = false;
362     bool duplicate = false;
363     struct uuid lswitch_uuid;
364
365     if (uuid_from_string(&lswitch_uuid, id)) {
366         is_uuid = true;
367         lswitch = nbrec_logical_switch_get_for_uuid(ctx->idl,
368                                                     &lswitch_uuid);
369     }
370
371     if (!lswitch) {
372         const struct nbrec_logical_switch *iter;
373
374         NBREC_LOGICAL_SWITCH_FOR_EACH(iter, ctx->idl) {
375             if (strcmp(iter->name, id)) {
376                 continue;
377             }
378             if (lswitch) {
379                 VLOG_WARN("There is more than one logical switch named '%s'. "
380                         "Use a UUID.", id);
381                 lswitch = NULL;
382                 duplicate = true;
383                 break;
384             }
385             lswitch = iter;
386         }
387     }
388
389     if (!lswitch && !duplicate) {
390         VLOG_WARN("lswitch not found for %s: '%s'",
391                 is_uuid ? "UUID" : "name", id);
392     }
393
394     return lswitch;
395 }
396
397 static void
398 print_lswitch(const struct nbrec_logical_switch *lswitch, struct ds *s)
399 {
400     ds_put_format(s, "    lswitch "UUID_FMT" (%s)\n",
401                   UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
402
403     for (size_t i = 0; i < lswitch->n_ports; i++) {
404         const struct nbrec_logical_port *lport = lswitch->ports[i];
405
406         ds_put_format(s, "        lport %s\n", lport->name);
407         if (lport->parent_name && lport->n_tag) {
408             ds_put_format(s, "            parent: %s, tag:%"PRIu64"\n",
409                           lport->parent_name, lport->tag[0]);
410         }
411         if (lport->n_macs) {
412             ds_put_cstr(s, "            macs:");
413             for (size_t j = 0; j < lport->n_macs; j++) {
414                 ds_put_format(s, " %s", lport->macs[j]);
415             }
416             ds_put_char(s, '\n');
417         }
418     }
419 }
420
421 static void
422 nbctl_show(struct ctl_context *ctx)
423 {
424     const struct nbrec_logical_switch *lswitch;
425
426     if (ctx->argc == 2) {
427         lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]);
428         if (lswitch) {
429             print_lswitch(lswitch, &ctx->output);
430         }
431     } else {
432         NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, ctx->idl) {
433             print_lswitch(lswitch, &ctx->output);
434         }
435     }
436 }
437
438 static void
439 nbctl_lswitch_add(struct ctl_context *ctx)
440 {
441     struct nbrec_logical_switch *lswitch;
442
443     lswitch = nbrec_logical_switch_insert(ctx->txn);
444     if (ctx->argc == 2) {
445         nbrec_logical_switch_set_name(lswitch, ctx->argv[1]);
446     }
447 }
448
449 static void
450 nbctl_lswitch_del(struct ctl_context *ctx)
451 {
452     const char *id = ctx->argv[1];
453     const struct nbrec_logical_switch *lswitch;
454
455     lswitch = lswitch_by_name_or_uuid(ctx, id);
456     if (!lswitch) {
457         return;
458     }
459
460     nbrec_logical_switch_delete(lswitch);
461 }
462
463 static void
464 nbctl_lswitch_list(struct ctl_context *ctx)
465 {
466     const struct nbrec_logical_switch *lswitch;
467     struct smap lswitches;
468
469     smap_init(&lswitches);
470     NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, ctx->idl) {
471         smap_add_format(&lswitches, lswitch->name, UUID_FMT " (%s)",
472                         UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
473     }
474     const struct smap_node **nodes = smap_sort(&lswitches);
475     for (size_t i = 0; i < smap_count(&lswitches); i++) {
476         const struct smap_node *node = nodes[i];
477         ds_put_format(&ctx->output, "%s\n", node->value);
478     }
479     smap_destroy(&lswitches);
480     free(nodes);
481 }
482
483 static void
484 nbctl_lswitch_set_external_id(struct ctl_context *ctx)
485 {
486     const char *id = ctx->argv[1];
487     const struct nbrec_logical_switch *lswitch;
488     struct smap new_external_ids;
489
490     lswitch = lswitch_by_name_or_uuid(ctx, id);
491     if (!lswitch) {
492         return;
493     }
494
495     smap_init(&new_external_ids);
496     smap_clone(&new_external_ids, &lswitch->external_ids);
497     if (ctx->argc == 4) {
498         smap_replace(&new_external_ids, ctx->argv[2], ctx->argv[3]);
499     } else {
500         smap_remove(&new_external_ids, ctx->argv[2]);
501     }
502     nbrec_logical_switch_set_external_ids(lswitch, &new_external_ids);
503     smap_destroy(&new_external_ids);
504 }
505
506 static void
507 nbctl_lswitch_get_external_id(struct ctl_context *ctx)
508 {
509     const char *id = ctx->argv[1];
510     const struct nbrec_logical_switch *lswitch;
511
512     lswitch = lswitch_by_name_or_uuid(ctx, id);
513     if (!lswitch) {
514         return;
515     }
516
517     if (ctx->argc == 3) {
518         const char *key = ctx->argv[2];
519         const char *value;
520
521         /* List one external ID */
522
523         value = smap_get(&lswitch->external_ids, key);
524         if (value) {
525             ds_put_format(&ctx->output, "%s\n", value);
526         }
527     } else {
528         struct smap_node *node;
529
530         /* List all external IDs */
531
532         SMAP_FOR_EACH(node, &lswitch->external_ids) {
533             ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
534         }
535     }
536 }
537 \f
538 static const struct nbrec_logical_port *
539 lport_by_name_or_uuid(struct ctl_context *ctx, const char *id)
540 {
541     const struct nbrec_logical_port *lport = NULL;
542     bool is_uuid = false;
543     struct uuid lport_uuid;
544
545     if (uuid_from_string(&lport_uuid, id)) {
546         is_uuid = true;
547         lport = nbrec_logical_port_get_for_uuid(ctx->idl, &lport_uuid);
548     }
549
550     if (!lport) {
551         NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->idl) {
552             if (!strcmp(lport->name, id)) {
553                 break;
554             }
555         }
556     }
557
558     if (!lport) {
559         VLOG_WARN("lport not found for %s: '%s'",
560                 is_uuid ? "UUID" : "name", id);
561     }
562
563     return lport;
564 }
565
566 static void
567 nbctl_lport_add(struct ctl_context *ctx)
568 {
569     struct nbrec_logical_port *lport;
570     const struct nbrec_logical_switch *lswitch;
571     int64_t tag;
572
573     lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]);
574     if (!lswitch) {
575         return;
576     }
577
578     if (ctx->argc != 3 && ctx->argc != 5) {
579         /* If a parent_name is specified, a tag must be specified as well. */
580         VLOG_WARN("Invalid arguments to lport-add.");
581         return;
582     }
583
584     if (ctx->argc == 5) {
585         /* Validate tag. */
586         if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag) || tag < 0 || tag > 4095) {
587             VLOG_WARN("Invalid tag '%s'", ctx->argv[4]);
588             return;
589         }
590     }
591
592     /* Create the logical port. */
593     lport = nbrec_logical_port_insert(ctx->txn);
594     nbrec_logical_port_set_name(lport, ctx->argv[2]);
595     if (ctx->argc == 5) {
596         nbrec_logical_port_set_parent_name(lport, ctx->argv[3]);
597         nbrec_logical_port_set_tag(lport, &tag, 1);
598     }
599
600     /* Insert the logical port into the logical switch. */
601     nbrec_logical_switch_verify_ports(lswitch);
602     struct nbrec_logical_port **new_ports = xmalloc(sizeof *new_ports *
603                                                     (lswitch->n_ports + 1));
604     memcpy(new_ports, lswitch->ports, sizeof *new_ports * lswitch->n_ports);
605     new_ports[lswitch->n_ports] = lport;
606     nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports + 1);
607     free(new_ports);
608 }
609
610 /* Removes lport 'lswitch->ports[idx]'. */
611 static void
612 remove_lport(const struct nbrec_logical_switch *lswitch, size_t idx)
613 {
614     const struct nbrec_logical_port *lport = lswitch->ports[idx];
615
616     /* First remove 'lport' from the array of ports.  This is what will
617      * actually cause the logical port to be deleted when the transaction is
618      * sent to the database server (due to garbage collection). */
619     struct nbrec_logical_port **new_ports
620         = xmemdup(lswitch->ports, sizeof *new_ports * lswitch->n_ports);
621     new_ports[idx] = new_ports[lswitch->n_ports - 1];
622     nbrec_logical_switch_verify_ports(lswitch);
623     nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports - 1);
624     free(new_ports);
625
626     /* Delete 'lport' from the IDL.  This won't have a real effect on the
627      * database server (the IDL will suppress it in fact) but it means that it
628      * won't show up when we iterate with NBREC_LOGICAL_PORT_FOR_EACH later. */
629     nbrec_logical_port_delete(lport);
630 }
631
632 static void
633 nbctl_lport_del(struct ctl_context *ctx)
634 {
635     const struct nbrec_logical_port *lport;
636
637     lport = lport_by_name_or_uuid(ctx, ctx->argv[1]);
638     if (!lport) {
639         return;
640     }
641
642     /* Find the switch that contains 'lport', then delete it. */
643     const struct nbrec_logical_switch *lswitch;
644     NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->idl) {
645         for (size_t i = 0; i < lswitch->n_ports; i++) {
646             if (lswitch->ports[i] == lport) {
647                 remove_lport(lswitch, i);
648                 return;
649             }
650         }
651     }
652
653     VLOG_WARN("logical port %s is not part of any logical switch",
654               ctx->argv[1]);
655 }
656
657 static void
658 nbctl_lport_list(struct ctl_context *ctx)
659 {
660     const char *id = ctx->argv[1];
661     const struct nbrec_logical_switch *lswitch;
662     struct smap lports;
663     size_t i;
664
665     lswitch = lswitch_by_name_or_uuid(ctx, id);
666     if (!lswitch) {
667         return;
668     }
669
670     smap_init(&lports);
671     for (i = 0; i < lswitch->n_ports; i++) {
672         const struct nbrec_logical_port *lport = lswitch->ports[i];
673         smap_add_format(&lports, lport->name, UUID_FMT " (%s)",
674                         UUID_ARGS(&lport->header_.uuid), lport->name);
675     }
676     const struct smap_node **nodes = smap_sort(&lports);
677     for (i = 0; i < smap_count(&lports); i++) {
678         const struct smap_node *node = nodes[i];
679         ds_put_format(&ctx->output, "%s\n", node->value);
680     }
681     smap_destroy(&lports);
682     free(nodes);
683 }
684
685 static void
686 nbctl_lport_get_parent(struct ctl_context *ctx)
687 {
688     const struct nbrec_logical_port *lport;
689
690     lport = lport_by_name_or_uuid(ctx, ctx->argv[1]);
691     if (!lport) {
692         return;
693     }
694
695     if (lport->parent_name) {
696         ds_put_format(&ctx->output, "%s\n", lport->parent_name);
697     }
698 }
699
700 static void
701 nbctl_lport_get_tag(struct ctl_context *ctx)
702 {
703     const struct nbrec_logical_port *lport;
704
705     lport = lport_by_name_or_uuid(ctx, ctx->argv[1]);
706     if (!lport) {
707         return;
708     }
709
710     if (lport->n_tag > 0) {
711         ds_put_format(&ctx->output, "%"PRId64"\n", lport->tag[0]);
712     }
713 }
714
715 static void
716 nbctl_lport_set_external_id(struct ctl_context *ctx)
717 {
718     const char *id = ctx->argv[1];
719     const struct nbrec_logical_port *lport;
720     struct smap new_external_ids;
721
722     lport = lport_by_name_or_uuid(ctx, id);
723     if (!lport) {
724         return;
725     }
726
727     smap_init(&new_external_ids);
728     smap_clone(&new_external_ids, &lport->external_ids);
729     if (ctx->argc == 4) {
730         smap_replace(&new_external_ids, ctx->argv[2], ctx->argv[3]);
731     } else {
732         smap_remove(&new_external_ids, ctx->argv[2]);
733     }
734     nbrec_logical_port_set_external_ids(lport, &new_external_ids);
735     smap_destroy(&new_external_ids);
736 }
737
738 static void
739 nbctl_lport_get_external_id(struct ctl_context *ctx)
740 {
741     const char *id = ctx->argv[1];
742     const struct nbrec_logical_port *lport;
743
744     lport = lport_by_name_or_uuid(ctx, id);
745     if (!lport) {
746         return;
747     }
748
749     if (ctx->argc == 3) {
750         const char *key = ctx->argv[2];
751         const char *value;
752
753         /* List one external ID */
754
755         value = smap_get(&lport->external_ids, key);
756         if (value) {
757             ds_put_format(&ctx->output, "%s\n", value);
758         }
759     } else {
760         struct smap_node *node;
761
762         /* List all external IDs */
763
764         SMAP_FOR_EACH(node, &lport->external_ids) {
765             ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
766         }
767     }
768 }
769
770 static void
771 nbctl_lport_set_macs(struct ctl_context *ctx)
772 {
773     const char *id = ctx->argv[1];
774     const struct nbrec_logical_port *lport;
775
776     lport = lport_by_name_or_uuid(ctx, id);
777     if (!lport) {
778         return;
779     }
780
781     nbrec_logical_port_set_macs(lport,
782             (const char **) ctx->argv + 2, ctx->argc - 2);
783 }
784
785 static void
786 nbctl_lport_get_macs(struct ctl_context *ctx)
787 {
788     const char *id = ctx->argv[1];
789     const struct nbrec_logical_port *lport;
790     struct svec macs;
791     const char *mac;
792     size_t i;
793
794     lport = lport_by_name_or_uuid(ctx, id);
795     if (!lport) {
796         return;
797     }
798
799     svec_init(&macs);
800     for (i = 0; i < lport->n_macs; i++) {
801         svec_add(&macs, lport->macs[i]);
802     }
803     svec_sort(&macs);
804     SVEC_FOR_EACH(i, mac, &macs) {
805         ds_put_format(&ctx->output, "%s\n", mac);
806     }
807     svec_destroy(&macs);
808 }
809
810 static void
811 nbctl_lport_set_port_security(struct ctl_context *ctx)
812 {
813     const char *id = ctx->argv[1];
814     const struct nbrec_logical_port *lport;
815
816     lport = lport_by_name_or_uuid(ctx, id);
817     if (!lport) {
818         return;
819     }
820
821     nbrec_logical_port_set_port_security(lport,
822             (const char **) ctx->argv + 2, ctx->argc - 2);
823 }
824
825 static void
826 nbctl_lport_get_port_security(struct ctl_context *ctx)
827 {
828     const char *id = ctx->argv[1];
829     const struct nbrec_logical_port *lport;
830     struct svec addrs;
831     const char *addr;
832     size_t i;
833
834     lport = lport_by_name_or_uuid(ctx, id);
835     if (!lport) {
836         return;
837     }
838
839     svec_init(&addrs);
840     for (i = 0; i < lport->n_port_security; i++) {
841         svec_add(&addrs, lport->port_security[i]);
842     }
843     svec_sort(&addrs);
844     SVEC_FOR_EACH(i, addr, &addrs) {
845         ds_put_format(&ctx->output, "%s\n", addr);
846     }
847     svec_destroy(&addrs);
848 }
849
850 static void
851 nbctl_lport_get_up(struct ctl_context *ctx)
852 {
853     const char *id = ctx->argv[1];
854     const struct nbrec_logical_port *lport;
855
856     lport = lport_by_name_or_uuid(ctx, id);
857     if (!lport) {
858         return;
859     }
860
861     ds_put_format(&ctx->output,
862                   "%s\n", (lport->up && *lport->up) ? "up" : "down");
863 }
864
865 static void
866 nbctl_lport_set_enabled(struct ctl_context *ctx)
867 {
868     const char *id = ctx->argv[1];
869     const char *state = ctx->argv[2];
870     const struct nbrec_logical_port *lport;
871
872     lport = lport_by_name_or_uuid(ctx, id);
873     if (!lport) {
874         return;
875     }
876
877     if (!strcasecmp(state, "enabled")) {
878         bool enabled = true;
879         nbrec_logical_port_set_enabled(lport, &enabled, 1);
880     } else if (!strcasecmp(state, "disabled")) {
881         bool enabled = false;
882         nbrec_logical_port_set_enabled(lport, &enabled, 1);
883     } else {
884         VLOG_ERR("Invalid state '%s' provided to lport-set-enabled", state);
885     }
886 }
887
888 static void
889 nbctl_lport_get_enabled(struct ctl_context *ctx)
890 {
891     const char *id = ctx->argv[1];
892     const struct nbrec_logical_port *lport;
893
894     lport = lport_by_name_or_uuid(ctx, id);
895     if (!lport) {
896         return;
897     }
898
899     ds_put_format(&ctx->output, "%s\n",
900                   !lport->enabled || *lport->enabled ? "enabled" : "disabled");
901 }
902
903 static void
904 nbctl_lport_set_type(struct ctl_context *ctx)
905 {
906     const char *id = ctx->argv[1];
907     const char *type = ctx->argv[2];
908     const struct nbrec_logical_port *lport;
909
910     lport = lport_by_name_or_uuid(ctx, id);
911     if (!lport) {
912         return;
913     }
914
915     nbrec_logical_port_set_type(lport, type);
916 }
917
918 static void
919 nbctl_lport_get_type(struct ctl_context *ctx)
920 {
921     const char *id = ctx->argv[1];
922     const struct nbrec_logical_port *lport;
923
924     lport = lport_by_name_or_uuid(ctx, id);
925     if (!lport) {
926         return;
927     }
928
929     ds_put_format(&ctx->output, "%s\n", lport->type);
930 }
931
932 static void
933 nbctl_lport_set_options(struct ctl_context *ctx)
934 {
935     const char *id = ctx->argv[1];
936     const struct nbrec_logical_port *lport;
937     size_t i;
938     struct smap options = SMAP_INITIALIZER(&options);
939
940     lport = lport_by_name_or_uuid(ctx, id);
941     if (!lport) {
942         return;
943     }
944
945     for (i = 2; i < ctx->argc; i++) {
946         char *key, *value;
947         value = xstrdup(ctx->argv[i]);
948         key = strsep(&value, "=");
949         if (value) {
950             smap_add(&options, key, value);
951         }
952         free(key);
953     }
954
955     nbrec_logical_port_set_options(lport, &options);
956
957     smap_destroy(&options);
958 }
959
960 static void
961 nbctl_lport_get_options(struct ctl_context *ctx)
962 {
963     const char *id = ctx->argv[1];
964     const struct nbrec_logical_port *lport;
965     struct smap_node *node;
966
967     lport = lport_by_name_or_uuid(ctx, id);
968     if (!lport) {
969         return;
970     }
971
972     SMAP_FOR_EACH(node, &lport->options) {
973         ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
974     }
975 }
976
977 enum {
978     DIR_FROM_LPORT,
979     DIR_TO_LPORT
980 };
981
982 static int
983 dir_encode(const char *dir)
984 {
985     if (!strcmp(dir, "from-lport")) {
986         return DIR_FROM_LPORT;
987     } else if (!strcmp(dir, "to-lport")) {
988         return DIR_TO_LPORT;
989     }
990
991     OVS_NOT_REACHED();
992 }
993
994 static int
995 acl_cmp(const void *acl1_, const void *acl2_)
996 {
997     const struct nbrec_acl *const *acl1p = acl1_;
998     const struct nbrec_acl *const *acl2p = acl2_;
999     const struct nbrec_acl *acl1 = *acl1p;
1000     const struct nbrec_acl *acl2 = *acl2p;
1001
1002     int dir1 = dir_encode(acl1->direction);
1003     int dir2 = dir_encode(acl2->direction);
1004
1005     if (dir1 != dir2) {
1006         return dir1 < dir2 ? -1 : 1;
1007     } else if (acl1->priority != acl2->priority) {
1008         return acl1->priority > acl2->priority ? -1 : 1;
1009     } else {
1010         return strcmp(acl1->match, acl2->match);
1011     }
1012 }
1013
1014 static void
1015 nbctl_acl_list(struct ctl_context *ctx)
1016 {
1017     const struct nbrec_logical_switch *lswitch;
1018     const struct nbrec_acl **acls;
1019     size_t i;
1020
1021     lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]);
1022     if (!lswitch) {
1023         return;
1024     }
1025
1026     acls = xmalloc(sizeof *acls * lswitch->n_acls);
1027     for (i = 0; i < lswitch->n_acls; i++) {
1028         acls[i] = lswitch->acls[i];
1029     }
1030
1031     qsort(acls, lswitch->n_acls, sizeof *acls, acl_cmp);
1032
1033     for (i = 0; i < lswitch->n_acls; i++) {
1034         const struct nbrec_acl *acl = acls[i];
1035         printf("%10s %5"PRId64" (%s) %s%s\n", acl->direction, acl->priority,
1036                 acl->match, acl->action, acl->log ? " log" : "");
1037     }
1038
1039     free(acls);
1040 }
1041
1042 static void
1043 nbctl_acl_add(struct ctl_context *ctx)
1044 {
1045     const struct nbrec_logical_switch *lswitch;
1046     const char *action = ctx->argv[5];
1047     const char *direction;
1048     int64_t priority;
1049
1050     lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]);
1051     if (!lswitch) {
1052         return;
1053     }
1054
1055     /* Validate direction.  Only require the first letter. */
1056     if (ctx->argv[2][0] == 't') {
1057         direction = "to-lport";
1058     } else if (ctx->argv[2][0] == 'f') {
1059         direction = "from-lport";
1060     } else {
1061         VLOG_WARN("Invalid direction '%s'", ctx->argv[2]);
1062         return;
1063     }
1064
1065     /* Validate priority. */
1066     if (!ovs_scan(ctx->argv[3], "%"SCNd64, &priority) || priority < 1
1067         || priority > 65535) {
1068         VLOG_WARN("Invalid priority '%s'", ctx->argv[3]);
1069         return;
1070     }
1071
1072     /* Validate action. */
1073     if (strcmp(action, "allow") && strcmp(action, "allow-related")
1074         && strcmp(action, "drop") && strcmp(action, "reject")) {
1075         VLOG_WARN("Invalid action '%s'", action);
1076         return;
1077     }
1078
1079     /* Create the acl. */
1080     struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn);
1081     nbrec_acl_set_priority(acl, priority);
1082     nbrec_acl_set_direction(acl, direction);
1083     nbrec_acl_set_match(acl, ctx->argv[4]);
1084     nbrec_acl_set_action(acl, action);
1085     if (shash_find(&ctx->options, "--log") != NULL) {
1086         nbrec_acl_set_log(acl, true);
1087     }
1088
1089     /* Insert the acl into the logical switch. */
1090     nbrec_logical_switch_verify_acls(lswitch);
1091     struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls *
1092                                           (lswitch->n_acls + 1));
1093     memcpy(new_acls, lswitch->acls, sizeof *new_acls * lswitch->n_acls);
1094     new_acls[lswitch->n_acls] = acl;
1095     nbrec_logical_switch_set_acls(lswitch, new_acls, lswitch->n_acls + 1);
1096     free(new_acls);
1097 }
1098
1099 static void
1100 nbctl_acl_del(struct ctl_context *ctx)
1101 {
1102     const struct nbrec_logical_switch *lswitch;
1103     const char *direction;
1104     int64_t priority = 0;
1105
1106     lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]);
1107     if (!lswitch) {
1108         return;
1109     }
1110
1111     if (ctx->argc != 2 && ctx->argc != 3 && ctx->argc != 5) {
1112         VLOG_WARN("Invalid number of arguments");
1113         return;
1114     }
1115
1116     if (ctx->argc == 2) {
1117         /* If direction, priority, and match are not specified, delete
1118          * all ACLs. */
1119         nbrec_logical_switch_verify_acls(lswitch);
1120         nbrec_logical_switch_set_acls(lswitch, NULL, 0);
1121         return;
1122     }
1123
1124     /* Validate direction.  Only require first letter. */
1125     if (ctx->argv[2][0] == 't') {
1126         direction = "to-lport";
1127     } else if (ctx->argv[2][0] == 'f') {
1128         direction = "from-lport";
1129     } else {
1130         VLOG_WARN("Invalid direction '%s'", ctx->argv[2]);
1131         return;
1132     }
1133
1134     /* If priority and match are not specified, delete all ACLs with the
1135      * specified direction. */
1136     if (ctx->argc == 3) {
1137         struct nbrec_acl **new_acls
1138             = xmalloc(sizeof *new_acls * lswitch->n_acls);
1139
1140         int n_acls = 0;
1141         for (size_t i = 0; i < lswitch->n_acls; i++) {
1142             if (strcmp(direction, lswitch->acls[i]->direction)) {
1143                 new_acls[n_acls++] = lswitch->acls[i];
1144             }
1145         }
1146
1147         nbrec_logical_switch_verify_acls(lswitch);
1148         nbrec_logical_switch_set_acls(lswitch, new_acls, n_acls);
1149         free(new_acls);
1150         return;
1151     }
1152
1153     /* Validate priority. */
1154     if (!ovs_scan(ctx->argv[3], "%"SCNd64, &priority) || priority < 1
1155         || priority > 65535) {
1156         VLOG_WARN("Invalid priority '%s'", ctx->argv[3]);
1157         return;
1158     }
1159
1160     /* Remove the matching rule. */
1161     for (size_t i = 0; i < lswitch->n_acls; i++) {
1162         struct nbrec_acl *acl = lswitch->acls[i];
1163
1164         if (priority == acl->priority && !strcmp(ctx->argv[4], acl->match) &&
1165              !strcmp(direction, acl->direction)) {
1166             struct nbrec_acl **new_acls
1167                 = xmemdup(lswitch->acls, sizeof *new_acls * lswitch->n_acls);
1168             new_acls[i] = lswitch->acls[lswitch->n_acls - 1];
1169             nbrec_logical_switch_verify_acls(lswitch);
1170             nbrec_logical_switch_set_acls(lswitch, new_acls,
1171                                           lswitch->n_acls - 1);
1172             free(new_acls);
1173             return;
1174         }
1175     }
1176 }
1177 \f
1178 static const struct ctl_table_class tables[] = {
1179     {&nbrec_table_logical_switch,
1180      {{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL},
1181       {NULL, NULL, NULL}}},
1182
1183     {&nbrec_table_logical_port,
1184      {{&nbrec_table_logical_port, &nbrec_logical_port_col_name, NULL},
1185       {NULL, NULL, NULL}}},
1186
1187     {&nbrec_table_acl,
1188      {{NULL, NULL, NULL},
1189       {NULL, NULL, NULL}}},
1190
1191     {&nbrec_table_logical_router,
1192      {{&nbrec_table_logical_router, &nbrec_logical_router_col_name, NULL},
1193       {NULL, NULL, NULL}}},
1194
1195     {&nbrec_table_logical_router_port,
1196      {{&nbrec_table_logical_router_port, &nbrec_logical_router_port_col_name,
1197        NULL},
1198       {NULL, NULL, NULL}}},
1199
1200     {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
1201 };
1202 \f
1203 static void
1204 run_prerequisites(struct ctl_command *commands, size_t n_commands,
1205                   struct ovsdb_idl *idl)
1206 {
1207     struct ctl_command *c;
1208
1209     for (c = commands; c < &commands[n_commands]; c++) {
1210         if (c->syntax->prerequisites) {
1211             struct ctl_context ctx;
1212
1213             ds_init(&c->output);
1214             c->table = NULL;
1215
1216             ctl_context_init(&ctx, c, idl, NULL, NULL, NULL);
1217             (c->syntax->prerequisites)(&ctx);
1218             ctl_context_done(&ctx, c);
1219
1220             ovs_assert(!c->output.string);
1221             ovs_assert(!c->table);
1222         }
1223     }
1224 }
1225
1226 static void
1227 do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands,
1228          struct ovsdb_idl *idl)
1229 {
1230     struct ovsdb_idl_txn *txn;
1231     enum ovsdb_idl_txn_status status;
1232     struct ovsdb_symbol_table *symtab;
1233     struct ctl_context ctx;
1234     struct ctl_command *c;
1235     struct shash_node *node;
1236     char *error = NULL;
1237
1238     txn = the_idl_txn = ovsdb_idl_txn_create(idl);
1239     if (dry_run) {
1240         ovsdb_idl_txn_set_dry_run(txn);
1241     }
1242
1243     ovsdb_idl_txn_add_comment(txn, "ovs-nbctl: %s", args);
1244
1245     symtab = ovsdb_symbol_table_create();
1246     for (c = commands; c < &commands[n_commands]; c++) {
1247         ds_init(&c->output);
1248         c->table = NULL;
1249     }
1250     ctl_context_init(&ctx, NULL, idl, txn, symtab, NULL);
1251     for (c = commands; c < &commands[n_commands]; c++) {
1252         ctl_context_init_command(&ctx, c);
1253         if (c->syntax->run) {
1254             (c->syntax->run)(&ctx);
1255         }
1256         ctl_context_done_command(&ctx, c);
1257
1258         if (ctx.try_again) {
1259             ctl_context_done(&ctx, NULL);
1260             goto try_again;
1261         }
1262     }
1263     ctl_context_done(&ctx, NULL);
1264
1265     SHASH_FOR_EACH (node, &symtab->sh) {
1266         struct ovsdb_symbol *symbol = node->data;
1267         if (!symbol->created) {
1268             ctl_fatal("row id \"%s\" is referenced but never created (e.g. "
1269                       "with \"-- --id=%s create ...\")",
1270                       node->name, node->name);
1271         }
1272         if (!symbol->strong_ref) {
1273             if (!symbol->weak_ref) {
1274                 VLOG_WARN("row id \"%s\" was created but no reference to it "
1275                           "was inserted, so it will not actually appear in "
1276                           "the database", node->name);
1277             } else {
1278                 VLOG_WARN("row id \"%s\" was created but only a weak "
1279                           "reference to it was inserted, so it will not "
1280                           "actually appear in the database", node->name);
1281             }
1282         }
1283     }
1284
1285     status = ovsdb_idl_txn_commit_block(txn);
1286     if (status == TXN_UNCHANGED || status == TXN_SUCCESS) {
1287         for (c = commands; c < &commands[n_commands]; c++) {
1288             if (c->syntax->postprocess) {
1289                 ctl_context_init(&ctx, c, idl, txn, symtab, NULL);
1290                 (c->syntax->postprocess)(&ctx);
1291                 ctl_context_done(&ctx, c);
1292             }
1293         }
1294     }
1295     error = xstrdup(ovsdb_idl_txn_get_error(txn));
1296
1297     switch (status) {
1298     case TXN_UNCOMMITTED:
1299     case TXN_INCOMPLETE:
1300         OVS_NOT_REACHED();
1301
1302     case TXN_ABORTED:
1303         /* Should not happen--we never call ovsdb_idl_txn_abort(). */
1304         ctl_fatal("transaction aborted");
1305
1306     case TXN_UNCHANGED:
1307     case TXN_SUCCESS:
1308         break;
1309
1310     case TXN_TRY_AGAIN:
1311         goto try_again;
1312
1313     case TXN_ERROR:
1314         ctl_fatal("transaction error: %s", error);
1315
1316     case TXN_NOT_LOCKED:
1317         /* Should not happen--we never call ovsdb_idl_set_lock(). */
1318         ctl_fatal("database not locked");
1319
1320     default:
1321         OVS_NOT_REACHED();
1322     }
1323     free(error);
1324
1325     ovsdb_symbol_table_destroy(symtab);
1326
1327     for (c = commands; c < &commands[n_commands]; c++) {
1328         struct ds *ds = &c->output;
1329
1330         if (c->table) {
1331             table_print(c->table, &table_style);
1332         } else if (oneline) {
1333             size_t j;
1334
1335             ds_chomp(ds, '\n');
1336             for (j = 0; j < ds->length; j++) {
1337                 int ch = ds->string[j];
1338                 switch (ch) {
1339                 case '\n':
1340                     fputs("\\n", stdout);
1341                     break;
1342
1343                 case '\\':
1344                     fputs("\\\\", stdout);
1345                     break;
1346
1347                 default:
1348                     putchar(ch);
1349                 }
1350             }
1351             putchar('\n');
1352         } else {
1353             fputs(ds_cstr(ds), stdout);
1354         }
1355         ds_destroy(&c->output);
1356         table_destroy(c->table);
1357         free(c->table);
1358
1359         shash_destroy_free_data(&c->options);
1360     }
1361     free(commands);
1362     ovsdb_idl_txn_destroy(txn);
1363     ovsdb_idl_destroy(idl);
1364
1365     exit(EXIT_SUCCESS);
1366
1367 try_again:
1368     /* Our transaction needs to be rerun, or a prerequisite was not met.  Free
1369      * resources and return so that the caller can try again. */
1370     if (txn) {
1371         ovsdb_idl_txn_abort(txn);
1372         ovsdb_idl_txn_destroy(txn);
1373         the_idl_txn = NULL;
1374     }
1375     ovsdb_symbol_table_destroy(symtab);
1376     for (c = commands; c < &commands[n_commands]; c++) {
1377         ds_destroy(&c->output);
1378         table_destroy(c->table);
1379         free(c->table);
1380     }
1381     free(error);
1382 }
1383
1384 /* Frees the current transaction and the underlying IDL and then calls
1385  * exit(status).
1386  *
1387  * Freeing the transaction and the IDL is not strictly necessary, but it makes
1388  * for a clean memory leak report from valgrind in the normal case.  That makes
1389  * it easier to notice real memory leaks. */
1390 static void
1391 nbctl_exit(int status)
1392 {
1393     if (the_idl_txn) {
1394         ovsdb_idl_txn_abort(the_idl_txn);
1395         ovsdb_idl_txn_destroy(the_idl_txn);
1396     }
1397     ovsdb_idl_destroy(the_idl);
1398     exit(status);
1399 }
1400
1401 static const struct ctl_command_syntax nbctl_commands[] = {
1402     { "show", 0, 1, "[LSWITCH]", NULL, nbctl_show, NULL, "", RO },
1403
1404     /* lswitch commands. */
1405     { "lswitch-add", 0, 1, "[LSWITCH]", NULL, nbctl_lswitch_add,
1406       NULL, "", RW },
1407     { "lswitch-del", 1, 1, "LSWITCH", NULL, nbctl_lswitch_del,
1408       NULL, "", RW },
1409     { "lswitch-list", 0, 0, "", NULL, nbctl_lswitch_list, NULL, "", RO },
1410     { "lswitch-set-external-id", 2, 3, "LSWITCH KEY [VALUE]", NULL,
1411       nbctl_lswitch_set_external_id, NULL, "", RW },
1412     { "lswitch-get-external-id", 1, 2, "LSWITCH [KEY]", NULL,
1413       nbctl_lswitch_get_external_id, NULL, "", RO },
1414
1415     /* acl commands. */
1416     { "acl-add", 5, 5, "LSWITCH DIRECTION PRIORITY MATCH ACTION", NULL,
1417       nbctl_acl_add, NULL, "--log", RW },
1418     { "acl-del", 1, 4, "LSWITCH [DIRECTION [PRIORITY MATCH]]", NULL,
1419       nbctl_acl_del, NULL, "", RW },
1420     { "acl-list", 1, 1, "LSWITCH", NULL, nbctl_acl_list, NULL, "", RO },
1421
1422     /* lport commands. */
1423     { "lport-add", 2, 4, "LSWITCH LPORT [PARENT] [TAG]", NULL, nbctl_lport_add,
1424       NULL, "", RW },
1425     { "lport-del", 1, 1, "LPORT", NULL, nbctl_lport_del, NULL, "", RO },
1426     { "lport-list", 1, 1, "LSWITCH", NULL, nbctl_lport_list, NULL, "", RO },
1427     { "lport-get-parent", 1, 1, "LPORT", NULL, nbctl_lport_get_parent, NULL,
1428       "", RO },
1429     { "lport-get-tag", 1, 1, "LPORT", NULL, nbctl_lport_get_tag, NULL, "",
1430       RO },
1431     { "lport-set-external-id", 2, 3, "LPORT KEY [VALUE]", NULL,
1432       nbctl_lport_set_external_id, NULL, "", RW },
1433     { "lport-get-external-id", 1, 2, "LPORT [KEY]", NULL,
1434       nbctl_lport_get_external_id, NULL, "", RO },
1435     { "lport-set-macs", 1, INT_MAX, "LPORT [MAC]...", NULL,
1436       nbctl_lport_set_macs, NULL, "", RW },
1437     { "lport-get-macs", 1, 1, "LPORT", NULL, nbctl_lport_get_macs, NULL,
1438       "", RO },
1439     { "lport-set-port-security", 0, INT_MAX, "LPORT [ADDRS]...", NULL,
1440       nbctl_lport_set_port_security, NULL, "", RW },
1441     { "lport-get-port-security", 1, 1, "LPORT", NULL,
1442       nbctl_lport_get_port_security, NULL, "", RO },
1443     { "lport-get-up", 1, 1, "LPORT", NULL, nbctl_lport_get_up, NULL, "", RO },
1444     { "lport-set-enabled", 2, 2, "LPORT STATE", NULL, nbctl_lport_set_enabled,
1445       NULL, "", RW },
1446     { "lport-get-enabled", 1, 1, "LPORT", NULL, nbctl_lport_get_enabled, NULL,
1447       "", RO },
1448     { "lport-set-type", 2, 2, "LPORT TYPE", NULL, nbctl_lport_set_type, NULL,
1449       "", RW },
1450     { "lport-get-type", 1, 1, "LPORT", NULL, nbctl_lport_get_type, NULL, "",
1451       RO },
1452     { "lport-set-options", 1, INT_MAX, "LPORT KEY=VALUE [KEY=VALUE]...", NULL,
1453       nbctl_lport_set_options, NULL, "", RW },
1454     { "lport-get-options", 1, 1, "LPORT", NULL, nbctl_lport_get_options, NULL,
1455       "", RO },
1456
1457     {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO},
1458 };
1459
1460 /* Registers nbctl and common db commands. */
1461 static void
1462 nbctl_cmd_init(void)
1463 {
1464     ctl_init(tables, NULL, nbctl_exit);
1465     ctl_register_commands(nbctl_commands);
1466 }