]>
Commit | Line | Data |
---|---|---|
0b1fae1b | 1 | /* Copyright (c) 2009, 2010 Nicira Networks |
f85f8ebb BP |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | ||
18 | #include "ovsdb.h" | |
19 | ||
20 | #include <errno.h> | |
21 | #include <getopt.h> | |
22 | #include <signal.h> | |
eb077b26 | 23 | #include <unistd.h> |
f85f8ebb | 24 | |
0b1fae1b | 25 | #include "column.h" |
f85f8ebb BP |
26 | #include "command-line.h" |
27 | #include "daemon.h" | |
bd06962a | 28 | #include "file.h" |
f85f8ebb BP |
29 | #include "json.h" |
30 | #include "jsonrpc.h" | |
31 | #include "jsonrpc-server.h" | |
32 | #include "leak-checker.h" | |
33 | #include "list.h" | |
0b1fae1b BP |
34 | #include "ovsdb-data.h" |
35 | #include "ovsdb-types.h" | |
f85f8ebb BP |
36 | #include "ovsdb-error.h" |
37 | #include "poll-loop.h" | |
38 | #include "process.h" | |
0b1fae1b | 39 | #include "row.h" |
9467fe62 | 40 | #include "stream-ssl.h" |
f85f8ebb | 41 | #include "stream.h" |
cc01d0bb | 42 | #include "stress.h" |
f85f8ebb | 43 | #include "svec.h" |
0b1fae1b | 44 | #include "table.h" |
f85f8ebb | 45 | #include "timeval.h" |
0b3e7a8b | 46 | #include "transaction.h" |
f85f8ebb BP |
47 | #include "trigger.h" |
48 | #include "util.h" | |
49 | #include "unixctl.h" | |
f85f8ebb | 50 | #include "vlog.h" |
5136ce49 | 51 | |
d98e6007 | 52 | VLOG_DEFINE_THIS_MODULE(ovsdb_server); |
f85f8ebb | 53 | |
973eab32 | 54 | #if HAVE_OPENSSL |
78876719 BP |
55 | /* SSL configuration. */ |
56 | static char *private_key_file; | |
57 | static char *certificate_file; | |
58 | static char *ca_cert_file; | |
59 | static bool bootstrap_ca_cert; | |
973eab32 | 60 | #endif |
78876719 | 61 | |
aa78de9d | 62 | static unixctl_cb_func ovsdb_server_exit; |
ada496b5 | 63 | static unixctl_cb_func ovsdb_server_compact; |
31d0b6c9 | 64 | static unixctl_cb_func ovsdb_server_reconnect; |
aa78de9d | 65 | |
f85f8ebb | 66 | static void parse_options(int argc, char *argv[], char **file_namep, |
475afa1b BP |
67 | struct shash *remotes, char **unixctl_pathp, |
68 | char **run_command); | |
f85f8ebb BP |
69 | static void usage(void) NO_RETURN; |
70 | ||
78876719 BP |
71 | static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, |
72 | const struct ovsdb *db, struct shash *remotes); | |
0b1fae1b | 73 | |
0b3e7a8b AE |
74 | static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, |
75 | const struct shash *remotes, | |
76 | struct ovsdb *db); | |
77 | ||
f85f8ebb BP |
78 | int |
79 | main(int argc, char *argv[]) | |
80 | { | |
aa78de9d | 81 | char *unixctl_path = NULL; |
475afa1b | 82 | char *run_command = NULL; |
f85f8ebb BP |
83 | struct unixctl_server *unixctl; |
84 | struct ovsdb_jsonrpc_server *jsonrpc; | |
0b1fae1b | 85 | struct shash remotes; |
f85f8ebb | 86 | struct ovsdb_error *error; |
ada496b5 | 87 | struct ovsdb_file *file; |
f85f8ebb | 88 | struct ovsdb *db; |
475afa1b | 89 | struct process *run_process; |
f85f8ebb | 90 | char *file_name; |
aa78de9d | 91 | bool exiting; |
f85f8ebb | 92 | int retval; |
0b3e7a8b | 93 | long long int status_timer = LLONG_MIN; |
f85f8ebb | 94 | |
40f0707c | 95 | proctitle_init(argc, argv); |
f85f8ebb | 96 | set_program_name(argv[0]); |
cc01d0bb | 97 | stress_init_command(); |
f85f8ebb BP |
98 | signal(SIGPIPE, SIG_IGN); |
99 | process_init(); | |
100 | ||
475afa1b BP |
101 | parse_options(argc, argv, &file_name, &remotes, &unixctl_path, |
102 | &run_command); | |
f85f8ebb | 103 | |
eb077b26 | 104 | die_if_already_running(); |
95440284 | 105 | daemonize_start(); |
eb077b26 | 106 | |
ada496b5 | 107 | error = ovsdb_file_open(file_name, false, &db, &file); |
f85f8ebb BP |
108 | if (error) { |
109 | ovs_fatal(0, "%s", ovsdb_error_to_string(error)); | |
110 | } | |
111 | ||
b93d3b6c | 112 | jsonrpc = ovsdb_jsonrpc_server_create(db); |
78876719 | 113 | reconfigure_from_db(jsonrpc, db, &remotes); |
f85f8ebb | 114 | |
aa78de9d | 115 | retval = unixctl_server_create(unixctl_path, &unixctl); |
f85f8ebb | 116 | if (retval) { |
4d12270a | 117 | exit(EXIT_FAILURE); |
f85f8ebb BP |
118 | } |
119 | ||
475afa1b BP |
120 | if (run_command) { |
121 | char *run_argv[4]; | |
122 | ||
123 | run_argv[0] = "/bin/sh"; | |
124 | run_argv[1] = "-c"; | |
125 | run_argv[2] = run_command; | |
126 | run_argv[3] = NULL; | |
127 | ||
128 | retval = process_start(run_argv, NULL, 0, NULL, 0, &run_process); | |
129 | if (retval) { | |
130 | ovs_fatal(retval, "%s: process failed to start", run_command); | |
131 | } | |
132 | } else { | |
133 | run_process = NULL; | |
134 | } | |
135 | ||
95440284 | 136 | daemonize_complete(); |
aa78de9d | 137 | |
95440284 | 138 | unixctl_command_register("exit", ovsdb_server_exit, &exiting); |
ada496b5 BP |
139 | unixctl_command_register("ovsdb-server/compact", ovsdb_server_compact, |
140 | file); | |
31d0b6c9 BP |
141 | unixctl_command_register("ovsdb-server/reconnect", ovsdb_server_reconnect, |
142 | jsonrpc); | |
aa78de9d BP |
143 | |
144 | exiting = false; | |
145 | while (!exiting) { | |
78876719 | 146 | reconfigure_from_db(jsonrpc, db, &remotes); |
f85f8ebb BP |
147 | ovsdb_jsonrpc_server_run(jsonrpc); |
148 | unixctl_server_run(unixctl); | |
149 | ovsdb_trigger_run(db, time_msec()); | |
475afa1b BP |
150 | if (run_process && process_exited(run_process)) { |
151 | exiting = true; | |
152 | } | |
f85f8ebb | 153 | |
0b3e7a8b AE |
154 | /* update Manager status(es) every 5 seconds */ |
155 | if (time_msec() >= status_timer) { | |
156 | status_timer = time_msec() + 5000; | |
157 | update_remote_status(jsonrpc, &remotes, db); | |
158 | } | |
159 | ||
f85f8ebb BP |
160 | ovsdb_jsonrpc_server_wait(jsonrpc); |
161 | unixctl_server_wait(unixctl); | |
162 | ovsdb_trigger_wait(db, time_msec()); | |
475afa1b BP |
163 | if (run_process) { |
164 | process_wait(run_process); | |
165 | } | |
6d37aaf1 BP |
166 | if (exiting) { |
167 | poll_immediate_wake(); | |
168 | } | |
0b3e7a8b | 169 | poll_timer_wait_until(status_timer); |
f85f8ebb BP |
170 | poll_block(); |
171 | } | |
23935e8b BP |
172 | ovsdb_jsonrpc_server_destroy(jsonrpc); |
173 | ovsdb_destroy(db); | |
174 | shash_destroy(&remotes); | |
175 | unixctl_server_destroy(unixctl); | |
f85f8ebb | 176 | |
475afa1b BP |
177 | if (run_process && process_exited(run_process)) { |
178 | int status = process_status(run_process); | |
179 | if (status) { | |
180 | ovs_fatal(0, "%s: child exited, %s", | |
181 | run_command, process_status_msg(status)); | |
182 | } | |
183 | } | |
184 | ||
f85f8ebb BP |
185 | return 0; |
186 | } | |
187 | ||
0b1fae1b | 188 | static void |
94db5407 BP |
189 | parse_db_column(const struct ovsdb *db, |
190 | const char *name_, | |
191 | const struct ovsdb_table **tablep, | |
192 | const struct ovsdb_column **columnp) | |
0b1fae1b | 193 | { |
b0ef0551 | 194 | char *name, *table_name, *column_name; |
0b1fae1b BP |
195 | const struct ovsdb_column *column; |
196 | const struct ovsdb_table *table; | |
0b1fae1b BP |
197 | char *save_ptr = NULL; |
198 | ||
199 | name = xstrdup(name_); | |
b0ef0551 | 200 | strtok_r(name, ":", &save_ptr); /* "db:" */ |
0b1fae1b BP |
201 | table_name = strtok_r(NULL, ",", &save_ptr); |
202 | column_name = strtok_r(NULL, ",", &save_ptr); | |
203 | if (!table_name || !column_name) { | |
78876719 | 204 | ovs_fatal(0, "\"%s\": invalid syntax", name_); |
0b1fae1b BP |
205 | } |
206 | ||
207 | table = ovsdb_get_table(db, table_name); | |
208 | if (!table) { | |
78876719 | 209 | ovs_fatal(0, "\"%s\": no table named %s", name_, table_name); |
0b1fae1b BP |
210 | } |
211 | ||
212 | column = ovsdb_table_schema_get_column(table->schema, column_name); | |
213 | if (!column) { | |
78876719 | 214 | ovs_fatal(0, "\"%s\": table \"%s\" has no column \"%s\"", |
0b1fae1b BP |
215 | name_, table_name, column_name); |
216 | } | |
78876719 | 217 | free(name); |
0b1fae1b | 218 | |
94db5407 BP |
219 | *columnp = column; |
220 | *tablep = table; | |
221 | } | |
222 | ||
223 | static void | |
224 | parse_db_string_column(const struct ovsdb *db, | |
225 | const char *name, | |
226 | const struct ovsdb_table **tablep, | |
227 | const struct ovsdb_column **columnp) | |
228 | { | |
229 | const struct ovsdb_column *column; | |
230 | const struct ovsdb_table *table; | |
231 | ||
232 | parse_db_column(db, name, &table, &column); | |
233 | ||
bd76d25d BP |
234 | if (column->type.key.type != OVSDB_TYPE_STRING |
235 | || column->type.value.type != OVSDB_TYPE_VOID) { | |
78876719 | 236 | ovs_fatal(0, "\"%s\": table \"%s\" column \"%s\" is " |
0b1fae1b | 237 | "not string or set of strings", |
94db5407 | 238 | name, table->schema->name, column->name); |
78876719 BP |
239 | } |
240 | ||
241 | *columnp = column; | |
242 | *tablep = table; | |
243 | } | |
244 | ||
973eab32 | 245 | #if HAVE_OPENSSL |
78876719 BP |
246 | static const char * |
247 | query_db_string(const struct ovsdb *db, const char *name) | |
248 | { | |
249 | if (!name || strncmp(name, "db:", 3)) { | |
250 | return name; | |
251 | } else { | |
252 | const struct ovsdb_column *column; | |
253 | const struct ovsdb_table *table; | |
254 | const struct ovsdb_row *row; | |
255 | ||
256 | parse_db_string_column(db, name, &table, &column); | |
257 | ||
4e8e4213 | 258 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { |
78876719 BP |
259 | const struct ovsdb_datum *datum; |
260 | size_t i; | |
261 | ||
262 | datum = &row->fields[column->index]; | |
263 | for (i = 0; i < datum->n; i++) { | |
264 | if (datum->keys[i].string[0]) { | |
265 | return datum->keys[i].string; | |
266 | } | |
267 | } | |
268 | } | |
269 | return NULL; | |
0b1fae1b | 270 | } |
78876719 | 271 | } |
973eab32 | 272 | #endif /* HAVE_OPENSSL */ |
78876719 | 273 | |
94db5407 BP |
274 | static struct ovsdb_jsonrpc_options * |
275 | add_remote(struct shash *remotes, const char *target) | |
276 | { | |
277 | struct ovsdb_jsonrpc_options *options; | |
278 | ||
279 | options = shash_find_data(remotes, target); | |
280 | if (!options) { | |
281 | options = ovsdb_jsonrpc_default_options(); | |
282 | shash_add(remotes, target, options); | |
283 | } | |
284 | ||
285 | return options; | |
286 | } | |
287 | ||
0b3e7a8b AE |
288 | static struct ovsdb_datum * |
289 | get_datum(struct ovsdb_row *row, const char *column_name, | |
290 | const enum ovsdb_atomic_type key_type, | |
291 | const enum ovsdb_atomic_type value_type, | |
292 | const size_t n_max) | |
94db5407 BP |
293 | { |
294 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
295 | const struct ovsdb_table_schema *schema = row->table->schema; | |
296 | const struct ovsdb_column *column; | |
94db5407 BP |
297 | |
298 | column = ovsdb_table_schema_get_column(schema, column_name); | |
299 | if (!column) { | |
300 | VLOG_DBG_RL(&rl, "Table `%s' has no `%s' column", | |
301 | schema->name, column_name); | |
0b3e7a8b | 302 | return NULL; |
94db5407 BP |
303 | } |
304 | ||
0b3e7a8b AE |
305 | if (column->type.key.type != key_type |
306 | || column->type.value.type != value_type | |
307 | || column->type.n_max != n_max) { | |
94db5407 BP |
308 | if (!VLOG_DROP_DBG(&rl)) { |
309 | char *type_name = ovsdb_type_to_english(&column->type); | |
310 | VLOG_DBG("Table `%s' column `%s' has type %s, not expected " | |
0b3e7a8b AE |
311 | "key type %s, value type %s, max elements %zd.", |
312 | schema->name, column_name, type_name, | |
313 | ovsdb_atomic_type_to_string(key_type), | |
314 | ovsdb_atomic_type_to_string(value_type), | |
315 | n_max); | |
316 | free(type_name); | |
94db5407 | 317 | } |
0b3e7a8b | 318 | return NULL; |
94db5407 BP |
319 | } |
320 | ||
0b3e7a8b AE |
321 | return &row->fields[column->index]; |
322 | } | |
323 | ||
324 | static const union ovsdb_atom * | |
325 | read_column(const struct ovsdb_row *row, const char *column_name, | |
326 | enum ovsdb_atomic_type type) | |
327 | { | |
328 | const struct ovsdb_datum *datum; | |
329 | ||
330 | datum = get_datum((struct ovsdb_row *) row, column_name, type, OVSDB_TYPE_VOID, 1); | |
331 | return datum && datum->n ? datum->keys : NULL; | |
94db5407 BP |
332 | } |
333 | ||
334 | static bool | |
335 | read_integer_column(const struct ovsdb_row *row, const char *column_name, | |
336 | long long int *integerp) | |
337 | { | |
338 | const union ovsdb_atom *atom; | |
339 | ||
340 | atom = read_column(row, column_name, OVSDB_TYPE_INTEGER); | |
341 | *integerp = atom ? atom->integer : 0; | |
342 | return atom != NULL; | |
343 | } | |
344 | ||
345 | static bool | |
346 | read_string_column(const struct ovsdb_row *row, const char *column_name, | |
347 | const char **stringp) | |
348 | { | |
349 | const union ovsdb_atom *atom; | |
350 | ||
351 | atom = read_column(row, column_name, OVSDB_TYPE_STRING); | |
352 | *stringp = atom ? atom->string : 0; | |
353 | return atom != NULL; | |
354 | } | |
355 | ||
0b3e7a8b AE |
356 | static void |
357 | write_bool_column(struct ovsdb_row *row, const char *column_name, bool value) | |
358 | { | |
359 | struct ovsdb_datum *datum = get_datum(row, column_name, OVSDB_TYPE_BOOLEAN, | |
360 | OVSDB_TYPE_VOID, 1); | |
361 | ||
362 | if (!datum) { | |
363 | return; | |
364 | } | |
365 | datum->keys[0].boolean = value; | |
366 | } | |
367 | ||
368 | static void | |
369 | write_string_string_column(struct ovsdb_row *row, const char *column_name, | |
370 | char **keys, char **values, size_t n) | |
371 | { | |
372 | const struct ovsdb_column *column; | |
373 | struct ovsdb_datum *datum; | |
374 | size_t i; | |
375 | ||
376 | column = ovsdb_table_schema_get_column(row->table->schema, column_name); | |
377 | datum = get_datum(row, column_name, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, | |
378 | UINT_MAX); | |
379 | if (!datum) { | |
380 | return; | |
381 | } | |
382 | ||
383 | /* Free existing data. */ | |
384 | ovsdb_datum_destroy(datum, &column->type); | |
385 | ||
386 | /* Allocate space for new values. */ | |
387 | datum->n = n; | |
388 | datum->keys = xmalloc(n * sizeof *datum->keys); | |
389 | datum->values = xmalloc(n * sizeof *datum->values); | |
390 | ||
391 | for (i = 0; i < n; ++i) { | |
392 | datum->keys[i].string = keys[i]; | |
393 | datum->values[i].string = values[i]; | |
394 | } | |
395 | ||
396 | /* Sort and check constraints. */ | |
397 | ovsdb_datum_sort_assert(datum, column->type.key.type); | |
398 | } | |
399 | ||
94db5407 BP |
400 | /* Adds a remote and options to 'remotes', based on the Manager table row in |
401 | * 'row'. */ | |
402 | static void | |
403 | add_manager_options(struct shash *remotes, const struct ovsdb_row *row) | |
404 | { | |
405 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
406 | struct ovsdb_jsonrpc_options *options; | |
407 | long long int max_backoff, probe_interval; | |
408 | const char *target; | |
409 | ||
410 | if (!read_string_column(row, "target", &target) || !target) { | |
411 | VLOG_INFO_RL(&rl, "Table `%s' has missing or invalid `target' column", | |
412 | row->table->schema->name); | |
413 | return; | |
414 | } | |
415 | ||
416 | options = add_remote(remotes, target); | |
417 | if (read_integer_column(row, "max_backoff", &max_backoff)) { | |
418 | options->max_backoff = max_backoff; | |
419 | } | |
f441c1a8 | 420 | if (read_integer_column(row, "inactivity_probe", &probe_interval)) { |
94db5407 BP |
421 | options->probe_interval = probe_interval; |
422 | } | |
423 | } | |
424 | ||
78876719 BP |
425 | static void |
426 | query_db_remotes(const char *name, const struct ovsdb *db, | |
427 | struct shash *remotes) | |
428 | { | |
429 | const struct ovsdb_column *column; | |
430 | const struct ovsdb_table *table; | |
431 | const struct ovsdb_row *row; | |
432 | ||
94db5407 | 433 | parse_db_column(db, name, &table, &column); |
0b1fae1b | 434 | |
94db5407 BP |
435 | if (column->type.key.type == OVSDB_TYPE_STRING |
436 | && column->type.value.type == OVSDB_TYPE_VOID) { | |
437 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { | |
438 | const struct ovsdb_datum *datum; | |
439 | size_t i; | |
440 | ||
441 | datum = &row->fields[column->index]; | |
442 | for (i = 0; i < datum->n; i++) { | |
443 | add_remote(remotes, datum->keys[i].string); | |
444 | } | |
445 | } | |
446 | } else if (column->type.key.type == OVSDB_TYPE_UUID | |
447 | && column->type.key.u.uuid.refTable | |
448 | && column->type.value.type == OVSDB_TYPE_VOID) { | |
449 | const struct ovsdb_table *ref_table = column->type.key.u.uuid.refTable; | |
450 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { | |
451 | const struct ovsdb_datum *datum; | |
452 | size_t i; | |
453 | ||
454 | datum = &row->fields[column->index]; | |
455 | for (i = 0; i < datum->n; i++) { | |
456 | const struct ovsdb_row *ref_row; | |
0b1fae1b | 457 | |
94db5407 BP |
458 | ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid); |
459 | if (ref_row) { | |
460 | add_manager_options(remotes, ref_row); | |
461 | } | |
462 | } | |
0b1fae1b BP |
463 | } |
464 | } | |
465 | } | |
466 | ||
0b3e7a8b AE |
467 | static void |
468 | update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn, | |
469 | const struct shash *statuses) | |
470 | { | |
471 | struct ovsdb_row *rw_row; | |
472 | const char *target; | |
473 | const struct ovsdb_jsonrpc_remote_status *status; | |
474 | char *keys[3], *values[3]; | |
475 | size_t n = 0; | |
476 | ||
477 | /* Get the "target" (protocol/host/port) spec. */ | |
478 | if (!read_string_column(row, "target", &target)) { | |
479 | /* Bad remote spec or incorrect schema. */ | |
480 | return; | |
481 | } | |
482 | ||
483 | /* Prepare to modify this row. */ | |
484 | rw_row = ovsdb_txn_row_modify(txn, row); | |
485 | ||
486 | /* Find status information for this target. */ | |
487 | status = shash_find_data(statuses, target); | |
488 | if (!status) { | |
489 | /* Should never happen, but just in case... */ | |
490 | return; | |
491 | } | |
492 | ||
493 | /* Update status information columns. */ | |
494 | ||
495 | write_bool_column(rw_row, "is_connected", | |
496 | status->is_connected); | |
497 | ||
498 | keys[n] = xstrdup("state"); | |
499 | values[n++] = xstrdup(status->state); | |
500 | keys[n] = xstrdup("time_in_state"); | |
501 | values[n++] = xasprintf("%u", status->state_elapsed); | |
502 | if (status->last_error) { | |
503 | keys[n] = xstrdup("last_error"); | |
504 | values[n++] = | |
505 | xstrdup(ovs_retval_to_string(status->last_error)); | |
506 | } | |
507 | write_string_string_column(rw_row, "status", keys, values, n); | |
508 | } | |
509 | ||
510 | static void | |
511 | update_remote_rows(const struct ovsdb *db, struct ovsdb_txn *txn, | |
512 | const char *remote_name, const struct shash *statuses) | |
513 | { | |
514 | const struct ovsdb_table *table, *ref_table; | |
515 | const struct ovsdb_column *column; | |
516 | const struct ovsdb_row *row; | |
517 | ||
518 | if (strncmp("db:", remote_name, 3)) { | |
519 | return; | |
520 | } | |
521 | ||
522 | parse_db_column(db, remote_name, &table, &column); | |
523 | ||
524 | if (column->type.key.type != OVSDB_TYPE_UUID | |
525 | || !column->type.key.u.uuid.refTable | |
526 | || column->type.value.type != OVSDB_TYPE_VOID) { | |
527 | return; | |
528 | } | |
529 | ||
530 | ref_table = column->type.key.u.uuid.refTable; | |
531 | ||
532 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { | |
533 | const struct ovsdb_datum *datum; | |
534 | size_t i; | |
535 | ||
536 | datum = &row->fields[column->index]; | |
537 | for (i = 0; i < datum->n; i++) { | |
538 | const struct ovsdb_row *ref_row; | |
539 | ||
540 | ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid); | |
541 | if (ref_row) { | |
542 | update_remote_row(ref_row, txn, statuses); | |
543 | } | |
544 | } | |
545 | } | |
546 | } | |
547 | ||
548 | static void | |
549 | update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, | |
550 | const struct shash *remotes, struct ovsdb *db) | |
551 | { | |
552 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
553 | struct shash_node *remote; | |
554 | struct shash statuses; | |
555 | struct ovsdb_txn *txn; | |
556 | const bool durable_txn = false; | |
557 | struct ovsdb_error *error; | |
558 | ||
559 | /* Get status of current connections. */ | |
560 | ovsdb_jsonrpc_server_get_remote_status(jsonrpc, &statuses); | |
561 | ||
562 | txn = ovsdb_txn_create(db); | |
563 | ||
564 | /* Iterate over --remote arguments given on command line. */ | |
565 | SHASH_FOR_EACH (remote, remotes) { | |
566 | update_remote_rows(db, txn, remote->name, &statuses); | |
567 | } | |
568 | ||
569 | error = ovsdb_txn_commit(txn, durable_txn); | |
570 | if (error) { | |
571 | VLOG_ERR_RL(&rl, "Failed to update remote status: %s", | |
572 | ovsdb_error_to_string(error)); | |
573 | } | |
574 | ||
575 | shash_destroy_free_data(&statuses); | |
576 | } | |
577 | ||
78876719 | 578 | /* Reconfigures ovsdb-server based on information in the database. */ |
0b1fae1b | 579 | static void |
78876719 BP |
580 | reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, |
581 | const struct ovsdb *db, struct shash *remotes) | |
0b1fae1b BP |
582 | { |
583 | struct shash resolved_remotes; | |
584 | struct shash_node *node; | |
585 | ||
78876719 | 586 | /* Configure remotes. */ |
0b1fae1b BP |
587 | shash_init(&resolved_remotes); |
588 | SHASH_FOR_EACH (node, remotes) { | |
589 | const char *name = node->name; | |
590 | ||
591 | if (!strncmp(name, "db:", 3)) { | |
592 | query_db_remotes(name, db, &resolved_remotes); | |
593 | } else { | |
94db5407 | 594 | add_remote(&resolved_remotes, name); |
0b1fae1b BP |
595 | } |
596 | } | |
597 | ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes); | |
598 | shash_destroy(&resolved_remotes); | |
0b1fae1b | 599 | |
973eab32 | 600 | #if HAVE_OPENSSL |
78876719 | 601 | /* Configure SSL. */ |
6f1e91b1 BP |
602 | stream_ssl_set_key_and_cert(query_db_string(db, private_key_file), |
603 | query_db_string(db, certificate_file)); | |
78876719 BP |
604 | stream_ssl_set_ca_cert_file(query_db_string(db, ca_cert_file), |
605 | bootstrap_ca_cert); | |
973eab32 | 606 | #endif |
78876719 | 607 | } |
0b1fae1b | 608 | |
aa78de9d | 609 | static void |
c69ee87c | 610 | ovsdb_server_exit(struct unixctl_conn *conn, const char *args OVS_UNUSED, |
aa78de9d BP |
611 | void *exiting_) |
612 | { | |
613 | bool *exiting = exiting_; | |
614 | *exiting = true; | |
615 | unixctl_command_reply(conn, 200, NULL); | |
616 | } | |
617 | ||
ada496b5 BP |
618 | static void |
619 | ovsdb_server_compact(struct unixctl_conn *conn, const char *args OVS_UNUSED, | |
620 | void *file_) | |
621 | { | |
622 | struct ovsdb_file *file = file_; | |
623 | struct ovsdb_error *error; | |
624 | ||
625 | VLOG_INFO("compacting database by user request"); | |
626 | error = ovsdb_file_compact(file); | |
627 | if (!error) { | |
628 | unixctl_command_reply(conn, 200, NULL); | |
629 | } else { | |
630 | char *s = ovsdb_error_to_string(error); | |
631 | ovsdb_error_destroy(error); | |
632 | unixctl_command_reply(conn, 503, s); | |
633 | free(s); | |
634 | } | |
635 | } | |
636 | ||
31d0b6c9 BP |
637 | /* "ovsdb-server/reconnect": makes ovsdb-server drop all of its JSON-RPC |
638 | * connections and reconnect. */ | |
639 | static void | |
640 | ovsdb_server_reconnect(struct unixctl_conn *conn, const char *args OVS_UNUSED, | |
641 | void *jsonrpc_) | |
642 | { | |
643 | struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_; | |
644 | ||
645 | ovsdb_jsonrpc_server_reconnect(jsonrpc); | |
646 | unixctl_command_reply(conn, 200, NULL); | |
647 | } | |
648 | ||
f85f8ebb BP |
649 | static void |
650 | parse_options(int argc, char *argv[], char **file_namep, | |
475afa1b BP |
651 | struct shash *remotes, char **unixctl_pathp, |
652 | char **run_command) | |
f85f8ebb BP |
653 | { |
654 | enum { | |
655 | OPT_DUMMY = UCHAR_MAX + 1, | |
0b1fae1b | 656 | OPT_REMOTE, |
aa78de9d | 657 | OPT_UNIXCTL, |
475afa1b | 658 | OPT_RUN, |
9467fe62 | 659 | OPT_BOOTSTRAP_CA_CERT, |
f85f8ebb BP |
660 | VLOG_OPTION_ENUMS, |
661 | LEAK_CHECKER_OPTION_ENUMS | |
662 | }; | |
663 | static struct option long_options[] = { | |
0b1fae1b | 664 | {"remote", required_argument, 0, OPT_REMOTE}, |
aa78de9d | 665 | {"unixctl", required_argument, 0, OPT_UNIXCTL}, |
475afa1b | 666 | {"run", required_argument, 0, OPT_RUN}, |
f85f8ebb BP |
667 | {"help", no_argument, 0, 'h'}, |
668 | {"version", no_argument, 0, 'V'}, | |
669 | DAEMON_LONG_OPTIONS, | |
670 | VLOG_LONG_OPTIONS, | |
671 | LEAK_CHECKER_LONG_OPTIONS, | |
9467fe62 BP |
672 | #ifdef HAVE_OPENSSL |
673 | {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, | |
78876719 BP |
674 | {"private-key", required_argument, 0, 'p'}, |
675 | {"certificate", required_argument, 0, 'c'}, | |
676 | {"ca-cert", required_argument, 0, 'C'}, | |
9467fe62 | 677 | #endif |
f85f8ebb BP |
678 | {0, 0, 0, 0}, |
679 | }; | |
680 | char *short_options = long_options_to_short_options(long_options); | |
681 | ||
0b1fae1b | 682 | shash_init(remotes); |
f85f8ebb BP |
683 | for (;;) { |
684 | int c; | |
685 | ||
686 | c = getopt_long(argc, argv, short_options, long_options, NULL); | |
687 | if (c == -1) { | |
688 | break; | |
689 | } | |
690 | ||
691 | switch (c) { | |
0b1fae1b BP |
692 | case OPT_REMOTE: |
693 | shash_add_once(remotes, optarg, NULL); | |
f85f8ebb BP |
694 | break; |
695 | ||
aa78de9d BP |
696 | case OPT_UNIXCTL: |
697 | *unixctl_pathp = optarg; | |
698 | break; | |
699 | ||
475afa1b BP |
700 | case OPT_RUN: |
701 | *run_command = optarg; | |
702 | break; | |
703 | ||
f85f8ebb BP |
704 | case 'h': |
705 | usage(); | |
706 | ||
707 | case 'V': | |
708 | OVS_PRINT_VERSION(0, 0); | |
709 | exit(EXIT_SUCCESS); | |
710 | ||
711 | VLOG_OPTION_HANDLERS | |
712 | DAEMON_OPTION_HANDLERS | |
713 | LEAK_CHECKER_OPTION_HANDLERS | |
714 | ||
9467fe62 | 715 | #ifdef HAVE_OPENSSL |
78876719 BP |
716 | case 'p': |
717 | private_key_file = optarg; | |
718 | break; | |
719 | ||
720 | case 'c': | |
721 | certificate_file = optarg; | |
722 | break; | |
723 | ||
724 | case 'C': | |
725 | ca_cert_file = optarg; | |
726 | bootstrap_ca_cert = false; | |
727 | break; | |
9467fe62 BP |
728 | |
729 | case OPT_BOOTSTRAP_CA_CERT: | |
78876719 BP |
730 | ca_cert_file = optarg; |
731 | bootstrap_ca_cert = true; | |
9467fe62 BP |
732 | break; |
733 | #endif | |
734 | ||
f85f8ebb BP |
735 | case '?': |
736 | exit(EXIT_FAILURE); | |
737 | ||
738 | default: | |
739 | abort(); | |
740 | } | |
741 | } | |
742 | free(short_options); | |
743 | ||
744 | argc -= optind; | |
745 | argv += optind; | |
746 | ||
cf3a5d91 | 747 | if (argc > 1) { |
f85f8ebb BP |
748 | ovs_fatal(0, "database file is only non-option argument; " |
749 | "use --help for usage"); | |
cf3a5d91 BP |
750 | } else if (argc < 1) { |
751 | ovs_fatal(0, "missing database file argument; use --help for usage"); | |
f85f8ebb BP |
752 | } |
753 | ||
754 | *file_namep = argv[0]; | |
755 | } | |
756 | ||
757 | static void | |
758 | usage(void) | |
759 | { | |
760 | printf("%s: Open vSwitch database server\n" | |
761 | "usage: %s [OPTIONS] DATABASE\n" | |
762 | "where DATABASE is a database file in ovsdb format.\n", | |
763 | program_name, program_name); | |
764 | printf("\nJSON-RPC options (may be specified any number of times):\n" | |
0b1fae1b | 765 | " --remote=REMOTE connect or listen to REMOTE\n"); |
9467fe62 | 766 | stream_usage("JSON-RPC", true, true, true); |
f85f8ebb BP |
767 | daemon_usage(); |
768 | vlog_usage(); | |
769 | printf("\nOther options:\n" | |
475afa1b | 770 | " --run COMMAND run COMMAND as subprocess then exit\n" |
7b38bdc8 | 771 | " --unixctl=SOCKET override default control socket name\n" |
f85f8ebb BP |
772 | " -h, --help display this help message\n" |
773 | " -V, --version display version information\n"); | |
774 | leak_checker_usage(); | |
775 | exit(EXIT_SUCCESS); | |
776 | } |