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