ovn: Introduce ovn-controller.
[cascardo/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 <errno.h>
19 #include <getopt.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "command-line.h"
25 #include "compiler.h"
26 #include "daemon.h"
27 #include "dirs.h"
28 #include "openvswitch/vconn.h"
29 #include "openvswitch/vlog.h"
30 #include "ovn/ovn-sb-idl.h"
31 #include "poll-loop.h"
32 #include "fatal-signal.h"
33 #include "lib/vswitch-idl.h"
34 #include "smap.h"
35 #include "stream.h"
36 #include "stream-ssl.h"
37 #include "unixctl.h"
38 #include "util.h"
39
40 #include "ovn-controller.h"
41 #include "bindings.h"
42 #include "chassis.h"
43
44 VLOG_DEFINE_THIS_MODULE(main);
45
46 static unixctl_cb_func ovn_controller_exit;
47
48 static void parse_options(int argc, char *argv[]);
49 OVS_NO_RETURN static void usage(void);
50
51 static char *ovs_remote;
52 static char *ovnsb_remote;
53
54
55 static void
56 get_initial_snapshot(struct ovsdb_idl *idl)
57 {
58     while (1) {
59         ovsdb_idl_run(idl);
60         if (ovsdb_idl_has_ever_connected(idl)) {
61             return;
62         }
63         ovsdb_idl_wait(idl);
64         poll_block();
65     }
66 }
67
68 /* Retrieve the OVN remote location from the "external-ids:ovn-remote"
69  * key and the chassis name from the "external-ids:system-id" key in the
70  * Open_vSwitch table of the OVS database instance. */
71 static void
72 get_core_config(struct controller_ctx *ctx)
73 {
74     const struct ovsrec_open_vswitch *cfg;
75
76     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
77     if (!cfg) {
78         VLOG_ERR("No Open_vSwitch row defined.");
79         ovsdb_idl_destroy(ctx->ovs_idl);
80         exit(EXIT_FAILURE);
81     }
82
83     while (1) {
84         const char *remote, *system_id;
85
86         ovsdb_idl_run(ctx->ovs_idl);
87
88         /* xxx This does not support changing OVN Southbound OVSDB mid-run. */
89         remote = smap_get(&cfg->external_ids, "ovn-remote");
90         if (!remote) {
91             VLOG_INFO("OVN OVSDB remote not specified.  Waiting...");
92             goto try_again;
93         }
94
95         system_id = smap_get(&cfg->external_ids, "system-id");
96         if (!system_id) {
97             VLOG_INFO("system-id not specified.  Waiting...");
98             goto try_again;
99         }
100
101         ovnsb_remote = xstrdup(remote);
102         ctx->chassis_name = xstrdup(system_id);
103         return;
104
105 try_again:
106         ovsdb_idl_wait(ctx->ovs_idl);
107         poll_block();
108     }
109
110 }
111
112 int
113 main(int argc, char *argv[])
114 {
115     struct unixctl_server *unixctl;
116     struct controller_ctx ctx = { .chassis_name = NULL };
117     bool exiting;
118     int retval;
119
120     ovs_cmdl_proctitle_init(argc, argv);
121     set_program_name(argv[0]);
122     parse_options(argc, argv);
123     fatal_ignore_sigpipe();
124
125     daemonize_start();
126
127     retval = unixctl_server_create(NULL, &unixctl);
128     if (retval) {
129         exit(EXIT_FAILURE);
130     }
131     unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
132
133     daemonize_complete();
134
135     ovsrec_init();
136     sbrec_init();
137
138     /* Connect to OVS OVSDB instance.  We do not monitor all tables by
139      * default, so modules must register their interest explicitly.  */
140     ctx.ovs_idl = ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true);
141
142     /* Register interest in "external_ids" column in "Open_vSwitch" table,
143      * since we'll need to get the OVN OVSDB remote. */
144     ovsdb_idl_add_table(ctx.ovs_idl, &ovsrec_table_open_vswitch);
145     ovsdb_idl_add_column(ctx.ovs_idl, &ovsrec_open_vswitch_col_external_ids);
146
147     chassis_init(&ctx);
148     bindings_init(&ctx);
149
150     get_initial_snapshot(ctx.ovs_idl);
151
152     get_core_config(&ctx);
153
154     ctx.ovnsb_idl = ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class,
155                                      true, true);
156
157     get_initial_snapshot(ctx.ovnsb_idl);
158
159     exiting = false;
160     while (!exiting) {
161         ovsdb_idl_run(ctx.ovs_idl);
162         ovsdb_idl_run(ctx.ovnsb_idl);
163
164         if (!ovsdb_idl_is_alive(ctx.ovnsb_idl)) {
165             int retval = ovsdb_idl_get_last_error(ctx.ovnsb_idl);
166             VLOG_ERR("%s: database connection failed (%s)",
167                      ovnsb_remote, ovs_retval_to_string(retval));
168             retval = EXIT_FAILURE;
169             break;
170         }
171
172         if (!ovsdb_idl_is_alive(ctx.ovs_idl)) {
173             int retval = ovsdb_idl_get_last_error(ctx.ovs_idl);
174             VLOG_ERR("%s: database connection failed (%s)",
175                      ovs_remote, ovs_retval_to_string(retval));
176             retval = EXIT_FAILURE;
177             break;
178         }
179
180         chassis_run(&ctx);
181         bindings_run(&ctx);
182         unixctl_server_run(unixctl);
183
184         unixctl_server_wait(unixctl);
185         if (exiting) {
186             poll_immediate_wake();
187         }
188
189         ovsdb_idl_wait(ctx.ovs_idl);
190         ovsdb_idl_wait(ctx.ovnsb_idl);
191         poll_block();
192     }
193
194     unixctl_server_destroy(unixctl);
195     bindings_destroy(&ctx);
196     chassis_destroy(&ctx);
197
198     ovsdb_idl_destroy(ctx.ovs_idl);
199     ovsdb_idl_destroy(ctx.ovnsb_idl);
200
201     exit(retval);
202 }
203
204 static void
205 parse_options(int argc, char *argv[])
206 {
207     enum {
208         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
209         VLOG_OPTION_ENUMS,
210         DAEMON_OPTION_ENUMS
211     };
212
213     static struct option long_options[] = {
214         {"help", no_argument, NULL, 'h'},
215         {"version", no_argument, NULL, 'V'},
216         VLOG_LONG_OPTIONS,
217         DAEMON_LONG_OPTIONS,
218         STREAM_SSL_LONG_OPTIONS,
219         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
220         {NULL, 0, NULL, 0}
221     };
222     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
223
224     for (;;) {
225         int c;
226
227         c = getopt_long(argc, argv, short_options, long_options, NULL);
228         if (c == -1) {
229             break;
230         }
231
232         switch (c) {
233         case 'h':
234             usage();
235
236         case 'V':
237             ovs_print_version(OFP13_VERSION, OFP13_VERSION);
238             exit(EXIT_SUCCESS);
239
240         VLOG_OPTION_HANDLERS
241         DAEMON_OPTION_HANDLERS
242         STREAM_SSL_OPTION_HANDLERS
243
244         case OPT_PEER_CA_CERT:
245             stream_ssl_set_peer_ca_cert_file(optarg);
246             break;
247
248         case '?':
249             exit(EXIT_FAILURE);
250
251         default:
252             abort();
253         }
254     }
255     free(short_options);
256
257     argc -= optind;
258     argv += optind;
259
260     if (argc == 0) {
261         ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
262     } else if (argc == 1) {
263         ovs_remote = argv[0];
264     } else {
265         VLOG_FATAL("exactly zero or one non-option argument required; "
266                    "use --help for usage");
267     }
268 }
269
270 static void
271 usage(void)
272 {
273     printf("%s: OVN controller\n"
274            "usage %s [OPTIONS] [OVS-DATABASE]\n"
275            "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
276                program_name, program_name);
277     stream_usage("OVS-DATABASE", true, false, false);
278     daemon_usage();
279     vlog_usage();
280     printf("\nOther options:\n"
281            "  -h, --help              display this help message\n"
282            "  -V, --version           display version information\n");
283     exit(EXIT_SUCCESS);
284 }
285
286 static void
287 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
288              const char *argv[] OVS_UNUSED, void *exiting_)
289 {
290     bool *exiting = exiting_;
291     *exiting = true;
292
293     unixctl_command_reply(conn, NULL);
294 }