diff options
Diffstat (limited to 'ovn/controller/ovn-controller.c')
-rw-r--r-- | ovn/controller/ovn-controller.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c new file mode 100644 index 000000000..35ab6ed81 --- /dev/null +++ b/ovn/controller/ovn-controller.c @@ -0,0 +1,294 @@ +/* Copyright (c) 2015 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> + +#include <errno.h> +#include <getopt.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> + +#include "command-line.h" +#include "compiler.h" +#include "daemon.h" +#include "dirs.h" +#include "openvswitch/vconn.h" +#include "openvswitch/vlog.h" +#include "ovn/ovn-sb-idl.h" +#include "poll-loop.h" +#include "fatal-signal.h" +#include "lib/vswitch-idl.h" +#include "smap.h" +#include "stream.h" +#include "stream-ssl.h" +#include "unixctl.h" +#include "util.h" + +#include "ovn-controller.h" +#include "bindings.h" +#include "chassis.h" + +VLOG_DEFINE_THIS_MODULE(main); + +static unixctl_cb_func ovn_controller_exit; + +static void parse_options(int argc, char *argv[]); +OVS_NO_RETURN static void usage(void); + +static char *ovs_remote; +static char *ovnsb_remote; + + +static void +get_initial_snapshot(struct ovsdb_idl *idl) +{ + while (1) { + ovsdb_idl_run(idl); + if (ovsdb_idl_has_ever_connected(idl)) { + return; + } + ovsdb_idl_wait(idl); + poll_block(); + } +} + +/* Retrieve the OVN remote location from the "external-ids:ovn-remote" + * key and the chassis name from the "external-ids:system-id" key in the + * Open_vSwitch table of the OVS database instance. */ +static void +get_core_config(struct controller_ctx *ctx) +{ + const struct ovsrec_open_vswitch *cfg; + + cfg = ovsrec_open_vswitch_first(ctx->ovs_idl); + if (!cfg) { + VLOG_ERR("No Open_vSwitch row defined."); + ovsdb_idl_destroy(ctx->ovs_idl); + exit(EXIT_FAILURE); + } + + while (1) { + const char *remote, *system_id; + + ovsdb_idl_run(ctx->ovs_idl); + + /* xxx This does not support changing OVN Southbound OVSDB mid-run. */ + remote = smap_get(&cfg->external_ids, "ovn-remote"); + if (!remote) { + VLOG_INFO("OVN OVSDB remote not specified. Waiting..."); + goto try_again; + } + + system_id = smap_get(&cfg->external_ids, "system-id"); + if (!system_id) { + VLOG_INFO("system-id not specified. Waiting..."); + goto try_again; + } + + ovnsb_remote = xstrdup(remote); + ctx->chassis_name = xstrdup(system_id); + return; + +try_again: + ovsdb_idl_wait(ctx->ovs_idl); + poll_block(); + } + +} + +int +main(int argc, char *argv[]) +{ + struct unixctl_server *unixctl; + struct controller_ctx ctx = { .chassis_name = NULL }; + bool exiting; + int retval; + + ovs_cmdl_proctitle_init(argc, argv); + set_program_name(argv[0]); + parse_options(argc, argv); + fatal_ignore_sigpipe(); + + daemonize_start(); + + retval = unixctl_server_create(NULL, &unixctl); + if (retval) { + exit(EXIT_FAILURE); + } + unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting); + + daemonize_complete(); + + ovsrec_init(); + sbrec_init(); + + /* Connect to OVS OVSDB instance. We do not monitor all tables by + * default, so modules must register their interest explicitly. */ + ctx.ovs_idl = ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true); + + /* Register interest in "external_ids" column in "Open_vSwitch" table, + * since we'll need to get the OVN OVSDB remote. */ + ovsdb_idl_add_table(ctx.ovs_idl, &ovsrec_table_open_vswitch); + ovsdb_idl_add_column(ctx.ovs_idl, &ovsrec_open_vswitch_col_external_ids); + + chassis_init(&ctx); + bindings_init(&ctx); + + get_initial_snapshot(ctx.ovs_idl); + + get_core_config(&ctx); + + ctx.ovnsb_idl = ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, + true, true); + + get_initial_snapshot(ctx.ovnsb_idl); + + exiting = false; + while (!exiting) { + ovsdb_idl_run(ctx.ovs_idl); + ovsdb_idl_run(ctx.ovnsb_idl); + + if (!ovsdb_idl_is_alive(ctx.ovnsb_idl)) { + int retval = ovsdb_idl_get_last_error(ctx.ovnsb_idl); + VLOG_ERR("%s: database connection failed (%s)", + ovnsb_remote, ovs_retval_to_string(retval)); + retval = EXIT_FAILURE; + break; + } + + if (!ovsdb_idl_is_alive(ctx.ovs_idl)) { + int retval = ovsdb_idl_get_last_error(ctx.ovs_idl); + VLOG_ERR("%s: database connection failed (%s)", + ovs_remote, ovs_retval_to_string(retval)); + retval = EXIT_FAILURE; + break; + } + + chassis_run(&ctx); + bindings_run(&ctx); + unixctl_server_run(unixctl); + + unixctl_server_wait(unixctl); + if (exiting) { + poll_immediate_wake(); + } + + ovsdb_idl_wait(ctx.ovs_idl); + ovsdb_idl_wait(ctx.ovnsb_idl); + poll_block(); + } + + unixctl_server_destroy(unixctl); + bindings_destroy(&ctx); + chassis_destroy(&ctx); + + ovsdb_idl_destroy(ctx.ovs_idl); + ovsdb_idl_destroy(ctx.ovnsb_idl); + + exit(retval); +} + +static void +parse_options(int argc, char *argv[]) +{ + enum { + OPT_PEER_CA_CERT = UCHAR_MAX + 1, + VLOG_OPTION_ENUMS, + DAEMON_OPTION_ENUMS + }; + + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + VLOG_LONG_OPTIONS, + DAEMON_LONG_OPTIONS, + STREAM_SSL_LONG_OPTIONS, + {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, + {NULL, 0, NULL, 0} + }; + char *short_options = ovs_cmdl_long_options_to_short_options(long_options); + + for (;;) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case 'h': + usage(); + + case 'V': + ovs_print_version(OFP13_VERSION, OFP13_VERSION); + exit(EXIT_SUCCESS); + + VLOG_OPTION_HANDLERS + DAEMON_OPTION_HANDLERS + STREAM_SSL_OPTION_HANDLERS + + case OPT_PEER_CA_CERT: + stream_ssl_set_peer_ca_cert_file(optarg); + break; + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + free(short_options); + + argc -= optind; + argv += optind; + + if (argc == 0) { + ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir()); + } else if (argc == 1) { + ovs_remote = argv[0]; + } else { + VLOG_FATAL("exactly zero or one non-option argument required; " + "use --help for usage"); + } +} + +static void +usage(void) +{ + printf("%s: OVN controller\n" + "usage %s [OPTIONS] [OVS-DATABASE]\n" + "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n", + program_name, program_name); + stream_usage("OVS-DATABASE", true, false, false); + daemon_usage(); + vlog_usage(); + printf("\nOther options:\n" + " -h, --help display this help message\n" + " -V, --version display version information\n"); + exit(EXIT_SUCCESS); +} + +static void +ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *exiting_) +{ + bool *exiting = exiting_; + *exiting = true; + + unixctl_command_reply(conn, NULL); +} |