ovn: Change type of 'chassis' column in 'Binding' table.
[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 "dynamic-string.h"
25 #include "fatal-signal.h"
26 #include "hash.h"
27 #include "hmap.h"
28 #include "json.h"
29 #include "ovn/lib/lex.h"
30 #include "ovn/lib/ovn-nb-idl.h"
31 #include "ovn/lib/ovn-sb-idl.h"
32 #include "poll-loop.h"
33 #include "stream.h"
34 #include "stream-ssl.h"
35 #include "unixctl.h"
36 #include "util.h"
37 #include "uuid.h"
38 #include "openvswitch/vlog.h"
39
40 VLOG_DEFINE_THIS_MODULE(ovn_northd);
41
42 static unixctl_cb_func ovn_northd_exit;
43
44 struct northd_context {
45     struct ovsdb_idl *ovnnb_idl;
46     struct ovsdb_idl *ovnsb_idl;
47     struct ovsdb_idl_txn *ovnnb_txn;
48     struct ovsdb_idl_txn *ovnsb_txn;
49 };
50
51 static const char *ovnnb_db;
52 static const char *ovnsb_db;
53
54 static const char *default_db(void);
55
56 static void
57 usage(void)
58 {
59     printf("\
60 %s: OVN northbound management daemon\n\
61 usage: %s [OPTIONS]\n\
62 \n\
63 Options:\n\
64   --ovnnb-db=DATABASE       connect to ovn-nb database at DATABASE\n\
65                             (default: %s)\n\
66   --ovnsb-db=DATABASE       connect to ovn-sb database at DATABASE\n\
67                             (default: %s)\n\
68   -h, --help                display this help message\n\
69   -o, --options             list available options\n\
70   -V, --version             display version information\n\
71 ", program_name, program_name, default_db(), default_db());
72     daemon_usage();
73     vlog_usage();
74     stream_usage("database", true, true, false);
75 }
76 \f
77 static int
78 compare_strings(const void *a_, const void *b_)
79 {
80     char *const *a = a_;
81     char *const *b = b_;
82     return strcmp(*a, *b);
83 }
84
85 /*
86  * Determine whether 2 arrays of MAC addresses are the same.  It's possible that
87  * the lists could be *very* long and this check is being done a lot (every
88  * time the OVN_Northbound database changes).
89  */
90 static bool
91 macs_equal(char **binding_macs_, size_t b_n_macs,
92            char **lport_macs_, size_t l_n_macs)
93 {
94     char **binding_macs, **lport_macs;
95     size_t bytes, i;
96
97     if (b_n_macs != l_n_macs) {
98         return false;
99     }
100
101     bytes = b_n_macs * sizeof binding_macs_[0];
102     binding_macs = xmalloc(bytes);
103     lport_macs = xmalloc(bytes);
104
105     memcpy(binding_macs, binding_macs_, bytes);
106     memcpy(lport_macs, lport_macs_, bytes);
107
108     qsort(binding_macs, b_n_macs, sizeof binding_macs[0], compare_strings);
109     qsort(lport_macs, l_n_macs, sizeof lport_macs[0], compare_strings);
110
111     for (i = 0; i < b_n_macs; i++) {
112         if (strcmp(binding_macs[i], lport_macs[i])) {
113             break;
114         }
115     }
116
117     free(binding_macs);
118     free(lport_macs);
119
120     return (i == b_n_macs) ? true : false;
121 }
122 \f
123 /* Pipeline generation.
124  *
125  * This code generates the Pipeline table in the southbound database, as a
126  * function of most of the northbound database.
127  */
128
129 /* Enough context to add a Pipeline row, using pipeline_add(). */
130 struct pipeline_ctx {
131     /* From northd_context. */
132     struct ovsdb_idl *ovnsb_idl;
133     struct ovsdb_idl_txn *ovnsb_txn;
134
135     /* Contains "struct pipeline_hash_node"s.  Used to figure out what existing
136      * Pipeline rows should be deleted: we index all of the Pipeline rows into
137      * this data structure, then as existing rows are generated we remove them.
138      * After generating all the rows, any remaining in 'pipeline_hmap' must be
139      * deleted from the database. */
140     struct hmap pipeline_hmap;
141 };
142
143 /* A row in the Pipeline table, indexed by its full contents, */
144 struct pipeline_hash_node {
145     struct hmap_node node;
146     const struct sbrec_pipeline *pipeline;
147 };
148
149 static size_t
150 pipeline_hash(const struct uuid *logical_datapath, uint8_t table_id,
151               uint16_t priority, const char *match, const char *actions)
152 {
153     size_t hash = uuid_hash(logical_datapath);
154     hash = hash_2words((table_id << 16) | priority, hash);
155     hash = hash_string(match, hash);
156     return hash_string(actions, hash);
157 }
158
159 static size_t
160 pipeline_hash_rec(const struct sbrec_pipeline *pipeline)
161 {
162     return pipeline_hash(&pipeline->logical_datapath, pipeline->table_id,
163                          pipeline->priority, pipeline->match,
164                          pipeline->actions);
165 }
166
167 /* Adds a row with the specified contents to the Pipeline table. */
168 static void
169 pipeline_add(struct pipeline_ctx *ctx,
170              const struct nbrec_logical_switch *logical_datapath,
171              uint8_t table_id,
172              uint16_t priority,
173              const char *match,
174              const char *actions)
175 {
176     struct pipeline_hash_node *hash_node;
177
178     /* Check whether such a row already exists in the Pipeline table.  If so,
179      * remove it from 'ctx->pipeline_hmap' and we're done. */
180     HMAP_FOR_EACH_WITH_HASH (hash_node, node,
181                              pipeline_hash(&logical_datapath->header_.uuid,
182                                            table_id, priority, match, actions),
183                              &ctx->pipeline_hmap) {
184         const struct sbrec_pipeline *pipeline = hash_node->pipeline;
185         if (uuid_equals(&pipeline->logical_datapath,
186                         &logical_datapath->header_.uuid)
187             && pipeline->table_id == table_id
188             && pipeline->priority == priority
189             && !strcmp(pipeline->match, match)
190             && !strcmp(pipeline->actions, actions)) {
191             hmap_remove(&ctx->pipeline_hmap, &hash_node->node);
192             free(hash_node);
193             return;
194         }
195     }
196
197     /* No such Pipeline row.  Add one. */
198     const struct sbrec_pipeline *pipeline;
199     pipeline = sbrec_pipeline_insert(ctx->ovnsb_txn);
200     sbrec_pipeline_set_logical_datapath(pipeline,
201                                         logical_datapath->header_.uuid);
202     sbrec_pipeline_set_table_id(pipeline, table_id);
203     sbrec_pipeline_set_priority(pipeline, priority);
204     sbrec_pipeline_set_match(pipeline, match);
205     sbrec_pipeline_set_actions(pipeline, actions);
206 }
207
208 /* Appends port security constraints on L2 address field 'eth_addr_field'
209  * (e.g. "eth.src" or "eth.dst") to 'match'.  'port_security', with
210  * 'n_port_security' elements, is the collection of port_security constraints
211  * from an OVN_NB Logical_Port row. */
212 static void
213 build_port_security(const char *eth_addr_field,
214                     char **port_security, size_t n_port_security,
215                     struct ds *match)
216 {
217     size_t base_len = match->length;
218     ds_put_format(match, " && %s == {", eth_addr_field);
219
220     size_t n = 0;
221     for (size_t i = 0; i < n_port_security; i++) {
222         uint8_t ea[ETH_ADDR_LEN];
223
224         if (eth_addr_from_string(port_security[i], ea)) {
225             ds_put_format(match, ETH_ADDR_FMT, ETH_ADDR_ARGS(ea));
226             ds_put_char(match, ' ');
227             n++;
228         }
229     }
230     ds_chomp(match, ' ');
231     ds_put_cstr(match, "}");
232
233     if (!n) {
234         match->length = base_len;
235     }
236 }
237
238 /* Updates the Pipeline table in the OVN_SB database, constructing its contents
239  * based on the OVN_NB database. */
240 static void
241 build_pipeline(struct northd_context *ctx)
242 {
243     struct pipeline_ctx pc = {
244         .ovnsb_idl = ctx->ovnsb_idl,
245         .ovnsb_txn = ctx->ovnsb_txn,
246         .pipeline_hmap = HMAP_INITIALIZER(&pc.pipeline_hmap)
247     };
248
249     /* Add all the Pipeline entries currently in the southbound database to
250      * 'pc.pipeline_hmap'.  We remove entries that we generate from the hmap,
251      * thus by the time we're done only entries that need to be removed
252      * remain. */
253     const struct sbrec_pipeline *pipeline;
254     SBREC_PIPELINE_FOR_EACH (pipeline, ctx->ovnsb_idl) {
255         struct pipeline_hash_node *hash_node = xzalloc(sizeof *hash_node);
256         hash_node->pipeline = pipeline;
257         hmap_insert(&pc.pipeline_hmap, &hash_node->node,
258                     pipeline_hash_rec(pipeline));
259     }
260
261     /* Table 0: Admission control framework. */
262     const struct nbrec_logical_switch *lswitch;
263     NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
264         /* Logical VLANs not supported. */
265         pipeline_add(&pc, lswitch, 0, 100, "vlan.present", "drop;");
266
267         /* Broadcast/multicast source address is invalid. */
268         pipeline_add(&pc, lswitch, 0, 100, "eth.src[40]", "drop;");
269
270         /* Port security flows have priority 50 (see below) and will continue
271          * to the next table if packet source is acceptable. */
272
273         /* Otherwise drop the packet. */
274         pipeline_add(&pc, lswitch, 0, 0, "1", "drop;");
275     }
276
277     /* Table 0: Ingress port security. */
278     const struct nbrec_logical_port *lport;
279     NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
280         struct ds match = DS_EMPTY_INITIALIZER;
281         ds_put_cstr(&match, "inport == ");
282         json_string_escape(lport->name, &match);
283         build_port_security("eth.src",
284                             lport->port_security, lport->n_port_security,
285                             &match);
286         pipeline_add(&pc, lport->lswitch, 0, 50, ds_cstr(&match), "next;");
287         ds_destroy(&match);
288     }
289
290     /* Table 1: Destination lookup, broadcast and multicast handling (priority
291      * 100). */
292     NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
293         struct ds actions;
294
295         ds_init(&actions);
296         NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
297             if (lport->lswitch == lswitch) {
298                 ds_put_cstr(&actions, "outport = ");
299                 json_string_escape(lport->name, &actions);
300                 ds_put_cstr(&actions, "; next; ");
301             }
302         }
303         ds_chomp(&actions, ' ');
304
305         pipeline_add(&pc, lswitch, 1, 100, "eth.dst[40]", ds_cstr(&actions));
306         ds_destroy(&actions);
307     }
308
309     /* Table 1: Destination lookup, unicast handling (priority 50),  */
310     struct unknown_actions {
311         struct hmap_node hmap_node;
312         const struct nbrec_logical_switch *ls;
313         struct ds actions;
314     };
315     struct hmap unknown_actions = HMAP_INITIALIZER(&unknown_actions);
316     NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
317         lswitch = lport->lswitch;
318         for (size_t i = 0; i < lport->n_macs; i++) {
319             uint8_t mac[ETH_ADDR_LEN];
320
321             if (eth_addr_from_string(lport->macs[i], mac)) {
322                 struct ds match, actions;
323
324                 ds_init(&match);
325                 ds_put_format(&match, "eth.dst == %s", lport->macs[i]);
326
327                 ds_init(&actions);
328                 ds_put_cstr(&actions, "outport = ");
329                 json_string_escape(lport->name, &actions);
330                 ds_put_cstr(&actions, "; next;");
331                 pipeline_add(&pc, lswitch, 1, 50,
332                              ds_cstr(&match), ds_cstr(&actions));
333                 ds_destroy(&actions);
334                 ds_destroy(&match);
335             } else if (!strcmp(lport->macs[i], "unknown")) {
336                 const struct uuid *uuid = &lswitch->header_.uuid;
337                 struct unknown_actions *ua = NULL;
338                 struct unknown_actions *iter;
339                 HMAP_FOR_EACH_WITH_HASH (iter, hmap_node, uuid_hash(uuid),
340                                          &unknown_actions) {
341                     if (uuid_equals(&iter->ls->header_.uuid, uuid)) {
342                         ua = iter;
343                         break;
344                     }
345                 }
346                 if (!ua) {
347                     ua = xmalloc(sizeof *ua);
348                     hmap_insert(&unknown_actions, &ua->hmap_node,
349                                 uuid_hash(uuid));
350                     ua->ls = lswitch;
351                     ds_init(&ua->actions);
352                 } else {
353                     ds_put_char(&ua->actions, ' ');
354                 }
355
356                 ds_put_cstr(&ua->actions, "outport = ");
357                 json_string_escape(lport->name, &ua->actions);
358                 ds_put_cstr(&ua->actions, "; next;");
359             } else {
360                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
361
362                 VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
363                              lport->name, lport->macs[i]);
364             }
365         }
366     }
367
368     /* Table 1: Destination lookup for unknown MACs (priority 0). */
369     struct unknown_actions *ua, *next_ua;
370     HMAP_FOR_EACH_SAFE (ua, next_ua, hmap_node, &unknown_actions) {
371         pipeline_add(&pc, ua->ls, 1, 0, "1", ds_cstr(&ua->actions));
372         hmap_remove(&unknown_actions, &ua->hmap_node);
373         ds_destroy(&ua->actions);
374         free(ua);
375     }
376     hmap_destroy(&unknown_actions);
377
378     /* Table 2: ACLs. */
379     const struct nbrec_acl *acl;
380     NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
381         const char *action;
382
383         action = (!strcmp(acl->action, "allow") ||
384                   !strcmp(acl->action, "allow-related"))
385                       ? "next;" : "drop;";
386         pipeline_add(&pc, acl->lswitch, 2, acl->priority, acl->match, action);
387     }
388     NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
389         pipeline_add(&pc, lswitch, 2, 0, "1", "next;");
390     }
391
392     /* Table 3: Egress port security. */
393     NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
394         pipeline_add(&pc, lswitch, 3, 100, "eth.dst[40]", "output;");
395     }
396     NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
397         struct ds match;
398
399         ds_init(&match);
400         ds_put_cstr(&match, "outport == ");
401         json_string_escape(lport->name, &match);
402         build_port_security("eth.dst",
403                             lport->port_security, lport->n_port_security,
404                             &match);
405
406         pipeline_add(&pc, lport->lswitch, 3, 50, ds_cstr(&match), "output;");
407
408         ds_destroy(&match);
409     }
410
411     /* Delete any existing Pipeline rows that were not re-generated.  */
412     struct pipeline_hash_node *hash_node, *next_hash_node;
413     HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &pc.pipeline_hmap) {
414         hmap_remove(&pc.pipeline_hmap, &hash_node->node);
415         sbrec_pipeline_delete(hash_node->pipeline);
416         free(hash_node);
417     }
418     hmap_destroy(&pc.pipeline_hmap);
419 }
420 \f
421 static bool
422 parents_equal(const struct sbrec_binding *binding,
423               const struct nbrec_logical_port *lport)
424 {
425     if (!!binding->parent_port != !!lport->parent_name) {
426         /* One is set and the other is not. */
427         return false;
428     }
429
430     if (binding->parent_port) {
431         /* Both are set. */
432         return strcmp(binding->parent_port, lport->parent_name) ? false : true;
433     }
434
435     /* Both are NULL. */
436     return true;
437 }
438
439 static bool
440 tags_equal(const struct sbrec_binding *binding,
441            const struct nbrec_logical_port *lport)
442 {
443     if (binding->n_tag != lport->n_tag) {
444         return false;
445     }
446
447     return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
448 }
449
450 struct binding_hash_node {
451     struct hmap_node lp_node; /* In 'lp_map', by binding->logical_port. */
452     struct hmap_node tk_node; /* In 'tk_map', by binding->tunnel_key. */
453     const struct sbrec_binding *binding;
454 };
455
456 static bool
457 tunnel_key_in_use(const struct hmap *tk_hmap, uint16_t tunnel_key)
458 {
459     const struct binding_hash_node *hash_node;
460
461     HMAP_FOR_EACH_IN_BUCKET (hash_node, tk_node, hash_int(tunnel_key, 0),
462                              tk_hmap) {
463         if (hash_node->binding->tunnel_key == tunnel_key) {
464             return true;
465         }
466     }
467     return false;
468 }
469
470 /* Chooses and returns a positive tunnel key that is not already in use in
471  * 'tk_hmap'.  Returns 0 if all tunnel keys are in use. */
472 static uint16_t
473 choose_tunnel_key(const struct hmap *tk_hmap)
474 {
475     static uint16_t prev;
476
477     for (uint16_t key = prev + 1; key != prev; key++) {
478         if (!tunnel_key_in_use(tk_hmap, key)) {
479             prev = key;
480             return key;
481         }
482     }
483
484     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
485     VLOG_WARN_RL(&rl, "all tunnel keys exhausted");
486     return 0;
487 }
488
489 /*
490  * When a change has occurred in the OVN_Northbound database, we go through and
491  * make sure that the contents of the Binding table in the OVN_Southbound
492  * database are up to date with the logical ports defined in the
493  * OVN_Northbound database.
494  */
495 static void
496 set_bindings(struct northd_context *ctx)
497 {
498     const struct sbrec_binding *binding;
499     const struct nbrec_logical_port *lport;
500
501     /*
502      * We will need to look up a binding for every logical port.  We don't want
503      * to have to do an O(n) search for every binding, so start out by hashing
504      * them on the logical port.
505      *
506      * As we go through every logical port, we will update the binding if it
507      * exists or create one otherwise.  When the update is done, we'll remove
508      * it from the hashmap.  At the end, any bindings left in the hashmap are
509      * for logical ports that have been deleted.
510      *
511      * We index the logical_port column because that's the shared key between
512      * the OVN_NB and OVN_SB databases.  We index the tunnel_key column to
513      * allow us to choose a unique tunnel key for any Binding rows we have to
514      * add.
515      */
516     struct hmap lp_hmap = HMAP_INITIALIZER(&lp_hmap);
517     struct hmap tk_hmap = HMAP_INITIALIZER(&tk_hmap);
518
519     SBREC_BINDING_FOR_EACH(binding, ctx->ovnsb_idl) {
520         struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
521         hash_node->binding = binding;
522         hmap_insert(&lp_hmap, &hash_node->lp_node,
523                     hash_string(binding->logical_port, 0));
524         hmap_insert(&tk_hmap, &hash_node->tk_node,
525                     hash_int(binding->tunnel_key, 0));
526     }
527
528     NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
529         struct binding_hash_node *hash_node;
530         binding = NULL;
531         HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node,
532                                 hash_string(lport->name, 0), &lp_hmap) {
533             if (!strcmp(lport->name, hash_node->binding->logical_port)) {
534                 binding = hash_node->binding;
535                 break;
536             }
537         }
538
539         struct uuid logical_datapath;
540         if (lport->lswitch) {
541             logical_datapath = lport->lswitch->header_.uuid;
542         } else {
543             uuid_zero(&logical_datapath);
544         }
545
546         if (binding) {
547             /* We found an existing binding for this logical port.  Update its
548              * contents. */
549
550             hmap_remove(&lp_hmap, &hash_node->lp_node);
551
552             if (!macs_equal(binding->mac, binding->n_mac,
553                         lport->macs, lport->n_macs)) {
554                 sbrec_binding_set_mac(binding,
555                         (const char **) lport->macs, lport->n_macs);
556             }
557             if (!parents_equal(binding, lport)) {
558                 sbrec_binding_set_parent_port(binding, lport->parent_name);
559             }
560             if (!tags_equal(binding, lport)) {
561                 sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
562             }
563             if (!uuid_equals(&binding->logical_datapath, &logical_datapath)) {
564                 sbrec_binding_set_logical_datapath(binding,
565                                                     logical_datapath);
566             }
567         } else {
568             /* There is no binding for this logical port, so create one. */
569
570             uint16_t tunnel_key = choose_tunnel_key(&tk_hmap);
571             if (!tunnel_key) {
572                 continue;
573             }
574
575             binding = sbrec_binding_insert(ctx->ovnsb_txn);
576             sbrec_binding_set_logical_port(binding, lport->name);
577             sbrec_binding_set_mac(binding,
578                     (const char **) lport->macs, lport->n_macs);
579             if (lport->parent_name && lport->n_tag > 0) {
580                 sbrec_binding_set_parent_port(binding, lport->parent_name);
581                 sbrec_binding_set_tag(binding, lport->tag, lport->n_tag);
582             }
583
584             sbrec_binding_set_tunnel_key(binding, tunnel_key);
585             sbrec_binding_set_logical_datapath(binding, logical_datapath);
586
587             /* Add the tunnel key to the tk_hmap so that we don't try to use it
588              * for another port.  (We don't want it in the lp_hmap because that
589              * would just get the Binding record deleted later.) */
590             struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
591             hash_node->binding = binding;
592             hmap_insert(&tk_hmap, &hash_node->tk_node,
593                         hash_int(binding->tunnel_key, 0));
594         }
595     }
596
597     struct binding_hash_node *hash_node;
598     HMAP_FOR_EACH (hash_node, lp_node, &lp_hmap) {
599         hmap_remove(&lp_hmap, &hash_node->lp_node);
600         sbrec_binding_delete(hash_node->binding);
601     }
602     hmap_destroy(&lp_hmap);
603
604     struct binding_hash_node *hash_node_next;
605     HMAP_FOR_EACH_SAFE (hash_node, hash_node_next, tk_node, &tk_hmap) {
606         hmap_remove(&tk_hmap, &hash_node->tk_node);
607         free(hash_node);
608     }
609     hmap_destroy(&tk_hmap);
610 }
611
612 static void
613 ovnnb_db_changed(struct northd_context *ctx)
614 {
615     VLOG_DBG("ovn-nb db contents have changed.");
616
617     set_bindings(ctx);
618     build_pipeline(ctx);
619 }
620
621 /*
622  * The only change we get notified about is if the 'chassis' column of the
623  * 'Binding' table changes.  When this column is not empty, it means we need to
624  * set the corresponding logical port as 'up' in the northbound DB.
625  */
626 static void
627 ovnsb_db_changed(struct northd_context *ctx)
628 {
629     struct hmap lports_hmap;
630     const struct sbrec_binding *binding;
631     const struct nbrec_logical_port *lport;
632
633     struct lport_hash_node {
634         struct hmap_node node;
635         const struct nbrec_logical_port *lport;
636     } *hash_node, *hash_node_next;
637
638     VLOG_DBG("Recalculating port up states for ovn-nb db.");
639
640     hmap_init(&lports_hmap);
641
642     NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
643         hash_node = xzalloc(sizeof *hash_node);
644         hash_node->lport = lport;
645         hmap_insert(&lports_hmap, &hash_node->node,
646                 hash_string(lport->name, 0));
647     }
648
649     SBREC_BINDING_FOR_EACH(binding, ctx->ovnsb_idl) {
650         lport = NULL;
651         HMAP_FOR_EACH_WITH_HASH(hash_node, node,
652                 hash_string(binding->logical_port, 0), &lports_hmap) {
653             if (!strcmp(binding->logical_port, hash_node->lport->name)) {
654                 lport = hash_node->lport;
655                 break;
656             }
657         }
658
659         if (!lport) {
660             /* The logical port doesn't exist for this binding.  This can
661              * happen under normal circumstances when ovn-northd hasn't gotten
662              * around to pruning the Binding yet. */
663             continue;
664         }
665
666         if (binding->chassis && (!lport->up || !*lport->up)) {
667             bool up = true;
668             nbrec_logical_port_set_up(lport, &up, 1);
669         } else if (!binding->chassis && (!lport->up || *lport->up)) {
670             bool up = false;
671             nbrec_logical_port_set_up(lport, &up, 1);
672         }
673     }
674
675     HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
676         hmap_remove(&lports_hmap, &hash_node->node);
677         free(hash_node);
678     }
679     hmap_destroy(&lports_hmap);
680 }
681 \f
682 static const char *
683 default_db(void)
684 {
685     static char *def;
686     if (!def) {
687         def = xasprintf("unix:%s/db.sock", ovs_rundir());
688     }
689     return def;
690 }
691
692 static void
693 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
694 {
695     enum {
696         DAEMON_OPTION_ENUMS,
697         VLOG_OPTION_ENUMS,
698     };
699     static const struct option long_options[] = {
700         {"ovnsb-db", required_argument, NULL, 'd'},
701         {"ovnnb-db", required_argument, NULL, 'D'},
702         {"help", no_argument, NULL, 'h'},
703         {"options", no_argument, NULL, 'o'},
704         {"version", no_argument, NULL, 'V'},
705         DAEMON_LONG_OPTIONS,
706         VLOG_LONG_OPTIONS,
707         STREAM_SSL_LONG_OPTIONS,
708         {NULL, 0, NULL, 0},
709     };
710     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
711
712     for (;;) {
713         int c;
714
715         c = getopt_long(argc, argv, short_options, long_options, NULL);
716         if (c == -1) {
717             break;
718         }
719
720         switch (c) {
721         DAEMON_OPTION_HANDLERS;
722         VLOG_OPTION_HANDLERS;
723         STREAM_SSL_OPTION_HANDLERS;
724
725         case 'd':
726             ovnsb_db = optarg;
727             break;
728
729         case 'D':
730             ovnnb_db = optarg;
731             break;
732
733         case 'h':
734             usage();
735             exit(EXIT_SUCCESS);
736
737         case 'o':
738             ovs_cmdl_print_options(long_options);
739             exit(EXIT_SUCCESS);
740
741         case 'V':
742             ovs_print_version(0, 0);
743             exit(EXIT_SUCCESS);
744
745         default:
746             break;
747         }
748     }
749
750     if (!ovnsb_db) {
751         ovnsb_db = default_db();
752     }
753
754     if (!ovnnb_db) {
755         ovnnb_db = default_db();
756     }
757
758     free(short_options);
759 }
760
761 int
762 main(int argc, char *argv[])
763 {
764     extern struct vlog_module VLM_reconnect;
765     struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
766     unsigned int ovnnb_seqno, ovn_seqno;
767     int res = EXIT_SUCCESS;
768     struct northd_context ctx = {
769         .ovnsb_txn = NULL,
770     };
771     bool ovnnb_changes_pending = false;
772     bool ovn_changes_pending = false;
773     struct unixctl_server *unixctl;
774     int retval;
775     bool exiting;
776
777     fatal_ignore_sigpipe();
778     set_program_name(argv[0]);
779     vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
780     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
781     parse_options(argc, argv);
782
783     daemonize_start();
784
785     retval = unixctl_server_create(NULL, &unixctl);
786     if (retval) {
787         exit(EXIT_FAILURE);
788     }
789     unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting);
790
791     daemonize_complete();
792
793     nbrec_init();
794     sbrec_init();
795
796     /* We want to detect all changes to the ovn-nb db. */
797     ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
798             &nbrec_idl_class, true, true);
799
800     /* There is only a small subset of changes to the ovn-sb db that ovn-northd
801      * has to care about, so we'll enable monitoring those directly. */
802     ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
803             &sbrec_idl_class, false, true);
804     ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_binding);
805     ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_logical_port);
806     ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_chassis);
807     ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_mac);
808     ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_tag);
809     ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_parent_port);
810     ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_logical_datapath);
811     ovsdb_idl_add_column(ovnsb_idl, &sbrec_binding_col_tunnel_key);
812     ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
813     ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
814     ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id);
815     ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_table_id);
816     ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_priority);
817     ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_priority);
818     ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_match);
819     ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_match);
820     ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_actions);
821     ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_actions);
822
823     /*
824      * The loop here just runs the IDL in a loop waiting for the seqno to
825      * change, which indicates that the contents of the db have changed.
826      *
827      * If the contents of the ovn-nb db change, the mappings to the ovn-sb
828      * db must be recalculated.
829      *
830      * If the contents of the ovn-sb db change, it means the 'up' state of
831      * a port may have changed, as that's the only type of change ovn-northd is
832      * watching for.
833      */
834
835     ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
836     ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
837     exiting = false;
838     while (!exiting) {
839         ovsdb_idl_run(ovnnb_idl);
840         ovsdb_idl_run(ovnsb_idl);
841         unixctl_server_run(unixctl);
842
843         if (!ovsdb_idl_is_alive(ovnnb_idl)) {
844             int retval = ovsdb_idl_get_last_error(ovnnb_idl);
845             VLOG_ERR("%s: database connection failed (%s)",
846                     ovnnb_db, ovs_retval_to_string(retval));
847             res = EXIT_FAILURE;
848             break;
849         }
850
851         if (!ovsdb_idl_is_alive(ovnsb_idl)) {
852             int retval = ovsdb_idl_get_last_error(ovnsb_idl);
853             VLOG_ERR("%s: database connection failed (%s)",
854                     ovnsb_db, ovs_retval_to_string(retval));
855             res = EXIT_FAILURE;
856             break;
857         }
858
859         if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
860             ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
861             ovnnb_changes_pending = true;
862         }
863
864         if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
865             ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
866             ovn_changes_pending = true;
867         }
868
869         /*
870          * If there are any pending changes, we delay recalculating the
871          * necessary updates until after an existing transaction finishes.
872          * This avoids the possibility of rapid updates causing ovn-northd to
873          * never be able to successfully make the corresponding updates to the
874          * other db.  Instead, pending changes are batched up until the next
875          * time we get a chance to calculate the new state and apply it.
876          */
877
878         if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
879             /*
880              * The OVN-nb db contents have changed, so create a transaction for
881              * updating the OVN-sb DB.
882              */
883             ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
884             ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
885                                       "ovn-northd: northbound db changed");
886             ovnnb_db_changed(&ctx);
887             ovnnb_changes_pending = false;
888         }
889
890         if (ovn_changes_pending && !ctx.ovnnb_txn) {
891             /*
892              * The OVN-sb db contents have changed, so create a transaction for
893              * updating the northbound DB.
894              */
895             ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
896             ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
897                                       "ovn-northd: southbound db changed");
898             ovnsb_db_changed(&ctx);
899             ovn_changes_pending = false;
900         }
901
902         if (ctx.ovnnb_txn) {
903             enum ovsdb_idl_txn_status txn_status;
904             txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
905             switch (txn_status) {
906             case TXN_UNCOMMITTED:
907             case TXN_INCOMPLETE:
908                 /* Come back around and try to commit this transaction again */
909                 break;
910             case TXN_ABORTED:
911             case TXN_TRY_AGAIN:
912             case TXN_NOT_LOCKED:
913             case TXN_ERROR:
914                 /* Something went wrong, so try creating a new transaction. */
915                 ovn_changes_pending = true;
916             case TXN_UNCHANGED:
917             case TXN_SUCCESS:
918                 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
919                 ctx.ovnnb_txn = NULL;
920             }
921         }
922
923         if (ctx.ovnsb_txn) {
924             enum ovsdb_idl_txn_status txn_status;
925             txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
926             switch (txn_status) {
927             case TXN_UNCOMMITTED:
928             case TXN_INCOMPLETE:
929                 /* Come back around and try to commit this transaction again */
930                 break;
931             case TXN_ABORTED:
932             case TXN_TRY_AGAIN:
933             case TXN_NOT_LOCKED:
934             case TXN_ERROR:
935                 /* Something went wrong, so try creating a new transaction. */
936                 ovnnb_changes_pending = true;
937             case TXN_UNCHANGED:
938             case TXN_SUCCESS:
939                 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
940                 ctx.ovnsb_txn = NULL;
941             }
942         }
943
944         if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
945                 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
946             ovsdb_idl_wait(ovnnb_idl);
947             ovsdb_idl_wait(ovnsb_idl);
948             if (ctx.ovnnb_txn) {
949                 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
950             }
951             if (ctx.ovnsb_txn) {
952                 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
953             }
954             unixctl_server_wait(unixctl);
955             if (exiting) {
956                 poll_immediate_wake();
957             }
958             poll_block();
959         }
960     }
961
962     unixctl_server_destroy(unixctl);
963     ovsdb_idl_destroy(ovnsb_idl);
964     ovsdb_idl_destroy(ovnnb_idl);
965
966     exit(res);
967 }
968
969 static void
970 ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
971                 const char *argv[] OVS_UNUSED, void *exiting_)
972 {
973     bool *exiting = exiting_;
974     *exiting = true;
975
976     unixctl_command_reply(conn, NULL);
977 }