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_northd);
38 struct northd_context {
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 parents_equal(const struct sbrec_bindings *binding,
119 const struct nbrec_logical_port *lport)
121 if (!!binding->parent_port != !!lport->parent_name) {
122 /* One is set and the other is not. */
126 if (binding->parent_port) {
128 return strcmp(binding->parent_port, lport->parent_name) ? false : true;
136 tags_equal(const struct sbrec_bindings *binding,
137 const struct nbrec_logical_port *lport)
139 if (binding->n_tag != lport->n_tag) {
143 return binding->n_tag ? (binding->tag[0] == lport->tag[0]) : true;
147 * When a change has occurred in the OVN_Northbound database, we go through and
148 * make sure that the contents of the Bindings table in the OVN_Southbound
149 * database are up to date with the logical ports defined in the
150 * OVN_Northbound database.
153 set_bindings(struct northd_context *ctx)
155 struct hmap bindings_hmap;
156 const struct sbrec_bindings *binding;
157 const struct nbrec_logical_port *lport;
159 struct binding_hash_node {
160 struct hmap_node node;
161 const struct sbrec_bindings *binding;
162 } *hash_node, *hash_node_next;
165 * We will need to look up a binding for every logical port. We don't want
166 * to have to do an O(n) search for every binding, so start out by hashing
167 * them on the logical port.
169 * As we go through every logical port, we will update the binding if it
170 * exists or create one otherwise. When the update is done, we'll remove it
171 * from the hashmap. At the end, any bindings left in the hashmap are for
172 * logical ports that have been deleted.
174 hmap_init(&bindings_hmap);
176 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
177 hash_node = xzalloc(sizeof *hash_node);
178 hash_node->binding = binding;
179 hmap_insert(&bindings_hmap, &hash_node->node,
180 hash_string(binding->logical_port, 0));
183 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
185 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
186 hash_string(lport->name, 0), &bindings_hmap) {
187 if (!strcmp(lport->name, hash_node->binding->logical_port)) {
188 binding = hash_node->binding;
194 /* We found an existing binding for this logical port. Update its
197 hmap_remove(&bindings_hmap, &hash_node->node);
201 if (!macs_equal(binding->mac, binding->n_mac,
202 lport->macs, lport->n_macs)) {
203 sbrec_bindings_set_mac(binding,
204 (const char **) lport->macs, lport->n_macs);
206 if (!parents_equal(binding, lport)) {
207 sbrec_bindings_set_parent_port(binding, lport->parent_name);
209 if (!tags_equal(binding, lport)) {
210 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
213 /* There is no binding for this logical port, so create one. */
215 binding = sbrec_bindings_insert(ctx->ovnsb_txn);
216 sbrec_bindings_set_logical_port(binding, lport->name);
217 sbrec_bindings_set_mac(binding,
218 (const char **) lport->macs, lport->n_macs);
219 if (lport->parent_name && lport->n_tag > 0) {
220 sbrec_bindings_set_parent_port(binding, lport->parent_name);
221 sbrec_bindings_set_tag(binding, lport->tag, lport->n_tag);
226 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
227 hmap_remove(&bindings_hmap, &hash_node->node);
228 sbrec_bindings_delete(hash_node->binding);
231 hmap_destroy(&bindings_hmap);
235 ovnnb_db_changed(struct northd_context *ctx)
237 VLOG_DBG("ovn-nb db contents have changed.");
243 * The only change we get notified about is if the 'chassis' column of the
244 * 'Bindings' table changes. When this column is not empty, it means we need to
245 * set the corresponding logical port as 'up' in the northbound DB.
248 ovnsb_db_changed(struct northd_context *ctx)
250 struct hmap lports_hmap;
251 const struct sbrec_bindings *binding;
252 const struct nbrec_logical_port *lport;
254 struct lport_hash_node {
255 struct hmap_node node;
256 const struct nbrec_logical_port *lport;
257 } *hash_node, *hash_node_next;
259 VLOG_DBG("Recalculating port up states for ovn-nb db.");
261 hmap_init(&lports_hmap);
263 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
264 hash_node = xzalloc(sizeof *hash_node);
265 hash_node->lport = lport;
266 hmap_insert(&lports_hmap, &hash_node->node,
267 hash_string(lport->name, 0));
270 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
272 HMAP_FOR_EACH_WITH_HASH(hash_node, node,
273 hash_string(binding->logical_port, 0), &lports_hmap) {
274 if (!strcmp(binding->logical_port, hash_node->lport->name)) {
275 lport = hash_node->lport;
281 /* The logical port doesn't exist for this binding. This can
282 * happen under normal circumstances when ovn-northd hasn't gotten
283 * around to pruning the Binding yet. */
287 if (*binding->chassis && (!lport->up || !*lport->up)) {
289 nbrec_logical_port_set_up(lport, &up, 1);
290 } else if (!*binding->chassis && (!lport->up || *lport->up)) {
292 nbrec_logical_port_set_up(lport, &up, 1);
296 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
297 hmap_remove(&lports_hmap, &hash_node->node);
300 hmap_destroy(&lports_hmap);
308 def = xasprintf("unix:%s/db.sock", ovs_rundir());
314 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
320 static const struct option long_options[] = {
321 {"ovnsb-db", required_argument, NULL, 'd'},
322 {"ovnnb-db", required_argument, NULL, 'D'},
323 {"help", no_argument, NULL, 'h'},
324 {"options", no_argument, NULL, 'o'},
325 {"version", no_argument, NULL, 'V'},
328 STREAM_SSL_LONG_OPTIONS,
331 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
336 c = getopt_long(argc, argv, short_options, long_options, NULL);
342 DAEMON_OPTION_HANDLERS;
343 VLOG_OPTION_HANDLERS;
344 STREAM_SSL_OPTION_HANDLERS;
359 ovs_cmdl_print_options(long_options);
363 ovs_print_version(0, 0);
372 ovnsb_db = default_db();
376 ovnnb_db = default_db();
383 main(int argc, char *argv[])
385 extern struct vlog_module VLM_reconnect;
386 struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
387 unsigned int ovnnb_seqno, ovn_seqno;
388 int res = EXIT_SUCCESS;
389 struct northd_context ctx = {
392 bool ovnnb_changes_pending = false;
393 bool ovn_changes_pending = false;
395 fatal_ignore_sigpipe();
396 set_program_name(argv[0]);
397 vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
398 vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
399 parse_options(argc, argv);
406 /* We want to detect all changes to the ovn-nb db. */
407 ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
408 &nbrec_idl_class, true, true);
410 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
411 * has to care about, so we'll enable monitoring those directly. */
412 ctx.ovnsb_idl = ovnsb_idl = ovsdb_idl_create(ovnsb_db,
413 &sbrec_idl_class, false, true);
414 ovsdb_idl_add_table(ovnsb_idl, &sbrec_table_bindings);
415 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_logical_port);
416 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_chassis);
417 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_mac);
418 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_tag);
419 ovsdb_idl_add_column(ovnsb_idl, &sbrec_bindings_col_parent_port);
422 * The loop here just runs the IDL in a loop waiting for the seqno to
423 * change, which indicates that the contents of the db have changed.
425 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
426 * db must be recalculated.
428 * If the contents of the ovn-sb db change, it means the 'up' state of
429 * a port may have changed, as that's the only type of change ovn-northd is
433 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
434 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
436 ovsdb_idl_run(ovnnb_idl);
437 ovsdb_idl_run(ovnsb_idl);
439 if (!ovsdb_idl_is_alive(ovnnb_idl)) {
440 int retval = ovsdb_idl_get_last_error(ovnnb_idl);
441 VLOG_ERR("%s: database connection failed (%s)",
442 ovnnb_db, ovs_retval_to_string(retval));
447 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
448 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
449 VLOG_ERR("%s: database connection failed (%s)",
450 ovnsb_db, ovs_retval_to_string(retval));
455 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
456 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
457 ovnnb_changes_pending = true;
460 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
461 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
462 ovn_changes_pending = true;
466 * If there are any pending changes, we delay recalculating the
467 * necessary updates until after an existing transaction finishes.
468 * This avoids the possibility of rapid updates causing ovn-northd to
469 * never be able to successfully make the corresponding updates to the
470 * other db. Instead, pending changes are batched up until the next
471 * time we get a chance to calculate the new state and apply it.
474 if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
476 * The OVN-nb db contents have changed, so create a transaction for
477 * updating the OVN-sb DB.
479 ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
480 ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
481 "ovn-northd: northbound db changed");
482 ovnnb_db_changed(&ctx);
483 ovnnb_changes_pending = false;
486 if (ovn_changes_pending && !ctx.ovnnb_txn) {
488 * The OVN-sb db contents have changed, so create a transaction for
489 * updating the northbound DB.
491 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
492 ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
493 "ovn-northd: southbound db changed");
494 ovnsb_db_changed(&ctx);
495 ovn_changes_pending = false;
499 enum ovsdb_idl_txn_status txn_status;
500 txn_status = ovsdb_idl_txn_commit(ctx.ovnnb_txn);
501 switch (txn_status) {
502 case TXN_UNCOMMITTED:
504 /* Come back around and try to commit this transaction again */
510 /* Something went wrong, so try creating a new transaction. */
511 ovn_changes_pending = true;
514 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
515 ctx.ovnnb_txn = NULL;
520 enum ovsdb_idl_txn_status txn_status;
521 txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
522 switch (txn_status) {
523 case TXN_UNCOMMITTED:
525 /* Come back around and try to commit this transaction again */
531 /* Something went wrong, so try creating a new transaction. */
532 ovnnb_changes_pending = true;
535 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
536 ctx.ovnsb_txn = NULL;
540 if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
541 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
542 ovsdb_idl_wait(ovnnb_idl);
543 ovsdb_idl_wait(ovnsb_idl);
545 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
548 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
554 ovsdb_idl_destroy(ovnsb_idl);
555 ovsdb_idl_destroy(ovnnb_idl);