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;
61 get_initial_snapshot(struct ovsdb_idl *idl)
65 if (ovsdb_idl_has_ever_connected(idl)) {
73 static const struct ovsrec_bridge *
74 get_br_int(struct ovsdb_idl *ovs_idl)
76 const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
81 const char *br_int_name = smap_get(&cfg->external_ids, "ovn-bridge");
83 br_int_name = DEFAULT_BRIDGE_NAME;
86 const struct ovsrec_bridge *br;
87 OVSREC_BRIDGE_FOR_EACH (br, ovs_idl) {
88 if (!strcmp(br->name, br_int_name)) {
93 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
94 VLOG_WARN_RL(&rl, "%s: integration bridge does not exist", br_int_name);
99 get_chassis_id(const struct ovsdb_idl *ovs_idl)
101 const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
102 return cfg ? smap_get(&cfg->external_ids, "system-id") : NULL;
105 /* Retrieves the OVN Southbound remote location from the
106 * "external-ids:ovn-remote" key in 'ovs_idl' and returns a copy of it.
108 * XXX ovn-controller does not support this changing mid-run, but that should
109 * be addressed later. */
111 get_ovnsb_remote(struct ovsdb_idl *ovs_idl)
114 ovsdb_idl_run(ovs_idl);
116 const struct ovsrec_open_vswitch *cfg
117 = ovsrec_open_vswitch_first(ovs_idl);
119 const char *remote = smap_get(&cfg->external_ids, "ovn-remote");
121 return xstrdup(remote);
125 VLOG_INFO("OVN OVSDB remote not specified. Waiting...");
126 ovsdb_idl_wait(ovs_idl);
132 struct ovsdb_idl *idl;
133 unsigned int skip_seqno;
135 struct ovsdb_idl_txn *committing_txn;
136 unsigned int precommit_seqno;
138 struct ovsdb_idl_txn *open_txn;
141 #define IDL_LOOP_INITIALIZER(IDL) { .idl = (IDL) }
144 idl_loop_destroy(struct idl_loop *loop)
147 ovsdb_idl_destroy(loop->idl);
151 static struct ovsdb_idl_txn *
152 idl_loop_run(struct idl_loop *loop)
154 ovsdb_idl_run(loop->idl);
155 loop->open_txn = (loop->committing_txn
156 || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
158 : ovsdb_idl_txn_create(loop->idl));
159 return loop->open_txn;
163 idl_loop_commit_and_wait(struct idl_loop *loop)
165 if (loop->open_txn) {
166 loop->committing_txn = loop->open_txn;
167 loop->open_txn = NULL;
169 loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
172 struct ovsdb_idl_txn *txn = loop->committing_txn;
174 enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
175 if (status != TXN_INCOMPLETE) {
178 /* We want to re-evaluate the database when it's changed from
179 * the contents that it had when we started the commit. (That
180 * might have already happened.) */
181 loop->skip_seqno = loop->precommit_seqno;
182 if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
183 poll_immediate_wake();
188 /* If the database has already changed since we started the
189 * commit, re-evaluate it immediately to avoid missing a change
191 if (ovsdb_idl_get_seqno(loop->idl) != loop->precommit_seqno) {
192 poll_immediate_wake();
202 case TXN_UNCOMMITTED:
207 ovsdb_idl_txn_destroy(txn);
208 loop->committing_txn = NULL;
212 ovsdb_idl_wait(loop->idl);
216 main(int argc, char *argv[])
218 struct unixctl_server *unixctl;
222 ovs_cmdl_proctitle_init(argc, argv);
223 set_program_name(argv[0]);
224 parse_options(argc, argv);
225 fatal_ignore_sigpipe();
229 retval = unixctl_server_create(NULL, &unixctl);
233 unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
235 daemonize_complete();
243 /* Connect to OVS OVSDB instance. We do not monitor all tables by
244 * default, so modules must register their interest explicitly. */
245 struct idl_loop ovs_idl_loop = IDL_LOOP_INITIALIZER(
246 ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true));
247 ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_open_vswitch);
248 ovsdb_idl_add_column(ovs_idl_loop.idl,
249 &ovsrec_open_vswitch_col_external_ids);
250 chassis_register_ovs_idl(ovs_idl_loop.idl);
251 encaps_register_ovs_idl(ovs_idl_loop.idl);
252 binding_register_ovs_idl(ovs_idl_loop.idl);
253 physical_register_ovs_idl(ovs_idl_loop.idl);
254 get_initial_snapshot(ovs_idl_loop.idl);
256 /* Connect to OVN SB database. */
257 char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl);
258 struct idl_loop ovnsb_idl_loop = IDL_LOOP_INITIALIZER(
259 ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));
260 get_initial_snapshot(ovnsb_idl_loop.idl);
265 struct controller_ctx ctx = {
266 .ovs_idl = ovs_idl_loop.idl,
267 .ovs_idl_txn = idl_loop_run(&ovs_idl_loop),
268 .ovnsb_idl = ovnsb_idl_loop.idl,
269 .ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop),
272 const struct ovsrec_bridge *br_int = get_br_int(ctx.ovs_idl);
273 const char *chassis_id = get_chassis_id(ctx.ovs_idl);
276 chassis_run(&ctx, chassis_id);
277 encaps_run(&ctx, br_int, chassis_id);
278 binding_run(&ctx, br_int, chassis_id);
284 struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
285 lflow_run(&ctx, &flow_table);
287 physical_run(&ctx, br_int, chassis_id, &flow_table);
289 ofctrl_put(&flow_table);
290 hmap_destroy(&flow_table);
293 unixctl_server_run(unixctl);
295 unixctl_server_wait(unixctl);
297 poll_immediate_wake();
300 idl_loop_commit_and_wait(&ovnsb_idl_loop);
301 idl_loop_commit_and_wait(&ovs_idl_loop);
309 /* It's time to exit. Clean up the databases. */
312 struct controller_ctx ctx = {
313 .ovs_idl = ovs_idl_loop.idl,
314 .ovs_idl_txn = idl_loop_run(&ovs_idl_loop),
315 .ovnsb_idl = ovnsb_idl_loop.idl,
316 .ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop),
319 const struct ovsrec_bridge *br_int = get_br_int(ctx.ovs_idl);
320 const char *chassis_id = get_chassis_id(ctx.ovs_idl);
322 /* Run all of the cleanup functions, even if one of them returns false.
323 * We're done if all of them return true. */
324 done = binding_cleanup(&ctx, chassis_id);
325 done = chassis_cleanup(&ctx, chassis_id) && done;
326 done = encaps_cleanup(&ctx, br_int) && done;
328 poll_immediate_wake();
331 idl_loop_commit_and_wait(&ovnsb_idl_loop);
332 idl_loop_commit_and_wait(&ovs_idl_loop);
336 unixctl_server_destroy(unixctl);
340 idl_loop_destroy(&ovs_idl_loop);
341 idl_loop_destroy(&ovnsb_idl_loop);
350 parse_options(int argc, char *argv[])
353 OPT_PEER_CA_CERT = UCHAR_MAX + 1,
358 static struct option long_options[] = {
359 {"help", no_argument, NULL, 'h'},
360 {"version", no_argument, NULL, 'V'},
363 STREAM_SSL_LONG_OPTIONS,
364 {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
367 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
372 c = getopt_long(argc, argv, short_options, long_options, NULL);
382 ovs_print_version(OFP13_VERSION, OFP13_VERSION);
386 DAEMON_OPTION_HANDLERS
387 STREAM_SSL_OPTION_HANDLERS
389 case OPT_PEER_CA_CERT:
390 stream_ssl_set_peer_ca_cert_file(optarg);
406 ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
407 } else if (argc == 1) {
408 ovs_remote = xstrdup(argv[0]);
410 VLOG_FATAL("exactly zero or one non-option argument required; "
411 "use --help for usage");
418 printf("%s: OVN controller\n"
419 "usage %s [OPTIONS] [OVS-DATABASE]\n"
420 "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
421 program_name, program_name);
422 stream_usage("OVS-DATABASE", true, false, false);
425 printf("\nOther options:\n"
426 " -h, --help display this help message\n"
427 " -V, --version display version information\n");
432 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
433 const char *argv[] OVS_UNUSED, void *exiting_)
435 bool *exiting = exiting_;
438 unixctl_command_reply(conn, NULL);