]> git.proxmox.com Git - mirror_ovs.git/blame - ovn/northd/ovn-northd.c
ovn-nbctl.8: Add descriptions for commands.
[mirror_ovs.git] / ovn / northd / ovn-northd.c
CommitLineData
ac0630a2
RB
1/*
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:
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
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.
13 */
14
15#include <config.h>
16
17#include <getopt.h>
18#include <stdlib.h>
19#include <stdio.h>
20
21#include "command-line.h"
67d9b930 22#include "daemon.h"
ac0630a2
RB
23#include "dirs.h"
24#include "fatal-signal.h"
4edcdcf4
RB
25#include "hash.h"
26#include "hmap.h"
ac0630a2 27#include "ovn/ovn-nb-idl.h"
ec78987f 28#include "ovn/ovn-sb-idl.h"
ac0630a2
RB
29#include "poll-loop.h"
30#include "stream.h"
31#include "stream-ssl.h"
32#include "util.h"
4edcdcf4 33#include "uuid.h"
ac0630a2
RB
34#include "openvswitch/vlog.h"
35
36VLOG_DEFINE_THIS_MODULE(ovn_nbd);
37
f93818dd
RB
38struct nbd_context {
39 struct ovsdb_idl *ovnnb_idl;
ec78987f 40 struct ovsdb_idl *ovnsb_idl;
f93818dd 41 struct ovsdb_idl_txn *ovnnb_txn;
3c78b3ca 42 struct ovsdb_idl_txn *ovnsb_txn;
f93818dd
RB
43};
44
ac0630a2 45static const char *ovnnb_db;
ec78987f 46static const char *ovnsb_db;
ac0630a2
RB
47
48static const char *default_db(void);
49
50static void
51usage(void)
52{
53 printf("\
54%s: OVN northbound management daemon\n\
55usage: %s [OPTIONS]\n\
56\n\
57Options:\n\
58 --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
59 (default: %s)\n\
ec78987f 60 --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
ac0630a2
RB
61 (default: %s)\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());
67d9b930 66 daemon_usage();
ac0630a2
RB
67 vlog_usage();
68 stream_usage("database", true, true, false);
69}
70\f
4edcdcf4
RB
71static int
72compare_strings(const void *a_, const void *b_)
73{
74 char *const *a = a_;
75 char *const *b = b_;
76 return strcmp(*a, *b);
77}
78
79/*
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).
83 */
84static bool
85macs_equal(char **binding_macs_, size_t b_n_macs,
86 char **lport_macs_, size_t l_n_macs)
87{
88 char **binding_macs, **lport_macs;
89 size_t bytes, i;
90
91 if (b_n_macs != l_n_macs) {
92 return false;
93 }
94
95 bytes = b_n_macs * sizeof binding_macs_[0];
96 binding_macs = xmalloc(bytes);
97 lport_macs = xmalloc(bytes);
98
99 memcpy(binding_macs, binding_macs_, bytes);
100 memcpy(lport_macs, lport_macs_, bytes);
101
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);
104
105 for (i = 0; i < b_n_macs; i++) {
106 if (strcmp(binding_macs[i], lport_macs[i])) {
107 break;
108 }
109 }
110
111 free(binding_macs);
112 free(lport_macs);
113
114 return (i == b_n_macs) ? true : false;
115}
116
117/*
118 * When a change has occurred in the OVN_Northbound database, we go through and
ec78987f
JP
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.
4edcdcf4 122 */
ac0630a2 123static void
4edcdcf4 124set_bindings(struct nbd_context *ctx)
ac0630a2 125{
4edcdcf4 126 struct hmap bindings_hmap;
ec78987f 127 const struct sbrec_bindings *binding;
4edcdcf4
RB
128 const struct nbrec_logical_port *lport;
129
130 struct binding_hash_node {
131 struct hmap_node node;
ec78987f 132 const struct sbrec_bindings *binding;
4edcdcf4
RB
133 } *hash_node, *hash_node_next;
134
135 /*
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.
139 *
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.
144 */
145 hmap_init(&bindings_hmap);
146
ec78987f 147 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
cf1486e0 148 hash_node = xzalloc(sizeof *hash_node);
4edcdcf4
RB
149 hash_node->binding = binding;
150 hmap_insert(&bindings_hmap, &hash_node->node,
151 hash_string(binding->logical_port, 0));
152 }
153
154 NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->ovnnb_idl) {
1d4e6b55 155 binding = NULL;
4edcdcf4
RB
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)) {
1d4e6b55 159 binding = hash_node->binding;
4edcdcf4
RB
160 break;
161 }
162 }
163
1d4e6b55 164 if (binding) {
4edcdcf4
RB
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. */
168
4edcdcf4
RB
169 hmap_remove(&bindings_hmap, &hash_node->node);
170 free(hash_node);
171 hash_node = NULL;
172
173 if (!macs_equal(binding->mac, binding->n_mac,
174 lport->macs, lport->n_macs)) {
ec78987f 175 sbrec_bindings_set_mac(binding,
4edcdcf4
RB
176 (const char **) lport->macs, lport->n_macs);
177 }
178 } else {
179 /* There is no binding for this logical port, so create one. */
180
3c78b3ca 181 binding = sbrec_bindings_insert(ctx->ovnsb_txn);
ec78987f
JP
182 sbrec_bindings_set_logical_port(binding, lport->name);
183 sbrec_bindings_set_mac(binding,
4edcdcf4
RB
184 (const char **) lport->macs, lport->n_macs);
185 }
186 }
187
188 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &bindings_hmap) {
189 hmap_remove(&bindings_hmap, &hash_node->node);
ec78987f 190 sbrec_bindings_delete(hash_node->binding);
4edcdcf4
RB
191 free(hash_node);
192 }
193 hmap_destroy(&bindings_hmap);
194}
195
196static void
197ovnnb_db_changed(struct nbd_context *ctx)
198{
c29734fc 199 VLOG_DBG("ovn-nb db contents have changed.");
4edcdcf4
RB
200
201 set_bindings(ctx);
ac0630a2
RB
202}
203
f93818dd
RB
204/*
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.
208 */
ac0630a2 209static void
ec78987f 210ovnsb_db_changed(struct nbd_context *ctx)
ac0630a2 211{
fc3113bc
RB
212 struct hmap lports_hmap;
213 const struct sbrec_bindings *binding;
214 const struct nbrec_logical_port *lport;
215
216 struct lport_hash_node {
217 struct hmap_node node;
218 const struct nbrec_logical_port *lport;
219 } *hash_node, *hash_node_next;
f93818dd
RB
220
221 VLOG_DBG("Recalculating port up states for ovn-nb db.");
222
fc3113bc 223 hmap_init(&lports_hmap);
f93818dd 224
fc3113bc
RB
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));
230 }
231
232 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
233 lport = NULL;
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;
238 break;
239 }
f93818dd
RB
240 }
241
f93818dd 242 if (!lport) {
fc3113bc
RB
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. */
f93818dd
RB
246 continue;
247 }
248
fc3113bc 249 if (*binding->chassis && (!lport->up || !*lport->up)) {
f93818dd
RB
250 bool up = true;
251 nbrec_logical_port_set_up(lport, &up, 1);
fc3113bc 252 } else if (!*binding->chassis && (!lport->up || *lport->up)) {
f93818dd
RB
253 bool up = false;
254 nbrec_logical_port_set_up(lport, &up, 1);
255 }
256 }
fc3113bc
RB
257
258 HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) {
259 hmap_remove(&lports_hmap, &hash_node->node);
260 free(hash_node);
261 }
262 hmap_destroy(&lports_hmap);
ac0630a2
RB
263}
264\f
265static const char *
266default_db(void)
267{
268 static char *def;
269 if (!def) {
270 def = xasprintf("unix:%s/db.sock", ovs_rundir());
271 }
272 return def;
273}
274
275static void
276parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
277{
278 enum {
67d9b930 279 DAEMON_OPTION_ENUMS,
ac0630a2
RB
280 VLOG_OPTION_ENUMS,
281 };
282 static const struct option long_options[] = {
ec78987f 283 {"ovnsb-db", required_argument, NULL, 'd'},
ac0630a2
RB
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'},
67d9b930 288 DAEMON_LONG_OPTIONS,
ac0630a2
RB
289 VLOG_LONG_OPTIONS,
290 STREAM_SSL_LONG_OPTIONS,
291 {NULL, 0, NULL, 0},
292 };
293 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
294
295 for (;;) {
296 int c;
297
298 c = getopt_long(argc, argv, short_options, long_options, NULL);
299 if (c == -1) {
300 break;
301 }
302
303 switch (c) {
67d9b930 304 DAEMON_OPTION_HANDLERS;
ac0630a2
RB
305 VLOG_OPTION_HANDLERS;
306 STREAM_SSL_OPTION_HANDLERS;
307
308 case 'd':
ec78987f 309 ovnsb_db = optarg;
ac0630a2
RB
310 break;
311
312 case 'D':
313 ovnnb_db = optarg;
314 break;
315
316 case 'h':
317 usage();
318 exit(EXIT_SUCCESS);
319
320 case 'o':
321 ovs_cmdl_print_options(long_options);
322 exit(EXIT_SUCCESS);
323
324 case 'V':
325 ovs_print_version(0, 0);
326 exit(EXIT_SUCCESS);
327
328 default:
329 break;
330 }
331 }
332
ec78987f
JP
333 if (!ovnsb_db) {
334 ovnsb_db = default_db();
ac0630a2
RB
335 }
336
337 if (!ovnnb_db) {
338 ovnnb_db = default_db();
339 }
340
341 free(short_options);
342}
343
344int
345main(int argc, char *argv[])
346{
347 extern struct vlog_module VLM_reconnect;
ec78987f 348 struct ovsdb_idl *ovnnb_idl, *ovnsb_idl;
ac0630a2
RB
349 unsigned int ovnnb_seqno, ovn_seqno;
350 int res = EXIT_SUCCESS;
f93818dd 351 struct nbd_context ctx = {
3c78b3ca 352 .ovnsb_txn = NULL,
f93818dd
RB
353 };
354 bool ovnnb_changes_pending = false;
355 bool ovn_changes_pending = false;
ac0630a2
RB
356
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);
67d9b930
RB
362
363 daemonize();
364
ac0630a2 365 nbrec_init();
ec78987f 366 sbrec_init();
ac0630a2
RB
367
368 /* We want to detect all changes to the ovn-nb db. */
f93818dd
RB
369 ctx.ovnnb_idl = ovnnb_idl = ovsdb_idl_create(ovnnb_db,
370 &nbrec_idl_class, true, true);
ac0630a2 371
91ae2065 372 /* There is only a small subset of changes to the ovn-sb db that ovn-northd
a0149f47 373 * has to care about, so we'll enable monitoring those directly. */
ec78987f
JP
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);
ac0630a2
RB
380
381 /*
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.
384 *
a0149f47
JP
385 * If the contents of the ovn-nb db change, the mappings to the ovn-sb
386 * db must be recalculated.
ac0630a2 387 *
a0149f47 388 * If the contents of the ovn-sb db change, it means the 'up' state of
91ae2065 389 * a port may have changed, as that's the only type of change ovn-northd is
a0149f47 390 * watching for.
ac0630a2
RB
391 */
392
393 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
ec78987f 394 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
ac0630a2
RB
395 for (;;) {
396 ovsdb_idl_run(ovnnb_idl);
ec78987f 397 ovsdb_idl_run(ovnsb_idl);
ac0630a2
RB
398
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));
403 res = EXIT_FAILURE;
404 break;
405 }
406
ec78987f
JP
407 if (!ovsdb_idl_is_alive(ovnsb_idl)) {
408 int retval = ovsdb_idl_get_last_error(ovnsb_idl);
ac0630a2 409 VLOG_ERR("%s: database connection failed (%s)",
ec78987f 410 ovnsb_db, ovs_retval_to_string(retval));
ac0630a2
RB
411 res = EXIT_FAILURE;
412 break;
413 }
414
415 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
416 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
f93818dd 417 ovnnb_changes_pending = true;
ac0630a2
RB
418 }
419
ec78987f
JP
420 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
421 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
f93818dd
RB
422 ovn_changes_pending = true;
423 }
424
425 /*
426 * If there are any pending changes, we delay recalculating the
427 * necessary updates until after an existing transaction finishes.
91ae2065
RB
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.
f93818dd
RB
432 */
433
3c78b3ca 434 if (ovnnb_changes_pending && !ctx.ovnsb_txn) {
f93818dd
RB
435 /*
436 * The OVN-nb db contents have changed, so create a transaction for
a0149f47 437 * updating the OVN-sb DB.
f93818dd 438 */
3c78b3ca 439 ctx.ovnsb_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
5da82071 440 ovsdb_idl_txn_add_comment(ctx.ovnsb_txn,
91ae2065 441 "ovn-northd: northbound db changed");
f93818dd
RB
442 ovnnb_db_changed(&ctx);
443 ovnnb_changes_pending = false;
444 }
445
446 if (ovn_changes_pending && !ctx.ovnnb_txn) {
447 /*
a0149f47 448 * The OVN-sb db contents have changed, so create a transaction for
f93818dd
RB
449 * updating the northbound DB.
450 */
451 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
5da82071 452 ovsdb_idl_txn_add_comment(ctx.ovnnb_txn,
91ae2065 453 "ovn-northd: southbound db changed");
ec78987f 454 ovnsb_db_changed(&ctx);
f93818dd
RB
455 ovn_changes_pending = false;
456 }
457
458 if (ctx.ovnnb_txn) {
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:
463 case TXN_INCOMPLETE:
464 /* Come back around and try to commit this transaction again */
465 break;
466 case TXN_ABORTED:
467 case TXN_TRY_AGAIN:
468 case TXN_NOT_LOCKED:
469 case TXN_ERROR:
470 /* Something went wrong, so try creating a new transaction. */
471 ovn_changes_pending = true;
472 case TXN_UNCHANGED:
473 case TXN_SUCCESS:
474 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
475 ctx.ovnnb_txn = NULL;
476 }
477 }
478
3c78b3ca 479 if (ctx.ovnsb_txn) {
f93818dd 480 enum ovsdb_idl_txn_status txn_status;
3c78b3ca 481 txn_status = ovsdb_idl_txn_commit(ctx.ovnsb_txn);
f93818dd
RB
482 switch (txn_status) {
483 case TXN_UNCOMMITTED:
484 case TXN_INCOMPLETE:
485 /* Come back around and try to commit this transaction again */
486 break;
487 case TXN_ABORTED:
488 case TXN_TRY_AGAIN:
489 case TXN_NOT_LOCKED:
490 case TXN_ERROR:
491 /* Something went wrong, so try creating a new transaction. */
492 ovnnb_changes_pending = true;
493 case TXN_UNCHANGED:
494 case TXN_SUCCESS:
3c78b3ca
JP
495 ovsdb_idl_txn_destroy(ctx.ovnsb_txn);
496 ctx.ovnsb_txn = NULL;
f93818dd 497 }
ac0630a2
RB
498 }
499
500 if (ovnnb_seqno == ovsdb_idl_get_seqno(ovnnb_idl) &&
ec78987f 501 ovn_seqno == ovsdb_idl_get_seqno(ovnsb_idl)) {
ac0630a2 502 ovsdb_idl_wait(ovnnb_idl);
ec78987f 503 ovsdb_idl_wait(ovnsb_idl);
f93818dd
RB
504 if (ctx.ovnnb_txn) {
505 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
506 }
3c78b3ca
JP
507 if (ctx.ovnsb_txn) {
508 ovsdb_idl_txn_wait(ctx.ovnsb_txn);
f93818dd 509 }
ac0630a2
RB
510 poll_block();
511 }
512 }
513
ec78987f 514 ovsdb_idl_destroy(ovnsb_idl);
ac0630a2
RB
515 ovsdb_idl_destroy(ovnnb_idl);
516
517 exit(res);
518}