From 0b3e7a8b717b8c50c24244f7054079ede61742f2 Mon Sep 17 00:00:00 2001 From: Andrew Evans Date: Fri, 28 Jan 2011 15:39:55 -0800 Subject: [PATCH] ovsdb-server: Write manager status information to Manager table. This commit makes the status of manager connections visible via the Manager table in the database. Two new columns have been created for this purpose: 'is_connected' and 'status'. The former is a boolean flag, and the latter is a string-string map which may contain the keys "last_error", "state", and "time_in_state". Requested-by: Keith Amidon Reviewed by: Ben Pfaff. Feature #3692. --- lib/jsonrpc.c | 15 +++ lib/jsonrpc.h | 5 + ovsdb/jsonrpc-server.c | 55 ++++++++++ ovsdb/jsonrpc-server.h | 13 +++ ovsdb/ovsdb-server.c | 208 ++++++++++++++++++++++++++++++++++--- vswitchd/vswitch.ovsschema | 12 ++- vswitchd/vswitch.xml | 32 ++++++ 7 files changed, 324 insertions(+), 16 deletions(-) diff --git a/lib/jsonrpc.c b/lib/jsonrpc.c index fb20bc585..afcc52082 100644 --- a/lib/jsonrpc.c +++ b/lib/jsonrpc.c @@ -876,6 +876,8 @@ jsonrpc_session_get_backlog(const struct jsonrpc_session *s) return s->rpc ? jsonrpc_get_backlog(s->rpc) : 0; } +/* Always returns a pointer to a valid C string, assuming 's' was initialized + * correctly. */ const char * jsonrpc_session_get_name(const struct jsonrpc_session *s) { @@ -947,6 +949,19 @@ jsonrpc_session_get_seqno(const struct jsonrpc_session *s) return s->seqno; } +int +jsonrpc_session_get_status(const struct jsonrpc_session *s) +{ + return s && s->rpc ? jsonrpc_get_status(s->rpc) : 0; +} + +void +jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *s, + struct reconnect_stats *stats) +{ + reconnect_get_stats(s->reconnect, time_msec(), stats); +} + void jsonrpc_session_force_reconnect(struct jsonrpc_session *s) { diff --git a/lib/jsonrpc.h b/lib/jsonrpc.h index d95148cbe..5100d7474 100644 --- a/lib/jsonrpc.h +++ b/lib/jsonrpc.h @@ -26,6 +26,7 @@ struct json; struct jsonrpc_msg; struct pstream; +struct reconnect_stats; struct stream; /* API for a JSON-RPC stream. */ @@ -113,6 +114,10 @@ void jsonrpc_session_recv_wait(struct jsonrpc_session *); bool jsonrpc_session_is_alive(const struct jsonrpc_session *); bool jsonrpc_session_is_connected(const struct jsonrpc_session *); unsigned int jsonrpc_session_get_seqno(const struct jsonrpc_session *); +int jsonrpc_session_get_status(const struct jsonrpc_session *); +void jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *, + struct reconnect_stats *); + void jsonrpc_session_force_reconnect(struct jsonrpc_session *); void jsonrpc_session_set_max_backoff(struct jsonrpc_session *, diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c index 3edcfffbd..92228be85 100644 --- a/ovsdb/jsonrpc-server.c +++ b/ovsdb/jsonrpc-server.c @@ -53,6 +53,9 @@ static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *); static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *); static void ovsdb_jsonrpc_session_set_all_options( struct ovsdb_jsonrpc_remote *, const struct ovsdb_jsonrpc_options *); +static void ovsdb_jsonrpc_session_get_status( + const struct ovsdb_jsonrpc_remote *, + struct shash *); /* Triggers. */ static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *, @@ -194,6 +197,21 @@ ovsdb_jsonrpc_server_del_remote(struct shash_node *node) free(remote); } +void +ovsdb_jsonrpc_server_get_remote_status(const struct ovsdb_jsonrpc_server *svr, + struct shash *statuses) +{ + struct shash_node *node; + + shash_init(statuses); + + SHASH_FOR_EACH (node, &svr->remotes) { + const struct ovsdb_jsonrpc_remote *remote = node->data; + + ovsdb_jsonrpc_session_get_status(remote, statuses); + } +} + /* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and * reconnect. */ void @@ -420,6 +438,43 @@ ovsdb_jsonrpc_session_set_all_options( } } +static void +ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote, + struct shash *shash) +{ + const struct ovsdb_jsonrpc_session *s; + const struct jsonrpc_session *js; + const char *name; + struct ovsdb_jsonrpc_remote_status *status; + struct reconnect_stats rstats; + + /* We only look at the first session in the list. There should be only one + * node in the list for outbound connections. We don't track status for + * each individual inbound connection if someone configures the DB that + * way. Since outbound connections are the norm, this is fine. */ + if (list_is_empty(&remote->sessions)) { + return; + } + s = CONTAINER_OF(remote->sessions.next, struct ovsdb_jsonrpc_session, node); + js = s->js; + if (!js) { + return; + } + name = jsonrpc_session_get_name(js); + + status = xzalloc(sizeof *status); + shash_add(shash, name, status); + + status->is_connected = jsonrpc_session_is_connected(js); + status->last_error = jsonrpc_session_get_status(js); + + jsonrpc_session_get_reconnect_stats(js, &rstats); + status->state = rstats.state; + status->state_elapsed = rstats.state_elapsed; + + return; +} + static const char * get_db_name(const struct ovsdb_jsonrpc_session *s) { diff --git a/ovsdb/jsonrpc-server.h b/ovsdb/jsonrpc-server.h index 9906e8167..e40487001 100644 --- a/ovsdb/jsonrpc-server.h +++ b/ovsdb/jsonrpc-server.h @@ -16,6 +16,8 @@ #ifndef OVSDB_JSONRPC_SERVER_H #define OVSDB_JSONRPC_SERVER_H 1 +#include + struct ovsdb; struct shash; @@ -32,6 +34,17 @@ struct ovsdb_jsonrpc_options *ovsdb_jsonrpc_default_options(void); void ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *, const struct shash *); +/* Status of a single remote connection. */ +struct ovsdb_jsonrpc_remote_status { + const char *state; + int last_error; + unsigned int state_elapsed; + bool is_connected; +}; +void ovsdb_jsonrpc_server_get_remote_status( + const struct ovsdb_jsonrpc_server *, + struct shash * /* of 'struct ovsdb_jsonrpc_remote_status' */ ); + void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *); void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *); diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c index b4f2e42e2..41f1146cc 100644 --- a/ovsdb/ovsdb-server.c +++ b/ovsdb/ovsdb-server.c @@ -43,6 +43,7 @@ #include "svec.h" #include "table.h" #include "timeval.h" +#include "transaction.h" #include "trigger.h" #include "util.h" #include "unixctl.h" @@ -70,6 +71,10 @@ static void usage(void) NO_RETURN; static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, const struct ovsdb *db, struct shash *remotes); +static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, + const struct shash *remotes, + struct ovsdb *db); + int main(int argc, char *argv[]) { @@ -85,6 +90,7 @@ main(int argc, char *argv[]) char *file_name; bool exiting; int retval; + long long int status_timer = LLONG_MIN; proctitle_init(argc, argv); set_program_name(argv[0]); @@ -145,6 +151,12 @@ main(int argc, char *argv[]) exiting = true; } + /* update Manager status(es) every 5 seconds */ + if (time_msec() >= status_timer) { + status_timer = time_msec() + 5000; + update_remote_status(jsonrpc, &remotes, db); + } + ovsdb_jsonrpc_server_wait(jsonrpc); unixctl_server_wait(unixctl); ovsdb_trigger_wait(db, time_msec()); @@ -154,6 +166,7 @@ main(int argc, char *argv[]) if (exiting) { poll_immediate_wake(); } + poll_timer_wait_until(status_timer); poll_block(); } ovsdb_jsonrpc_server_destroy(jsonrpc); @@ -272,36 +285,50 @@ add_remote(struct shash *remotes, const char *target) return options; } -static const union ovsdb_atom * -read_column(const struct ovsdb_row *row, const char *column_name, - enum ovsdb_atomic_type type) +static struct ovsdb_datum * +get_datum(struct ovsdb_row *row, const char *column_name, + const enum ovsdb_atomic_type key_type, + const enum ovsdb_atomic_type value_type, + const size_t n_max) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); const struct ovsdb_table_schema *schema = row->table->schema; const struct ovsdb_column *column; - const struct ovsdb_datum *datum; column = ovsdb_table_schema_get_column(schema, column_name); if (!column) { VLOG_DBG_RL(&rl, "Table `%s' has no `%s' column", schema->name, column_name); - return false; + return NULL; } - if (column->type.key.type != type - || column->type.value.type != OVSDB_TYPE_VOID - || column->type.n_max != 1) { + if (column->type.key.type != key_type + || column->type.value.type != value_type + || column->type.n_max != n_max) { if (!VLOG_DROP_DBG(&rl)) { char *type_name = ovsdb_type_to_english(&column->type); VLOG_DBG("Table `%s' column `%s' has type %s, not expected " - "type %s.", schema->name, column_name, type_name, - ovsdb_atomic_type_to_string(type)); + "key type %s, value type %s, max elements %zd.", + schema->name, column_name, type_name, + ovsdb_atomic_type_to_string(key_type), + ovsdb_atomic_type_to_string(value_type), + n_max); + free(type_name); } - return false; + return NULL; } - datum = &row->fields[column->index]; - return datum->n ? datum->keys : NULL; + return &row->fields[column->index]; +} + +static const union ovsdb_atom * +read_column(const struct ovsdb_row *row, const char *column_name, + enum ovsdb_atomic_type type) +{ + const struct ovsdb_datum *datum; + + datum = get_datum((struct ovsdb_row *) row, column_name, type, OVSDB_TYPE_VOID, 1); + return datum && datum->n ? datum->keys : NULL; } static bool @@ -326,6 +353,50 @@ read_string_column(const struct ovsdb_row *row, const char *column_name, return atom != NULL; } +static void +write_bool_column(struct ovsdb_row *row, const char *column_name, bool value) +{ + struct ovsdb_datum *datum = get_datum(row, column_name, OVSDB_TYPE_BOOLEAN, + OVSDB_TYPE_VOID, 1); + + if (!datum) { + return; + } + datum->keys[0].boolean = value; +} + +static void +write_string_string_column(struct ovsdb_row *row, const char *column_name, + char **keys, char **values, size_t n) +{ + const struct ovsdb_column *column; + struct ovsdb_datum *datum; + size_t i; + + column = ovsdb_table_schema_get_column(row->table->schema, column_name); + datum = get_datum(row, column_name, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, + UINT_MAX); + if (!datum) { + return; + } + + /* Free existing data. */ + ovsdb_datum_destroy(datum, &column->type); + + /* Allocate space for new values. */ + datum->n = n; + datum->keys = xmalloc(n * sizeof *datum->keys); + datum->values = xmalloc(n * sizeof *datum->values); + + for (i = 0; i < n; ++i) { + datum->keys[i].string = keys[i]; + datum->values[i].string = values[i]; + } + + /* Sort and check constraints. */ + ovsdb_datum_sort_assert(datum, column->type.key.type); +} + /* Adds a remote and options to 'remotes', based on the Manager table row in * 'row'. */ static void @@ -393,6 +464,117 @@ query_db_remotes(const char *name, const struct ovsdb *db, } } +static void +update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn, + const struct shash *statuses) +{ + struct ovsdb_row *rw_row; + const char *target; + const struct ovsdb_jsonrpc_remote_status *status; + char *keys[3], *values[3]; + size_t n = 0; + + /* Get the "target" (protocol/host/port) spec. */ + if (!read_string_column(row, "target", &target)) { + /* Bad remote spec or incorrect schema. */ + return; + } + + /* Prepare to modify this row. */ + rw_row = ovsdb_txn_row_modify(txn, row); + + /* Find status information for this target. */ + status = shash_find_data(statuses, target); + if (!status) { + /* Should never happen, but just in case... */ + return; + } + + /* Update status information columns. */ + + write_bool_column(rw_row, "is_connected", + status->is_connected); + + keys[n] = xstrdup("state"); + values[n++] = xstrdup(status->state); + keys[n] = xstrdup("time_in_state"); + values[n++] = xasprintf("%u", status->state_elapsed); + if (status->last_error) { + keys[n] = xstrdup("last_error"); + values[n++] = + xstrdup(ovs_retval_to_string(status->last_error)); + } + write_string_string_column(rw_row, "status", keys, values, n); +} + +static void +update_remote_rows(const struct ovsdb *db, struct ovsdb_txn *txn, + const char *remote_name, const struct shash *statuses) +{ + const struct ovsdb_table *table, *ref_table; + const struct ovsdb_column *column; + const struct ovsdb_row *row; + + if (strncmp("db:", remote_name, 3)) { + return; + } + + parse_db_column(db, remote_name, &table, &column); + + if (column->type.key.type != OVSDB_TYPE_UUID + || !column->type.key.u.uuid.refTable + || column->type.value.type != OVSDB_TYPE_VOID) { + return; + } + + ref_table = column->type.key.u.uuid.refTable; + + HMAP_FOR_EACH (row, hmap_node, &table->rows) { + const struct ovsdb_datum *datum; + size_t i; + + datum = &row->fields[column->index]; + for (i = 0; i < datum->n; i++) { + const struct ovsdb_row *ref_row; + + ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid); + if (ref_row) { + update_remote_row(ref_row, txn, statuses); + } + } + } +} + +static void +update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, + const struct shash *remotes, struct ovsdb *db) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + struct shash_node *remote; + struct shash statuses; + struct ovsdb_txn *txn; + const bool durable_txn = false; + struct ovsdb_error *error; + + /* Get status of current connections. */ + ovsdb_jsonrpc_server_get_remote_status(jsonrpc, &statuses); + + txn = ovsdb_txn_create(db); + + /* Iterate over --remote arguments given on command line. */ + SHASH_FOR_EACH (remote, remotes) { + update_remote_rows(db, txn, remote->name, &statuses); + } + + error = ovsdb_txn_commit(txn, durable_txn); + if (error) { + VLOG_ERR_RL(&rl, "Failed to update remote status: %s", + ovsdb_error_to_string(error)); + } + + shash_destroy_free_data(&statuses); +} + /* Reconfigures ovsdb-server based on information in the database. */ static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc, diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index 8d14221c3..855349975 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", - "version": "1.1.0", - "cksum": "815766362 15358", + "version": "1.2.0", + "cksum": "2368100022 15573", "tables": { "Open_vSwitch": { "columns": { @@ -412,7 +412,13 @@ "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", - "min": 0, "max": "unlimited"}}}}, + "min": 0, "max": "unlimited"}}, + "is_connected": { + "type": "boolean", + "ephemeral": true}, + "status": { + "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, + "ephemeral": true}}}, "SSL": { "columns": { "private_key": { diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 6565b9582..a02ea5343 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2037,6 +2037,38 @@ unique. No common key-value pairs are currently defined. + + + + true if currently connected to this manager, + false otherwise. + + + +

Key-value pairs that report manager status.

+
+
last_error
+
A human-readable description of the last error on the connection + to the manager; i.e. strerror(errno). This key + will exist only if an error has occurred.
+
+
+
state
+
The state of the connection to the manager. Possible values + are: VOID (connection is disabled), + BACKOFF (attempting to reconnect at an increasing + period), CONNECT_IN_PROGRESS (attempting to connect), + ACTIVE (connected, remote host responsive), and + IDLE (remote host unresponsive, disconnecting). These + values may change in the future. They are provided only for human + consumption.
+
+
+
time_in_state
+
Milliseconds since the state key changed.
+
+
+
-- 2.43.0