]> git.proxmox.com Git - mirror_ovs.git/blob - ovn/controller/ovn-controller.c
Merge "master" into "ovn".
[mirror_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 }