ovn: Rename ovn-nbd to ovn-northd.
[cascardo/ovs.git] / ovn / northd / ovn-northd.c
1 /*
2  * Licensed under the Apache License, Version 2.0 (the "License");
3  * you may not use this file except in compliance with the License.
4  * You may obtain a copy of the License at:
5  *
6  *     http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14
15 #include <config.h>
16
17 #include <getopt.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20
21 #include "command-line.h"
22 #include "daemon.h"
23 #include "dirs.h"
24 #include "fatal-signal.h"
25 #include "hash.h"
26 #include "hmap.h"
27 #include "ovn/ovn-nb-idl.h"
28 #include "ovn/ovn-sb-idl.h"
29 #include "poll-loop.h"
30 #include "stream.h"
31 #include "stream-ssl.h"
32 #include "util.h"
33 #include "uuid.h"
34 #include "openvswitch/vlog.h"
35
36 VLOG_DEFINE_THIS_MODULE(ovn_nbd);
37
38 struct nbd_context {
39     struct ovsdb_idl *ovnnb_idl;
40     struct ovsdb_idl *ovnsb_idl;
41     struct ovsdb_idl_txn *ovnnb_txn;
42     struct ovsdb_idl_txn *ovnsb_txn;
43 };
44
45 static const char *ovnnb_db;
46 static const char *ovnsb_db;
47
48 static const char *default_db(void);
49
50 static void
51 usage(void)
52 {
53     printf("\
54 %s: OVN northbound management daemon\n\
55 usage: %s [OPTIONS]\n\
56 \n\
57 Options:\n\
58   --ovnnb-db=DATABASE       connect to ovn-nb database at DATABASE\n\
59                             (default: %s)\n\
60   --ovnsb-db=DATABASE       connect to ovn-sb database at DATABASE\n\
61                             (default: %s)\n\
62   -h, --help                display this help message\n\
63   -o, --options             list available options\n\
64   -V, --version             display version information\n\
65 ", program_name, program_name, default_db(), default_db());
66     daemon_usage();
67     vlog_usage();
68     stream_usage("database", true, true, false);
69 }
70 \f
71 static int
72 compare_strings(const void *a_, const void *b_)
73 {
74     char *const *a = a_;
75     char *const *b = b_;
76     return strcmp(*a, *b);
77 }
78
79 /*
80  * Determine whether 2 arrays of MAC addresses are the same.  It's possible that
81  * the lists could be *very* long and this check is being done a lot (every
82  * time the OVN_Northbound database changes).
83  */
84 static bool
85 macs_equal(char **binding_macs_, size_t b_n_macs,
86            char **lport_macs_, size_t l_n_macs)
87 {
88     char **binding_macs, **lport_macs;
89     size_t bytes, i;
90
91     if (b_n_macs != l_n_macs) {
92         return false;
93     }
94
95     bytes = b_n_macs * sizeof binding_macs_[0];
96     binding_macs = xmalloc(bytes);
97     lport_macs = xmalloc(bytes);
98
99     memcpy(binding_macs, binding_macs_, bytes);
100     memcpy(lport_macs, lport_macs_, bytes);
101
102     qsort(binding_macs, b_n_macs, sizeof binding_macs[0], compare_strings);
103     qsort(lport_macs, l_n_macs, sizeof lport_macs[0], compare_strings);
104
105     for (i = 0; i < b_n_macs; i++) {
106         if (strcmp(binding_macs[i], lport_macs[i])) {
107             break;
108         }
109     }
110
111     free(binding_macs);
112     free(lport_macs);
113
114     return (i == b_n_macs) ? true : false;
115 }
116
117 /*
118  * When a change has occurred in the OVN_Northbound database, we go through and
119  * make sure that the contents of the Bindings table in the OVN_Southbound
120  * database are up to date with the logical ports defined in the
121  * OVN_Northbound database.
122  */
123 static void
124 set_bindings(struct nbd_context *ctx)
125 {
126     struct hmap bindings_hmap;
127     const struct sbrec_bindings *binding;
128     const struct nbrec_logical_port *lport;
129
130     struct binding_hash_node {
131         struct hmap_node node;
132         const struct sbrec_bindings *binding;
133     } *hash_node, *hash_node_next;
134
135     /*
136      * We will need to look up a binding for every logical port.  We don't want
137      * to have to do an O(n) search for every binding, so start out by hashing
138      * them on the logical port.
139      *
140      * As we go through every logical port, we will update the binding if it
141      * exists or create one otherwise.  When the update is done, we'll remove it
142      * from the hashmap.  At the end, any bindings left in the hashmap are for
143      * logical ports that have been deleted.
144      */
145     hmap_init(&bindings_hmap);
146
147     SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
148         hash_node = xzalloc(sizeof *hash_node);
149         hash_node->binding = binding;
150         hmap_insert(&bindings_hmap, &hash_node->node,
151                 hash_string(binding->logical_port, 0));
152     }
153
154     NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
155         binding = NULL;
156         HMAP_FOR_EACH_WITH_HASH(hash_node, node,
157                 hash_string(lport->name, 0), &bindings_hmap) {
158             if (!strcmp(lport->name, hash_node->binding->logical_port)) {
159                 binding = hash_node->binding;
160                 break;
161             }
162         }
163
164         if (binding) {
165             /* We found an existing binding for this logical port.  Update its
166              * contents.  Right now the only thing we expect that could change
167              * is the list of MAC addresses. */
168
169             hmap_remove(&bindings_hmap, &hash_node->node);
170             free(hash_node);
171             hash_node = NULL;
172
173             if (!macs_equal(binding->mac, binding->n_mac,
174                         lport->macs, lport->n_macs)) {
175                 sbrec_bindings_set_mac(binding,
176                         (const char **) lport->macs, lport->n_macs);
177             }
178         } else {
179             /* There is no binding for this logical port, so create one. */
180
181             binding = sbrec_bindings_insert(ctx->ovnsb_txn);
182             sbrec_bindings_set_logical_port(binding, lport->name);
183             sbrec_bindings_set_mac(binding,
184                     (const char **) lport->macs, lport->n_macs);
185         }
186     }
187
188     HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
189         hmap_remove(&bindings_hmap, &hash_node->node);
190         sbrec_bindings_delete(hash_node->binding);
191         free(hash_node);
192     }
193     hmap_destroy(&bindings_hmap);
194 }
195
196 static void
197 ovnnb_db_changed(struct nbd_context *ctx)
198 {
199     VLOG_DBG("ovn-northd: ovn-nb db contents have changed.\n");
200
201     set_bindings(ctx);
202 }
203
204 /*
205  * The only change we get notified about is if the 'chassis' column of the
206  * 'Bindings' table changes.  When this column is not empty, it means we need to
207  * set the corresponding logical port as 'up' in the northbound DB.
208  */
209 static void
210 ovnsb_db_changed(struct nbd_context *ctx)
211 {
212     struct hmap lports_hmap;
213     const struct sbrec_bindings *binding;
214     const struct nbrec_logical_port *lport;
215
216     struct lport_hash_node {
217         struct hmap_node node;
218         const struct nbrec_logical_port *lport;
219     } *hash_node, *hash_node_next;
220
221     VLOG_DBG("Recalculating port up states for ovn-nb db.");
222
223     hmap_init(&lports_hmap);
224
225     NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
226         hash_node = xzalloc(sizeof *hash_node);
227         hash_node->lport = lport;
228         hmap_insert(&lports_hmap, &hash_node->node,
229                 hash_string(lport->name, 0));
230     }
231
232     SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
233         lport = NULL;
234         HMAP_FOR_EACH_WITH_HASH(hash_node, node,
235                 hash_string(binding->logical_port, 0), &lports_hmap) {
236             if (!strcmp(binding->logical_port, hash_node->lport->name)) {
237                 lport = hash_node->lport;
238                 break;
239             }
240         }
241
242         if (!lport) {
243             /* The logical port doesn't exist for this binding.  This can happen
244              * under normal circumstances when ovn-nbd hasn't gotten around to
245              * pruning the Binding yet. */
246             continue;
247         }
248
249         if (*binding->chassis && (!lport->up || !*lport->up)) {
250             bool up = true;
251             nbrec_logical_port_set_up(lport, &up, 1);
252         } else if (!*binding->chassis && (!lport->up || *lport->up)) {
253             bool up = false;
254             nbrec_logical_port_set_up(lport, &up, 1);
255         }
256     }
257
258     HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
259         hmap_remove(&lports_hmap, &hash_node->node);
260         free(hash_node);
261     }
262     hmap_destroy(&lports_hmap);
263 }
264 \f
265 static const char *
266 default_db(void)
267 {
268     static char *def;
269     if (!def) {
270         def = xasprintf("unix:%s/db.sock", ovs_rundir());
271     }
272     return def;
273 }
274
275 static void
276 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
277 {
278     enum {
279         DAEMON_OPTION_ENUMS,
280         VLOG_OPTION_ENUMS,
281     };
282     static const struct option long_options[] = {
283         {"ovnsb-db", required_argument, NULL, 'd'},
284         {"ovnnb-db", required_argument, NULL, 'D'},
285         {"help", no_argument, NULL, 'h'},
286         {"options", no_argument, NULL, 'o'},
287         {"version", no_argument, NULL, 'V'},
288         DAEMON_LONG_OPTIONS,
289         VLOG_LONG_OPTIONS,
290         STREAM_SSL_LONG_OPTIONS,
291         {NULL, 0, NULL, 0},
292     };
293     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
294
295     for (;;) {
296         int c;
297
298         c = getopt_long(argc, argv, short_options, long_options, NULL);
299         if (c == -1) {
300             break;
301         }
302
303         switch (c) {
304         DAEMON_OPTION_HANDLERS;
305         VLOG_OPTION_HANDLERS;
306         STREAM_SSL_OPTION_HANDLERS;
307
308         case 'd':
309             ovnsb_db = optarg;
310             break;
311
312         case 'D':
313             ovnnb_db = optarg;
314             break;
315
316         case 'h':
317             usage();
318             exit(EXIT_SUCCESS);
319
320         case 'o':
321             ovs_cmdl_print_options(long_options);
322             exit(EXIT_SUCCESS);
323
324         case 'V':
325             ovs_print_version(0, 0);
326             exit(EXIT_SUCCESS);
327
328         default:
329             break;
330         }
331     }
332
333     if (!ovnsb_db) {
334         ovnsb_db = default_db();
335     }
336
337     if (!ovnnb_db) {
338         ovnnb_db = default_db();
339     }
340
341     free(short_options);
342 }
343
344 int
345 main(int argc, char *argv[])
346 {
347     extern struct vlog_module VLM_reconnect;
348     struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
349     unsigned int ovnnb_seqno, ovn_seqno;
350     int res = EXIT_SUCCESS;
351     struct nbd_context ctx = {
352         .ovnsb_txn = NULL,
353     };
354     bool ovnnb_changes_pending = false;
355     bool ovn_changes_pending = false;
356
357     fatal_ignore_sigpipe();
358     set_program_name(argv[0]);
359     vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
360     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
361     parse_options(argc, argv);
362
363     daemonize();
364
365     nbrec_init();
366     sbrec_init();
367
368     /* We want to detect all changes to the ovn-nb db. */
369     ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
370             &nbrec_idl_class, true, true);
371
372     /* There is only a small subset of changes to the ovn-sb db that ovn-northd
373      * has to care about, so we'll enable monitoring those directly. */
374     ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
375             &sbrec_idl_class, false, true);
376     ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_bindings);
377     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_port);
378     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_chassis);
379     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_mac);
380
381     /*
382      * The loop here just runs the IDL in a loop waiting for the seqno to
383      * change, which indicates that the contents of the db have changed.
384      *
385      * If the contents of the ovn-nb db change, the mappings to the ovn-sb
386      * db must be recalculated.
387      *
388      * If the contents of the ovn-sb db change, it means the 'up' state of
389      * a port may have changed, as that's the only type of change ovn-northd is
390      * watching for.
391      */
392
393     ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
394     ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
395     for (;;) {
396         ovsdb_idl_run(ovnnb_idl);
397         ovsdb_idl_run(ovnsb_idl);
398
399         if (!ovsdb_idl_is_alive(ovnnb_idl)) {
400             int retval = ovsdb_idl_get_last_error(ovnnb_idl);
401             VLOG_ERR("%s: database connection failed (%s)",
402                     ovnnb_db, ovs_retval_to_string(retval));
403             res = EXIT_FAILURE;
404             break;
405         }
406
407         if (!ovsdb_idl_is_alive(ovnsb_idl)) {
408             int retval = ovsdb_idl_get_last_error(ovnsb_idl);
409             VLOG_ERR("%s: database connection failed (%s)",
410                     ovnsb_db, ovs_retval_to_string(retval));
411             res = EXIT_FAILURE;
412             break;
413         }
414
415         if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
416             ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
417             ovnnb_changes_pending = true;
418         }
419
420         if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
421             ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
422             ovn_changes_pending = true;
423         }
424
425         /*
426          * If there are any pending changes, we delay recalculating the
427          * necessary updates until after an existing transaction finishes.
428          * This avoids the possibility of rapid updates causing ovn-northd to
429          * never be able to successfully make the corresponding updates to the
430          * other db.  Instead, pending changes are batched up until the next
431          * time we get a chance to calculate the new state and apply it.
432          */
433
434         if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
435             /*
436              * The OVN-nb db contents have changed, so create a transaction for
437              * updating the OVN-sb DB.
438              */
439             ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
440             ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
441                                       "ovn-northd: northbound db changed");
442             ovnnb_db_changed(&ctx);
443             ovnnb_changes_pending = false;
444         }
445
446         if (ovn_changes_pending && !ctx.ovnnb_txn) {
447             /*
448              * The OVN-sb db contents have changed, so create a transaction for
449              * updating the northbound DB.
450              */
451             ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
452             ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
453                                       "ovn-northd: southbound db changed");
454             ovnsb_db_changed(&ctx);
455             ovn_changes_pending = false;
456         }
457
458         if (ctx.ovnnb_txn) {
459             enum ovsdb_idl_txn_status txn_status;
460             txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
461             switch (txn_status) {
462             case TXN_UNCOMMITTED:
463             case TXN_INCOMPLETE:
464                 /* Come back around and try to commit this transaction again */
465                 break;
466             case TXN_ABORTED:
467             case TXN_TRY_AGAIN:
468             case TXN_NOT_LOCKED:
469             case TXN_ERROR:
470                 /* Something went wrong, so try creating a new transaction. */
471                 ovn_changes_pending = true;
472             case TXN_UNCHANGED:
473             case TXN_SUCCESS:
474                 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
475                 ctx.ovnnb_txn = NULL;
476             }
477         }
478
479         if (ctx.ovnsb_txn) {
480             enum ovsdb_idl_txn_status txn_status;
481             txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
482             switch (txn_status) {
483             case TXN_UNCOMMITTED:
484             case TXN_INCOMPLETE:
485                 /* Come back around and try to commit this transaction again */
486                 break;
487             case TXN_ABORTED:
488             case TXN_TRY_AGAIN:
489             case TXN_NOT_LOCKED:
490             case TXN_ERROR:
491                 /* Something went wrong, so try creating a new transaction. */
492                 ovnnb_changes_pending = true;
493             case TXN_UNCHANGED:
494             case TXN_SUCCESS:
495                 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
496                 ctx.ovnsb_txn = NULL;
497             }
498         }
499
500         if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
501                 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
502             ovsdb_idl_wait(ovnnb_idl);
503             ovsdb_idl_wait(ovnsb_idl);
504             if (ctx.ovnnb_txn) {
505                 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
506             }
507             if (ctx.ovnsb_txn) {
508                 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
509             }
510             poll_block();
511         }
512     }
513
514     ovsdb_idl_destroy(ovnsb_idl);
515     ovsdb_idl_destroy(ovnnb_idl);
516
517     exit(res);
518 }