1 /* Copyright (c) 2015 Nicira, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "ovn-controller.h"
26 #include "command-line.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"
38 #include "stream-ssl.h"
49 VLOG_DEFINE_THIS_MODULE(main);
51 static unixctl_cb_func ovn_controller_exit;
53 #define DEFAULT_BRIDGE_NAME "br-int"
55 static void parse_options(int argc, char *argv[]);
56 OVS_NO_RETURN static void usage(void);
58 static char *ovs_remote;
59 static char *ovnsb_remote;
63 get_initial_snapshot(struct ovsdb_idl *idl)
67 if (ovsdb_idl_has_ever_connected(idl)) {
75 static const struct ovsrec_bridge *
76 get_bridge(struct controller_ctx *ctx, const char *name)
78 const struct ovsrec_bridge *br;
80 OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
81 if (!strcmp(br->name, name)) {
89 /* Retrieve the OVN integration bridge from the "external-ids:ovn-bridge"
90 * key, the remote location from the "external-ids:ovn-remote" key, and
91 * the chassis name from the "external-ids:system-id" key in the
92 * Open_vSwitch table of the OVS database instance.
94 * xxx ovn-controller does not support changing any of these mid-run,
95 * xxx but that should be addressed later. */
97 get_core_config(struct controller_ctx *ctx, char **br_int_namep)
100 ovsdb_idl_run(ctx->ovs_idl);
102 const struct ovsrec_open_vswitch *cfg;
103 cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
105 VLOG_ERR("No Open_vSwitch row defined.");
106 ovsdb_idl_destroy(ctx->ovs_idl);
110 const struct ovsrec_bridge *br_int;
111 const char *remote, *system_id, *br_int_name;
113 br_int_name = smap_get(&cfg->external_ids, "ovn-bridge");
115 br_int_name = DEFAULT_BRIDGE_NAME;
118 br_int = get_bridge(ctx, br_int_name);
120 VLOG_INFO("Integration bridge '%s' does not exist. Waiting...",
125 remote = smap_get(&cfg->external_ids, "ovn-remote");
127 VLOG_INFO("OVN OVSDB remote not specified. Waiting...");
131 system_id = smap_get(&cfg->external_ids, "system-id");
133 VLOG_INFO("system-id not specified. Waiting...");
137 ovnsb_remote = xstrdup(remote);
138 ctx->chassis_id = xstrdup(system_id);
139 *br_int_namep = xstrdup(br_int_name);
143 ovsdb_idl_wait(ctx->ovs_idl);
150 struct ovsdb_idl *idl;
151 unsigned int skip_seqno;
153 struct ovsdb_idl_txn *committing_txn;
154 unsigned int precommit_seqno;
156 struct ovsdb_idl_txn *open_txn;
159 #define IDL_LOOP_INITIALIZER(IDL) { .idl = (IDL) }
162 idl_loop_destroy(struct idl_loop *loop)
165 ovsdb_idl_destroy(loop->idl);
169 static struct ovsdb_idl_txn *
170 idl_loop_run(struct idl_loop *loop)
172 ovsdb_idl_run(loop->idl);
173 loop->open_txn = (loop->committing_txn
174 || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
176 : ovsdb_idl_txn_create(loop->idl));
177 return loop->open_txn;
181 idl_loop_commit_and_wait(struct idl_loop *loop)
183 if (loop->open_txn) {
184 loop->committing_txn = loop->open_txn;
185 loop->open_txn = NULL;
187 loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
190 struct ovsdb_idl_txn *txn = loop->committing_txn;
192 enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
193 if (status != TXN_INCOMPLETE) {
196 /* We want to re-evaluate the database when it's changed from
197 * the contents that it had when we started the commit. (That
198 * might have already happened.) */
199 loop->skip_seqno = loop->precommit_seqno;
200 if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
201 poll_immediate_wake();
206 /* If the database has already changed since we started the
207 * commit, re-evaluate it immediately to avoid missing a change
209 if (ovsdb_idl_get_seqno(loop->idl) != loop->precommit_seqno) {
210 poll_immediate_wake();
220 case TXN_UNCOMMITTED:
225 ovsdb_idl_txn_destroy(txn);
226 loop->committing_txn = NULL;
230 ovsdb_idl_wait(loop->idl);
234 main(int argc, char *argv[])
236 struct unixctl_server *unixctl;
237 struct controller_ctx ctx = { .chassis_id = NULL };
241 ovs_cmdl_proctitle_init(argc, argv);
242 set_program_name(argv[0]);
243 parse_options(argc, argv);
244 fatal_ignore_sigpipe();
248 retval = unixctl_server_create(NULL, &unixctl);
252 unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
254 daemonize_complete();
261 /* Connect to OVS OVSDB instance. We do not monitor all tables by
262 * default, so modules must register their interest explicitly. */
263 ctx.ovs_idl = ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true);
265 /* Register interest in "external_ids" column in "Open_vSwitch" table,
266 * since we'll need to get the OVN OVSDB remote. */
267 ovsdb_idl_add_table(ctx.ovs_idl, &ovsrec_table_open_vswitch);
268 ovsdb_idl_add_column(ctx.ovs_idl, &ovsrec_open_vswitch_col_external_ids);
276 get_initial_snapshot(ctx.ovs_idl);
279 get_core_config(&ctx, &br_int_name);
281 ctx.ovnsb_idl = ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class,
283 get_initial_snapshot(ctx.ovnsb_idl);
285 struct idl_loop ovnsb_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovnsb_idl);
286 struct idl_loop ovs_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovs_idl);
291 ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop);
292 ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop);
294 /* xxx If run into any surprising changes, we exit. We should
295 * xxx handle this more gracefully. */
296 const struct ovsrec_bridge *br_int = get_bridge(&ctx, br_int_name);
298 VLOG_ERR("Integration bridge '%s' disappeared", br_int_name);
299 retval = EXIT_FAILURE;
304 encaps_run(&ctx, br_int);
305 binding_run(&ctx, br_int);
307 struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
308 pipeline_run(&ctx, &flow_table);
309 physical_run(&ctx, br_int, &flow_table);
310 ofctrl_run(br_int, &flow_table);
311 hmap_destroy(&flow_table);
313 unixctl_server_run(unixctl);
315 unixctl_server_wait(unixctl);
317 poll_immediate_wake();
320 idl_loop_commit_and_wait(&ovnsb_idl_loop);
321 idl_loop_commit_and_wait(&ovs_idl_loop);
327 /* It's time to exit. Clean up the databases. */
330 ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop);
331 ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop);
333 /* xxx If run into any surprising changes, we exit. We should
334 * xxx handle this more gracefully. */
335 const struct ovsrec_bridge *br_int = get_bridge(&ctx, br_int_name);
337 VLOG_ERR("Integration bridge '%s' disappeared", br_int_name);
338 retval = EXIT_FAILURE;
342 /* Run all of the cleanup functions, even if one of them returns false.
343 * We're done if all of them return true. */
344 done = binding_cleanup(&ctx);
345 done = chassis_cleanup(&ctx) && done;
346 done = encaps_cleanup(&ctx, br_int) && done;
348 poll_immediate_wake();
351 idl_loop_commit_and_wait(&ovnsb_idl_loop);
352 idl_loop_commit_and_wait(&ovs_idl_loop);
357 unixctl_server_destroy(unixctl);
358 pipeline_destroy(&ctx);
361 idl_loop_destroy(&ovs_idl_loop);
362 idl_loop_destroy(&ovnsb_idl_loop);
365 free(ctx.chassis_id);
373 parse_options(int argc, char *argv[])
376 OPT_PEER_CA_CERT = UCHAR_MAX + 1,
381 static struct option long_options[] = {
382 {"help", no_argument, NULL, 'h'},
383 {"version", no_argument, NULL, 'V'},
386 STREAM_SSL_LONG_OPTIONS,
387 {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
390 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
395 c = getopt_long(argc, argv, short_options, long_options, NULL);
405 ovs_print_version(OFP13_VERSION, OFP13_VERSION);
409 DAEMON_OPTION_HANDLERS
410 STREAM_SSL_OPTION_HANDLERS
412 case OPT_PEER_CA_CERT:
413 stream_ssl_set_peer_ca_cert_file(optarg);
429 ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
430 } else if (argc == 1) {
431 ovs_remote = xstrdup(argv[0]);
433 VLOG_FATAL("exactly zero or one non-option argument required; "
434 "use --help for usage");
441 printf("%s: OVN controller\n"
442 "usage %s [OPTIONS] [OVS-DATABASE]\n"
443 "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
444 program_name, program_name);
445 stream_usage("OVS-DATABASE", true, false, false);
448 printf("\nOther options:\n"
449 " -h, --help display this help message\n"
450 " -V, --version display version information\n");
455 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
456 const char *argv[] OVS_UNUSED, void *exiting_)
458 bool *exiting = exiting_;
461 unixctl_command_reply(conn, NULL);