]> git.proxmox.com Git - mirror_ovs.git/blob - ovn/controller/ovn-controller.c
ovn: Introduce ovn-controller.
[mirror_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 }