]> git.proxmox.com Git - mirror_ovs.git/blob - ovn/controller/ovn-controller.c
ovn-controller: Explicitly pass the flow table from function to function.
[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 while (1) {
99 ovsdb_idl_run(ctx->ovs_idl);
100
101 const struct ovsrec_open_vswitch *cfg;
102 cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
103 if (!cfg) {
104 VLOG_ERR("No Open_vSwitch row defined.");
105 ovsdb_idl_destroy(ctx->ovs_idl);
106 exit(EXIT_FAILURE);
107 }
108
109 const struct ovsrec_bridge *br_int;
110 const char *remote, *system_id, *br_int_name;
111
112 br_int_name = smap_get(&cfg->external_ids, "ovn-bridge");
113 if (!br_int_name) {
114 br_int_name = DEFAULT_BRIDGE_NAME;
115 }
116 ctx->br_int_name = xstrdup(br_int_name);
117
118 br_int = get_bridge(ctx, ctx->br_int_name);
119 if (!br_int) {
120 VLOG_INFO("Integration bridge '%s' does not exist. Waiting...",
121 ctx->br_int_name);
122 goto try_again;
123 }
124
125 remote = smap_get(&cfg->external_ids, "ovn-remote");
126 if (!remote) {
127 VLOG_INFO("OVN OVSDB remote not specified. Waiting...");
128 goto try_again;
129 }
130
131 system_id = smap_get(&cfg->external_ids, "system-id");
132 if (!system_id) {
133 VLOG_INFO("system-id not specified. Waiting...");
134 goto try_again;
135 }
136
137 ovnsb_remote = xstrdup(remote);
138 ctx->chassis_id = xstrdup(system_id);
139 return;
140
141 try_again:
142 ovsdb_idl_wait(ctx->ovs_idl);
143 poll_block();
144 }
145
146 }
147
148 struct idl_loop {
149 struct ovsdb_idl *idl;
150 unsigned int skip_seqno;
151
152 struct ovsdb_idl_txn *committing_txn;
153 unsigned int precommit_seqno;
154
155 struct ovsdb_idl_txn *open_txn;
156 };
157
158 #define IDL_LOOP_INITIALIZER(IDL) { .idl = (IDL) }
159
160 static void
161 idl_loop_destroy(struct idl_loop *loop)
162 {
163 if (loop) {
164 ovsdb_idl_destroy(loop->idl);
165 }
166 }
167
168 static struct ovsdb_idl_txn *
169 idl_loop_run(struct idl_loop *loop)
170 {
171 ovsdb_idl_run(loop->idl);
172 loop->open_txn = (loop->committing_txn
173 || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
174 ? NULL
175 : ovsdb_idl_txn_create(loop->idl));
176 return loop->open_txn;
177 }
178
179 static void
180 idl_loop_commit_and_wait(struct idl_loop *loop)
181 {
182 if (loop->open_txn) {
183 loop->committing_txn = loop->open_txn;
184 loop->open_txn = NULL;
185
186 loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
187 }
188
189 struct ovsdb_idl_txn *txn = loop->committing_txn;
190 if (txn) {
191 enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
192 switch (status) {
193 case TXN_INCOMPLETE:
194 break;
195
196 case TXN_TRY_AGAIN:
197 loop->skip_seqno = loop->precommit_seqno;
198 if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
199 poll_immediate_wake();
200 }
201 /* Fall through. */
202 case TXN_UNCHANGED:
203 case TXN_ABORTED:
204 case TXN_SUCCESS:
205 case TXN_NOT_LOCKED:
206 case TXN_ERROR:
207 ovsdb_idl_txn_destroy(txn);
208 loop->committing_txn = NULL;
209 break;
210
211 case TXN_UNCOMMITTED:
212 OVS_NOT_REACHED();
213
214 }
215 }
216
217 ovsdb_idl_wait(loop->idl);
218 }
219
220 int
221 main(int argc, char *argv[])
222 {
223 struct unixctl_server *unixctl;
224 struct controller_ctx ctx = { .chassis_id = NULL };
225 bool exiting;
226 int retval;
227
228 ovs_cmdl_proctitle_init(argc, argv);
229 set_program_name(argv[0]);
230 parse_options(argc, argv);
231 fatal_ignore_sigpipe();
232
233 daemonize_start();
234
235 retval = unixctl_server_create(NULL, &unixctl);
236 if (retval) {
237 exit(EXIT_FAILURE);
238 }
239 unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
240
241 daemonize_complete();
242
243 ovsrec_init();
244 sbrec_init();
245
246 ofctrl_init();
247
248 /* Connect to OVS OVSDB instance. We do not monitor all tables by
249 * default, so modules must register their interest explicitly. */
250 ctx.ovs_idl = ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true);
251
252 /* Register interest in "external_ids" column in "Open_vSwitch" table,
253 * since we'll need to get the OVN OVSDB remote. */
254 ovsdb_idl_add_table(ctx.ovs_idl, &ovsrec_table_open_vswitch);
255 ovsdb_idl_add_column(ctx.ovs_idl, &ovsrec_open_vswitch_col_external_ids);
256
257 chassis_init(&ctx);
258 binding_init(&ctx);
259 physical_init(&ctx);
260 pipeline_init();
261
262 get_initial_snapshot(ctx.ovs_idl);
263
264 get_core_config(&ctx);
265
266 ctx.ovnsb_idl = ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class,
267 true, true);
268 get_initial_snapshot(ctx.ovnsb_idl);
269
270 struct idl_loop ovnsb_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovnsb_idl);
271 struct idl_loop ovs_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovs_idl);
272
273 /* Main loop. */
274 exiting = false;
275 while (!exiting) {
276 ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop);
277 ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop);
278
279 /* xxx If run into any surprising changes, we exit. We should
280 * xxx handle this more gracefully. */
281 ctx.br_int = get_bridge(&ctx, ctx.br_int_name);
282 if (!ctx.br_int) {
283 VLOG_ERR("Integration bridge '%s' disappeared",
284 ctx.br_int_name);
285 retval = EXIT_FAILURE;
286 goto exit;
287 }
288
289 chassis_run(&ctx);
290 binding_run(&ctx);
291
292 struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
293 pipeline_run(&ctx, &flow_table);
294 physical_run(&ctx, &flow_table);
295 ofctrl_run(&ctx, &flow_table);
296 hmap_destroy(&flow_table);
297
298 unixctl_server_run(unixctl);
299
300 unixctl_server_wait(unixctl);
301 if (exiting) {
302 poll_immediate_wake();
303 }
304
305 idl_loop_commit_and_wait(&ovnsb_idl_loop);
306 idl_loop_commit_and_wait(&ovs_idl_loop);
307
308 ofctrl_wait();
309 poll_block();
310 }
311
312 /* It's time to exit. Clean up the databases. */
313 bool done = false;
314 while (!done) {
315 ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop);
316 ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop);
317
318 /* xxx If run into any surprising changes, we exit. We should
319 * xxx handle this more gracefully. */
320 ctx.br_int = get_bridge(&ctx, ctx.br_int_name);
321 if (!ctx.br_int) {
322 VLOG_ERR("Integration bridge '%s' disappeared",
323 ctx.br_int_name);
324 retval = EXIT_FAILURE;
325 goto exit;
326 }
327
328 /* Run both the binding and chassis cleanup, even if one of them
329 * returns false. We're done if both return true. */
330 done = binding_cleanup(&ctx);
331 done = chassis_cleanup(&ctx) && done;
332 if (done) {
333 poll_immediate_wake();
334 }
335
336 idl_loop_commit_and_wait(&ovnsb_idl_loop);
337 idl_loop_commit_and_wait(&ovs_idl_loop);
338 poll_block();
339 }
340
341 exit:
342 unixctl_server_destroy(unixctl);
343 pipeline_destroy(&ctx);
344 ofctrl_destroy();
345
346 idl_loop_destroy(&ovs_idl_loop);
347 idl_loop_destroy(&ovnsb_idl_loop);
348
349 free(ctx.br_int_name);
350 free(ctx.chassis_id);
351 free(ovnsb_remote);
352 free(ovs_remote);
353
354 exit(retval);
355 }
356
357 static void
358 parse_options(int argc, char *argv[])
359 {
360 enum {
361 OPT_PEER_CA_CERT = UCHAR_MAX + 1,
362 VLOG_OPTION_ENUMS,
363 DAEMON_OPTION_ENUMS
364 };
365
366 static struct option long_options[] = {
367 {"help", no_argument, NULL, 'h'},
368 {"version", no_argument, NULL, 'V'},
369 VLOG_LONG_OPTIONS,
370 DAEMON_LONG_OPTIONS,
371 STREAM_SSL_LONG_OPTIONS,
372 {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
373 {NULL, 0, NULL, 0}
374 };
375 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
376
377 for (;;) {
378 int c;
379
380 c = getopt_long(argc, argv, short_options, long_options, NULL);
381 if (c == -1) {
382 break;
383 }
384
385 switch (c) {
386 case 'h':
387 usage();
388
389 case 'V':
390 ovs_print_version(OFP13_VERSION, OFP13_VERSION);
391 exit(EXIT_SUCCESS);
392
393 VLOG_OPTION_HANDLERS
394 DAEMON_OPTION_HANDLERS
395 STREAM_SSL_OPTION_HANDLERS
396
397 case OPT_PEER_CA_CERT:
398 stream_ssl_set_peer_ca_cert_file(optarg);
399 break;
400
401 case '?':
402 exit(EXIT_FAILURE);
403
404 default:
405 abort();
406 }
407 }
408 free(short_options);
409
410 argc -= optind;
411 argv += optind;
412
413 if (argc == 0) {
414 ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
415 } else if (argc == 1) {
416 ovs_remote = xstrdup(argv[0]);
417 } else {
418 VLOG_FATAL("exactly zero or one non-option argument required; "
419 "use --help for usage");
420 }
421 }
422
423 static void
424 usage(void)
425 {
426 printf("%s: OVN controller\n"
427 "usage %s [OPTIONS] [OVS-DATABASE]\n"
428 "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
429 program_name, program_name);
430 stream_usage("OVS-DATABASE", true, false, false);
431 daemon_usage();
432 vlog_usage();
433 printf("\nOther options:\n"
434 " -h, --help display this help message\n"
435 " -V, --version display version information\n");
436 exit(EXIT_SUCCESS);
437 }
438
439 static void
440 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
441 const char *argv[] OVS_UNUSED, void *exiting_)
442 {
443 bool *exiting = exiting_;
444 *exiting = true;
445
446 unixctl_command_reply(conn, NULL);
447 }