2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

ovsdb-client: Make "monitor" command able to monitor all tables.

Users may find this feature useful, but the reason to add this feature is
to allow a test to use it in an upcoming commit.

Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Ben Pfaff 2013-03-27 14:26:21 -07:00
parent b1a6083a2d
commit 4227b22182
3 changed files with 225 additions and 65 deletions

3
NEWS
View File

@ -51,6 +51,9 @@ Post-v2.0.0
incorrectly that ovs-controller was a necessary or desirable part
of an Open vSwitch deployment.
- Added vlog option to export to a UDP syslog sink.
- ovsdb-client:
* The "monitor" command can now monitor all tables in a database,
instead of being limited to a single table.
v2.0.0 - 15 Oct 2013

View File

@ -30,6 +30,8 @@ ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
\fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR
[\fIcolumn\fR[\fB,\fIcolumn\fR]...]...
.br
\fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR
.br
\fBovsdb\-client help\fR
.IP "Output formatting options:"
[\fB\-\-format=\fIformat\fR]
@ -128,6 +130,14 @@ line.
If \fB\-\-detach\fR is used with \fBmonitor\fR, then \fBovsdb\-client\fR
detaches after it has successfully received and printed the initial
contents of \fItable\fR.
.
.IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR"
Connects to \fIserver\fR and monitors the contents of all tables in
\fIdatabase\fR. Prints initial values and all kinds of changes to all
columns in the database. The \fB\-\-detach\fR option causes
\fBovsdb\-client\fR to detach after it successfully receives and
prints the initial database contents.
.
.SH OPTIONS
.SS "Output Formatting Options"
Much of the output from \fBovsdb\-client\fR is in the form of tables.

View File

