mirror of
https://github.com/openvswitch/ovs
synced 2025-10-27 15:18:06 +00:00
With this commit, Open vSwitch permits a bridge to have any number of OpenFlow controllers. When multiple controllers are configured, Open vSwitch connects to all of them simultaneously. Details of configuration are in the vswitch schema documentation. OpenFlow 1.0 does not specify how multiple controllers coordinate in interacting with a single switch, so more than one controller should be specified only if the controllers are themselves designed to coordinate with each other. An upcoming commit will provide a simple means for coordination between multiple controllers. Feature #2495.
240 lines
7.4 KiB
C
240 lines
7.4 KiB
C
/*
|
|
* Copyright (c) 2008, 2009, 2010 Nicira Networks.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "status.h"
|
|
#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.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 "packets.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();
|
|
uint32_t remote_ip = rconn_get_remote_ip(rconn);
|
|
uint32_t local_ip = rconn_get_local_ip(rconn);
|
|
|
|
status_reply_put(sr, "name=%s", rconn_get_name(rconn));
|
|
if (remote_ip) {
|
|
status_reply_put(sr, "remote-ip="IP_FMT, IP_ARGS(&remote_ip));
|
|
status_reply_put(sr, "remote-port=%d",
|
|
ntohs(rconn_get_remote_port(rconn)));
|
|
status_reply_put(sr, "local-ip="IP_FMT, IP_ARGS(&local_ip));
|
|
status_reply_put(sr, "local-port=%d",
|
|
ntohs(rconn_get_local_port(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, "probe-interval=%d", rconn_get_probe_interval(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, "last-received=%ld",
|
|
(long int) (now - rconn_get_last_received(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_;
|
|
uint64_t datapath_id;
|
|
struct svec listeners;
|
|
size_t i;
|
|
|
|
datapath_id = ofproto_get_datapath_id(ofproto);
|
|
if (datapath_id) {
|
|
status_reply_put(sr, "datapath-id=%016"PRIx64, datapath_id);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 = xzalloc(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);
|
|
}
|
|
}
|