]>
Commit | Line | Data |
---|---|---|
fed00ab1 AW |
1 | /* |
2 | * Copyright (c) 2015 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 "db-ctl-base.h" | |
31 | ||
32 | #include "command-line.h" | |
33 | #include "compiler.h" | |
34 | #include "dynamic-string.h" | |
35 | #include "fatal-signal.h" | |
36 | #include "json.h" | |
37 | #include "ovsdb-data.h" | |
38 | #include "ovsdb-idl.h" | |
39 | #include "poll-loop.h" | |
40 | #include "process.h" | |
41 | #include "sset.h" | |
42 | #include "shash.h" | |
43 | #include "table.h" | |
44 | #include "timeval.h" | |
45 | #include "util.h" | |
46 | #include "openvswitch/vlog.h" | |
47 | #include "ovn/lib/ovn-sb-idl.h" | |
48 | ||
49 | VLOG_DEFINE_THIS_MODULE(sbctl); | |
50 | ||
51 | struct sbctl_context; | |
52 | ||
53 | /* --db: The database server to contact. */ | |
54 | static const char *db; | |
55 | ||
56 | /* --oneline: Write each command's output as a single line? */ | |
57 | static bool oneline; | |
58 | ||
59 | /* --dry-run: Do not commit any changes. */ | |
60 | static bool dry_run; | |
61 | ||
62 | /* --timeout: Time to wait for a connection to 'db'. */ | |
63 | static int timeout; | |
64 | ||
65 | /* Format for table output. */ | |
66 | static struct table_style table_style = TABLE_STYLE_DEFAULT; | |
67 | ||
68 | /* The IDL we're using and the current transaction, if any. | |
69 | * This is for use by sbctl_exit() only, to allow it to clean up. | |
70 | * Other code should use its context arguments. */ | |
71 | static struct ovsdb_idl *the_idl; | |
72 | static struct ovsdb_idl_txn *the_idl_txn; | |
73 | OVS_NO_RETURN static void sbctl_exit(int status); | |
74 | ||
75 | static void sbctl_cmd_init(void); | |
76 | OVS_NO_RETURN static void usage(void); | |
77 | static void parse_options(int argc, char *argv[], struct shash *local_options); | |
78 | static void run_prerequisites(struct ctl_command[], size_t n_commands, | |
79 | struct ovsdb_idl *); | |
80 | static void do_sbctl(const char *args, struct ctl_command *, size_t n, | |
81 | struct ovsdb_idl *); | |
82 | ||
83 | int | |
84 | main(int argc, char *argv[]) | |
85 | { | |
86 | extern struct vlog_module VLM_reconnect; | |
87 | struct ovsdb_idl *idl; | |
88 | struct ctl_command *commands; | |
89 | struct shash local_options; | |
90 | unsigned int seqno; | |
91 | size_t n_commands; | |
92 | char *args; | |
93 | ||
94 | set_program_name(argv[0]); | |
95 | fatal_ignore_sigpipe(); | |
96 | vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); | |
97 | vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN); | |
98 | sbrec_init(); | |
99 | ||
100 | sbctl_cmd_init(); | |
101 | ||
102 | /* Log our arguments. This is often valuable for debugging systems. */ | |
103 | args = process_escape_args(argv); | |
104 | VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args); | |
105 | ||
106 | /* Parse command line. */ | |
107 | shash_init(&local_options); | |
108 | parse_options(argc, argv, &local_options); | |
109 | commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, | |
110 | &n_commands); | |
111 | ||
112 | if (timeout) { | |
113 | time_alarm(timeout); | |
114 | } | |
115 | ||
116 | /* Initialize IDL. */ | |
117 | idl = the_idl = ovsdb_idl_create(db, &sbrec_idl_class, false, false); | |
118 | run_prerequisites(commands, n_commands, idl); | |
119 | ||
120 | /* Execute the commands. | |
121 | * | |
122 | * 'seqno' is the database sequence number for which we last tried to | |
123 | * execute our transaction. There's no point in trying to commit more than | |
124 | * once for any given sequence number, because if the transaction fails | |
125 | * it's because the database changed and we need to obtain an up-to-date | |
126 | * view of the database before we try the transaction again. */ | |
127 | seqno = ovsdb_idl_get_seqno(idl); | |
128 | for (;;) { | |
129 | ovsdb_idl_run(idl); | |
130 | if (!ovsdb_idl_is_alive(idl)) { | |
131 | int retval = ovsdb_idl_get_last_error(idl); | |
132 | ctl_fatal("%s: database connection failed (%s)", | |
133 | db, ovs_retval_to_string(retval)); | |
134 | } | |
135 | ||
136 | if (seqno != ovsdb_idl_get_seqno(idl)) { | |
137 | seqno = ovsdb_idl_get_seqno(idl); | |
138 | do_sbctl(args, commands, n_commands, idl); | |
139 | } | |
140 | ||
141 | if (seqno == ovsdb_idl_get_seqno(idl)) { | |
142 | ovsdb_idl_wait(idl); | |
143 | poll_block(); | |
144 | } | |
145 | } | |
146 | } | |
147 | ||
148 | static void | |
149 | parse_options(int argc, char *argv[], struct shash *local_options) | |
150 | { | |
151 | enum { | |
152 | OPT_DB = UCHAR_MAX + 1, | |
153 | OPT_ONELINE, | |
154 | OPT_NO_SYSLOG, | |
155 | OPT_DRY_RUN, | |
156 | OPT_PEER_CA_CERT, | |
157 | OPT_LOCAL, | |
158 | OPT_COMMANDS, | |
159 | OPT_OPTIONS, | |
160 | VLOG_OPTION_ENUMS, | |
161 | TABLE_OPTION_ENUMS | |
162 | }; | |
163 | static const struct option global_long_options[] = { | |
164 | {"db", required_argument, NULL, OPT_DB}, | |
165 | {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, | |
166 | {"dry-run", no_argument, NULL, OPT_DRY_RUN}, | |
167 | {"oneline", no_argument, NULL, OPT_ONELINE}, | |
168 | {"timeout", required_argument, NULL, 't'}, | |
169 | {"help", no_argument, NULL, 'h'}, | |
170 | {"commands", no_argument, NULL, OPT_COMMANDS}, | |
171 | {"options", no_argument, NULL, OPT_OPTIONS}, | |
172 | {"version", no_argument, NULL, 'V'}, | |
173 | VLOG_LONG_OPTIONS, | |
174 | TABLE_LONG_OPTIONS, | |
175 | {NULL, 0, NULL, 0}, | |
176 | }; | |
177 | const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; | |
178 | char *tmp, *short_options; | |
179 | ||
180 | struct option *options; | |
181 | size_t allocated_options; | |
182 | size_t n_options; | |
183 | size_t i; | |
184 | ||
185 | tmp = ovs_cmdl_long_options_to_short_options(global_long_options); | |
186 | short_options = xasprintf("+%s", tmp); | |
187 | free(tmp); | |
188 | ||
189 | /* We want to parse both global and command-specific options here, but | |
190 | * getopt_long() isn't too convenient for the job. We copy our global | |
191 | * options into a dynamic array, then append all of the command-specific | |
192 | * options. */ | |
193 | options = xmemdup(global_long_options, sizeof global_long_options); | |
194 | allocated_options = ARRAY_SIZE(global_long_options); | |
195 | n_options = n_global_long_options; | |
196 | ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); | |
197 | table_style.format = TF_LIST; | |
198 | ||
199 | for (;;) { | |
200 | int idx; | |
201 | int c; | |
202 | ||
203 | c = getopt_long(argc, argv, short_options, options, &idx); | |
204 | if (c == -1) { | |
205 | break; | |
206 | } | |
207 | ||
208 | switch (c) { | |
209 | case OPT_DB: | |
210 | db = optarg; | |
211 | break; | |
212 | ||
213 | case OPT_ONELINE: | |
214 | oneline = true; | |
215 | break; | |
216 | ||
217 | case OPT_NO_SYSLOG: | |
218 | vlog_set_levels(&VLM_sbctl, VLF_SYSLOG, VLL_WARN); | |
219 | break; | |
220 | ||
221 | case OPT_DRY_RUN: | |
222 | dry_run = true; | |
223 | break; | |
224 | ||
225 | case OPT_LOCAL: | |
226 | if (shash_find(local_options, options[idx].name)) { | |
227 | ctl_fatal("'%s' option specified multiple times", | |
228 | options[idx].name); | |
229 | } | |
230 | shash_add_nocopy(local_options, | |
231 | xasprintf("--%s", options[idx].name), | |
232 | optarg ? xstrdup(optarg) : NULL); | |
233 | break; | |
234 | ||
235 | case 'h': | |
236 | usage(); | |
237 | ||
238 | case OPT_COMMANDS: | |
239 | ctl_print_commands(); | |
240 | ||
241 | case OPT_OPTIONS: | |
242 | ctl_print_options(global_long_options); | |
243 | ||
244 | case 'V': | |
245 | ovs_print_version(0, 0); | |
246 | printf("DB Schema %s\n", sbrec_get_db_version()); | |
247 | exit(EXIT_SUCCESS); | |
248 | ||
249 | case 't': | |
250 | timeout = strtoul(optarg, NULL, 10); | |
251 | if (timeout < 0) { | |
252 | ctl_fatal("value %s on -t or --timeout is invalid", | |
253 | optarg); | |
254 | } | |
255 | break; | |
256 | ||
257 | VLOG_OPTION_HANDLERS | |
258 | TABLE_OPTION_HANDLERS(&table_style) | |
259 | ||
260 | case '?': | |
261 | exit(EXIT_FAILURE); | |
262 | ||
263 | default: | |
264 | abort(); | |
265 | } | |
266 | } | |
267 | free(short_options); | |
268 | ||
269 | if (!db) { | |
270 | db = ctl_default_db(); | |
271 | } | |
272 | ||
273 | for (i = n_global_long_options; options[i].name; i++) { | |
274 | free(CONST_CAST(char *, options[i].name)); | |
275 | } | |
276 | free(options); | |
277 | } | |
278 | ||
279 | static void | |
280 | usage(void) | |
281 | { | |
282 | printf("\ | |
283 | %s: ovs-vswitchd management utility\n\ | |
284 | \n\ | |
285 | for debugging and testing only, never use it in production\n\ | |
286 | \n\ | |
287 | usage: %s [OPTIONS] COMMAND [ARG...]\n\ | |
288 | \n\ | |
289 | SouthBound DB commands:\n\ | |
290 | show print overview of database contents\n\ | |
291 | \n\ | |
292 | Chassis commands:\n\ | |
293 | chassis-add CHASSIS ENCAP-TYPE ENCAP-IP create a new chassis named\n\ | |
294 | CHASSIS with one encapsulation\n\ | |
295 | entry of ENCAP-TYPE and ENCAP-IP\n\ | |
296 | chassis-del CHASSIS delete CHASSIS and all of its encaps,\n\ | |
297 | and gateway_ports\n\ | |
298 | \n\ | |
299 | Port binding commands:\n\ | |
300 | lport-bind LPORT CHASSIS bind logical port LPORT to CHASSIS\n\ | |
301 | lport-unbind LPORT reset the port binding of logical port LPORT\n\ | |
302 | \n\ | |
303 | %s\ | |
304 | \n\ | |
305 | Options:\n\ | |
306 | --db=DATABASE connect to DATABASE\n\ | |
307 | (default: %s)\n\ | |
308 | -t, --timeout=SECS wait at most SECS seconds for ovs-vswitchd\n\ | |
309 | --dry-run do not commit changes to database\n\ | |
310 | --oneline print exactly one line of output per command\n", | |
311 | program_name, program_name, ctl_get_db_cmd_usage(), ctl_default_db()); | |
312 | vlog_usage(); | |
313 | printf("\ | |
314 | --no-syslog equivalent to --verbose=sbctl:syslog:warn\n"); | |
315 | printf("\n\ | |
316 | Other options:\n\ | |
317 | -h, --help display this help message\n\ | |
318 | -V, --version display version information\n"); | |
319 | exit(EXIT_SUCCESS); | |
320 | } | |
321 | ||
322 | \f | |
323 | /* ovs-sbctl specific context. Inherits the 'struct ctl_context' as base. */ | |
324 | struct sbctl_context { | |
325 | struct ctl_context base; | |
326 | ||
327 | /* A cache of the contents of the database. | |
328 | * | |
329 | * A command that needs to use any of this information must first call | |
330 | * sbctl_context_populate_cache(). A command that changes anything that | |
331 | * could invalidate the cache must either call | |
332 | * sbctl_context_invalidate_cache() or manually update the cache to | |
333 | * maintain its correctness. */ | |
334 | bool cache_valid; | |
335 | /* Maps from chassis name to struct sbctl_chassis. */ | |
336 | struct shash chassis; | |
337 | /* Maps from lport name to struct sbctl_port_binding. */ | |
338 | struct shash port_bindings; | |
339 | }; | |
340 | ||
341 | /* Casts 'base' into 'strcut sbctl_context'. */ | |
342 | static struct sbctl_context * | |
343 | sbctl_context_cast(struct ctl_context *base) | |
344 | { | |
345 | return CONTAINER_OF(base, struct sbctl_context, base); | |
346 | } | |
347 | ||
348 | struct sbctl_chassis { | |
349 | const struct sbrec_chassis *ch_cfg; | |
350 | }; | |
351 | ||
352 | struct sbctl_port_binding { | |
353 | const struct sbrec_port_binding *bd_cfg; | |
354 | }; | |
355 | ||
356 | static void | |
357 | sbctl_context_invalidate_cache(struct ctl_context *ctx) | |
358 | { | |
359 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
360 | ||
361 | if (!sbctl_ctx->cache_valid) { | |
362 | return; | |
363 | } | |
364 | sbctl_ctx->cache_valid = false; | |
365 | shash_destroy_free_data(&sbctl_ctx->chassis); | |
366 | shash_destroy_free_data(&sbctl_ctx->port_bindings); | |
367 | } | |
368 | ||
369 | static void | |
370 | sbctl_context_populate_cache(struct ctl_context *ctx) | |
371 | { | |
372 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
373 | const struct sbrec_chassis *chassis_rec; | |
374 | const struct sbrec_port_binding *port_binding_rec; | |
375 | struct sset chassis, port_bindings; | |
376 | ||
377 | if (sbctl_ctx->cache_valid) { | |
378 | /* Cache is already populated. */ | |
379 | return; | |
380 | } | |
381 | sbctl_ctx->cache_valid = true; | |
382 | shash_init(&sbctl_ctx->chassis); | |
383 | shash_init(&sbctl_ctx->port_bindings); | |
384 | sset_init(&chassis); | |
385 | SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->idl) { | |
386 | struct sbctl_chassis *ch; | |
387 | ||
388 | if (!sset_add(&chassis, chassis_rec->name)) { | |
389 | VLOG_WARN("database contains duplicate chassis name (%s)", | |
390 | chassis_rec->name); | |
391 | continue; | |
392 | } | |
393 | ||
394 | ch = xmalloc(sizeof *ch); | |
395 | ch->ch_cfg = chassis_rec; | |
396 | shash_add(&sbctl_ctx->chassis, chassis_rec->name, ch); | |
397 | } | |
398 | sset_destroy(&chassis); | |
399 | ||
400 | sset_init(&port_bindings); | |
401 | SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->idl) { | |
402 | struct sbctl_port_binding *bd; | |
403 | ||
404 | if (!sset_add(&port_bindings, port_binding_rec->logical_port)) { | |
405 | VLOG_WARN("database contains duplicate port binding for logical " | |
406 | "port (%s)", | |
407 | port_binding_rec->logical_port); | |
408 | continue; | |
409 | } | |
410 | ||
411 | bd = xmalloc(sizeof *bd); | |
412 | bd->bd_cfg = port_binding_rec; | |
413 | shash_add(&sbctl_ctx->port_bindings, port_binding_rec->logical_port, | |
414 | bd); | |
415 | } | |
416 | sset_destroy(&port_bindings); | |
417 | } | |
418 | ||
419 | static void | |
420 | check_conflicts(struct sbctl_context *sbctl_ctx, const char *name, | |
421 | char *msg) | |
422 | { | |
423 | if (shash_find(&sbctl_ctx->chassis, name)) { | |
424 | ctl_fatal("%s because a chassis named %s already exists", | |
425 | msg, name); | |
426 | } | |
427 | free(msg); | |
428 | } | |
429 | ||
430 | static struct sbctl_chassis * | |
431 | find_chassis(struct sbctl_context *sbctl_ctx, const char *name, | |
432 | bool must_exist) | |
433 | { | |
434 | struct sbctl_chassis *sbctl_ch; | |
435 | ||
436 | ovs_assert(sbctl_ctx->cache_valid); | |
437 | ||
438 | sbctl_ch = shash_find_data(&sbctl_ctx->chassis, name); | |
439 | if (must_exist && !sbctl_ch) { | |
440 | ctl_fatal("no chassis named %s", name); | |
441 | } | |
442 | ||
443 | return sbctl_ch; | |
444 | } | |
445 | ||
446 | static struct sbctl_port_binding * | |
447 | find_port_binding(struct sbctl_context *sbctl_ctx, const char *name, | |
448 | bool must_exist) | |
449 | { | |
450 | struct sbctl_port_binding *bd; | |
451 | ||
452 | ovs_assert(sbctl_ctx->cache_valid); | |
453 | ||
454 | bd = shash_find_data(&sbctl_ctx->port_bindings, name); | |
455 | if (must_exist && !bd) { | |
456 | ctl_fatal("no port named %s", name); | |
457 | } | |
458 | ||
459 | return bd; | |
460 | } | |
461 | ||
462 | static void | |
463 | pre_get_info(struct ctl_context *ctx) | |
464 | { | |
465 | ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_name); | |
466 | ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_encaps); | |
467 | ||
468 | ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_type); | |
469 | ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_ip); | |
470 | ||
471 | ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_logical_port); | |
472 | ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis); | |
473 | } | |
474 | ||
475 | static struct cmd_show_table cmd_show_tables[] = { | |
476 | {&sbrec_table_chassis, | |
477 | &sbrec_chassis_col_name, | |
478 | {&sbrec_chassis_col_encaps, | |
479 | NULL, | |
480 | NULL}}, | |
481 | ||
482 | {&sbrec_table_encap, | |
483 | &sbrec_encap_col_type, | |
484 | {&sbrec_encap_col_ip, | |
485 | &sbrec_encap_col_options, | |
486 | NULL}}, | |
487 | ||
488 | {NULL, NULL, {NULL, NULL, NULL}}, | |
489 | }; | |
490 | ||
491 | static void | |
492 | cmd_chassis_add(struct ctl_context *ctx) | |
493 | { | |
494 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
495 | struct sbrec_chassis *ch; | |
496 | struct sbrec_encap *encap; | |
497 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
498 | const char *ch_name, *encap_type, *encap_ip; | |
499 | ||
500 | ch_name = ctx->argv[1]; | |
501 | encap_type = ctx->argv[2]; | |
502 | encap_ip = ctx->argv[3]; | |
503 | ||
504 | sbctl_context_populate_cache(ctx); | |
505 | if (may_exist) { | |
506 | struct sbctl_chassis *sbctl_ch; | |
507 | ||
508 | sbctl_ch = find_chassis(sbctl_ctx, ch_name, false); | |
509 | if (sbctl_ch) { | |
510 | return; | |
511 | } | |
512 | } | |
513 | check_conflicts(sbctl_ctx, ch_name, | |
514 | xasprintf("cannot create a chassis named %s", ch_name)); | |
515 | ch = sbrec_chassis_insert(ctx->txn); | |
516 | sbrec_chassis_set_name(ch, ch_name); | |
517 | encap = sbrec_encap_insert(ctx->txn); | |
518 | sbrec_encap_set_type(encap, encap_type); | |
519 | sbrec_encap_set_ip(encap, encap_ip); | |
520 | sbrec_chassis_set_encaps(ch, &encap, 1); | |
521 | sbctl_context_invalidate_cache(ctx); | |
522 | } | |
523 | ||
524 | static void | |
525 | cmd_chassis_del(struct ctl_context *ctx) | |
526 | { | |
527 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
528 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
529 | struct sbctl_chassis *sbctl_ch; | |
530 | ||
531 | sbctl_context_populate_cache(ctx); | |
532 | sbctl_ch = find_chassis(sbctl_ctx, ctx->argv[1], must_exist); | |
533 | if (sbctl_ch) { | |
534 | if (sbctl_ch->ch_cfg) { | |
535 | sbrec_chassis_delete(sbctl_ch->ch_cfg); | |
536 | } | |
537 | shash_find_and_delete(&sbctl_ctx->chassis, ctx->argv[1]); | |
538 | free(sbctl_ch); | |
539 | } | |
540 | } | |
541 | ||
542 | static void | |
543 | cmd_lport_bind(struct ctl_context *ctx) | |
544 | { | |
545 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
546 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
547 | struct sbctl_chassis *sbctl_ch; | |
548 | struct sbctl_port_binding *sbctl_bd; | |
549 | char *lport_name, *ch_name; | |
550 | ||
551 | /* port_binding must exist, chassis must exist! */ | |
552 | lport_name = ctx->argv[1]; | |
553 | ch_name = ctx->argv[2]; | |
554 | ||
555 | sbctl_context_populate_cache(ctx); | |
556 | sbctl_bd = find_port_binding(sbctl_ctx, lport_name, true); | |
557 | sbctl_ch = find_chassis(sbctl_ctx, ch_name, true); | |
558 | ||
559 | if (sbctl_bd->bd_cfg->chassis) { | |
560 | if (may_exist && sbctl_bd->bd_cfg->chassis == sbctl_ch->ch_cfg) { | |
561 | return; | |
562 | } else { | |
563 | ctl_fatal("lport (%s) has already been binded to chassis (%s)", | |
564 | lport_name, sbctl_bd->bd_cfg->chassis->name); | |
565 | } | |
566 | } | |
567 | sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, sbctl_ch->ch_cfg); | |
568 | sbctl_context_invalidate_cache(ctx); | |
569 | } | |
570 | ||
571 | static void | |
572 | cmd_lport_unbind(struct ctl_context *ctx) | |
573 | { | |
574 | struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); | |
575 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
576 | struct sbctl_port_binding *sbctl_bd; | |
577 | char *lport_name; | |
578 | ||
579 | lport_name = ctx->argv[1]; | |
580 | sbctl_context_populate_cache(ctx); | |
581 | sbctl_bd = find_port_binding(sbctl_ctx, lport_name, must_exist); | |
582 | if (sbctl_bd) { | |
583 | sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, NULL); | |
584 | } | |
585 | } | |
586 | ||
587 | \f | |
588 | static const struct ctl_table_class tables[] = { | |
589 | {&sbrec_table_chassis, | |
590 | {{&sbrec_table_chassis, &sbrec_chassis_col_name, NULL}, | |
591 | {NULL, NULL, NULL}}}, | |
592 | ||
593 | {&sbrec_table_encap, | |
594 | {{NULL, NULL, NULL}, | |
595 | {NULL, NULL, NULL}}}, | |
596 | ||
597 | {&sbrec_table_logical_flow, | |
598 | {{&sbrec_table_logical_flow, NULL, | |
599 | &sbrec_logical_flow_col_logical_datapath}, | |
600 | {NULL, NULL, NULL}}}, | |
601 | ||
602 | {&sbrec_table_multicast_group, | |
603 | {{NULL, NULL, NULL}, | |
604 | {NULL, NULL, NULL}}}, | |
605 | ||
606 | {&sbrec_table_datapath_binding, | |
607 | {{NULL, NULL, NULL}, | |
608 | {NULL, NULL, NULL}}}, | |
609 | ||
610 | {&sbrec_table_port_binding, | |
611 | {{&sbrec_table_port_binding, &sbrec_port_binding_col_logical_port, NULL}, | |
612 | {NULL, NULL, NULL}}}, | |
613 | ||
614 | {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} | |
615 | }; | |
616 | ||
617 | \f | |
618 | static void | |
619 | sbctl_context_init_command(struct sbctl_context *sbctl_ctx, | |
620 | struct ctl_command *command) | |
621 | { | |
622 | ctl_context_init_command(&sbctl_ctx->base, command); | |
623 | } | |
624 | ||
625 | static void | |
626 | sbctl_context_init(struct sbctl_context *sbctl_ctx, | |
627 | struct ctl_command *command, struct ovsdb_idl *idl, | |
628 | struct ovsdb_idl_txn *txn, | |
629 | struct ovsdb_symbol_table *symtab) | |
630 | { | |
631 | ctl_context_init(&sbctl_ctx->base, command, idl, txn, symtab, | |
632 | sbctl_context_invalidate_cache); | |
633 | sbctl_ctx->cache_valid = false; | |
634 | } | |
635 | ||
636 | static void | |
637 | sbctl_context_done_command(struct sbctl_context *sbctl_ctx, | |
638 | struct ctl_command *command) | |
639 | { | |
640 | ctl_context_done_command(&sbctl_ctx->base, command); | |
641 | } | |
642 | ||
643 | static void | |
644 | sbctl_context_done(struct sbctl_context *sbctl_ctx, | |
645 | struct ctl_command *command) | |
646 | { | |
647 | ctl_context_done(&sbctl_ctx->base, command); | |
648 | } | |
649 | ||
650 | static void | |
651 | run_prerequisites(struct ctl_command *commands, size_t n_commands, | |
652 | struct ovsdb_idl *idl) | |
653 | { | |
654 | struct ctl_command *c; | |
655 | ||
656 | for (c = commands; c < &commands[n_commands]; c++) { | |
657 | if (c->syntax->prerequisites) { | |
658 | struct sbctl_context sbctl_ctx; | |
659 | ||
660 | ds_init(&c->output); | |
661 | c->table = NULL; | |
662 | ||
663 | sbctl_context_init(&sbctl_ctx, c, idl, NULL, NULL); | |
664 | (c->syntax->prerequisites)(&sbctl_ctx.base); | |
665 | sbctl_context_done(&sbctl_ctx, c); | |
666 | ||
667 | ovs_assert(!c->output.string); | |
668 | ovs_assert(!c->table); | |
669 | } | |
670 | } | |
671 | } | |
672 | ||
673 | static void | |
674 | do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands, | |
675 | struct ovsdb_idl *idl) | |
676 | { | |
677 | struct ovsdb_idl_txn *txn; | |
678 | enum ovsdb_idl_txn_status status; | |
679 | struct ovsdb_symbol_table *symtab; | |
680 | struct sbctl_context sbctl_ctx; | |
681 | struct ctl_command *c; | |
682 | struct shash_node *node; | |
683 | char *error = NULL; | |
684 | ||
685 | txn = the_idl_txn = ovsdb_idl_txn_create(idl); | |
686 | if (dry_run) { | |
687 | ovsdb_idl_txn_set_dry_run(txn); | |
688 | } | |
689 | ||
690 | ovsdb_idl_txn_add_comment(txn, "ovs-sbctl: %s", args); | |
691 | ||
692 | symtab = ovsdb_symbol_table_create(); | |
693 | for (c = commands; c < &commands[n_commands]; c++) { | |
694 | ds_init(&c->output); | |
695 | c->table = NULL; | |
696 | } | |
697 | sbctl_context_init(&sbctl_ctx, NULL, idl, txn, symtab); | |
698 | for (c = commands; c < &commands[n_commands]; c++) { | |
699 | sbctl_context_init_command(&sbctl_ctx, c); | |
700 | if (c->syntax->run) { | |
701 | (c->syntax->run)(&sbctl_ctx.base); | |
702 | } | |
703 | sbctl_context_done_command(&sbctl_ctx, c); | |
704 | ||
705 | if (sbctl_ctx.base.try_again) { | |
706 | sbctl_context_done(&sbctl_ctx, NULL); | |
707 | goto try_again; | |
708 | } | |
709 | } | |
710 | sbctl_context_done(&sbctl_ctx, NULL); | |
711 | ||
712 | SHASH_FOR_EACH (node, &symtab->sh) { | |
713 | struct ovsdb_symbol *symbol = node->data; | |
714 | if (!symbol->created) { | |
715 | ctl_fatal("row id \"%s\" is referenced but never created (e.g. " | |
716 | "with \"-- --id=%s create ...\")", | |
717 | node->name, node->name); | |
718 | } | |
719 | if (!symbol->strong_ref) { | |
720 | if (!symbol->weak_ref) { | |
721 | VLOG_WARN("row id \"%s\" was created but no reference to it " | |
722 | "was inserted, so it will not actually appear in " | |
723 | "the database", node->name); | |
724 | } else { | |
725 | VLOG_WARN("row id \"%s\" was created but only a weak " | |
726 | "reference to it was inserted, so it will not " | |
727 | "actually appear in the database", node->name); | |
728 | } | |
729 | } | |
730 | } | |
731 | ||
732 | status = ovsdb_idl_txn_commit_block(txn); | |
733 | if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { | |
734 | for (c = commands; c < &commands[n_commands]; c++) { | |
735 | if (c->syntax->postprocess) { | |
736 | sbctl_context_init(&sbctl_ctx, c, idl, txn, symtab); | |
737 | (c->syntax->postprocess)(&sbctl_ctx.base); | |
738 | sbctl_context_done(&sbctl_ctx, c); | |
739 | } | |
740 | } | |
741 | } | |
742 | error = xstrdup(ovsdb_idl_txn_get_error(txn)); | |
743 | ||
744 | switch (status) { | |
745 | case TXN_UNCOMMITTED: | |
746 | case TXN_INCOMPLETE: | |
747 | OVS_NOT_REACHED(); | |
748 | ||
749 | case TXN_ABORTED: | |
750 | /* Should not happen--we never call ovsdb_idl_txn_abort(). */ | |
751 | ctl_fatal("transaction aborted"); | |
752 | ||
753 | case TXN_UNCHANGED: | |
754 | case TXN_SUCCESS: | |
755 | break; | |
756 | ||
757 | case TXN_TRY_AGAIN: | |
758 | goto try_again; | |
759 | ||
760 | case TXN_ERROR: | |
761 | ctl_fatal("transaction error: %s", error); | |
762 | ||
763 | case TXN_NOT_LOCKED: | |
764 | /* Should not happen--we never call ovsdb_idl_set_lock(). */ | |
765 | ctl_fatal("database not locked"); | |
766 | ||
767 | default: | |
768 | OVS_NOT_REACHED(); | |
769 | } | |
770 | free(error); | |
771 | ||
772 | ovsdb_symbol_table_destroy(symtab); | |
773 | ||
774 | for (c = commands; c < &commands[n_commands]; c++) { | |
775 | struct ds *ds = &c->output; | |
776 | ||
777 | if (c->table) { | |
778 | table_print(c->table, &table_style); | |
779 | } else if (oneline) { | |
780 | size_t j; | |
781 | ||
782 | ds_chomp(ds, '\n'); | |
783 | for (j = 0; j < ds->length; j++) { | |
784 | int ch = ds->string[j]; | |
785 | switch (ch) { | |
786 | case '\n': | |
787 | fputs("\\n", stdout); | |
788 | break; | |
789 | ||
790 | case '\\': | |
791 | fputs("\\\\", stdout); | |
792 | break; | |
793 | ||
794 | default: | |
795 | putchar(ch); | |
796 | } | |
797 | } | |
798 | putchar('\n'); | |
799 | } else { | |
800 | fputs(ds_cstr(ds), stdout); | |
801 | } | |
802 | ds_destroy(&c->output); | |
803 | table_destroy(c->table); | |
804 | free(c->table); | |
805 | ||
806 | shash_destroy_free_data(&c->options); | |
807 | } | |
808 | free(commands); | |
809 | ovsdb_idl_txn_destroy(txn); | |
810 | ovsdb_idl_destroy(idl); | |
811 | ||
812 | exit(EXIT_SUCCESS); | |
813 | ||
814 | try_again: | |
815 | /* Our transaction needs to be rerun, or a prerequisite was not met. Free | |
816 | * resources and return so that the caller can try again. */ | |
817 | if (txn) { | |
818 | ovsdb_idl_txn_abort(txn); | |
819 | ovsdb_idl_txn_destroy(txn); | |
820 | the_idl_txn = NULL; | |
821 | } | |
822 | ovsdb_symbol_table_destroy(symtab); | |
823 | for (c = commands; c < &commands[n_commands]; c++) { | |
824 | ds_destroy(&c->output); | |
825 | table_destroy(c->table); | |
826 | free(c->table); | |
827 | } | |
828 | free(error); | |
829 | } | |
830 | ||
831 | /* Frees the current transaction and the underlying IDL and then calls | |
832 | * exit(status). | |
833 | * | |
834 | * Freeing the transaction and the IDL is not strictly necessary, but it makes | |
835 | * for a clean memory leak report from valgrind in the normal case. That makes | |
836 | * it easier to notice real memory leaks. */ | |
837 | static void | |
838 | sbctl_exit(int status) | |
839 | { | |
840 | if (the_idl_txn) { | |
841 | ovsdb_idl_txn_abort(the_idl_txn); | |
842 | ovsdb_idl_txn_destroy(the_idl_txn); | |
843 | } | |
844 | ovsdb_idl_destroy(the_idl); | |
845 | exit(status); | |
846 | } | |
847 | ||
848 | static const struct ctl_command_syntax sbctl_commands[] = { | |
849 | /* Chassis commands. */ | |
850 | {"chassis-add", 3, 3, "CHASSIS ENCAP-TYPE ENCAP-IP", pre_get_info, | |
851 | cmd_chassis_add, NULL, "--may-exist", RW}, | |
852 | {"chassis-del", 1, 1, "CHASSIS", pre_get_info, cmd_chassis_del, NULL, | |
853 | "--if-exists", RW}, | |
854 | ||
855 | /* Port binding commands. */ | |
856 | {"lport-bind", 2, 2, "LPORT CHASSIS", pre_get_info, cmd_lport_bind, NULL, | |
857 | "--may-exist", RW}, | |
858 | {"lport-unbind", 1, 1, "LPORT", pre_get_info, cmd_lport_unbind, NULL, | |
859 | "--if-exists", RW}, | |
860 | ||
861 | /* SSL commands (To Be Added). */ | |
862 | ||
863 | {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, | |
864 | }; | |
865 | ||
866 | /* Registers sbctl and common db commands. */ | |
867 | static void | |
868 | sbctl_cmd_init(void) | |
869 | { | |
870 | ctl_init(tables, cmd_show_tables, sbctl_exit); | |
871 | ctl_register_commands(sbctl_commands); | |
872 | } |