ovn-controller: Free some more memory before exit.
[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 <errno.h>
19 #include <getopt.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "command-line.h"
25 #include "compiler.h"
26 #include "daemon.h"
27 #include "dirs.h"
28 #include "openvswitch/vconn.h"
29 #include "openvswitch/vlog.h"
30 #include "ovn/lib/ovn-sb-idl.h"
31 #include "poll-loop.h"
32 #include "fatal-signal.h"
33 #include "lib/vswitch-idl.h"
34 #include "smap.h"
35 #include "stream.h"
36 #include "stream-ssl.h"
37 #include "unixctl.h"
38 #include "util.h"
39
40 #include "ovn-controller.h"
41 #include "bindings.h"
42 #include "chassis.h"
43
44 VLOG_DEFINE_THIS_MODULE(main);
45
46 static unixctl_cb_func ovn_controller_exit;
47
48 #define DEFAULT_BRIDGE_NAME "br-int"
49
50 static void parse_options(int argc, char *argv[]);
51 OVS_NO_RETURN static void usage(void);
52
53 static char *ovs_remote;
54 static char *ovnsb_remote;
55
56
57 static void
58 get_initial_snapshot(struct ovsdb_idl *idl)
59 {
60     while (1) {
61         ovsdb_idl_run(idl);
62         if (ovsdb_idl_has_ever_connected(idl)) {
63             return;
64         }
65         ovsdb_idl_wait(idl);
66         poll_block();
67     }
68 }
69
70 static const struct ovsrec_bridge *
71 get_bridge(struct controller_ctx *ctx, const char *name)
72 {
73     const struct ovsrec_bridge *br;
74
75     OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
76         if (!strcmp(br->name, name)) {
77             return br;
78         }
79     }
80
81     return NULL;
82 }
83
84 /* Retrieve the OVN integration bridge from the "external-ids:ovn-bridge"
85  * key, the remote location from the "external-ids:ovn-remote" key, and
86  * the chassis name from the "external-ids:system-id" key in the
87  * Open_vSwitch table of the OVS database instance.
88  *
89  * xxx ovn-controller does not support changing any of these mid-run,
90  * xxx but that should be addressed later. */
91 static void
92 get_core_config(struct controller_ctx *ctx)
93 {
94     const struct ovsrec_open_vswitch *cfg;
95
96     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
97     if (!cfg) {
98         VLOG_ERR("No Open_vSwitch row defined.");
99         ovsdb_idl_destroy(ctx->ovs_idl);
100         exit(EXIT_FAILURE);
101     }
102
103     while (1) {
104         const struct ovsrec_bridge *br_int;
105         const char *remote, *system_id, *br_int_name;
106
107         ovsdb_idl_run(ctx->ovs_idl);
108
109         br_int_name = smap_get(&cfg->external_ids, "ovn-bridge");
110         if (!br_int_name) {
111             br_int_name = DEFAULT_BRIDGE_NAME;
112         }
113         ctx->br_int_name = xstrdup(br_int_name);
114
115         br_int = get_bridge(ctx, ctx->br_int_name);
116         if (!br_int) {
117             VLOG_INFO("Integration bridge '%s' does not exist.  Waiting...",
118                       ctx->br_int_name);
119             goto try_again;
120         }
121
122         remote = smap_get(&cfg->external_ids, "ovn-remote");
123         if (!remote) {
124             VLOG_INFO("OVN OVSDB remote not specified.  Waiting...");
125             goto try_again;
126         }
127
128         system_id = smap_get(&cfg->external_ids, "system-id");
129         if (!system_id) {
130             VLOG_INFO("system-id not specified.  Waiting...");
131             goto try_again;
132         }
133
134         ovnsb_remote = xstrdup(remote);
135         ctx->chassis_id = xstrdup(system_id);
136         return;
137
138 try_again:
139         ovsdb_idl_wait(ctx->ovs_idl);
140         poll_block();
141     }
142
143 }
144
145 int
146 main(int argc, char *argv[])
147 {
148     struct unixctl_server *unixctl;
149     struct controller_ctx ctx = { .chassis_id = NULL };
150     bool exiting;
151     int retval;
152
153     ovs_cmdl_proctitle_init(argc, argv);
154     set_program_name(argv[0]);
155     parse_options(argc, argv);
156     fatal_ignore_sigpipe();
157
158     daemonize_start();
159
160     retval = unixctl_server_create(NULL, &unixctl);
161     if (retval) {
162         exit(EXIT_FAILURE);
163     }
164     unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
165
166     daemonize_complete();
167
168     ovsrec_init();
169     sbrec_init();
170
171     /* Connect to OVS OVSDB instance.  We do not monitor all tables by
172      * default, so modules must register their interest explicitly.  */
173     ctx.ovs_idl = ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true);
174
175     /* Register interest in "external_ids" column in "Open_vSwitch" table,
176      * since we'll need to get the OVN OVSDB remote. */
177     ovsdb_idl_add_table(ctx.ovs_idl, &ovsrec_table_open_vswitch);
178     ovsdb_idl_add_column(ctx.ovs_idl, &ovsrec_open_vswitch_col_external_ids);
179
180     chassis_init(&ctx);
181     bindings_init(&ctx);
182
183     get_initial_snapshot(ctx.ovs_idl);
184
185     get_core_config(&ctx);
186
187     ctx.ovnsb_idl = ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class,
188                                      true, true);
189
190     get_initial_snapshot(ctx.ovnsb_idl);
191
192     exiting = false;
193     while (!exiting) {
194         ovsdb_idl_run(ctx.ovs_idl);
195         ovsdb_idl_run(ctx.ovnsb_idl);
196
197         /* xxx If run into any surprising changes, we exit.  We should
198          * xxx handle this more gracefully. */
199         ctx.br_int = get_bridge(&ctx, ctx.br_int_name);
200         if (!ctx.br_int) {
201             VLOG_ERR("Integration bridge '%s' disappeared",
202                      ctx.br_int_name);
203             retval = EXIT_FAILURE;
204             break;
205         }
206
207         if (!ovsdb_idl_is_alive(ctx.ovnsb_idl)) {
208             int retval = ovsdb_idl_get_last_error(ctx.ovnsb_idl);
209             VLOG_ERR("%s: database connection failed (%s)",
210                      ovnsb_remote, ovs_retval_to_string(retval));
211             retval = EXIT_FAILURE;
212             break;
213         }
214
215         if (!ovsdb_idl_is_alive(ctx.ovs_idl)) {
216             int retval = ovsdb_idl_get_last_error(ctx.ovs_idl);
217             VLOG_ERR("%s: database connection failed (%s)",
218                      ovs_remote, ovs_retval_to_string(retval));
219             retval = EXIT_FAILURE;
220             break;
221         }
222
223         chassis_run(&ctx);
224         bindings_run(&ctx);
225         unixctl_server_run(unixctl);
226
227         unixctl_server_wait(unixctl);
228         if (exiting) {
229             poll_immediate_wake();
230         }
231
232         ovsdb_idl_wait(ctx.ovs_idl);
233         ovsdb_idl_wait(ctx.ovnsb_idl);
234         poll_block();
235     }
236
237     unixctl_server_destroy(unixctl);
238     bindings_destroy(&ctx);
239     chassis_destroy(&ctx);
240
241     ovsdb_idl_destroy(ctx.ovs_idl);
242     ovsdb_idl_destroy(ctx.ovnsb_idl);
243
244     free(ctx.br_int_name);
245     free(ctx.chassis_id);
246     free(ovnsb_remote);
247     free(ovs_remote);
248
249     exit(retval);
250 }
251
252 static void
253 parse_options(int argc, char *argv[])
254 {
255     enum {
256         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
257         VLOG_OPTION_ENUMS,
258         DAEMON_OPTION_ENUMS
259     };
260
261     static struct option long_options[] = {
262         {"help", no_argument, NULL, 'h'},
263         {"version", no_argument, NULL, 'V'},
264         VLOG_LONG_OPTIONS,
265         DAEMON_LONG_OPTIONS,
266         STREAM_SSL_LONG_OPTIONS,
267         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
268         {NULL, 0, NULL, 0}
269     };
270     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
271
272     for (;;) {
273         int c;
274
275         c = getopt_long(argc, argv, short_options, long_options, NULL);
276         if (c == -1) {
277             break;
278         }
279
280         switch (c) {
281         case 'h':
282             usage();
283
284         case 'V':
285             ovs_print_version(OFP13_VERSION, OFP13_VERSION);
286             exit(EXIT_SUCCESS);
287
288         VLOG_OPTION_HANDLERS
289         DAEMON_OPTION_HANDLERS
290         STREAM_SSL_OPTION_HANDLERS
291
292         case OPT_PEER_CA_CERT:
293             stream_ssl_set_peer_ca_cert_file(optarg);
294             break;
295
296         case '?':
297             exit(EXIT_FAILURE);
298
299         default:
300             abort();
301         }
302     }
303     free(short_options);
304
305     argc -= optind;
306     argv += optind;
307
308     if (argc == 0) {
309         ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
310     } else if (argc == 1) {
311         ovs_remote = xstrdup(argv[0]);
312     } else {
313         VLOG_FATAL("exactly zero or one non-option argument required; "
314                    "use --help for usage");
315     }
316 }
317
318 static void
319 usage(void)
320 {
321     printf("%s: OVN controller\n"
322            "usage %s [OPTIONS] [OVS-DATABASE]\n"
323            "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
324                program_name, program_name);
325     stream_usage("OVS-DATABASE", true, false, false);
326     daemon_usage();
327     vlog_usage();
328     printf("\nOther options:\n"
329            "  -h, --help              display this help message\n"
330            "  -V, --version           display version information\n");
331     exit(EXIT_SUCCESS);
332 }
333
334 static void
335 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
336              const char *argv[] OVS_UNUSED, void *exiting_)
337 {
338     bool *exiting = exiting_;
339     *exiting = true;
340
341     unixctl_command_reply(conn, NULL);
342 }