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;
60 static const struct ovsrec_bridge *
61 get_bridge(struct ovsdb_idl *ovs_idl, const char *br_name)
63 const struct ovsrec_bridge *br;
64 OVSREC_BRIDGE_FOR_EACH (br, ovs_idl) {
65 if (!strcmp(br->name, br_name)) {
72 static const struct ovsrec_bridge *
73 create_br_int(struct controller_ctx *ctx,
74 const struct ovsrec_open_vswitch *cfg,
75 const char *bridge_name)
77 if (!ctx->ovs_idl_txn) {
81 ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
82 "ovn-controller: creating integration bridge '%s'", bridge_name);
84 struct ovsrec_interface *iface;
85 iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
86 ovsrec_interface_set_name(iface, bridge_name);
87 ovsrec_interface_set_type(iface, "internal");
89 struct ovsrec_port *port;
90 port = ovsrec_port_insert(ctx->ovs_idl_txn);
91 ovsrec_port_set_name(port, bridge_name);
92 ovsrec_port_set_interfaces(port, &iface, 1);
94 struct ovsrec_bridge *bridge;
95 bridge = ovsrec_bridge_insert(ctx->ovs_idl_txn);
96 ovsrec_bridge_set_name(bridge, bridge_name);
97 ovsrec_bridge_set_fail_mode(bridge, "secure");
98 const struct smap oc = SMAP_CONST1(&oc, "disable-in-band", "true");
99 ovsrec_bridge_set_other_config(bridge, &oc);
100 ovsrec_bridge_set_ports(bridge, &port, 1);
102 struct ovsrec_bridge **bridges;
103 size_t bytes = sizeof *bridges * cfg->n_bridges;
104 bridges = xmalloc(bytes + sizeof *bridges);
105 memcpy(bridges, cfg->bridges, bytes);
106 bridges[cfg->n_bridges] = bridge;
107 ovsrec_open_vswitch_verify_bridges(cfg);
108 ovsrec_open_vswitch_set_bridges(cfg, bridges, cfg->n_bridges + 1);
113 static const struct ovsrec_bridge *
114 get_br_int(struct controller_ctx *ctx)
116 const struct ovsrec_open_vswitch *cfg;
117 cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
122 const char *br_int_name = smap_get(&cfg->external_ids, "ovn-bridge");
124 br_int_name = DEFAULT_BRIDGE_NAME;
127 const struct ovsrec_bridge *br;
128 br = get_bridge(ctx->ovs_idl, br_int_name);
130 return create_br_int(ctx, cfg, br_int_name);
136 get_chassis_id(const struct ovsdb_idl *ovs_idl)
138 const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
139 return cfg ? smap_get(&cfg->external_ids, "system-id") : NULL;
143 patch_port_name(const struct ovsrec_bridge *b1, const struct ovsrec_bridge *b2)
145 return xasprintf("patch-%s-to-%s", b1->name, b2->name);
149 * Return true if the port is a patch port from b1 to b2
152 match_patch_port(const struct ovsrec_port *port,
153 const struct ovsrec_bridge *b1,
154 const struct ovsrec_bridge *b2)
156 struct ovsrec_interface *iface;
158 char *peer_port_name;
161 peer_port_name = patch_port_name(b2, b1);
163 for (i = 0; i < port->n_interfaces; i++) {
164 iface = port->interfaces[i];
165 if (strcmp(iface->type, "patch")) {
169 peer = smap_get(&iface->options, "peer");
170 if (peer && !strcmp(peer, peer_port_name)) {
176 free(peer_port_name);
182 create_patch_port(struct controller_ctx *ctx,
184 const struct ovsrec_bridge *b1,
185 const struct ovsrec_bridge *b2)
187 if (!ctx->ovs_idl_txn) {
191 char *port_name = patch_port_name(b1, b2);
192 char *peer_port_name = patch_port_name(b2, b1);
194 ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
195 "ovn-controller: creating patch port '%s' from '%s' to '%s'",
196 port_name, b1->name, b2->name);
198 struct ovsrec_interface *iface;
199 iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
200 ovsrec_interface_set_name(iface, port_name);
201 ovsrec_interface_set_type(iface, "patch");
202 const struct smap options = SMAP_CONST1(&options, "peer", peer_port_name);
203 ovsrec_interface_set_options(iface, &options);
205 struct ovsrec_port *port;
206 port = ovsrec_port_insert(ctx->ovs_idl_txn);
207 ovsrec_port_set_name(port, port_name);
208 ovsrec_port_set_interfaces(port, &iface, 1);
209 const struct smap ids = SMAP_CONST1(&ids, "ovn-patch-port", network);
210 ovsrec_port_set_external_ids(port, &ids);
212 struct ovsrec_port **ports;
213 ports = xmalloc(sizeof *ports * (b1->n_ports + 1));
214 memcpy(ports, b1->ports, sizeof *ports * b1->n_ports);
215 ports[b1->n_ports] = port;
216 ovsrec_bridge_verify_ports(b1);
217 ovsrec_bridge_set_ports(b1, ports, b1->n_ports + 1);
221 free(peer_port_name);
225 create_patch_ports(struct controller_ctx *ctx,
227 struct shash *existing_ports,
228 const struct ovsrec_bridge *b1,
229 const struct ovsrec_bridge *b2)
233 for (i = 0; i < b1->n_ports; i++) {
234 if (match_patch_port(b1->ports[i], b1, b2)) {
235 /* Patch port already exists on b1 */
236 shash_find_and_delete(existing_ports, b1->ports[i]->name);
240 if (i == b1->n_ports) {
241 create_patch_port(ctx, network, b1, b2);
246 init_existing_ports(struct controller_ctx *ctx,
247 struct shash *existing_ports)
249 const struct ovsrec_port *port;
251 OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) {
252 if (smap_get(&port->external_ids, "ovn-patch-port")) {
253 shash_add(existing_ports, port->name, port);
259 remove_port(struct controller_ctx *ctx,
260 const struct ovsrec_port *port)
262 const struct ovsrec_bridge *bridge;
264 /* We know the port we want to delete, but we have to find the bridge its on
265 * to do so. Note this only runs on a config change that should be pretty
267 OVSREC_BRIDGE_FOR_EACH (bridge, ctx->ovs_idl) {
269 for (i = 0; i < bridge->n_ports; i++) {
270 if (bridge->ports[i] != port) {
273 struct ovsrec_port **new_ports;
274 new_ports = xmemdup(bridge->ports,
275 sizeof *new_ports * (bridge->n_ports - 1));
276 if (i != bridge->n_ports - 1) {
277 /* Removed port was not last */
278 new_ports[i] = bridge->ports[bridge->n_ports - 1];
280 ovsrec_bridge_verify_ports(bridge);
281 ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
283 ovsrec_port_delete(port);
290 parse_bridge_mappings(struct controller_ctx *ctx,
291 const struct ovsrec_bridge *br_int,
292 const char *mappings_cfg)
294 struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
295 init_existing_ports(ctx, &existing_ports);
297 char *cur, *next, *start;
298 next = start = xstrdup(mappings_cfg);
299 while ((cur = strsep(&next, ",")) && *cur) {
300 char *network, *bridge = cur;
301 const struct ovsrec_bridge *ovs_bridge;
303 network = strsep(&bridge, ":");
304 if (!bridge || !*network || !*bridge) {
305 VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
310 ovs_bridge = get_bridge(ctx->ovs_idl, bridge);
312 VLOG_WARN("Bridge '%s' not found for network '%s'",
317 create_patch_ports(ctx, network, &existing_ports, br_int, ovs_bridge);
318 create_patch_ports(ctx, network, &existing_ports, ovs_bridge, br_int);
322 /* Any ports left in existing_ports are related to configuration that has
323 * been removed, so we should delete the ports now. */
324 struct shash_node *port_node, *port_next_node;
325 SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) {
326 struct ovsrec_port *port = port_node->data;
327 shash_delete(&existing_ports, port_node);
328 remove_port(ctx, port);
330 shash_destroy(&existing_ports);
334 init_bridge_mappings(struct controller_ctx *ctx,
335 const struct ovsrec_bridge *br_int)
337 const char *mappings_cfg = "";
338 const struct ovsrec_open_vswitch *cfg;
340 cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
342 mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
347 parse_bridge_mappings(ctx, br_int, mappings_cfg);
350 /* Retrieves the OVN Southbound remote location from the
351 * "external-ids:ovn-remote" key in 'ovs_idl' and returns a copy of it.
353 * XXX ovn-controller does not support this changing mid-run, but that should
354 * be addressed later. */
356 get_ovnsb_remote(struct ovsdb_idl *ovs_idl)
359 ovsdb_idl_run(ovs_idl);
361 const struct ovsrec_open_vswitch *cfg
362 = ovsrec_open_vswitch_first(ovs_idl);
364 const char *remote = smap_get(&cfg->external_ids, "ovn-remote");
366 return xstrdup(remote);
370 VLOG_INFO("OVN OVSDB remote not specified. Waiting...");
371 ovsdb_idl_wait(ovs_idl);
377 main(int argc, char *argv[])
379 struct unixctl_server *unixctl;
383 ovs_cmdl_proctitle_init(argc, argv);
384 set_program_name(argv[0]);
385 service_start(&argc, &argv);
386 parse_options(argc, argv);
387 fatal_ignore_sigpipe();
391 retval = unixctl_server_create(NULL, &unixctl);
395 unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
397 daemonize_complete();
405 /* Connect to OVS OVSDB instance. We do not monitor all tables by
406 * default, so modules must register their interest explicitly. */
407 struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
408 ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true));
409 ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_open_vswitch);
410 ovsdb_idl_add_column(ovs_idl_loop.idl,
411 &ovsrec_open_vswitch_col_external_ids);
412 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_open_vswitch_col_bridges);
413 ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_interface);
414 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_name);
415 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_type);
416 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_options);
417 ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_port);
418 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_name);
419 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_interfaces);
420 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_external_ids);
421 ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_bridge);
422 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_ports);
423 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_name);
424 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_fail_mode);
425 ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_other_config);
426 chassis_register_ovs_idl(ovs_idl_loop.idl);
427 encaps_register_ovs_idl(ovs_idl_loop.idl);
428 binding_register_ovs_idl(ovs_idl_loop.idl);
429 physical_register_ovs_idl(ovs_idl_loop.idl);
430 ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl);
432 /* Connect to OVN SB database. */
433 char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl);
434 struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
435 ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));
436 ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl);
441 struct controller_ctx ctx = {
442 .ovs_idl = ovs_idl_loop.idl,
443 .ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop),
444 .ovnsb_idl = ovnsb_idl_loop.idl,
445 .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
448 const struct ovsrec_bridge *br_int = get_br_int(&ctx);
449 const char *chassis_id = get_chassis_id(ctx.ovs_idl);
451 /* Map bridges to local nets from ovn-bridge-mappings */
453 init_bridge_mappings(&ctx, br_int);
457 chassis_run(&ctx, chassis_id);
458 encaps_run(&ctx, br_int, chassis_id);
459 binding_run(&ctx, br_int, chassis_id);
463 enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int);
465 struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
466 lflow_run(&ctx, &flow_table);
468 physical_run(&ctx, mff_ovn_geneve,
469 br_int, chassis_id, &flow_table);
471 ofctrl_put(&flow_table);
472 hmap_destroy(&flow_table);
475 unixctl_server_run(unixctl);
477 unixctl_server_wait(unixctl);
479 poll_immediate_wake();
482 ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
483 ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
489 if (should_service_stop()) {
494 /* It's time to exit. Clean up the databases. */
497 struct controller_ctx ctx = {
498 .ovs_idl = ovs_idl_loop.idl,
499 .ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop),
500 .ovnsb_idl = ovnsb_idl_loop.idl,
501 .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
504 const struct ovsrec_bridge *br_int = get_br_int(&ctx);
505 const char *chassis_id = get_chassis_id(ctx.ovs_idl);
507 /* Run all of the cleanup functions, even if one of them returns false.
508 * We're done if all of them return true. */
509 done = binding_cleanup(&ctx, chassis_id);
510 done = chassis_cleanup(&ctx, chassis_id) && done;
511 done = encaps_cleanup(&ctx, br_int) && done;
513 poll_immediate_wake();
516 ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
517 ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
521 unixctl_server_destroy(unixctl);
525 ovsdb_idl_loop_destroy(&ovs_idl_loop);
526 ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
536 parse_options(int argc, char *argv[])
539 OPT_PEER_CA_CERT = UCHAR_MAX + 1,
540 OPT_BOOTSTRAP_CA_CERT,
545 static struct option long_options[] = {
546 {"help", no_argument, NULL, 'h'},
547 {"version", no_argument, NULL, 'V'},
550 STREAM_SSL_LONG_OPTIONS,
551 {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
552 {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
555 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
560 c = getopt_long(argc, argv, short_options, long_options, NULL);
570 ovs_print_version(OFP13_VERSION, OFP13_VERSION);
574 DAEMON_OPTION_HANDLERS
575 STREAM_SSL_OPTION_HANDLERS
577 case OPT_PEER_CA_CERT:
578 stream_ssl_set_peer_ca_cert_file(optarg);
581 case OPT_BOOTSTRAP_CA_CERT:
582 stream_ssl_set_ca_cert_file(optarg, true);
598 ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
599 } else if (argc == 1) {
600 ovs_remote = xstrdup(argv[0]);
602 VLOG_FATAL("exactly zero or one non-option argument required; "
603 "use --help for usage");
610 printf("%s: OVN controller\n"
611 "usage %s [OPTIONS] [OVS-DATABASE]\n"
612 "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
613 program_name, program_name);
614 stream_usage("OVS-DATABASE", true, false, false);
617 printf("\nOther options:\n"
618 " -h, --help display this help message\n"
619 " -V, --version display version information\n");
624 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
625 const char *argv[] OVS_UNUSED, void *exiting_)
627 bool *exiting = exiting_;
630 unixctl_command_reply(conn, NULL);