2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-02 15:25:22 +00:00

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 <keith@nicira.com>
Reviewed by: Ben Pfaff.
Feature #3692.
This commit is contained in:
Andrew Evans
2011-01-28 15:39:55 -08:00
parent f696f12fbe
commit 0b3e7a8b71
7 changed files with 324 additions and 16 deletions

View File

@@ -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)
{

View File

@@ -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 *,

View File

@@ -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)
{

View File

@@ -16,6 +16,8 @@
#ifndef OVSDB_JSONRPC_SERVER_H
#define OVSDB_JSONRPC_SERVER_H 1
#include <stdbool.h>
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 *);

View File

@@ -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,

View File

@@ -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": {

View File

@@ -2037,6 +2037,38 @@
unique. No common key-value pairs are currently defined.
</column>
</group>
<group title="Status">
<column name="is_connected">
<code>true</code> if currently connected to this manager,
<code>false</code> otherwise.
</column>
<column name="status">
<p>Key-value pairs that report manager status.</p>
<dl>
<dt><code>last_error</code></dt>
<dd>A human-readable description of the last error on the connection
to the manager; i.e. <code>strerror(errno)</code>. This key
will exist only if an error has occurred.</dd>
</dl>
<dl>
<dt><code>state</code></dt>
<dd>The state of the connection to the manager. Possible values
are: <code>VOID</code> (connection is disabled),
<code>BACKOFF</code> (attempting to reconnect at an increasing
period), <code>CONNECT_IN_PROGRESS</code> (attempting to connect),
<code>ACTIVE</code> (connected, remote host responsive), and
<code>IDLE</code> (remote host unresponsive, disconnecting). These
values may change in the future. They are provided only for human
consumption.</dd>
</dl>
<dl>
<dt><code>time_in_state</code></dt>
<dd>Milliseconds since the <code>state</code> key changed.</dd>
</dl>
</column>
</group>
</table>
<table name="NetFlow">