[ovs-dev] [PATCH 1/2] feature: Add ovs-appctl log/controller command.
Alex Wang
alexw at nicira.com
Mon Aug 5 03:14:30 UTC 2013
This commit adds a new command "ovs-appctl log/controller" to ovs.
This command allows user to log OpenFlow messages between bridge and
its controllers into a specified file.
Signed-off-by: Alex Wang <alexw at nicira.com>
---
lib/automake.mk | 2 ++
lib/ovs-snoop.c | 71 +++++++++++++++++++++++++++++++++++++++
lib/ovs-snoop.h | 28 +++++++++++++++
lib/rconn.c | 26 ++++++++++++++
lib/rconn.h | 3 ++
ofproto/automake.mk | 3 +-
ofproto/connmgr.c | 63 ++++++++++++++++++++++++++++++++++
ofproto/connmgr.h | 4 +++
ofproto/ofproto-dpif.c | 66 ++++++++++++++++++++++++++++++++++++
ofproto/ofproto-log-unixctl.man | 37 ++++++++++++++++++++
tests/ofproto-dpif.at | 53 +++++++++++++++++++++++++++++
11 files changed, 355 insertions(+), 1 deletion(-)
create mode 100644 lib/ovs-snoop.c
create mode 100644 lib/ovs-snoop.h
create mode 100644 ofproto/ofproto-log-unixctl.man
diff --git a/lib/automake.mk b/lib/automake.mk
index b46a868..f422422 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -131,6 +131,8 @@ lib_libopenvswitch_a_SOURCES = \
lib/ovs-atomic-pthreads.c \
lib/ovs-atomic-pthreads.h \
lib/ovs-atomic.h \
+ lib/ovs-snoop.c \
+ lib/ovs-snoop.h \
lib/ovs-thread.c \
lib/ovs-thread.h \
lib/ovsdb-data.c \
diff --git a/lib/ovs-snoop.c b/lib/ovs-snoop.c
new file mode 100644
index 0000000..54e6bc1
--- /dev/null
+++ b/lib/ovs-snoop.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ *
+ * 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 "ovs-snoop.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include "dirs.h"
+#include "stream.h"
+#include "stream-fd.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ovs_snoop);
+
+/* Opens a snoop file of name 'file_name'. Uses default file name if
+ * 'file_name' is null. Then creates a fd stream, stores the pointer of
+ * the stream in 'streamp'. Returns 0 if successful, otherwise a
+ * positive errno value. */
+int
+snoop_open(struct stream **streamp, const char *file_name)
+{
+ char *snoop_file_name;
+ int retval, new_fd;
+
+ snoop_file_name = (file_name
+ ? xstrdup(file_name)
+ : xasprintf("%s/ovs-snoop.log", ovs_logdir()));
+
+ /* Open new snoop file. */
+ new_fd = open(snoop_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
+ if (new_fd < 0) {
+ VLOG_WARN("failed to open %s for snoop logging: %s",
+ snoop_file_name, ovs_strerror(errno));
+ retval = errno;
+ goto exit;
+ }
+ retval = new_fd_stream(snoop_file_name, new_fd, 0, streamp);
+exit:
+ free(snoop_file_name);
+ return retval;
+}
+
+/* Closes snoop file. */
+void
+snoop_close(struct stream *stream)
+{
+ stream_close(stream);
+}
+
+/* Writes 'message' to the snoop log. */
+void
+snoop_log(struct stream *stream, const void *msg, size_t size)
+{
+ stream_send(stream, msg, size);
+ stream_send(stream, "\n", 1);
+}
diff --git a/lib/ovs-snoop.h b/lib/ovs-snoop.h
new file mode 100644
index 0000000..bd5e37d
--- /dev/null
+++ b/lib/ovs-snoop.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ *
+ * 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.
+ */
+
+#ifndef OVS_SNOOP_H
+#define OVS_SNOOP_H 1
+
+#include <stddef.h>
+
+struct stream;
+
+int snoop_open(struct stream **, const char *file_name);
+void snoop_close(struct stream *);
+void snoop_log(struct stream *, const void *msg, size_t size);
+
+#endif /* ovs-snoop.h */
diff --git a/lib/rconn.c b/lib/rconn.c
index 39a12c9..2d36d75 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -25,6 +25,7 @@
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
+#include "ovs-snoop.h"
#include "poll-loop.h"
#include "sat-math.h"
#include "timeval.h"
@@ -131,6 +132,9 @@ struct rconn {
struct vconn *monitors[8];
size_t n_monitors;
+ /* Controller logging. */
+ struct stream *snoop_stream;
+
uint32_t allowed_versions;
};
@@ -220,6 +224,8 @@ rconn_create(int probe_interval, int max_backoff, uint8_t dscp,
rc->n_monitors = 0;
rc->allowed_versions = allowed_versions;
+ rc->snoop_stream = NULL;
+
return rc;
}
@@ -580,6 +586,9 @@ rconn_recv(struct rconn *rc)
int error = vconn_recv(rc->vconn, &buffer);
if (!error) {
copy_to_monitor(rc, buffer);
+ if (rc->snoop_stream) {
+ snoop_log(rc->snoop_stream, buffer->data, buffer->size);
+ }
if (rc->probably_admitted || is_admitted_msg(buffer)
|| time_now() - rc->last_connected >= 30) {
rc->probably_admitted = true;
@@ -628,6 +637,9 @@ rconn_send(struct rconn *rc, struct ofpbuf *b,
if (rconn_is_connected(rc)) {
COVERAGE_INC(rconn_queued);
copy_to_monitor(rc, b);
+ if (rc->snoop_stream) {
+ snoop_log(rc->snoop_stream, b->data, b->size);
+ }
b->private_p = counter;
if (counter) {
rconn_packet_counter_inc(counter, b->size);
@@ -697,6 +709,20 @@ rconn_add_monitor(struct rconn *rc, struct vconn *vconn)
}
}
+/* Enables writing to snoop log of 'rc'. */
+void
+rconn_set_snoop(struct rconn *rc, struct stream *stream)
+{
+ rc->snoop_stream = stream;
+}
+
+/* Disables writing to snoop log of 'rc'. */
+void
+rconn_clear_snoop(struct rconn *rc)
+{
+ rc->snoop_stream = NULL;
+}
+
/* Returns 'rc''s name. This is a name for human consumption, appropriate for
* use in log messages. It is not necessarily a name that may be passed
* directly to, e.g., vconn_open(). */
diff --git a/lib/rconn.h b/lib/rconn.h
index aa30238..4dd7a9c 100644
--- a/lib/rconn.h
+++ b/lib/rconn.h
@@ -36,6 +36,7 @@
struct vconn;
struct rconn_packet_counter;
+struct stream;
struct rconn *rconn_create(int inactivity_probe_interval,
int max_backoff, uint8_t dscp,
@@ -47,6 +48,8 @@ void rconn_set_max_backoff(struct rconn *, int max_backoff);
int rconn_get_max_backoff(const struct rconn *);
void rconn_set_probe_interval(struct rconn *, int inactivity_probe_interval);
int rconn_get_probe_interval(const struct rconn *);
+void rconn_set_snoop(struct rconn *, struct stream *);
+void rconn_clear_snoop(struct rconn *);
void rconn_connect(struct rconn *, const char *target, const char *name);
void rconn_connect_unreliably(struct rconn *,
diff --git a/ofproto/automake.mk b/ofproto/automake.mk
index af9a12a..8b1066f 100644
--- a/ofproto/automake.mk
+++ b/ofproto/automake.mk
@@ -48,7 +48,8 @@ BUILT_SOURCES += ofproto/ipfix-entities.def
CLEANFILES += ofproto/ipfix-entities.def
-MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man
+MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man \
+ ofproto/ofproto-log-unixctl.man
# IPFIX entity definition macros generation from IANA's XML definition.
EXTRA_DIST += ofproto/ipfix.xml
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 4264934..3defbe3 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -30,6 +30,7 @@
#include "ofp-util.h"
#include "ofpbuf.h"
#include "ofproto-provider.h"
+#include "ovs-snoop.h"
#include "pinsched.h"
#include "poll-loop.h"
#include "pktbuf.h"
@@ -172,6 +173,9 @@ struct connmgr {
struct sockaddr_in *extra_in_band_remotes;
size_t n_extra_remotes;
int in_band_queue;
+
+ /* Controller logging. */
+ struct stream *snoop_stream;
};
static void update_in_band_remotes(struct connmgr *);
@@ -211,6 +215,7 @@ connmgr_create(struct ofproto *ofproto,
mgr->n_extra_remotes = 0;
mgr->in_band_queue = -1;
+ mgr->snoop_stream = NULL;
return mgr;
}
@@ -814,6 +819,64 @@ add_snooper(struct connmgr *mgr, struct vconn *vconn)
vconn_close(vconn);
}
}
+
+/* Sets all controller rconns of mgr to snoop OpenFlow packets. */
+void
+connmgr_snoop_set_all(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ ovs_assert(mgr->snoop_stream);
+ /* Snoop all controller rconns. */
+ HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) {
+ rconn_set_snoop(ofconn->rconn, mgr->snoop_stream);
+ }
+}
+
+/* Checks the existence of controller matching the given 'target'.
+ * If a controller is found, set its rconn to snoop OpenFlow packets. */
+int
+connmgr_snoop_set_one(struct connmgr *mgr, const char *target)
+{
+ struct ofconn *ofconn;
+
+ ovs_assert(mgr->snoop_stream);
+ ofconn = find_controller_by_target(mgr, target);
+ if (ofconn) {
+ rconn_set_snoop(ofconn->rconn, mgr->snoop_stream);
+ return 0;
+ }
+ return -1;
+}
+
+/* Clears the snooping of all controller rconns of mgr. */
+static void
+connmgr_snoop_clear_all(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) {
+ rconn_clear_snoop(ofconn->rconn);
+ }
+}
+
+/* Opens the snoop file for this bridge. */
+int
+connmgr_log_open(struct connmgr *mgr, const char *name)
+{
+ ovs_assert(!mgr->snoop_stream);
+ return snoop_open(&mgr->snoop_stream, name);
+}
+
+/* Closes the snoop file for this bridge. */
+void
+connmgr_log_close(struct connmgr *mgr)
+{
+ snoop_close(mgr->snoop_stream);
+ connmgr_snoop_clear_all(mgr);
+ mgr->snoop_stream = NULL;
+}
+
/* Public ofconn functions. */
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index f92a523..1a7a61a 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -92,6 +92,10 @@ void connmgr_reconnect(const struct connmgr *);
int connmgr_set_snoops(struct connmgr *, const struct sset *snoops);
bool connmgr_has_snoops(const struct connmgr *);
void connmgr_get_snoops(const struct connmgr *, struct sset *snoops);
+void connmgr_snoop_set_all(struct connmgr *);
+int connmgr_snoop_set_one(struct connmgr *, const char *target);
+int connmgr_log_open(struct connmgr *, const char *name);
+void connmgr_log_close(struct connmgr *);
/* Individual connections to OpenFlow controllers. */
enum ofconn_type ofconn_get_type(const struct ofconn *);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 7425855..f7a4778 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -501,6 +501,9 @@ struct ofproto_dpif {
struct ovs_mutex flow_mod_mutex;
struct list flow_mods OVS_GUARDED;
size_t n_flow_mods OVS_GUARDED;
+
+ /* Controller logging. */
+ bool ctrl_logging;
};
/* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only
@@ -1318,6 +1321,7 @@ construct(struct ofproto *ofproto_)
ofproto->n_hit = 0;
ofproto->n_missed = 0;
+ ofproto->ctrl_logging = false;
return error;
}
@@ -5599,6 +5603,64 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
ds_destroy(&ds);
}
+static void
+ofproto_unixctl_log_controller(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ struct ofproto_dpif *ofproto;
+
+ ofproto = ofproto_dpif_lookup(argv[1]);
+ if (!ofproto) {
+ unixctl_command_reply_error(conn, "Unknown bridge name");
+ return;
+ }
+
+ /* Stops snooping when nothing is specified in the arguments. */
+ if (argc == 2) {
+ connmgr_log_close(ofproto->up.connmgr);
+ ofproto->ctrl_logging = false;
+ } else {
+ int error;
+
+ /* If bridge is currently logging controller, return. */
+ if (ofproto->ctrl_logging) {
+ unixctl_command_reply_error(conn, "Bridge already in logging "
+ "controllers");
+ return;
+ }
+
+ /* Opens the log file. If name is 'default', use the default path.
+ * Otherwise, use the specified name. */
+ error = connmgr_log_open(ofproto->up.connmgr,
+ !strcmp(argv[2], "default")
+ ? NULL : argv[2]);
+ if (error) {
+ unixctl_command_reply_error(conn, "Open log file failed");
+ return;
+ }
+
+ /* If nothing is specified, log all controllers. */
+ if (argc == 3) {
+ connmgr_snoop_set_all(ofproto->up.connmgr);
+ } else {
+ size_t i;
+
+ /* Log only the specified controllers. If there is a
+ * incorrect name, clear all configurations. */
+ for (i = 3; i < argc; i++) {
+ if(connmgr_snoop_set_one(ofproto->up.connmgr, argv[i])) {
+ connmgr_log_close(ofproto->up.connmgr);
+ unixctl_command_reply_error(conn, "Incorrect controller "
+ "name, reconfigure needed");
+ return;
+ }
+ }
+ }
+ ofproto->ctrl_logging = true;
+ }
+ unixctl_command_reply(conn, NULL);
+}
+
struct trace_ctx {
struct xlate_out xout;
struct xlate_in xin;
@@ -6341,6 +6403,10 @@ ofproto_dpif_unixctl_init(void)
ofproto_unixctl_dpif_disable_megaflows, NULL);
unixctl_command_register("dpif/enable-megaflows", "", 0, 0,
ofproto_unixctl_dpif_enable_megaflows, NULL);
+ unixctl_command_register("log/controller",
+ "bridge file|default [controllers]",
+ 1, INT_MAX, ofproto_unixctl_log_controller, NULL);
+
}
/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
diff --git a/ofproto/ofproto-log-unixctl.man b/ofproto/ofproto-log-unixctl.man
new file mode 100644
index 0000000..4357982
--- /dev/null
+++ b/ofproto/ofproto-log-unixctl.man
@@ -0,0 +1,37 @@
+.SS "LOG COMMANDS"
+These commands manage the logging of OpenFlow messages.
+.
+.IP "\fBofproto/trace\fR \fIbridge\fR \fIfile|default\fR [\fIcontrollers\fR]"
+Logs the OpenFlow messages exchanged between \fIbridge\fR and
+\fIcontroller(s)\fR into \fIfile\fR. After setting the log,
+you must use \fBofproto/trace\fR \fIbridge\fR before reset the
+bridge to another set of controllers. The arguments are:
+.
+.RS
+.IP "\fIbridge\fR"
+\fIbridge\fR is the name of the target bridge.
+.
+.IP "\fIfile|default\fR"
+\fIfile\fR can be an absolute or relative path of the log file
+to be created. If there is already a file with same name, the
+OpenFLow messages are appended to the log file. If \fIdefault\fR
+is specified, the log file will be named as "ovs-snoop.log" and
+be created at same directory as \fBvlog\fR.
+.
+.IP "Incomplete information."
+If nothing is specified after the \fIfile|default\fR, Open vSwitch
+will log all OpenFlow messages between \fIbridge\fR and all its
+controller(s). Alternatively, you can specify a set of controllers
+to log using the [\fIcontrollers\fR].
+.RS
+.IP \fIcontrollers\fR
+\fIcontrollers\fR can be one or more controller name strings in the
+same form as the output of "\fBovs-vsctl get-controller\fR" command.
+If there is an incorrect controller name, the command will configure
+nothing and report error.
+.RE
+.RE
+.IP "\fBofproto/trace\fR \fIbridge\fR"
+Resets the logging of OpenFlow messages exchanged between \fIbridge\fR
+and controller(s). After this command, the \fIbridge\fR can be reset
+to log another set of controllers.
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 2c14af6..3b9ecf9 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -2682,3 +2682,56 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 5
])
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - log/controller])
+OVS_VSWITCHD_START()
+
+# Test the creation of default (ovs-snoop.log) file.
+AT_CHECK([ovs-appctl log/controller br0 default], [0])
+AT_CHECK([ls ovs-snoop.log], [0], [dnl
+ovs-snoop.log
+])
+
+# Test the error message of set log/controller when is already in
+# logging.
+AT_CHECK([ovs-appctl log/controller br0 default], [2], [], [dnl
+Bridge already in logging controllers
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+# Test the reset of log/controller.
+AT_CHECK([ovs-appctl log/controller br0], [0])
+
+# Test the creation of specified file (./log_controller.log) file.
+AT_CHECK([ovs-appctl log/controller br0 "./log_controller.log"], [0])
+AT_CHECK([ls log_controller.log], [0], [dnl
+log_controller.log
+])
+
+# Test the reset of log/controller.
+AT_CHECK([ovs-appctl log/controller br0], [0])
+
+# Test the error report of a wrong controller name.
+AT_CHECK([ovs-appctl log/controller br0 default tcp:1.2.3.4:8000], [2], [], [dnl
+Incorrect controller name, reconfigure needed
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+# Should still be able to log/controller br0.
+AT_CHECK([ovs-appctl log/controller br0 default], [0])
+AT_CHECK([ovs-appctl log/controller br0], [0])
+
+# Test the use of incorrect file name.
+AT_CHECK([ovs-appctl log/controller br0 "./non_exist_dir/log_controller.log"], [2], [], [dnl
+Open log file failed
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+# Detect the warning log message
+AT_CHECK([sed -n "s/^.*\(|ovs_snoop|WARN|.*\)$/\1/p" ovs-vswitchd.log], [0], [dnl
+|ovs_snoop|WARN|failed to open ./non_exist_dir/log_controller.log for snoop logging: No such file or directory
+])
+# Delete the warning log message
+AT_CHECK([sed "/|ovs_snoop|WARN|/d" ovs-vswitchd.log > ovs-vswitchd.log], [0])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
--
1.7.9.5
More information about the dev
mailing list