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 *ovnsb_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 hash_node = xzalloc(sizeof *hash_node);
149 hash_node->binding = binding;
150 hmap_insert(&bindings_hmap, &hash_node->node,
151 hash_string(binding->logical_port, 0));
154 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)) {
159 binding = hash_node->binding;
165 /* We found an existing binding for this logical port. Update its
166 * contents. Right now the only thing we expect that could change
167 * is the list of MAC addresses. */
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->ovnsb_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-northd: 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 struct hmap lports_hmap;
213 const struct sbrec_bindings *binding;
214 const struct nbrec_logical_port *lport;
216 struct lport_hash_node {
217 struct hmap_node node;
218 const struct nbrec_logical_port *lport;
219 } *hash_node, *hash_node_next;
221 VLOG_DBG("Recalculating port up states for ovn-nb db.");
223 hmap_init(&lports_hmap);
225 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
226 hash_node = xzalloc(sizeof *hash_node);
227 hash_node->lport = lport;
228 hmap_insert(&lports_hmap, &hash_node->node,
229 hash_string(lport->name, 0));
232 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
234 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
235 hash_string(binding->logical_port, 0), &lports_hmap) {
236 if (!strcmp(binding->logical_port, hash_node->lport->name)) {
237 lport = hash_node->lport;
243 /* The logical port doesn't exist for this binding. This can happen
244 * under normal circumstances when ovn-nbd hasn't gotten around to
245 * pruning the Binding yet. */
249 if (*binding->chassis && (!lport->up || !*lport->up)) {
251 nbrec_logical_port_set_up(lport, &up, 1);
252 } else if (!*binding->chassis && (!lport->up || *lport->up)) {
254 nbrec_logical_port_set_up(lport, &up, 1);
258 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
259 hmap_remove(&lports_hmap, &hash_node->node);
262 hmap_destroy(&lports_hmap);
270 def = xasprintf("unix:%s/db.sock", ovs_rundir());
276 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
282 static const struct option long_options[] = {
283 {"ovnsb-db", required_argument, NULL, 'd'},
284 {"ovnnb-db", required_argument, NULL, 'D'},
285 {"help", no_argument, NULL, 'h'},
286 {"options", no_argument, NULL, 'o'},
287 {"version", no_argument, NULL, 'V'},
290 STREAM_SSL_LONG_OPTIONS,
293 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
298 c = getopt_long(argc, argv, short_options, long_options, NULL);
304 DAEMON_OPTION_HANDLERS;
305 VLOG_OPTION_HANDLERS;
306 STREAM_SSL_OPTION_HANDLERS;
321 ovs_cmdl_print_options(long_options);
325 ovs_print_version(0, 0);
334 ovnsb_db = default_db();
338 ovnnb_db = default_db();
345 main(int argc, char *argv[])
347 extern struct vlog_module VLM_reconnect;
348 struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
349 unsigned int ovnnb_seqno, ovn_seqno;
350 int res = EXIT_SUCCESS;
351 struct nbd_context ctx = {
354 bool ovnnb_changes_pending = false;
355 bool ovn_changes_pending = false;
357 fatal_ignore_sigpipe();
358 set_program_name(argv[0]);
359 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
360 vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
361 parse_options(argc, argv);
368 /* We want to detect all changes to the ovn-nb db. */
369 ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
370 &nbrec_idl_class, true, true);
372 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
373 * has to care about, so we'll enable monitoring those directly. */
374 ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
375 &sbrec_idl_class, false, true);
376 ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_bindings);
377 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_port);
378 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_chassis);
379 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_mac);
382 * The loop here just runs the IDL in a loop waiting for the seqno to
383 * change, which indicates that the contents of the db have changed.
385 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
386 * db must be recalculated.
388 * If the contents of the ovn-sb db change, it means the 'up' state of
389 * a port may have changed, as that's the only type of change ovn-northd is
393 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
394 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
396 ovsdb_idl_run(ovnnb_idl);
397 ovsdb_idl_run(ovnsb_idl);
399 if (!ovsdb_idl_is_alive(ovnnb_idl)) {
400 int retval = ovsdb_idl_get_last_error(ovnnb_idl);
401 VLOG_ERR("%s: database connection failed (%s)",
402 ovnnb_db, ovs_retval_to_string(retval));
407 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
408 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
409 VLOG_ERR("%s: database connection failed (%s)",
410 ovnsb_db, ovs_retval_to_string(retval));
415 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
416 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
417 ovnnb_changes_pending = true;
420 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
421 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
422 ovn_changes_pending = true;
426 * If there are any pending changes, we delay recalculating the
427 * necessary updates until after an existing transaction finishes.
428 * This avoids the possibility of rapid updates causing ovn-northd to
429 * never be able to successfully make the corresponding updates to the
430 * other db. Instead, pending changes are batched up until the next
431 * time we get a chance to calculate the new state and apply it.
434 if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
436 * The OVN-nb db contents have changed, so create a transaction for
437 * updating the OVN-sb DB.
439 ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
440 ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
441 "ovn-northd: northbound db changed");
442 ovnnb_db_changed(&ctx);
443 ovnnb_changes_pending = false;
446 if (ovn_changes_pending && !ctx.ovnnb_txn) {
448 * The OVN-sb db contents have changed, so create a transaction for
449 * updating the northbound DB.
451 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
452 ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
453 "ovn-northd: southbound db changed");
454 ovnsb_db_changed(&ctx);
455 ovn_changes_pending = false;
459 enum ovsdb_idl_txn_status txn_status;
460 txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
461 switch (txn_status) {
462 case TXN_UNCOMMITTED:
464 /* Come back around and try to commit this transaction again */
470 /* Something went wrong, so try creating a new transaction. */
471 ovn_changes_pending = true;
474 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
475 ctx.ovnnb_txn = NULL;
480 enum ovsdb_idl_txn_status txn_status;
481 txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
482 switch (txn_status) {
483 case TXN_UNCOMMITTED:
485 /* Come back around and try to commit this transaction again */
491 /* Something went wrong, so try creating a new transaction. */
492 ovnnb_changes_pending = true;
495 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
496 ctx.ovnsb_txn = NULL;
500 if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
501 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
502 ovsdb_idl_wait(ovnnb_idl);
503 ovsdb_idl_wait(ovnsb_idl);
505 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
508 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
514 ovsdb_idl_destroy(ovnsb_idl);
515 ovsdb_idl_destroy(ovnnb_idl);