ovn-controller: Pass 'chassis_id' explicitly to functions that need it.
[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 "ovn-controller.h"
19
20 #include <errno.h>
21 #include <getopt.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "command-line.h"
27 #include "compiler.h"
28 #include "daemon.h"
29 #include "dirs.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"
36 #include "smap.h"
37 #include "stream.h"
38 #include "stream-ssl.h"
39 #include "unixctl.h"
40 #include "util.h"
41
42 #include "ofctrl.h"
43 #include "binding.h"
44 #include "chassis.h"
45 #include "encaps.h"
46 #include "physical.h"
47 #include "pipeline.h"
48
49 VLOG_DEFINE_THIS_MODULE(main);
50
51 static unixctl_cb_func ovn_controller_exit;
52
53 #define DEFAULT_BRIDGE_NAME "br-int"
54
55 static void parse_options(int argc, char *argv[]);
56 OVS_NO_RETURN static void usage(void);
57
58 static char *ovs_remote;
59 static char *ovnsb_remote;
60
61
62 static void
63 get_initial_snapshot(struct ovsdb_idl *idl)
64 {
65     while (1) {
66         ovsdb_idl_run(idl);
67         if (ovsdb_idl_has_ever_connected(idl)) {
68             return;
69         }
70         ovsdb_idl_wait(idl);
71         poll_block();
72     }
73 }
74
75 static const struct ovsrec_bridge *
76 get_bridge(struct controller_ctx *ctx, const char *name)
77 {
78     const struct ovsrec_bridge *br;
79
80     OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
81         if (!strcmp(br->name, name)) {
82             return br;
83         }
84     }
85
86     return NULL;
87 }
88
89 /* Retrieve the OVN integration bridge from the "external-ids:ovn-bridge"
90  * key, the remote location from the "external-ids:ovn-remote" key, and
91  * the chassis name from the "external-ids:system-id" key in the
92  * Open_vSwitch table of the OVS database instance.
93  *
94  * xxx ovn-controller does not support changing any of these mid-run,
95  * xxx but that should be addressed later. */
96 static void
97 get_core_config(struct controller_ctx *ctx, char **br_int_namep,
98                 char **chassis_idp)
99 {
100     while (1) {
101         ovsdb_idl_run(ctx->ovs_idl);
102
103         const struct ovsrec_open_vswitch *cfg;
104         cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
105         if (!cfg) {
106             VLOG_ERR("No Open_vSwitch row defined.");
107             ovsdb_idl_destroy(ctx->ovs_idl);
108             exit(EXIT_FAILURE);
109         }
110
111         const struct ovsrec_bridge *br_int;
112         const char *remote, *system_id, *br_int_name;
113
114         br_int_name = smap_get(&cfg->external_ids, "ovn-bridge");
115         if (!br_int_name) {
116             br_int_name = DEFAULT_BRIDGE_NAME;
117         }
118
119         br_int = get_bridge(ctx, br_int_name);
120         if (!br_int) {
121             VLOG_INFO("Integration bridge '%s' does not exist.  Waiting...",
122                       br_int_name);
123             goto try_again;
124         }
125
126         remote = smap_get(&cfg->external_ids, "ovn-remote");
127         if (!remote) {
128             VLOG_INFO("OVN OVSDB remote not specified.  Waiting...");
129             goto try_again;
130         }
131
132         system_id = smap_get(&cfg->external_ids, "system-id");
133         if (!system_id) {
134             VLOG_INFO("system-id not specified.  Waiting...");
135             goto try_again;
136         }
137
138         ovnsb_remote = xstrdup(remote);
139         *chassis_idp = xstrdup(system_id);
140         *br_int_namep = xstrdup(br_int_name);
141         return;
142
143 try_again:
144         ovsdb_idl_wait(ctx->ovs_idl);
145         poll_block();
146     }
147
148 }
149
150 struct idl_loop {
151     struct ovsdb_idl *idl;
152     unsigned int skip_seqno;
153
154     struct ovsdb_idl_txn *committing_txn;
155     unsigned int precommit_seqno;
156
157     struct ovsdb_idl_txn *open_txn;
158 };
159
160 #define IDL_LOOP_INITIALIZER(IDL) { .idl = (IDL) }
161
162 static void
163 idl_loop_destroy(struct idl_loop *loop)
164 {
165     if (loop) {
166         ovsdb_idl_destroy(loop->idl);
167     }
168 }
169
170 static struct ovsdb_idl_txn *
171 idl_loop_run(struct idl_loop *loop)
172 {
173     ovsdb_idl_run(loop->idl);
174     loop->open_txn = (loop->committing_txn
175                       || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
176                       ? NULL
177                       : ovsdb_idl_txn_create(loop->idl));
178     return loop->open_txn;
179 }
180
181 static void
182 idl_loop_commit_and_wait(struct idl_loop *loop)
183 {
184     if (loop->open_txn) {
185         loop->committing_txn = loop->open_txn;
186         loop->open_txn = NULL;
187
188         loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
189     }
190
191     struct ovsdb_idl_txn *txn = loop->committing_txn;
192     if (txn) {
193         enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
194         if (status != TXN_INCOMPLETE) {
195             switch (status) {
196             case TXN_TRY_AGAIN:
197                 /* We want to re-evaluate the database when it's changed from
198                  * the contents that it had when we started the commit.  (That
199                  * might have already happened.) */
200                 loop->skip_seqno = loop->precommit_seqno;
201                 if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
202                     poll_immediate_wake();
203                 }
204                 break;
205
206             case TXN_SUCCESS:
207                 /* If the database has already changed since we started the
208                  * commit, re-evaluate it immediately to avoid missing a change
209                  * for a while. */
210                 if (ovsdb_idl_get_seqno(loop->idl) != loop->precommit_seqno) {
211                     poll_immediate_wake();
212                 }
213                 break;
214
215             case TXN_UNCHANGED:
216             case TXN_ABORTED:
217             case TXN_NOT_LOCKED:
218             case TXN_ERROR:
219                 break;
220
221             case TXN_UNCOMMITTED:
222             case TXN_INCOMPLETE:
223                 OVS_NOT_REACHED();
224
225             }
226             ovsdb_idl_txn_destroy(txn);
227             loop->committing_txn = NULL;
228         }
229     }
230
231     ovsdb_idl_wait(loop->idl);
232 }
233
234 int
235 main(int argc, char *argv[])
236 {
237     struct unixctl_server *unixctl;
238     struct controller_ctx ctx = { .ovs_idl = NULL };
239     bool exiting;
240     int retval;
241
242     ovs_cmdl_proctitle_init(argc, argv);
243     set_program_name(argv[0]);
244     parse_options(argc, argv);
245     fatal_ignore_sigpipe();
246
247     daemonize_start();
248
249     retval = unixctl_server_create(NULL, &unixctl);
250     if (retval) {
251         exit(EXIT_FAILURE);
252     }
253     unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
254
255     daemonize_complete();
256
257     ovsrec_init();
258     sbrec_init();
259
260     ofctrl_init();
261
262     /* Connect to OVS OVSDB instance.  We do not monitor all tables by
263      * default, so modules must register their interest explicitly.  */
264     ctx.ovs_idl = ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true);
265
266     /* Register interest in "external_ids" column in "Open_vSwitch" table,
267      * since we'll need to get the OVN OVSDB remote. */
268     ovsdb_idl_add_table(ctx.ovs_idl, &ovsrec_table_open_vswitch);
269     ovsdb_idl_add_column(ctx.ovs_idl, &ovsrec_open_vswitch_col_external_ids);
270
271     chassis_init(&ctx);
272     encaps_init(&ctx);
273     binding_init(&ctx);
274     physical_init(&ctx);
275     pipeline_init();
276
277     get_initial_snapshot(ctx.ovs_idl);
278
279     char *br_int_name, *chassis_id;
280     get_core_config(&ctx, &br_int_name, &chassis_id);
281
282     ctx.ovnsb_idl = ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class,
283                                      true, true);
284     get_initial_snapshot(ctx.ovnsb_idl);
285
286     struct idl_loop ovnsb_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovnsb_idl);
287     struct idl_loop ovs_idl_loop = IDL_LOOP_INITIALIZER(ctx.ovs_idl);
288
289     /* Main loop. */
290     exiting = false;
291     while (!exiting) {
292         ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop);
293         ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop);
294
295         /* xxx If run into any surprising changes, we exit.  We should
296          * xxx handle this more gracefully. */
297         const struct ovsrec_bridge *br_int = get_bridge(&ctx, br_int_name);
298         if (!br_int) {
299             VLOG_ERR("Integration bridge '%s' disappeared", br_int_name);
300             retval = EXIT_FAILURE;
301             goto exit;
302         }
303
304         chassis_run(&ctx, chassis_id);
305         encaps_run(&ctx, br_int, chassis_id);
306         binding_run(&ctx, br_int, chassis_id);
307
308         struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
309         pipeline_run(&ctx, &flow_table);
310         physical_run(&ctx, br_int, chassis_id, &flow_table);
311         ofctrl_run(br_int, &flow_table);
312         hmap_destroy(&flow_table);
313
314         unixctl_server_run(unixctl);
315
316         unixctl_server_wait(unixctl);
317         if (exiting) {
318             poll_immediate_wake();
319         }
320
321         idl_loop_commit_and_wait(&ovnsb_idl_loop);
322         idl_loop_commit_and_wait(&ovs_idl_loop);
323
324         ofctrl_wait();
325         poll_block();
326     }
327
328     /* It's time to exit.  Clean up the databases. */
329     bool done = false;
330     while (!done) {
331         ctx.ovnsb_idl_txn = idl_loop_run(&ovnsb_idl_loop);
332         ctx.ovs_idl_txn = idl_loop_run(&ovs_idl_loop);
333
334         /* xxx If run into any surprising changes, we exit.  We should
335          * xxx handle this more gracefully. */
336         const struct ovsrec_bridge *br_int = get_bridge(&ctx, br_int_name);
337         if (!br_int) {
338             VLOG_ERR("Integration bridge '%s' disappeared", br_int_name);
339             retval = EXIT_FAILURE;
340             goto exit;
341         }
342
343         /* Run all of the cleanup functions, even if one of them returns false.
344          * We're done if all of them return true. */
345         done = binding_cleanup(&ctx, chassis_id);
346         done = chassis_cleanup(&ctx, chassis_id) && done;
347         done = encaps_cleanup(&ctx, br_int) && done;
348         if (done) {
349             poll_immediate_wake();
350         }
351
352         idl_loop_commit_and_wait(&ovnsb_idl_loop);
353         idl_loop_commit_and_wait(&ovs_idl_loop);
354         poll_block();
355     }
356
357 exit:
358     unixctl_server_destroy(unixctl);
359     pipeline_destroy(&ctx);
360     ofctrl_destroy();
361
362     idl_loop_destroy(&ovs_idl_loop);
363     idl_loop_destroy(&ovnsb_idl_loop);
364
365     free(br_int_name);
366     free(chassis_id);
367     free(ovnsb_remote);
368     free(ovs_remote);
369
370     exit(retval);
371 }
372
373 static void
374 parse_options(int argc, char *argv[])
375 {
376     enum {
377         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
378         VLOG_OPTION_ENUMS,
379         DAEMON_OPTION_ENUMS
380     };
381
382     static struct option long_options[] = {
383         {"help", no_argument, NULL, 'h'},
384         {"version", no_argument, NULL, 'V'},
385         VLOG_LONG_OPTIONS,
386         DAEMON_LONG_OPTIONS,
387         STREAM_SSL_LONG_OPTIONS,
388         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
389         {NULL, 0, NULL, 0}
390     };
391     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
392
393     for (;;) {
394         int c;
395
396         c = getopt_long(argc, argv, short_options, long_options, NULL);
397         if (c == -1) {
398             break;
399         }
400
401         switch (c) {
402         case 'h':
403             usage();
404
405         case 'V':
406             ovs_print_version(OFP13_VERSION, OFP13_VERSION);
407             exit(EXIT_SUCCESS);
408
409         VLOG_OPTION_HANDLERS
410         DAEMON_OPTION_HANDLERS
411         STREAM_SSL_OPTION_HANDLERS
412
413         case OPT_PEER_CA_CERT:
414             stream_ssl_set_peer_ca_cert_file(optarg);
415             break;
416
417         case '?':
418             exit(EXIT_FAILURE);
419
420         default:
421             abort();
422         }
423     }
424     free(short_options);
425
426     argc -= optind;
427     argv += optind;
428
429     if (argc == 0) {
430         ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
431     } else if (argc == 1) {
432         ovs_remote = xstrdup(argv[0]);
433     } else {
434         VLOG_FATAL("exactly zero or one non-option argument required; "
435                    "use --help for usage");
436     }
437 }
438
439 static void
440 usage(void)
441 {
442     printf("%s: OVN controller\n"
443            "usage %s [OPTIONS] [OVS-DATABASE]\n"
444            "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
445                program_name, program_name);
446     stream_usage("OVS-DATABASE", true, false, false);
447     daemon_usage();
448     vlog_usage();
449     printf("\nOther options:\n"
450            "  -h, --help              display this help message\n"
451            "  -V, --version           display version information\n");
452     exit(EXIT_SUCCESS);
453 }
454
455 static void
456 ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
457              const char *argv[] OVS_UNUSED, void *exiting_)
458 {
459     bool *exiting = exiting_;
460     *exiting = true;
461
462     unixctl_command_reply(conn, NULL);
463 }