@ -37,12 +37,14 @@
#include "ovsdb.h"
#include "ovsdb-data.h"
#include "ovsdb-error.h"
#include "poll-loop.h"
#include "sort.h"
#include "svec.h"
#include "stream.h"
#include "stream-ssl.h"
#include "table.h"
#include "timeval.h"
#include "unixctl.h"
#include "util.h"
#include "vlog.h"
@ -253,6 +255,9 @@ usage(void)
" monitor contents of COLUMNs in TABLE in DATABASE on SERVER.\n"
" COLUMNs may include !initial, !insert, !delete, !modify\n"
" to avoid seeing the specified kinds of changes.\n"
"\n monitor [SERVER] [DATABASE] ALL\n"
" monitor all changes to all columns in all tables\n"
" in DATBASE on SERVER.\n"
"\n dump [SERVER] [DATABASE]\n"
" dump contents of DATABASE on SERVER to stdout\n"
"\nThe default SERVER is unix:%s/db.sock.\n"
@ -503,6 +508,13 @@ do_transact(struct jsonrpc *rpc, const char *database OVS_UNUSED,
putchar('\n');
jsonrpc_msg_destroy(reply);
}
/* "monitor" command. */
struct monitored_table {
struct ovsdb_table_schema *table;
struct ovsdb_column_set columns;
};
static void
monitor_print_row(struct json *row, const char *type, const char *uuid,
@ -533,30 +545,24 @@ monitor_print_row(struct json *row, const char *type, const char *uuid,
}
static void
monitor_print(struct json *table_updates,
const struct ovsdb_table_schema *table,
const struct ovsdb_column_set *columns, bool initial)
monitor_print_table(struct json *table_update,
const struct monitored_table *mt, char *caption,
bool initial)
{
struct json *table_update;
const struct ovsdb_table_schema *table = mt->table;
const struct ovsdb_column_set *columns = &mt->columns;
struct shash_node *node;
struct table t;
size_t i;
if (table_update->type != JSON_OBJECT) {
ovs_error(0, "<table-update> for table %s is not object", table->name);
return;
}
table_init(&t);
table_set_timestamp(&t, timestamp);
if (table_updates->type != JSON_OBJECT) {
ovs_error(0, "<table-updates> is not object");
return;
}
table_update = shash_find_data(json_object(table_updates), table->name);
if (!table_update) {
return;
}
if (table_update->type != JSON_OBJECT) {
ovs_error(0, "<table-update> is not object");
return;
}
table_set_caption(&t, caption);
table_add_column(&t, "row");
table_add_column(&t, "action");
@ -588,6 +594,30 @@ monitor_print(struct json *table_updates,
table_destroy(&t);
}
static void
monitor_print(struct json *table_updates,
const struct monitored_table *mts, size_t n_mts,
bool initial)
{
size_t i;
if (table_updates->type != JSON_OBJECT) {
ovs_error(0, "<table-updates> is not object");
return;
}
for (i = 0; i < n_mts; i++) {
const struct monitored_table *mt = &mts[i];
struct json *table_update = shash_find_data(json_object(table_updates),
mt->table->name);
if (table_update) {
monitor_print_table(table_update, mt,
n_mts > 1 ? xstrdup(mt->table->name) : NULL,
initial);
}
}
}
static void
add_column(const char *server, const struct ovsdb_column *column,
struct ovsdb_column_set *columns, struct json *columns_json)
@ -653,7 +683,7 @@ parse_monitor_columns(char *arg, const char *server, const char *database,
}
free(nodes);
add_column(server, ovsdb_table_schema_get_column(table,"_version"),
add_column(server, ovsdb_table_schema_get_column(table, "_version"),
columns, columns_json);
}
@ -670,24 +700,59 @@ parse_monitor_columns(char *arg, const char *server, const char *database,
}
static void
do_monitor(struct jsonrpc *rpc, const char *database,
int argc, char *argv[])
ovsdb_client_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
const char *argv[] OVS_UNUSED, void *exiting_)
{
const char *server = jsonrpc_get_name(rpc);
const char *table_name = argv[0];
struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
struct ovsdb_table_schema *table;
struct ovsdb_schema *schema;
struct jsonrpc_msg *request;
struct json *monitor, *monitor_request_array,
*monitor_requests, *request_id;
bool *exiting = exiting_;
*exiting = true;
unixctl_command_reply(conn, NULL);
}
schema = fetch_schema(rpc, database);
table = shash_find_data(&schema->tables, table_name);
if (!table) {
ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
server, database, table_name);
static void
ovsdb_client_block(struct unixctl_conn *conn, int argc OVS_UNUSED,
const char *argv[] OVS_UNUSED, void *blocked_)
{
bool *blocked = blocked_;
if (!*blocked) {
*blocked = true;
unixctl_command_reply(conn, NULL);
} else {
unixctl_command_reply(conn, "already blocking");
}
}
static void
ovsdb_client_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED,
const char *argv[] OVS_UNUSED, void *blocked_)
{
bool *blocked = blocked_;
if (*blocked) {
*blocked = false;
unixctl_command_reply(conn, NULL);
} else {
unixctl_command_reply(conn, "already unblocked");
}
}
static void
add_monitored_table(int argc, char *argv[],
const char *server, const char *database,
struct ovsdb_table_schema *table,
struct json *monitor_requests,
struct monitored_table **mts,
size_t *n_mts, size_t *allocated_mts)
{
struct json *monitor_request_array;
struct monitored_table *mt;
if (*n_mts >= *allocated_mts) {
*mts = x2nrealloc(*mts, allocated_mts, sizeof **mts);
}
mt = &(*mts)[(*n_mts)++];
mt->table = table;
ovsdb_column_set_init(&mt->columns);
monitor_request_array = json_array_create_empty();
if (argc > 1) {
@ -697,58 +762,140 @@ do_monitor(struct jsonrpc *rpc, const char *database,
json_array_add(
monitor_request_array,
parse_monitor_columns(argv[i], server, database, table,
&columns));
&mt->columns));
}
} else {
/* Allocate a writable empty string since parse_monitor_columns() is
* going to strtok() it and that's risky with literal "". */
/* Allocate a writable empty string since parse_monitor_columns()
* is going to strtok() it and that's risky with literal "". */
char empty[] = "";
json_array_add(
monitor_request_array,
parse_monitor_columns(empty, server, database, table, &columns));
parse_monitor_columns(empty, server, database,
table, &mt->columns));
}
json_object_put(monitor_requests, table->name, monitor_request_array);
}
static void
do_monitor(struct jsonrpc *rpc, const char *database,
int argc, char *argv[])
{
const char *server = jsonrpc_get_name(rpc);
const char *table_name = argv[0];
struct unixctl_server *unixctl;
struct ovsdb_schema *schema;
struct jsonrpc_msg *request;
struct json *monitor, *monitor_requests, *request_id;
bool exiting = false;
bool blocked = false;
struct monitored_table *mts;
size_t n_mts, allocated_mts;
daemon_save_fd(STDOUT_FILENO);
daemonize_start();
if (get_detach()) {
int error;
error = unixctl_server_create(NULL, &unixctl);
if (error) {
ovs_fatal(error, "failed to create unixctl server");
}
unixctl_command_register("exit", "", 0, 0,
ovsdb_client_exit, &exiting);
unixctl_command_register("ovsdb-client/block", "", 0, 0,
ovsdb_client_block, &blocked);
unixctl_command_register("ovsdb-client/unblock", "", 0, 0,
ovsdb_client_unblock, &blocked);
} else {
unixctl = NULL;
}
schema = fetch_schema(rpc, database);
monitor_requests = json_object_create();
json_object_put(monitor_requests, table_name, monitor_request_array);
mts = NULL;
n_mts = allocated_mts = 0;
if (strcmp(table_name, "ALL")) {
struct ovsdb_table_schema *table;
table = shash_find_data(&schema->tables, table_name);
if (!table) {
ovs_fatal(0, "%s: %s does not have a table named \"%s\"",
server, database, table_name);
}
add_monitored_table(argc, argv, server, database, table,
monitor_requests, &mts, &n_mts, &allocated_mts);
} else {
size_t n = shash_count(&schema->tables);
const struct shash_node **nodes = shash_sort(&schema->tables);
size_t i;
for (i = 0; i < n; i++) {
struct ovsdb_table_schema *table = nodes[i]->data;
add_monitored_table(argc, argv, server, database, table,
monitor_requests,
&mts, &n_mts, &allocated_mts);
}
free(nodes);
}
monitor = json_array_create_3(json_string_create(database),
json_null_create(), monitor_requests);
request = jsonrpc_create_request("monitor", monitor, NULL);
request_id = json_clone(request->id);
jsonrpc_send(rpc, request);
for (;;) {
struct jsonrpc_msg *msg;
int error;
unixctl_server_run(unixctl);
while (!blocked) {
struct jsonrpc_msg *msg;
int error;
error = jsonrpc_recv_block(rpc, &msg);
if (error) {
ovsdb_schema_destroy(schema);
ovs_fatal(error, "%s: receive failed", server);
}
if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
msg->id));
} else if (msg->type == JSONRPC_REPLY
&& json_equal(msg->id, request_id)) {
monitor_print(msg->result, table, &columns, true);
fflush(stdout);
if (get_detach()) {
daemon_save_fd(STDOUT_FILENO);
daemonize();
error = jsonrpc_recv(rpc, &msg);
if (error == EAGAIN) {
break;
} else if (error) {
ovs_fatal(error, "%s: receive failed", server);
}
} else if (msg->type == JSONRPC_NOTIFY
&& !strcmp(msg->method, "update")) {
struct json *params = msg->params;
if (params->type == JSON_ARRAY
&& params->u.array.n == 2
&& params->u.array.elems[0]->type == JSON_NULL) {
monitor_print(params->u.array.elems[1],
table, &columns, false);
if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) {
jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params),
msg->id));
} else if (msg->type == JSONRPC_REPLY
&& json_equal(msg->id, request_id)) {
monitor_print(msg->result, mts, n_mts, true);
fflush(stdout);
daemonize_complete();
} else if (msg->type == JSONRPC_NOTIFY
&& !strcmp(msg->method, "update")) {
struct json *params = msg->params;
if (params->type == JSON_ARRAY
&& params->u.array.n == 2
&& params->u.array.elems[0]->type == JSON_NULL) {
monitor_print(params->u.array.elems[1], mts, n_mts, false);
fflush(stdout);
}
}
jsonrpc_msg_destroy(msg);
}
jsonrpc_msg_destroy(msg);
if (exiting) {
break;
}
jsonrpc_run(rpc);
jsonrpc_wait(rpc);
if (!blocked) {
jsonrpc_recv_wait(rpc);
}
unixctl_server_wait(unixctl);
poll_block();
}
}