ovn-controller: Support multiple encaps simultaneously.
[cascardo/ovs.git] / ovn / controller / ovn-controller.c
1 /* Copyright (c) 2015 Nicira, Inc.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <config.h>
17
18 #include "ovn-controller.h"
19
20 #include <errno.h>
21 #include <getopt.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "command-line.h"
27 #include "compiler.h"
28 #include "daemon.h"
29 #include "dirs.h"
30 #include "dynamic-string.h"
31 #include "openvswitch/vconn.h"
32 #include "openvswitch/vlog.h"
33 #include "ovn/lib/ovn-sb-idl.h"
34 #include "poll-loop.h"
35 #include "fatal-signal.h"
36 #include "lib/vswitch-idl.h"
37 #include "smap.h"
38 #include "stream.h"
39 #include "stream-ssl.h"
40 #include "unixctl.h"
41 #include "util.h"
42
43 #include "ofctrl.h"
44 #include "binding.h"
45 #include "chassis.h"
46 #include "encaps.h"
47 #include "physical.h"
48 #include "lflow.h"
49
50 VLOG_DEFINE_THIS_MODULE(main);
51
52 static unixctl_cb_func ovn_controller_exit;
53 static unixctl_cb_func ct_zone_list;
54
55 #define DEFAULT_BRIDGE_NAME "br-int"
56
57 static void parse_options(int argc, char *argv[]);
58 OVS_NO_RETURN static void usage(void);
59
60 static char *ovs_remote;
61
62 const struct sbrec_chassis *
63 get_chassis(struct ovsdb_idl *ovnsb_idl, const char *chassis_id)
64 {
65     const struct sbrec_chassis *chassis_rec;
66
67     SBREC_CHASSIS_FOR_EACH(chassis_rec, ovnsb_idl) {
68         if (!strcmp(chassis_rec->name, chassis_id)) {
69             break;
70         }
71     }
72
73     return chassis_rec;
74 }
75
76 uint32_t
77 get_tunnel_type(const char *name)
78 {
79     if (!strcmp(name, "geneve")) {
80         return GENEVE;
81     } else if (!strcmp(name, "stt")) {
82         return STT;
83     } else if (!strcmp(name, "vxlan")) {
84         return VXLAN;
85     }
86
87     return 0;
88 }
89
90 static const struct ovsrec_bridge *
91 get_bridge(struct ovsdb_idl *ovs_idl, const char *br_name)
92 {
93     const struct ovsrec_bridge *br;
94     OVSREC_BRIDGE_FOR_EACH (br, ovs_idl) {
95         if (!strcmp(br->name, br_name)) {
96             return br;
97         }
98     }
99     return NULL;
100 }
101
102 static const struct ovsrec_bridge *
103 create_br_int(struct controller_ctx *ctx,
104               const struct ovsrec_open_vswitch *cfg,
105               const char *bridge_name)
106 {
107     if (!ctx->ovs_idl_txn) {
108         return NULL;
109     }
110
111     ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
112             "ovn-controller: creating integration bridge '%s'", bridge_name);
113
114     struct ovsrec_interface *iface;
115     iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
116     ovsrec_interface_set_name(iface, bridge_name);
117     ovsrec_interface_set_type(iface, "internal");
118
119     struct ovsrec_port *port;
120     port = ovsrec_port_insert(ctx->ovs_idl_txn);
121     ovsrec_port_set_name(port, bridge_name);
122     ovsrec_port_set_interfaces(port, &iface, 1);
123
124     struct ovsrec_bridge *bridge;
125     bridge = ovsrec_bridge_insert(ctx->ovs_idl_txn);
126     ovsrec_bridge_set_name(bridge, bridge_name);
127     ovsrec_bridge_set_fail_mode(bridge, "secure");
128     const struct smap oc = SMAP_CONST1(&oc, "disable-in-band", "true");
129     ovsrec_bridge_set_other_config(bridge, &oc);
130     ovsrec_bridge_set_ports(bridge, &port, 1);
131
132     struct ovsrec_bridge **bridges;
133     size_t bytes = sizeof *bridges * cfg->n_bridges;
134     bridges = xmalloc(bytes + sizeof *bridges);
135     memcpy(bridges, cfg->bridges, bytes);
136     bridges[cfg->n_bridges] = bridge;
137     ovsrec_open_vswitch_verify_bridges(cfg);
138     ovsrec_open_vswitch_set_bridges(cfg, bridges, cfg->n_bridges + 1);
139
140     return bridge;
141 }
142
143 static const struct ovsrec_bridge *
144 get_br_int(struct controller_ctx *ctx)
145 {
146     const struct ovsrec_open_vswitch *cfg;
147     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
148     if (!cfg) {
149         return NULL;
150     }
151
152     const char *br_int_name = smap_get(&cfg->external_ids, "ovn-bridge");
153     if (!br_int_name) {
154         br_int_name = DEFAULT_BRIDGE_NAME;
155     }
156
157     const struct ovsrec_bridge *br;
158     br = get_bridge(ctx->ovs_idl, br_int_name);
159     if (!br) {
160         return create_br_int(ctx, cfg, br_int_name);
161     }
162     return br;
163 }
164
165 static const char *
166 get_chassis_id(const struct ovsdb_idl *ovs_idl)
167 {
168     const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
169     return cfg ? smap_get(&cfg->external_ids, "system-id") : NULL;
170 }
171
172 static char *
173 patch_port_name(const struct ovsrec_bridge *b1, const struct ovsrec_bridge *b2)
174 {
175     return xasprintf("patch-%s-to-%s", b1->name, b2->name);
176 }
177
178 /*
179  * Return true if the port is a patch port from b1 to b2
180  */
181 static bool
182 match_patch_port(const struct ovsrec_port *port,
183                  const struct ovsrec_bridge *b1,
184                  const struct ovsrec_bridge *b2)
185 {
186     struct ovsrec_interface *iface;
187     size_t i;
188     char *peer_port_name;
189     bool res = false;
190
191     peer_port_name = patch_port_name(b2, b1);
192
193     for (i = 0; i < port->n_interfaces; i++) {
194         iface = port->interfaces[i];
195         if (strcmp(iface->type, "patch")) {
196             continue;
197         }
198         const char *peer;
199         peer = smap_get(&iface->options, "peer");
200         if (peer && !strcmp(peer, peer_port_name)) {
201             res = true;
202             break;
203         }
204     }
205
206     free(peer_port_name);
207
208     return res;
209 }
210
211 static void
212 create_patch_port(struct controller_ctx *ctx,
213                   const char *network,
214                   const struct ovsrec_bridge *b1,
215                   const struct ovsrec_bridge *b2)
216 {
217     if (!ctx->ovs_idl_txn) {
218         return;
219     }
220
221     char *port_name = patch_port_name(b1, b2);
222     char *peer_port_name = patch_port_name(b2, b1);
223
224     ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
225             "ovn-controller: creating patch port '%s' from '%s' to '%s'",
226             port_name, b1->name, b2->name);
227
228     struct ovsrec_interface *iface;
229     iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
230     ovsrec_interface_set_name(iface, port_name);
231     ovsrec_interface_set_type(iface, "patch");
232     const struct smap options = SMAP_CONST1(&options, "peer", peer_port_name);
233     ovsrec_interface_set_options(iface, &options);
234
235     struct ovsrec_port *port;
236     port = ovsrec_port_insert(ctx->ovs_idl_txn);
237     ovsrec_port_set_name(port, port_name);
238     ovsrec_port_set_interfaces(port, &iface, 1);
239     const struct smap ids = SMAP_CONST1(&ids, "ovn-patch-port", network);
240     ovsrec_port_set_external_ids(port, &ids);
241
242     struct ovsrec_port **ports;
243     ports = xmalloc(sizeof *ports * (b1->n_ports + 1));
244     memcpy(ports, b1->ports, sizeof *ports * b1->n_ports);
245     ports[b1->n_ports] = port;
246     ovsrec_bridge_verify_ports(b1);
247     ovsrec_bridge_set_ports(b1, ports, b1->n_ports + 1);
248
249     free(ports);
250     free(port_name);
251     free(peer_port_name);
252 }
253
254 static void
255 create_patch_ports(struct controller_ctx *ctx,
256                    const char *network,
257                    struct shash *existing_ports,
258                    const struct ovsrec_bridge *b1,
259                    const struct ovsrec_bridge *b2)
260 {
261     size_t i;
262
263     for (i = 0; i < b1->n_ports; i++) {
264         if (match_patch_port(b1->ports[i], b1, b2)) {
265             /* Patch port already exists on b1 */
266             shash_find_and_delete(existing_ports, b1->ports[i]->name);
267             break;
268         }
269     }
270     if (i == b1->n_ports) {
271         create_patch_port(ctx, network, b1, b2);
272     }
273 }
274
275 static void
276 init_existing_ports(struct controller_ctx *ctx,
277                     struct shash *existing_ports)
278 {
279     const struct ovsrec_port *port;
280
281     OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) {
282         if (smap_get(&port->external_ids, "ovn-patch-port")) {
283             shash_add(existing_ports, port->name, port);
284         }
285     }
286 }
287
288 static void
289 remove_port(struct controller_ctx *ctx,
290             const struct ovsrec_port *port)
291 {
292     const struct ovsrec_bridge *bridge;
293
294     /* We know the port we want to delete, but we have to find the bridge its on
295      * to do so.  Note this only runs on a config change that should be pretty
296      * rare. */
297     OVSREC_BRIDGE_FOR_EACH (bridge, ctx->ovs_idl) {
298         size_t i;
299         for (i = 0; i < bridge->n_ports; i++) {
300             if (bridge->ports[i] != port) {
301                 continue;
302             }
303             struct ovsrec_port **new_ports;
304             new_ports = xmemdup(bridge->ports,
305                     sizeof *new_ports * (bridge->n_ports - 1));
306             if (i != bridge->n_ports - 1) {
307                 /* Removed port was not last */
308                 new_ports[i] = bridge->ports[bridge->n_ports - 1];
309             }
310             ovsrec_bridge_verify_ports(bridge);
311             ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
312             free(new_ports);
313             ovsrec_port_delete(port);
314             return;
315         }
316     }
317 }
318
319 static void
320 parse_bridge_mappings(struct controller_ctx *ctx,
321                       const struct ovsrec_bridge *br_int,
322                       const char *mappings_cfg)
323 {
324     struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
325     init_existing_ports(ctx, &existing_ports);
326
327     char *cur, *next, *start;
328     next = start = xstrdup(mappings_cfg);
329     while ((cur = strsep(&next, ",")) && *cur) {
330         char *network, *bridge = cur;
331         const struct ovsrec_bridge *ovs_bridge;
332
333         network = strsep(&bridge, ":");
334         if (!bridge || !*network || !*bridge) {
335             VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
336                     mappings_cfg);
337             break;
338         }
339
340         ovs_bridge = get_bridge(ctx->ovs_idl, bridge);
341         if (!ovs_bridge) {
342             VLOG_WARN("Bridge '%s' not found for network '%s'",
343                     bridge, network);
344             continue;
345         }
346
347         create_patch_ports(ctx, network, &existing_ports, br_int, ovs_bridge);
348         create_patch_ports(ctx, network, &existing_ports, ovs_bridge, br_int);
349     }
350     free(start);
351
352     /* Any ports left in existing_ports are related to configuration that has
353      * been removed, so we should delete the ports now. */
354     struct shash_node *port_node, *port_next_node;
355     SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) {
356         struct ovsrec_port *port = port_node->data;
357         shash_delete(&existing_ports, port_node);
358         remove_port(ctx, port);
359     }
360     shash_destroy(&existing_ports);
361 }
362
363 static void
364 init_bridge_mappings(struct controller_ctx *ctx,
365                      const struct ovsrec_bridge *br_int)
366 {
367     const char *mappings_cfg = "";
368     const struct ovsrec_open_vswitch *cfg;
369
370     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
371     if (cfg) {
372         mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
373         if (!mappings_cfg) {
374             mappings_cfg = "";
375         }
376     }
377     parse_bridge_mappings(ctx, br_int, mappings_cfg);
378 }
379
380 /* Retrieves the OVN Southbound remote location from the
381  * "external-ids:ovn-remote" key in 'ovs_idl' and returns a copy of it.
382  *
383  * XXX ovn-controller does not support this changing mid-run, but that should
384  * be addressed later. */
385 static char *
386 get_ovnsb_remote(struct ovsdb_idl *ovs_idl)
387 {
388     while (1) {
389         ovsdb_idl_run(ovs_idl);
390
391         const struct ovsrec_open_vswitch *cfg
392             = ovsrec_open_vswitch_first(ovs_idl);
393         if (cfg) {
394             const char *remote = smap_get(&cfg->external_ids, "ovn-remote");
395             if (remote) {
396                 return xstrdup(remote);
397             }
398         }
399
400         VLOG_INFO("OVN OVSDB remote not specified.  Waiting...");
401         ovsdb_idl_wait(ovs_idl);
402         poll_block();
403     }
404 }
405
406 int
407 main(int argc, char *argv[])
408 {
409     struct unixctl_server *unixctl;
410     bool exiting;
411     int retval;
412
413     ovs_cmdl_proctitle_init(argc, argv);
414     set_program_name(argv[0]);
415     service_start(&argc, &argv);
416     parse_options(argc, argv);
417     fatal_ignore_sigpipe();
418
419     daemonize_start(false);
420
421     retval = unixctl_server_create(NULL, &unixctl);
422     if (retval) {
423         exit(EXIT_FAILURE);
424     }
425     unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
426
427     daemonize_complete();
428
429     ovsrec_init();
430     sbrec_init();
431
432     ofctrl_init();
433     lflow_init();
434
435     /* Connect to OVS OVSDB instance.  We do not monitor all tables by
436      * default, so modules must register their interest explicitly.  */
437     struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
438         ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true));
439     ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_open_vswitch);
440     ovsdb_idl_add_column(ovs_idl_loop.idl,
441                          &ovsrec_open_vswitch_col_external_ids);
442     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_open_vswitch_col_bridges);
443     ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_interface);
444     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_name);
445     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_type);
446     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_options);
447     ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_port);
448     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_name);
449     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_interfaces);
450     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_external_ids);
451     ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_bridge);
452     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_ports);
453     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_name);
454     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_fail_mode);
455     ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_other_config);
456     chassis_register_ovs_idl(ovs_idl_loop.idl);
457     encaps_register_ovs_idl(ovs_idl_loop.idl);
458     binding_register_ovs_idl(ovs_idl_loop.idl);
459     physical_register_ovs_idl(ovs_idl_loop.idl);
460     ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl);
461
462     /* Connect to OVN SB database. */
463     char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl);
464     struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
465         ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));
466     ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl);
467
468     /* Initialize connection tracking zones. */
469     struct simap ct_zones = SIMAP_INITIALIZER(&ct_zones);
470     unsigned long ct_zone_bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)];
471     bitmap_set1(ct_zone_bitmap, 0); /* Zone 0 is reserved. */
472     unixctl_command_register("ct-zone-list", "", 0, 0,
473                              ct_zone_list, &ct_zones);
474
475     /* Main loop. */
476     exiting = false;
477     while (!exiting) {
478         struct controller_ctx ctx = {
479             .ovs_idl = ovs_idl_loop.idl,
480             .ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop),
481             .ovnsb_idl = ovnsb_idl_loop.idl,
482             .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
483         };
484
485         const struct ovsrec_bridge *br_int = get_br_int(&ctx);
486         const char *chassis_id = get_chassis_id(ctx.ovs_idl);
487
488         /* Map bridges to local nets from ovn-bridge-mappings */
489         if (br_int) {
490             init_bridge_mappings(&ctx, br_int);
491         }
492
493         if (chassis_id) {
494             chassis_run(&ctx, chassis_id);
495             encaps_run(&ctx, br_int, chassis_id);
496             binding_run(&ctx, br_int, chassis_id, &ct_zones, ct_zone_bitmap);
497         }
498
499         if (br_int) {
500             enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int);
501
502             struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
503             lflow_run(&ctx, &flow_table, &ct_zones);
504             if (chassis_id) {
505                 physical_run(&ctx, mff_ovn_geneve,
506                              br_int, chassis_id, &ct_zones, &flow_table);
507             }
508             ofctrl_put(&flow_table);
509             hmap_destroy(&flow_table);
510         }
511
512         unixctl_server_run(unixctl);
513
514         unixctl_server_wait(unixctl);
515         if (exiting) {
516             poll_immediate_wake();
517         }
518
519         ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
520         ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
521
522         if (br_int) {
523             ofctrl_wait();
524         }
525         poll_block();
526         if (should_service_stop()) {
527             exiting = true;
528         }
529     }
530
531     /* It's time to exit.  Clean up the databases. */
532     bool done = false;
533     while (!done) {
534         struct controller_ctx ctx = {
535             .ovs_idl = ovs_idl_loop.idl,
536             .ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop),
537             .ovnsb_idl = ovnsb_idl_loop.idl,
538             .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
539         };
540
541         const struct ovsrec_bridge *br_int = get_br_int(&ctx);
542         const char *chassis_id = get_chassis_id(ctx.ovs_idl);
543
544         /* Run all of the cleanup functions, even if one of them returns false.
545          * We're done if all of them return true. */
546         done = binding_cleanup(&ctx, chassis_id);
547         done = chassis_cleanup(&ctx, chassis_id) && done;
548         done = encaps_cleanup(&ctx, br_int) && done;
549         if (done) {
550             poll_immediate_wake();
551         }
552
553         ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
554         ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
555         poll_block();
556     }
557
558     unixctl_server_destroy(unixctl);
559     lflow_destroy();
560     ofctrl_destroy();
561
562     simap_destroy(&ct_zones);
563
564     ovsdb_idl_loop_destroy(&ovs_idl_loop);
565     ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
566
567     free(ovnsb_remote);
568     free(ovs_remote);
569     service_stop();
570
571     exit(retval);
572 }
573
574 static void
575 parse_options(int argc, char *argv[])
576 {
577     enum {
578         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
579         OPT_BOOTSTRAP_CA_CERT,
580         VLOG_OPTION_ENUMS,
581         DAEMON_OPTION_ENUMS
582     };
583
584     static struct option long_options[] = {
585         {"help", no_argument, NULL, 'h'},
586         {"version", no_argument, NULL, 'V'},
587         VLOG_LONG_OPTIONS,
588         DAEMON_LONG_OPTIONS,
589         STREAM_SSL_LONG_OPTIONS,
590         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
591         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
592         {NULL, 0, NULL, 0}
593     };
594     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
595
596     for (;;) {
597         int c;
598
599         c = getopt_long(argc, argv, short_options, long_options, NULL);
600         if (c == -1) {
601             break;
602         }
603
604         switch (c) {
605         case 'h':
606             usage();
607
608         case 'V':
609             ovs_print_version(OFP13_VERSION, OFP13_VERSION);
610             exit(EXIT_SUCCESS);
611
612         VLOG_OPTION_HANDLERS
613         DAEMON_OPTION_HANDLERS
614         STREAM_SSL_OPTION_HANDLERS
615
616         case OPT_PEER_CA_CERT:
617             stream_ssl_set_peer_ca_cert_file(optarg);
618             break;
619
620         case OPT_BOOTSTRAP_CA_CERT:
621             stream_ssl_set_ca_cert_file(optarg, true);
622             break;
623
624         case '?':
625             exit(EXIT_FAILURE);
626
627         default:
628             abort();
629         }
630     }
631     free(short_options);
632
633     argc -= optind;
634     argv += optind;
635
636     if (argc == 0) {
637         ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
638     } else if (argc == 1) {
639         ovs_remote = xstrdup(argv[0]);
640     } else {
641         VLOG_FATAL("exactly zero or one non-option argument required; "
642                    "use --help for usage");
643     }
644 }
645
646 static void
647 usage(void)
648 {
649     printf("%s: OVN controller\n"
650            "usage %s [OPTIONS] [OVS-DATABASE]\n"
651            "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
652                program_name, program_name);
653     stream_usage("OVS-DATABASE", true, false, false);
654     daemon_usage();
655     vlog_usage();
656     printf("\nOther options:\n"
657            "  -h, --help              display this help message\n"
658            "  -V, --version           display version information\n");
659     exit(EXIT_SUCCESS);
660 }
661
662 static void
663 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
664              const char *argv[] OVS_UNUSED, void *exiting_)
665 {
666     bool *exiting = exiting_;
667     *exiting = true;
668
669     unixctl_command_reply(conn, NULL);
670 }
671
672 static void
673 ct_zone_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
674              const char *argv[] OVS_UNUSED, void *ct_zones_)
675 {
676     struct simap *ct_zones = ct_zones_;
677     struct ds ds = DS_EMPTY_INITIALIZER;
678     struct simap_node *zone;
679
680     SIMAP_FOR_EACH(zone, ct_zones) {
681         ds_put_format(&ds, "%s %d\n", zone->name, zone->data);
682     }
683
684     unixctl_command_reply(conn, ds_cstr(&ds));
685     ds_destroy(&ds);
686 }