Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2015, 2016 Nicira, Inc.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at:
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #include <config.h>
18 : :
19 : : #include <ctype.h>
20 : : #include <errno.h>
21 : : #include <float.h>
22 : : #include <getopt.h>
23 : : #include <inttypes.h>
24 : : #include <signal.h>
25 : : #include <stdarg.h>
26 : : #include <stdlib.h>
27 : : #include <string.h>
28 : : #include <unistd.h>
29 : :
30 : : #include "command-line.h"
31 : : #include "compiler.h"
32 : : #include "db-ctl-base.h"
33 : : #include "dirs.h"
34 : : #include "fatal-signal.h"
35 : : #include "openvswitch/dynamic-string.h"
36 : : #include "openvswitch/json.h"
37 : : #include "openvswitch/shash.h"
38 : : #include "openvswitch/vlog.h"
39 : : #include "ovn/lib/ovn-sb-idl.h"
40 : : #include "ovn/lib/ovn-util.h"
41 : : #include "ovsdb-data.h"
42 : : #include "ovsdb-idl.h"
43 : : #include "poll-loop.h"
44 : : #include "process.h"
45 : : #include "sset.h"
46 : : #include "stream-ssl.h"
47 : : #include "stream.h"
48 : : #include "table.h"
49 : : #include "timeval.h"
50 : : #include "util.h"
51 : :
52 : 362 : VLOG_DEFINE_THIS_MODULE(sbctl);
53 : :
54 : : struct sbctl_context;
55 : :
56 : : /* --db: The database server to contact. */
57 : : static const char *db;
58 : :
59 : : /* --oneline: Write each command's output as a single line? */
60 : : static bool oneline;
61 : :
62 : : /* --dry-run: Do not commit any changes. */
63 : : static bool dry_run;
64 : :
65 : : /* --timeout: Time to wait for a connection to 'db'. */
66 : : static int timeout;
67 : :
68 : : /* Format for table output. */
69 : : static struct table_style table_style = TABLE_STYLE_DEFAULT;
70 : :
71 : : /* The IDL we're using and the current transaction, if any.
72 : : * This is for use by sbctl_exit() only, to allow it to clean up.
73 : : * Other code should use its context arguments. */
74 : : static struct ovsdb_idl *the_idl;
75 : : static struct ovsdb_idl_txn *the_idl_txn;
76 : : OVS_NO_RETURN static void sbctl_exit(int status);
77 : :
78 : : static void sbctl_cmd_init(void);
79 : : OVS_NO_RETURN static void usage(void);
80 : : static void parse_options(int argc, char *argv[], struct shash *local_options);
81 : : static void run_prerequisites(struct ctl_command[], size_t n_commands,
82 : : struct ovsdb_idl *);
83 : : static bool do_sbctl(const char *args, struct ctl_command *, size_t n,
84 : : struct ovsdb_idl *);
85 : :
86 : : int
87 : 181 : main(int argc, char *argv[])
88 : : {
89 : : struct ovsdb_idl *idl;
90 : : struct ctl_command *commands;
91 : : struct shash local_options;
92 : : unsigned int seqno;
93 : : size_t n_commands;
94 : : char *args;
95 : :
96 : 181 : set_program_name(argv[0]);
97 : 181 : fatal_ignore_sigpipe();
98 : 181 : vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
99 : 181 : vlog_set_levels_from_string_assert("reconnect:warn");
100 : 181 : sbrec_init();
101 : :
102 : 181 : sbctl_cmd_init();
103 : :
104 : : /* Log our arguments. This is often valuable for debugging systems. */
105 : 181 : args = process_escape_args(argv);
106 [ + + ][ + + ]: 181 : VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args);
107 : :
108 : : /* Parse command line. */
109 : 181 : shash_init(&local_options);
110 : 181 : parse_options(argc, argv, &local_options);
111 : 181 : commands = ctl_parse_commands(argc - optind, argv + optind, &local_options,
112 : : &n_commands);
113 : :
114 [ - + ]: 181 : if (timeout) {
115 : 0 : time_alarm(timeout);
116 : : }
117 : :
118 : : /* Initialize IDL. */
119 : 181 : idl = the_idl = ovsdb_idl_create(db, &sbrec_idl_class, false, false);
120 : 181 : run_prerequisites(commands, n_commands, idl);
121 : :
122 : : /* Execute the commands.
123 : : *
124 : : * 'seqno' is the database sequence number for which we last tried to
125 : : * execute our transaction. There's no point in trying to commit more than
126 : : * once for any given sequence number, because if the transaction fails
127 : : * it's because the database changed and we need to obtain an up-to-date
128 : : * view of the database before we try the transaction again. */
129 : 181 : seqno = ovsdb_idl_get_seqno(idl);
130 : : for (;;) {
131 : 482 : ovsdb_idl_run(idl);
132 [ - + ]: 482 : if (!ovsdb_idl_is_alive(idl)) {
133 : 0 : int retval = ovsdb_idl_get_last_error(idl);
134 : 0 : ctl_fatal("%s: database connection failed (%s)",
135 : : db, ovs_retval_to_string(retval));
136 : : }
137 : :
138 [ + + ]: 482 : if (seqno != ovsdb_idl_get_seqno(idl)) {
139 : 181 : seqno = ovsdb_idl_get_seqno(idl);
140 [ + - ]: 181 : if (do_sbctl(args, commands, n_commands, idl)) {
141 : 181 : free(args);
142 : 181 : exit(EXIT_SUCCESS);
143 : : }
144 : : }
145 : :
146 [ + - ]: 301 : if (seqno == ovsdb_idl_get_seqno(idl)) {
147 : 301 : ovsdb_idl_wait(idl);
148 : 301 : poll_block();
149 : : }
150 : 301 : }
151 : : }
152 : :
153 : : static void
154 : 181 : parse_options(int argc, char *argv[], struct shash *local_options)
155 : : {
156 : : enum {
157 : : OPT_DB = UCHAR_MAX + 1,
158 : : OPT_ONELINE,
159 : : OPT_NO_SYSLOG,
160 : : OPT_DRY_RUN,
161 : : OPT_LOCAL,
162 : : OPT_COMMANDS,
163 : : OPT_OPTIONS,
164 : : VLOG_OPTION_ENUMS,
165 : : TABLE_OPTION_ENUMS
166 : : };
167 : : static const struct option global_long_options[] = {
168 : : {"db", required_argument, NULL, OPT_DB},
169 : : {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG},
170 : : {"dry-run", no_argument, NULL, OPT_DRY_RUN},
171 : : {"oneline", no_argument, NULL, OPT_ONELINE},
172 : : {"timeout", required_argument, NULL, 't'},
173 : : {"help", no_argument, NULL, 'h'},
174 : : {"commands", no_argument, NULL, OPT_COMMANDS},
175 : : {"options", no_argument, NULL, OPT_OPTIONS},
176 : : {"version", no_argument, NULL, 'V'},
177 : : VLOG_LONG_OPTIONS,
178 : : STREAM_SSL_LONG_OPTIONS,
179 : : TABLE_LONG_OPTIONS,
180 : : {NULL, 0, NULL, 0},
181 : : };
182 : 181 : const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
183 : : char *tmp, *short_options;
184 : :
185 : : struct option *options;
186 : : size_t allocated_options;
187 : : size_t n_options;
188 : : size_t i;
189 : :
190 : 181 : tmp = ovs_cmdl_long_options_to_short_options(global_long_options);
191 : 181 : short_options = xasprintf("+%s", tmp);
192 : 181 : free(tmp);
193 : :
194 : : /* We want to parse both global and command-specific options here, but
195 : : * getopt_long() isn't too convenient for the job. We copy our global
196 : : * options into a dynamic array, then append all of the command-specific
197 : : * options. */
198 : 181 : options = xmemdup(global_long_options, sizeof global_long_options);
199 : 181 : allocated_options = ARRAY_SIZE(global_long_options);
200 : 181 : n_options = n_global_long_options;
201 : 181 : ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL);
202 : 181 : table_style.format = TF_LIST;
203 : :
204 : : for (;;) {
205 : : int idx;
206 : : int c;
207 : :
208 : 244 : c = getopt_long(argc, argv, short_options, options, &idx);
209 [ + + ]: 244 : if (c == -1) {
210 : 181 : break;
211 : : }
212 : :
213 [ - - - - : 63 : switch (c) {
+ - - - -
- - - - -
+ + + - -
- - - -
- ]
214 : : case OPT_DB:
215 : 0 : db = optarg;
216 : 0 : break;
217 : :
218 : : case OPT_ONELINE:
219 : 0 : oneline = true;
220 : 0 : break;
221 : :
222 : : case OPT_NO_SYSLOG:
223 : 0 : vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN);
224 : 0 : break;
225 : :
226 : : case OPT_DRY_RUN:
227 : 0 : dry_run = true;
228 : 0 : break;
229 : :
230 : : case OPT_LOCAL:
231 [ - + ]: 33 : if (shash_find(local_options, options[idx].name)) {
232 : 0 : ctl_fatal("'%s' option specified multiple times",
233 : 0 : options[idx].name);
234 : : }
235 : 33 : shash_add_nocopy(local_options,
236 : 33 : xasprintf("--%s", options[idx].name),
237 : 33 : nullable_xstrdup(optarg));
238 : 33 : break;
239 : :
240 : : case 'h':
241 : 0 : usage();
242 : :
243 : : case OPT_COMMANDS:
244 : 0 : ctl_print_commands();
245 : :
246 : : case OPT_OPTIONS:
247 : 0 : ctl_print_options(global_long_options);
248 : :
249 : : case 'V':
250 : 0 : ovs_print_version(0, 0);
251 : 0 : printf("DB Schema %s\n", sbrec_get_db_version());
252 : 0 : exit(EXIT_SUCCESS);
253 : :
254 : : case 't':
255 : 0 : timeout = strtoul(optarg, NULL, 10);
256 [ # # ]: 0 : if (timeout < 0) {
257 : 0 : ctl_fatal("value %s on -t or --timeout is invalid", optarg);
258 : : }
259 : 0 : break;
260 : :
261 : 0 : VLOG_OPTION_HANDLERS
262 : 30 : TABLE_OPTION_HANDLERS(&table_style)
263 : 0 : STREAM_SSL_OPTION_HANDLERS
264 : :
265 : : case '?':
266 : 0 : exit(EXIT_FAILURE);
267 : :
268 : : default:
269 : 0 : abort();
270 : : }
271 : 63 : }
272 : 181 : free(short_options);
273 : :
274 [ + - ]: 181 : if (!db) {
275 : 181 : db = default_sb_db();
276 : : }
277 : :
278 [ + + ]: 1086 : for (i = n_global_long_options; options[i].name; i++) {
279 : 905 : free(CONST_CAST(char *, options[i].name));
280 : : }
281 : 181 : free(options);
282 : 181 : }
283 : :
284 : : static void
285 : 0 : usage(void)
286 : : {
287 : 0 : printf("\
288 : : %s: OVN southbound DB management utility\n\
289 : : \n\
290 : : For debugging and testing only, not for use in production.\n\
291 : : \n\
292 : : usage: %s [OPTIONS] COMMAND [ARG...]\n\
293 : : \n\
294 : : General commands:\n\
295 : : show print overview of database contents\n\
296 : : \n\
297 : : Chassis commands:\n\
298 : : chassis-add CHASSIS ENCAP-TYPE ENCAP-IP create a new chassis named\n\
299 : : CHASSIS with ENCAP-TYPE tunnels\n\
300 : : and ENCAP-IP\n\
301 : : chassis-del CHASSIS delete CHASSIS and all of its encaps\n\
302 : : and gateway_ports\n\
303 : : \n\
304 : : Port binding commands:\n\
305 : : lsp-bind PORT CHASSIS bind logical port PORT to CHASSIS\n\
306 : : lsp-unbind PORT reset the port binding of logical port PORT\n\
307 : : \n\
308 : : Logical flow commands:\n\
309 : : lflow-list [DATAPATH] List logical flows for all or a single datapath\n\
310 : : dump-flows [DATAPATH] Alias for lflow-list\n\
311 : : \n\
312 : : %s\
313 : : \n\
314 : : Options:\n\
315 : : --db=DATABASE connect to DATABASE\n\
316 : : (default: %s)\n\
317 : : -t, --timeout=SECS wait at most SECS seconds\n\
318 : : --dry-run do not commit changes to database\n\
319 : : --oneline print exactly one line of output per command\n",
320 : : program_name, program_name, ctl_get_db_cmd_usage(),
321 : : default_sb_db());
322 : 0 : vlog_usage();
323 : 0 : printf("\
324 : : --no-syslog equivalent to --verbose=sbctl:syslog:warn\n");
325 : 0 : printf("\n\
326 : : Other options:\n\
327 : : -h, --help display this help message\n\
328 : : -V, --version display version information\n");
329 : 0 : stream_usage("database", true, true, false);
330 : 0 : exit(EXIT_SUCCESS);
331 : : }
332 : :
333 : :
334 : : /* ovs-sbctl specific context. Inherits the 'struct ctl_context' as base. */
335 : : struct sbctl_context {
336 : : struct ctl_context base;
337 : :
338 : : /* A cache of the contents of the database.
339 : : *
340 : : * A command that needs to use any of this information must first call
341 : : * sbctl_context_populate_cache(). A command that changes anything that
342 : : * could invalidate the cache must either call
343 : : * sbctl_context_invalidate_cache() or manually update the cache to
344 : : * maintain its correctness. */
345 : : bool cache_valid;
346 : : /* Maps from chassis name to struct sbctl_chassis. */
347 : : struct shash chassis;
348 : : /* Maps from lport name to struct sbctl_port_binding. */
349 : : struct shash port_bindings;
350 : : };
351 : :
352 : : /* Casts 'base' into 'struct sbctl_context'. */
353 : : static struct sbctl_context *
354 : 420 : sbctl_context_cast(struct ctl_context *base)
355 : : {
356 : 420 : return CONTAINER_OF(base, struct sbctl_context, base);
357 : : }
358 : :
359 : : struct sbctl_chassis {
360 : : const struct sbrec_chassis *ch_cfg;
361 : : };
362 : :
363 : : struct sbctl_port_binding {
364 : : const struct sbrec_port_binding *bd_cfg;
365 : : };
366 : :
367 : : static void
368 : 378 : sbctl_context_invalidate_cache(struct ctl_context *ctx)
369 : : {
370 : 378 : struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
371 : :
372 [ + + ]: 378 : if (!sbctl_ctx->cache_valid) {
373 : 357 : return;
374 : : }
375 : 21 : sbctl_ctx->cache_valid = false;
376 : 21 : shash_destroy_free_data(&sbctl_ctx->chassis);
377 : 21 : shash_destroy_free_data(&sbctl_ctx->port_bindings);
378 : : }
379 : :
380 : : static void
381 : 21 : sbctl_context_populate_cache(struct ctl_context *ctx)
382 : : {
383 : 21 : struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
384 : : const struct sbrec_chassis *chassis_rec;
385 : : const struct sbrec_port_binding *port_binding_rec;
386 : : struct sset chassis, port_bindings;
387 : :
388 [ - + ]: 21 : if (sbctl_ctx->cache_valid) {
389 : : /* Cache is already populated. */
390 : 0 : return;
391 : : }
392 : 21 : sbctl_ctx->cache_valid = true;
393 : 21 : shash_init(&sbctl_ctx->chassis);
394 : 21 : shash_init(&sbctl_ctx->port_bindings);
395 : 21 : sset_init(&chassis);
396 [ + + ]: 49 : SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->idl) {
397 : : struct sbctl_chassis *ch;
398 : :
399 [ - + ]: 28 : if (!sset_add(&chassis, chassis_rec->name)) {
400 [ # # ]: 0 : VLOG_WARN("database contains duplicate chassis name (%s)",
401 : : chassis_rec->name);
402 : 0 : continue;
403 : : }
404 : :
405 : 28 : ch = xmalloc(sizeof *ch);
406 : 28 : ch->ch_cfg = chassis_rec;
407 : 28 : shash_add(&sbctl_ctx->chassis, chassis_rec->name, ch);
408 : : }
409 : 21 : sset_destroy(&chassis);
410 : :
411 : 21 : sset_init(&port_bindings);
412 [ + + ]: 49 : SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->idl) {
413 : : struct sbctl_port_binding *bd;
414 : :
415 [ - + ]: 28 : if (!sset_add(&port_bindings, port_binding_rec->logical_port)) {
416 [ # # ]: 0 : VLOG_WARN("database contains duplicate port binding for logical "
417 : : "port (%s)",
418 : : port_binding_rec->logical_port);
419 : 0 : continue;
420 : : }
421 : :
422 : 28 : bd = xmalloc(sizeof *bd);
423 : 28 : bd->bd_cfg = port_binding_rec;
424 : 28 : shash_add(&sbctl_ctx->port_bindings, port_binding_rec->logical_port,
425 : : bd);
426 : : }
427 : 21 : sset_destroy(&port_bindings);
428 : : }
429 : :
430 : : static void
431 : 9 : check_conflicts(struct sbctl_context *sbctl_ctx, const char *name,
432 : : char *msg)
433 : : {
434 [ - + ]: 9 : if (shash_find(&sbctl_ctx->chassis, name)) {
435 : 0 : ctl_fatal("%s because a chassis named %s already exists",
436 : : msg, name);
437 : : }
438 : 9 : free(msg);
439 : 9 : }
440 : :
441 : : static struct sbctl_chassis *
442 : 12 : find_chassis(struct sbctl_context *sbctl_ctx, const char *name,
443 : : bool must_exist)
444 : : {
445 : : struct sbctl_chassis *sbctl_ch;
446 : :
447 [ - + ]: 12 : ovs_assert(sbctl_ctx->cache_valid);
448 : :
449 : 12 : sbctl_ch = shash_find_data(&sbctl_ctx->chassis, name);
450 [ + - ][ - + ]: 12 : if (must_exist && !sbctl_ch) {
451 : 0 : ctl_fatal("no chassis named %s", name);
452 : : }
453 : :
454 : 12 : return sbctl_ch;
455 : : }
456 : :
457 : : static struct sbctl_port_binding *
458 : 10 : find_port_binding(struct sbctl_context *sbctl_ctx, const char *name,
459 : : bool must_exist)
460 : : {
461 : : struct sbctl_port_binding *bd;
462 : :
463 [ - + ]: 10 : ovs_assert(sbctl_ctx->cache_valid);
464 : :
465 : 10 : bd = shash_find_data(&sbctl_ctx->port_bindings, name);
466 [ + - ][ - + ]: 10 : if (must_exist && !bd) {
467 : 0 : ctl_fatal("no port named %s", name);
468 : : }
469 : :
470 : 10 : return bd;
471 : : }
472 : :
473 : : static void
474 : 30 : pre_get_info(struct ctl_context *ctx)
475 : : {
476 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_name);
477 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_encaps);
478 : :
479 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_type);
480 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_ip);
481 : :
482 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_logical_port);
483 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis);
484 : :
485 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_logical_datapath);
486 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_pipeline);
487 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_actions);
488 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_priority);
489 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_table_id);
490 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_match);
491 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_external_ids);
492 : :
493 : 30 : ovsdb_idl_add_column(ctx->idl, &sbrec_datapath_binding_col_external_ids);
494 : 30 : }
495 : :
496 : : static struct cmd_show_table cmd_show_tables[] = {
497 : : {&sbrec_table_chassis,
498 : : &sbrec_chassis_col_name,
499 : : {&sbrec_chassis_col_hostname,
500 : : &sbrec_chassis_col_encaps,
501 : : NULL},
502 : : {&sbrec_table_port_binding,
503 : : &sbrec_port_binding_col_logical_port,
504 : : &sbrec_port_binding_col_chassis}},
505 : :
506 : : {&sbrec_table_encap,
507 : : &sbrec_encap_col_type,
508 : : {&sbrec_encap_col_ip,
509 : : &sbrec_encap_col_options,
510 : : NULL},
511 : : {NULL, NULL, NULL}},
512 : :
513 : : {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}},
514 : : };
515 : :
516 : : static void
517 : 33 : sbctl_init(struct ctl_context *ctx OVS_UNUSED)
518 : : {
519 : 33 : }
520 : :
521 : : static void
522 : 9 : cmd_chassis_add(struct ctl_context *ctx)
523 : : {
524 : 9 : struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
525 : 9 : bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
526 : : const char *ch_name, *encap_types, *encap_ip;
527 : :
528 : 9 : ch_name = ctx->argv[1];
529 : 9 : encap_types = ctx->argv[2];
530 : 9 : encap_ip = ctx->argv[3];
531 : :
532 : 9 : sbctl_context_populate_cache(ctx);
533 [ - + ]: 9 : if (may_exist) {
534 : : struct sbctl_chassis *sbctl_ch;
535 : :
536 : 0 : sbctl_ch = find_chassis(sbctl_ctx, ch_name, false);
537 [ # # ]: 0 : if (sbctl_ch) {
538 : 0 : return;
539 : : }
540 : : }
541 : 9 : check_conflicts(sbctl_ctx, ch_name,
542 : : xasprintf("cannot create a chassis named %s", ch_name));
543 : :
544 : : struct sset encap_set;
545 : 9 : sset_from_delimited_string(&encap_set, encap_types, ",");
546 : :
547 : 9 : size_t n_encaps = sset_count(&encap_set);
548 : 9 : struct sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps);
549 : 9 : const struct smap options = SMAP_CONST1(&options, "csum", "true");
550 : : const char *encap_type;
551 : 9 : int i = 0;
552 [ + - ][ + + ]: 20 : SSET_FOR_EACH (encap_type, &encap_set){
[ + + ]
553 : 11 : encaps[i] = sbrec_encap_insert(ctx->txn);
554 : :
555 : 11 : sbrec_encap_set_type(encaps[i], encap_type);
556 : 11 : sbrec_encap_set_ip(encaps[i], encap_ip);
557 : 11 : sbrec_encap_set_options(encaps[i], &options);
558 : 11 : i++;
559 : : }
560 : 9 : sset_destroy(&encap_set);
561 : :
562 : 9 : struct sbrec_chassis *ch = sbrec_chassis_insert(ctx->txn);
563 : 9 : sbrec_chassis_set_name(ch, ch_name);
564 : 9 : sbrec_chassis_set_encaps(ch, encaps, n_encaps);
565 : 9 : free(encaps);
566 : :
567 : 9 : sbctl_context_invalidate_cache(ctx);
568 : : }
569 : :
570 : : static void
571 : 2 : cmd_chassis_del(struct ctl_context *ctx)
572 : : {
573 : 2 : struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
574 : 2 : bool must_exist = !shash_find(&ctx->options, "--if-exists");
575 : : struct sbctl_chassis *sbctl_ch;
576 : :
577 : 2 : sbctl_context_populate_cache(ctx);
578 : 2 : sbctl_ch = find_chassis(sbctl_ctx, ctx->argv[1], must_exist);
579 [ + - ]: 2 : if (sbctl_ch) {
580 [ + - ]: 2 : if (sbctl_ch->ch_cfg) {
581 : : size_t i;
582 : :
583 [ + + ]: 4 : for (i = 0; i < sbctl_ch->ch_cfg->n_encaps; i++) {
584 : 2 : sbrec_encap_delete(sbctl_ch->ch_cfg->encaps[i]);
585 : : }
586 : 2 : sbrec_chassis_delete(sbctl_ch->ch_cfg);
587 : : }
588 : 2 : shash_find_and_delete(&sbctl_ctx->chassis, ctx->argv[1]);
589 : 2 : free(sbctl_ch);
590 : : }
591 : 2 : }
592 : :
593 : : static void
594 : 10 : cmd_lsp_bind(struct ctl_context *ctx)
595 : : {
596 : 10 : struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
597 : 10 : bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
598 : : struct sbctl_chassis *sbctl_ch;
599 : : struct sbctl_port_binding *sbctl_bd;
600 : : char *lport_name, *ch_name;
601 : :
602 : : /* port_binding must exist, chassis must exist! */
603 : 10 : lport_name = ctx->argv[1];
604 : 10 : ch_name = ctx->argv[2];
605 : :
606 : 10 : sbctl_context_populate_cache(ctx);
607 : 10 : sbctl_bd = find_port_binding(sbctl_ctx, lport_name, true);
608 : 10 : sbctl_ch = find_chassis(sbctl_ctx, ch_name, true);
609 : :
610 [ - + ]: 10 : if (sbctl_bd->bd_cfg->chassis) {
611 [ # # ][ # # ]: 0 : if (may_exist && sbctl_bd->bd_cfg->chassis == sbctl_ch->ch_cfg) {
612 : 0 : return;
613 : : } else {
614 : 0 : ctl_fatal("lport (%s) has already been binded to chassis (%s)",
615 : 0 : lport_name, sbctl_bd->bd_cfg->chassis->name);
616 : : }
617 : : }
618 : 10 : sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, sbctl_ch->ch_cfg);
619 : 10 : sbctl_context_invalidate_cache(ctx);
620 : : }
621 : :
622 : : static void
623 : 0 : cmd_lsp_unbind(struct ctl_context *ctx)
624 : : {
625 : 0 : struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
626 : 0 : bool must_exist = !shash_find(&ctx->options, "--if-exists");
627 : : struct sbctl_port_binding *sbctl_bd;
628 : : char *lport_name;
629 : :
630 : 0 : lport_name = ctx->argv[1];
631 : 0 : sbctl_context_populate_cache(ctx);
632 : 0 : sbctl_bd = find_port_binding(sbctl_ctx, lport_name, must_exist);
633 [ # # ]: 0 : if (sbctl_bd) {
634 : 0 : sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, NULL);
635 : : }
636 : 0 : }
637 : :
638 : : enum {
639 : : PL_INGRESS,
640 : : PL_EGRESS,
641 : : };
642 : :
643 : : /* Help ensure we catch any future pipeline values */
644 : : static int
645 : 18812 : pipeline_encode(const char *pl)
646 : : {
647 [ + + ]: 18812 : if (!strcmp(pl, "ingress")) {
648 : 14506 : return PL_INGRESS;
649 [ + - ]: 4306 : } else if (!strcmp(pl, "egress")) {
650 : 4306 : return PL_EGRESS;
651 : : }
652 : :
653 : 0 : OVS_NOT_REACHED();
654 : : }
655 : :
656 : : static int
657 : 9406 : lflow_cmp(const void *lf1_, const void *lf2_)
658 : : {
659 : 9406 : const struct sbrec_logical_flow *const *lf1p = lf1_;
660 : 9406 : const struct sbrec_logical_flow *const *lf2p = lf2_;
661 : 9406 : const struct sbrec_logical_flow *lf1 = *lf1p;
662 : 9406 : const struct sbrec_logical_flow *lf2 = *lf2p;
663 : :
664 : 9406 : int pl1 = pipeline_encode(lf1->pipeline);
665 : 9406 : int pl2 = pipeline_encode(lf2->pipeline);
666 : :
667 : : #define CMP(expr) \
668 : : do { \
669 : : int res; \
670 : : res = (expr); \
671 : : if (res) { \
672 : : return res; \
673 : : } \
674 : : } while (0)
675 : :
676 [ + + ]: 9406 : CMP(uuid_compare_3way(&lf1->logical_datapath->header_.uuid,
677 : : &lf2->logical_datapath->header_.uuid));
678 [ + + ]: 7131 : CMP(pl1 - pl2);
679 [ + + ][ + + ]: 6144 : CMP(lf1->table_id > lf2->table_id ? 1 :
[ + + ]
680 : : (lf1->table_id < lf2->table_id ? -1 : 0));
681 [ + + ][ + + ]: 2776 : CMP(lf1->priority > lf2->priority ? -1 :
682 : : (lf1->priority < lf2->priority ? 1 : 0));
683 [ + - ]: 1772 : CMP(strcmp(lf1->match, lf2->match));
684 : :
685 : : #undef CMP
686 : :
687 : 0 : return 0;
688 : : }
689 : :
690 : : static void
691 : 9 : cmd_lflow_list(struct ctl_context *ctx)
692 : : {
693 [ - + ]: 9 : const char *datapath = ctx->argc == 2 ? ctx->argv[1] : NULL;
694 : 9 : struct uuid datapath_uuid = { .parts = { 0, }};
695 : : const struct sbrec_logical_flow **lflows;
696 : : const struct sbrec_logical_flow *lflow;
697 : 9 : size_t n_flows = 0, n_capacity = 64;
698 : :
699 [ - + ][ # # ]: 9 : if (datapath && !uuid_from_string(&datapath_uuid, datapath)) {
700 [ # # ]: 0 : VLOG_ERR("Invalid format of datapath UUID");
701 : 0 : return;
702 : : }
703 : :
704 : 9 : lflows = xmalloc(sizeof *lflows * n_capacity);
705 [ + + ]: 1479 : SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) {
706 [ + + ]: 1470 : if (n_flows == n_capacity) {
707 : 13 : lflows = x2nrealloc(lflows, &n_capacity, sizeof *lflows);
708 : : }
709 : 1470 : lflows[n_flows] = lflow;
710 : 1470 : n_flows++;
711 : : }
712 : :
713 : 9 : qsort(lflows, n_flows, sizeof *lflows, lflow_cmp);
714 : :
715 : 9 : const char *cur_pipeline = "";
716 : : size_t i;
717 [ + + ]: 1479 : for (i = 0; i < n_flows; i++) {
718 : 1470 : lflow = lflows[i];
719 [ - + ][ # # ]: 1470 : if (datapath && !uuid_equals(&datapath_uuid,
720 : 0 : &lflow->logical_datapath->header_.uuid)) {
721 : 0 : continue;
722 : : }
723 [ + + ]: 1470 : if (strcmp(cur_pipeline, lflow->pipeline)) {
724 : 52 : printf("Datapath: \"%s\" ("UUID_FMT") Pipeline: %s\n",
725 : 52 : smap_get_def(&lflow->logical_datapath->external_ids,
726 : : "name", ""),
727 : 156 : UUID_ARGS(&lflow->logical_datapath->header_.uuid),
728 : : lflow->pipeline);
729 : 52 : cur_pipeline = lflow->pipeline;
730 : : }
731 : :
732 : 1470 : printf(" table=%-2" PRId64 "(%-19s), priority=%-5" PRId64
733 : : ", match=(%s), action=(%s)\n",
734 : : lflow->table_id,
735 : : smap_get_def(&lflow->external_ids, "stage-name", ""),
736 : : lflow->priority, lflow->match, lflow->actions);
737 : : }
738 : :
739 : 9 : free(lflows);
740 : : }
741 : :
742 : :
743 : : static const struct ctl_table_class tables[] = {
744 : : {&sbrec_table_sb_global,
745 : : {{&sbrec_table_sb_global, NULL, NULL},
746 : : {NULL, NULL, NULL}}},
747 : :
748 : : {&sbrec_table_chassis,
749 : : {{&sbrec_table_chassis, &sbrec_chassis_col_name, NULL},
750 : : {NULL, NULL, NULL}}},
751 : :
752 : : {&sbrec_table_encap,
753 : : {{NULL, NULL, NULL},
754 : : {NULL, NULL, NULL}}},
755 : :
756 : : {&sbrec_table_logical_flow,
757 : : {{&sbrec_table_logical_flow, NULL,
758 : : &sbrec_logical_flow_col_logical_datapath},
759 : : {NULL, NULL, NULL}}},
760 : :
761 : : {&sbrec_table_multicast_group,
762 : : {{NULL, NULL, NULL},
763 : : {NULL, NULL, NULL}}},
764 : :
765 : : {&sbrec_table_datapath_binding,
766 : : {{NULL, NULL, NULL},
767 : : {NULL, NULL, NULL}}},
768 : :
769 : : {&sbrec_table_port_binding,
770 : : {{&sbrec_table_port_binding, &sbrec_port_binding_col_logical_port, NULL},
771 : : {NULL, NULL, NULL}}},
772 : :
773 : : {&sbrec_table_mac_binding,
774 : : {{&sbrec_table_mac_binding, &sbrec_mac_binding_col_logical_port, NULL},
775 : : {NULL, NULL, NULL}}},
776 : :
777 : : {&sbrec_table_address_set,
778 : : {{&sbrec_table_address_set, &sbrec_address_set_col_name, NULL},
779 : : {NULL, NULL, NULL}}},
780 : :
781 : : {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
782 : : };
783 : :
784 : :
785 : : static void
786 : 192 : sbctl_context_init_command(struct sbctl_context *sbctl_ctx,
787 : : struct ctl_command *command)
788 : : {
789 : 192 : ctl_context_init_command(&sbctl_ctx->base, command);
790 : 192 : }
791 : :
792 : : static void
793 : 352 : sbctl_context_init(struct sbctl_context *sbctl_ctx,
794 : : struct ctl_command *command, struct ovsdb_idl *idl,
795 : : struct ovsdb_idl_txn *txn,
796 : : struct ovsdb_symbol_table *symtab)
797 : : {
798 : 352 : ctl_context_init(&sbctl_ctx->base, command, idl, txn, symtab,
799 : : sbctl_context_invalidate_cache);
800 : 352 : sbctl_ctx->cache_valid = false;
801 : 352 : }
802 : :
803 : : static void
804 : 192 : sbctl_context_done_command(struct sbctl_context *sbctl_ctx,
805 : : struct ctl_command *command)
806 : : {
807 : 192 : ctl_context_done_command(&sbctl_ctx->base, command);
808 : 192 : }
809 : :
810 : : static void
811 : 352 : sbctl_context_done(struct sbctl_context *sbctl_ctx,
812 : : struct ctl_command *command)
813 : : {
814 : 352 : ctl_context_done(&sbctl_ctx->base, command);
815 : 352 : }
816 : :
817 : : static void
818 : 181 : run_prerequisites(struct ctl_command *commands, size_t n_commands,
819 : : struct ovsdb_idl *idl)
820 : : {
821 : 181 : ovsdb_idl_add_table(idl, &sbrec_table_sb_global);
822 : :
823 [ + + ]: 373 : for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) {
824 [ + + ]: 192 : if (c->syntax->prerequisites) {
825 : : struct sbctl_context sbctl_ctx;
826 : :
827 : 159 : ds_init(&c->output);
828 : 159 : c->table = NULL;
829 : :
830 : 159 : sbctl_context_init(&sbctl_ctx, c, idl, NULL, NULL);
831 : 159 : (c->syntax->prerequisites)(&sbctl_ctx.base);
832 : 159 : sbctl_context_done(&sbctl_ctx, c);
833 : :
834 [ - + ]: 159 : ovs_assert(!c->output.string);
835 [ - + ]: 159 : ovs_assert(!c->table);
836 : : }
837 : : }
838 : 181 : }
839 : :
840 : : static bool
841 : 181 : do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands,
842 : : struct ovsdb_idl *idl)
843 : : {
844 : : struct ovsdb_idl_txn *txn;
845 : : enum ovsdb_idl_txn_status status;
846 : : struct ovsdb_symbol_table *symtab;
847 : : struct sbctl_context sbctl_ctx;
848 : : struct ctl_command *c;
849 : : struct shash_node *node;
850 : 181 : char *error = NULL;
851 : :
852 : 181 : txn = the_idl_txn = ovsdb_idl_txn_create(idl);
853 [ - + ]: 181 : if (dry_run) {
854 : 0 : ovsdb_idl_txn_set_dry_run(txn);
855 : : }
856 : :
857 : 181 : ovsdb_idl_txn_add_comment(txn, "ovs-sbctl: %s", args);
858 : :
859 : 181 : const struct sbrec_sb_global *sb = sbrec_sb_global_first(idl);
860 [ + + ]: 181 : if (!sb) {
861 : : /* XXX add verification that table is empty */
862 : 44 : sb = sbrec_sb_global_insert(txn);
863 : : }
864 : :
865 : 181 : symtab = ovsdb_symbol_table_create();
866 [ + + ]: 373 : for (c = commands; c < &commands[n_commands]; c++) {
867 : 192 : ds_init(&c->output);
868 : 192 : c->table = NULL;
869 : : }
870 : 181 : sbctl_context_init(&sbctl_ctx, NULL, idl, txn, symtab);
871 [ + + ]: 373 : for (c = commands; c < &commands[n_commands]; c++) {
872 : 192 : sbctl_context_init_command(&sbctl_ctx, c);
873 [ + - ]: 192 : if (c->syntax->run) {
874 : 192 : (c->syntax->run)(&sbctl_ctx.base);
875 : : }
876 : 192 : sbctl_context_done_command(&sbctl_ctx, c);
877 : :
878 [ - + ]: 192 : if (sbctl_ctx.base.try_again) {
879 : 0 : sbctl_context_done(&sbctl_ctx, NULL);
880 : 0 : goto try_again;
881 : : }
882 : : }
883 : 181 : sbctl_context_done(&sbctl_ctx, NULL);
884 : :
885 [ + + ][ - + ]: 186 : SHASH_FOR_EACH (node, &symtab->sh) {
886 : 5 : struct ovsdb_symbol *symbol = node->data;
887 [ - + ]: 5 : if (!symbol->created) {
888 : 0 : ctl_fatal("row id \"%s\" is referenced but never created (e.g. "
889 : : "with \"-- --id=%s create ...\")",
890 : : node->name, node->name);
891 : : }
892 [ - + ]: 5 : if (!symbol->strong_ref) {
893 [ # # ]: 0 : if (!symbol->weak_ref) {
894 [ # # ]: 0 : VLOG_WARN("row id \"%s\" was created but no reference to it "
895 : : "was inserted, so it will not actually appear in "
896 : : "the database", node->name);
897 : : } else {
898 [ # # ]: 0 : VLOG_WARN("row id \"%s\" was created but only a weak "
899 : : "reference to it was inserted, so it will not "
900 : : "actually appear in the database", node->name);
901 : : }
902 : : }
903 : : }
904 : :
905 : 181 : status = ovsdb_idl_txn_commit_block(txn);
906 [ + + ][ + - ]: 181 : if (status == TXN_UNCHANGED || status == TXN_SUCCESS) {
907 [ + + ]: 373 : for (c = commands; c < &commands[n_commands]; c++) {
908 [ + + ]: 192 : if (c->syntax->postprocess) {
909 : 12 : sbctl_context_init(&sbctl_ctx, c, idl, txn, symtab);
910 : 12 : (c->syntax->postprocess)(&sbctl_ctx.base);
911 : 12 : sbctl_context_done(&sbctl_ctx, c);
912 : : }
913 : : }
914 : : }
915 : 181 : error = xstrdup(ovsdb_idl_txn_get_error(txn));
916 : :
917 [ - - + - : 181 : switch (status) {
- - - ]
918 : : case TXN_UNCOMMITTED:
919 : : case TXN_INCOMPLETE:
920 : 0 : OVS_NOT_REACHED();
921 : :
922 : : case TXN_ABORTED:
923 : : /* Should not happen--we never call ovsdb_idl_txn_abort(). */
924 : 0 : ctl_fatal("transaction aborted");
925 : :
926 : : case TXN_UNCHANGED:
927 : : case TXN_SUCCESS:
928 : 181 : break;
929 : :
930 : : case TXN_TRY_AGAIN:
931 : 0 : goto try_again;
932 : :
933 : : case TXN_ERROR:
934 : 0 : ctl_fatal("transaction error: %s", error);
935 : :
936 : : case TXN_NOT_LOCKED:
937 : : /* Should not happen--we never call ovsdb_idl_set_lock(). */
938 : 0 : ctl_fatal("database not locked");
939 : :
940 : : default:
941 : 0 : OVS_NOT_REACHED();
942 : : }
943 : 181 : free(error);
944 : :
945 : 181 : ovsdb_symbol_table_destroy(symtab);
946 : :
947 [ + + ]: 373 : for (c = commands; c < &commands[n_commands]; c++) {
948 : 192 : struct ds *ds = &c->output;
949 : :
950 [ + + ]: 192 : if (c->table) {
951 : 92 : table_print(c->table, &table_style);
952 [ - + ]: 100 : } else if (oneline) {
953 : : size_t j;
954 : :
955 : 0 : ds_chomp(ds, '\n');
956 [ # # ]: 0 : for (j = 0; j < ds->length; j++) {
957 : 0 : int ch = ds->string[j];
958 [ # # # ]: 0 : switch (ch) {
959 : : case '\n':
960 : 0 : fputs("\\n", stdout);
961 : 0 : break;
962 : :
963 : : case '\\':
964 : 0 : fputs("\\\\", stdout);
965 : 0 : break;
966 : :
967 : : default:
968 : 0 : putchar(ch);
969 : : }
970 : : }
971 : 0 : putchar('\n');
972 : : } else {
973 : 100 : fputs(ds_cstr(ds), stdout);
974 : : }
975 : 192 : ds_destroy(&c->output);
976 : 192 : table_destroy(c->table);
977 : 192 : free(c->table);
978 : :
979 : 192 : shash_destroy_free_data(&c->options);
980 : : }
981 : 181 : free(commands);
982 : 181 : ovsdb_idl_txn_destroy(txn);
983 : 181 : ovsdb_idl_destroy(idl);
984 : :
985 : 181 : return true;
986 : :
987 : : try_again:
988 : : /* Our transaction needs to be rerun, or a prerequisite was not met. Free
989 : : * resources and return so that the caller can try again. */
990 [ # # ]: 0 : if (txn) {
991 : 0 : ovsdb_idl_txn_abort(txn);
992 : 0 : ovsdb_idl_txn_destroy(txn);
993 : 0 : the_idl_txn = NULL;
994 : : }
995 : 0 : ovsdb_symbol_table_destroy(symtab);
996 [ # # ]: 0 : for (c = commands; c < &commands[n_commands]; c++) {
997 : 0 : ds_destroy(&c->output);
998 : 0 : table_destroy(c->table);
999 : 0 : free(c->table);
1000 : : }
1001 : 0 : free(error);
1002 : 181 : return false;
1003 : : }
1004 : :
1005 : : /* Frees the current transaction and the underlying IDL and then calls
1006 : : * exit(status).
1007 : : *
1008 : : * Freeing the transaction and the IDL is not strictly necessary, but it makes
1009 : : * for a clean memory leak report from valgrind in the normal case. That makes
1010 : : * it easier to notice real memory leaks. */
1011 : : static void
1012 : 0 : sbctl_exit(int status)
1013 : : {
1014 [ # # ]: 0 : if (the_idl_txn) {
1015 : 0 : ovsdb_idl_txn_abort(the_idl_txn);
1016 : 0 : ovsdb_idl_txn_destroy(the_idl_txn);
1017 : : }
1018 : 0 : ovsdb_idl_destroy(the_idl);
1019 : 0 : exit(status);
1020 : : }
1021 : :
1022 : : static const struct ctl_command_syntax sbctl_commands[] = {
1023 : : { "init", 0, 0, "", NULL, sbctl_init, NULL, "", RW },
1024 : :
1025 : : /* Chassis commands. */
1026 : : {"chassis-add", 3, 3, "CHASSIS ENCAP-TYPE ENCAP-IP", pre_get_info,
1027 : : cmd_chassis_add, NULL, "--may-exist", RW},
1028 : : {"chassis-del", 1, 1, "CHASSIS", pre_get_info, cmd_chassis_del, NULL,
1029 : : "--if-exists", RW},
1030 : :
1031 : : /* Port binding commands. */
1032 : : {"lsp-bind", 2, 2, "PORT CHASSIS", pre_get_info, cmd_lsp_bind, NULL,
1033 : : "--may-exist", RW},
1034 : : {"lsp-unbind", 1, 1, "PORT", pre_get_info, cmd_lsp_unbind, NULL,
1035 : : "--if-exists", RW},
1036 : :
1037 : : /* Logical flow commands */
1038 : : {"lflow-list", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL,
1039 : : "", RO},
1040 : : {"dump-flows", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL,
1041 : : "", RO}, /* Friendly alias for lflow-list */
1042 : :
1043 : : /* SSL commands (To Be Added). */
1044 : :
1045 : : {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
1046 : : };
1047 : :
1048 : : /* Registers sbctl and common db commands. */
1049 : : static void
1050 : 181 : sbctl_cmd_init(void)
1051 : : {
1052 : 181 : ctl_init(tables, cmd_show_tables, sbctl_exit);
1053 : 181 : ctl_register_commands(sbctl_commands);
1054 : 181 : }
|