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/ovn-nb-idl.h"
31 #include "ovn/ovn-sb-idl.h"
32 #include "poll-loop.h"
34 #include "stream-ssl.h"
37 #include "openvswitch/vlog.h"
39 VLOG_DEFINE_THIS_MODULE(ovn_northd);
41 struct northd_context {
42 struct ovsdb_idl *ovnnb_idl;
43 struct ovsdb_idl *ovnsb_idl;
44 struct ovsdb_idl_txn *ovnnb_txn;
45 struct ovsdb_idl_txn *ovnsb_txn;
48 static const char *ovnnb_db;
49 static const char *ovnsb_db;
51 static const char *default_db(void);
57 %s: OVN northbound management daemon\n\
58 usage: %s [OPTIONS]\n\
61 --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
63 --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
65 -h, --help display this help message\n\
66 -o, --options list available options\n\
67 -V, --version display version information\n\
68 ", program_name, program_name, default_db(), default_db());
71 stream_usage("database", true, true, false);
75 compare_strings(const void *a_, const void *b_)
79 return strcmp(*a, *b);
83 * Determine whether 2 arrays of MAC addresses are the same. It's possible that
84 * the lists could be *very* long and this check is being done a lot (every
85 * time the OVN_Northbound database changes).
88 macs_equal(char **binding_macs_, size_t b_n_macs,
89 char **lport_macs_, size_t l_n_macs)
91 char **binding_macs, **lport_macs;
94 if (b_n_macs != l_n_macs) {
98 bytes = b_n_macs * sizeof binding_macs_[0];
99 binding_macs = xmalloc(bytes);
100 lport_macs = xmalloc(bytes);
102 memcpy(binding_macs, binding_macs_, bytes);
103 memcpy(lport_macs, lport_macs_, bytes);
105 qsort(binding_macs, b_n_macs, sizeof binding_macs[0], compare_strings);
106 qsort(lport_macs, l_n_macs, sizeof lport_macs[0], compare_strings);
108 for (i = 0; i < b_n_macs; i++) {
109 if (strcmp(binding_macs[i], lport_macs[i])) {
117 return (i == b_n_macs) ? true : false;
120 /* Pipeline generation.
122 * This code generates the Pipeline table in the southbound database, as a
123 * function of most of the northbound database.
126 /* Enough context to add a Pipeline row, using pipeline_add(). */
127 struct pipeline_ctx {
128 /* From northd_context. */
129 struct ovsdb_idl *ovnsb_idl;
130 struct ovsdb_idl_txn *ovnsb_txn;
132 /* Contains "struct pipeline_hash_node"s. Used to figure out what existing
133 * Pipeline rows should be deleted: we index all of the Pipeline rows into
134 * this data structure, then as existing rows are generated we remove them.
135 * After generating all the rows, any remaining in 'pipeline_hmap' must be
136 * deleted from the database. */
137 struct hmap pipeline_hmap;
140 /* A row in the Pipeline table, indexed by its full contents, */
141 struct pipeline_hash_node {
142 struct hmap_node node;
143 const struct sbrec_pipeline *pipeline;
147 pipeline_hash(const struct uuid *logical_datapath, uint8_t table_id,
148 uint16_t priority, const char *match, const char *actions)
150 size_t hash = uuid_hash(logical_datapath);
151 hash = hash_2words((table_id << 16) | priority, hash);
152 hash = hash_string(match, hash);
153 return hash_string(actions, hash);
157 pipeline_hash_rec(const struct sbrec_pipeline *pipeline)
159 return pipeline_hash(&pipeline->logical_datapath, pipeline->table_id,
160 pipeline->priority, pipeline->match,
164 /* Adds a row with the specified contents to the Pipeline table. */
166 pipeline_add(struct pipeline_ctx *ctx,
167 const struct nbrec_logical_switch *logical_datapath,
173 struct pipeline_hash_node *hash_node;
175 /* Check whether such a row already exists in the Pipeline table. If so,
176 * remove it from 'ctx->pipeline_hmap' and we're done. */
177 HMAP_FOR_EACH_WITH_HASH (hash_node, node,
178 pipeline_hash(&logical_datapath->header_.uuid,
179 table_id, priority, match, actions),
180 &ctx->pipeline_hmap) {
181 const struct sbrec_pipeline *pipeline = hash_node->pipeline;
182 if (uuid_equals(&pipeline->logical_datapath,
183 &logical_datapath->header_.uuid)
184 && pipeline->table_id == table_id
185 && pipeline->priority == priority
186 && !strcmp(pipeline->match, match)
187 && !strcmp(pipeline->actions, actions)) {
188 hmap_remove(&ctx->pipeline_hmap, &hash_node->node);
194 /* No such Pipeline row. Add one. */
195 const struct sbrec_pipeline *pipeline;
196 pipeline = sbrec_pipeline_insert(ctx->ovnsb_txn);
197 sbrec_pipeline_set_logical_datapath(pipeline,
198 logical_datapath->header_.uuid);
199 sbrec_pipeline_set_table_id(pipeline, table_id);
200 sbrec_pipeline_set_priority(pipeline, priority);
201 sbrec_pipeline_set_match(pipeline, match);
202 sbrec_pipeline_set_actions(pipeline, actions);
205 /* A single port security constraint. This is a parsed version of a single
206 * member of the port_security column in the OVN_NB Logical_Port table.
208 * Each token has type LEX_T_END if that field is missing, otherwise
209 * LEX_T_INTEGER or LEX_T_MASKED_INTEGER. */
210 struct ps_constraint {
211 struct lex_token eth;
212 struct lex_token ip4;
213 struct lex_token ip6;
216 /* Parses a member of the port_security column 'ps' into 'c'. Returns true if
217 * successful, false on syntax error. */
219 parse_port_security(const char *ps, struct ps_constraint *c)
221 c->eth.type = LEX_T_END;
222 c->ip4.type = LEX_T_END;
223 c->ip6.type = LEX_T_END;
226 lexer_init(&lexer, ps);
228 if (lexer.token.type == LEX_T_INTEGER ||
229 lexer.token.type == LEX_T_MASKED_INTEGER) {
232 t = (lexer.token.format == LEX_F_IPV4 ? &c->ip4
233 : lexer.token.format == LEX_F_IPV6 ? &c->ip6
234 : lexer.token.format == LEX_F_ETHERNET ? &c->eth
237 if (t->type == LEX_T_END) {
240 VLOG_INFO("%s: port_security has duplicate %s address",
241 ps, lex_format_to_string(lexer.token.format));
244 lexer_match(&lexer, LEX_T_COMMA);
249 VLOG_INFO("%s: syntax error in port_security", ps);
250 lexer_destroy(&lexer);
252 } while (lexer.token.type != LEX_T_END);
253 lexer_destroy(&lexer);
258 /* Appends port security constraints on L2 address field 'eth_addr_field'
259 * (e.g. "eth.src" or "eth.dst") to 'match'. 'port_security', with
260 * 'n_port_security' elements, is the collection of port_security constraints
261 * from an OVN_NB Logical_Port row.
263 * (This is naive; it's not yet possible to express complete L2 and L3 port
264 * security constraints as a single Boolean expression.) */
266 build_port_security(const char *eth_addr_field,
267 char **port_security, size_t n_port_security,
270 size_t base_len = match->length;
271 ds_put_format(match, " && %s == {", eth_addr_field);
274 for (size_t i = 0; i < n_port_security; i++) {
275 struct ps_constraint c;
276 if (parse_port_security(port_security[i], &c)
277 && c.eth.type != LEX_T_END) {
278 lex_token_format(&c.eth, match);
279 ds_put_char(match, ' ');
283 ds_put_cstr(match, "}");
286 match->length = base_len;
290 /* Updates the Pipeline table in the OVN_SB database, constructing its contents
291 * based on the OVN_NB database. */
293 build_pipeline(struct northd_context *ctx)
295 struct pipeline_ctx pc = {
296 .ovnsb_idl = ctx->ovnsb_idl,
297 .ovnsb_txn = ctx->ovnsb_txn,
298 .pipeline_hmap = HMAP_INITIALIZER(&pc.pipeline_hmap)
301 /* Add all the Pipeline entries currently in the southbound database to
302 * 'pc.pipeline_hmap'. We remove entries that we generate from the hmap,
303 * thus by the time we're done only entries that need to be removed
305 const struct sbrec_pipeline *pipeline;
306 SBREC_PIPELINE_FOR_EACH (pipeline, ctx->ovnsb_idl) {
307 struct pipeline_hash_node *hash_node = xzalloc(sizeof *hash_node);
308 hash_node->pipeline = pipeline;
309 hmap_insert(&pc.pipeline_hmap, &hash_node->node,
310 pipeline_hash_rec(pipeline));
313 /* Table 0: Admission control framework. */
314 const struct nbrec_logical_switch *lswitch;
315 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
316 /* Logical VLANs not supported. */
317 pipeline_add(&pc, lswitch, 0, 100, "vlan.present", "drop");
319 /* Broadcast/multicast source address is invalid. */
320 pipeline_add(&pc, lswitch, 0, 100, "eth.src[40]", "drop");
322 /* Port security flows have priority 50 (see below) and will resubmit
323 * if packet source is acceptable. */
325 /* Otherwise drop the packet. */
326 pipeline_add(&pc, lswitch, 0, 0, "1", "drop");
329 /* Table 0: Ingress port security. */
330 const struct nbrec_logical_port *lport;
331 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
332 struct ds match = DS_EMPTY_INITIALIZER;
333 ds_put_cstr(&match, "inport == ");
334 json_string_escape(lport->name, &match);
335 build_port_security("eth.src",
336 lport->port_security, lport->n_port_security,
338 pipeline_add(&pc, lport->lswitch, 0, 50, ds_cstr(&match), "resubmit");
342 /* Table 1: Destination lookup, broadcast and multicast handling (priority
344 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
348 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
349 if (lport->lswitch == lswitch) {
350 ds_put_cstr(&actions, "outport = ");
351 json_string_escape(lport->name, &actions);
352 ds_put_cstr(&actions, "; resubmit; ");
355 ds_chomp(&actions, ' ');
357 pipeline_add(&pc, lswitch, 1, 100, "eth.dst[40]", ds_cstr(&actions));
358 ds_destroy(&actions);
361 /* Table 1: Destination lookup, unicast handling (priority 50), */
362 struct ds unknown_actions = DS_EMPTY_INITIALIZER;
363 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
364 for (size_t i = 0; i < lport->n_macs; i++) {
365 uint8_t mac[ETH_ADDR_LEN];
367 if (eth_addr_from_string(lport->macs[i], mac)) {
368 struct ds match, actions;
371 ds_put_format(&match, "eth.dst == %s", lport->macs[i]);
374 ds_put_cstr(&actions, "outport = ");
375 json_string_escape(lport->name, &actions);
376 ds_put_cstr(&actions, "; resubmit;");
377 pipeline_add(&pc, lport->lswitch, 1, 50,
378 ds_cstr(&match), ds_cstr(&actions));
379 ds_destroy(&actions);
381 } else if (!strcmp(lport->macs[i], "unknown")) {
382 ds_put_cstr(&unknown_actions, "outport = ");
383 json_string_escape(lport->name, &unknown_actions);
384 ds_put_cstr(&unknown_actions, "; resubmit; ");
386 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
388 VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in macs column",
389 lport->name, lport->macs[i]);
394 /* Table 1: Destination lookup for unknown MACs (priority 0). */
395 if (unknown_actions.length) {
396 ds_chomp(&unknown_actions, ' ');
397 pipeline_add(&pc, lport->lswitch, 1, 0, "1",
398 ds_cstr(&unknown_actions));
400 ds_destroy(&unknown_actions);
403 const struct nbrec_acl *acl;
404 NBREC_ACL_FOR_EACH (acl, ctx->ovnnb_idl) {
407 action = (!strcmp(acl->action, "allow") ||
408 !strcmp(acl->action, "allow-related")) ? "resubmit" : "drop";
409 pipeline_add(&pc, acl->lswitch, 2, acl->priority, acl->match, action);
411 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->ovnnb_idl) {
412 pipeline_add(&pc, lswitch, 2, 0, "1", "resubmit");
415 /* Table 3: Egress port security. */
416 NBREC_LOGICAL_PORT_FOR_EACH (lport, ctx->ovnnb_idl) {
417 struct ds match, actions;
420 ds_put_cstr(&match, "outport == ");
421 json_string_escape(lport->name, &match);
422 build_port_security("eth.dst",
423 lport->port_security, lport->n_port_security,
427 ds_put_cstr(&actions, "output(");
428 json_string_escape(lport->name, &actions);
429 ds_put_char(&actions, ')');
431 pipeline_add(&pc, lport->lswitch, 3, 50,
432 ds_cstr(&match), ds_cstr(&actions));
434 ds_destroy(&actions);
438 /* Delete any existing Pipeline rows that were not re-generated. */
439 struct pipeline_hash_node *hash_node, *next_hash_node;
440 HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &pc.pipeline_hmap) {
441 hmap_remove(&pc.pipeline_hmap, &hash_node->node);
442 sbrec_pipeline_delete(hash_node->pipeline);
445 hmap_destroy(&pc.pipeline_hmap);
449 parents_equal(const struct sbrec_bindings *binding,
450 const struct nbrec_logical_port *lport)
452 if (!!binding->parent_port != !!lport->parent_name) {
453 /* One is set and the other is not. */
457 if (binding->parent_port) {
459 return strcmp(binding->parent_port, lport->parent_name) ? false : true;
467 tags_equal(const struct sbrec_bindings *binding,
468 const struct nbrec_logical_port *lport)
470 if (binding->n_tag != lport->n_tag) {
474 return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
478 * When a change has occurred in the OVN_Northbound database, we go through and
479 * make sure that the contents of the Bindings table in the OVN_Southbound
480 * database are up to date with the logical ports defined in the
481 * OVN_Northbound database.
484 set_bindings(struct northd_context *ctx)
486 struct hmap bindings_hmap;
487 const struct sbrec_bindings *binding;
488 const struct nbrec_logical_port *lport;
490 struct binding_hash_node {
491 struct hmap_node node;
492 const struct sbrec_bindings *binding;
493 } *hash_node, *hash_node_next;
496 * We will need to look up a binding for every logical port. We don't want
497 * to have to do an O(n) search for every binding, so start out by hashing
498 * 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 it
502 * from the hashmap. At the end, any bindings left in the hashmap are for
503 * logical ports that have been deleted.
505 hmap_init(&bindings_hmap);
507 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
508 hash_node = xzalloc(sizeof *hash_node);
509 hash_node->binding = binding;
510 hmap_insert(&bindings_hmap, &hash_node->node,
511 hash_string(binding->logical_port, 0));
514 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
516 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
517 hash_string(lport->name, 0), &bindings_hmap) {
518 if (!strcmp(lport->name, hash_node->binding->logical_port)) {
519 binding = hash_node->binding;
525 /* We found an existing binding for this logical port. Update its
528 hmap_remove(&bindings_hmap, &hash_node->node);
532 if (!macs_equal(binding->mac, binding->n_mac,
533 lport->macs, lport->n_macs)) {
534 sbrec_bindings_set_mac(binding,
535 (const char **) lport->macs, lport->n_macs);
537 if (!parents_equal(binding, lport)) {
538 sbrec_bindings_set_parent_port(binding, lport->parent_name);
540 if (!tags_equal(binding, lport)) {
541 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
544 /* There is no binding for this logical port, so create one. */
546 binding = sbrec_bindings_insert(ctx->ovnsb_txn);
547 sbrec_bindings_set_logical_port(binding, lport->name);
548 sbrec_bindings_set_mac(binding,
549 (const char **) lport->macs, lport->n_macs);
550 if (lport->parent_name && lport->n_tag > 0) {
551 sbrec_bindings_set_parent_port(binding, lport->parent_name);
552 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
557 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
558 hmap_remove(&bindings_hmap, &hash_node->node);
559 sbrec_bindings_delete(hash_node->binding);
562 hmap_destroy(&bindings_hmap);
566 ovnnb_db_changed(struct northd_context *ctx)
568 VLOG_DBG("ovn-nb db contents have changed.");
575 * The only change we get notified about is if the 'chassis' column of the
576 * 'Bindings' table changes. When this column is not empty, it means we need to
577 * set the corresponding logical port as 'up' in the northbound DB.
580 ovnsb_db_changed(struct northd_context *ctx)
582 struct hmap lports_hmap;
583 const struct sbrec_bindings *binding;
584 const struct nbrec_logical_port *lport;
586 struct lport_hash_node {
587 struct hmap_node node;
588 const struct nbrec_logical_port *lport;
589 } *hash_node, *hash_node_next;
591 VLOG_DBG("Recalculating port up states for ovn-nb db.");
593 hmap_init(&lports_hmap);
595 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
596 hash_node = xzalloc(sizeof *hash_node);
597 hash_node->lport = lport;
598 hmap_insert(&lports_hmap, &hash_node->node,
599 hash_string(lport->name, 0));
602 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
604 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
605 hash_string(binding->logical_port, 0), &lports_hmap) {
606 if (!strcmp(binding->logical_port, hash_node->lport->name)) {
607 lport = hash_node->lport;
613 /* The logical port doesn't exist for this binding. This can
614 * happen under normal circumstances when ovn-northd hasn't gotten
615 * around to pruning the Binding yet. */
619 if (*binding->chassis && (!lport->up || !*lport->up)) {
621 nbrec_logical_port_set_up(lport, &up, 1);
622 } else if (!*binding->chassis && (!lport->up || *lport->up)) {
624 nbrec_logical_port_set_up(lport, &up, 1);
628 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
629 hmap_remove(&lports_hmap, &hash_node->node);
632 hmap_destroy(&lports_hmap);
640 def = xasprintf("unix:%s/db.sock", ovs_rundir());
646 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
652 static const struct option long_options[] = {
653 {"ovnsb-db", required_argument, NULL, 'd'},
654 {"ovnnb-db", required_argument, NULL, 'D'},
655 {"help", no_argument, NULL, 'h'},
656 {"options", no_argument, NULL, 'o'},
657 {"version", no_argument, NULL, 'V'},
660 STREAM_SSL_LONG_OPTIONS,
663 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
668 c = getopt_long(argc, argv, short_options, long_options, NULL);
674 DAEMON_OPTION_HANDLERS;
675 VLOG_OPTION_HANDLERS;
676 STREAM_SSL_OPTION_HANDLERS;
691 ovs_cmdl_print_options(long_options);
695 ovs_print_version(0, 0);
704 ovnsb_db = default_db();
708 ovnnb_db = default_db();
715 main(int argc, char *argv[])
717 extern struct vlog_module VLM_reconnect;
718 struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
719 unsigned int ovnnb_seqno, ovn_seqno;
720 int res = EXIT_SUCCESS;
721 struct northd_context ctx = {
724 bool ovnnb_changes_pending = false;
725 bool ovn_changes_pending = false;
727 fatal_ignore_sigpipe();
728 set_program_name(argv[0]);
729 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
730 vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
731 parse_options(argc, argv);
738 /* We want to detect all changes to the ovn-nb db. */
739 ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
740 &nbrec_idl_class, true, true);
742 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
743 * has to care about, so we'll enable monitoring those directly. */
744 ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
745 &sbrec_idl_class, false, true);
746 ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_bindings);
747 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_port);
748 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_chassis);
749 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_mac);
750 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tag);
751 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_parent_port);
752 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
753 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_logical_datapath);
754 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_table_id);
755 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_table_id);
756 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_priority);
757 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_priority);
758 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_match);
759 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_match);
760 ovsdb_idl_add_column(ovnsb_idl, &sbrec_pipeline_col_actions);
761 ovsdb_idl_omit_alert(ovnsb_idl, &sbrec_pipeline_col_actions);
764 * The loop here just runs the IDL in a loop waiting for the seqno to
765 * change, which indicates that the contents of the db have changed.
767 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
768 * db must be recalculated.
770 * If the contents of the ovn-sb db change, it means the 'up' state of
771 * a port may have changed, as that's the only type of change ovn-northd is
775 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
776 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
778 ovsdb_idl_run(ovnnb_idl);
779 ovsdb_idl_run(ovnsb_idl);
781 if (!ovsdb_idl_is_alive(ovnnb_idl)) {
782 int retval = ovsdb_idl_get_last_error(ovnnb_idl);
783 VLOG_ERR("%s: database connection failed (%s)",
784 ovnnb_db, ovs_retval_to_string(retval));
789 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
790 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
791 VLOG_ERR("%s: database connection failed (%s)",
792 ovnsb_db, ovs_retval_to_string(retval));
797 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
798 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
799 ovnnb_changes_pending = true;
802 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
803 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
804 ovn_changes_pending = true;
808 * If there are any pending changes, we delay recalculating the
809 * necessary updates until after an existing transaction finishes.
810 * This avoids the possibility of rapid updates causing ovn-northd to
811 * never be able to successfully make the corresponding updates to the
812 * other db. Instead, pending changes are batched up until the next
813 * time we get a chance to calculate the new state and apply it.
816 if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
818 * The OVN-nb db contents have changed, so create a transaction for
819 * updating the OVN-sb DB.
821 ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
822 ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
823 "ovn-northd: northbound db changed");
824 ovnnb_db_changed(&ctx);
825 ovnnb_changes_pending = false;
828 if (ovn_changes_pending && !ctx.ovnnb_txn) {
830 * The OVN-sb db contents have changed, so create a transaction for
831 * updating the northbound DB.
833 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
834 ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
835 "ovn-northd: southbound db changed");
836 ovnsb_db_changed(&ctx);
837 ovn_changes_pending = false;
841 enum ovsdb_idl_txn_status txn_status;
842 txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
843 switch (txn_status) {
844 case TXN_UNCOMMITTED:
846 /* Come back around and try to commit this transaction again */
852 /* Something went wrong, so try creating a new transaction. */
853 ovn_changes_pending = true;
856 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
857 ctx.ovnnb_txn = NULL;
862 enum ovsdb_idl_txn_status txn_status;
863 txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
864 switch (txn_status) {
865 case TXN_UNCOMMITTED:
867 /* Come back around and try to commit this transaction again */
873 /* Something went wrong, so try creating a new transaction. */
874 ovnnb_changes_pending = true;
877 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
878 ctx.ovnsb_txn = NULL;
882 if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
883 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
884 ovsdb_idl_wait(ovnnb_idl);
885 ovsdb_idl_wait(ovnsb_idl);
887 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
890 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
896 ovsdb_idl_destroy(ovnsb_idl);
897 ovsdb_idl_destroy(ovnnb_idl);