]>
Commit | Line | Data |
---|---|---|
fed00ab1 | 1 | /* |
3f5b5f7b | 2 | * Copyright (c) 2015, 2016, 2017 Nicira, Inc. |
fed00ab1 AW |
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 | ||
c2f4c39b | 30 | #include "colors.h" |
fed00ab1 AW |
31 | #include "command-line.h" |
32 | #include "compiler.h" | |
8ad609c0 BP |
33 | #include "db-ctl-base.h" |
34 | #include "dirs.h" | |
fed00ab1 | 35 | #include "fatal-signal.h" |
8ad609c0 | 36 | #include "openvswitch/dynamic-string.h" |
ee89ea7b | 37 | #include "openvswitch/json.h" |
c2f4c39b BP |
38 | #include "openvswitch/ofp-actions.h" |
39 | #include "openvswitch/ofp-print.h" | |
8ad609c0 | 40 | #include "openvswitch/shash.h" |
c2f4c39b | 41 | #include "openvswitch/vconn.h" |
8ad609c0 BP |
42 | #include "openvswitch/vlog.h" |
43 | #include "ovn/lib/ovn-sb-idl.h" | |
44 | #include "ovn/lib/ovn-util.h" | |
fed00ab1 AW |
45 | #include "ovsdb-data.h" |
46 | #include "ovsdb-idl.h" | |
47 | #include "poll-loop.h" | |
48 | #include "process.h" | |
49 | #include "sset.h" | |
dda825b8 | 50 | #include "stream-ssl.h" |
66cf8908 | 51 | #include "stream.h" |
fed00ab1 AW |
52 | #include "table.h" |
53 | #include "timeval.h" | |
54 | #include "util.h" | |
10471820 | 55 | #include "svec.h" |
fed00ab1 AW |
56 | |
57 | VLOG_DEFINE_THIS_MODULE(sbctl); | |
58 | ||
59 | struct sbctl_context; | |
60 | ||
61 | /* --db: The database server to contact. */ | |
62 | static const char *db; | |
63 | ||
64 | /* --oneline: Write each command's output as a single line? */ | |
65 | static bool oneline; | |
66 | ||
67 | /* --dry-run: Do not commit any changes. */ | |
68 | static bool dry_run; | |
69 | ||
70 | /* --timeout: Time to wait for a connection to 'db'. */ | |
71 | static int timeout; | |
72 | ||
73 | /* Format for table output. */ | |
74 | static struct table_style table_style = TABLE_STYLE_DEFAULT; | |
75 | ||
76 | /* The IDL we're using and the current transaction, if any. | |
77 | * This is for use by sbctl_exit() only, to allow it to clean up. | |
78 | * Other code should use its context arguments. */ | |
79 | static struct ovsdb_idl *the_idl; | |
80 | static struct ovsdb_idl_txn *the_idl_txn; | |
81 | OVS_NO_RETURN static void sbctl_exit(int status); | |
82 | ||
83 | static void sbctl_cmd_init(void); | |
84 | OVS_NO_RETURN static void usage(void); | |
85 | static void parse_options(int argc, char *argv[], struct shash *local_options); | |
86 | static void run_prerequisites(struct ctl_command[], size_t n_commands, | |
87 | struct ovsdb_idl *); | |
8ee574c3 | 88 | static bool do_sbctl(const char *args, struct ctl_command *, size_t n, |
fed00ab1 AW |
89 | struct ovsdb_idl *); |
90 | ||
91 | int | |
92 | main(int argc, char *argv[]) | |
93 | { | |
fed00ab1 AW |
94 | struct ovsdb_idl *idl; |
95 | struct ctl_command *commands; | |
96 | struct shash local_options; | |
97 | unsigned int seqno; | |
98 | size_t n_commands; | |
99 | char *args; | |
100 | ||
101 | set_program_name(argv[0]); | |
102 | fatal_ignore_sigpipe(); | |
103 | vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); | |
45863ce5 | 104 | vlog_set_levels_from_string_assert("reconnect:warn"); |
fed00ab1 AW |
105 | |
106 | sbctl_cmd_init(); | |
107 | ||
108 | /* Log our arguments. This is often valuable for debugging systems. */ | |
109 | args = process_escape_args(argv); | |
110 | VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args); | |
111 | ||
112 | /* Parse command line. */ | |
113 | shash_init(&local_options); | |
114 | parse_options(argc, argv, &local_options); | |
115 | commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, | |
116 | &n_commands); | |
117 | ||
118 | if (timeout) { | |
119 | time_alarm(timeout); | |
120 | } | |
121 | ||
122 | /* Initialize IDL. */ | |
123 | idl = the_idl = ovsdb_idl_create(db, &sbrec_idl_class, false, false); | |
124 | run_prerequisites(commands, n_commands, idl); | |
125 | ||
126 | /* Execute the commands. | |
127 | * | |
128 | * 'seqno' is the database sequence number for which we last tried to | |
129 | * execute our transaction. There's no point in trying to commit more than | |
130 | * once for any given sequence number, because if the transaction fails | |
131 | * it's because the database changed and we need to obtain an up-to-date | |
132 | * view of the database before we try the transaction again. */ | |
133 | seqno = ovsdb_idl_get_seqno(idl); | |
134 | for (;;) { | |
135 | ovsdb_idl_run(idl); | |
136 | if (!ovsdb_idl_is_alive(idl)) { | |
137 | int retval = ovsdb_idl_get_last_error(idl); | |
138 | ctl_fatal("%s: database connection failed (%s)", | |
139 | db, ovs_retval_to_string(retval)); | |
140 | } | |
141 | ||
142 | if (seqno != ovsdb_idl_get_seqno(idl)) { | |
143 | seqno = ovsdb_idl_get_seqno(idl); | |
8ee574c3 WT |
144 | if (do_sbctl(args, commands, n_commands, idl)) { |
145 | free(args); | |
146 | exit(EXIT_SUCCESS); | |
147 | } | |
fed00ab1 AW |
148 | } |
149 | ||
150 | if (seqno == ovsdb_idl_get_seqno(idl)) { | |
151 | ovsdb_idl_wait(idl); | |
152 | poll_block(); | |
153 | } | |
154 | } | |
155 | } | |
156 | ||
157 | static void | |
158 | parse_options(int argc, char *argv[], struct shash *local_options) | |
159 | { | |
160 | enum { | |
161 | OPT_DB = UCHAR_MAX + 1, | |
162 | OPT_ONELINE, | |
163 | OPT_NO_SYSLOG, | |
164 | OPT_DRY_RUN, | |
fed00ab1 AW |
165 | OPT_LOCAL, |
166 | OPT_COMMANDS, | |
167 | OPT_OPTIONS, | |
168 | VLOG_OPTION_ENUMS, | |
e18a1d08 ER |
169 | TABLE_OPTION_ENUMS, |
170 | SSL_OPTION_ENUMS, | |
fed00ab1 AW |
171 | }; |
172 | static const struct option global_long_options[] = { | |
173 | {"db", required_argument, NULL, OPT_DB}, | |
174 | {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, | |
175 | {"dry-run", no_argument, NULL, OPT_DRY_RUN}, | |
176 | {"oneline", no_argument, NULL, OPT_ONELINE}, | |
177 | {"timeout", required_argument, NULL, 't'}, | |
178 | {"help", no_argument, NULL, 'h'}, | |
179 | {"commands", no_argument, NULL, OPT_COMMANDS}, | |
180 | {"options", no_argument, NULL, OPT_OPTIONS}, | |
181 | {"version", no_argument, NULL, 'V'}, | |
182 | VLOG_LONG_OPTIONS, | |
dda825b8 | 183 | STREAM_SSL_LONG_OPTIONS, |
fed00ab1 AW |
184 | TABLE_LONG_OPTIONS, |
185 | {NULL, 0, NULL, 0}, | |
186 | }; | |
187 | const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; | |
188 | char *tmp, *short_options; | |
189 | ||
190 | struct option *options; | |
191 | size_t allocated_options; | |
192 | size_t n_options; | |
193 | size_t i; | |
194 | ||
195 | tmp = ovs_cmdl_long_options_to_short_options(global_long_options); | |
196 | short_options = xasprintf("+%s", tmp); | |
197 | free(tmp); | |
198 | ||
199 | /* We want to parse both global and command-specific options here, but | |
200 | * getopt_long() isn't too convenient for the job. We copy our global | |
201 | * options into a dynamic array, then append all of the command-specific | |
202 | * options. */ | |
203 | options = xmemdup(global_long_options, sizeof global_long_options); | |
204 | allocated_options = ARRAY_SIZE(global_long_options); | |
205 | n_options = n_global_long_options; | |
206 | ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); | |
fed00ab1 AW |
207 | |
208 | for (;;) { | |
209 | int idx; | |
210 | int c; | |
211 | ||
212 | c = getopt_long(argc, argv, short_options, options, &idx); | |
213 | if (c == -1) { | |
214 | break; | |
215 | } | |
216 | ||
217 | switch (c) { | |
218 | case OPT_DB: | |
219 | db = optarg; | |
220 | break; | |
221 | ||
222 | case OPT_ONELINE: | |
223 | oneline = true; | |
224 | break; | |
225 | ||
226 | case OPT_NO_SYSLOG: | |
922fed06 | 227 | vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); |
fed00ab1 AW |
228 | break; |
229 | ||
230 | case OPT_DRY_RUN: | |
231 | dry_run = true; | |
232 | break; | |
233 | ||
234 | case OPT_LOCAL: | |
235 | if (shash_find(local_options, options[idx].name)) { | |
236 | ctl_fatal("'%s' option specified multiple times", | |
237 | options[idx].name); | |
238 | } | |
239 | shash_add_nocopy(local_options, | |
240 | xasprintf("--%s", options[idx].name), | |
2225c0b9 | 241 | nullable_xstrdup(optarg)); |
fed00ab1 AW |
242 | break; |
243 | ||
244 | case 'h': | |
245 | usage(); | |
246 | ||
247 | case OPT_COMMANDS: | |
248 | ctl_print_commands(); | |
249 | ||
250 | case OPT_OPTIONS: | |
251 | ctl_print_options(global_long_options); | |
252 | ||
253 | case 'V': | |
254 | ovs_print_version(0, 0); | |
255 | printf("DB Schema %s\n", sbrec_get_db_version()); | |
256 | exit(EXIT_SUCCESS); | |
257 | ||
258 | case 't': | |
259 | timeout = strtoul(optarg, NULL, 10); | |
260 | if (timeout < 0) { | |
b31301bc | 261 | ctl_fatal("value %s on -t or --timeout is invalid", optarg); |
fed00ab1 AW |
262 | } |
263 | break; | |
264 | ||
265 | VLOG_OPTION_HANDLERS | |
266 | TABLE_OPTION_HANDLERS(&table_style) | |
dda825b8 | 267 | STREAM_SSL_OPTION_HANDLERS |
fed00ab1 AW |
268 | |
269 | case '?': | |
270 | exit(EXIT_FAILURE); | |
271 | ||
272 | default: | |
273 | abort(); | |
274 | } | |
275 | } | |
276 | free(short_options); | |
277 | ||
278 | if (!db) { | |
8ad609c0 | 279 | db = default_sb_db(); |
fed00ab1 AW |
280 | } |
281 | ||
282 | for (i = n_global_long_options; options[i].name; i++) { | |
283 | free(CONST_CAST(char *, options[i].name)); | |
284 | } | |
285 | free(options); | |
286 | } | |
287 | ||
288 | static void | |
289 | usage(void) | |
290 | { | |
291 | printf("\ | |
66cf8908 | 292 | %s: OVN southbound DB management utility\n\ |
fed00ab1 | 293 | \n\ |
fed00ab1 AW |
294 | usage: %s [OPTIONS] COMMAND [ARG...]\n\ |
295 | \n\ | |
66cf8908 | 296 | General commands:\n\ |
fed00ab1 AW |
297 | show print overview of database contents\n\ |
298 | \n\ | |
299 | Chassis commands:\n\ | |
300 | chassis-add CHASSIS ENCAP-TYPE ENCAP-IP create a new chassis named\n\ | |
3c653533 JP |
301 | CHASSIS with ENCAP-TYPE tunnels\n\ |
302 | and ENCAP-IP\n\ | |
303 | chassis-del CHASSIS delete CHASSIS and all of its encaps\n\ | |
fed00ab1 AW |
304 | and gateway_ports\n\ |
305 | \n\ | |
306 | Port binding commands:\n\ | |
f0f96ba8 RB |
307 | lsp-bind PORT CHASSIS bind logical port PORT to CHASSIS\n\ |
308 | lsp-unbind PORT reset the port binding of logical port PORT\n\ | |
fed00ab1 | 309 | \n\ |
dc70b67b | 310 | Logical flow commands:\n\ |
c80eac1f BP |
311 | lflow-list [DATAPATH] [LFLOW...] List logical flows for DATAPATH\n\ |
312 | dump-flows [DATAPATH] [LFLOW...] Alias for lflow-list\n\ | |
dc70b67b | 313 | \n\ |
10471820 LR |
314 | Connection commands:\n\ |
315 | get-connection print the connections\n\ | |
316 | del-connection delete the connections\n\ | |
317 | set-connection TARGET... set the list of connections to TARGET...\n\ | |
318 | \n\ | |
319 | SSL commands:\n\ | |
320 | get-ssl print the SSL configuration\n\ | |
321 | del-ssl delete the SSL configuration\n\ | |
322 | set-ssl PRIV-KEY CERT CA-CERT set the SSL configuration\n\ | |
323 | \n\ | |
fed00ab1 AW |
324 | %s\ |
325 | \n\ | |
326 | Options:\n\ | |
327 | --db=DATABASE connect to DATABASE\n\ | |
328 | (default: %s)\n\ | |
66cf8908 | 329 | -t, --timeout=SECS wait at most SECS seconds\n\ |
fed00ab1 AW |
330 | --dry-run do not commit changes to database\n\ |
331 | --oneline print exactly one line of output per command\n", | |
8ad609c0 BP |
332 | program_name, program_name, ctl_get_db_cmd_usage(), |
333 | default_sb_db()); | |
bcb58ce0 | 334 | table_usage(); |
fed00ab1 AW |
335 | vlog_usage(); |
336 | printf("\ | |
337 | --no-syslog equivalent to --verbose=sbctl:syslog:warn\n"); | |
338 | printf("\n\ | |
339 | Other options:\n\ | |
340 | -h, --help display this help message\n\ | |
341 | -V, --version display version information\n"); | |
66cf8908 | 342 | stream_usage("database", true, true, false); |
fed00ab1 AW |
343 | exit(EXIT_SUCCESS); |
344 | } | |
345 | ||
346 | \f | |
347 | /* ovs-sbctl specific context. Inherits the 'struct ctl_context' as base. */ | |
348 | struct sbctl_context { | |
349 | struct ctl_context base; | |
350 | ||
351 | /* A cache of the contents of the database. | |
352 | * | |
353 | * A command that needs to use any of this information must first call | |
354 | * sbctl_context_populate_cache(). A command that changes anything that | |
355 | * could invalidate the cache must either call | |
356 | * sbctl_context_invalidate_cache() or manually update the cache to | |
357 | * maintain its correctness. */ | |
358 | bool cache_valid; | |
359 | /* Maps from chassis name to struct sbctl_chassis. */ | |
360 | struct shash chassis; | |
361 | /* Maps from lport name to struct sbctl_port_binding. */ | |
362 | struct shash port_bindings; | |
363 | }; | |
364 | ||
ec4eed45 | 365 | /* Casts 'base' into 'struct sbctl_context'. */ |
fed00ab1 AW |
366 | static struct sbctl_context * |
367 | sbctl_context_cast(struct ctl_context *base) | |
368 | { | |
369 | return CONTAINER_OF(base, struct sbctl_context, base); | |
370 | } | |
371 | ||
372 | struct sbctl_chassis { | |
373 | const struct sbrec_chassis *ch_cfg; | |
374 | }; | |
375 | ||
376 | struct sbctl_port_binding { | |
377 | const struct sbrec_port_binding *bd_cfg; | |
378 | }; | |
379 | ||
380 | static void | |
381 | sbctl_context_invalidate_cache(struct ctl_context *ctx) | |
382 | { | |
383 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
384 | ||
385 | if (!sbctl_ctx->cache_valid) { | |
386 | return; | |
387 | } | |
388 | sbctl_ctx->cache_valid = false; | |
389 | shash_destroy_free_data(&sbctl_ctx->chassis); | |
390 | shash_destroy_free_data(&sbctl_ctx->port_bindings); | |
391 | } | |
392 | ||
393 | static void | |
394 | sbctl_context_populate_cache(struct ctl_context *ctx) | |
395 | { | |
396 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
397 | const struct sbrec_chassis *chassis_rec; | |
398 | const struct sbrec_port_binding *port_binding_rec; | |
399 | struct sset chassis, port_bindings; | |
400 | ||
401 | if (sbctl_ctx->cache_valid) { | |
402 | /* Cache is already populated. */ | |
403 | return; | |
404 | } | |
405 | sbctl_ctx->cache_valid = true; | |
406 | shash_init(&sbctl_ctx->chassis); | |
407 | shash_init(&sbctl_ctx->port_bindings); | |
408 | sset_init(&chassis); | |
409 | SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->idl) { | |
410 | struct sbctl_chassis *ch; | |
411 | ||
412 | if (!sset_add(&chassis, chassis_rec->name)) { | |
413 | VLOG_WARN("database contains duplicate chassis name (%s)", | |
414 | chassis_rec->name); | |
415 | continue; | |
416 | } | |
417 | ||
418 | ch = xmalloc(sizeof *ch); | |
419 | ch->ch_cfg = chassis_rec; | |
420 | shash_add(&sbctl_ctx->chassis, chassis_rec->name, ch); | |
421 | } | |
422 | sset_destroy(&chassis); | |
423 | ||
424 | sset_init(&port_bindings); | |
425 | SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->idl) { | |
426 | struct sbctl_port_binding *bd; | |
427 | ||
428 | if (!sset_add(&port_bindings, port_binding_rec->logical_port)) { | |
429 | VLOG_WARN("database contains duplicate port binding for logical " | |
430 | "port (%s)", | |
431 | port_binding_rec->logical_port); | |
432 | continue; | |
433 | } | |
434 | ||
435 | bd = xmalloc(sizeof *bd); | |
436 | bd->bd_cfg = port_binding_rec; | |
437 | shash_add(&sbctl_ctx->port_bindings, port_binding_rec->logical_port, | |
438 | bd); | |
439 | } | |
440 | sset_destroy(&port_bindings); | |
441 | } | |
442 | ||
443 | static void | |
444 | check_conflicts(struct sbctl_context *sbctl_ctx, const char *name, | |
445 | char *msg) | |
446 | { | |
447 | if (shash_find(&sbctl_ctx->chassis, name)) { | |
448 | ctl_fatal("%s because a chassis named %s already exists", | |
449 | msg, name); | |
450 | } | |
451 | free(msg); | |
452 | } | |
453 | ||
454 | static struct sbctl_chassis * | |
455 | find_chassis(struct sbctl_context *sbctl_ctx, const char *name, | |
456 | bool must_exist) | |
457 | { | |
458 | struct sbctl_chassis *sbctl_ch; | |
459 | ||
460 | ovs_assert(sbctl_ctx->cache_valid); | |
461 | ||
462 | sbctl_ch = shash_find_data(&sbctl_ctx->chassis, name); | |
463 | if (must_exist && !sbctl_ch) { | |
464 | ctl_fatal("no chassis named %s", name); | |
465 | } | |
466 | ||
467 | return sbctl_ch; | |
468 | } | |
469 | ||
470 | static struct sbctl_port_binding * | |
471 | find_port_binding(struct sbctl_context *sbctl_ctx, const char *name, | |
472 | bool must_exist) | |
473 | { | |
474 | struct sbctl_port_binding *bd; | |
475 | ||
476 | ovs_assert(sbctl_ctx->cache_valid); | |
477 | ||
478 | bd = shash_find_data(&sbctl_ctx->port_bindings, name); | |
479 | if (must_exist && !bd) { | |
480 | ctl_fatal("no port named %s", name); | |
481 | } | |
482 | ||
483 | return bd; | |
484 | } | |
485 | ||
486 | static void | |
487 | pre_get_info(struct ctl_context *ctx) | |
488 | { | |
489 | ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_name); | |
490 | ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_encaps); | |
491 | ||
492 | ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_type); | |
493 | ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_ip); | |
494 | ||
495 | ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_logical_port); | |
496 | ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis); | |
dc70b67b RB |
497 | |
498 | ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_logical_datapath); | |
499 | ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_pipeline); | |
500 | ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_actions); | |
501 | ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_priority); | |
502 | ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_table_id); | |
503 | ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_match); | |
ebbcddeb | 504 | ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_external_ids); |
0f8e9c12 BP |
505 | |
506 | ovsdb_idl_add_column(ctx->idl, &sbrec_datapath_binding_col_external_ids); | |
fed00ab1 AW |
507 | } |
508 | ||
509 | static struct cmd_show_table cmd_show_tables[] = { | |
510 | {&sbrec_table_chassis, | |
511 | &sbrec_chassis_col_name, | |
2229f3ec RB |
512 | {&sbrec_chassis_col_hostname, |
513 | &sbrec_chassis_col_encaps, | |
016e4684 AW |
514 | NULL}, |
515 | {&sbrec_table_port_binding, | |
516 | &sbrec_port_binding_col_logical_port, | |
517 | &sbrec_port_binding_col_chassis}}, | |
fed00ab1 AW |
518 | |
519 | {&sbrec_table_encap, | |
520 | &sbrec_encap_col_type, | |
521 | {&sbrec_encap_col_ip, | |
522 | &sbrec_encap_col_options, | |
016e4684 AW |
523 | NULL}, |
524 | {NULL, NULL, NULL}}, | |
fed00ab1 | 525 | |
016e4684 | 526 | {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}}, |
fed00ab1 AW |
527 | }; |
528 | ||
fa183acc BP |
529 | static void |
530 | sbctl_init(struct ctl_context *ctx OVS_UNUSED) | |
531 | { | |
532 | } | |
533 | ||
fed00ab1 AW |
534 | static void |
535 | cmd_chassis_add(struct ctl_context *ctx) | |
536 | { | |
537 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
fed00ab1 | 538 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; |
3c653533 | 539 | const char *ch_name, *encap_types, *encap_ip; |
fed00ab1 AW |
540 | |
541 | ch_name = ctx->argv[1]; | |
3c653533 | 542 | encap_types = ctx->argv[2]; |
fed00ab1 AW |
543 | encap_ip = ctx->argv[3]; |
544 | ||
545 | sbctl_context_populate_cache(ctx); | |
546 | if (may_exist) { | |
547 | struct sbctl_chassis *sbctl_ch; | |
548 | ||
549 | sbctl_ch = find_chassis(sbctl_ctx, ch_name, false); | |
550 | if (sbctl_ch) { | |
551 | return; | |
552 | } | |
553 | } | |
554 | check_conflicts(sbctl_ctx, ch_name, | |
555 | xasprintf("cannot create a chassis named %s", ch_name)); | |
3c653533 | 556 | |
63a10e1e BP |
557 | struct sset encap_set; |
558 | sset_from_delimited_string(&encap_set, encap_types, ","); | |
3c653533 JP |
559 | |
560 | size_t n_encaps = sset_count(&encap_set); | |
561 | struct sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps); | |
36283d78 | 562 | const struct smap options = SMAP_CONST1(&options, "csum", "true"); |
3c653533 JP |
563 | const char *encap_type; |
564 | int i = 0; | |
565 | SSET_FOR_EACH (encap_type, &encap_set){ | |
566 | encaps[i] = sbrec_encap_insert(ctx->txn); | |
567 | ||
568 | sbrec_encap_set_type(encaps[i], encap_type); | |
569 | sbrec_encap_set_ip(encaps[i], encap_ip); | |
36283d78 | 570 | sbrec_encap_set_options(encaps[i], &options); |
3c653533 JP |
571 | i++; |
572 | } | |
573 | sset_destroy(&encap_set); | |
574 | ||
575 | struct sbrec_chassis *ch = sbrec_chassis_insert(ctx->txn); | |
fed00ab1 | 576 | sbrec_chassis_set_name(ch, ch_name); |
3c653533 JP |
577 | sbrec_chassis_set_encaps(ch, encaps, n_encaps); |
578 | free(encaps); | |
579 | ||
fed00ab1 AW |
580 | sbctl_context_invalidate_cache(ctx); |
581 | } | |
582 | ||
583 | static void | |
584 | cmd_chassis_del(struct ctl_context *ctx) | |
585 | { | |
586 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
587 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
588 | struct sbctl_chassis *sbctl_ch; | |
589 | ||
590 | sbctl_context_populate_cache(ctx); | |
591 | sbctl_ch = find_chassis(sbctl_ctx, ctx->argv[1], must_exist); | |
592 | if (sbctl_ch) { | |
593 | if (sbctl_ch->ch_cfg) { | |
3b74b8c0 AW |
594 | size_t i; |
595 | ||
596 | for (i = 0; i < sbctl_ch->ch_cfg->n_encaps; i++) { | |
597 | sbrec_encap_delete(sbctl_ch->ch_cfg->encaps[i]); | |
598 | } | |
fed00ab1 AW |
599 | sbrec_chassis_delete(sbctl_ch->ch_cfg); |
600 | } | |
601 | shash_find_and_delete(&sbctl_ctx->chassis, ctx->argv[1]); | |
602 | free(sbctl_ch); | |
603 | } | |
604 | } | |
605 | ||
606 | static void | |
f0f96ba8 | 607 | cmd_lsp_bind(struct ctl_context *ctx) |
fed00ab1 AW |
608 | { |
609 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
610 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
611 | struct sbctl_chassis *sbctl_ch; | |
612 | struct sbctl_port_binding *sbctl_bd; | |
613 | char *lport_name, *ch_name; | |
614 | ||
615 | /* port_binding must exist, chassis must exist! */ | |
616 | lport_name = ctx->argv[1]; | |
617 | ch_name = ctx->argv[2]; | |
618 | ||
619 | sbctl_context_populate_cache(ctx); | |
620 | sbctl_bd = find_port_binding(sbctl_ctx, lport_name, true); | |
621 | sbctl_ch = find_chassis(sbctl_ctx, ch_name, true); | |
622 | ||
623 | if (sbctl_bd->bd_cfg->chassis) { | |
624 | if (may_exist && sbctl_bd->bd_cfg->chassis == sbctl_ch->ch_cfg) { | |
625 | return; | |
626 | } else { | |
627 | ctl_fatal("lport (%s) has already been binded to chassis (%s)", | |
628 | lport_name, sbctl_bd->bd_cfg->chassis->name); | |
629 | } | |
630 | } | |
631 | sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, sbctl_ch->ch_cfg); | |
632 | sbctl_context_invalidate_cache(ctx); | |
633 | } | |
634 | ||
635 | static void | |
f0f96ba8 | 636 | cmd_lsp_unbind(struct ctl_context *ctx) |
fed00ab1 AW |
637 | { |
638 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
639 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
640 | struct sbctl_port_binding *sbctl_bd; | |
641 | char *lport_name; | |
642 | ||
643 | lport_name = ctx->argv[1]; | |
644 | sbctl_context_populate_cache(ctx); | |
645 | sbctl_bd = find_port_binding(sbctl_ctx, lport_name, must_exist); | |
646 | if (sbctl_bd) { | |
647 | sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, NULL); | |
648 | } | |
649 | } | |
650 | ||
dc70b67b RB |
651 | enum { |
652 | PL_INGRESS, | |
653 | PL_EGRESS, | |
654 | }; | |
655 | ||
656 | /* Help ensure we catch any future pipeline values */ | |
657 | static int | |
658 | pipeline_encode(const char *pl) | |
659 | { | |
660 | if (!strcmp(pl, "ingress")) { | |
661 | return PL_INGRESS; | |
662 | } else if (!strcmp(pl, "egress")) { | |
663 | return PL_EGRESS; | |
664 | } | |
665 | ||
666 | OVS_NOT_REACHED(); | |
667 | } | |
668 | ||
669 | static int | |
670 | lflow_cmp(const void *lf1_, const void *lf2_) | |
671 | { | |
35107449 BP |
672 | const struct sbrec_logical_flow *const *lf1p = lf1_; |
673 | const struct sbrec_logical_flow *const *lf2p = lf2_; | |
674 | const struct sbrec_logical_flow *lf1 = *lf1p; | |
675 | const struct sbrec_logical_flow *lf2 = *lf2p; | |
dc70b67b RB |
676 | |
677 | int pl1 = pipeline_encode(lf1->pipeline); | |
678 | int pl2 = pipeline_encode(lf2->pipeline); | |
679 | ||
680 | #define CMP(expr) \ | |
681 | do { \ | |
682 | int res; \ | |
683 | res = (expr); \ | |
684 | if (res) { \ | |
685 | return res; \ | |
686 | } \ | |
687 | } while (0) | |
688 | ||
689 | CMP(uuid_compare_3way(&lf1->logical_datapath->header_.uuid, | |
690 | &lf2->logical_datapath->header_.uuid)); | |
691 | CMP(pl1 - pl2); | |
692 | CMP(lf1->table_id > lf2->table_id ? 1 : | |
693 | (lf1->table_id < lf2->table_id ? -1 : 0)); | |
694 | CMP(lf1->priority > lf2->priority ? -1 : | |
695 | (lf1->priority < lf2->priority ? 1 : 0)); | |
696 | CMP(strcmp(lf1->match, lf2->match)); | |
697 | ||
698 | #undef CMP | |
699 | ||
700 | return 0; | |
701 | } | |
702 | ||
c80eac1f BP |
703 | static char * |
704 | parse_partial_uuid(char *s) | |
705 | { | |
706 | /* Accept a full or partial UUID. */ | |
2052f3a1 | 707 | if (uuid_is_partial_string(s)) { |
c80eac1f BP |
708 | return s; |
709 | } | |
710 | ||
711 | /* Accept a full or partial UUID prefixed by 0x, since "ovs-ofctl | |
712 | * dump-flows" prints cookies prefixed by 0x. */ | |
713 | if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') | |
2052f3a1 | 714 | && uuid_is_partial_string(s + 2)) { |
c80eac1f BP |
715 | return s + 2; |
716 | } | |
717 | ||
718 | /* Not a (partial) UUID. */ | |
719 | return NULL; | |
720 | } | |
721 | ||
8b8d09dc HZ |
722 | static const char * |
723 | strip_leading_zero(const char *s) | |
724 | { | |
725 | return s + strspn(s, "0"); | |
726 | } | |
727 | ||
c80eac1f BP |
728 | static bool |
729 | is_partial_uuid_match(const struct uuid *uuid, const char *match) | |
730 | { | |
731 | char uuid_s[UUID_LEN + 1]; | |
732 | snprintf(uuid_s, sizeof uuid_s, UUID_FMT, UUID_ARGS(uuid)); | |
733 | ||
4e3000a0 BP |
734 | /* We strip leading zeros because we want to accept cookie values derived |
735 | * from UUIDs, and cookie values are printed without leading zeros because | |
736 | * they're just numbers. */ | |
8b8d09dc HZ |
737 | const char *s1 = strip_leading_zero(uuid_s); |
738 | const char *s2 = strip_leading_zero(match); | |
739 | ||
740 | return !strncmp(s1, s2, strlen(s2)); | |
c80eac1f BP |
741 | } |
742 | ||
c2f4c39b BP |
743 | static char * |
744 | default_ovs(void) | |
745 | { | |
746 | return xasprintf("unix:%s/br-int.mgmt", ovs_rundir()); | |
747 | } | |
748 | ||
749 | static struct vconn * | |
750 | sbctl_open_vconn(struct shash *options) | |
751 | { | |
752 | struct shash_node *ovs = shash_find(options, "--ovs"); | |
753 | if (!ovs) { | |
754 | return NULL; | |
755 | } | |
756 | ||
757 | char *remote = ovs->data ? xstrdup(ovs->data) : default_ovs(); | |
758 | struct vconn *vconn; | |
759 | int retval = vconn_open_block(remote, 1 << OFP13_VERSION, 0, &vconn); | |
760 | if (retval) { | |
761 | VLOG_WARN("%s: connection failed (%s)", remote, ovs_strerror(retval)); | |
762 | } | |
763 | free(remote); | |
764 | return vconn; | |
765 | } | |
766 | ||
767 | static void | |
768 | sbctl_dump_openflow(struct vconn *vconn, const struct uuid *uuid, bool stats) | |
769 | { | |
770 | struct ofputil_flow_stats_request fsr = { | |
771 | .cookie = htonll(uuid->parts[0]), | |
772 | .cookie_mask = OVS_BE64_MAX, | |
773 | .out_port = OFPP_ANY, | |
774 | .out_group = OFPG_ANY, | |
775 | .table_id = OFPTT_ALL, | |
776 | }; | |
777 | ||
778 | struct ofputil_flow_stats *fses; | |
779 | size_t n_fses; | |
780 | int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM, | |
781 | &fses, &n_fses); | |
782 | if (error) { | |
783 | VLOG_WARN("%s: error obtaining flow stats (%s)", | |
784 | vconn_get_name(vconn), ovs_strerror(error)); | |
785 | return; | |
786 | } | |
787 | ||
788 | if (n_fses) { | |
789 | struct ds s = DS_EMPTY_INITIALIZER; | |
790 | for (size_t i = 0; i < n_fses; i++) { | |
791 | const struct ofputil_flow_stats *fs = &fses[i]; | |
792 | ||
793 | ds_clear(&s); | |
794 | if (stats) { | |
795 | ofp_print_flow_stats(&s, fs); | |
796 | } else { | |
797 | ds_put_format(&s, " %stable=%s%"PRIu8" ", | |
798 | colors.special, colors.end, fs->table_id); | |
799 | match_format(&fs->match, &s, OFP_DEFAULT_PRIORITY); | |
800 | if (ds_last(&s) != ' ') { | |
801 | ds_put_char(&s, ' '); | |
802 | } | |
803 | ||
804 | ds_put_format(&s, "%sactions=%s", colors.actions, colors.end); | |
805 | ofpacts_format(fs->ofpacts, fs->ofpacts_len, &s); | |
806 | } | |
807 | printf(" %s\n", ds_cstr(&s)); | |
808 | } | |
809 | ds_destroy(&s); | |
810 | } | |
811 | ||
812 | for (size_t i = 0; i < n_fses; i++) { | |
813 | free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); | |
814 | } | |
815 | free(fses); | |
816 | } | |
817 | ||
dc70b67b RB |
818 | static void |
819 | cmd_lflow_list(struct ctl_context *ctx) | |
820 | { | |
c80eac1f BP |
821 | const struct sbrec_datapath_binding *datapath = NULL; |
822 | if (ctx->argc > 1) { | |
bed7aef9 BP |
823 | datapath = (const struct sbrec_datapath_binding *) |
824 | ctl_get_row(ctx, &sbrec_table_datapath_binding, | |
825 | ctx->argv[1], false); | |
c80eac1f BP |
826 | if (datapath) { |
827 | ctx->argc--; | |
828 | ctx->argv++; | |
829 | } | |
830 | } | |
dc70b67b | 831 | |
c80eac1f BP |
832 | for (size_t i = 1; i < ctx->argc; i++) { |
833 | char *s = parse_partial_uuid(ctx->argv[i]); | |
834 | if (!s) { | |
835 | ctl_fatal("%s is not a UUID or the beginning of a UUID", | |
836 | ctx->argv[i]); | |
837 | } | |
838 | ctx->argv[i] = s; | |
dc70b67b RB |
839 | } |
840 | ||
c2f4c39b BP |
841 | struct vconn *vconn = sbctl_open_vconn(&ctx->options); |
842 | bool stats = shash_find(&ctx->options, "--stats") != NULL; | |
843 | ||
c80eac1f BP |
844 | const struct sbrec_logical_flow **lflows = NULL; |
845 | size_t n_flows = 0; | |
846 | size_t n_capacity = 0; | |
847 | const struct sbrec_logical_flow *lflow; | |
dc70b67b | 848 | SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) { |
c80eac1f BP |
849 | if (datapath && lflow->logical_datapath != datapath) { |
850 | continue; | |
851 | } | |
852 | ||
dc70b67b RB |
853 | if (n_flows == n_capacity) { |
854 | lflows = x2nrealloc(lflows, &n_capacity, sizeof *lflows); | |
855 | } | |
856 | lflows[n_flows] = lflow; | |
857 | n_flows++; | |
858 | } | |
dc70b67b RB |
859 | qsort(lflows, n_flows, sizeof *lflows, lflow_cmp); |
860 | ||
c80eac1f BP |
861 | bool print_uuid = shash_find(&ctx->options, "--uuid") != NULL; |
862 | ||
863 | const struct sbrec_logical_flow *prev = NULL; | |
864 | for (size_t i = 0; i < n_flows; i++) { | |
dc70b67b | 865 | lflow = lflows[i]; |
c80eac1f BP |
866 | |
867 | /* Figure out whether to print this particular flow. By default, we | |
868 | * print all flows, but if any UUIDs were listed on the command line | |
869 | * then we only print the matching ones. */ | |
870 | bool include; | |
871 | if (ctx->argc > 1) { | |
872 | include = false; | |
873 | for (size_t j = 1; j < ctx->argc; j++) { | |
874 | if (is_partial_uuid_match(&lflow->header_.uuid, | |
875 | ctx->argv[j])) { | |
876 | include = true; | |
877 | break; | |
878 | } | |
879 | } | |
880 | } else { | |
881 | include = true; | |
882 | } | |
883 | if (!include) { | |
dc70b67b RB |
884 | continue; |
885 | } | |
c80eac1f BP |
886 | |
887 | /* Print a header line for this datapath or pipeline, if we haven't | |
888 | * already done so. */ | |
889 | if (!prev | |
890 | || prev->logical_datapath != lflow->logical_datapath | |
891 | || strcmp(prev->pipeline, lflow->pipeline)) { | |
f658b27c BP |
892 | printf("Datapath:"); |
893 | ||
894 | const struct smap *ids = &lflow->logical_datapath->external_ids; | |
895 | const char *name = smap_get(ids, "name"); | |
896 | const char *name2 = smap_get(ids, "name2"); | |
897 | if (name && name2) { | |
898 | printf(" \"%s\" aka \"%s\"", name, name2); | |
899 | } else if (name || name2) { | |
900 | printf(" \"%s\"", name ? name : name2); | |
901 | } | |
902 | printf(" ("UUID_FMT") Pipeline: %s\n", | |
0f8e9c12 BP |
903 | UUID_ARGS(&lflow->logical_datapath->header_.uuid), |
904 | lflow->pipeline); | |
dc70b67b | 905 | } |
ebbcddeb | 906 | |
c80eac1f BP |
907 | /* Print the flow. */ |
908 | printf(" "); | |
909 | if (print_uuid) { | |
910 | printf("uuid=0x%08"PRIx32", ", lflow->header_.uuid.parts[0]); | |
911 | } | |
912 | printf("table=%-2"PRId64"(%-19s), priority=%-5"PRId64 | |
ebbcddeb | 913 | ", match=(%s), action=(%s)\n", |
f99f67bd BP |
914 | lflow->table_id, |
915 | smap_get_def(&lflow->external_ids, "stage-name", ""), | |
ebbcddeb | 916 | lflow->priority, lflow->match, lflow->actions); |
c2f4c39b BP |
917 | if (vconn) { |
918 | sbctl_dump_openflow(vconn, &lflow->header_.uuid, stats); | |
919 | } | |
c80eac1f | 920 | prev = lflow; |
dc70b67b RB |
921 | } |
922 | ||
c2f4c39b | 923 | vconn_close(vconn); |
dc70b67b RB |
924 | free(lflows); |
925 | } | |
926 | ||
10471820 LR |
927 | static void |
928 | verify_connections(struct ctl_context *ctx) | |
929 | { | |
930 | const struct sbrec_sb_global *sb_global = sbrec_sb_global_first(ctx->idl); | |
931 | const struct sbrec_connection *conn; | |
932 | ||
933 | sbrec_sb_global_verify_connections(sb_global); | |
934 | ||
935 | SBREC_CONNECTION_FOR_EACH(conn, ctx->idl) { | |
936 | sbrec_connection_verify_target(conn); | |
937 | } | |
938 | } | |
939 | ||
940 | static void | |
941 | pre_connection(struct ctl_context *ctx) | |
942 | { | |
943 | ovsdb_idl_add_column(ctx->idl, &sbrec_sb_global_col_connections); | |
944 | ovsdb_idl_add_column(ctx->idl, &sbrec_connection_col_target); | |
945 | ovsdb_idl_add_column(ctx->idl, &sbrec_connection_col_read_only); | |
946 | } | |
947 | ||
948 | static void | |
949 | cmd_get_connection(struct ctl_context *ctx) | |
950 | { | |
951 | const struct sbrec_connection *conn; | |
952 | struct svec targets; | |
953 | size_t i; | |
954 | ||
955 | verify_connections(ctx); | |
956 | ||
957 | /* Print the targets in sorted order for reproducibility. */ | |
958 | svec_init(&targets); | |
959 | ||
960 | SBREC_CONNECTION_FOR_EACH(conn, ctx->idl) { | |
961 | char *s; | |
962 | ||
963 | s = xasprintf("%s %s", conn->read_only ? "read-only" : "read-write", | |
964 | conn->target); | |
965 | svec_add(&targets, s); | |
966 | free(s); | |
967 | } | |
968 | ||
969 | svec_sort_unique(&targets); | |
970 | for (i = 0; i < targets.n; i++) { | |
971 | ds_put_format(&ctx->output, "%s\n", targets.names[i]); | |
972 | } | |
973 | svec_destroy(&targets); | |
974 | } | |
975 | ||
976 | static void | |
977 | delete_connections(struct ctl_context *ctx) | |
978 | { | |
979 | const struct sbrec_sb_global *sb_global = sbrec_sb_global_first(ctx->idl); | |
980 | const struct sbrec_connection *conn, *next; | |
981 | ||
982 | /* Delete Manager rows pointed to by 'connection_options' column. */ | |
983 | SBREC_CONNECTION_FOR_EACH_SAFE(conn, next, ctx->idl) { | |
984 | sbrec_connection_delete(conn); | |
985 | } | |
986 | ||
987 | /* Delete 'Manager' row refs in 'manager_options' column. */ | |
988 | sbrec_sb_global_set_connections(sb_global, NULL, 0); | |
989 | } | |
990 | ||
991 | static void | |
992 | cmd_del_connection(struct ctl_context *ctx) | |
993 | { | |
994 | verify_connections(ctx); | |
995 | delete_connections(ctx); | |
996 | } | |
997 | ||
998 | static void | |
999 | insert_connections(struct ctl_context *ctx, char *targets[], size_t n) | |
1000 | { | |
1001 | const struct sbrec_sb_global *sb_global = sbrec_sb_global_first(ctx->idl); | |
1002 | struct sbrec_connection **connections; | |
1003 | size_t i, conns=0; | |
1004 | bool read_only = false; | |
1005 | ||
1006 | /* Insert each connection in a new row in Connection table. */ | |
1007 | connections = xmalloc(n * sizeof *connections); | |
1008 | for (i = 0; i < n; i++) { | |
1009 | if (!strcmp(targets[i], "read-only")) { | |
1010 | read_only = true; | |
1011 | continue; | |
1012 | } else if (!strcmp(targets[i], "read-write")) { | |
1013 | read_only = false; | |
1014 | continue; | |
1015 | } else if (stream_verify_name(targets[i]) && | |
1016 | pstream_verify_name(targets[i])) { | |
1017 | VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); | |
1018 | } | |
1019 | ||
1020 | connections[conns] = sbrec_connection_insert(ctx->txn); | |
1021 | sbrec_connection_set_target(connections[conns], targets[i]); | |
1022 | sbrec_connection_set_read_only(connections[conns], read_only); | |
1023 | conns++; | |
1024 | } | |
1025 | ||
1026 | /* Store uuids of new connection rows in 'connection' column. */ | |
1027 | sbrec_sb_global_set_connections(sb_global, connections, conns); | |
1028 | free(connections); | |
1029 | } | |
1030 | ||
1031 | static void | |
1032 | cmd_set_connection(struct ctl_context *ctx) | |
1033 | { | |
1034 | const size_t n = ctx->argc - 1; | |
1035 | ||
1036 | verify_connections(ctx); | |
1037 | delete_connections(ctx); | |
1038 | insert_connections(ctx, &ctx->argv[1], n); | |
1039 | } | |
1040 | ||
1041 | static void | |
1042 | pre_cmd_get_ssl(struct ctl_context *ctx) | |
1043 | { | |
1044 | ovsdb_idl_add_column(ctx->idl, &sbrec_sb_global_col_ssl); | |
1045 | ||
1046 | ovsdb_idl_add_column(ctx->idl, &sbrec_ssl_col_private_key); | |
1047 | ovsdb_idl_add_column(ctx->idl, &sbrec_ssl_col_certificate); | |
1048 | ovsdb_idl_add_column(ctx->idl, &sbrec_ssl_col_ca_cert); | |
1049 | ovsdb_idl_add_column(ctx->idl, &sbrec_ssl_col_bootstrap_ca_cert); | |
1050 | } | |
1051 | ||
1052 | static void | |
1053 | cmd_get_ssl(struct ctl_context *ctx) | |
1054 | { | |
1055 | const struct sbrec_sb_global *sb_global = sbrec_sb_global_first(ctx->idl); | |
1056 | const struct sbrec_ssl *ssl = sbrec_ssl_first(ctx->idl); | |
1057 | ||
1058 | sbrec_sb_global_verify_ssl(sb_global); | |
1059 | if (ssl) { | |
1060 | sbrec_ssl_verify_private_key(ssl); | |
1061 | sbrec_ssl_verify_certificate(ssl); | |
1062 | sbrec_ssl_verify_ca_cert(ssl); | |
1063 | sbrec_ssl_verify_bootstrap_ca_cert(ssl); | |
1064 | ||
1065 | ds_put_format(&ctx->output, "Private key: %s\n", ssl->private_key); | |
1066 | ds_put_format(&ctx->output, "Certificate: %s\n", ssl->certificate); | |
1067 | ds_put_format(&ctx->output, "CA Certificate: %s\n", ssl->ca_cert); | |
1068 | ds_put_format(&ctx->output, "Bootstrap: %s\n", | |
1069 | ssl->bootstrap_ca_cert ? "true" : "false"); | |
1070 | } | |
1071 | } | |
1072 | ||
1073 | static void | |
1074 | pre_cmd_del_ssl(struct ctl_context *ctx) | |
1075 | { | |
1076 | ovsdb_idl_add_column(ctx->idl, &sbrec_sb_global_col_ssl); | |
1077 | } | |
1078 | ||
1079 | static void | |
1080 | cmd_del_ssl(struct ctl_context *ctx) | |
1081 | { | |
1082 | const struct sbrec_sb_global *sb_global = sbrec_sb_global_first(ctx->idl); | |
1083 | const struct sbrec_ssl *ssl = sbrec_ssl_first(ctx->idl); | |
1084 | ||
1085 | if (ssl) { | |
1086 | sbrec_sb_global_verify_ssl(sb_global); | |
1087 | sbrec_ssl_delete(ssl); | |
1088 | sbrec_sb_global_set_ssl(sb_global, NULL); | |
1089 | } | |
1090 | } | |
1091 | ||
1092 | static void | |
1093 | pre_cmd_set_ssl(struct ctl_context *ctx) | |
1094 | { | |
1095 | ovsdb_idl_add_column(ctx->idl, &sbrec_sb_global_col_ssl); | |
1096 | } | |
1097 | ||
1098 | static void | |
1099 | cmd_set_ssl(struct ctl_context *ctx) | |
1100 | { | |
1101 | bool bootstrap = shash_find(&ctx->options, "--bootstrap"); | |
1102 | const struct sbrec_sb_global *sb_global = sbrec_sb_global_first(ctx->idl); | |
1103 | const struct sbrec_ssl *ssl = sbrec_ssl_first(ctx->idl); | |
1104 | ||
1105 | sbrec_sb_global_verify_ssl(sb_global); | |
1106 | if (ssl) { | |
1107 | sbrec_ssl_delete(ssl); | |
1108 | } | |
1109 | ssl = sbrec_ssl_insert(ctx->txn); | |
1110 | ||
1111 | sbrec_ssl_set_private_key(ssl, ctx->argv[1]); | |
1112 | sbrec_ssl_set_certificate(ssl, ctx->argv[2]); | |
1113 | sbrec_ssl_set_ca_cert(ssl, ctx->argv[3]); | |
1114 | ||
1115 | sbrec_ssl_set_bootstrap_ca_cert(ssl, bootstrap); | |
1116 | ||
1117 | sbrec_sb_global_set_ssl(sb_global, ssl); | |
1118 | } | |
1119 | ||
fed00ab1 | 1120 | \f |
3f5b5f7b | 1121 | static const struct ctl_table_class tables[SBREC_N_TABLES] = { |
15931827 | 1122 | [SBREC_TABLE_CHASSIS].row_ids[0] = {&sbrec_chassis_col_name, NULL, NULL}, |
fa183acc | 1123 | |
682b4171 BP |
1124 | [SBREC_TABLE_DATAPATH_BINDING].row_ids |
1125 | = {{&sbrec_datapath_binding_col_external_ids, "name", NULL}, | |
f658b27c | 1126 | {&sbrec_datapath_binding_col_external_ids, "name2", NULL}, |
682b4171 BP |
1127 | {&sbrec_datapath_binding_col_external_ids, "logical-switch", NULL}, |
1128 | {&sbrec_datapath_binding_col_external_ids, "logical-router", NULL}}, | |
1129 | ||
f658b27c BP |
1130 | [SBREC_TABLE_PORT_BINDING].row_ids |
1131 | = {{&sbrec_port_binding_col_logical_port, NULL, NULL}, | |
1132 | {&sbrec_port_binding_col_external_ids, "name", NULL}}, | |
9c39b120 | 1133 | |
3f5b5f7b | 1134 | [SBREC_TABLE_MAC_BINDING].row_ids[0] = |
15931827 | 1135 | {&sbrec_mac_binding_col_logical_port, NULL, NULL}, |
7fff4eb7 | 1136 | |
15931827 BP |
1137 | [SBREC_TABLE_ADDRESS_SET].row_ids[0] |
1138 | = {&sbrec_address_set_col_name, NULL, NULL}, | |
fed00ab1 AW |
1139 | }; |
1140 | ||
1141 | \f | |
1142 | static void | |
1143 | sbctl_context_init_command(struct sbctl_context *sbctl_ctx, | |
1144 | struct ctl_command *command) | |
1145 | { | |
1146 | ctl_context_init_command(&sbctl_ctx->base, command); | |
1147 | } | |
1148 | ||
1149 | static void | |
1150 | sbctl_context_init(struct sbctl_context *sbctl_ctx, | |
1151 | struct ctl_command *command, struct ovsdb_idl *idl, | |
1152 | struct ovsdb_idl_txn *txn, | |
1153 | struct ovsdb_symbol_table *symtab) | |
1154 | { | |
1155 | ctl_context_init(&sbctl_ctx->base, command, idl, txn, symtab, | |
1156 | sbctl_context_invalidate_cache); | |
1157 | sbctl_ctx->cache_valid = false; | |
1158 | } | |
1159 | ||
1160 | static void | |
1161 | sbctl_context_done_command(struct sbctl_context *sbctl_ctx, | |
1162 | struct ctl_command *command) | |
1163 | { | |
1164 | ctl_context_done_command(&sbctl_ctx->base, command); | |
1165 | } | |
1166 | ||
1167 | static void | |
1168 | sbctl_context_done(struct sbctl_context *sbctl_ctx, | |
1169 | struct ctl_command *command) | |
1170 | { | |
1171 | ctl_context_done(&sbctl_ctx->base, command); | |
1172 | } | |
1173 | ||
1174 | static void | |
1175 | run_prerequisites(struct ctl_command *commands, size_t n_commands, | |
1176 | struct ovsdb_idl *idl) | |
1177 | { | |
fa183acc | 1178 | ovsdb_idl_add_table(idl, &sbrec_table_sb_global); |
fed00ab1 | 1179 | |
fa183acc | 1180 | for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { |
fed00ab1 AW |
1181 | if (c->syntax->prerequisites) { |
1182 | struct sbctl_context sbctl_ctx; | |
1183 | ||
1184 | ds_init(&c->output); | |
1185 | c->table = NULL; | |
1186 | ||
1187 | sbctl_context_init(&sbctl_ctx, c, idl, NULL, NULL); | |
1188 | (c->syntax->prerequisites)(&sbctl_ctx.base); | |
1189 | sbctl_context_done(&sbctl_ctx, c); | |
1190 | ||
1191 | ovs_assert(!c->output.string); | |
1192 | ovs_assert(!c->table); | |
1193 | } | |
1194 | } | |
1195 | } | |
1196 | ||
8ee574c3 | 1197 | static bool |
fed00ab1 AW |
1198 | do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands, |
1199 | struct ovsdb_idl *idl) | |
1200 | { | |
1201 | struct ovsdb_idl_txn *txn; | |
1202 | enum ovsdb_idl_txn_status status; | |
1203 | struct ovsdb_symbol_table *symtab; | |
1204 | struct sbctl_context sbctl_ctx; | |
1205 | struct ctl_command *c; | |
1206 | struct shash_node *node; | |
1207 | char *error = NULL; | |
1208 | ||
1209 | txn = the_idl_txn = ovsdb_idl_txn_create(idl); | |
1210 | if (dry_run) { | |
1211 | ovsdb_idl_txn_set_dry_run(txn); | |
1212 | } | |
1213 | ||
1214 | ovsdb_idl_txn_add_comment(txn, "ovs-sbctl: %s", args); | |
1215 | ||
fa183acc BP |
1216 | const struct sbrec_sb_global *sb = sbrec_sb_global_first(idl); |
1217 | if (!sb) { | |
1218 | /* XXX add verification that table is empty */ | |
1219 | sb = sbrec_sb_global_insert(txn); | |
1220 | } | |
1221 | ||
fed00ab1 AW |
1222 | symtab = ovsdb_symbol_table_create(); |
1223 | for (c = commands; c < &commands[n_commands]; c++) { | |
1224 | ds_init(&c->output); | |
1225 | c->table = NULL; | |
1226 | } | |
1227 | sbctl_context_init(&sbctl_ctx, NULL, idl, txn, symtab); | |
1228 | for (c = commands; c < &commands[n_commands]; c++) { | |
1229 | sbctl_context_init_command(&sbctl_ctx, c); | |
1230 | if (c->syntax->run) { | |
1231 | (c->syntax->run)(&sbctl_ctx.base); | |
1232 | } | |
1233 | sbctl_context_done_command(&sbctl_ctx, c); | |
1234 | ||
1235 | if (sbctl_ctx.base.try_again) { | |
1236 | sbctl_context_done(&sbctl_ctx, NULL); | |
1237 | goto try_again; | |
1238 | } | |
1239 | } | |
1240 | sbctl_context_done(&sbctl_ctx, NULL); | |
1241 | ||
1242 | SHASH_FOR_EACH (node, &symtab->sh) { | |
1243 | struct ovsdb_symbol *symbol = node->data; | |
1244 | if (!symbol->created) { | |
1245 | ctl_fatal("row id \"%s\" is referenced but never created (e.g. " | |
b31301bc BP |
1246 | "with \"-- --id=%s create ...\")", |
1247 | node->name, node->name); | |
fed00ab1 AW |
1248 | } |
1249 | if (!symbol->strong_ref) { | |
1250 | if (!symbol->weak_ref) { | |
1251 | VLOG_WARN("row id \"%s\" was created but no reference to it " | |
1252 | "was inserted, so it will not actually appear in " | |
1253 | "the database", node->name); | |
1254 | } else { | |
1255 | VLOG_WARN("row id \"%s\" was created but only a weak " | |
1256 | "reference to it was inserted, so it will not " | |
1257 | "actually appear in the database", node->name); | |
1258 | } | |
1259 | } | |
1260 | } | |
1261 | ||
1262 | status = ovsdb_idl_txn_commit_block(txn); | |
1263 | if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { | |
1264 | for (c = commands; c < &commands[n_commands]; c++) { | |
1265 | if (c->syntax->postprocess) { | |
1266 | sbctl_context_init(&sbctl_ctx, c, idl, txn, symtab); | |
1267 | (c->syntax->postprocess)(&sbctl_ctx.base); | |
1268 | sbctl_context_done(&sbctl_ctx, c); | |
1269 | } | |
1270 | } | |
1271 | } | |
1272 | error = xstrdup(ovsdb_idl_txn_get_error(txn)); | |
1273 | ||
1274 | switch (status) { | |
1275 | case TXN_UNCOMMITTED: | |
1276 | case TXN_INCOMPLETE: | |
1277 | OVS_NOT_REACHED(); | |
1278 | ||
1279 | case TXN_ABORTED: | |
1280 | /* Should not happen--we never call ovsdb_idl_txn_abort(). */ | |
1281 | ctl_fatal("transaction aborted"); | |
1282 | ||
1283 | case TXN_UNCHANGED: | |
1284 | case TXN_SUCCESS: | |
1285 | break; | |
1286 | ||
1287 | case TXN_TRY_AGAIN: | |
1288 | goto try_again; | |
1289 | ||
1290 | case TXN_ERROR: | |
1291 | ctl_fatal("transaction error: %s", error); | |
1292 | ||
1293 | case TXN_NOT_LOCKED: | |
1294 | /* Should not happen--we never call ovsdb_idl_set_lock(). */ | |
1295 | ctl_fatal("database not locked"); | |
1296 | ||
1297 | default: | |
1298 | OVS_NOT_REACHED(); | |
1299 | } | |
1300 | free(error); | |
1301 | ||
1302 | ovsdb_symbol_table_destroy(symtab); | |
1303 | ||
1304 | for (c = commands; c < &commands[n_commands]; c++) { | |
1305 | struct ds *ds = &c->output; | |
1306 | ||
1307 | if (c->table) { | |
1308 | table_print(c->table, &table_style); | |
1309 | } else if (oneline) { | |
1310 | size_t j; | |
1311 | ||
1312 | ds_chomp(ds, '\n'); | |
1313 | for (j = 0; j < ds->length; j++) { | |
1314 | int ch = ds->string[j]; | |
1315 | switch (ch) { | |
1316 | case '\n': | |
1317 | fputs("\\n", stdout); | |
1318 | break; | |
1319 | ||
1320 | case '\\': | |
1321 | fputs("\\\\", stdout); | |
1322 | break; | |
1323 | ||
1324 | default: | |
1325 | putchar(ch); | |
1326 | } | |
1327 | } | |
1328 | putchar('\n'); | |
1329 | } else { | |
1330 | fputs(ds_cstr(ds), stdout); | |
1331 | } | |
1332 | ds_destroy(&c->output); | |
1333 | table_destroy(c->table); | |
1334 | free(c->table); | |
1335 | ||
1336 | shash_destroy_free_data(&c->options); | |
1337 | } | |
1338 | free(commands); | |
1339 | ovsdb_idl_txn_destroy(txn); | |
1340 | ovsdb_idl_destroy(idl); | |
1341 | ||
8ee574c3 | 1342 | return true; |
fed00ab1 AW |
1343 | |
1344 | try_again: | |
1345 | /* Our transaction needs to be rerun, or a prerequisite was not met. Free | |
1346 | * resources and return so that the caller can try again. */ | |
1347 | if (txn) { | |
1348 | ovsdb_idl_txn_abort(txn); | |
1349 | ovsdb_idl_txn_destroy(txn); | |
1350 | the_idl_txn = NULL; | |
1351 | } | |
1352 | ovsdb_symbol_table_destroy(symtab); | |
1353 | for (c = commands; c < &commands[n_commands]; c++) { | |
1354 | ds_destroy(&c->output); | |
1355 | table_destroy(c->table); | |
1356 | free(c->table); | |
1357 | } | |
1358 | free(error); | |
8ee574c3 | 1359 | return false; |
fed00ab1 AW |
1360 | } |
1361 | ||
1362 | /* Frees the current transaction and the underlying IDL and then calls | |
1363 | * exit(status). | |
1364 | * | |
1365 | * Freeing the transaction and the IDL is not strictly necessary, but it makes | |
1366 | * for a clean memory leak report from valgrind in the normal case. That makes | |
1367 | * it easier to notice real memory leaks. */ | |
1368 | static void | |
1369 | sbctl_exit(int status) | |
1370 | { | |
1371 | if (the_idl_txn) { | |
1372 | ovsdb_idl_txn_abort(the_idl_txn); | |
1373 | ovsdb_idl_txn_destroy(the_idl_txn); | |
1374 | } | |
1375 | ovsdb_idl_destroy(the_idl); | |
1376 | exit(status); | |
1377 | } | |
1378 | ||
1379 | static const struct ctl_command_syntax sbctl_commands[] = { | |
fa183acc BP |
1380 | { "init", 0, 0, "", NULL, sbctl_init, NULL, "", RW }, |
1381 | ||
fed00ab1 AW |
1382 | /* Chassis commands. */ |
1383 | {"chassis-add", 3, 3, "CHASSIS ENCAP-TYPE ENCAP-IP", pre_get_info, | |
1384 | cmd_chassis_add, NULL, "--may-exist", RW}, | |
1385 | {"chassis-del", 1, 1, "CHASSIS", pre_get_info, cmd_chassis_del, NULL, | |
1386 | "--if-exists", RW}, | |
1387 | ||
1388 | /* Port binding commands. */ | |
f0f96ba8 | 1389 | {"lsp-bind", 2, 2, "PORT CHASSIS", pre_get_info, cmd_lsp_bind, NULL, |
fed00ab1 | 1390 | "--may-exist", RW}, |
f0f96ba8 | 1391 | {"lsp-unbind", 1, 1, "PORT", pre_get_info, cmd_lsp_unbind, NULL, |
fed00ab1 AW |
1392 | "--if-exists", RW}, |
1393 | ||
dc70b67b | 1394 | /* Logical flow commands */ |
c80eac1f BP |
1395 | {"lflow-list", 0, INT_MAX, "[DATAPATH] [LFLOW...]", |
1396 | pre_get_info, cmd_lflow_list, NULL, | |
c2f4c39b | 1397 | "--uuid,--ovs?,--stats", RO}, |
c80eac1f BP |
1398 | {"dump-flows", 0, INT_MAX, "[DATAPATH] [LFLOW...]", |
1399 | pre_get_info, cmd_lflow_list, NULL, | |
c2f4c39b | 1400 | "--uuid,--ovs?,--stats", RO}, /* Friendly alias for lflow-list */ |
dc70b67b | 1401 | |
10471820 LR |
1402 | /* Connection commands. */ |
1403 | {"get-connection", 0, 0, "", pre_connection, cmd_get_connection, NULL, "", RO}, | |
1404 | {"del-connection", 0, 0, "", pre_connection, cmd_del_connection, NULL, "", RW}, | |
1405 | {"set-connection", 1, INT_MAX, "TARGET...", pre_connection, cmd_set_connection, | |
1406 | NULL, "", RW}, | |
1407 | ||
1408 | /* SSL commands. */ | |
1409 | {"get-ssl", 0, 0, "", pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO}, | |
1410 | {"del-ssl", 0, 0, "", pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW}, | |
1411 | {"set-ssl", 3, 3, "PRIVATE-KEY CERTIFICATE CA-CERT", pre_cmd_set_ssl, | |
1412 | cmd_set_ssl, NULL, "--bootstrap", RW}, | |
fed00ab1 AW |
1413 | |
1414 | {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, | |
1415 | }; | |
1416 | ||
1417 | /* Registers sbctl and common db commands. */ | |
1418 | static void | |
1419 | sbctl_cmd_init(void) | |
1420 | { | |
3f5b5f7b | 1421 | ctl_init(sbrec_table_classes, tables, cmd_show_tables, sbctl_exit); |
fed00ab1 AW |
1422 | ctl_register_commands(sbctl_commands); |
1423 | } |