diff options
Diffstat (limited to 'secchan/status.c')
-rw-r--r-- | secchan/status.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/secchan/status.c b/secchan/status.c new file mode 100644 index 000000000..cf67d51c4 --- /dev/null +++ b/secchan/status.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2008, 2009 Nicira Networks. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <config.h> +#include "status.h" +#include <arpa/inet.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include "dynamic-string.h" +#include "list.h" +#include "ofpbuf.h" +#include "ofproto.h" +#include "openflow/nicira-ext.h" +#include "rconn.h" +#include "svec.h" +#include "timeval.h" +#include "vconn.h" + +#define THIS_MODULE VLM_status +#include "vlog.h" + +struct status_category { + struct list node; + char *name; + void (*cb)(struct status_reply *, void *aux); + void *aux; +}; + +struct switch_status { + time_t booted; + struct status_category *config_cat; + struct status_category *switch_cat; + struct list categories; +}; + +struct status_reply { + struct status_category *category; + struct ds request; + struct ds output; +}; + +int +switch_status_handle_request(struct switch_status *ss, struct rconn *rconn, + struct nicira_header *request) +{ + struct status_category *c; + struct nicira_header *reply; + struct status_reply sr; + struct ofpbuf *b; + int retval; + + sr.request.string = (void *) (request + 1); + sr.request.length = ntohs(request->header.length) - sizeof *request; + ds_init(&sr.output); + LIST_FOR_EACH (c, struct status_category, node, &ss->categories) { + if (!memcmp(c->name, sr.request.string, + MIN(strlen(c->name), sr.request.length))) { + sr.category = c; + c->cb(&sr, c->aux); + } + } + reply = make_openflow_xid(sizeof *reply + sr.output.length, + OFPT_VENDOR, request->header.xid, &b); + reply->vendor = htonl(NX_VENDOR_ID); + reply->subtype = htonl(NXT_STATUS_REPLY); + memcpy(reply + 1, sr.output.string, sr.output.length); + retval = rconn_send(rconn, b, NULL); + if (retval && retval != EAGAIN) { + VLOG_WARN("send failed (%s)", strerror(retval)); + } + ds_destroy(&sr.output); + return 0; +} + +void +rconn_status_cb(struct status_reply *sr, void *rconn_) +{ + struct rconn *rconn = rconn_; + time_t now = time_now(); + + status_reply_put(sr, "name=%s", rconn_get_name(rconn)); + status_reply_put(sr, "state=%s", rconn_get_state(rconn)); + status_reply_put(sr, "backoff=%d", rconn_get_backoff(rconn)); + status_reply_put(sr, "is-connected=%s", + rconn_is_connected(rconn) ? "true" : "false"); + status_reply_put(sr, "sent-msgs=%u", rconn_packets_sent(rconn)); + status_reply_put(sr, "received-msgs=%u", rconn_packets_received(rconn)); + status_reply_put(sr, "attempted-connections=%u", + rconn_get_attempted_connections(rconn)); + status_reply_put(sr, "successful-connections=%u", + rconn_get_successful_connections(rconn)); + status_reply_put(sr, "last-connection=%ld", + (long int) (now - rconn_get_last_connection(rconn))); + status_reply_put(sr, "time-connected=%lu", + rconn_get_total_time_connected(rconn)); + status_reply_put(sr, "state-elapsed=%u", rconn_get_state_elapsed(rconn)); +} + +static void +config_status_cb(struct status_reply *sr, void *ofproto_) +{ + const struct ofproto *ofproto = ofproto_; + struct svec listeners; + int probe_interval, max_backoff; + size_t i; + + svec_init(&listeners); + ofproto_get_listeners(ofproto, &listeners); + for (i = 0; i < listeners.n; i++) { + status_reply_put(sr, "management%zu=%s", i, listeners.names[i]); + } + svec_destroy(&listeners); + + probe_interval = ofproto_get_probe_interval(ofproto); + if (probe_interval) { + status_reply_put(sr, "probe-interval=%d", probe_interval); + } + + max_backoff = ofproto_get_max_backoff(ofproto); + if (max_backoff) { + status_reply_put(sr, "max-backoff=%d", max_backoff); + } +} + +static void +switch_status_cb(struct status_reply *sr, void *ss_) +{ + struct switch_status *ss = ss_; + time_t now = time_now(); + + status_reply_put(sr, "now=%ld", (long int) now); + status_reply_put(sr, "uptime=%ld", (long int) (now - ss->booted)); + status_reply_put(sr, "pid=%ld", (long int) getpid()); +} + +struct switch_status * +switch_status_create(const struct ofproto *ofproto) +{ + struct switch_status *ss = xcalloc(1, sizeof *ss); + ss->booted = time_now(); + list_init(&ss->categories); + ss->config_cat = switch_status_register(ss, "config", config_status_cb, + (void *) ofproto); + ss->switch_cat = switch_status_register(ss, "switch", switch_status_cb, + ss); + return ss; +} + +void +switch_status_destroy(struct switch_status *ss) +{ + if (ss) { + /* Orphan any remaining categories, so that unregistering them later + * won't write to bad memory. */ + struct status_category *c, *next; + LIST_FOR_EACH_SAFE (c, next, + struct status_category, node, &ss->categories) { + list_init(&c->node); + } + switch_status_unregister(ss->config_cat); + switch_status_unregister(ss->switch_cat); + free(ss); + } +} + +struct status_category * +switch_status_register(struct switch_status *ss, + const char *category, + status_cb_func *cb, void *aux) +{ + struct status_category *c = xmalloc(sizeof *c); + c->cb = cb; + c->aux = aux; + c->name = xstrdup(category); + list_push_back(&ss->categories, &c->node); + return c; +} + +void +switch_status_unregister(struct status_category *c) +{ + if (c) { + if (!list_is_empty(&c->node)) { + list_remove(&c->node); + } + free(c->name); + free(c); + } +} + +void +status_reply_put(struct status_reply *sr, const char *content, ...) +{ + size_t old_length = sr->output.length; + size_t added; + va_list args; + + /* Append the status reply to the output. */ + ds_put_format(&sr->output, "%s.", sr->category->name); + va_start(args, content); + ds_put_format_valist(&sr->output, content, args); + va_end(args); + if (ds_last(&sr->output) != '\n') { + ds_put_char(&sr->output, '\n'); + } + + /* Drop what we just added if it doesn't match the request. */ + added = sr->output.length - old_length; + if (added < sr->request.length + || memcmp(&sr->output.string[old_length], + sr->request.string, sr->request.length)) { + ds_truncate(&sr->output, old_length); + } +} |