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 "fatal-signal.h"
27 #include "ovn/ovn-nb-idl.h"
28 #include "ovn/ovn-sb-idl.h"
29 #include "poll-loop.h"
31 #include "stream-ssl.h"
34 #include "openvswitch/vlog.h"
36 VLOG_DEFINE_THIS_MODULE(ovn_nbd
);
39 struct ovsdb_idl
*ovnnb_idl
;
40 struct ovsdb_idl
*ovnsb_idl
;
41 struct ovsdb_idl_txn
*ovnnb_txn
;
42 struct ovsdb_idl_txn
*ovn_txn
;
45 static const char *ovnnb_db
;
46 static const char *ovnsb_db
;
48 static const char *default_db(void);
54 %s: OVN northbound management daemon\n\
55 usage: %s [OPTIONS]\n\
58 --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
60 --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
62 -h, --help display this help message\n\
63 -o, --options list available options\n\
64 -V, --version display version information\n\
65 ", program_name
, program_name
, default_db(), default_db());
68 stream_usage("database", true, true, false);
72 compare_strings(const void *a_
, const void *b_
)
76 return strcmp(*a
, *b
);
80 * Determine whether 2 arrays of MAC addresses are the same. It's possible that
81 * the lists could be *very* long and this check is being done a lot (every
82 * time the OVN_Northbound database changes).
85 macs_equal(char **binding_macs_
, size_t b_n_macs
,
86 char **lport_macs_
, size_t l_n_macs
)
88 char **binding_macs
, **lport_macs
;
91 if (b_n_macs
!= l_n_macs
) {
95 bytes
= b_n_macs
* sizeof binding_macs_
[0];
96 binding_macs
= xmalloc(bytes
);
97 lport_macs
= xmalloc(bytes
);
99 memcpy(binding_macs
, binding_macs_
, bytes
);
100 memcpy(lport_macs
, lport_macs_
, bytes
);
102 qsort(binding_macs
, b_n_macs
, sizeof binding_macs
[0], compare_strings
);
103 qsort(lport_macs
, l_n_macs
, sizeof lport_macs
[0], compare_strings
);
105 for (i
= 0; i
< b_n_macs
; i
++) {
106 if (strcmp(binding_macs
[i
], lport_macs
[i
])) {
114 return (i
== b_n_macs
) ? true : false;
118 * When a change has occurred in the OVN_Northbound database, we go through and
119 * make sure that the contents of the Bindings table in the OVN_Southbound
120 * database are up to date with the logical ports defined in the
121 * OVN_Northbound database.
124 set_bindings(struct nbd_context
*ctx
)
126 struct hmap bindings_hmap
;
127 const struct sbrec_bindings
*binding
;
128 const struct nbrec_logical_port
*lport
;
130 struct binding_hash_node
{
131 struct hmap_node node
;
132 const struct sbrec_bindings
*binding
;
133 } *hash_node
, *hash_node_next
;
136 * We will need to look up a binding for every logical port. We don't want
137 * to have to do an O(n) search for every binding, so start out by hashing
138 * them on the logical port.
140 * As we go through every logical port, we will update the binding if it
141 * exists or create one otherwise. When the update is done, we'll remove it
142 * from the hashmap. At the end, any bindings left in the hashmap are for
143 * logical ports that have been deleted.
145 hmap_init(&bindings_hmap
);
147 SBREC_BINDINGS_FOR_EACH(binding
, ctx
->ovnsb_idl
) {
148 struct binding_hash_node
*hash_node
= xzalloc(sizeof *hash_node
);
150 hash_node
->binding
= binding
;
151 hmap_insert(&bindings_hmap
, &hash_node
->node
,
152 hash_string(binding
->logical_port
, 0));
155 NBREC_LOGICAL_PORT_FOR_EACH(lport
, ctx
->ovnnb_idl
) {
156 HMAP_FOR_EACH_WITH_HASH(hash_node
, node
,
157 hash_string(lport
->name
, 0), &bindings_hmap
) {
158 if (!strcmp(lport
->name
, hash_node
->binding
->logical_port
)) {
164 /* We found an existing binding for this logical port. Update its
165 * contents. Right now the only thing we expect that could change
166 * is the list of MAC addresses. */
168 binding
= hash_node
->binding
;
169 hmap_remove(&bindings_hmap
, &hash_node
->node
);
173 if (!macs_equal(binding
->mac
, binding
->n_mac
,
174 lport
->macs
, lport
->n_macs
)) {
175 sbrec_bindings_set_mac(binding
,
176 (const char **) lport
->macs
, lport
->n_macs
);
179 /* There is no binding for this logical port, so create one. */
181 binding
= sbrec_bindings_insert(ctx
->ovn_txn
);
182 sbrec_bindings_set_logical_port(binding
, lport
->name
);
183 sbrec_bindings_set_mac(binding
,
184 (const char **) lport
->macs
, lport
->n_macs
);
188 HMAP_FOR_EACH_SAFE(hash_node
, hash_node_next
, node
, &bindings_hmap
) {
189 hmap_remove(&bindings_hmap
, &hash_node
->node
);
190 sbrec_bindings_delete(hash_node
->binding
);
193 hmap_destroy(&bindings_hmap
);
197 ovnnb_db_changed(struct nbd_context
*ctx
)
199 VLOG_DBG("ovn-nbd: ovn-nb db contents have changed.\n");
205 * The only change we get notified about is if the 'chassis' column of the
206 * 'Bindings' table changes. When this column is not empty, it means we need to
207 * set the corresponding logical port as 'up' in the northbound DB.
210 ovnsb_db_changed(struct nbd_context
*ctx
)
212 const struct sbrec_bindings
*bindings
;
214 VLOG_DBG("Recalculating port up states for ovn-nb db.");
216 SBREC_BINDINGS_FOR_EACH(bindings
, ctx
->ovnsb_idl
) {
217 const struct nbrec_logical_port
*lport
;
218 struct uuid lport_uuid
;
220 if (!uuid_from_string(&lport_uuid
, bindings
->logical_port
)) {
221 VLOG_WARN("Invalid logical port UUID '%s' in Bindings table.",
222 bindings
->logical_port
);
226 lport
= nbrec_logical_port_get_for_uuid(ctx
->ovnnb_idl
, &lport_uuid
);
228 VLOG_WARN("No logical port '%s' found in OVN-nb db.",
229 bindings
->logical_port
);
233 if (*bindings
->chassis
&& (!lport
->up
|| !*lport
->up
)) {
235 nbrec_logical_port_set_up(lport
, &up
, 1);
236 } else if (!*bindings
->chassis
&& (!lport
->up
|| *lport
->up
)) {
238 nbrec_logical_port_set_up(lport
, &up
, 1);
248 def
= xasprintf("unix:%s/db.sock", ovs_rundir());
254 parse_options(int argc OVS_UNUSED
, char *argv
[] OVS_UNUSED
)
260 static const struct option long_options
[] = {
261 {"ovnsb-db", required_argument
, NULL
, 'd'},
262 {"ovnnb-db", required_argument
, NULL
, 'D'},
263 {"help", no_argument
, NULL
, 'h'},
264 {"options", no_argument
, NULL
, 'o'},
265 {"version", no_argument
, NULL
, 'V'},
268 STREAM_SSL_LONG_OPTIONS
,
271 char *short_options
= ovs_cmdl_long_options_to_short_options(long_options
);
276 c
= getopt_long(argc
, argv
, short_options
, long_options
, NULL
);
282 DAEMON_OPTION_HANDLERS
;
283 VLOG_OPTION_HANDLERS
;
284 STREAM_SSL_OPTION_HANDLERS
;
299 ovs_cmdl_print_options(long_options
);
303 ovs_print_version(0, 0);
312 ovnsb_db
= default_db();
316 ovnnb_db
= default_db();
323 main(int argc
, char *argv
[])
325 extern struct vlog_module VLM_reconnect
;
326 struct ovsdb_idl
*ovnnb_idl
, *ovnsb_idl
;
327 unsigned int ovnnb_seqno
, ovn_seqno
;
328 int res
= EXIT_SUCCESS
;
329 struct nbd_context ctx
= {
332 bool ovnnb_changes_pending
= false;
333 bool ovn_changes_pending
= false;
335 fatal_ignore_sigpipe();
336 set_program_name(argv
[0]);
337 vlog_set_levels(NULL
, VLF_CONSOLE
, VLL_WARN
);
338 vlog_set_levels(&VLM_reconnect
, VLF_ANY_DESTINATION
, VLL_WARN
);
339 parse_options(argc
, argv
);
346 /* We want to detect all changes to the ovn-nb db. */
347 ctx
.ovnnb_idl
= ovnnb_idl
= ovsdb_idl_create(ovnnb_db
,
348 &nbrec_idl_class
, true, true);
350 /* There is only a small subset of changes to the ovn db that ovn-nbd has to
351 * care about, so we'll enable monitoring those directly. */
352 ctx
.ovnsb_idl
= ovnsb_idl
= ovsdb_idl_create(ovnsb_db
,
353 &sbrec_idl_class
, false, true);
354 ovsdb_idl_add_table(ovnsb_idl
, &sbrec_table_bindings
);
355 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_logical_port
);
356 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_chassis
);
357 ovsdb_idl_add_column(ovnsb_idl
, &sbrec_bindings_col_mac
);
360 * The loop here just runs the IDL in a loop waiting for the seqno to
361 * change, which indicates that the contents of the db have changed.
363 * If the contents of the ovn-nb db change, the mappings to the ovn db must
366 * If the contents of the ovn db change, it means the 'up' state of a port
367 * may have changed, as that's the only type of change ovn-nbd is watching
371 ovnnb_seqno
= ovsdb_idl_get_seqno(ovnnb_idl
);
372 ovn_seqno
= ovsdb_idl_get_seqno(ovnsb_idl
);
374 ovsdb_idl_run(ovnnb_idl
);
375 ovsdb_idl_run(ovnsb_idl
);
377 if (!ovsdb_idl_is_alive(ovnnb_idl
)) {
378 int retval
= ovsdb_idl_get_last_error(ovnnb_idl
);
379 VLOG_ERR("%s: database connection failed (%s)",
380 ovnnb_db
, ovs_retval_to_string(retval
));
385 if (!ovsdb_idl_is_alive(ovnsb_idl
)) {
386 int retval
= ovsdb_idl_get_last_error(ovnsb_idl
);
387 VLOG_ERR("%s: database connection failed (%s)",
388 ovnsb_db
, ovs_retval_to_string(retval
));
393 if (ovnnb_seqno
!= ovsdb_idl_get_seqno(ovnnb_idl
)) {
394 ovnnb_seqno
= ovsdb_idl_get_seqno(ovnnb_idl
);
395 ovnnb_changes_pending
= true;
398 if (ovn_seqno
!= ovsdb_idl_get_seqno(ovnsb_idl
)) {
399 ovn_seqno
= ovsdb_idl_get_seqno(ovnsb_idl
);
400 ovn_changes_pending
= true;
404 * If there are any pending changes, we delay recalculating the
405 * necessary updates until after an existing transaction finishes.
406 * This avoids the possibility of rapid updates causing ovn-nbd to never
407 * be able to successfully make the corresponding updates to the other
408 * db. Instead, pending changes are batched up until the next time we
409 * get a chance to calculate the new state and apply it.
412 if (ovnnb_changes_pending
&& !ctx
.ovn_txn
) {
414 * The OVN-nb db contents have changed, so create a transaction for
415 * updating the OVN DB.
417 ctx
.ovn_txn
= ovsdb_idl_txn_create(ctx
.ovnsb_idl
);
418 ovnnb_db_changed(&ctx
);
419 ovnnb_changes_pending
= false;
422 if (ovn_changes_pending
&& !ctx
.ovnnb_txn
) {
424 * The OVN db contents have changed, so create a transaction for
425 * updating the northbound DB.
427 ctx
.ovnnb_txn
= ovsdb_idl_txn_create(ctx
.ovnnb_idl
);
428 ovnsb_db_changed(&ctx
);
429 ovn_changes_pending
= false;
433 enum ovsdb_idl_txn_status txn_status
;
434 txn_status
= ovsdb_idl_txn_commit(ctx
.ovnnb_txn
);
435 switch (txn_status
) {
436 case TXN_UNCOMMITTED
:
438 /* Come back around and try to commit this transaction again */
444 /* Something went wrong, so try creating a new transaction. */
445 ovn_changes_pending
= true;
448 ovsdb_idl_txn_destroy(ctx
.ovnnb_txn
);
449 ctx
.ovnnb_txn
= NULL
;
454 enum ovsdb_idl_txn_status txn_status
;
455 txn_status
= ovsdb_idl_txn_commit(ctx
.ovn_txn
);
456 switch (txn_status
) {
457 case TXN_UNCOMMITTED
:
459 /* Come back around and try to commit this transaction again */
465 /* Something went wrong, so try creating a new transaction. */
466 ovnnb_changes_pending
= true;
469 ovsdb_idl_txn_destroy(ctx
.ovn_txn
);
474 if (ovnnb_seqno
== ovsdb_idl_get_seqno(ovnnb_idl
) &&
475 ovn_seqno
== ovsdb_idl_get_seqno(ovnsb_idl
)) {
476 ovsdb_idl_wait(ovnnb_idl
);
477 ovsdb_idl_wait(ovnsb_idl
);
479 ovsdb_idl_txn_wait(ctx
.ovnnb_txn
);
482 ovsdb_idl_txn_wait(ctx
.ovn_txn
);
488 ovsdb_idl_destroy(ovnsb_idl
);
489 ovsdb_idl_destroy(ovnnb_idl
);