]> git.proxmox.com Git - mirror_ovs.git/blob - ovn/ovn-nbd.c
ovn: Rename "ovn" database to "ovn-sb".
[mirror_ovs.git] / ovn / ovn-nbd.c
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"
22 #include "daemon.h"
23 #include "dirs.h"
24 #include "fatal-signal.h"
25 #include "hash.h"
26 #include "hmap.h"
27 #include "ovn/ovn-nb-idl.h"
28 #include "ovn/ovn-sb-idl.h"
29 #include "poll-loop.h"
30 #include "stream.h"
31 #include "stream-ssl.h"
32 #include "util.h"
33 #include "uuid.h"
34 #include "openvswitch/vlog.h"
35
36 VLOG_DEFINE_THIS_MODULE(ovn_nbd);
37
38 struct nbd_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 *ovn_txn;
43 };
44
45 static const char *ovnnb_db;
46 static const char *ovnsb_db;
47
48 static const char *default_db(void);
49
50 static void
51 usage(void)
52 {
53 printf("\
54 %s: OVN northbound management daemon\n\
55 usage: %s [OPTIONS]\n\
56 \n\
57 Options:\n\
58 --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
59 (default: %s)\n\
60 --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
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());
66 daemon_usage();
67 vlog_usage();
68 stream_usage("database", true, true, false);
69 }
70 \f
71 static int
72 compare_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 */
84 static bool
85 macs_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
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.
122 */
123 static void
124 set_bindings(struct nbd_context *ctx)
125 {
126 struct hmap bindings_hmap;
127 const struct sbrec_bindings *binding;
128 const struct nbrec_logical_port *lport;
129
130 struct binding_hash_node {
131 struct hmap_node node;
132 const struct sbrec_bindings *binding;
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
147 SBREC_BINDINGS_FOR_EACH(binding, ctx->ovnsb_idl) {
148 struct binding_hash_node *hash_node = xzalloc(sizeof *hash_node);
149
150 hash_node->binding = binding;
151 hmap_insert(&bindings_hmap, &hash_node->node,
152 hash_string(binding->logical_port, 0));
153 }
154
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)) {
159 break;
160 }
161 }
162
163 if (hash_node) {
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. */
167
168 binding = hash_node->binding;
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)) {
175 sbrec_bindings_set_mac(binding,
176 (const char **) lport->macs, lport->n_macs);
177 }
178 } else {
179 /* There is no binding for this logical port, so create one. */
180
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);
185 }
186 }
187
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);
191 free(hash_node);
192 }
193 hmap_destroy(&bindings_hmap);
194 }
195
196 static void
197 ovnnb_db_changed(struct nbd_context *ctx)
198 {
199 VLOG_DBG("ovn-nbd: ovn-nb db contents have changed.\n");
200
201 set_bindings(ctx);
202 }
203
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 */
209 static void
210 ovnsb_db_changed(struct nbd_context *ctx)
211 {
212 const struct sbrec_bindings *bindings;
213
214 VLOG_DBG("Recalculating port up states for ovn-nb db.");
215
216 SBREC_BINDINGS_FOR_EACH(bindings, ctx->ovnsb_idl) {
217 const struct nbrec_logical_port *lport;
218 struct uuid lport_uuid;
219
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);
223 continue;
224 }
225
226 lport = nbrec_logical_port_get_for_uuid(ctx->ovnnb_idl, &lport_uuid);
227 if (!lport) {
228 VLOG_WARN("No logical port '%s' found in OVN-nb db.",
229 bindings->logical_port);
230 continue;
231 }
232
233 if (*bindings->chassis && (!lport->up || !*lport->up)) {
234 bool up = true;
235 nbrec_logical_port_set_up(lport, &up, 1);
236 } else if (!*bindings->chassis && (!lport->up || *lport->up)) {
237 bool up = false;
238 nbrec_logical_port_set_up(lport, &up, 1);
239 }
240 }
241 }
242 \f
243 static const char *
244 default_db(void)
245 {
246 static char *def;
247 if (!def) {
248 def = xasprintf("unix:%s/db.sock", ovs_rundir());
249 }
250 return def;
251 }
252
253 static void
254 parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
255 {
256 enum {
257 DAEMON_OPTION_ENUMS,
258 VLOG_OPTION_ENUMS,
259 };
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'},
266 DAEMON_LONG_OPTIONS,
267 VLOG_LONG_OPTIONS,
268 STREAM_SSL_LONG_OPTIONS,
269 {NULL, 0, NULL, 0},
270 };
271 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
272
273 for (;;) {
274 int c;
275
276 c = getopt_long(argc, argv, short_options, long_options, NULL);
277 if (c == -1) {
278 break;
279 }
280
281 switch (c) {
282 DAEMON_OPTION_HANDLERS;
283 VLOG_OPTION_HANDLERS;
284 STREAM_SSL_OPTION_HANDLERS;
285
286 case 'd':
287 ovnsb_db = optarg;
288 break;
289
290 case 'D':
291 ovnnb_db = optarg;
292 break;
293
294 case 'h':
295 usage();
296 exit(EXIT_SUCCESS);
297
298 case 'o':
299 ovs_cmdl_print_options(long_options);
300 exit(EXIT_SUCCESS);
301
302 case 'V':
303 ovs_print_version(0, 0);
304 exit(EXIT_SUCCESS);
305
306 default:
307 break;
308 }
309 }
310
311 if (!ovnsb_db) {
312 ovnsb_db = default_db();
313 }
314
315 if (!ovnnb_db) {
316 ovnnb_db = default_db();
317 }
318
319 free(short_options);
320 }
321
322 int
323 main(int argc, char *argv[])
324 {
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 = {
330 .ovn_txn = NULL,
331 };
332 bool ovnnb_changes_pending = false;
333 bool ovn_changes_pending = false;
334
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);
340
341 daemonize();
342
343 nbrec_init();
344 sbrec_init();
345
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);
349
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);
358
359 /*
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.
362 *
363 * If the contents of the ovn-nb db change, the mappings to the ovn db must
364 * be recalculated.
365 *
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
368 * for.
369 */
370
371 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
372 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
373 for (;;) {
374 ovsdb_idl_run(ovnnb_idl);
375 ovsdb_idl_run(ovnsb_idl);
376
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));
381 res = EXIT_FAILURE;
382 break;
383 }
384
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));
389 res = EXIT_FAILURE;
390 break;
391 }
392
393 if (ovnnb_seqno != ovsdb_idl_get_seqno(ovnnb_idl)) {
394 ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl);
395 ovnnb_changes_pending = true;
396 }
397
398 if (ovn_seqno != ovsdb_idl_get_seqno(ovnsb_idl)) {
399 ovn_seqno = ovsdb_idl_get_seqno(ovnsb_idl);
400 ovn_changes_pending = true;
401 }
402
403 /*
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.
410 */
411
412 if (ovnnb_changes_pending && !ctx.ovn_txn) {
413 /*
414 * The OVN-nb db contents have changed, so create a transaction for
415 * updating the OVN DB.
416 */
417 ctx.ovn_txn = ovsdb_idl_txn_create(ctx.ovnsb_idl);
418 ovnnb_db_changed(&ctx);
419 ovnnb_changes_pending = false;
420 }
421
422 if (ovn_changes_pending && !ctx.ovnnb_txn) {
423 /*
424 * The OVN db contents have changed, so create a transaction for
425 * updating the northbound DB.
426 */
427 ctx.ovnnb_txn = ovsdb_idl_txn_create(ctx.ovnnb_idl);
428 ovnsb_db_changed(&ctx);
429 ovn_changes_pending = false;
430 }
431
432 if (ctx.ovnnb_txn) {
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:
437 case TXN_INCOMPLETE:
438 /* Come back around and try to commit this transaction again */
439 break;
440 case TXN_ABORTED:
441 case TXN_TRY_AGAIN:
442 case TXN_NOT_LOCKED:
443 case TXN_ERROR:
444 /* Something went wrong, so try creating a new transaction. */
445 ovn_changes_pending = true;
446 case TXN_UNCHANGED:
447 case TXN_SUCCESS:
448 ovsdb_idl_txn_destroy(ctx.ovnnb_txn);
449 ctx.ovnnb_txn = NULL;
450 }
451 }
452
453 if (ctx.ovn_txn) {
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:
458 case TXN_INCOMPLETE:
459 /* Come back around and try to commit this transaction again */
460 break;
461 case TXN_ABORTED:
462 case TXN_TRY_AGAIN:
463 case TXN_NOT_LOCKED:
464 case TXN_ERROR:
465 /* Something went wrong, so try creating a new transaction. */
466 ovnnb_changes_pending = true;
467 case TXN_UNCHANGED:
468 case TXN_SUCCESS:
469 ovsdb_idl_txn_destroy(ctx.ovn_txn);
470 ctx.ovn_txn = NULL;
471 }
472 }
473
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);
478 if (ctx.ovnnb_txn) {
479 ovsdb_idl_txn_wait(ctx.ovnnb_txn);
480 }
481 if (ctx.ovn_txn) {
482 ovsdb_idl_txn_wait(ctx.ovn_txn);
483 }
484 poll_block();
485 }
486 }
487
488 ovsdb_idl_destroy(ovnsb_idl);
489 ovsdb_idl_destroy(ovnnb_idl);
490
491 exit(res);
492 }