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