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:
6 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "command-line.h"
24 #include "dynamic-string.h"
25 #include "fatal-signal.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"
34 #include "stream-ssl.h"
38 #include "openvswitch/vlog.h"
40 VLOG_DEFINE_THIS_MODULE(ovn_northd);
42 static unixctl_cb_func ovn_northd_exit;
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;
51 static const char *ovnnb_db;
52 static const char *ovnsb_db;
54 static const char *default_db(void);
60 %s: OVN northbound management daemon\n\
61 usage: %s [OPTIONS]\n\
64 --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
66 --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\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());
74 stream_usage("database", true, true, false);
78 compare_strings(const void *a_, const void *b_)
82 return strcmp(*a, *b);
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).
91 macs_equal(char **binding_macs_, size_t b_n_macs,
92 char **lport_macs_, size_t l_n_macs)
94 char **binding_macs, **lport_macs;
97 if (b_n_macs != l_n_macs) {
101 bytes = b_n_macs * sizeof binding_macs_[0];
102 binding_macs = xmalloc(bytes);
103 lport_macs = xmalloc(bytes);
105 memcpy(binding_macs, binding_macs_, bytes);
106 memcpy(lport_macs, lport_macs_, bytes);
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);
111 for (i = 0; i < b_n_macs; i++) {
112 if (strcmp(binding_macs[i], lport_macs[i])) {
120 return (i == b_n_macs) ? true : false;
123 /* Pipeline generation.
125 * This code generates the Pipeline table in the southbound database, as a
126 * function of most of the northbound database.
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;
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;
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;
150 pipeline_hash(const struct uuid *logical_datapath, uint8_t table_id,
151 uint16_t priority, const char *match, const char *actions)
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);
160 pipeline_hash_rec(const struct sbrec_pipeline *pipeline)
162 return pipeline_hash(&pipeline->logical_datapath, pipeline->table_id,
163 pipeline->priority, pipeline->match,
167 /* Adds a row with the specified contents to the Pipeline table. */
169 pipeline_add(struct pipeline_ctx *ctx,
170 const struct nbrec_logical_switch *logical_datapath,
176 struct pipeline_hash_node *hash_node;
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);
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);
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. */
213 build_port_security(const char *eth_addr_field,
214 char **port_security, size_t n_port_security,
217 size_t base_len = match->length;
218 ds_put_format(match, " && %s == {", eth_addr_field);
221 for (size_t i = 0; i < n_port_security; i++) {
222 uint8_t ea[ETH_ADDR_LEN];
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, ' ');
230 ds_chomp(match, ' ');
231 ds_put_cstr(match, "}");
234 match->length = base_len;
239 lport_is_enabled(const struct nbrec_logical_port *lport)
241 return !lport->enabled || *lport->enabled;
244 /* Updates the Pipeline table in the OVN_SB database, constructing its contents
245 * based on the OVN_NB database. */
247 build_pipeline(struct northd_context *ctx)
249 struct pipeline_ctx pc = {
250 .ovnsb_idl = ctx->ovnsb_idl,
251 .ovnsb_txn = ctx->ovnsb_txn,
252 .pipeline_hmap = HMAP_INITIALIZER(&pc.pipeline_hmap)
255 /* Add all the Pipeline entries currently in the southbound database to
256 * 'pc.pipeline_hmap'. We remove entries that we generate from the hmap,
257 * thus by the time we're done only entries that need to be removed
259 const struct sbrec_pipeline *pipeline;
260 SBREC_PIPELINE_FOR_EACH (pipeline, ctx->ovnsb_idl) {
261 struct pipeline_hash_node *hash_node = xzalloc(sizeof *hash_node);
262 hash_node->pipeline = pipeline;
263 hmap_insert(&pc.pipeline_hmap, &hash_node->node,
264 pipeline_hash_rec(pipeline));
267 /* Table 0: Admission control framework. */
268 const struct nbrec_logical_switch *lswitch;
269 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
270 /* Logical VLANs not supported. */
271 pipeline_add(&pc, lswitch, 0, 100, "vlan.present", "drop;");
273 /* Broadcast/multicast source address is invalid. */
274 pipeline_add(&pc, lswitch, 0, 100, "eth.src[40]", "drop;");
276 /* Port security flows have priority 50 (see below) and will continue
277 * to the next table if packet source is acceptable. */
279 /* Otherwise drop the packet. */
280 pipeline_add(&pc, lswitch, 0, 0, "1", "drop;");
283 /* Table 0: Ingress port security. */
284 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
285 for (size_t i = 0; i < lswitch->n_ports; i++) {
286 const struct nbrec_logical_port *lport = lswitch->ports[i];
287 struct ds match = DS_EMPTY_INITIALIZER;
288 ds_put_cstr(&match, "inport == ");
289 json_string_escape(lport->name, &match);
290 build_port_security("eth.src",
291 lport->port_security, lport->n_port_security,
293 pipeline_add(&pc, lswitch, 0, 50, ds_cstr(&match),
294 lport_is_enabled(lport) ? "next;" : "drop;");
299 /* Table 1: Destination lookup:
301 * - Broadcast and multicast handling (priority 100).
302 * - Unicast handling (priority 50).
303 * - Unknown unicast address handling (priority 0).
305 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
306 struct ds bcast; /* Actions for broadcast on 'lswitch'. */
307 struct ds unknown; /* Actions for unknown MACs on 'lswitch'. */
311 for (size_t i = 0; i < lswitch->n_ports; i++) {
312 const struct nbrec_logical_port *lport = lswitch->ports[i];
314 ds_put_cstr(&bcast, "outport = ");
315 json_string_escape(lport->name, &bcast);
316 ds_put_cstr(&bcast, "; next; ");
318 for (size_t j = 0; j < lport->n_macs; j++) {
319 const char *s = lport->macs[j];
320 uint8_t mac[ETH_ADDR_LEN];
322 if (eth_addr_from_string(s, mac)) {
323 struct ds match, unicast;
326 ds_put_format(&match, "eth.dst == %s", s);
329 ds_put_cstr(&unicast, "outport = ");
330 json_string_escape(lport->name, &unicast);
331 ds_put_cstr(&unicast, "; next;");
332 pipeline_add(&pc, lswitch, 1, 50,
333 ds_cstr(&match), ds_cstr(&unicast));
334 ds_destroy(&unicast);
336 } else if (!strcmp(s, "unknown")) {
337 ds_put_cstr(&unknown, "outport = ");
338 json_string_escape(lport->name, &unknown);
339 ds_put_cstr(&unknown, "; next; ");
341 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
343 VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
349 ds_chomp(&bcast, ' ');
350 pipeline_add(&pc, lswitch, 1, 100, "eth.dst[40]", ds_cstr(&bcast));
353 if (unknown.length) {
354 ds_chomp(&unknown, ' ');
355 pipeline_add(&pc, lswitch, 1, 0, "1", ds_cstr(&unknown));
357 ds_destroy(&unknown);
361 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
362 for (size_t i = 0; i < lswitch->n_acls; i++) {
363 const struct nbrec_acl *acl = lswitch->acls[i];
365 NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
366 pipeline_add(&pc, lswitch, 2, acl->priority, acl->match,
367 (!strcmp(acl->action, "allow") ||
368 !strcmp(acl->action, "allow-related")
369 ? "next;" : "drop;"));
373 pipeline_add(&pc, lswitch, 2, 0, "1", "next;");
376 /* Table 3: Egress port security. */
377 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
378 pipeline_add(&pc, lswitch, 3, 100, "eth.dst[40]", "output;");
380 for (size_t i = 0; i < lswitch->n_ports; i++) {
381 const struct nbrec_logical_port *lport = lswitch->ports[i];
385 ds_put_cstr(&match, "outport == ");
386 json_string_escape(lport->name, &match);
387 build_port_security("eth.dst",
388 lport->port_security, lport->n_port_security,
391 pipeline_add(&pc, lswitch, 3, 50, ds_cstr(&match),
392 lport_is_enabled(lport) ? "output;" : "drop;");
398 /* Delete any existing Pipeline rows that were not re-generated. */
399 struct pipeline_hash_node *hash_node, *next_hash_node;
400 HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &pc.pipeline_hmap) {
401 hmap_remove(&pc.pipeline_hmap, &hash_node->node);
402 sbrec_pipeline_delete(hash_node->pipeline);
405 hmap_destroy(&pc.pipeline_hmap);
409 * Take two string columns and return true if:
411 * - both are set and the strings are equal
414 strings_equal(const char *s1, const char *s2)
417 /* One is set and the other is not. */
423 return strcmp(s1, s2) ? false : true;
431 * Take two int64_t columns and return true if either:
433 * - both are set and the first integers in each are equal
436 int64_first_equal(int64_t *i1, size_t n_i1, int64_t *i2, size_t n_i2)
442 return i1 ? (i1[0] == i2[0]) : true;
445 struct port_binding_hash_node {
446 struct hmap_node lp_node; /* In 'lp_map', by binding->logical_port. */
447 struct hmap_node tk_node; /* In 'tk_map', by binding->tunnel_key. */
448 const struct sbrec_port_binding *binding;
452 tunnel_key_in_use(const struct hmap *tk_hmap, uint16_t tunnel_key)
454 const struct port_binding_hash_node *hash_node;
456 HMAP_FOR_EACH_IN_BUCKET (hash_node, tk_node, hash_int(tunnel_key, 0),
458 if (hash_node->binding->tunnel_key == tunnel_key) {
465 /* Chooses and returns a positive tunnel key that is not already in use in
466 * 'tk_hmap'. Returns 0 if all tunnel keys are in use. */
468 choose_tunnel_key(const struct hmap *tk_hmap)
470 static uint16_t prev;
472 for (uint16_t key = prev + 1; key != prev; key++) {
473 if (!tunnel_key_in_use(tk_hmap, key)) {
479 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
480 VLOG_WARN_RL(&rl, "all tunnel keys exhausted");
485 * When a change has occurred in the OVN_Northbound database, we go through and
486 * make sure that the contents of the Port_Binding table in the OVN_Southbound
487 * database are up to date with the logical ports defined in the
488 * OVN_Northbound database.
491 set_port_bindings(struct northd_context *ctx)
493 const struct sbrec_port_binding *binding;
496 * We will need to look up a port binding for every logical port. We don't
497 * want to have to do an O(n) search for every binding, so start out by
498 * hashing them on the logical port.
500 * As we go through every logical port, we will update the binding if it
501 * exists or create one otherwise. When the update is done, we'll remove
502 * it from the hashmap. At the end, any bindings left in the hashmap are
503 * for logical ports that have been deleted.
505 * We index the logical_port column because that's the shared key between
506 * the OVN_NB and OVN_SB databases. We index the tunnel_key column to
507 * allow us to choose a unique tunnel key for any Port_Binding rows we have
510 struct hmap lp_hmap = HMAP_INITIALIZER(&lp_hmap);
511 struct hmap tk_hmap = HMAP_INITIALIZER(&tk_hmap);
513 SBREC_PORT_BINDING_FOR_EACH(binding, ctx->ovnsb_idl) {
514 struct port_binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
515 hash_node->binding = binding;
516 hmap_insert(&lp_hmap, &hash_node->lp_node,
517 hash_string(binding->logical_port, 0));
518 hmap_insert(&tk_hmap, &hash_node->tk_node,
519 hash_int(binding->tunnel_key, 0));
522 const struct nbrec_logical_switch *lswitch;
523 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
524 const struct uuid *logical_datapath = &lswitch->header_.uuid;
526 for (size_t i = 0; i < lswitch->n_ports; i++) {
527 const struct nbrec_logical_port *lport = lswitch->ports[i];
528 struct port_binding_hash_node *hash_node;
530 HMAP_FOR_EACH_WITH_HASH(hash_node, lp_node,
531 hash_string(lport->name, 0), &lp_hmap) {
532 if (!strcmp(lport->name, hash_node->binding->logical_port)) {
533 binding = hash_node->binding;
539 /* We found an existing binding for this logical port. Update
542 hmap_remove(&lp_hmap, &hash_node->lp_node);
544 if (!macs_equal(binding->mac, binding->n_mac,
545 lport->macs, lport->n_macs)) {
546 sbrec_port_binding_set_mac(binding,
547 (const char **) lport->macs,
550 if (!strings_equal(binding->parent_port, lport->parent_name)) {
551 sbrec_port_binding_set_parent_port(binding,
554 if (!int64_first_equal(binding->tag, binding->n_tag,
555 lport->tag, lport->n_tag)) {
556 sbrec_port_binding_set_tag(binding,
557 lport->tag, lport->n_tag);
559 if (!uuid_equals(&binding->logical_datapath,
561 sbrec_port_binding_set_logical_datapath(binding,
564 if (!strings_equal(binding->type, lport->type)) {
565 sbrec_port_binding_set_type(binding, lport->type);
567 if (!smap_equal(&binding->options, &lport->options)) {
568 sbrec_port_binding_set_options(binding, &lport->options);
571 /* There is no binding for this logical port, so create one. */
573 uint16_t tunnel_key = choose_tunnel_key(&tk_hmap);
578 binding = sbrec_port_binding_insert(ctx->ovnsb_txn);
579 sbrec_port_binding_set_logical_port(binding, lport->name);
580 sbrec_port_binding_set_mac(binding,
581 (const char **) lport->macs,
583 if (lport->parent_name && lport->n_tag > 0) {
584 sbrec_port_binding_set_parent_port(binding,
586 sbrec_port_binding_set_tag(binding,
587 lport->tag, lport->n_tag);
590 sbrec_port_binding_set_tunnel_key(binding, tunnel_key);
591 sbrec_port_binding_set_logical_datapath(binding,
594 sbrec_port_binding_set_type(binding, lport->type);
595 sbrec_port_binding_set_options(binding, &lport->options);
597 /* Add the tunnel key to the tk_hmap so that we don't try to
598 * use it for another port. (We don't want it in the lp_hmap
599 * because that would just get the Binding record deleted
601 struct port_binding_hash_node *hash_node
602 = xzalloc(sizeof *hash_node);
603 hash_node->binding = binding;
604 hmap_insert(&tk_hmap, &hash_node->tk_node,
605 hash_int(binding->tunnel_key, 0));
610 struct port_binding_hash_node *hash_node;
611 HMAP_FOR_EACH (hash_node, lp_node, &lp_hmap) {
612 hmap_remove(&lp_hmap, &hash_node->lp_node);
613 sbrec_port_binding_delete(hash_node->binding);
615 hmap_destroy(&lp_hmap);
617 struct port_binding_hash_node *hash_node_next;
618 HMAP_FOR_EACH_SAFE (hash_node, hash_node_next, tk_node, &tk_hmap) {
619 hmap_remove(&tk_hmap, &hash_node->tk_node);
622 hmap_destroy(&tk_hmap);
626 ovnnb_db_changed(struct northd_context *ctx)
628 VLOG_DBG("ovn-nb db contents have changed.");
630 set_port_bindings(ctx);
635 * The only change we get notified about is if the 'chassis' column of the
636 * 'Port_Binding' table changes. When this column is not empty, it means we
637 * need to set the corresponding logical port as 'up' in the northbound DB.
640 ovnsb_db_changed(struct northd_context *ctx)
642 struct hmap lports_hmap;
643 const struct sbrec_port_binding *binding;
644 const struct nbrec_logical_port *lport;
646 struct lport_hash_node {
647 struct hmap_node node;
648 const struct nbrec_logical_port *lport;
649 } *hash_node, *hash_node_next;
651 VLOG_DBG("Recalculating port up states for ovn-nb db.");
653 hmap_init(&lports_hmap);
655 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
656 hash_node = xzalloc(sizeof *hash_node);
657 hash_node->lport = lport;
658 hmap_insert(&lports_hmap, &hash_node->node,
659 hash_string(lport->name, 0));
662 SBREC_PORT_BINDING_FOR_EACH(binding, ctx->ovnsb_idl) {
664 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
665 hash_string(binding->logical_port, 0), &lports_hmap) {
666 if (!strcmp(binding->logical_port, hash_node->lport->name)) {
667 lport = hash_node->lport;
673 /* The logical port doesn't exist for this port binding. This can
674 * happen under normal circumstances when ovn-northd hasn't gotten
675 * around to pruning the Port_Binding yet. */
679 if (binding->chassis && (!lport->up || !*lport->up)) {
681 nbrec_logical_port_set_up(lport, &up, 1);
682 } else if (!binding->chassis && (!lport->up || *lport->up)) {
684 nbrec_logical_port_set_up(lport, &up, 1);
688 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
689 hmap_remove(&lports_hmap, &hash_node->node);
692 hmap_destroy(&lports_hmap);
700 def = xasprintf("unix:%s/db.sock", ovs_rundir());
706 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
712 static const struct option long_options[] = {
713 {"ovnsb-db", required_argument, NULL, 'd'},
714 {"ovnnb-db", required_argument, NULL, 'D'},
715 {"help", no_argument, NULL, 'h'},
716 {"options", no_argument, NULL, 'o'},
717 {"version", no_argument, NULL, 'V'},
720 STREAM_SSL_LONG_OPTIONS,
723 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
728 c = getopt_long(argc, argv, short_options, long_options, NULL);
734 DAEMON_OPTION_HANDLERS;
735 VLOG_OPTION_HANDLERS;
736 STREAM_SSL_OPTION_HANDLERS;
751 ovs_cmdl_print_options(long_options);
755 ovs_print_version(0, 0);
764 ovnsb_db = default_db();
768 ovnnb_db = default_db();
775 main(int argc, char *argv[])
777 extern struct vlog_module VLM_reconnect;
778 struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
779 unsigned int ovnnb_seqno, ovn_seqno;
780 int res = EXIT_SUCCESS;
781 struct northd_context ctx = {
784 bool ovnnb_changes_pending = false;
785 bool ovn_changes_pending = false;
786 struct unixctl_server *unixctl;
790 fatal_ignore_sigpipe();
791 set_program_name(argv[0]);
792 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
793 vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
794 parse_options(argc, argv);
798 retval = unixctl_server_create(NULL, &unixctl);
802 unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting);
804 daemonize_complete();
809 /* We want to detect all changes to the ovn-nb db. */
810 ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
811 &nbrec_idl_class, true, true);
813 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
814 * has to care about, so we'll enable monitoring those directly. */
815 ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
816 &sbrec_idl_class, false, true);
817 ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_port_binding);
818 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_logical_port);
819 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_chassis);
820 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_mac);
821 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_tag);
822 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_parent_port);
823 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_logical_datapath);
824 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_tunnel_key);
825 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_type);
826 ovsdb_idl_add_column(ovnsb_idl, &sbrec_port_binding_col_options);
827 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
828 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
829 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id);
830 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_table_id);
831 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_priority);
832 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_priority);
833 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_match);
834 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_match);
835 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_actions);
836 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_actions);
839 * The loop here just runs the IDL in a loop waiting for the seqno to
840 * change, which indicates that the contents of the db have changed.
842 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
843 * db must be recalculated.
845 * If the contents of the ovn-sb db change, it means the 'up' state of
846 * a port may have changed, as that's the only type of change ovn-northd is
850 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
851 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
854 ovsdb_idl_run(ovnnb_idl);
855 ovsdb_idl_run(ovnsb_idl);
856 unixctl_server_run(unixctl);
858 if (!ovsdb_idl_is_alive(ovnnb_idl)) {
859 int retval = ovsdb_idl_get_last_error(ovnnb_idl);
860 VLOG_ERR("%s: database connection failed (%s)",
861 ovnnb_db, ovs_retval_to_string(retval));
866 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
867 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
868 VLOG_ERR("%s: database connection failed (%s)",
869 ovnsb_db, ovs_retval_to_string(retval));
874 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
875 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
876 ovnnb_changes_pending = true;
879 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
880 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
881 ovn_changes_pending = true;
885 * If there are any pending changes, we delay recalculating the
886 * necessary updates until after an existing transaction finishes.
887 * This avoids the possibility of rapid updates causing ovn-northd to
888 * never be able to successfully make the corresponding updates to the
889 * other db. Instead, pending changes are batched up until the next
890 * time we get a chance to calculate the new state and apply it.
893 if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
895 * The OVN-nb db contents have changed, so create a transaction for
896 * updating the OVN-sb DB.
898 ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
899 ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
900 "ovn-northd: northbound db changed");
901 ovnnb_db_changed(&ctx);
902 ovnnb_changes_pending = false;
905 if (ovn_changes_pending && !ctx.ovnnb_txn) {
907 * The OVN-sb db contents have changed, so create a transaction for
908 * updating the northbound DB.
910 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
911 ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
912 "ovn-northd: southbound db changed");
913 ovnsb_db_changed(&ctx);
914 ovn_changes_pending = false;
918 enum ovsdb_idl_txn_status txn_status;
919 txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
920 switch (txn_status) {
921 case TXN_UNCOMMITTED:
923 /* Come back around and try to commit this transaction again */
929 /* Something went wrong, so try creating a new transaction. */
930 ovn_changes_pending = true;
933 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
934 ctx.ovnnb_txn = NULL;
939 enum ovsdb_idl_txn_status txn_status;
940 txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
941 switch (txn_status) {
942 case TXN_UNCOMMITTED:
944 /* Come back around and try to commit this transaction again */
950 /* Something went wrong, so try creating a new transaction. */
951 ovnnb_changes_pending = true;
954 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
955 ctx.ovnsb_txn = NULL;
959 if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
960 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
961 ovsdb_idl_wait(ovnnb_idl);
962 ovsdb_idl_wait(ovnsb_idl);
964 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
967 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
969 unixctl_server_wait(unixctl);
971 poll_immediate_wake();
977 unixctl_server_destroy(unixctl);
978 ovsdb_idl_destroy(ovnsb_idl);
979 ovsdb_idl_destroy(ovnnb_idl);
985 ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
986 const char *argv[] OVS_UNUSED, void *exiting_)
988 bool *exiting = exiting_;
991 unixctl_command_reply(conn, NULL);