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"
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 /* Appends port security constraints on L2 address field 'eth_addr_field'
206 * (e.g. "eth.src" or "eth.dst") to 'match'. 'port_security', with
207 * 'n_port_security' elements, is the collection of port_security constraints
208 * from an OVN_NB Logical_Port row. */
210 build_port_security(const char *eth_addr_field
,
211 char **port_security
, size_t n_port_security
,
214 size_t base_len
= match
->length
;
215 ds_put_format(match
, " && %s == {", eth_addr_field
);
218 for (size_t i
= 0; i
< n_port_security
; i
++) {
219 uint8_t ea
[ETH_ADDR_LEN
];
221 if (eth_addr_from_string(port_security
[i
], ea
)) {
222 ds_put_format(match
, ETH_ADDR_FMT
, ETH_ADDR_ARGS(ea
));
223 ds_put_char(match
, ' ');
227 ds_chomp(match
, ' ');
228 ds_put_cstr(match
, "}");
231 match
->length
= base_len
;
235 /* Updates the Pipeline table in the OVN_SB database, constructing its contents
236 * based on the OVN_NB database. */
238 build_pipeline(struct northd_context
*ctx
)
240 struct pipeline_ctx pc
= {
241 .ovnsb_idl
= ctx
->ovnsb_idl
,
242 .ovnsb_txn
= ctx
->ovnsb_txn
,
243 .pipeline_hmap
= HMAP_INITIALIZER(&pc
.pipeline_hmap
)
246 /* Add all the Pipeline entries currently in the southbound database to
247 * 'pc.pipeline_hmap'. We remove entries that we generate from the hmap,
248 * thus by the time we're done only entries that need to be removed
250 const struct sbrec_pipeline
*pipeline
;
251 SBREC_PIPELINE_FOR_EACH (pipeline
, ctx
->ovnsb_idl
) {
252 struct pipeline_hash_node
*hash_node
= xzalloc(sizeof *hash_node
);
253 hash_node
->pipeline
= pipeline
;
254 hmap_insert(&pc
.pipeline_hmap
, &hash_node
->node
,
255 pipeline_hash_rec(pipeline
));
258 /* Table 0: Admission control framework. */
259 const struct nbrec_logical_switch
*lswitch
;
260 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch
, ctx
->ovnnb_idl
) {
261 /* Logical VLANs not supported. */
262 pipeline_add(&pc
, lswitch
, 0, 100, "vlan.present", "drop;");
264 /* Broadcast/multicast source address is invalid. */
265 pipeline_add(&pc
, lswitch
, 0, 100, "eth.src[40]", "drop;");
267 /* Port security flows have priority 50 (see below) and will continue
268 * to the next table if packet source is acceptable. */
270 /* Otherwise drop the packet. */
271 pipeline_add(&pc
, lswitch
, 0, 0, "1", "drop;");
274 /* Table 0: Ingress port security. */
275 const struct nbrec_logical_port
*lport
;
276 NBREC_LOGICAL_PORT_FOR_EACH (lport
, ctx
->ovnnb_idl
) {
277 struct ds match
= DS_EMPTY_INITIALIZER
;
278 ds_put_cstr(&match
, "inport == ");
279 json_string_escape(lport
->name
, &match
);
280 build_port_security("eth.src",
281 lport
->port_security
, lport
->n_port_security
,
283 pipeline_add(&pc
, lport
->lswitch
, 0, 50, ds_cstr(&match
), "next;");
287 /* Table 1: Destination lookup, broadcast and multicast handling (priority
289 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch
, ctx
->ovnnb_idl
) {
293 NBREC_LOGICAL_PORT_FOR_EACH (lport
, ctx
->ovnnb_idl
) {
294 if (lport
->lswitch
== lswitch
) {
295 ds_put_cstr(&actions
, "outport = ");
296 json_string_escape(lport
->name
, &actions
);
297 ds_put_cstr(&actions
, "; next; ");
300 ds_chomp(&actions
, ' ');
302 pipeline_add(&pc
, lswitch
, 1, 100, "eth.dst[40]", ds_cstr(&actions
));
303 ds_destroy(&actions
);
306 /* Table 1: Destination lookup, unicast handling (priority 50), */
307 struct unknown_actions
{
308 struct hmap_node hmap_node
;
309 const struct nbrec_logical_switch
*ls
;
312 struct hmap unknown_actions
= HMAP_INITIALIZER(&unknown_actions
);
313 NBREC_LOGICAL_PORT_FOR_EACH (lport
, ctx
->ovnnb_idl
) {
314 lswitch
= lport
->lswitch
;
315 for (size_t i
= 0; i
< lport
->n_macs
; i
++) {
316 uint8_t mac
[ETH_ADDR_LEN
];
318 if (eth_addr_from_string(lport
->macs
[i
], mac
)) {
319 struct ds match
, actions
;
322 ds_put_format(&match
, "eth.dst == %s", lport
->macs
[i
]);
325 ds_put_cstr(&actions
, "outport = ");
326 json_string_escape(lport
->name
, &actions
);
327 ds_put_cstr(&actions
, "; next;");
328 pipeline_add(&pc
, lswitch
, 1, 50,
329 ds_cstr(&match
), ds_cstr(&actions
));
330 ds_destroy(&actions
);
332 } else if (!strcmp(lport
->macs
[i
], "unknown")) {
333 const struct uuid
*uuid
= &lswitch
->header_
.uuid
;
334 struct unknown_actions
*ua
= NULL
;
335 struct unknown_actions
*iter
;
336 HMAP_FOR_EACH_WITH_HASH (iter
, hmap_node
, uuid_hash(uuid
),
338 if (uuid_equals(&iter
->ls
->header_
.uuid
, uuid
)) {
344 ua
= xmalloc(sizeof *ua
);
345 hmap_insert(&unknown_actions
, &ua
->hmap_node
,
348 ds_init(&ua
->actions
);
350 ds_put_char(&ua
->actions
, ' ');
353 ds_put_cstr(&ua
->actions
, "outport = ");
354 json_string_escape(lport
->name
, &ua
->actions
);
355 ds_put_cstr(&ua
->actions
, "; next;");
357 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 1);
359 VLOG_INFO_RL(&rl
, "%s: invalid syntax '%s' in macs column",
360 lport
->name
, lport
->macs
[i
]);
365 /* Table 1: Destination lookup for unknown MACs (priority 0). */
366 struct unknown_actions
*ua
, *next_ua
;
367 HMAP_FOR_EACH_SAFE (ua
, next_ua
, hmap_node
, &unknown_actions
) {
368 pipeline_add(&pc
, ua
->ls
, 1, 0, "1", ds_cstr(&ua
->actions
));
369 hmap_remove(&unknown_actions
, &ua
->hmap_node
);
370 ds_destroy(&ua
->actions
);
373 hmap_destroy(&unknown_actions
);
376 const struct nbrec_acl
*acl
;
377 NBREC_ACL_FOR_EACH (acl
, ctx
->ovnnb_idl
) {
380 action
= (!strcmp(acl
->action
, "allow") ||
381 !strcmp(acl
->action
, "allow-related"))
383 pipeline_add(&pc
, acl
->lswitch
, 2, acl
->priority
, acl
->match
, action
);
385 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch
, ctx
->ovnnb_idl
) {
386 pipeline_add(&pc
, lswitch
, 2, 0, "1", "next;");
389 /* Table 3: Egress port security. */
390 NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch
, ctx
->ovnnb_idl
) {
391 pipeline_add(&pc
, lswitch
, 3, 100, "eth.dst[40]", "output;");
393 NBREC_LOGICAL_PORT_FOR_EACH (lport
, ctx
->ovnnb_idl
) {
397 ds_put_cstr(&match
, "outport == ");
398 json_string_escape(lport
->name
, &match
);
399 build_port_security("eth.dst",
400 lport
->port_security
, lport
->n_port_security
,
403 pipeline_add(&pc
, lport
->lswitch
, 3, 50, ds_cstr(&match
), "output;");
408 /* Delete any existing Pipeline rows that were not re-generated. */
409 struct pipeline_hash_node
*hash_node
, *next_hash_node
;
410 HMAP_FOR_EACH_SAFE (hash_node
, next_hash_node
, node
, &pc
.pipeline_hmap
) {
411 hmap_remove(&pc
.pipeline_hmap
, &hash_node
->node
);
412 sbrec_pipeline_delete(hash_node
->pipeline
);
415 hmap_destroy(&pc
.pipeline_hmap
);
419 parents_equal(const struct sbrec_bindings
*binding
,
420 const struct nbrec_logical_port
*lport
)
422 if (!!binding
->parent_port
!= !!lport
->parent_name
) {
423 /* One is set and the other is not. */
427 if (binding
->parent_port
) {
429 return strcmp(binding
->parent_port
, lport
->parent_name
) ? false : true;
437 tags_equal(const struct sbrec_bindings
*binding
,
438 const struct nbrec_logical_port
*lport
)
440 if (binding
->n_tag
!= lport
->n_tag
) {
444 return binding
->n_tag
? (binding
->tag
[0] == lport
->tag
[0]) : true;
448 * When a change has occurred in the OVN_Northbound database, we go through and
449 * make sure that the contents of the Bindings table in the OVN_Southbound
450 * database are up to date with the logical ports defined in the
451 * OVN_Northbound database.
454 set_bindings(struct northd_context
*ctx
)
456 struct hmap bindings_hmap
;
457 const struct sbrec_bindings
*binding
;
458 const struct nbrec_logical_port
*lport
;
460 struct binding_hash_node
{
461 struct hmap_node node
;
462 const struct sbrec_bindings
*binding
;
463 } *hash_node
, *hash_node_next
;
466 * We will need to look up a binding for every logical port. We don't want
467 * to have to do an O(n) search for every binding, so start out by hashing
468 * them on the logical port.
470 * As we go through every logical port, we will update the binding if it
471 * exists or create one otherwise. When the update is done, we'll remove it
472 * from the hashmap. At the end, any bindings left in the hashmap are for
473 * logical ports that have been deleted.
475 hmap_init(&bindings_hmap
);
477 SBREC_BINDINGS_FOR_EACH(binding
, ctx
->ovnsb_idl
) {
478 hash_node
= xzalloc(sizeof *hash_node
);
479 hash_node
->binding
= binding
;
480 hmap_insert(&bindings_hmap
, &hash_node
->node
,
481 hash_string(binding
->logical_port
, 0));
484 NBREC_LOGICAL_PORT_FOR_EACH(lport
, ctx
->ovnnb_idl
) {
486 HMAP_FOR_EACH_WITH_HASH(hash_node
, node
,
487 hash_string(lport
->name
, 0), &bindings_hmap
) {
488 if (!strcmp(lport
->name
, hash_node
->binding
->logical_port
)) {
489 binding
= hash_node
->binding
;
495 /* We found an existing binding for this logical port. Update its
498 hmap_remove(&bindings_hmap
, &hash_node
->node
);
502 if (!macs_equal(binding
->mac
, binding
->n_mac
,
503 lport
->macs
, lport
->n_macs
)) {
504 sbrec_bindings_set_mac(binding
,
505 (const char **) lport
->macs
, lport
->n_macs
);
507 if (!parents_equal(binding
, lport
)) {
508 sbrec_bindings_set_parent_port(binding
, lport
->parent_name
);
510 if (!tags_equal(binding
, lport
)) {
511 sbrec_bindings_set_tag(binding
, lport
->tag
, lport
->n_tag
);
514 /* There is no binding for this logical port, so create one. */
516 binding
= sbrec_bindings_insert(ctx
->ovnsb_txn
);
517 sbrec_bindings_set_logical_port(binding
, lport
->name
);
518 sbrec_bindings_set_mac(binding
,
519 (const char **) lport
->macs
, lport
->n_macs
);
520 if (lport
->parent_name
&& lport
->n_tag
> 0) {
521 sbrec_bindings_set_parent_port(binding
, lport
->parent_name
);
522 sbrec_bindings_set_tag(binding
, lport
->tag
, lport
->n_tag
);
527 HMAP_FOR_EACH_SAFE(hash_node
, hash_node_next
, node
, &bindings_hmap
) {
528 hmap_remove(&bindings_hmap
, &hash_node
->node
);
529 sbrec_bindings_delete(hash_node
->binding
);
532 hmap_destroy(&bindings_hmap
);
536 ovnnb_db_changed(struct northd_context
*ctx
)
538 VLOG_DBG("ovn-nb db contents have changed.");
545 * The only change we get notified about is if the 'chassis' column of the
546 * 'Bindings' table changes. When this column is not empty, it means we need to
547 * set the corresponding logical port as 'up' in the northbound DB.
550 ovnsb_db_changed(struct northd_context
*ctx
)
552 struct hmap lports_hmap
;
553 const struct sbrec_bindings
*binding
;
554 const struct nbrec_logical_port
*lport
;
556 struct lport_hash_node
{
557 struct hmap_node node
;
558 const struct nbrec_logical_port
*lport
;
559 } *hash_node
, *hash_node_next
;
561 VLOG_DBG("Recalculating port up states for ovn-nb db.");
563 hmap_init(&lports_hmap
);
565 NBREC_LOGICAL_PORT_FOR_EACH(lport
, ctx
->ovnnb_idl
) {
566 hash_node
= xzalloc(sizeof *hash_node
);
567 hash_node
->lport
= lport
;
568 hmap_insert(&lports_hmap
, &hash_node
->node
,
569 hash_string(lport
->name
, 0));
572 SBREC_BINDINGS_FOR_EACH(binding
, ctx
->ovnsb_idl
) {
574 HMAP_FOR_EACH_WITH_HASH(hash_node
, node
,
575 hash_string(binding
->logical_port
, 0), &lports_hmap
) {
576 if (!strcmp(binding
->logical_port
, hash_node
->lport
->name
)) {
577 lport
= hash_node
->lport
;
583 /* The logical port doesn't exist for this binding. This can
584 * happen under normal circumstances when ovn-northd hasn't gotten
585 * around to pruning the Binding yet. */
589 if (*binding
->chassis
&& (!lport
->up
|| !*lport
->up
)) {
591 nbrec_logical_port_set_up(lport
, &up
, 1);
592 } else if (!*binding
->chassis
&& (!lport
->up
|| *lport
->up
)) {
594 nbrec_logical_port_set_up(lport
, &up
, 1);
598 HMAP_FOR_EACH_SAFE(hash_node
, hash_node_next
, node
, &lports_hmap
) {
599 hmap_remove(&lports_hmap
, &hash_node
->node
);
602 hmap_destroy(&lports_hmap
);
610 def
= xasprintf("unix:%s/db.sock", ovs_rundir());
616 parse_options(int argc OVS_UNUSED
, char *argv
[] OVS_UNUSED
)
622 static const struct option long_options
[] = {
623 {"ovnsb-db", required_argument
, NULL
, 'd'},
624 {"ovnnb-db", required_argument
, NULL
, 'D'},
625 {"help", no_argument
, NULL
, 'h'},
626 {"options", no_argument
, NULL
, 'o'},
627 {"version", no_argument
, NULL
, 'V'},
630 STREAM_SSL_LONG_OPTIONS
,
633 char *short_options
= ovs_cmdl_long_options_to_short_options(long_options
);
638 c
= getopt_long(argc
, argv
, short_options
, long_options
, NULL
);
644 DAEMON_OPTION_HANDLERS
;
645 VLOG_OPTION_HANDLERS
;
646 STREAM_SSL_OPTION_HANDLERS
;
661 ovs_cmdl_print_options(long_options
);
665 ovs_print_version(0, 0);
674 ovnsb_db
= default_db();
678 ovnnb_db
= default_db();
685 main(int argc
, char *argv
[])
687 extern struct vlog_module VLM_reconnect
;
688 struct ovsdb_idl
*ovnnb_idl
, *ovnsb_idl
;
689 unsigned int ovnnb_seqno
, ovn_seqno
;
690 int res
= EXIT_SUCCESS
;
691 struct northd_context ctx
= {
694 bool ovnnb_changes_pending
= false;
695 bool ovn_changes_pending
= false;
697 fatal_ignore_sigpipe();
698 set_program_name(argv
[0]);
699 vlog_set_levels(NULL
, VLF_CONSOLE
, VLL_WARN
);
700 vlog_set_levels(&VLM_reconnect
, VLF_ANY_DESTINATION
, VLL_WARN
);
701 parse_options(argc
, argv
);
708 /* We want to detect all changes to the ovn-nb db. */
709 ctx
.ovnnb_idl
= ovnnb_idl
= ovsdb_idl_create(ovnnb_db
,
710 &nbrec_idl_class
, true, true);
712 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
713 * has to care about, so we'll enable monitoring those directly. */
714 ctx
.ovnsb_idl
= ovnsb_idl
= ovsdb_idl_create(ovnsb_db
,
715 &sbrec_idl_class
, false, true);
716 ovsdb_idl_add_table(ovnsb_idl
, &sbrec_table_bindings
);
717 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_logical_port
);
718 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_chassis
);
719 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_mac
);
720 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_tag
);
721 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_parent_port
);
722 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_pipeline_col_logical_datapath
);
723 ovsdb_idl_omit_alert(ovnsb_idl
, &sbrec_pipeline_col_logical_datapath
);
724 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_pipeline_col_table_id
);
725 ovsdb_idl_omit_alert(ovnsb_idl
, &sbrec_pipeline_col_table_id
);
726 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_pipeline_col_priority
);
727 ovsdb_idl_omit_alert(ovnsb_idl
, &sbrec_pipeline_col_priority
);
728 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_pipeline_col_match
);
729 ovsdb_idl_omit_alert(ovnsb_idl
, &sbrec_pipeline_col_match
);
730 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_pipeline_col_actions
);
731 ovsdb_idl_omit_alert(ovnsb_idl
, &sbrec_pipeline_col_actions
);
734 * The loop here just runs the IDL in a loop waiting for the seqno to
735 * change, which indicates that the contents of the db have changed.
737 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
738 * db must be recalculated.
740 * If the contents of the ovn-sb db change, it means the 'up' state of
741 * a port may have changed, as that's the only type of change ovn-northd is
745 ovnnb_seqno
= ovsdb_idl_get_seqno(ovnnb_idl
);
746 ovn_seqno
= ovsdb_idl_get_seqno(ovnsb_idl
);
748 ovsdb_idl_run(ovnnb_idl
);
749 ovsdb_idl_run(ovnsb_idl
);
751 if (!ovsdb_idl_is_alive(ovnnb_idl
)) {
752 int retval
= ovsdb_idl_get_last_error(ovnnb_idl
);
753 VLOG_ERR("%s: database connection failed (%s)",
754 ovnnb_db
, ovs_retval_to_string(retval
));
759 if (!ovsdb_idl_is_alive(ovnsb_idl
)) {
760 int retval
= ovsdb_idl_get_last_error(ovnsb_idl
);
761 VLOG_ERR("%s: database connection failed (%s)",
762 ovnsb_db
, ovs_retval_to_string(retval
));
767 if (ovnnb_seqno
!= ovsdb_idl_get_seqno(ovnnb_idl
)) {
768 ovnnb_seqno
= ovsdb_idl_get_seqno(ovnnb_idl
);
769 ovnnb_changes_pending
= true;
772 if (ovn_seqno
!= ovsdb_idl_get_seqno(ovnsb_idl
)) {
773 ovn_seqno
= ovsdb_idl_get_seqno(ovnsb_idl
);
774 ovn_changes_pending
= true;
778 * If there are any pending changes, we delay recalculating the
779 * necessary updates until after an existing transaction finishes.
780 * This avoids the possibility of rapid updates causing ovn-northd to
781 * never be able to successfully make the corresponding updates to the
782 * other db. Instead, pending changes are batched up until the next
783 * time we get a chance to calculate the new state and apply it.
786 if (ovnnb_changes_pending
&& !ctx
.ovnsb_txn
) {
788 * The OVN-nb db contents have changed, so create a transaction for
789 * updating the OVN-sb DB.
791 ctx
.ovnsb_txn
= ovsdb_idl_txn_create(ctx
.ovnsb_idl
);
792 ovsdb_idl_txn_add_comment(ctx
.ovnsb_txn
,
793 "ovn-northd: northbound db changed");
794 ovnnb_db_changed(&ctx
);
795 ovnnb_changes_pending
= false;
798 if (ovn_changes_pending
&& !ctx
.ovnnb_txn
) {
800 * The OVN-sb db contents have changed, so create a transaction for
801 * updating the northbound DB.
803 ctx
.ovnnb_txn
= ovsdb_idl_txn_create(ctx
.ovnnb_idl
);
804 ovsdb_idl_txn_add_comment(ctx
.ovnnb_txn
,
805 "ovn-northd: southbound db changed");
806 ovnsb_db_changed(&ctx
);
807 ovn_changes_pending
= false;
811 enum ovsdb_idl_txn_status txn_status
;
812 txn_status
= ovsdb_idl_txn_commit(ctx
.ovnnb_txn
);
813 switch (txn_status
) {
814 case TXN_UNCOMMITTED
:
816 /* Come back around and try to commit this transaction again */
822 /* Something went wrong, so try creating a new transaction. */
823 ovn_changes_pending
= true;
826 ovsdb_idl_txn_destroy(ctx
.ovnnb_txn
);
827 ctx
.ovnnb_txn
= NULL
;
832 enum ovsdb_idl_txn_status txn_status
;
833 txn_status
= ovsdb_idl_txn_commit(ctx
.ovnsb_txn
);
834 switch (txn_status
) {
835 case TXN_UNCOMMITTED
:
837 /* Come back around and try to commit this transaction again */
843 /* Something went wrong, so try creating a new transaction. */
844 ovnnb_changes_pending
= true;
847 ovsdb_idl_txn_destroy(ctx
.ovnsb_txn
);
848 ctx
.ovnsb_txn
= NULL
;
852 if (ovnnb_seqno
== ovsdb_idl_get_seqno(ovnnb_idl
) &&
853 ovn_seqno
== ovsdb_idl_get_seqno(ovnsb_idl
)) {
854 ovsdb_idl_wait(ovnnb_idl
);
855 ovsdb_idl_wait(ovnsb_idl
);
857 ovsdb_idl_txn_wait(ctx
.ovnnb_txn
);
860 ovsdb_idl_txn_wait(ctx
.ovnsb_txn
);
866 ovsdb_idl_destroy(ovnsb_idl
);
867 ovsdb_idl_destroy(ovnnb_idl
);