ovn-northd: Update Bindings for container integration.
[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_northd);
37
38 struct northd_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 static bool
118 parents_equal(const struct sbrec_bindings *binding,
119               const struct nbrec_logical_port *lport)
120 {
121     if (!!binding->parent_port != !!lport->parent_name) {
122         /* One is set and the other is not. */
123         return false;
124     }
125
126     if (binding->parent_port) {
127         /* Both are set. */
128         return strcmp(binding->parent_port, lport->parent_name) ? false : true;
129     }
130
131     /* Both are NULL. */
132     return true;
133 }
134
135 static bool
136 tags_equal(const struct sbrec_bindings *binding,
137            const struct nbrec_logical_port *lport)
138 {
139     if (binding->n_tag != lport->n_tag) {
140         return false;
141     }
142
143     return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
144 }
145
146 /*
147  * When a change has occurred in the OVN_Northbound database, we go through and
148  * make sure that the contents of the Bindings table in the OVN_Southbound
149  * database are up to date with the logical ports defined in the
150  * OVN_Northbound database.
151  */
152 static void
153 set_bindings(struct northd_context *ctx)
154 {
155     struct hmap bindings_hmap;
156     const struct sbrec_bindings *binding;
157     const struct nbrec_logical_port *lport;
158
159     struct binding_hash_node {
160         struct hmap_node node;
161         const struct sbrec_bindings *binding;
162     } *hash_node, *hash_node_next;
163
164     /*
165      * We will need to look up a binding for every logical port.  We don't want
166      * to have to do an O(n) search for every binding, so start out by hashing
167      * them on the logical port.
168      *
169      * As we go through every logical port, we will update the binding if it
170      * exists or create one otherwise.  When the update is done, we'll remove it
171      * from the hashmap.  At the end, any bindings left in the hashmap are for
172      * logical ports that have been deleted.
173      */
174     hmap_init(&bindings_hmap);
175
176     SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
177         hash_node = xzalloc(sizeof *hash_node);
178         hash_node->binding = binding;
179         hmap_insert(&bindings_hmap, &hash_node->node,
180                 hash_string(binding->logical_port, 0));
181     }
182
183     NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
184         binding = NULL;
185         HMAP_FOR_EACH_WITH_HASH(hash_node, node,
186                 hash_string(lport->name, 0), &bindings_hmap) {
187             if (!strcmp(lport->name, hash_node->binding->logical_port)) {
188                 binding = hash_node->binding;
189                 break;
190             }
191         }
192
193         if (binding) {
194             /* We found an existing binding for this logical port.  Update its
195              * contents. */
196
197             hmap_remove(&bindings_hmap, &hash_node->node);
198             free(hash_node);
199             hash_node = NULL;
200
201             if (!macs_equal(binding->mac, binding->n_mac,
202                         lport->macs, lport->n_macs)) {
203                 sbrec_bindings_set_mac(binding,
204                         (const char **) lport->macs, lport->n_macs);
205             }
206             if (!parents_equal(binding, lport)) {
207                 sbrec_bindings_set_parent_port(binding, lport->parent_name);
208             }
209             if (!tags_equal(binding, lport)) {
210                 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
211             }
212         } else {
213             /* There is no binding for this logical port, so create one. */
214
215             binding = sbrec_bindings_insert(ctx->ovnsb_txn);
216             sbrec_bindings_set_logical_port(binding, lport->name);
217             sbrec_bindings_set_mac(binding,
218                     (const char **) lport->macs, lport->n_macs);
219             if (lport->parent_name && lport->n_tag > 0) {
220                 sbrec_bindings_set_parent_port(binding, lport->parent_name);
221                 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
222             }
223         }
224     }
225
226     HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
227         hmap_remove(&bindings_hmap, &hash_node->node);
228         sbrec_bindings_delete(hash_node->binding);
229         free(hash_node);
230     }
231     hmap_destroy(&bindings_hmap);
232 }
233
234 static void
235 ovnnb_db_changed(struct northd_context *ctx)
236 {
237     VLOG_DBG("ovn-nb db contents have changed.");
238
239     set_bindings(ctx);
240 }
241
242 /*
243  * The only change we get notified about is if the 'chassis' column of the
244  * 'Bindings' table changes.  When this column is not empty, it means we need to
245  * set the corresponding logical port as 'up' in the northbound DB.
246  */
247 static void
248 ovnsb_db_changed(struct northd_context *ctx)
249 {
250     struct hmap lports_hmap;
251     const struct sbrec_bindings *binding;
252     const struct nbrec_logical_port *lport;
253
254     struct lport_hash_node {
255         struct hmap_node node;
256         const struct nbrec_logical_port *lport;
257     } *hash_node, *hash_node_next;
258
259     VLOG_DBG("Recalculating port up states for ovn-nb db.");
260
261     hmap_init(&lports_hmap);
262
263     NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
264         hash_node = xzalloc(sizeof *hash_node);
265         hash_node->lport = lport;
266         hmap_insert(&lports_hmap, &hash_node->node,
267                 hash_string(lport->name, 0));
268     }
269
270     SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
271         lport = NULL;
272         HMAP_FOR_EACH_WITH_HASH(hash_node, node,
273                 hash_string(binding->logical_port, 0), &lports_hmap) {
274             if (!strcmp(binding->logical_port, hash_node->lport->name)) {
275                 lport = hash_node->lport;
276                 break;
277             }
278         }
279
280         if (!lport) {
281             /* The logical port doesn't exist for this binding.  This can
282              * happen under normal circumstances when ovn-northd hasn't gotten
283              * around to pruning the Binding yet. */
284             continue;
285         }
286
287         if (*binding->chassis && (!lport->up || !*lport->up)) {
288             bool up = true;
289             nbrec_logical_port_set_up(lport, &up, 1);
290         } else if (!*binding->chassis && (!lport->up || *lport->up)) {
291             bool up = false;
292             nbrec_logical_port_set_up(lport, &up, 1);
293         }
294     }
295
296     HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
297         hmap_remove(&lports_hmap, &hash_node->node);
298         free(hash_node);
299     }
300     hmap_destroy(&lports_hmap);
301 }
302 \f
303 static const char *
304 default_db(void)
305 {
306     static char *def;
307     if (!def) {
308         def = xasprintf("unix:%s/db.sock", ovs_rundir());
309     }
310     return def;
311 }
312
313 static void
314 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
315 {
316     enum {
317         DAEMON_OPTION_ENUMS,
318         VLOG_OPTION_ENUMS,
319     };
320     static const struct option long_options[] = {
321         {"ovnsb-db", required_argument, NULL, 'd'},
322         {"ovnnb-db", required_argument, NULL, 'D'},
323         {"help", no_argument, NULL, 'h'},
324         {"options", no_argument, NULL, 'o'},
325         {"version", no_argument, NULL, 'V'},
326         DAEMON_LONG_OPTIONS,
327         VLOG_LONG_OPTIONS,
328         STREAM_SSL_LONG_OPTIONS,
329         {NULL, 0, NULL, 0},
330     };
331     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
332
333     for (;;) {
334         int c;
335
336         c = getopt_long(argc, argv, short_options, long_options, NULL);
337         if (c == -1) {
338             break;
339         }
340
341         switch (c) {
342         DAEMON_OPTION_HANDLERS;
343         VLOG_OPTION_HANDLERS;
344         STREAM_SSL_OPTION_HANDLERS;
345
346         case 'd':
347             ovnsb_db = optarg;
348             break;
349
350         case 'D':
351             ovnnb_db = optarg;
352             break;
353
354         case 'h':
355             usage();
356             exit(EXIT_SUCCESS);
357
358         case 'o':
359             ovs_cmdl_print_options(long_options);
360             exit(EXIT_SUCCESS);
361
362         case 'V':
363             ovs_print_version(0, 0);
364             exit(EXIT_SUCCESS);
365
366         default:
367             break;
368         }
369     }
370
371     if (!ovnsb_db) {
372         ovnsb_db = default_db();
373     }
374
375     if (!ovnnb_db) {
376         ovnnb_db = default_db();
377     }
378
379     free(short_options);
380 }
381
382 int
383 main(int argc, char *argv[])
384 {
385     extern struct vlog_module VLM_reconnect;
386     struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
387     unsigned int ovnnb_seqno, ovn_seqno;
388     int res = EXIT_SUCCESS;
389     struct northd_context ctx = {
390         .ovnsb_txn = NULL,
391     };
392     bool ovnnb_changes_pending = false;
393     bool ovn_changes_pending = false;
394
395     fatal_ignore_sigpipe();
396     set_program_name(argv[0]);
397     vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
398     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
399     parse_options(argc, argv);
400
401     daemonize();
402
403     nbrec_init();
404     sbrec_init();
405
406     /* We want to detect all changes to the ovn-nb db. */
407     ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
408             &nbrec_idl_class, true, true);
409
410     /* There is only a small subset of changes to the ovn-sb db that ovn-northd
411      * has to care about, so we'll enable monitoring those directly. */
412     ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
413             &sbrec_idl_class, false, true);
414     ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_bindings);
415     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_port);
416     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_chassis);
417     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_mac);
418     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tag);
419     ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_parent_port);
420
421     /*
422      * The loop here just runs the IDL in a loop waiting for the seqno to
423      * change, which indicates that the contents of the db have changed.
424      *
425      * If the contents of the ovn-nb db change, the mappings to the ovn-sb
426      * db must be recalculated.
427      *
428      * If the contents of the ovn-sb db change, it means the 'up' state of
429      * a port may have changed, as that's the only type of change ovn-northd is
430      * watching for.
431      */
432
433     ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
434     ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
435     for (;;) {
436         ovsdb_idl_run(ovnnb_idl);
437         ovsdb_idl_run(ovnsb_idl);
438
439         if (!ovsdb_idl_is_alive(ovnnb_idl)) {
440             int retval = ovsdb_idl_get_last_error(ovnnb_idl);
441             VLOG_ERR("%s: database connection failed (%s)",
442                     ovnnb_db, ovs_retval_to_string(retval));
443             res = EXIT_FAILURE;
444             break;
445         }
446
447         if (!ovsdb_idl_is_alive(ovnsb_idl)) {
448             int retval = ovsdb_idl_get_last_error(ovnsb_idl);
449             VLOG_ERR("%s: database connection failed (%s)",
450                     ovnsb_db, ovs_retval_to_string(retval));
451             res = EXIT_FAILURE;
452             break;
453         }
454
455         if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
456             ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
457             ovnnb_changes_pending = true;
458         }
459
460         if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
461             ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
462             ovn_changes_pending = true;
463         }
464
465         /*
466          * If there are any pending changes, we delay recalculating the
467          * necessary updates until after an existing transaction finishes.
468          * This avoids the possibility of rapid updates causing ovn-northd to
469          * never be able to successfully make the corresponding updates to the
470          * other db.  Instead, pending changes are batched up until the next
471          * time we get a chance to calculate the new state and apply it.
472          */
473
474         if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
475             /*
476              * The OVN-nb db contents have changed, so create a transaction for
477              * updating the OVN-sb DB.
478              */
479             ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
480             ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
481                                       "ovn-northd: northbound db changed");
482             ovnnb_db_changed(&ctx);
483             ovnnb_changes_pending = false;
484         }
485
486         if (ovn_changes_pending && !ctx.ovnnb_txn) {
487             /*
488              * The OVN-sb db contents have changed, so create a transaction for
489              * updating the northbound DB.
490              */
491             ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
492             ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
493                                       "ovn-northd: southbound db changed");
494             ovnsb_db_changed(&ctx);
495             ovn_changes_pending = false;
496         }
497
498         if (ctx.ovnnb_txn) {
499             enum ovsdb_idl_txn_status txn_status;
500             txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
501             switch (txn_status) {
502             case TXN_UNCOMMITTED:
503             case TXN_INCOMPLETE:
504                 /* Come back around and try to commit this transaction again */
505                 break;
506             case TXN_ABORTED:
507             case TXN_TRY_AGAIN:
508             case TXN_NOT_LOCKED:
509             case TXN_ERROR:
510                 /* Something went wrong, so try creating a new transaction. */
511                 ovn_changes_pending = true;
512             case TXN_UNCHANGED:
513             case TXN_SUCCESS:
514                 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
515                 ctx.ovnnb_txn = NULL;
516             }
517         }
518
519         if (ctx.ovnsb_txn) {
520             enum ovsdb_idl_txn_status txn_status;
521             txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
522             switch (txn_status) {
523             case TXN_UNCOMMITTED:
524             case TXN_INCOMPLETE:
525                 /* Come back around and try to commit this transaction again */
526                 break;
527             case TXN_ABORTED:
528             case TXN_TRY_AGAIN:
529             case TXN_NOT_LOCKED:
530             case TXN_ERROR:
531                 /* Something went wrong, so try creating a new transaction. */
532                 ovnnb_changes_pending = true;
533             case TXN_UNCHANGED:
534             case TXN_SUCCESS:
535                 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
536                 ctx.ovnsb_txn = NULL;
537             }
538         }
539
540         if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
541                 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
542             ovsdb_idl_wait(ovnnb_idl);
543             ovsdb_idl_wait(ovnsb_idl);
544             if (ctx.ovnnb_txn) {
545                 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
546             }
547             if (ctx.ovnsb_txn) {
548                 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
549             }
550             poll_block();
551         }
552     }
553
554     ovsdb_idl_destroy(ovnsb_idl);
555     ovsdb_idl_destroy(ovnnb_idl);
556
557     exit(res);
558 }