]>
Commit | Line | Data |
---|---|---|
a416ff28 RB |
1 | /* |
2 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | * you may not use this file except in compliance with the License. | |
4 | * You may obtain a copy of the License at: | |
5 | * | |
6 | * http://www.apache.org/licenses/LICENSE-2.0 | |
7 | * | |
8 | * Unless required by applicable law or agreed to in writing, software | |
9 | * distributed under the License is distributed on an "AS IS" BASIS, | |
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | * See the License for the specific language governing permissions and | |
12 | * limitations under the License. | |
13 | */ | |
14 | ||
15 | #include <config.h> | |
16 | ||
17 | #include <getopt.h> | |
bf5fa52a | 18 | #include <inttypes.h> |
a416ff28 RB |
19 | #include <stdlib.h> |
20 | #include <stdio.h> | |
21 | ||
22 | #include "command-line.h" | |
70799ae8 | 23 | #include "db-ctl-base.h" |
a416ff28 RB |
24 | #include "dirs.h" |
25 | #include "fatal-signal.h" | |
ee89ea7b | 26 | #include "openvswitch/json.h" |
e3df8838 | 27 | #include "ovn/lib/ovn-nb-idl.h" |
8ad609c0 | 28 | #include "ovn/lib/ovn-util.h" |
69e73316 | 29 | #include "packets.h" |
a416ff28 | 30 | #include "poll-loop.h" |
afc9da0b | 31 | #include "process.h" |
2a547e72 | 32 | #include "smap.h" |
4685e523 | 33 | #include "sset.h" |
a416ff28 RB |
34 | #include "stream.h" |
35 | #include "stream-ssl.h" | |
2a547e72 | 36 | #include "svec.h" |
70799ae8 BP |
37 | #include "table.h" |
38 | #include "timeval.h" | |
a416ff28 RB |
39 | #include "util.h" |
40 | #include "openvswitch/vlog.h" | |
41 | ||
70799ae8 | 42 | VLOG_DEFINE_THIS_MODULE(nbctl); |
a416ff28 | 43 | |
70799ae8 BP |
44 | /* --db: The database server to contact. */ |
45 | static const char *db; | |
46 | ||
47 | /* --oneline: Write each command's output as a single line? */ | |
48 | static bool oneline; | |
49 | ||
50 | /* --dry-run: Do not commit any changes. */ | |
51 | static bool dry_run; | |
52 | ||
fa183acc BP |
53 | /* --wait=TYPE: Wait for configuration change to take effect? */ |
54 | enum nbctl_wait_type { | |
55 | NBCTL_WAIT_NONE, /* Do not wait. */ | |
56 | NBCTL_WAIT_SB, /* Wait for southbound database updates. */ | |
57 | NBCTL_WAIT_HV /* Wait for hypervisors to catch up. */ | |
58 | }; | |
59 | static enum nbctl_wait_type wait_type = NBCTL_WAIT_NONE; | |
60 | ||
de32cec7 BP |
61 | /* Should we wait (if specified by 'wait_type') even if the commands don't |
62 | * change the database at all? */ | |
63 | static bool force_wait = false; | |
64 | ||
70799ae8 BP |
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 nbctl_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 nbctl_exit(int status); | |
77 | ||
78 | static void nbctl_cmd_init(void); | |
79 | OVS_NO_RETURN static void usage(void); | |
80 | static void parse_options(int argc, char *argv[], struct shash *local_options); | |
70799ae8 BP |
81 | static void run_prerequisites(struct ctl_command[], size_t n_commands, |
82 | struct ovsdb_idl *); | |
8ee574c3 | 83 | static bool do_nbctl(const char *args, struct ctl_command *, size_t n, |
70799ae8 | 84 | struct ovsdb_idl *); |
281977f7 NS |
85 | static const struct nbrec_dhcp_options *dhcp_options_get( |
86 | struct ctl_context *ctx, const char *id, bool must_exist); | |
70799ae8 BP |
87 | |
88 | int | |
89 | main(int argc, char *argv[]) | |
90 | { | |
a416ff28 | 91 | struct ovsdb_idl *idl; |
70799ae8 BP |
92 | struct ctl_command *commands; |
93 | struct shash local_options; | |
94 | unsigned int seqno; | |
95 | size_t n_commands; | |
96 | char *args; | |
a416ff28 | 97 | |
70799ae8 BP |
98 | set_program_name(argv[0]); |
99 | fatal_ignore_sigpipe(); | |
100 | vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); | |
45863ce5 | 101 | vlog_set_levels_from_string_assert("reconnect:warn"); |
70799ae8 BP |
102 | |
103 | nbctl_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, | |
108 | "Called as %s", args); | |
109 | ||
110 | /* Parse command line. */ | |
111 | shash_init(&local_options); | |
112 | parse_options(argc, argv, &local_options); | |
113 | commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, | |
114 | &n_commands); | |
115 | ||
116 | if (timeout) { | |
117 | time_alarm(timeout); | |
118 | } | |
119 | ||
120 | /* Initialize IDL. */ | |
121 | idl = the_idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false); | |
122 | run_prerequisites(commands, n_commands, idl); | |
123 | ||
124 | /* Execute the commands. | |
125 | * | |
126 | * 'seqno' is the database sequence number for which we last tried to | |
127 | * execute our transaction. There's no point in trying to commit more than | |
128 | * once for any given sequence number, because if the transaction fails | |
129 | * it's because the database changed and we need to obtain an up-to-date | |
130 | * view of the database before we try the transaction again. */ | |
131 | seqno = ovsdb_idl_get_seqno(idl); | |
132 | for (;;) { | |
133 | ovsdb_idl_run(idl); | |
134 | if (!ovsdb_idl_is_alive(idl)) { | |
135 | int retval = ovsdb_idl_get_last_error(idl); | |
136 | ctl_fatal("%s: database connection failed (%s)", | |
137 | db, ovs_retval_to_string(retval)); | |
138 | } | |
139 | ||
140 | if (seqno != ovsdb_idl_get_seqno(idl)) { | |
141 | seqno = ovsdb_idl_get_seqno(idl); | |
8ee574c3 WT |
142 | if (do_nbctl(args, commands, n_commands, idl)) { |
143 | free(args); | |
144 | exit(EXIT_SUCCESS); | |
145 | } | |
70799ae8 | 146 | } |
a416ff28 | 147 | |
70799ae8 BP |
148 | if (seqno == ovsdb_idl_get_seqno(idl)) { |
149 | ovsdb_idl_wait(idl); | |
150 | poll_block(); | |
151 | } | |
152 | } | |
153 | } | |
154 | ||
70799ae8 BP |
155 | static void |
156 | parse_options(int argc, char *argv[], struct shash *local_options) | |
157 | { | |
158 | enum { | |
159 | OPT_DB = UCHAR_MAX + 1, | |
160 | OPT_NO_SYSLOG, | |
fa183acc BP |
161 | OPT_NO_WAIT, |
162 | OPT_WAIT, | |
70799ae8 BP |
163 | OPT_DRY_RUN, |
164 | OPT_ONELINE, | |
165 | OPT_LOCAL, | |
166 | OPT_COMMANDS, | |
167 | OPT_OPTIONS, | |
168 | VLOG_OPTION_ENUMS, | |
169 | TABLE_OPTION_ENUMS | |
170 | }; | |
171 | static const struct option global_long_options[] = { | |
172 | {"db", required_argument, NULL, OPT_DB}, | |
173 | {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, | |
fa183acc BP |
174 | {"no-wait", no_argument, NULL, OPT_NO_WAIT}, |
175 | {"wait", required_argument, NULL, OPT_WAIT}, | |
70799ae8 BP |
176 | {"dry-run", no_argument, NULL, OPT_DRY_RUN}, |
177 | {"oneline", no_argument, NULL, OPT_ONELINE}, | |
178 | {"timeout", required_argument, NULL, 't'}, | |
179 | {"help", no_argument, NULL, 'h'}, | |
180 | {"commands", no_argument, NULL, OPT_COMMANDS}, | |
181 | {"options", no_argument, NULL, OPT_OPTIONS}, | |
182 | {"version", no_argument, NULL, 'V'}, | |
183 | VLOG_LONG_OPTIONS, | |
184 | STREAM_SSL_LONG_OPTIONS, | |
185 | TABLE_LONG_OPTIONS, | |
186 | {NULL, 0, NULL, 0}, | |
187 | }; | |
188 | const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; | |
189 | char *tmp, *short_options; | |
190 | ||
191 | struct option *options; | |
192 | size_t allocated_options; | |
193 | size_t n_options; | |
194 | size_t i; | |
195 | ||
196 | tmp = ovs_cmdl_long_options_to_short_options(global_long_options); | |
197 | short_options = xasprintf("+%s", tmp); | |
198 | free(tmp); | |
199 | ||
200 | /* We want to parse both global and command-specific options here, but | |
201 | * getopt_long() isn't too convenient for the job. We copy our global | |
202 | * options into a dynamic array, then append all of the command-specific | |
203 | * options. */ | |
204 | options = xmemdup(global_long_options, sizeof global_long_options); | |
205 | allocated_options = ARRAY_SIZE(global_long_options); | |
206 | n_options = n_global_long_options; | |
207 | ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); | |
208 | table_style.format = TF_LIST; | |
209 | ||
210 | for (;;) { | |
211 | int idx; | |
212 | int c; | |
213 | ||
214 | c = getopt_long(argc, argv, short_options, options, &idx); | |
215 | if (c == -1) { | |
216 | break; | |
217 | } | |
218 | ||
219 | switch (c) { | |
220 | case OPT_DB: | |
221 | db = optarg; | |
222 | break; | |
223 | ||
224 | case OPT_ONELINE: | |
225 | oneline = true; | |
226 | break; | |
227 | ||
228 | case OPT_NO_SYSLOG: | |
922fed06 | 229 | vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); |
70799ae8 BP |
230 | break; |
231 | ||
fa183acc BP |
232 | case OPT_NO_WAIT: |
233 | wait_type = NBCTL_WAIT_NONE; | |
234 | break; | |
235 | ||
236 | case OPT_WAIT: | |
237 | if (!strcmp(optarg, "none")) { | |
238 | wait_type = NBCTL_WAIT_NONE; | |
239 | } else if (!strcmp(optarg, "sb")) { | |
240 | wait_type = NBCTL_WAIT_SB; | |
241 | } else if (!strcmp(optarg, "hv")) { | |
242 | wait_type = NBCTL_WAIT_HV; | |
243 | } else { | |
244 | ctl_fatal("argument to --wait must be " | |
245 | "\"none\", \"sb\", or \"hv\""); | |
246 | } | |
247 | break; | |
248 | ||
70799ae8 BP |
249 | case OPT_DRY_RUN: |
250 | dry_run = true; | |
251 | break; | |
252 | ||
253 | case OPT_LOCAL: | |
254 | if (shash_find(local_options, options[idx].name)) { | |
255 | ctl_fatal("'%s' option specified multiple times", | |
256 | options[idx].name); | |
257 | } | |
258 | shash_add_nocopy(local_options, | |
259 | xasprintf("--%s", options[idx].name), | |
2225c0b9 | 260 | nullable_xstrdup(optarg)); |
70799ae8 BP |
261 | break; |
262 | ||
263 | case 'h': | |
264 | usage(); | |
265 | exit(EXIT_SUCCESS); | |
266 | ||
267 | case OPT_COMMANDS: | |
268 | ctl_print_commands(); | |
269 | ||
270 | case OPT_OPTIONS: | |
271 | ctl_print_options(global_long_options); | |
272 | ||
273 | case 'V': | |
274 | ovs_print_version(0, 0); | |
275 | printf("DB Schema %s\n", nbrec_get_db_version()); | |
276 | exit(EXIT_SUCCESS); | |
277 | ||
278 | case 't': | |
279 | timeout = strtoul(optarg, NULL, 10); | |
280 | if (timeout < 0) { | |
281 | ctl_fatal("value %s on -t or --timeout is invalid", optarg); | |
282 | } | |
283 | break; | |
284 | ||
285 | VLOG_OPTION_HANDLERS | |
286 | TABLE_OPTION_HANDLERS(&table_style) | |
287 | STREAM_SSL_OPTION_HANDLERS | |
288 | ||
289 | case '?': | |
290 | exit(EXIT_FAILURE); | |
291 | ||
292 | default: | |
293 | abort(); | |
294 | } | |
295 | } | |
241d9625 | 296 | free(short_options); |
70799ae8 BP |
297 | |
298 | if (!db) { | |
8ad609c0 | 299 | db = default_nb_db(); |
70799ae8 BP |
300 | } |
301 | ||
302 | for (i = n_global_long_options; options[i].name; i++) { | |
303 | free(CONST_CAST(char *, options[i].name)); | |
304 | } | |
305 | free(options); | |
306 | } | |
a416ff28 RB |
307 | |
308 | static void | |
309 | usage(void) | |
310 | { | |
311 | printf("\ | |
312 | %s: OVN northbound DB management utility\n\ | |
313 | usage: %s [OPTIONS] COMMAND [ARG...]\n\ | |
314 | \n\ | |
907a0edf | 315 | General commands:\n\ |
fa183acc | 316 | init initialize the database\n\ |
907a0edf | 317 | show print overview of database contents\n\ |
ea46a4e9 | 318 | show SWITCH print overview of database contents for SWITCH\n\ |
fa2a27b2 | 319 | show ROUTER print overview of database contents for ROUTER\n\ |
907a0edf | 320 | \n\ |
a367622a | 321 | Logical switch commands:\n\ |
ea46a4e9 JP |
322 | ls-add [SWITCH] create a logical switch named SWITCH\n\ |
323 | ls-del SWITCH delete SWITCH and all its ports\n\ | |
324 | ls-list print the names of all logical switches\n\ | |
a416ff28 | 325 | \n\ |
8f96455d | 326 | ACL commands:\n\ |
ea46a4e9 JP |
327 | acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\ |
328 | add an ACL to SWITCH\n\ | |
329 | acl-del SWITCH [DIRECTION [PRIORITY MATCH]]\n\ | |
330 | remove ACLs from SWITCH\n\ | |
331 | acl-list SWITCH print ACLs for SWITCH\n\ | |
8f96455d | 332 | \n\ |
80f408f4 | 333 | Logical switch port commands:\n\ |
ea46a4e9 JP |
334 | lsp-add SWITCH PORT add logical port PORT on SWITCH\n\ |
335 | lsp-add SWITCH PORT PARENT TAG\n\ | |
336 | add logical port PORT on SWITCH with PARENT\n\ | |
bf5fa52a | 337 | on TAG\n\ |
31ed1192 | 338 | lsp-del PORT delete PORT from its attached switch\n\ |
ea46a4e9 | 339 | lsp-list SWITCH print the names of all logical ports on SWITCH\n\ |
31ed1192 JP |
340 | lsp-get-parent PORT get the parent of PORT if set\n\ |
341 | lsp-get-tag PORT get the PORT's tag if set\n\ | |
342 | lsp-set-addresses PORT [ADDRESS]...\n\ | |
343 | set MAC or MAC+IP addresses for PORT.\n\ | |
dd34473c | 344 | lsp-get-addresses PORT get a list of MAC or MAC+IP addresses on PORT\n\ |
31ed1192 JP |
345 | lsp-set-port-security PORT [ADDRS]...\n\ |
346 | set port security addresses for PORT.\n\ | |
347 | lsp-get-port-security PORT get PORT's port security addresses\n\ | |
348 | lsp-get-up PORT get state of PORT ('up' or 'down')\n\ | |
349 | lsp-set-enabled PORT STATE\n\ | |
350 | set administrative state PORT\n\ | |
e9e8bcdf | 351 | ('enabled' or 'disabled')\n\ |
31ed1192 | 352 | lsp-get-enabled PORT get administrative state PORT\n\ |
e9e8bcdf | 353 | ('enabled' or 'disabled')\n\ |
31ed1192 JP |
354 | lsp-set-type PORT TYPE set the type for PORT\n\ |
355 | lsp-get-type PORT get the type for PORT\n\ | |
356 | lsp-set-options PORT KEY=VALUE [KEY=VALUE]...\n\ | |
357 | set options related to the type of PORT\n\ | |
358 | lsp-get-options PORT get the type specific options for PORT\n\ | |
281977f7 NS |
359 | lsp-set-dhcpv4-options PORT [DHCP_OPTIONS_UUID]\n\ |
360 | set dhcpv4 options for PORT\n\ | |
361 | lsp-get-dhcpv4-options PORT get the dhcpv4 options for PORT\n\ | |
a416ff28 | 362 | \n\ |
fa2a27b2 JP |
363 | Logical router commands:\n\ |
364 | lr-add [ROUTER] create a logical router named ROUTER\n\ | |
365 | lr-del ROUTER delete ROUTER and all its ports\n\ | |
366 | lr-list print the names of all logical routers\n\ | |
367 | \n\ | |
31114af7 | 368 | Logical router port commands:\n\ |
4685e523 | 369 | lrp-add ROUTER PORT MAC NETWORK... [peer=PEER]\n\ |
31114af7 JP |
370 | add logical port PORT on ROUTER\n\ |
371 | lrp-del PORT delete PORT from its attached router\n\ | |
372 | lrp-list ROUTER print the names of all ports on ROUTER\n\ | |
373 | lrp-set-enabled PORT STATE\n\ | |
374 | set administrative state PORT\n\ | |
375 | ('enabled' or 'disabled')\n\ | |
376 | lrp-get-enabled PORT get administrative state PORT\n\ | |
377 | ('enabled' or 'disabled')\n\ | |
378 | \n\ | |
e48ccf3c | 379 | Route commands:\n\ |
440a9f4b | 380 | [--policy=POLICY] lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\ |
e48ccf3c JP |
381 | add a route to ROUTER\n\ |
382 | lr-route-del ROUTER [PREFIX]\n\ | |
383 | remove routes from ROUTER\n\ | |
384 | lr-route-list ROUTER print routes for ROUTER\n\ | |
385 | \n\ | |
e2bfcad6 | 386 | LB commands:\n\ |
387 | lb-add LB VIP[:PORT] IP[:PORT]... [PROTOCOL]\n\ | |
388 | create a load-balancer or add a VIP to an\n\ | |
389 | existing load balancer\n\ | |
390 | lb-del LB [VIP] remove a load-balancer or just the VIP from\n\ | |
391 | the load balancer\n\ | |
392 | lb-list [LB] print load-balancers\n\ | |
393 | lr-lb-add ROUTER LB add a load-balancer to ROUTER\n\ | |
394 | lr-lb-del ROUTER [LB] remove load-balancers from ROUTER\n\ | |
395 | lr-lb-list ROUTER print load-balancers\n\ | |
396 | ls-lb-add SWITCH LB add a load-balancer to SWITCH\n\ | |
397 | ls-lb-del SWITCH [LB] remove load-balancers from SWITCH\n\ | |
398 | ls-lb-list SWITCH print load-balancers\n\ | |
281977f7 NS |
399 | \n\ |
400 | DHCP Options commands:\n\ | |
401 | dhcp-options-create CIDR [EXTERNAL_IDS]\n\ | |
402 | create a DHCP options row with CIDR\n\ | |
403 | dhcp-options-del DHCP_OPTIONS_UUID\n\ | |
404 | delete DHCP_OPTIONS_UUID\n\ | |
405 | dhcp-options-list \n\ | |
406 | lists the DHCP_Options rows\n\ | |
407 | dhcp-options-set-options DHCP_OPTIONS_UUID KEY=VALUE [KEY=VALUE]...\n\ | |
b4ec31a9 | 408 | set DHCP options for DHCP_OPTIONS_UUID\n\ |
281977f7 | 409 | dhcp-options-get-options DHCO_OPTIONS_UUID \n\ |
b4ec31a9 | 410 | displays the DHCP options for DHCP_OPTIONS_UUID\n\ |
281977f7 | 411 | \n\ |
7ba0c32f WL |
412 | %s\ |
413 | \n\ | |
de32cec7 BP |
414 | Synchronization command (use with --wait=sb|hv):\n\ |
415 | sync wait even for earlier changes to take effect\n\ | |
416 | \n\ | |
a416ff28 | 417 | Options:\n\ |
70799ae8 BP |
418 | --db=DATABASE connect to DATABASE\n\ |
419 | (default: %s)\n\ | |
fa183acc BP |
420 | --no-wait, --wait=none do not wait for OVN reconfiguration (default)\n\ |
421 | --wait=sb wait for southbound database update\n\ | |
422 | --wait=hv wait for all chassis to catch up\n\ | |
70799ae8 BP |
423 | -t, --timeout=SECS wait at most SECS seconds\n\ |
424 | --dry-run do not commit changes to database\n\ | |
425 | --oneline print exactly one line of output per command\n", | |
8ad609c0 BP |
426 | program_name, program_name, ctl_get_db_cmd_usage(), |
427 | default_nb_db()); | |
a416ff28 | 428 | vlog_usage(); |
70799ae8 BP |
429 | printf("\ |
430 | --no-syslog equivalent to --verbose=nbctl:syslog:warn\n"); | |
431 | printf("\n\ | |
432 | Other options:\n\ | |
433 | -h, --help display this help message\n\ | |
434 | -V, --version display version information\n"); | |
435 | exit(EXIT_SUCCESS); | |
a416ff28 RB |
436 | } |
437 | \f | |
7127583d | 438 | |
fa2a27b2 | 439 | /* Find a logical router given its id. */ |
7127583d | 440 | static const struct nbrec_logical_router * |
fa2a27b2 | 441 | lr_by_name_or_uuid(struct ctl_context *ctx, const char *id, |
7127583d NG |
442 | bool must_exist) |
443 | { | |
fa2a27b2 | 444 | const struct nbrec_logical_router *lr = NULL; |
7127583d | 445 | bool is_uuid = false; |
fa2a27b2 | 446 | struct uuid lr_uuid; |
7127583d | 447 | |
fa2a27b2 | 448 | if (uuid_from_string(&lr_uuid, id)) { |
7127583d | 449 | is_uuid = true; |
fa2a27b2 | 450 | lr = nbrec_logical_router_get_for_uuid(ctx->idl, &lr_uuid); |
7127583d NG |
451 | } |
452 | ||
fa2a27b2 | 453 | if (!lr) { |
7127583d NG |
454 | const struct nbrec_logical_router *iter; |
455 | ||
456 | NBREC_LOGICAL_ROUTER_FOR_EACH(iter, ctx->idl) { | |
457 | if (strcmp(iter->name, id)) { | |
458 | continue; | |
459 | } | |
fa2a27b2 | 460 | if (lr) { |
7127583d NG |
461 | ctl_fatal("Multiple logical routers named '%s'. " |
462 | "Use a UUID.", id); | |
463 | } | |
fa2a27b2 | 464 | lr = iter; |
7127583d NG |
465 | } |
466 | } | |
467 | ||
fa2a27b2 JP |
468 | if (!lr && must_exist) { |
469 | ctl_fatal("%s: router %s not found", id, is_uuid ? "UUID" : "name"); | |
7127583d NG |
470 | } |
471 | ||
fa2a27b2 | 472 | return lr; |
7127583d NG |
473 | } |
474 | ||
a416ff28 | 475 | static const struct nbrec_logical_switch * |
ea46a4e9 | 476 | ls_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist) |
a416ff28 | 477 | { |
ea46a4e9 | 478 | const struct nbrec_logical_switch *ls = NULL; |
a416ff28 | 479 | |
ea46a4e9 JP |
480 | struct uuid ls_uuid; |
481 | bool is_uuid = uuid_from_string(&ls_uuid, id); | |
d24d3372 | 482 | if (is_uuid) { |
ea46a4e9 | 483 | ls = nbrec_logical_switch_get_for_uuid(ctx->idl, &ls_uuid); |
c1f4da63 RB |
484 | } |
485 | ||
ea46a4e9 | 486 | if (!ls) { |
a416ff28 RB |
487 | const struct nbrec_logical_switch *iter; |
488 | ||
70799ae8 | 489 | NBREC_LOGICAL_SWITCH_FOR_EACH(iter, ctx->idl) { |
a416ff28 RB |
490 | if (strcmp(iter->name, id)) { |
491 | continue; | |
492 | } | |
ea46a4e9 | 493 | if (ls) { |
d24d3372 BP |
494 | ctl_fatal("Multiple logical switches named '%s'. " |
495 | "Use a UUID.", id); | |
a416ff28 | 496 | } |
ea46a4e9 | 497 | ls = iter; |
a416ff28 RB |
498 | } |
499 | } | |
500 | ||
ea46a4e9 JP |
501 | if (!ls && must_exist) { |
502 | ctl_fatal("%s: switch %s not found", id, is_uuid ? "UUID" : "name"); | |
a416ff28 RB |
503 | } |
504 | ||
ea46a4e9 | 505 | return ls; |
a416ff28 RB |
506 | } |
507 | ||
e2bfcad6 | 508 | static const struct nbrec_load_balancer * |
509 | lb_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist) | |
510 | { | |
511 | const struct nbrec_load_balancer *lb = NULL; | |
512 | ||
513 | struct uuid lb_uuid; | |
514 | bool is_uuid = uuid_from_string(&lb_uuid, id); | |
515 | if (is_uuid) { | |
516 | lb = nbrec_load_balancer_get_for_uuid(ctx->idl, &lb_uuid); | |
517 | } | |
518 | ||
519 | if (!lb) { | |
520 | const struct nbrec_load_balancer *iter; | |
521 | ||
522 | NBREC_LOAD_BALANCER_FOR_EACH(iter, ctx->idl) { | |
523 | if (strcmp(iter->name, id)) { | |
524 | continue; | |
525 | } | |
526 | if (lb) { | |
527 | ctl_fatal("Multiple load balancers named '%s'. " | |
528 | "Use a UUID.", id); | |
529 | } | |
530 | lb = iter; | |
531 | } | |
532 | } | |
533 | ||
534 | if (!lb && must_exist) { | |
535 | ctl_fatal("%s: load balancer %s not found", id, | |
536 | is_uuid ? "UUID" : "name"); | |
537 | } | |
538 | ||
539 | return lb; | |
540 | } | |
541 | ||
fa2a27b2 JP |
542 | /* Given pointer to logical router, this routine prints the router |
543 | * information. */ | |
7127583d | 544 | static void |
fa2a27b2 | 545 | print_lr(const struct nbrec_logical_router *lr, struct ds *s) |
7127583d | 546 | { |
fa2a27b2 JP |
547 | ds_put_format(s, " router "UUID_FMT" (%s)\n", |
548 | UUID_ARGS(&lr->header_.uuid), lr->name); | |
7127583d | 549 | |
fa2a27b2 | 550 | for (size_t i = 0; i < lr->n_ports; i++) { |
31114af7 JP |
551 | const struct nbrec_logical_router_port *lrp = lr->ports[i]; |
552 | ds_put_format(s, " port %s\n", lrp->name); | |
553 | if (lrp->mac) { | |
7127583d | 554 | ds_put_cstr(s, " mac: "); |
34a88a45 JP |
555 | ds_put_format(s, "\"%s\"\n", lrp->mac); |
556 | } | |
557 | if (lrp->n_networks) { | |
558 | ds_put_cstr(s, " networks: ["); | |
559 | for (size_t j = 0; j < lrp->n_networks; j++) { | |
560 | ds_put_format(s, "%s\"%s\"", | |
561 | j == 0 ? "" : ", ", | |
562 | lrp->networks[j]); | |
563 | } | |
564 | ds_put_cstr(s, "]\n"); | |
7127583d | 565 | } |
7127583d NG |
566 | } |
567 | } | |
568 | ||
907a0edf | 569 | static void |
ea46a4e9 | 570 | print_ls(const struct nbrec_logical_switch *ls, struct ds *s) |
907a0edf | 571 | { |
ea46a4e9 JP |
572 | ds_put_format(s, " switch "UUID_FMT" (%s)\n", |
573 | UUID_ARGS(&ls->header_.uuid), ls->name); | |
907a0edf | 574 | |
ea46a4e9 JP |
575 | for (size_t i = 0; i < ls->n_ports; i++) { |
576 | const struct nbrec_logical_switch_port *lsp = ls->ports[i]; | |
907a0edf | 577 | |
80f408f4 JP |
578 | ds_put_format(s, " port %s\n", lsp->name); |
579 | if (lsp->parent_name) { | |
580 | ds_put_format(s, " parent: %s\n", lsp->parent_name); | |
bb4c7107 | 581 | } |
80f408f4 JP |
582 | if (lsp->n_tag) { |
583 | ds_put_format(s, " tag: %"PRIu64"\n", lsp->tag[0]); | |
445a266a | 584 | } |
80f408f4 | 585 | if (lsp->n_addresses) { |
ebd3ce14 | 586 | ds_put_cstr(s, " addresses: ["); |
80f408f4 | 587 | for (size_t j = 0; j < lsp->n_addresses; j++) { |
ebd3ce14 RB |
588 | ds_put_format(s, "%s\"%s\"", |
589 | j == 0 ? "" : ", ", | |
80f408f4 | 590 | lsp->addresses[j]); |
907a0edf | 591 | } |
ebd3ce14 | 592 | ds_put_cstr(s, "]\n"); |
907a0edf JP |
593 | } |
594 | } | |
595 | } | |
596 | ||
fa183acc BP |
597 | static void |
598 | nbctl_init(struct ctl_context *ctx OVS_UNUSED) | |
599 | { | |
600 | } | |
601 | ||
de32cec7 BP |
602 | static void |
603 | nbctl_pre_sync(struct ctl_context *ctx OVS_UNUSED) | |
604 | { | |
605 | if (wait_type != NBCTL_WAIT_NONE) { | |
606 | force_wait = true; | |
607 | } else { | |
608 | VLOG_INFO("\"sync\" command has no effect without --wait"); | |
609 | } | |
610 | } | |
611 | ||
612 | static void | |
613 | nbctl_sync(struct ctl_context *ctx OVS_UNUSED) | |
614 | { | |
615 | } | |
616 | ||
907a0edf | 617 | static void |
70799ae8 | 618 | nbctl_show(struct ctl_context *ctx) |
907a0edf | 619 | { |
ea46a4e9 | 620 | const struct nbrec_logical_switch *ls; |
907a0edf JP |
621 | |
622 | if (ctx->argc == 2) { | |
ea46a4e9 JP |
623 | ls = ls_by_name_or_uuid(ctx, ctx->argv[1], false); |
624 | if (ls) { | |
625 | print_ls(ls, &ctx->output); | |
907a0edf JP |
626 | } |
627 | } else { | |
ea46a4e9 JP |
628 | NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) { |
629 | print_ls(ls, &ctx->output); | |
907a0edf JP |
630 | } |
631 | } | |
fa2a27b2 | 632 | const struct nbrec_logical_router *lr; |
7127583d NG |
633 | |
634 | if (ctx->argc == 2) { | |
fa2a27b2 JP |
635 | lr = lr_by_name_or_uuid(ctx, ctx->argv[1], false); |
636 | if (lr) { | |
637 | print_lr(lr, &ctx->output); | |
7127583d NG |
638 | } |
639 | } else { | |
fa2a27b2 JP |
640 | NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) { |
641 | print_lr(lr, &ctx->output); | |
7127583d NG |
642 | } |
643 | } | |
644 | } | |
645 | ||
a416ff28 | 646 | static void |
ea46a4e9 | 647 | nbctl_ls_add(struct ctl_context *ctx) |
a416ff28 | 648 | { |
ea46a4e9 | 649 | const char *ls_name = ctx->argc == 2 ? ctx->argv[1] : NULL; |
4ff6642f BP |
650 | |
651 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
652 | bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL; | |
653 | if (may_exist && add_duplicate) { | |
654 | ctl_fatal("--may-exist and --add-duplicate may not be used together"); | |
655 | } | |
656 | ||
ea46a4e9 | 657 | if (ls_name) { |
4ff6642f | 658 | if (!add_duplicate) { |
ea46a4e9 JP |
659 | const struct nbrec_logical_switch *ls; |
660 | NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) { | |
661 | if (!strcmp(ls->name, ls_name)) { | |
4ff6642f BP |
662 | if (may_exist) { |
663 | return; | |
664 | } | |
ea46a4e9 JP |
665 | ctl_fatal("%s: a switch with this name already exists", |
666 | ls_name); | |
4ff6642f BP |
667 | } |
668 | } | |
669 | } | |
670 | } else if (may_exist) { | |
671 | ctl_fatal("--may-exist requires specifying a name"); | |
672 | } else if (add_duplicate) { | |
673 | ctl_fatal("--add-duplicate requires specifying a name"); | |
674 | } | |
675 | ||
ea46a4e9 JP |
676 | struct nbrec_logical_switch *ls; |
677 | ls = nbrec_logical_switch_insert(ctx->txn); | |
678 | if (ls_name) { | |
679 | nbrec_logical_switch_set_name(ls, ls_name); | |
a416ff28 RB |
680 | } |
681 | } | |
682 | ||
683 | static void | |
ea46a4e9 | 684 | nbctl_ls_del(struct ctl_context *ctx) |
a416ff28 | 685 | { |
d24d3372 | 686 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); |
a416ff28 | 687 | const char *id = ctx->argv[1]; |
ea46a4e9 | 688 | const struct nbrec_logical_switch *ls; |
a416ff28 | 689 | |
ea46a4e9 JP |
690 | ls = ls_by_name_or_uuid(ctx, id, must_exist); |
691 | if (!ls) { | |
a416ff28 RB |
692 | return; |
693 | } | |
694 | ||
ea46a4e9 | 695 | nbrec_logical_switch_delete(ls); |
a416ff28 RB |
696 | } |
697 | ||
698 | static void | |
ea46a4e9 | 699 | nbctl_ls_list(struct ctl_context *ctx) |
a416ff28 | 700 | { |
ea46a4e9 | 701 | const struct nbrec_logical_switch *ls; |
2a547e72 | 702 | struct smap lswitches; |
a416ff28 | 703 | |
2a547e72 | 704 | smap_init(&lswitches); |
ea46a4e9 JP |
705 | NBREC_LOGICAL_SWITCH_FOR_EACH(ls, ctx->idl) { |
706 | smap_add_format(&lswitches, ls->name, UUID_FMT " (%s)", | |
707 | UUID_ARGS(&ls->header_.uuid), ls->name); | |
a416ff28 | 708 | } |
2a547e72 JP |
709 | const struct smap_node **nodes = smap_sort(&lswitches); |
710 | for (size_t i = 0; i < smap_count(&lswitches); i++) { | |
711 | const struct smap_node *node = nodes[i]; | |
70799ae8 | 712 | ds_put_format(&ctx->output, "%s\n", node->value); |
2a547e72 JP |
713 | } |
714 | smap_destroy(&lswitches); | |
715 | free(nodes); | |
a416ff28 | 716 | } |
a416ff28 | 717 | \f |
80f408f4 JP |
718 | static const struct nbrec_logical_switch_port * |
719 | lsp_by_name_or_uuid(struct ctl_context *ctx, const char *id, | |
720 | bool must_exist) | |
a416ff28 | 721 | { |
80f408f4 | 722 | const struct nbrec_logical_switch_port *lsp = NULL; |
a416ff28 | 723 | |
80f408f4 JP |
724 | struct uuid lsp_uuid; |
725 | bool is_uuid = uuid_from_string(&lsp_uuid, id); | |
d24d3372 | 726 | if (is_uuid) { |
80f408f4 | 727 | lsp = nbrec_logical_switch_port_get_for_uuid(ctx->idl, &lsp_uuid); |
c1f4da63 RB |
728 | } |
729 | ||
80f408f4 JP |
730 | if (!lsp) { |
731 | NBREC_LOGICAL_SWITCH_PORT_FOR_EACH(lsp, ctx->idl) { | |
732 | if (!strcmp(lsp->name, id)) { | |
a416ff28 RB |
733 | break; |
734 | } | |
735 | } | |
736 | } | |
737 | ||
80f408f4 JP |
738 | if (!lsp && must_exist) { |
739 | ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name"); | |
a416ff28 RB |
740 | } |
741 | ||
80f408f4 | 742 | return lsp; |
a416ff28 RB |
743 | } |
744 | ||
ea46a4e9 | 745 | /* Returns the logical switch that contains 'lsp'. */ |
d24d3372 | 746 | static const struct nbrec_logical_switch * |
ea46a4e9 | 747 | lsp_to_ls(const struct ovsdb_idl *idl, |
80f408f4 | 748 | const struct nbrec_logical_switch_port *lsp) |
d24d3372 | 749 | { |
ea46a4e9 JP |
750 | const struct nbrec_logical_switch *ls; |
751 | NBREC_LOGICAL_SWITCH_FOR_EACH (ls, idl) { | |
752 | for (size_t i = 0; i < ls->n_ports; i++) { | |
753 | if (ls->ports[i] == lsp) { | |
754 | return ls; | |
d24d3372 BP |
755 | } |
756 | } | |
757 | } | |
758 | ||
759 | /* Can't happen because of the database schema */ | |
760 | ctl_fatal("logical port %s is not part of any logical switch", | |
80f408f4 | 761 | lsp->name); |
d24d3372 BP |
762 | } |
763 | ||
764 | static const char * | |
ea46a4e9 | 765 | ls_get_name(const struct nbrec_logical_switch *ls, |
d24d3372 BP |
766 | char uuid_s[UUID_LEN + 1], size_t uuid_s_size) |
767 | { | |
ea46a4e9 JP |
768 | if (ls->name[0]) { |
769 | return ls->name; | |
d24d3372 | 770 | } |
ea46a4e9 | 771 | snprintf(uuid_s, uuid_s_size, UUID_FMT, UUID_ARGS(&ls->header_.uuid)); |
d24d3372 BP |
772 | return uuid_s; |
773 | } | |
774 | ||
a416ff28 | 775 | static void |
80f408f4 | 776 | nbctl_lsp_add(struct ctl_context *ctx) |
a416ff28 | 777 | { |
d24d3372 BP |
778 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; |
779 | ||
ea46a4e9 JP |
780 | const struct nbrec_logical_switch *ls; |
781 | ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true); | |
a416ff28 | 782 | |
d24d3372 BP |
783 | const char *parent_name; |
784 | int64_t tag; | |
785 | if (ctx->argc == 3) { | |
786 | parent_name = NULL; | |
787 | tag = -1; | |
788 | } else if (ctx->argc == 5) { | |
789 | /* Validate tag. */ | |
790 | parent_name = ctx->argv[3]; | |
791 | if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag) | |
792 | || tag < 0 || tag > 4095) { | |
793 | ctl_fatal("%s: invalid tag", ctx->argv[4]); | |
794 | } | |
795 | } else { | |
31ed1192 | 796 | ctl_fatal("lsp-add with parent must also specify a tag"); |
a416ff28 RB |
797 | } |
798 | ||
80f408f4 JP |
799 | const char *lsp_name = ctx->argv[2]; |
800 | const struct nbrec_logical_switch_port *lsp; | |
801 | lsp = lsp_by_name_or_uuid(ctx, lsp_name, false); | |
802 | if (lsp) { | |
d24d3372 | 803 | if (!may_exist) { |
31ed1192 | 804 | ctl_fatal("%s: a port with this name already exists", |
80f408f4 | 805 | lsp_name); |
d24d3372 | 806 | } |
bf5fa52a | 807 | |
d24d3372 | 808 | const struct nbrec_logical_switch *lsw; |
ea46a4e9 JP |
809 | lsw = lsp_to_ls(ctx->idl, lsp); |
810 | if (lsw != ls) { | |
d24d3372 | 811 | char uuid_s[UUID_LEN + 1]; |
ea46a4e9 JP |
812 | ctl_fatal("%s: port already exists but in switch %s", lsp_name, |
813 | ls_get_name(lsw, uuid_s, sizeof uuid_s)); | |
d24d3372 BP |
814 | } |
815 | ||
816 | if (parent_name) { | |
80f408f4 | 817 | if (!lsp->parent_name) { |
31ed1192 | 818 | ctl_fatal("%s: port already exists but has no parent", |
80f408f4 JP |
819 | lsp_name); |
820 | } else if (strcmp(parent_name, lsp->parent_name)) { | |
31ed1192 | 821 | ctl_fatal("%s: port already exists with different parent %s", |
80f408f4 | 822 | lsp_name, lsp->parent_name); |
d24d3372 BP |
823 | } |
824 | ||
b511690b GS |
825 | if (!lsp->n_tag_request) { |
826 | ctl_fatal("%s: port already exists but has no tag_request", | |
80f408f4 | 827 | lsp_name); |
b511690b | 828 | } else if (lsp->tag_request[0] != tag) { |
31ed1192 | 829 | ctl_fatal("%s: port already exists with different " |
b511690b GS |
830 | "tag_request %"PRId64, lsp_name, |
831 | lsp->tag_request[0]); | |
d24d3372 BP |
832 | } |
833 | } else { | |
80f408f4 | 834 | if (lsp->parent_name) { |
31ed1192 | 835 | ctl_fatal("%s: port already exists but has parent %s", |
80f408f4 | 836 | lsp_name, lsp->parent_name); |
d24d3372 | 837 | } |
bf5fa52a | 838 | } |
d24d3372 BP |
839 | |
840 | return; | |
bf5fa52a RB |
841 | } |
842 | ||
445a266a | 843 | /* Create the logical port. */ |
80f408f4 JP |
844 | lsp = nbrec_logical_switch_port_insert(ctx->txn); |
845 | nbrec_logical_switch_port_set_name(lsp, lsp_name); | |
d24d3372 | 846 | if (tag >= 0) { |
80f408f4 | 847 | nbrec_logical_switch_port_set_parent_name(lsp, parent_name); |
b511690b | 848 | nbrec_logical_switch_port_set_tag_request(lsp, &tag, 1); |
bf5fa52a | 849 | } |
445a266a BP |
850 | |
851 | /* Insert the logical port into the logical switch. */ | |
ea46a4e9 | 852 | nbrec_logical_switch_verify_ports(ls); |
80f408f4 | 853 | struct nbrec_logical_switch_port **new_ports = xmalloc(sizeof *new_ports * |
ea46a4e9 JP |
854 | (ls->n_ports + 1)); |
855 | memcpy(new_ports, ls->ports, sizeof *new_ports * ls->n_ports); | |
856 | new_ports[ls->n_ports] = CONST_CAST(struct nbrec_logical_switch_port *, | |
80f408f4 | 857 | lsp); |
ea46a4e9 | 858 | nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports + 1); |
445a266a BP |
859 | free(new_ports); |
860 | } | |
861 | ||
ea46a4e9 | 862 | /* Removes logical switch port 'ls->ports[idx]'. */ |
445a266a | 863 | static void |
ea46a4e9 | 864 | remove_lsp(const struct nbrec_logical_switch *ls, size_t idx) |
445a266a | 865 | { |
ea46a4e9 | 866 | const struct nbrec_logical_switch_port *lsp = ls->ports[idx]; |
445a266a | 867 | |
80f408f4 | 868 | /* First remove 'lsp' from the array of ports. This is what will |
445a266a BP |
869 | * actually cause the logical port to be deleted when the transaction is |
870 | * sent to the database server (due to garbage collection). */ | |
80f408f4 | 871 | struct nbrec_logical_switch_port **new_ports |
ea46a4e9 JP |
872 | = xmemdup(ls->ports, sizeof *new_ports * ls->n_ports); |
873 | new_ports[idx] = new_ports[ls->n_ports - 1]; | |
874 | nbrec_logical_switch_verify_ports(ls); | |
875 | nbrec_logical_switch_set_ports(ls, new_ports, ls->n_ports - 1); | |
445a266a BP |
876 | free(new_ports); |
877 | ||
80f408f4 | 878 | /* Delete 'lsp' from the IDL. This won't have a real effect on the |
445a266a | 879 | * database server (the IDL will suppress it in fact) but it means that it |
80f408f4 JP |
880 | * won't show up when we iterate with NBREC_LOGICAL_SWITCH_PORT_FOR_EACH |
881 | * later. */ | |
882 | nbrec_logical_switch_port_delete(lsp); | |
a416ff28 RB |
883 | } |
884 | ||
885 | static void | |
80f408f4 | 886 | nbctl_lsp_del(struct ctl_context *ctx) |
a416ff28 | 887 | { |
d24d3372 | 888 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); |
80f408f4 | 889 | const struct nbrec_logical_switch_port *lsp; |
a416ff28 | 890 | |
80f408f4 JP |
891 | lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], must_exist); |
892 | if (!lsp) { | |
a416ff28 RB |
893 | return; |
894 | } | |
895 | ||
80f408f4 | 896 | /* Find the switch that contains 'lsp', then delete it. */ |
ea46a4e9 JP |
897 | const struct nbrec_logical_switch *ls; |
898 | NBREC_LOGICAL_SWITCH_FOR_EACH (ls, ctx->idl) { | |
899 | for (size_t i = 0; i < ls->n_ports; i++) { | |
900 | if (ls->ports[i] == lsp) { | |
901 | remove_lsp(ls, i); | |
445a266a BP |
902 | return; |
903 | } | |
904 | } | |
a416ff28 | 905 | } |
a416ff28 | 906 | |
d24d3372 BP |
907 | /* Can't happen because of the database schema. */ |
908 | ctl_fatal("logical port %s is not part of any logical switch", | |
445a266a BP |
909 | ctx->argv[1]); |
910 | } | |
a416ff28 RB |
911 | |
912 | static void | |
80f408f4 | 913 | nbctl_lsp_list(struct ctl_context *ctx) |
a416ff28 | 914 | { |
a416ff28 | 915 | const char *id = ctx->argv[1]; |
ea46a4e9 | 916 | const struct nbrec_logical_switch *ls; |
80f408f4 | 917 | struct smap lsps; |
2a547e72 | 918 | size_t i; |
a416ff28 | 919 | |
ea46a4e9 | 920 | ls = ls_by_name_or_uuid(ctx, id, true); |
a416ff28 | 921 | |
80f408f4 | 922 | smap_init(&lsps); |
ea46a4e9 JP |
923 | for (i = 0; i < ls->n_ports; i++) { |
924 | const struct nbrec_logical_switch_port *lsp = ls->ports[i]; | |
80f408f4 JP |
925 | smap_add_format(&lsps, lsp->name, UUID_FMT " (%s)", |
926 | UUID_ARGS(&lsp->header_.uuid), lsp->name); | |
a416ff28 | 927 | } |
80f408f4 JP |
928 | const struct smap_node **nodes = smap_sort(&lsps); |
929 | for (i = 0; i < smap_count(&lsps); i++) { | |
2a547e72 | 930 | const struct smap_node *node = nodes[i]; |
70799ae8 | 931 | ds_put_format(&ctx->output, "%s\n", node->value); |
2a547e72 | 932 | } |
80f408f4 | 933 | smap_destroy(&lsps); |
2a547e72 | 934 | free(nodes); |
a416ff28 RB |
935 | } |
936 | ||
bf5fa52a | 937 | static void |
80f408f4 | 938 | nbctl_lsp_get_parent(struct ctl_context *ctx) |
bf5fa52a | 939 | { |
80f408f4 | 940 | const struct nbrec_logical_switch_port *lsp; |
bf5fa52a | 941 | |
80f408f4 JP |
942 | lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], true); |
943 | if (lsp->parent_name) { | |
944 | ds_put_format(&ctx->output, "%s\n", lsp->parent_name); | |
bf5fa52a RB |
945 | } |
946 | } | |
947 | ||
948 | static void | |
80f408f4 | 949 | nbctl_lsp_get_tag(struct ctl_context *ctx) |
bf5fa52a | 950 | { |
80f408f4 | 951 | const struct nbrec_logical_switch_port *lsp; |
bf5fa52a | 952 | |
80f408f4 JP |
953 | lsp = lsp_by_name_or_uuid(ctx, ctx->argv[1], true); |
954 | if (lsp->n_tag > 0) { | |
955 | ds_put_format(&ctx->output, "%"PRId64"\n", lsp->tag[0]); | |
bf5fa52a RB |
956 | } |
957 | } | |
958 | ||
a416ff28 | 959 | static void |
80f408f4 | 960 | nbctl_lsp_set_addresses(struct ctl_context *ctx) |
a416ff28 | 961 | { |
a416ff28 | 962 | const char *id = ctx->argv[1]; |
80f408f4 | 963 | const struct nbrec_logical_switch_port *lsp; |
a416ff28 | 964 | |
80f408f4 | 965 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
a416ff28 | 966 | |
69e73316 RB |
967 | int i; |
968 | for (i = 2; i < ctx->argc; i++) { | |
969 | struct eth_addr ea; | |
970 | ||
8639f9be | 971 | if (strcmp(ctx->argv[i], "unknown") && strcmp(ctx->argv[i], "dynamic") |
69e73316 RB |
972 | && !ovs_scan(ctx->argv[i], ETH_ADDR_SCAN_FMT, |
973 | ETH_ADDR_SCAN_ARGS(ea))) { | |
d24d3372 BP |
974 | ctl_fatal("%s: Invalid address format. See ovn-nb(5). " |
975 | "Hint: An Ethernet address must be " | |
976 | "listed before an IP address, together as a single " | |
977 | "argument.", ctx->argv[i]); | |
69e73316 RB |
978 | } |
979 | } | |
980 | ||
80f408f4 | 981 | nbrec_logical_switch_port_set_addresses(lsp, |
a416ff28 RB |
982 | (const char **) ctx->argv + 2, ctx->argc - 2); |
983 | } | |
984 | ||
985 | static void | |
80f408f4 | 986 | nbctl_lsp_get_addresses(struct ctl_context *ctx) |
a416ff28 | 987 | { |
a416ff28 | 988 | const char *id = ctx->argv[1]; |
80f408f4 | 989 | const struct nbrec_logical_switch_port *lsp; |
2fa326a3 | 990 | struct svec addresses; |
2a547e72 | 991 | const char *mac; |
a416ff28 RB |
992 | size_t i; |
993 | ||
80f408f4 | 994 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
a416ff28 | 995 | |
2fa326a3 | 996 | svec_init(&addresses); |
80f408f4 JP |
997 | for (i = 0; i < lsp->n_addresses; i++) { |
998 | svec_add(&addresses, lsp->addresses[i]); | |
2a547e72 | 999 | } |
2fa326a3 BP |
1000 | svec_sort(&addresses); |
1001 | SVEC_FOR_EACH(i, mac, &addresses) { | |
70799ae8 | 1002 | ds_put_format(&ctx->output, "%s\n", mac); |
a416ff28 | 1003 | } |
2fa326a3 | 1004 | svec_destroy(&addresses); |
a416ff28 | 1005 | } |
65fb6826 | 1006 | |
92207865 | 1007 | static void |
80f408f4 | 1008 | nbctl_lsp_set_port_security(struct ctl_context *ctx) |
92207865 | 1009 | { |
92207865 | 1010 | const char *id = ctx->argv[1]; |
80f408f4 | 1011 | const struct nbrec_logical_switch_port *lsp; |
92207865 | 1012 | |
80f408f4 JP |
1013 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
1014 | nbrec_logical_switch_port_set_port_security(lsp, | |
92207865 BP |
1015 | (const char **) ctx->argv + 2, ctx->argc - 2); |
1016 | } | |
1017 | ||
1018 | static void | |
80f408f4 | 1019 | nbctl_lsp_get_port_security(struct ctl_context *ctx) |
92207865 | 1020 | { |
92207865 | 1021 | const char *id = ctx->argv[1]; |
80f408f4 | 1022 | const struct nbrec_logical_switch_port *lsp; |
2a547e72 JP |
1023 | struct svec addrs; |
1024 | const char *addr; | |
92207865 BP |
1025 | size_t i; |
1026 | ||
80f408f4 | 1027 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
2a547e72 | 1028 | svec_init(&addrs); |
80f408f4 JP |
1029 | for (i = 0; i < lsp->n_port_security; i++) { |
1030 | svec_add(&addrs, lsp->port_security[i]); | |
2a547e72 JP |
1031 | } |
1032 | svec_sort(&addrs); | |
1033 | SVEC_FOR_EACH(i, addr, &addrs) { | |
70799ae8 | 1034 | ds_put_format(&ctx->output, "%s\n", addr); |
92207865 | 1035 | } |
2a547e72 | 1036 | svec_destroy(&addrs); |
92207865 BP |
1037 | } |
1038 | ||
65fb6826 | 1039 | static void |
80f408f4 | 1040 | nbctl_lsp_get_up(struct ctl_context *ctx) |
65fb6826 | 1041 | { |
65fb6826 | 1042 | const char *id = ctx->argv[1]; |
80f408f4 | 1043 | const struct nbrec_logical_switch_port *lsp; |
65fb6826 | 1044 | |
80f408f4 | 1045 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
70799ae8 | 1046 | ds_put_format(&ctx->output, |
80f408f4 | 1047 | "%s\n", (lsp->up && *lsp->up) ? "up" : "down"); |
65fb6826 | 1048 | } |
e9e8bcdf | 1049 | |
7127583d NG |
1050 | static bool |
1051 | parse_enabled(const char *state) | |
1052 | { | |
1053 | if (!strcasecmp(state, "enabled")) { | |
1054 | return true; | |
1055 | } else if (!strcasecmp(state, "disabled")) { | |
1056 | return false; | |
1057 | } else { | |
1058 | ctl_fatal("%s: state must be \"enabled\" or \"disabled\"", state); | |
1059 | } | |
1060 | } | |
1061 | ||
e9e8bcdf | 1062 | static void |
80f408f4 | 1063 | nbctl_lsp_set_enabled(struct ctl_context *ctx) |
e9e8bcdf | 1064 | { |
e9e8bcdf RB |
1065 | const char *id = ctx->argv[1]; |
1066 | const char *state = ctx->argv[2]; | |
80f408f4 | 1067 | const struct nbrec_logical_switch_port *lsp; |
e9e8bcdf | 1068 | |
80f408f4 | 1069 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
7127583d | 1070 | bool enabled = parse_enabled(state); |
80f408f4 | 1071 | nbrec_logical_switch_port_set_enabled(lsp, &enabled, 1); |
7127583d NG |
1072 | } |
1073 | ||
e9e8bcdf | 1074 | static void |
80f408f4 | 1075 | nbctl_lsp_get_enabled(struct ctl_context *ctx) |
e9e8bcdf | 1076 | { |
e9e8bcdf | 1077 | const char *id = ctx->argv[1]; |
80f408f4 | 1078 | const struct nbrec_logical_switch_port *lsp; |
e9e8bcdf | 1079 | |
80f408f4 | 1080 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
70799ae8 | 1081 | ds_put_format(&ctx->output, "%s\n", |
80f408f4 | 1082 | !lsp->enabled || *lsp->enabled ? "enabled" : "disabled"); |
e9e8bcdf | 1083 | } |
f2903c22 RB |
1084 | |
1085 | static void | |
80f408f4 | 1086 | nbctl_lsp_set_type(struct ctl_context *ctx) |
f2903c22 | 1087 | { |
f2903c22 RB |
1088 | const char *id = ctx->argv[1]; |
1089 | const char *type = ctx->argv[2]; | |
80f408f4 | 1090 | const struct nbrec_logical_switch_port *lsp; |
f2903c22 | 1091 | |
80f408f4 JP |
1092 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
1093 | nbrec_logical_switch_port_set_type(lsp, type); | |
f2903c22 RB |
1094 | } |
1095 | ||
1096 | static void | |
80f408f4 | 1097 | nbctl_lsp_get_type(struct ctl_context *ctx) |
f2903c22 | 1098 | { |
f2903c22 | 1099 | const char *id = ctx->argv[1]; |
80f408f4 | 1100 | const struct nbrec_logical_switch_port *lsp; |
f2903c22 | 1101 | |
80f408f4 JP |
1102 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
1103 | ds_put_format(&ctx->output, "%s\n", lsp->type); | |
f2903c22 RB |
1104 | } |
1105 | ||
1106 | static void | |
80f408f4 | 1107 | nbctl_lsp_set_options(struct ctl_context *ctx) |
f2903c22 | 1108 | { |
f2903c22 | 1109 | const char *id = ctx->argv[1]; |
80f408f4 | 1110 | const struct nbrec_logical_switch_port *lsp; |
f2903c22 RB |
1111 | size_t i; |
1112 | struct smap options = SMAP_INITIALIZER(&options); | |
1113 | ||
80f408f4 | 1114 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
f2903c22 RB |
1115 | for (i = 2; i < ctx->argc; i++) { |
1116 | char *key, *value; | |
1117 | value = xstrdup(ctx->argv[i]); | |
1118 | key = strsep(&value, "="); | |
1119 | if (value) { | |
1120 | smap_add(&options, key, value); | |
1121 | } | |
1122 | free(key); | |
1123 | } | |
1124 | ||
80f408f4 | 1125 | nbrec_logical_switch_port_set_options(lsp, &options); |
f2903c22 RB |
1126 | |
1127 | smap_destroy(&options); | |
1128 | } | |
1129 | ||
1130 | static void | |
80f408f4 | 1131 | nbctl_lsp_get_options(struct ctl_context *ctx) |
f2903c22 | 1132 | { |
f2903c22 | 1133 | const char *id = ctx->argv[1]; |
80f408f4 | 1134 | const struct nbrec_logical_switch_port *lsp; |
f2903c22 RB |
1135 | struct smap_node *node; |
1136 | ||
80f408f4 JP |
1137 | lsp = lsp_by_name_or_uuid(ctx, id, true); |
1138 | SMAP_FOR_EACH(node, &lsp->options) { | |
70799ae8 | 1139 | ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value); |
f2903c22 RB |
1140 | } |
1141 | } | |
8f96455d | 1142 | |
281977f7 NS |
1143 | static void |
1144 | nbctl_lsp_set_dhcpv4_options(struct ctl_context *ctx) | |
1145 | { | |
1146 | const char *id = ctx->argv[1]; | |
1147 | const struct nbrec_logical_switch_port *lsp; | |
1148 | ||
1149 | lsp = lsp_by_name_or_uuid(ctx, id, true); | |
1150 | const struct nbrec_dhcp_options *dhcp_opt = NULL; | |
1151 | if (ctx->argc == 3 ) { | |
1152 | dhcp_opt = dhcp_options_get(ctx, ctx->argv[2], true); | |
1153 | } | |
1154 | ||
1155 | if (dhcp_opt) { | |
1156 | ovs_be32 ip; | |
1157 | unsigned int plen; | |
1158 | char *error = ip_parse_cidr(dhcp_opt->cidr, &ip, &plen); | |
1159 | if (error){ | |
1160 | free(error); | |
1161 | ctl_fatal("DHCP options cidr '%s' is not IPv4", dhcp_opt->cidr); | |
281977f7 NS |
1162 | } |
1163 | } | |
1164 | nbrec_logical_switch_port_set_dhcpv4_options(lsp, dhcp_opt); | |
1165 | } | |
1166 | ||
1167 | static void | |
1168 | nbctl_lsp_get_dhcpv4_options(struct ctl_context *ctx) | |
1169 | { | |
1170 | const char *id = ctx->argv[1]; | |
1171 | const struct nbrec_logical_switch_port *lsp; | |
1172 | ||
1173 | lsp = lsp_by_name_or_uuid(ctx, id, true); | |
1174 | if (lsp->dhcpv4_options) { | |
1175 | ds_put_format(&ctx->output, UUID_FMT " (%s)\n", | |
1176 | UUID_ARGS(&lsp->dhcpv4_options->header_.uuid), | |
1177 | lsp->dhcpv4_options->cidr); | |
1178 | } | |
1179 | } | |
1180 | ||
8f96455d JP |
1181 | enum { |
1182 | DIR_FROM_LPORT, | |
1183 | DIR_TO_LPORT | |
1184 | }; | |
1185 | ||
1186 | static int | |
1187 | dir_encode(const char *dir) | |
1188 | { | |
1189 | if (!strcmp(dir, "from-lport")) { | |
1190 | return DIR_FROM_LPORT; | |
1191 | } else if (!strcmp(dir, "to-lport")) { | |
1192 | return DIR_TO_LPORT; | |
1193 | } | |
1194 | ||
1195 | OVS_NOT_REACHED(); | |
1196 | } | |
1197 | ||
1198 | static int | |
1199 | acl_cmp(const void *acl1_, const void *acl2_) | |
1200 | { | |
1201 | const struct nbrec_acl *const *acl1p = acl1_; | |
1202 | const struct nbrec_acl *const *acl2p = acl2_; | |
1203 | const struct nbrec_acl *acl1 = *acl1p; | |
1204 | const struct nbrec_acl *acl2 = *acl2p; | |
1205 | ||
1206 | int dir1 = dir_encode(acl1->direction); | |
1207 | int dir2 = dir_encode(acl2->direction); | |
1208 | ||
1209 | if (dir1 != dir2) { | |
1210 | return dir1 < dir2 ? -1 : 1; | |
1211 | } else if (acl1->priority != acl2->priority) { | |
1212 | return acl1->priority > acl2->priority ? -1 : 1; | |
1213 | } else { | |
1214 | return strcmp(acl1->match, acl2->match); | |
1215 | } | |
1216 | } | |
1217 | ||
1218 | static void | |
70799ae8 | 1219 | nbctl_acl_list(struct ctl_context *ctx) |
8f96455d | 1220 | { |
ea46a4e9 | 1221 | const struct nbrec_logical_switch *ls; |
8f96455d JP |
1222 | const struct nbrec_acl **acls; |
1223 | size_t i; | |
1224 | ||
ea46a4e9 | 1225 | ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true); |
8f96455d | 1226 | |
ea46a4e9 JP |
1227 | acls = xmalloc(sizeof *acls * ls->n_acls); |
1228 | for (i = 0; i < ls->n_acls; i++) { | |
1229 | acls[i] = ls->acls[i]; | |
8f96455d JP |
1230 | } |
1231 | ||
ea46a4e9 | 1232 | qsort(acls, ls->n_acls, sizeof *acls, acl_cmp); |
8f96455d | 1233 | |
ea46a4e9 | 1234 | for (i = 0; i < ls->n_acls; i++) { |
8f96455d | 1235 | const struct nbrec_acl *acl = acls[i]; |
9e891740 JP |
1236 | ds_put_format(&ctx->output, "%10s %5"PRId64" (%s) %s%s\n", |
1237 | acl->direction, acl->priority, | |
1238 | acl->match, acl->action, acl->log ? " log" : ""); | |
8f96455d JP |
1239 | } |
1240 | ||
1241 | free(acls); | |
1242 | } | |
1243 | ||
d24d3372 BP |
1244 | static const char * |
1245 | parse_direction(const char *arg) | |
8f96455d | 1246 | { |
8f96455d | 1247 | /* Validate direction. Only require the first letter. */ |
d24d3372 BP |
1248 | if (arg[0] == 't') { |
1249 | return "to-lport"; | |
1250 | } else if (arg[0] == 'f') { | |
1251 | return "from-lport"; | |
8f96455d | 1252 | } else { |
d24d3372 | 1253 | ctl_fatal("%s: direction must be \"to-lport\" or \"from-lport\"", arg); |
8f96455d | 1254 | } |
d24d3372 | 1255 | } |
8f96455d | 1256 | |
d24d3372 BP |
1257 | static int |
1258 | parse_priority(const char *arg) | |
1259 | { | |
8f96455d | 1260 | /* Validate priority. */ |
d24d3372 BP |
1261 | int64_t priority; |
1262 | if (!ovs_scan(arg, "%"SCNd64, &priority) | |
1263 | || priority < 0 || priority > 32767) { | |
1264 | ctl_fatal("%s: priority must in range 0...32767", arg); | |
8f96455d | 1265 | } |
d24d3372 BP |
1266 | return priority; |
1267 | } | |
1268 | ||
1269 | static void | |
1270 | nbctl_acl_add(struct ctl_context *ctx) | |
1271 | { | |
ea46a4e9 | 1272 | const struct nbrec_logical_switch *ls; |
d24d3372 BP |
1273 | const char *action = ctx->argv[5]; |
1274 | ||
ea46a4e9 | 1275 | ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true); |
d24d3372 BP |
1276 | |
1277 | const char *direction = parse_direction(ctx->argv[2]); | |
1278 | int64_t priority = parse_priority(ctx->argv[3]); | |
8f96455d JP |
1279 | |
1280 | /* Validate action. */ | |
1281 | if (strcmp(action, "allow") && strcmp(action, "allow-related") | |
1282 | && strcmp(action, "drop") && strcmp(action, "reject")) { | |
d24d3372 BP |
1283 | ctl_fatal("%s: action must be one of \"allow\", \"allow-related\", " |
1284 | "\"drop\", and \"reject\"", action); | |
8f96455d JP |
1285 | return; |
1286 | } | |
1287 | ||
1288 | /* Create the acl. */ | |
70799ae8 | 1289 | struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn); |
8f96455d JP |
1290 | nbrec_acl_set_priority(acl, priority); |
1291 | nbrec_acl_set_direction(acl, direction); | |
1292 | nbrec_acl_set_match(acl, ctx->argv[4]); | |
1293 | nbrec_acl_set_action(acl, action); | |
70799ae8 | 1294 | if (shash_find(&ctx->options, "--log") != NULL) { |
8f96455d JP |
1295 | nbrec_acl_set_log(acl, true); |
1296 | } | |
1297 | ||
1298 | /* Insert the acl into the logical switch. */ | |
ea46a4e9 JP |
1299 | nbrec_logical_switch_verify_acls(ls); |
1300 | struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * (ls->n_acls + 1)); | |
1301 | memcpy(new_acls, ls->acls, sizeof *new_acls * ls->n_acls); | |
1302 | new_acls[ls->n_acls] = acl; | |
1303 | nbrec_logical_switch_set_acls(ls, new_acls, ls->n_acls + 1); | |
8f96455d JP |
1304 | free(new_acls); |
1305 | } | |
1306 | ||
1307 | static void | |
70799ae8 | 1308 | nbctl_acl_del(struct ctl_context *ctx) |
8f96455d | 1309 | { |
ea46a4e9 JP |
1310 | const struct nbrec_logical_switch *ls; |
1311 | ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true); | |
8f96455d JP |
1312 | |
1313 | if (ctx->argc != 2 && ctx->argc != 3 && ctx->argc != 5) { | |
d24d3372 | 1314 | ctl_fatal("cannot specify priority without match"); |
8f96455d JP |
1315 | } |
1316 | ||
1317 | if (ctx->argc == 2) { | |
1318 | /* If direction, priority, and match are not specified, delete | |
1319 | * all ACLs. */ | |
ea46a4e9 JP |
1320 | nbrec_logical_switch_verify_acls(ls); |
1321 | nbrec_logical_switch_set_acls(ls, NULL, 0); | |
8f96455d JP |
1322 | return; |
1323 | } | |
1324 | ||
d24d3372 | 1325 | const char *direction = parse_direction(ctx->argv[2]); |
8f96455d JP |
1326 | |
1327 | /* If priority and match are not specified, delete all ACLs with the | |
1328 | * specified direction. */ | |
1329 | if (ctx->argc == 3) { | |
ea46a4e9 | 1330 | struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * ls->n_acls); |
8f96455d JP |
1331 | |
1332 | int n_acls = 0; | |
ea46a4e9 JP |
1333 | for (size_t i = 0; i < ls->n_acls; i++) { |
1334 | if (strcmp(direction, ls->acls[i]->direction)) { | |
1335 | new_acls[n_acls++] = ls->acls[i]; | |
8f96455d JP |
1336 | } |
1337 | } | |
1338 | ||
ea46a4e9 JP |
1339 | nbrec_logical_switch_verify_acls(ls); |
1340 | nbrec_logical_switch_set_acls(ls, new_acls, n_acls); | |
8f96455d JP |
1341 | free(new_acls); |
1342 | return; | |
1343 | } | |
1344 | ||
d24d3372 | 1345 | int64_t priority = parse_priority(ctx->argv[3]); |
8f96455d JP |
1346 | |
1347 | /* Remove the matching rule. */ | |
ea46a4e9 JP |
1348 | for (size_t i = 0; i < ls->n_acls; i++) { |
1349 | struct nbrec_acl *acl = ls->acls[i]; | |
8f96455d JP |
1350 | |
1351 | if (priority == acl->priority && !strcmp(ctx->argv[4], acl->match) && | |
1352 | !strcmp(direction, acl->direction)) { | |
1353 | struct nbrec_acl **new_acls | |
ea46a4e9 JP |
1354 | = xmemdup(ls->acls, sizeof *new_acls * ls->n_acls); |
1355 | new_acls[i] = ls->acls[ls->n_acls - 1]; | |
1356 | nbrec_logical_switch_verify_acls(ls); | |
1357 | nbrec_logical_switch_set_acls(ls, new_acls, | |
1358 | ls->n_acls - 1); | |
8f96455d JP |
1359 | free(new_acls); |
1360 | return; | |
1361 | } | |
1362 | } | |
1363 | } | |
e2bfcad6 | 1364 | |
1365 | static void | |
1366 | nbctl_lb_add(struct ctl_context *ctx) | |
1367 | { | |
1368 | const char *lb_name = ctx->argv[1]; | |
1369 | const char *lb_vip = ctx->argv[2]; | |
1370 | char *lb_ips = ctx->argv[3]; | |
1371 | ||
1372 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
1373 | bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL; | |
1374 | ||
1375 | const char *lb_proto; | |
1376 | bool is_update_proto = false; | |
1377 | bool is_vip_with_port = true; | |
1378 | ||
1379 | if (ctx->argc == 4) { | |
1380 | /* Default protocol. */ | |
1381 | lb_proto = "tcp"; | |
1382 | } else { | |
1383 | /* Validate protocol. */ | |
1384 | lb_proto = ctx->argv[4]; | |
1385 | is_update_proto = true; | |
1386 | if (strcmp(lb_proto, "tcp") && strcmp(lb_proto, "udp")) { | |
1387 | ctl_fatal("%s: protocol must be one of \"tcp\", \"udp\".", | |
1388 | lb_proto); | |
1389 | } | |
1390 | } | |
1391 | ||
1392 | ovs_be32 ipv4 = 0; | |
1393 | ovs_be16 port = 0; | |
1394 | char *error = ip_parse_port(lb_vip, &ipv4, &port); | |
1395 | if (error) { | |
1396 | free(error); | |
1397 | if (!ip_parse(lb_vip, &ipv4)) { | |
1398 | ctl_fatal("%s: should be an IPv4 address (or an IPv4 address " | |
1399 | "and a port number with : as a separator).", lb_vip); | |
1400 | } | |
1401 | ||
1402 | if (is_update_proto) { | |
1403 | ctl_fatal("Protocol is unnecessary when no port of vip " | |
1404 | "is given."); | |
1405 | } | |
1406 | is_vip_with_port = false; | |
1407 | } | |
1408 | ||
1409 | char *token = NULL, *save_ptr = NULL; | |
1410 | struct ds lb_ips_new = DS_EMPTY_INITIALIZER; | |
1411 | for (token = strtok_r(lb_ips, ",", &save_ptr); | |
1412 | token != NULL; token = strtok_r(NULL, ",", &save_ptr)) { | |
1413 | if (is_vip_with_port) { | |
1414 | error = ip_parse_port(token, &ipv4, &port); | |
1415 | if (error) { | |
1416 | free(error); | |
1417 | ds_destroy(&lb_ips_new); | |
1418 | ctl_fatal("%s: should be an IPv4 address and a port " | |
1419 | "number with : as a separator.", token); | |
1420 | } | |
1421 | } else { | |
1422 | if (!ip_parse(token, &ipv4)) { | |
1423 | ds_destroy(&lb_ips_new); | |
1424 | ctl_fatal("%s: should be an IPv4 address.", token); | |
1425 | } | |
1426 | } | |
1427 | ds_put_format(&lb_ips_new, "%s%s", | |
1428 | lb_ips_new.length ? "," : "", token); | |
1429 | } | |
1430 | ||
1431 | const struct nbrec_load_balancer *lb = NULL; | |
1432 | if (!add_duplicate) { | |
1433 | lb = lb_by_name_or_uuid(ctx, lb_name, false); | |
1434 | if (lb) { | |
1435 | if (smap_get(&lb->vips, lb_vip)) { | |
1436 | if (!may_exist) { | |
1437 | ds_destroy(&lb_ips_new); | |
1438 | ctl_fatal("%s: a load balancer with this vip (%s) " | |
1439 | "already exists", lb_name, lb_vip); | |
1440 | } | |
1441 | /* Update the vips. */ | |
1442 | smap_replace(CONST_CAST(struct smap *, &lb->vips), | |
1443 | lb_vip, ds_cstr(&lb_ips_new)); | |
1444 | } else { | |
1445 | /* Add the new vips. */ | |
1446 | smap_add(CONST_CAST(struct smap *, &lb->vips), | |
1447 | lb_vip, ds_cstr(&lb_ips_new)); | |
1448 | } | |
1449 | ||
1450 | /* Update the load balancer. */ | |
1451 | if (is_update_proto) { | |
1452 | nbrec_load_balancer_verify_protocol(lb); | |
1453 | nbrec_load_balancer_set_protocol(lb, lb_proto); | |
1454 | } | |
1455 | nbrec_load_balancer_verify_vips(lb); | |
1456 | nbrec_load_balancer_set_vips(lb, &lb->vips); | |
1457 | ds_destroy(&lb_ips_new); | |
1458 | return; | |
1459 | } | |
1460 | } | |
1461 | ||
1462 | /* Create the load balancer. */ | |
1463 | lb = nbrec_load_balancer_insert(ctx->txn); | |
1464 | nbrec_load_balancer_set_name(lb, lb_name); | |
1465 | nbrec_load_balancer_set_protocol(lb, lb_proto); | |
1466 | smap_add(CONST_CAST(struct smap *, &lb->vips), | |
1467 | lb_vip, ds_cstr(&lb_ips_new)); | |
1468 | nbrec_load_balancer_set_vips(lb, &lb->vips); | |
1469 | ds_destroy(&lb_ips_new); | |
1470 | } | |
1471 | ||
1472 | static void | |
1473 | nbctl_lb_del(struct ctl_context *ctx) | |
1474 | { | |
1475 | const char *id = ctx->argv[1]; | |
1476 | const struct nbrec_load_balancer *lb = NULL; | |
1477 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
1478 | ||
1479 | lb = lb_by_name_or_uuid(ctx, id, false); | |
1480 | if (!lb) { | |
1481 | return; | |
1482 | } | |
1483 | ||
1484 | if (ctx->argc == 3) { | |
1485 | const char *lb_vip = ctx->argv[2]; | |
1486 | if (smap_get(&lb->vips, lb_vip)) { | |
1487 | smap_remove(CONST_CAST(struct smap *, &lb->vips), lb_vip); | |
1488 | if (smap_is_empty(&lb->vips)) { | |
1489 | nbrec_load_balancer_delete(lb); | |
1490 | return; | |
1491 | } | |
1492 | ||
1493 | /* Delete the vip of the load balancer. */ | |
1494 | nbrec_load_balancer_verify_vips(lb); | |
1495 | nbrec_load_balancer_set_vips(lb, &lb->vips); | |
1496 | return; | |
1497 | } | |
1498 | if (must_exist) { | |
1499 | ctl_fatal("vip %s is not part of the load balancer.", | |
1500 | lb_vip); | |
1501 | } | |
1502 | return; | |
1503 | } | |
1504 | nbrec_load_balancer_delete(lb); | |
1505 | } | |
1506 | ||
1507 | static void | |
1508 | lb_info_add_smap(const struct nbrec_load_balancer *lb, | |
1509 | struct smap *lbs) | |
1510 | { | |
1511 | struct ds key = DS_EMPTY_INITIALIZER; | |
1512 | struct ds val = DS_EMPTY_INITIALIZER; | |
1513 | char *error, *protocol; | |
1514 | ovs_be32 ipv4 = 0; | |
1515 | ovs_be16 port = 0; | |
1516 | ||
1517 | const struct smap_node **nodes = smap_sort(&lb->vips); | |
1518 | if (nodes) { | |
1519 | for (int i = 0; i < smap_count(&lb->vips); i++) { | |
1520 | const struct smap_node *node = nodes[i]; | |
1521 | protocol = lb->protocol; | |
1522 | error = ip_parse_port(node->key, &ipv4, &port); | |
1523 | if (error) { | |
1524 | free(error); | |
1525 | protocol = "tcp/udp"; | |
1526 | } | |
1527 | ||
1528 | i == 0 ? ds_put_format(&val, | |
1529 | UUID_FMT " %-20.16s%-11.7s%-25.21s%s", | |
1530 | UUID_ARGS(&lb->header_.uuid), | |
1531 | lb->name, protocol, | |
1532 | node->key, node->value) | |
1533 | : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s", | |
1534 | "", protocol, | |
1535 | node->key, node->value); | |
1536 | } | |
1537 | ||
1538 | ds_put_format(&key, "%-20.16s", lb->name); | |
1539 | smap_add(lbs, ds_cstr(&key), ds_cstr(&val)); | |
1540 | ||
1541 | ds_destroy(&key); | |
1542 | ds_destroy(&val); | |
1543 | free(nodes); | |
1544 | } | |
1545 | } | |
1546 | ||
1547 | static void | |
1548 | lb_info_print(struct ctl_context *ctx, struct smap *lbs) | |
1549 | { | |
1550 | const struct smap_node **nodes = smap_sort(lbs); | |
1551 | if (nodes) { | |
1552 | ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.21s%s\n", | |
1553 | "UUID", "LB", "PROTO", "VIP", "IPs"); | |
1554 | for (size_t i = 0; i < smap_count(lbs); i++) { | |
1555 | const struct smap_node *node = nodes[i]; | |
1556 | ds_put_format(&ctx->output, "%s\n", node->value); | |
1557 | } | |
1558 | ||
1559 | free(nodes); | |
1560 | } | |
1561 | } | |
1562 | ||
1563 | static void | |
1564 | lb_info_list_all(struct ctl_context *ctx, | |
1565 | const char *lb_name, bool lb_check) | |
1566 | { | |
1567 | const struct nbrec_load_balancer *lb; | |
1568 | struct smap lbs = SMAP_INITIALIZER(&lbs); | |
1569 | ||
1570 | NBREC_LOAD_BALANCER_FOR_EACH(lb, ctx->idl) { | |
1571 | if (lb_check && strcmp(lb->name, lb_name)) { | |
1572 | continue; | |
1573 | } | |
1574 | lb_info_add_smap(lb, &lbs); | |
1575 | } | |
1576 | ||
1577 | lb_info_print(ctx, &lbs); | |
1578 | smap_destroy(&lbs); | |
1579 | } | |
1580 | ||
1581 | static void | |
1582 | nbctl_lb_list(struct ctl_context *ctx) | |
1583 | { | |
1584 | if (ctx->argc == 1) { | |
1585 | lb_info_list_all(ctx, NULL, false); | |
1586 | } else if (ctx->argc == 2) { | |
1587 | lb_info_list_all(ctx, ctx->argv[1], true); | |
1588 | } | |
1589 | } | |
1590 | ||
1591 | static void | |
1592 | nbctl_lr_lb_add(struct ctl_context *ctx) | |
1593 | { | |
1594 | const struct nbrec_logical_router *lr; | |
1595 | const struct nbrec_load_balancer *new_lb; | |
1596 | ||
1597 | lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); | |
1598 | new_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true); | |
1599 | ||
1600 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
1601 | for (int i = 0; i < lr->n_load_balancer; i++) { | |
1602 | const struct nbrec_load_balancer *lb | |
1603 | = lr->load_balancer[i]; | |
1604 | ||
1605 | if (uuid_equals(&new_lb->header_.uuid, &lb->header_.uuid)) { | |
1606 | if (may_exist) { | |
1607 | return; | |
1608 | } | |
1609 | ctl_fatal(UUID_FMT " : a load balancer with this UUID already " | |
1610 | "exists", UUID_ARGS(&lb->header_.uuid)); | |
1611 | } | |
1612 | } | |
1613 | ||
1614 | /* Insert the load balancer into the logical router. */ | |
1615 | nbrec_logical_router_verify_load_balancer(lr); | |
1616 | struct nbrec_load_balancer **new_lbs | |
1617 | = xmalloc(sizeof *new_lbs * (lr->n_load_balancer + 1)); | |
1618 | ||
1619 | memcpy(new_lbs, lr->load_balancer, sizeof *new_lbs * lr->n_load_balancer); | |
1620 | new_lbs[lr->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer *, | |
1621 | new_lb); | |
1622 | nbrec_logical_router_set_load_balancer(lr, new_lbs, | |
1623 | lr->n_load_balancer + 1); | |
1624 | free(new_lbs); | |
1625 | } | |
1626 | ||
1627 | static void | |
1628 | nbctl_lr_lb_del(struct ctl_context *ctx) | |
1629 | { | |
1630 | const struct nbrec_logical_router *lr; | |
1631 | const struct nbrec_load_balancer *del_lb; | |
1632 | lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); | |
1633 | ||
1634 | if (ctx->argc == 2) { | |
1635 | /* If load-balancer is not specified, remove | |
1636 | * all load-balancers from the logical router. */ | |
1637 | nbrec_logical_router_verify_load_balancer(lr); | |
1638 | nbrec_logical_router_set_load_balancer(lr, NULL, 0); | |
1639 | return; | |
1640 | } | |
1641 | ||
1642 | del_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true); | |
1643 | for (size_t i = 0; i < lr->n_load_balancer; i++) { | |
1644 | const struct nbrec_load_balancer *lb | |
1645 | = lr->load_balancer[i]; | |
1646 | ||
1647 | if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) { | |
1648 | /* Remove the matching rule. */ | |
1649 | nbrec_logical_router_verify_load_balancer(lr); | |
1650 | ||
1651 | struct nbrec_load_balancer **new_lbs | |
1652 | = xmemdup(lr->load_balancer, | |
1653 | sizeof *new_lbs * lr->n_load_balancer); | |
1654 | new_lbs[i] = lr->load_balancer[lr->n_load_balancer - 1]; | |
1655 | nbrec_logical_router_set_load_balancer(lr, new_lbs, | |
1656 | lr->n_load_balancer - 1); | |
1657 | free(new_lbs); | |
1658 | return; | |
1659 | } | |
1660 | } | |
1661 | ||
1662 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
1663 | if (must_exist) { | |
1664 | ctl_fatal("load balancer %s is not part of any logical router.", | |
1665 | del_lb->name); | |
1666 | } | |
1667 | } | |
1668 | ||
1669 | static void | |
1670 | nbctl_lr_lb_list(struct ctl_context *ctx) | |
1671 | { | |
1672 | const char *lr_name = ctx->argv[1]; | |
1673 | const struct nbrec_logical_router *lr; | |
1674 | struct smap lbs = SMAP_INITIALIZER(&lbs); | |
1675 | ||
1676 | lr = lr_by_name_or_uuid(ctx, lr_name, true); | |
1677 | for (int i = 0; i < lr->n_load_balancer; i++) { | |
1678 | const struct nbrec_load_balancer *lb | |
1679 | = lr->load_balancer[i]; | |
1680 | lb_info_add_smap(lb, &lbs); | |
1681 | } | |
1682 | ||
1683 | lb_info_print(ctx, &lbs); | |
1684 | smap_destroy(&lbs); | |
1685 | } | |
1686 | ||
1687 | static void | |
1688 | nbctl_ls_lb_add(struct ctl_context *ctx) | |
1689 | { | |
1690 | const struct nbrec_logical_switch *ls; | |
1691 | const struct nbrec_load_balancer *new_lb; | |
1692 | ||
1693 | ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true); | |
1694 | new_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true); | |
1695 | ||
1696 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
1697 | for (int i = 0; i < ls->n_load_balancer; i++) { | |
1698 | const struct nbrec_load_balancer *lb | |
1699 | = ls->load_balancer[i]; | |
1700 | ||
1701 | if (uuid_equals(&new_lb->header_.uuid, &lb->header_.uuid)) { | |
1702 | if (may_exist) { | |
1703 | return; | |
1704 | } | |
1705 | ctl_fatal(UUID_FMT " : a load balancer with this UUID already " | |
1706 | "exists", UUID_ARGS(&lb->header_.uuid)); | |
1707 | } | |
1708 | } | |
1709 | ||
1710 | /* Insert the load balancer into the logical switch. */ | |
1711 | nbrec_logical_switch_verify_load_balancer(ls); | |
1712 | struct nbrec_load_balancer **new_lbs | |
1713 | = xmalloc(sizeof *new_lbs * (ls->n_load_balancer + 1)); | |
1714 | ||
1715 | memcpy(new_lbs, ls->load_balancer, sizeof *new_lbs * ls->n_load_balancer); | |
1716 | new_lbs[ls->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer *, | |
1717 | new_lb); | |
1718 | nbrec_logical_switch_set_load_balancer(ls, new_lbs, | |
1719 | ls->n_load_balancer + 1); | |
1720 | free(new_lbs); | |
1721 | } | |
1722 | ||
1723 | static void | |
1724 | nbctl_ls_lb_del(struct ctl_context *ctx) | |
1725 | { | |
1726 | const struct nbrec_logical_switch *ls; | |
1727 | const struct nbrec_load_balancer *del_lb; | |
1728 | ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true); | |
1729 | ||
1730 | if (ctx->argc == 2) { | |
1731 | /* If load-balancer is not specified, remove | |
1732 | * all load-balancers from the logical switch. */ | |
1733 | nbrec_logical_switch_verify_load_balancer(ls); | |
1734 | nbrec_logical_switch_set_load_balancer(ls, NULL, 0); | |
1735 | return; | |
1736 | } | |
1737 | ||
1738 | del_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true); | |
1739 | for (size_t i = 0; i < ls->n_load_balancer; i++) { | |
1740 | const struct nbrec_load_balancer *lb | |
1741 | = ls->load_balancer[i]; | |
1742 | ||
1743 | if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) { | |
1744 | /* Remove the matching rule. */ | |
1745 | nbrec_logical_switch_verify_load_balancer(ls); | |
1746 | ||
1747 | struct nbrec_load_balancer **new_lbs | |
1748 | = xmemdup(ls->load_balancer, | |
1749 | sizeof *new_lbs * ls->n_load_balancer); | |
1750 | new_lbs[i] = ls->load_balancer[ls->n_load_balancer - 1]; | |
1751 | nbrec_logical_switch_set_load_balancer(ls, new_lbs, | |
1752 | ls->n_load_balancer - 1); | |
1753 | free(new_lbs); | |
1754 | return; | |
1755 | } | |
1756 | } | |
1757 | ||
1758 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
1759 | if (must_exist) { | |
1760 | ctl_fatal("load balancer %s is not part of any logical switch.", | |
1761 | del_lb->name); | |
1762 | } | |
1763 | } | |
1764 | ||
1765 | static void | |
1766 | nbctl_ls_lb_list(struct ctl_context *ctx) | |
1767 | { | |
1768 | const char *ls_name = ctx->argv[1]; | |
1769 | const struct nbrec_logical_switch *ls; | |
1770 | struct smap lbs = SMAP_INITIALIZER(&lbs); | |
1771 | ||
1772 | ls = ls_by_name_or_uuid(ctx, ls_name, true); | |
1773 | for (int i = 0; i < ls->n_load_balancer; i++) { | |
1774 | const struct nbrec_load_balancer *lb | |
1775 | = ls->load_balancer[i]; | |
1776 | lb_info_add_smap(lb, &lbs); | |
1777 | } | |
1778 | ||
1779 | lb_info_print(ctx, &lbs); | |
1780 | smap_destroy(&lbs); | |
1781 | } | |
1782 | ||
fa2a27b2 JP |
1783 | static void |
1784 | nbctl_lr_add(struct ctl_context *ctx) | |
1785 | { | |
1786 | const char *lr_name = ctx->argc == 2 ? ctx->argv[1] : NULL; | |
1787 | ||
1788 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
1789 | bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL; | |
1790 | if (may_exist && add_duplicate) { | |
1791 | ctl_fatal("--may-exist and --add-duplicate may not be used together"); | |
1792 | } | |
1793 | ||
1794 | if (lr_name) { | |
1795 | if (!add_duplicate) { | |
1796 | const struct nbrec_logical_router *lr; | |
1797 | NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->idl) { | |
1798 | if (!strcmp(lr->name, lr_name)) { | |
1799 | if (may_exist) { | |
1800 | return; | |
1801 | } | |
1802 | ctl_fatal("%s: a router with this name already exists", | |
1803 | lr_name); | |
1804 | } | |
1805 | } | |
1806 | } | |
1807 | } else if (may_exist) { | |
1808 | ctl_fatal("--may-exist requires specifying a name"); | |
1809 | } else if (add_duplicate) { | |
1810 | ctl_fatal("--add-duplicate requires specifying a name"); | |
1811 | } | |
1812 | ||
1813 | struct nbrec_logical_router *lr; | |
1814 | lr = nbrec_logical_router_insert(ctx->txn); | |
1815 | if (lr_name) { | |
1816 | nbrec_logical_router_set_name(lr, lr_name); | |
1817 | } | |
1818 | } | |
1819 | ||
1820 | static void | |
1821 | nbctl_lr_del(struct ctl_context *ctx) | |
1822 | { | |
1823 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
1824 | const char *id = ctx->argv[1]; | |
1825 | const struct nbrec_logical_router *lr; | |
1826 | ||
1827 | lr = lr_by_name_or_uuid(ctx, id, must_exist); | |
1828 | if (!lr) { | |
1829 | return; | |
1830 | } | |
1831 | ||
1832 | nbrec_logical_router_delete(lr); | |
1833 | } | |
1834 | ||
1835 | static void | |
1836 | nbctl_lr_list(struct ctl_context *ctx) | |
1837 | { | |
1838 | const struct nbrec_logical_router *lr; | |
1839 | struct smap lrs; | |
1840 | ||
1841 | smap_init(&lrs); | |
1842 | NBREC_LOGICAL_ROUTER_FOR_EACH(lr, ctx->idl) { | |
1843 | smap_add_format(&lrs, lr->name, UUID_FMT " (%s)", | |
1844 | UUID_ARGS(&lr->header_.uuid), lr->name); | |
1845 | } | |
1846 | const struct smap_node **nodes = smap_sort(&lrs); | |
1847 | for (size_t i = 0; i < smap_count(&lrs); i++) { | |
1848 | const struct smap_node *node = nodes[i]; | |
1849 | ds_put_format(&ctx->output, "%s\n", node->value); | |
1850 | } | |
1851 | smap_destroy(&lrs); | |
1852 | free(nodes); | |
1853 | } | |
e48ccf3c | 1854 | |
281977f7 NS |
1855 | static const struct nbrec_dhcp_options * |
1856 | dhcp_options_get(struct ctl_context *ctx, const char *id, bool must_exist) | |
1857 | { | |
1858 | struct uuid dhcp_opts_uuid; | |
1859 | const struct nbrec_dhcp_options *dhcp_opts = NULL; | |
1860 | if (uuid_from_string(&dhcp_opts_uuid, id)) { | |
1861 | dhcp_opts = nbrec_dhcp_options_get_for_uuid(ctx->idl, &dhcp_opts_uuid); | |
1862 | } | |
1863 | ||
1864 | if (!dhcp_opts && must_exist) { | |
1865 | ctl_fatal("%s: dhcp options UUID not found", id); | |
1866 | } | |
1867 | return dhcp_opts; | |
1868 | } | |
1869 | ||
1870 | static void | |
1871 | nbctl_dhcp_options_create(struct ctl_context *ctx) | |
1872 | { | |
1873 | /* Validate the cidr */ | |
1874 | ovs_be32 ip; | |
1875 | unsigned int plen; | |
1876 | char *error = ip_parse_cidr(ctx->argv[1], &ip, &plen); | |
1877 | if (error){ | |
1878 | /* check if its IPv6 cidr */ | |
1879 | free(error); | |
1880 | struct in6_addr ipv6; | |
1881 | error = ipv6_parse_cidr(ctx->argv[1], &ipv6, &plen); | |
1882 | if (error) { | |
1883 | free(error); | |
1884 | ctl_fatal("Invalid cidr format '%s'", ctx->argv[1]); | |
281977f7 NS |
1885 | } |
1886 | } | |
1887 | ||
1888 | struct nbrec_dhcp_options *dhcp_opts = nbrec_dhcp_options_insert(ctx->txn); | |
1889 | nbrec_dhcp_options_set_cidr(dhcp_opts, ctx->argv[1]); | |
1890 | ||
1891 | struct smap ext_ids = SMAP_INITIALIZER(&ext_ids); | |
1892 | for (size_t i = 2; i < ctx->argc; i++) { | |
1893 | char *key, *value; | |
1894 | value = xstrdup(ctx->argv[i]); | |
1895 | key = strsep(&value, "="); | |
1896 | if (value) { | |
1897 | smap_add(&ext_ids, key, value); | |
1898 | } | |
1899 | free(key); | |
1900 | } | |
1901 | ||
1902 | nbrec_dhcp_options_set_external_ids(dhcp_opts, &ext_ids); | |
1903 | smap_destroy(&ext_ids); | |
1904 | } | |
1905 | ||
1906 | static void | |
1907 | nbctl_dhcp_options_set_options(struct ctl_context *ctx) | |
1908 | { | |
1909 | const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get( | |
1910 | ctx, ctx->argv[1], true); | |
1911 | ||
1912 | struct smap dhcp_options = SMAP_INITIALIZER(&dhcp_options); | |
1913 | for (size_t i = 2; i < ctx->argc; i++) { | |
1914 | char *key, *value; | |
1915 | value = xstrdup(ctx->argv[i]); | |
1916 | key = strsep(&value, "="); | |
1917 | if (value) { | |
1918 | smap_add(&dhcp_options, key, value); | |
1919 | } | |
1920 | free(key); | |
1921 | } | |
1922 | ||
1923 | nbrec_dhcp_options_set_options(dhcp_opts, &dhcp_options); | |
1924 | smap_destroy(&dhcp_options); | |
1925 | } | |
1926 | ||
1927 | static void | |
1928 | nbctl_dhcp_options_get_options(struct ctl_context *ctx) | |
1929 | { | |
1930 | const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get( | |
1931 | ctx, ctx->argv[1], true); | |
1932 | ||
1933 | struct smap_node *node; | |
1934 | SMAP_FOR_EACH(node, &dhcp_opts->options) { | |
1935 | ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value); | |
1936 | } | |
1937 | } | |
1938 | ||
1939 | static void | |
1940 | nbctl_dhcp_options_del(struct ctl_context *ctx) | |
1941 | { | |
1942 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
1943 | const char *id = ctx->argv[1]; | |
1944 | const struct nbrec_dhcp_options *dhcp_opts; | |
1945 | ||
1946 | dhcp_opts = dhcp_options_get(ctx, id, must_exist); | |
1947 | if (!dhcp_opts) { | |
1948 | return; | |
1949 | } | |
1950 | ||
1951 | nbrec_dhcp_options_delete(dhcp_opts); | |
1952 | } | |
1953 | ||
1954 | static void | |
1955 | nbctl_dhcp_options_list(struct ctl_context *ctx) | |
1956 | { | |
1957 | const struct nbrec_dhcp_options *dhcp_opts; | |
1958 | struct smap dhcp_options; | |
1959 | ||
1960 | smap_init(&dhcp_options); | |
1961 | NBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opts, ctx->idl) { | |
1962 | smap_add_format(&dhcp_options, dhcp_opts->cidr, UUID_FMT, | |
1963 | UUID_ARGS(&dhcp_opts->header_.uuid)); | |
1964 | } | |
1965 | const struct smap_node **nodes = smap_sort(&dhcp_options); | |
1966 | for (size_t i = 0; i < smap_count(&dhcp_options); i++) { | |
1967 | const struct smap_node *node = nodes[i]; | |
1968 | ds_put_format(&ctx->output, "%s\n", node->value); | |
1969 | } | |
1970 | smap_destroy(&dhcp_options); | |
1971 | free(nodes); | |
1972 | } | |
1973 | ||
e48ccf3c JP |
1974 | /* The caller must free the returned string. */ |
1975 | static char * | |
1976 | normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen) | |
1977 | { | |
1978 | ovs_be32 network = ipv4 & be32_prefix_mask(plen); | |
1979 | if (plen == 32) { | |
1980 | return xasprintf(IP_FMT, IP_ARGS(network)); | |
1981 | } else { | |
1982 | return xasprintf(IP_FMT"/%d", IP_ARGS(network), plen); | |
1983 | } | |
1984 | } | |
1985 | ||
1986 | /* The caller must free the returned string. */ | |
1987 | static char * | |
1988 | normalize_ipv6_prefix(struct in6_addr ipv6, unsigned int plen) | |
1989 | { | |
1990 | char network_s[INET6_ADDRSTRLEN]; | |
1991 | ||
1992 | struct in6_addr mask = ipv6_create_mask(plen); | |
1993 | struct in6_addr network = ipv6_addr_bitand(&ipv6, &mask); | |
1994 | ||
1995 | inet_ntop(AF_INET6, &network, network_s, INET6_ADDRSTRLEN); | |
1996 | if (plen == 128) { | |
1997 | return xasprintf("%s", network_s); | |
1998 | } else { | |
1999 | return xasprintf("%s/%d", network_s, plen); | |
2000 | } | |
2001 | } | |
2002 | ||
2003 | /* The caller must free the returned string. */ | |
2004 | static char * | |
2005 | normalize_prefix_str(const char *orig_prefix) | |
2006 | { | |
2007 | unsigned int plen; | |
2008 | ovs_be32 ipv4; | |
2009 | char *error; | |
2010 | ||
2011 | error = ip_parse_cidr(orig_prefix, &ipv4, &plen); | |
2012 | if (!error) { | |
2013 | return normalize_ipv4_prefix(ipv4, plen); | |
2014 | } else { | |
2015 | struct in6_addr ipv6; | |
2016 | free(error); | |
2017 | ||
2018 | error = ipv6_parse_cidr(orig_prefix, &ipv6, &plen); | |
2019 | if (error) { | |
2020 | free(error); | |
2021 | return NULL; | |
2022 | } | |
2023 | return normalize_ipv6_prefix(ipv6, plen); | |
2024 | } | |
2025 | } | |
2026 | \f | |
2027 | static void | |
2028 | nbctl_lr_route_add(struct ctl_context *ctx) | |
2029 | { | |
2030 | const struct nbrec_logical_router *lr; | |
2031 | lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); | |
2032 | char *prefix, *next_hop; | |
2033 | ||
440a9f4b GS |
2034 | const char *policy = shash_find_data(&ctx->options, "--policy"); |
2035 | if (policy && strcmp(policy, "src-ip") && strcmp(policy, "dst-ip")) { | |
2036 | ctl_fatal("bad policy: %s", policy); | |
2037 | } | |
2038 | ||
e48ccf3c JP |
2039 | prefix = normalize_prefix_str(ctx->argv[2]); |
2040 | if (!prefix) { | |
2041 | ctl_fatal("bad prefix argument: %s", ctx->argv[2]); | |
2042 | } | |
2043 | ||
2044 | next_hop = normalize_prefix_str(ctx->argv[3]); | |
2045 | if (!next_hop) { | |
b712dea0 | 2046 | free(prefix); |
e48ccf3c JP |
2047 | ctl_fatal("bad next hop argument: %s", ctx->argv[3]); |
2048 | } | |
2049 | ||
2050 | if (strchr(prefix, '.')) { | |
2051 | ovs_be32 hop_ipv4; | |
2052 | if (!ip_parse(ctx->argv[3], &hop_ipv4)) { | |
b712dea0 | 2053 | free(prefix); |
2054 | free(next_hop); | |
e48ccf3c JP |
2055 | ctl_fatal("bad IPv4 nexthop argument: %s", ctx->argv[3]); |
2056 | } | |
2057 | } else { | |
2058 | struct in6_addr hop_ipv6; | |
2059 | if (!ipv6_parse(ctx->argv[3], &hop_ipv6)) { | |
b712dea0 | 2060 | free(prefix); |
2061 | free(next_hop); | |
e48ccf3c JP |
2062 | ctl_fatal("bad IPv6 nexthop argument: %s", ctx->argv[3]); |
2063 | } | |
2064 | } | |
2065 | ||
2066 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
2067 | for (int i = 0; i < lr->n_static_routes; i++) { | |
2068 | const struct nbrec_logical_router_static_route *route | |
2069 | = lr->static_routes[i]; | |
2070 | char *rt_prefix; | |
2071 | ||
2072 | rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix); | |
2073 | if (!rt_prefix) { | |
2074 | /* Ignore existing prefix we couldn't parse. */ | |
2075 | continue; | |
2076 | } | |
2077 | ||
2078 | if (strcmp(rt_prefix, prefix)) { | |
2079 | free(rt_prefix); | |
2080 | continue; | |
2081 | } | |
2082 | ||
2083 | if (!may_exist) { | |
b712dea0 | 2084 | free(next_hop); |
85d87d16 | 2085 | free(rt_prefix); |
e48ccf3c JP |
2086 | ctl_fatal("duplicate prefix: %s", prefix); |
2087 | } | |
2088 | ||
2089 | /* Update the next hop for an existing route. */ | |
2090 | nbrec_logical_router_verify_static_routes(lr); | |
2091 | nbrec_logical_router_static_route_verify_ip_prefix(route); | |
2092 | nbrec_logical_router_static_route_verify_nexthop(route); | |
2093 | nbrec_logical_router_static_route_set_ip_prefix(route, prefix); | |
2094 | nbrec_logical_router_static_route_set_nexthop(route, next_hop); | |
01a528f3 GS |
2095 | if (ctx->argc == 5) { |
2096 | nbrec_logical_router_static_route_set_output_port(route, | |
2097 | ctx->argv[4]); | |
2098 | } | |
440a9f4b GS |
2099 | if (policy) { |
2100 | nbrec_logical_router_static_route_set_policy(route, policy); | |
2101 | } | |
e48ccf3c JP |
2102 | free(rt_prefix); |
2103 | free(next_hop); | |
2104 | free(prefix); | |
2105 | return; | |
2106 | } | |
2107 | ||
2108 | struct nbrec_logical_router_static_route *route; | |
2109 | route = nbrec_logical_router_static_route_insert(ctx->txn); | |
2110 | nbrec_logical_router_static_route_set_ip_prefix(route, prefix); | |
2111 | nbrec_logical_router_static_route_set_nexthop(route, next_hop); | |
2112 | if (ctx->argc == 5) { | |
2113 | nbrec_logical_router_static_route_set_output_port(route, ctx->argv[4]); | |
2114 | } | |
440a9f4b GS |
2115 | if (policy) { |
2116 | nbrec_logical_router_static_route_set_policy(route, policy); | |
2117 | } | |
e48ccf3c JP |
2118 | |
2119 | nbrec_logical_router_verify_static_routes(lr); | |
2120 | struct nbrec_logical_router_static_route **new_routes | |
2121 | = xmalloc(sizeof *new_routes * (lr->n_static_routes + 1)); | |
2122 | memcpy(new_routes, lr->static_routes, | |
2123 | sizeof *new_routes * lr->n_static_routes); | |
2124 | new_routes[lr->n_static_routes] = route; | |
2125 | nbrec_logical_router_set_static_routes(lr, new_routes, | |
2126 | lr->n_static_routes + 1); | |
2127 | free(new_routes); | |
2128 | free(next_hop); | |
2129 | free(prefix); | |
2130 | } | |
2131 | ||
2132 | static void | |
2133 | nbctl_lr_route_del(struct ctl_context *ctx) | |
2134 | { | |
2135 | const struct nbrec_logical_router *lr; | |
2136 | lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); | |
2137 | ||
2138 | if (ctx->argc == 2) { | |
2139 | /* If a prefix is not specified, delete all routes. */ | |
2140 | nbrec_logical_router_set_static_routes(lr, NULL, 0); | |
2141 | return; | |
2142 | } | |
2143 | ||
2144 | char *prefix = normalize_prefix_str(ctx->argv[2]); | |
2145 | if (!prefix) { | |
2146 | ctl_fatal("bad prefix argument: %s", ctx->argv[2]); | |
2147 | } | |
2148 | ||
2149 | for (int i = 0; i < lr->n_static_routes; i++) { | |
2150 | char *rt_prefix = normalize_prefix_str(lr->static_routes[i]->ip_prefix); | |
2151 | if (!rt_prefix) { | |
2152 | /* Ignore existing prefix we couldn't parse. */ | |
2153 | continue; | |
2154 | } | |
2155 | ||
2156 | if (!strcmp(prefix, rt_prefix)) { | |
2157 | struct nbrec_logical_router_static_route **new_routes | |
2158 | = xmemdup(lr->static_routes, | |
2159 | sizeof *new_routes * lr->n_static_routes); | |
2160 | ||
2161 | new_routes[i] = lr->static_routes[lr->n_static_routes - 1]; | |
2162 | nbrec_logical_router_verify_static_routes(lr); | |
2163 | nbrec_logical_router_set_static_routes(lr, new_routes, | |
2164 | lr->n_static_routes - 1); | |
2165 | free(new_routes); | |
2166 | free(rt_prefix); | |
2167 | free(prefix); | |
2168 | return; | |
2169 | } | |
2170 | free(rt_prefix); | |
2171 | } | |
2172 | ||
2173 | if (!shash_find(&ctx->options, "--if-exists")) { | |
2174 | ctl_fatal("no matching prefix: %s", prefix); | |
2175 | } | |
2176 | free(prefix); | |
2177 | } | |
fa2a27b2 | 2178 | \f |
31114af7 JP |
2179 | static const struct nbrec_logical_router_port * |
2180 | lrp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist) | |
2181 | { | |
2182 | const struct nbrec_logical_router_port *lrp = NULL; | |
2183 | ||
2184 | struct uuid lrp_uuid; | |
2185 | bool is_uuid = uuid_from_string(&lrp_uuid, id); | |
2186 | if (is_uuid) { | |
2187 | lrp = nbrec_logical_router_port_get_for_uuid(ctx->idl, &lrp_uuid); | |
2188 | } | |
2189 | ||
2190 | if (!lrp) { | |
2191 | NBREC_LOGICAL_ROUTER_PORT_FOR_EACH(lrp, ctx->idl) { | |
2192 | if (!strcmp(lrp->name, id)) { | |
2193 | break; | |
2194 | } | |
2195 | } | |
2196 | } | |
2197 | ||
2198 | if (!lrp && must_exist) { | |
2199 | ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : "name"); | |
2200 | } | |
2201 | ||
2202 | return lrp; | |
2203 | } | |
2204 | ||
2205 | /* Returns the logical router that contains 'lrp'. */ | |
2206 | static const struct nbrec_logical_router * | |
2207 | lrp_to_lr(const struct ovsdb_idl *idl, | |
2208 | const struct nbrec_logical_router_port *lrp) | |
2209 | { | |
2210 | const struct nbrec_logical_router *lr; | |
2211 | NBREC_LOGICAL_ROUTER_FOR_EACH (lr, idl) { | |
2212 | for (size_t i = 0; i < lr->n_ports; i++) { | |
2213 | if (lr->ports[i] == lrp) { | |
2214 | return lr; | |
2215 | } | |
2216 | } | |
2217 | } | |
2218 | ||
2219 | /* Can't happen because of the database schema */ | |
2220 | ctl_fatal("port %s is not part of any logical router", | |
2221 | lrp->name); | |
2222 | } | |
2223 | ||
2224 | static const char * | |
2225 | lr_get_name(const struct nbrec_logical_router *lr, char uuid_s[UUID_LEN + 1], | |
2226 | size_t uuid_s_size) | |
2227 | { | |
2228 | if (lr->name[0]) { | |
2229 | return lr->name; | |
2230 | } | |
2231 | snprintf(uuid_s, uuid_s_size, UUID_FMT, UUID_ARGS(&lr->header_.uuid)); | |
2232 | return uuid_s; | |
2233 | } | |
2234 | ||
2235 | static void | |
2236 | nbctl_lrp_add(struct ctl_context *ctx) | |
2237 | { | |
2238 | bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; | |
2239 | ||
2240 | const struct nbrec_logical_router *lr; | |
2241 | lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); | |
2242 | ||
2243 | const char *lrp_name = ctx->argv[2]; | |
2244 | const char *mac = ctx->argv[3]; | |
4685e523 JP |
2245 | const char **networks = (const char **) &ctx->argv[4]; |
2246 | ||
2247 | int n_networks = ctx->argc - 4; | |
2248 | for (int i = 4; i < ctx->argc; i++) { | |
2249 | if (strchr(ctx->argv[i], '=')) { | |
2250 | n_networks = i - 4; | |
2251 | break; | |
2252 | } | |
2253 | } | |
2254 | ||
2255 | if (!n_networks) { | |
2256 | ctl_fatal("%s: router port requires specifying a network", lrp_name); | |
2257 | } | |
2258 | ||
2259 | char **settings = (char **) &ctx->argv[n_networks + 4]; | |
2260 | int n_settings = ctx->argc - 4 - n_networks; | |
31114af7 JP |
2261 | |
2262 | const struct nbrec_logical_router_port *lrp; | |
2263 | lrp = lrp_by_name_or_uuid(ctx, lrp_name, false); | |
2264 | if (lrp) { | |
2265 | if (!may_exist) { | |
2266 | ctl_fatal("%s: a port with this name already exists", | |
2267 | lrp_name); | |
2268 | } | |
2269 | ||
2270 | const struct nbrec_logical_router *bound_lr; | |
2271 | bound_lr = lrp_to_lr(ctx->idl, lrp); | |
2272 | if (bound_lr != lr) { | |
2273 | char uuid_s[UUID_LEN + 1]; | |
2274 | ctl_fatal("%s: port already exists but in router %s", lrp_name, | |
2275 | lr_get_name(bound_lr, uuid_s, sizeof uuid_s)); | |
2276 | } | |
2277 | ||
2278 | if (strcmp(mac, lrp->mac)) { | |
2279 | ctl_fatal("%s: port already exists with mac %s", lrp_name, | |
2280 | lrp->mac); | |
2281 | } | |
2282 | ||
4685e523 JP |
2283 | struct sset new_networks = SSET_INITIALIZER(&new_networks); |
2284 | for (int i = 0; i < n_networks; i++) { | |
2285 | sset_add(&new_networks, networks[i]); | |
2286 | } | |
2287 | ||
2288 | struct sset orig_networks = SSET_INITIALIZER(&orig_networks); | |
2289 | sset_add_array(&orig_networks, lrp->networks, lrp->n_networks); | |
2290 | ||
88e5c2a4 | 2291 | bool same_networks = sset_equals(&orig_networks, &new_networks); |
2292 | sset_destroy(&orig_networks); | |
2293 | sset_destroy(&new_networks); | |
2294 | if (!same_networks) { | |
4685e523 JP |
2295 | ctl_fatal("%s: port already exists with different network", |
2296 | lrp_name); | |
2297 | } | |
2298 | ||
4685e523 JP |
2299 | /* Special-case sanity-check of peer ports. */ |
2300 | const char *peer = NULL; | |
2301 | for (int i = 0; i < n_settings; i++) { | |
2302 | if (!strncmp(settings[i], "peer=", 5)) { | |
2303 | peer = settings[i] + 5; | |
2304 | break; | |
2305 | } | |
31114af7 JP |
2306 | } |
2307 | ||
2308 | if ((!peer != !lrp->peer) || | |
2309 | (lrp->peer && strcmp(peer, lrp->peer))) { | |
2310 | ctl_fatal("%s: port already exists with mismatching peer", | |
2311 | lrp_name); | |
2312 | } | |
2313 | ||
2314 | return; | |
2315 | } | |
2316 | ||
2317 | struct eth_addr ea; | |
10c3fcdf | 2318 | if (!eth_addr_from_string(mac, &ea)) { |
4685e523 | 2319 | ctl_fatal("%s: invalid mac address %s", lrp_name, mac); |
31114af7 JP |
2320 | } |
2321 | ||
4685e523 JP |
2322 | for (int i = 0; i < n_networks; i++) { |
2323 | ovs_be32 ipv4; | |
2324 | unsigned int plen; | |
2325 | char *error = ip_parse_cidr(networks[i], &ipv4, &plen); | |
31114af7 JP |
2326 | if (error) { |
2327 | free(error); | |
4685e523 JP |
2328 | struct in6_addr ipv6; |
2329 | error = ipv6_parse_cidr(networks[i], &ipv6, &plen); | |
2330 | if (error) { | |
2331 | free(error); | |
2332 | ctl_fatal("%s: invalid network address: %s", lrp_name, | |
2333 | networks[i]); | |
2334 | } | |
31114af7 JP |
2335 | } |
2336 | } | |
2337 | ||
2338 | /* Create the logical port. */ | |
2339 | lrp = nbrec_logical_router_port_insert(ctx->txn); | |
2340 | nbrec_logical_router_port_set_name(lrp, lrp_name); | |
2341 | nbrec_logical_router_port_set_mac(lrp, mac); | |
4685e523 JP |
2342 | nbrec_logical_router_port_set_networks(lrp, networks, n_networks); |
2343 | ||
2344 | for (int i = 0; i < n_settings; i++) { | |
2345 | ctl_set_column("Logical_Router_Port", &lrp->header_, settings[i], | |
2346 | ctx->symtab); | |
31114af7 JP |
2347 | } |
2348 | ||
2349 | /* Insert the logical port into the logical router. */ | |
2350 | nbrec_logical_router_verify_ports(lr); | |
2351 | struct nbrec_logical_router_port **new_ports = xmalloc(sizeof *new_ports * | |
2352 | (lr->n_ports + 1)); | |
2353 | memcpy(new_ports, lr->ports, sizeof *new_ports * lr->n_ports); | |
2354 | new_ports[lr->n_ports] = CONST_CAST(struct nbrec_logical_router_port *, | |
2355 | lrp); | |
2356 | nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports + 1); | |
2357 | free(new_ports); | |
2358 | } | |
2359 | ||
2360 | /* Removes logical router port 'lr->ports[idx]'. */ | |
2361 | static void | |
2362 | remove_lrp(const struct nbrec_logical_router *lr, size_t idx) | |
2363 | { | |
2364 | const struct nbrec_logical_router_port *lrp = lr->ports[idx]; | |
2365 | ||
2366 | /* First remove 'lrp' from the array of ports. This is what will | |
2367 | * actually cause the logical port to be deleted when the transaction is | |
2368 | * sent to the database server (due to garbage collection). */ | |
2369 | struct nbrec_logical_router_port **new_ports | |
2370 | = xmemdup(lr->ports, sizeof *new_ports * lr->n_ports); | |
2371 | new_ports[idx] = new_ports[lr->n_ports - 1]; | |
2372 | nbrec_logical_router_verify_ports(lr); | |
2373 | nbrec_logical_router_set_ports(lr, new_ports, lr->n_ports - 1); | |
2374 | free(new_ports); | |
2375 | ||
2376 | /* Delete 'lrp' from the IDL. This won't have a real effect on | |
2377 | * the database server (the IDL will suppress it in fact) but it | |
2378 | * means that it won't show up when we iterate with | |
2379 | * NBREC_LOGICAL_ROUTER_PORT_FOR_EACH later. */ | |
2380 | nbrec_logical_router_port_delete(lrp); | |
2381 | } | |
2382 | ||
2383 | static void | |
2384 | nbctl_lrp_del(struct ctl_context *ctx) | |
2385 | { | |
2386 | bool must_exist = !shash_find(&ctx->options, "--if-exists"); | |
2387 | const struct nbrec_logical_router_port *lrp; | |
2388 | ||
2389 | lrp = lrp_by_name_or_uuid(ctx, ctx->argv[1], must_exist); | |
2390 | if (!lrp) { | |
2391 | return; | |
2392 | } | |
2393 | ||
2394 | /* Find the router that contains 'lrp', then delete it. */ | |
2395 | const struct nbrec_logical_router *lr; | |
2396 | NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->idl) { | |
2397 | for (size_t i = 0; i < lr->n_ports; i++) { | |
2398 | if (lr->ports[i] == lrp) { | |
2399 | remove_lrp(lr, i); | |
2400 | return; | |
2401 | } | |
2402 | } | |
2403 | } | |
2404 | ||
2405 | /* Can't happen because of the database schema. */ | |
2406 | ctl_fatal("logical port %s is not part of any logical router", | |
2407 | ctx->argv[1]); | |
2408 | } | |
2409 | ||
2410 | /* Print a list of logical router ports. */ | |
2411 | static void | |
2412 | nbctl_lrp_list(struct ctl_context *ctx) | |
2413 | { | |
2414 | const char *id = ctx->argv[1]; | |
2415 | const struct nbrec_logical_router *lr; | |
2416 | struct smap lrps; | |
2417 | size_t i; | |
2418 | ||
2419 | lr = lr_by_name_or_uuid(ctx, id, true); | |
2420 | ||
2421 | smap_init(&lrps); | |
2422 | for (i = 0; i < lr->n_ports; i++) { | |
2423 | const struct nbrec_logical_router_port *lrp = lr->ports[i]; | |
2424 | smap_add_format(&lrps, lrp->name, UUID_FMT " (%s)", | |
2425 | UUID_ARGS(&lrp->header_.uuid), lrp->name); | |
2426 | } | |
2427 | const struct smap_node **nodes = smap_sort(&lrps); | |
2428 | for (i = 0; i < smap_count(&lrps); i++) { | |
2429 | const struct smap_node *node = nodes[i]; | |
2430 | ds_put_format(&ctx->output, "%s\n", node->value); | |
2431 | } | |
2432 | smap_destroy(&lrps); | |
2433 | free(nodes); | |
2434 | } | |
2435 | ||
2436 | /* Set the logical router port admin-enabled state. */ | |
2437 | static void | |
2438 | nbctl_lrp_set_enabled(struct ctl_context *ctx) | |
2439 | { | |
2440 | const char *id = ctx->argv[1]; | |
2441 | const char *state = ctx->argv[2]; | |
2442 | const struct nbrec_logical_router_port *lrp; | |
2443 | ||
2444 | lrp = lrp_by_name_or_uuid(ctx, id, true); | |
2445 | if (!lrp) { | |
2446 | return; | |
2447 | } | |
2448 | ||
2449 | bool enabled = parse_enabled(state); | |
2450 | nbrec_logical_router_port_set_enabled(lrp, &enabled, 1); | |
2451 | } | |
2452 | ||
2453 | /* Print admin-enabled state for logical router port. */ | |
2454 | static void | |
2455 | nbctl_lrp_get_enabled(struct ctl_context *ctx) | |
2456 | { | |
2457 | const char *id = ctx->argv[1]; | |
2458 | const struct nbrec_logical_router_port *lrp; | |
2459 | ||
2460 | lrp = lrp_by_name_or_uuid(ctx, id, true); | |
2461 | if (!lrp) { | |
2462 | return; | |
2463 | } | |
2464 | ||
2465 | ds_put_format(&ctx->output, "%s\n", | |
2466 | !lrp->enabled || | |
2467 | *lrp->enabled ? "enabled" : "disabled"); | |
2468 | } | |
2469 | \f | |
e48ccf3c | 2470 | struct ipv4_route { |
440a9f4b | 2471 | int priority; |
e48ccf3c JP |
2472 | ovs_be32 addr; |
2473 | const struct nbrec_logical_router_static_route *route; | |
2474 | }; | |
2475 | ||
2476 | static int | |
2477 | ipv4_route_cmp(const void *route1_, const void *route2_) | |
2478 | { | |
2479 | const struct ipv4_route *route1p = route1_; | |
2480 | const struct ipv4_route *route2p = route2_; | |
2481 | ||
440a9f4b GS |
2482 | if (route1p->priority != route2p->priority) { |
2483 | return route1p->priority > route2p->priority ? -1 : 1; | |
e48ccf3c JP |
2484 | } else if (route1p->addr != route2p->addr) { |
2485 | return ntohl(route1p->addr) < ntohl(route2p->addr) ? -1 : 1; | |
2486 | } else { | |
2487 | return 0; | |
2488 | } | |
2489 | } | |
2490 | ||
2491 | struct ipv6_route { | |
440a9f4b | 2492 | int priority; |
e48ccf3c JP |
2493 | struct in6_addr addr; |
2494 | const struct nbrec_logical_router_static_route *route; | |
2495 | }; | |
2496 | ||
2497 | static int | |
2498 | ipv6_route_cmp(const void *route1_, const void *route2_) | |
2499 | { | |
2500 | const struct ipv6_route *route1p = route1_; | |
2501 | const struct ipv6_route *route2p = route2_; | |
2502 | ||
440a9f4b GS |
2503 | if (route1p->priority != route2p->priority) { |
2504 | return route1p->priority > route2p->priority ? -1 : 1; | |
e48ccf3c JP |
2505 | } |
2506 | return memcmp(&route1p->addr, &route2p->addr, sizeof(route1p->addr)); | |
2507 | } | |
2508 | ||
2509 | static void | |
2510 | print_route(const struct nbrec_logical_router_static_route *route, struct ds *s) | |
2511 | { | |
2512 | ||
2513 | char *prefix = normalize_prefix_str(route->ip_prefix); | |
2514 | char *next_hop = normalize_prefix_str(route->nexthop); | |
2515 | ds_put_format(s, "%25s %25s", prefix, next_hop); | |
2516 | free(prefix); | |
2517 | free(next_hop); | |
2518 | ||
440a9f4b GS |
2519 | if (route->policy) { |
2520 | ds_put_format(s, " %s", route->policy); | |
2521 | } else { | |
2522 | ds_put_format(s, " %s", "dst-ip"); | |
2523 | } | |
2524 | ||
e48ccf3c JP |
2525 | if (route->output_port) { |
2526 | ds_put_format(s, " %s", route->output_port); | |
2527 | } | |
2528 | ds_put_char(s, '\n'); | |
2529 | } | |
2530 | ||
2531 | static void | |
2532 | nbctl_lr_route_list(struct ctl_context *ctx) | |
2533 | { | |
2534 | const struct nbrec_logical_router *lr; | |
2535 | struct ipv4_route *ipv4_routes; | |
2536 | struct ipv6_route *ipv6_routes; | |
2537 | size_t n_ipv4_routes = 0; | |
2538 | size_t n_ipv6_routes = 0; | |
2539 | ||
2540 | lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); | |
2541 | ||
2542 | ipv4_routes = xmalloc(sizeof *ipv4_routes * lr->n_static_routes); | |
2543 | ipv6_routes = xmalloc(sizeof *ipv6_routes * lr->n_static_routes); | |
2544 | ||
2545 | for (int i = 0; i < lr->n_static_routes; i++) { | |
2546 | const struct nbrec_logical_router_static_route *route | |
2547 | = lr->static_routes[i]; | |
2548 | unsigned int plen; | |
2549 | ovs_be32 ipv4; | |
440a9f4b | 2550 | const char *policy = route->policy ? route->policy : "dst-ip"; |
e48ccf3c | 2551 | char *error; |
e48ccf3c JP |
2552 | error = ip_parse_cidr(route->ip_prefix, &ipv4, &plen); |
2553 | if (!error) { | |
440a9f4b GS |
2554 | ipv4_routes[n_ipv4_routes].priority = !strcmp(policy, "dst-ip") |
2555 | ? (2 * plen) + 1 | |
2556 | : 2 * plen; | |
e48ccf3c JP |
2557 | ipv4_routes[n_ipv4_routes].addr = ipv4; |
2558 | ipv4_routes[n_ipv4_routes].route = route; | |
2559 | n_ipv4_routes++; | |
2560 | } else { | |
2561 | free(error); | |
2562 | ||
2563 | struct in6_addr ipv6; | |
ed46c522 WT |
2564 | error = ipv6_parse_cidr(route->ip_prefix, &ipv6, &plen); |
2565 | if (!error) { | |
440a9f4b GS |
2566 | ipv6_routes[n_ipv6_routes].priority = !strcmp(policy, "dst-ip") |
2567 | ? (2 * plen) + 1 | |
2568 | : 2 * plen; | |
e48ccf3c JP |
2569 | ipv6_routes[n_ipv6_routes].addr = ipv6; |
2570 | ipv6_routes[n_ipv6_routes].route = route; | |
2571 | n_ipv6_routes++; | |
2572 | } else { | |
2573 | /* Invalid prefix. */ | |
2574 | VLOG_WARN("router "UUID_FMT" (%s) has invalid prefix: %s", | |
2575 | UUID_ARGS(&lr->header_.uuid), lr->name, | |
2576 | route->ip_prefix); | |
2577 | free(error); | |
2578 | continue; | |
2579 | } | |
2580 | } | |
2581 | } | |
2582 | ||
2583 | qsort(ipv4_routes, n_ipv4_routes, sizeof *ipv4_routes, ipv4_route_cmp); | |
2584 | qsort(ipv6_routes, n_ipv6_routes, sizeof *ipv6_routes, ipv6_route_cmp); | |
2585 | ||
2586 | if (n_ipv4_routes) { | |
2587 | ds_put_cstr(&ctx->output, "IPv4 Routes\n"); | |
2588 | } | |
2589 | for (int i = 0; i < n_ipv4_routes; i++) { | |
2590 | print_route(ipv4_routes[i].route, &ctx->output); | |
2591 | } | |
2592 | ||
2593 | if (n_ipv6_routes) { | |
2594 | ds_put_format(&ctx->output, "%sIPv6 Routes\n", | |
2595 | n_ipv4_routes ? "\n" : ""); | |
2596 | } | |
2597 | for (int i = 0; i < n_ipv6_routes; i++) { | |
2598 | print_route(ipv6_routes[i].route, &ctx->output); | |
2599 | } | |
2600 | ||
2601 | free(ipv4_routes); | |
2602 | free(ipv6_routes); | |
2603 | } | |
2604 | ||
70799ae8 | 2605 | static const struct ctl_table_class tables[] = { |
fa183acc BP |
2606 | {&nbrec_table_nb_global, |
2607 | {{&nbrec_table_nb_global, NULL, NULL}, | |
2608 | {NULL, NULL, NULL}}}, | |
2609 | ||
70799ae8 BP |
2610 | {&nbrec_table_logical_switch, |
2611 | {{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL}, | |
2612 | {NULL, NULL, NULL}}}, | |
2613 | ||
80f408f4 JP |
2614 | {&nbrec_table_logical_switch_port, |
2615 | {{&nbrec_table_logical_switch_port, &nbrec_logical_switch_port_col_name, | |
2616 | NULL}, | |
70799ae8 BP |
2617 | {NULL, NULL, NULL}}}, |
2618 | ||
2619 | {&nbrec_table_acl, | |
2620 | {{NULL, NULL, NULL}, | |
2621 | {NULL, NULL, NULL}}}, | |
2622 | ||
7a15be69 GS |
2623 | {&nbrec_table_load_balancer, |
2624 | {{NULL, NULL, NULL}, | |
2625 | {NULL, NULL, NULL}}}, | |
2626 | ||
70799ae8 BP |
2627 | {&nbrec_table_logical_router, |
2628 | {{&nbrec_table_logical_router, &nbrec_logical_router_col_name, NULL}, | |
2629 | {NULL, NULL, NULL}}}, | |
2630 | ||
2631 | {&nbrec_table_logical_router_port, | |
2632 | {{&nbrec_table_logical_router_port, &nbrec_logical_router_port_col_name, | |
2633 | NULL}, | |
2634 | {NULL, NULL, NULL}}}, | |
2635 | ||
28dc3fe9 SR |
2636 | {&nbrec_table_logical_router_static_route, |
2637 | {{&nbrec_table_logical_router_static_route, NULL, | |
2638 | NULL}, | |
2639 | {NULL, NULL, NULL}}}, | |
2640 | ||
de297547 GS |
2641 | {&nbrec_table_nat, |
2642 | {{&nbrec_table_nat, NULL, | |
2643 | NULL}, | |
2644 | {NULL, NULL, NULL}}}, | |
2645 | ||
ea382567 RB |
2646 | {&nbrec_table_address_set, |
2647 | {{&nbrec_table_address_set, &nbrec_address_set_col_name, NULL}, | |
2648 | {NULL, NULL, NULL}}}, | |
2649 | ||
281977f7 NS |
2650 | {&nbrec_table_dhcp_options, |
2651 | {{&nbrec_table_dhcp_options, NULL, | |
2652 | NULL}, | |
2653 | {NULL, NULL, NULL}}}, | |
2654 | ||
1a03fc7d BS |
2655 | {&nbrec_table_qos, |
2656 | {{&nbrec_table_qos, NULL, | |
2657 | NULL}, | |
2658 | {NULL, NULL, NULL}}}, | |
2659 | ||
70799ae8 BP |
2660 | {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} |
2661 | }; | |
2662 | \f | |
a416ff28 | 2663 | static void |
70799ae8 BP |
2664 | run_prerequisites(struct ctl_command *commands, size_t n_commands, |
2665 | struct ovsdb_idl *idl) | |
a416ff28 | 2666 | { |
fa183acc BP |
2667 | ovsdb_idl_add_table(idl, &nbrec_table_nb_global); |
2668 | if (wait_type == NBCTL_WAIT_SB) { | |
2669 | ovsdb_idl_add_column(idl, &nbrec_nb_global_col_sb_cfg); | |
2670 | } else if (wait_type == NBCTL_WAIT_HV) { | |
2671 | ovsdb_idl_add_column(idl, &nbrec_nb_global_col_hv_cfg); | |
2672 | } | |
a416ff28 | 2673 | |
fa183acc | 2674 | for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { |
70799ae8 BP |
2675 | if (c->syntax->prerequisites) { |
2676 | struct ctl_context ctx; | |
a416ff28 | 2677 | |
70799ae8 BP |
2678 | ds_init(&c->output); |
2679 | c->table = NULL; | |
a416ff28 | 2680 | |
70799ae8 BP |
2681 | ctl_context_init(&ctx, c, idl, NULL, NULL, NULL); |
2682 | (c->syntax->prerequisites)(&ctx); | |
2683 | ctl_context_done(&ctx, c); | |
a416ff28 | 2684 | |
70799ae8 BP |
2685 | ovs_assert(!c->output.string); |
2686 | ovs_assert(!c->table); | |
2687 | } | |
2688 | } | |
2689 | } | |
a416ff28 | 2690 | |
8ee574c3 | 2691 | static bool |
70799ae8 BP |
2692 | do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, |
2693 | struct ovsdb_idl *idl) | |
2694 | { | |
2695 | struct ovsdb_idl_txn *txn; | |
2696 | enum ovsdb_idl_txn_status status; | |
2697 | struct ovsdb_symbol_table *symtab; | |
2698 | struct ctl_context ctx; | |
2699 | struct ctl_command *c; | |
2700 | struct shash_node *node; | |
fa183acc | 2701 | int64_t next_cfg = 0; |
70799ae8 BP |
2702 | char *error = NULL; |
2703 | ||
2704 | txn = the_idl_txn = ovsdb_idl_txn_create(idl); | |
2705 | if (dry_run) { | |
2706 | ovsdb_idl_txn_set_dry_run(txn); | |
2707 | } | |
2708 | ||
2709 | ovsdb_idl_txn_add_comment(txn, "ovs-nbctl: %s", args); | |
2710 | ||
fa183acc BP |
2711 | const struct nbrec_nb_global *nb = nbrec_nb_global_first(idl); |
2712 | if (!nb) { | |
2713 | /* XXX add verification that table is empty */ | |
2714 | nb = nbrec_nb_global_insert(txn); | |
2715 | } | |
2716 | ||
2717 | if (wait_type != NBCTL_WAIT_NONE) { | |
de32cec7 BP |
2718 | ovsdb_idl_txn_increment(txn, &nb->header_, &nbrec_nb_global_col_nb_cfg, |
2719 | force_wait); | |
fa183acc BP |
2720 | } |
2721 | ||
70799ae8 BP |
2722 | symtab = ovsdb_symbol_table_create(); |
2723 | for (c = commands; c < &commands[n_commands]; c++) { | |
2724 | ds_init(&c->output); | |
2725 | c->table = NULL; | |
2726 | } | |
2727 | ctl_context_init(&ctx, NULL, idl, txn, symtab, NULL); | |
2728 | for (c = commands; c < &commands[n_commands]; c++) { | |
2729 | ctl_context_init_command(&ctx, c); | |
2730 | if (c->syntax->run) { | |
2731 | (c->syntax->run)(&ctx); | |
2732 | } | |
2733 | ctl_context_done_command(&ctx, c); | |
a416ff28 | 2734 | |
70799ae8 BP |
2735 | if (ctx.try_again) { |
2736 | ctl_context_done(&ctx, NULL); | |
2737 | goto try_again; | |
2738 | } | |
2739 | } | |
2740 | ctl_context_done(&ctx, NULL); | |
a416ff28 | 2741 | |
70799ae8 BP |
2742 | SHASH_FOR_EACH (node, &symtab->sh) { |
2743 | struct ovsdb_symbol *symbol = node->data; | |
2744 | if (!symbol->created) { | |
2745 | ctl_fatal("row id \"%s\" is referenced but never created (e.g. " | |
2746 | "with \"-- --id=%s create ...\")", | |
2747 | node->name, node->name); | |
2748 | } | |
2749 | if (!symbol->strong_ref) { | |
2750 | if (!symbol->weak_ref) { | |
2751 | VLOG_WARN("row id \"%s\" was created but no reference to it " | |
2752 | "was inserted, so it will not actually appear in " | |
2753 | "the database", node->name); | |
2754 | } else { | |
2755 | VLOG_WARN("row id \"%s\" was created but only a weak " | |
2756 | "reference to it was inserted, so it will not " | |
2757 | "actually appear in the database", node->name); | |
2758 | } | |
a416ff28 RB |
2759 | } |
2760 | } | |
2761 | ||
70799ae8 | 2762 | status = ovsdb_idl_txn_commit_block(txn); |
fa183acc BP |
2763 | if (wait_type != NBCTL_WAIT_NONE && status == TXN_SUCCESS) { |
2764 | next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); | |
2765 | } | |
70799ae8 BP |
2766 | if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { |
2767 | for (c = commands; c < &commands[n_commands]; c++) { | |
2768 | if (c->syntax->postprocess) { | |
2769 | ctl_context_init(&ctx, c, idl, txn, symtab, NULL); | |
2770 | (c->syntax->postprocess)(&ctx); | |
2771 | ctl_context_done(&ctx, c); | |
2772 | } | |
2773 | } | |
a416ff28 | 2774 | } |
70799ae8 | 2775 | error = xstrdup(ovsdb_idl_txn_get_error(txn)); |
a416ff28 | 2776 | |
70799ae8 BP |
2777 | switch (status) { |
2778 | case TXN_UNCOMMITTED: | |
2779 | case TXN_INCOMPLETE: | |
2780 | OVS_NOT_REACHED(); | |
a416ff28 | 2781 | |
70799ae8 BP |
2782 | case TXN_ABORTED: |
2783 | /* Should not happen--we never call ovsdb_idl_txn_abort(). */ | |
2784 | ctl_fatal("transaction aborted"); | |
a416ff28 | 2785 | |
70799ae8 BP |
2786 | case TXN_UNCHANGED: |
2787 | case TXN_SUCCESS: | |
2788 | break; | |
a416ff28 | 2789 | |
70799ae8 BP |
2790 | case TXN_TRY_AGAIN: |
2791 | goto try_again; | |
a416ff28 | 2792 | |
70799ae8 BP |
2793 | case TXN_ERROR: |
2794 | ctl_fatal("transaction error: %s", error); | |
a416ff28 | 2795 | |
70799ae8 BP |
2796 | case TXN_NOT_LOCKED: |
2797 | /* Should not happen--we never call ovsdb_idl_set_lock(). */ | |
2798 | ctl_fatal("database not locked"); | |
a416ff28 | 2799 | |
70799ae8 BP |
2800 | default: |
2801 | OVS_NOT_REACHED(); | |
2802 | } | |
2803 | free(error); | |
afc9da0b | 2804 | |
70799ae8 | 2805 | ovsdb_symbol_table_destroy(symtab); |
a416ff28 | 2806 | |
70799ae8 BP |
2807 | for (c = commands; c < &commands[n_commands]; c++) { |
2808 | struct ds *ds = &c->output; | |
a416ff28 | 2809 | |
70799ae8 BP |
2810 | if (c->table) { |
2811 | table_print(c->table, &table_style); | |
2812 | } else if (oneline) { | |
2813 | size_t j; | |
a416ff28 | 2814 | |
70799ae8 BP |
2815 | ds_chomp(ds, '\n'); |
2816 | for (j = 0; j < ds->length; j++) { | |
2817 | int ch = ds->string[j]; | |
2818 | switch (ch) { | |
2819 | case '\n': | |
2820 | fputs("\\n", stdout); | |
2821 | break; | |
2822 | ||
2823 | case '\\': | |
2824 | fputs("\\\\", stdout); | |
2825 | break; | |
2826 | ||
2827 | default: | |
2828 | putchar(ch); | |
2829 | } | |
a416ff28 | 2830 | } |
70799ae8 BP |
2831 | putchar('\n'); |
2832 | } else { | |
2833 | fputs(ds_cstr(ds), stdout); | |
a416ff28 | 2834 | } |
70799ae8 BP |
2835 | ds_destroy(&c->output); |
2836 | table_destroy(c->table); | |
2837 | free(c->table); | |
a416ff28 | 2838 | |
70799ae8 | 2839 | shash_destroy_free_data(&c->options); |
a416ff28 | 2840 | } |
70799ae8 | 2841 | free(commands); |
fa183acc BP |
2842 | |
2843 | if (wait_type != NBCTL_WAIT_NONE && status != TXN_UNCHANGED) { | |
2844 | ovsdb_idl_enable_reconnect(idl); | |
2845 | for (;;) { | |
2846 | ovsdb_idl_run(idl); | |
2847 | NBREC_NB_GLOBAL_FOR_EACH (nb, idl) { | |
2848 | int64_t cur_cfg = (wait_type == NBCTL_WAIT_SB | |
2849 | ? nb->sb_cfg | |
2850 | : nb->hv_cfg); | |
2851 | if (cur_cfg >= next_cfg) { | |
2852 | goto done; | |
2853 | } | |
2854 | } | |
2855 | ovsdb_idl_wait(idl); | |
2856 | poll_block(); | |
2857 | } | |
2858 | done: ; | |
2859 | } | |
2860 | ||
70799ae8 BP |
2861 | ovsdb_idl_txn_destroy(txn); |
2862 | ovsdb_idl_destroy(idl); | |
2863 | ||
8ee574c3 | 2864 | return true; |
a416ff28 | 2865 | |
70799ae8 BP |
2866 | try_again: |
2867 | /* Our transaction needs to be rerun, or a prerequisite was not met. Free | |
2868 | * resources and return so that the caller can try again. */ | |
2869 | if (txn) { | |
2870 | ovsdb_idl_txn_abort(txn); | |
2871 | ovsdb_idl_txn_destroy(txn); | |
2872 | the_idl_txn = NULL; | |
a416ff28 | 2873 | } |
70799ae8 BP |
2874 | ovsdb_symbol_table_destroy(symtab); |
2875 | for (c = commands; c < &commands[n_commands]; c++) { | |
2876 | ds_destroy(&c->output); | |
2877 | table_destroy(c->table); | |
2878 | free(c->table); | |
2879 | } | |
2880 | free(error); | |
8ee574c3 | 2881 | return false; |
70799ae8 BP |
2882 | } |
2883 | ||
2884 | /* Frees the current transaction and the underlying IDL and then calls | |
2885 | * exit(status). | |
2886 | * | |
2887 | * Freeing the transaction and the IDL is not strictly necessary, but it makes | |
2888 | * for a clean memory leak report from valgrind in the normal case. That makes | |
2889 | * it easier to notice real memory leaks. */ | |
2890 | static void | |
2891 | nbctl_exit(int status) | |
2892 | { | |
2893 | if (the_idl_txn) { | |
2894 | ovsdb_idl_txn_abort(the_idl_txn); | |
2895 | ovsdb_idl_txn_destroy(the_idl_txn); | |
2896 | } | |
2897 | ovsdb_idl_destroy(the_idl); | |
2898 | exit(status); | |
2899 | } | |
2900 | ||
2901 | static const struct ctl_command_syntax nbctl_commands[] = { | |
fa183acc | 2902 | { "init", 0, 0, "", NULL, nbctl_init, NULL, "", RW }, |
de32cec7 | 2903 | { "sync", 0, 0, "", nbctl_pre_sync, nbctl_sync, NULL, "", RO }, |
ea46a4e9 | 2904 | { "show", 0, 1, "[SWITCH]", NULL, nbctl_show, NULL, "", RO }, |
70799ae8 | 2905 | |
ea46a4e9 JP |
2906 | /* logical switch commands. */ |
2907 | { "ls-add", 0, 1, "[SWITCH]", NULL, nbctl_ls_add, NULL, | |
2908 | "--may-exist,--add-duplicate", RW }, | |
2909 | { "ls-del", 1, 1, "SWITCH", NULL, nbctl_ls_del, NULL, "--if-exists", RW }, | |
2910 | { "ls-list", 0, 0, "", NULL, nbctl_ls_list, NULL, "", RO }, | |
70799ae8 BP |
2911 | |
2912 | /* acl commands. */ | |
ea46a4e9 | 2913 | { "acl-add", 5, 5, "SWITCH DIRECTION PRIORITY MATCH ACTION", NULL, |
70799ae8 | 2914 | nbctl_acl_add, NULL, "--log", RW }, |
ea46a4e9 | 2915 | { "acl-del", 1, 4, "SWITCH [DIRECTION [PRIORITY MATCH]]", NULL, |
70799ae8 | 2916 | nbctl_acl_del, NULL, "", RW }, |
ea46a4e9 | 2917 | { "acl-list", 1, 1, "SWITCH", NULL, nbctl_acl_list, NULL, "", RO }, |
70799ae8 | 2918 | |
80f408f4 | 2919 | /* logical switch port commands. */ |
ea46a4e9 | 2920 | { "lsp-add", 2, 4, "SWITCH PORT [PARENT] [TAG]", NULL, nbctl_lsp_add, |
d24d3372 | 2921 | NULL, "--may-exist", RW }, |
31ed1192 | 2922 | { "lsp-del", 1, 1, "PORT", NULL, nbctl_lsp_del, NULL, "--if-exists", RW }, |
ea46a4e9 | 2923 | { "lsp-list", 1, 1, "SWITCH", NULL, nbctl_lsp_list, NULL, "", RO }, |
31ed1192 | 2924 | { "lsp-get-parent", 1, 1, "PORT", NULL, nbctl_lsp_get_parent, NULL, |
70799ae8 | 2925 | "", RO }, |
31ed1192 JP |
2926 | { "lsp-get-tag", 1, 1, "PORT", NULL, nbctl_lsp_get_tag, NULL, "", RO }, |
2927 | { "lsp-set-addresses", 1, INT_MAX, "PORT [ADDRESS]...", NULL, | |
80f408f4 | 2928 | nbctl_lsp_set_addresses, NULL, "", RW }, |
31ed1192 | 2929 | { "lsp-get-addresses", 1, 1, "PORT", NULL, nbctl_lsp_get_addresses, NULL, |
70799ae8 | 2930 | "", RO }, |
31ed1192 | 2931 | { "lsp-set-port-security", 0, INT_MAX, "PORT [ADDRS]...", NULL, |
80f408f4 | 2932 | nbctl_lsp_set_port_security, NULL, "", RW }, |
31ed1192 | 2933 | { "lsp-get-port-security", 1, 1, "PORT", NULL, |
80f408f4 | 2934 | nbctl_lsp_get_port_security, NULL, "", RO }, |
31ed1192 JP |
2935 | { "lsp-get-up", 1, 1, "PORT", NULL, nbctl_lsp_get_up, NULL, "", RO }, |
2936 | { "lsp-set-enabled", 2, 2, "PORT STATE", NULL, nbctl_lsp_set_enabled, | |
70799ae8 | 2937 | NULL, "", RW }, |
31ed1192 | 2938 | { "lsp-get-enabled", 1, 1, "PORT", NULL, nbctl_lsp_get_enabled, NULL, |
70799ae8 | 2939 | "", RO }, |
31ed1192 | 2940 | { "lsp-set-type", 2, 2, "PORT TYPE", NULL, nbctl_lsp_set_type, NULL, |
70799ae8 | 2941 | "", RW }, |
31ed1192 JP |
2942 | { "lsp-get-type", 1, 1, "PORT", NULL, nbctl_lsp_get_type, NULL, "", RO }, |
2943 | { "lsp-set-options", 1, INT_MAX, "PORT KEY=VALUE [KEY=VALUE]...", NULL, | |
80f408f4 | 2944 | nbctl_lsp_set_options, NULL, "", RW }, |
31ed1192 | 2945 | { "lsp-get-options", 1, 1, "PORT", NULL, nbctl_lsp_get_options, NULL, |
70799ae8 | 2946 | "", RO }, |
281977f7 NS |
2947 | { "lsp-set-dhcpv4-options", 1, 2, "PORT [DHCP_OPT_UUID]", NULL, |
2948 | nbctl_lsp_set_dhcpv4_options, NULL, "", RW }, | |
2949 | { "lsp-get-dhcpv4-options", 1, 1, "PORT", NULL, | |
2950 | nbctl_lsp_get_dhcpv4_options, NULL, "", RO }, | |
70799ae8 | 2951 | |
fa2a27b2 JP |
2952 | /* logical router commands. */ |
2953 | { "lr-add", 0, 1, "[ROUTER]", NULL, nbctl_lr_add, NULL, | |
2954 | "--may-exist,--add-duplicate", RW }, | |
2955 | { "lr-del", 1, 1, "ROUTER", NULL, nbctl_lr_del, NULL, "--if-exists", RW }, | |
2956 | { "lr-list", 0, 0, "", NULL, nbctl_lr_list, NULL, "", RO }, | |
2957 | ||
31114af7 | 2958 | /* logical router port commands. */ |
4685e523 JP |
2959 | { "lrp-add", 4, INT_MAX, |
2960 | "ROUTER PORT MAC NETWORK... [COLUMN[:KEY]=VALUE]...", | |
2961 | NULL, nbctl_lrp_add, NULL, "--may-exist", RW }, | |
2962 | { "lrp-del", 1, 1, "PORT", NULL, nbctl_lrp_del, NULL, "--if-exists", RW }, | |
31114af7 JP |
2963 | { "lrp-list", 1, 1, "ROUTER", NULL, nbctl_lrp_list, NULL, "", RO }, |
2964 | { "lrp-set-enabled", 2, 2, "PORT STATE", NULL, nbctl_lrp_set_enabled, | |
2965 | NULL, "", RW }, | |
2966 | { "lrp-get-enabled", 1, 1, "PORT", NULL, nbctl_lrp_get_enabled, | |
2967 | NULL, "", RO }, | |
2968 | ||
e48ccf3c JP |
2969 | /* logical router route commands. */ |
2970 | { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL, | |
440a9f4b | 2971 | nbctl_lr_route_add, NULL, "--may-exist,--policy=", RW }, |
e48ccf3c JP |
2972 | { "lr-route-del", 1, 2, "ROUTER [PREFIX]", NULL, nbctl_lr_route_del, |
2973 | NULL, "--if-exists", RW }, | |
2974 | { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL, | |
2975 | "", RO }, | |
2976 | ||
e2bfcad6 | 2977 | /* load balancer commands. */ |
2978 | { "lb-add", 3, 4, "LB VIP[:PORT] IP[:PORT]... [PROTOCOL]", NULL, | |
2979 | nbctl_lb_add, NULL, "--may-exist,--add-duplicate", RW }, | |
2980 | { "lb-del", 1, 2, "LB [VIP]", NULL, nbctl_lb_del, NULL, | |
2981 | "--if-exists", RW }, | |
2982 | { "lb-list", 0, 1, "[LB]", NULL, nbctl_lb_list, NULL, "", RO }, | |
2983 | { "lr-lb-add", 2, 2, "ROUTER LB", NULL, nbctl_lr_lb_add, NULL, | |
2984 | "--may-exist", RW }, | |
2985 | { "lr-lb-del", 1, 2, "ROUTER [LB]", NULL, nbctl_lr_lb_del, NULL, | |
2986 | "--if-exists", RW }, | |
2987 | { "lr-lb-list", 1, 1, "ROUTER", NULL, nbctl_lr_lb_list, NULL, | |
2988 | "", RO }, | |
2989 | { "ls-lb-add", 2, 2, "SWITCH LB", NULL, nbctl_ls_lb_add, NULL, | |
2990 | "--may-exist", RW }, | |
2991 | { "ls-lb-del", 1, 2, "SWITCH [LB]", NULL, nbctl_ls_lb_del, NULL, | |
2992 | "--if-exists", RW }, | |
2993 | { "ls-lb-list", 1, 1, "SWITCH", NULL, nbctl_ls_lb_list, NULL, | |
2994 | "", RO }, | |
2995 | ||
281977f7 NS |
2996 | /* DHCP_Options commands */ |
2997 | {"dhcp-options-create", 1, INT_MAX, "CIDR [EXTERNAL:IDS]", NULL, | |
2998 | nbctl_dhcp_options_create, NULL, "", RW }, | |
2999 | {"dhcp-options-del", 1, 1, "DHCP_OPT_UUID", NULL, | |
3000 | nbctl_dhcp_options_del, NULL, "", RW}, | |
3001 | {"dhcp-options-list", 0, 0, "", NULL, nbctl_dhcp_options_list, NULL, "", RO}, | |
3002 | {"dhcp-options-set-options", 1, INT_MAX, "DHCP_OPT_UUID KEY=VALUE [KEY=VALUE]...", | |
3003 | NULL, nbctl_dhcp_options_set_options, NULL, "", RW }, | |
3004 | {"dhcp-options-get-options", 1, 1, "DHCP_OPT_UUID", NULL, | |
3005 | nbctl_dhcp_options_get_options, NULL, "", RO }, | |
3006 | ||
70799ae8 BP |
3007 | {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO}, |
3008 | }; | |
a416ff28 | 3009 | |
70799ae8 BP |
3010 | /* Registers nbctl and common db commands. */ |
3011 | static void | |
3012 | nbctl_cmd_init(void) | |
3013 | { | |
3014 | ctl_init(tables, NULL, nbctl_exit); | |
3015 | ctl_register_commands(nbctl_commands); | |
a416ff28 | 3016 | } |