ofproto-dpif: Add ovs-appctl commands for ovs-dpctl functions.
authorJustin Pettit <jpettit@nicira.com>
Wed, 26 Sep 2012 01:47:02 +0000 (18:47 -0700)
committerJustin Pettit <jpettit@nicira.com>
Fri, 2 Nov 2012 05:54:27 +0000 (22:54 -0700)
These commands will be useful in a future commit that makes multiple
bridges share a single backing datapath.  The ovs-dpctl commands will
show information about the backing datapath, so it will be difficult to
determine which information belongs to which bridge.  The new "dpif/*"
ovs-appctl commands return information about the bridge--regardless of
how the backing datapath is configured.

Signed-off-by: Justin Pettit <jpettit@nicira.com>
NEWS
manpages.mk
ofproto/automake.mk
ofproto/ofproto-dpif-unixctl.man [new file with mode: 0644]
ofproto/ofproto-dpif.c
tests/ofproto-dpif.at
tests/ofproto-macros.at
utilities/ovs-dpctl.8.in
vswitchd/ovs-vswitchd.8.in

diff --git a/NEWS b/NEWS
index a052f22..ea412be 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,9 @@ v1.9.0 - xx xxx xxxx
     - ovs-dpctl:
       - Support requesting the port number with the "port_no" option in
         the "add-if" command.
+    - ovs-appctl:
+      - New "dpif/dump-dps", "dpif/show", and "dpif/dump-flows" command
+        that mimic the equivalent ovs-dpctl commands.
     - ovs-pki: The "online PKI" features have been removed, along with
       the ovs-pki-cgi program that facilitated it, because of some
       alarmist insecurity claims.  We do not believe that these claims
index 01700c3..c878144 100644 (file)
@@ -241,6 +241,7 @@ vswitchd/ovs-vswitchd.8: \
        lib/stress-unixctl.man \
        lib/vlog-unixctl.man \
        lib/vlog.man \
+       ofproto/ofproto-dpif-unixctl.man \
        ofproto/ofproto-unixctl.man \
        ovsdb/remote-active.man \
        ovsdb/remote-passive.man
@@ -254,6 +255,7 @@ lib/ssl.man:
 lib/stress-unixctl.man:
 lib/vlog-unixctl.man:
 lib/vlog.man:
+ofproto/ofproto-dpif-unixctl.man:
 ofproto/ofproto-unixctl.man:
 ovsdb/remote-active.man:
 ovsdb/remote-passive.man:
index ab889af..9088292 100644 (file)
@@ -31,4 +31,4 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/pinsched.c \
        ofproto/pinsched.h
 
-MAN_FRAGMENTS += ofproto/ofproto-unixctl.man
+MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man
diff --git a/ofproto/ofproto-dpif-unixctl.man b/ofproto/ofproto-dpif-unixctl.man
new file mode 100644 (file)
index 0000000..daacd3a
--- /dev/null
@@ -0,0 +1,34 @@
+.SS "DATAPATH COMMANDS"
+These commands manage logical datapaths.  They are are similar to the
+equivalent \fBovs\-dpctl\fR commands.
+.
+.IP "\fBdpif/dump\-dps\fR"
+Prints the name of each configured datapath on a separate line.
+.
+.IP "\fBdpif/show\fR [\fIdp\fR...]"
+Prints a summary of configured datapaths, including statistics and a
+list of connected ports.  The port information includes the OpenFlow
+port number, datapath port number, and the type.  (The local port is
+identified as OpenFlow port 65534.)
+.IP
+If one or more datapaths are specified, information on only those
+datapaths are displayed.  Otherwise, information about all configured
+datapaths are shown.
+.
+.IP "\fBdpif/dump\-flows \fIdp\fR"
+Prints to the console all flow entries in datapath \fIdp\fR's
+flow table.
+.IP
+This command is primarily useful for debugging Open vSwitch.  The flow
+table entries that it displays are not OpenFlow flow entries.  Instead,
+they are different and considerably simpler flows maintained by the
+datapath module.  If you wish to see the OpenFlow flow entries, use
+\fBovs\-ofctl dump\-flows\fR.
+.
+.IP "\fBdpif/del\-flows \fIdp\fR"
+Deletes all flow entries from datapath \fIdp\fR's flow table and
+underlying datapath implementation (e.g., kernel datapath module).
+.IP
+This command is primarily useful for debugging Open vSwitch.  As
+discussed in \fBdpif/dump\-flows\fR, these entries are
+not OpenFlow flow entries.
index e576716..65e560e 100644 (file)
@@ -49,6 +49,7 @@
 #include "ofproto-dpif-sflow.h"
 #include "poll-loop.h"
 #include "simap.h"
+#include "smap.h"
 #include "timer.h"
 #include "unaligned.h"
 #include "unixctl.h"
@@ -7043,6 +7044,208 @@ ofproto_dpif_self_check(struct unixctl_conn *conn,
     ds_destroy(&reply);
 }
 
+/* Store the current ofprotos in 'ofproto_shash'.  Returns a sorted list
+ * of the 'ofproto_shash' nodes.  It is the responsibility of the caller
+ * to destroy 'ofproto_shash' and free the returned value. */
+static const struct shash_node **
+get_ofprotos(struct shash *ofproto_shash)
+{
+    const struct ofproto_dpif *ofproto;
+
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        char *name = xasprintf("%s@%s", ofproto->up.type, ofproto->up.name);
+        shash_add_nocopy(ofproto_shash, name, ofproto);
+    }
+
+    return shash_sort(ofproto_shash);
+}
+
+static void
+ofproto_unixctl_dpif_dump_dps(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                              const char *argv[] OVS_UNUSED,
+                              void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct shash ofproto_shash;
+    const struct shash_node **sorted_ofprotos;
+    int i;
+
+    shash_init(&ofproto_shash);
+    sorted_ofprotos = get_ofprotos(&ofproto_shash);
+    for (i = 0; i < shash_count(&ofproto_shash); i++) {
+        const struct shash_node *node = sorted_ofprotos[i];
+        ds_put_format(&ds, "%s\n", node->name);
+    }
+
+    shash_destroy(&ofproto_shash);
+    free(sorted_ofprotos);
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
+{
+    struct dpif_dp_stats s;
+    const struct shash_node **ports;
+    int i;
+
+    dpif_get_dp_stats(ofproto->dpif, &s);
+
+    ds_put_format(ds, "%s@%s:\n", ofproto->up.type, ofproto->up.name);
+    ds_put_format(ds,
+                  "\tlookups: hit:%"PRIu64" missed:%"PRIu64" lost:%"PRIu64"\n",
+                  s.n_hit, s.n_missed, s.n_lost);
+    ds_put_format(ds, "\tflows: %"PRIu64"\n", s.n_flows);
+
+    ports = shash_sort(&ofproto->up.port_by_name);
+    for (i = 0; i < shash_count(&ofproto->up.port_by_name); i++) {
+        const struct shash_node *node = ports[i];
+        struct ofport *ofport = node->data;
+        const char *name = netdev_get_name(ofport->netdev);
+        const char *type = netdev_get_type(ofport->netdev);
+
+        ds_put_format(ds, "\t%s %u/%u:", name, ofport->ofp_port,
+                      ofp_port_to_odp_port(ofproto, ofport->ofp_port));
+        if (strcmp(type, "system")) {
+            struct netdev *netdev;
+            int error;
+
+            ds_put_format(ds, " (%s", type);
+
+            error = netdev_open(name, type, &netdev);
+            if (!error) {
+                struct smap config;
+
+                smap_init(&config);
+                error = netdev_get_config(netdev, &config);
+                if (!error) {
+                    const struct smap_node **nodes;
+                    size_t i;
+
+                    nodes = smap_sort(&config);
+                    for (i = 0; i < smap_count(&config); i++) {
+                        const struct smap_node *node = nodes[i];
+                        ds_put_format(ds, "%c %s=%s", i ? ',' : ':',
+                                      node->key, node->value);
+                    }
+                    free(nodes);
+                }
+                smap_destroy(&config);
+
+                netdev_close(netdev);
+            }
+            ds_put_char(ds, ')');
+        }
+        ds_put_char(ds, '\n');
+    }
+    free(ports);
+}
+
+static void
+ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc,
+                          const char *argv[], void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct ofproto_dpif *ofproto;
+
+    if (argc > 1) {
+        int i;
+        for (i = 1; i < argc; i++) {
+            ofproto = ofproto_dpif_lookup(argv[i]);
+            if (!ofproto) {
+                ds_put_format(&ds, "Unknown bridge %s (use dpif/dump-dps "
+                                   "for help)", argv[i]);
+                unixctl_command_reply_error(conn, ds_cstr(&ds));
+                return;
+            }
+            show_dp_format(ofproto, &ds);
+        }
+    } else {
+        struct shash ofproto_shash;
+        const struct shash_node **sorted_ofprotos;
+        int i;
+
+        shash_init(&ofproto_shash);
+        sorted_ofprotos = get_ofprotos(&ofproto_shash);
+        for (i = 0; i < shash_count(&ofproto_shash); i++) {
+            const struct shash_node *node = sorted_ofprotos[i];
+            show_dp_format(node->data, &ds);
+        }
+
+        shash_destroy(&ofproto_shash);
+        free(sorted_ofprotos);
+    }
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
+                                int argc OVS_UNUSED, const char *argv[],
+                                void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    const struct ofproto_dpif *ofproto;
+    struct subfacet *subfacet;
+
+    ofproto = ofproto_dpif_lookup(argv[1]);
+    if (!ofproto) {
+        unixctl_command_reply_error(conn, "no such bridge");
+        return;
+    }
+
+    HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
+        struct odputil_keybuf keybuf;
+        struct ofpbuf key;
+
+        subfacet_get_key(subfacet, &keybuf, &key);
+        odp_flow_key_format(key.data, key.size, &ds);
+
+        ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
+                      subfacet->dp_packet_count, subfacet->dp_byte_count);
+        if (subfacet->used) {
+            ds_put_format(&ds, "%.3fs",
+                          (time_msec() - subfacet->used) / 1000.0);
+        } else {
+            ds_put_format(&ds, "never");
+        }
+        if (subfacet->facet->tcp_flags) {
+            ds_put_cstr(&ds, ", flags:");
+            packet_format_tcp_flags(&ds, subfacet->facet->tcp_flags);
+        }
+
+        ds_put_cstr(&ds, ", actions:");
+        format_odp_actions(&ds, subfacet->actions, subfacet->actions_len);
+        ds_put_char(&ds, '\n');
+    }
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+ofproto_unixctl_dpif_del_flows(struct unixctl_conn *conn,
+                               int argc OVS_UNUSED, const char *argv[],
+                               void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct ofproto_dpif *ofproto;
+
+    ofproto = ofproto_dpif_lookup(argv[1]);
+    if (!ofproto) {
+        unixctl_command_reply_error(conn, "no such bridge");
+        return;
+    }
+
+    flush(&ofproto->up);
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
 static void
 ofproto_dpif_unixctl_init(void)
 {
@@ -7066,6 +7269,14 @@ ofproto_dpif_unixctl_init(void)
                              ofproto_dpif_unclog, NULL);
     unixctl_command_register("ofproto/self-check", "[bridge]", 0, 1,
                              ofproto_dpif_self_check, NULL);
+    unixctl_command_register("dpif/dump-dps", "", 0, 0,
+                             ofproto_unixctl_dpif_dump_dps, NULL);
+    unixctl_command_register("dpif/show", "[bridge]", 0, INT_MAX,
+                             ofproto_unixctl_dpif_show, NULL);
+    unixctl_command_register("dpif/dump-flows", "bridge", 1, 1,
+                             ofproto_unixctl_dpif_dump_flows, NULL);
+    unixctl_command_register("dpif/del-flows", "bridge", 1, 1,
+                             ofproto_unixctl_dpif_del_flows, NULL);
 }
 \f
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
index a49e04d..fb4cb73 100644 (file)
@@ -1190,3 +1190,103 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0],
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-dps])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl dpif/dump-dps], [0], [dnl
+dummy@br0
+dummy@br1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/show])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+dummy@br0:
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (dummy)
+       p2 2/2: (dummy)
+dummy@br1:
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br1 65534/101: (dummy)
+       p3 3/3: (dummy)
+])
+
+AT_CHECK([ovs-appctl dpif/show br0], [0], [dnl
+dummy@br0:
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (dummy)
+       p2 2/2: (dummy)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-flows])
+OVS_VSWITCHD_START([add-br br1 -- \
+                    set bridge br1 datapath-type=dummy fail-mode=secure])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/del-flows])
+OVS_VSWITCHD_START([add-br br1 -- \
+                    set bridge br1 datapath-type=dummy fail-mode=secure])
+ADD_OF_PORTS([br0], [1], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'], [0], [success
+])
+AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'], [0], [success
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+AT_CHECK([ovs-appctl dpif/del-flows br0])
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 8b8de15..ee0d853 100644 (file)
@@ -18,6 +18,7 @@ m4_divert_pop([PREPARE_TESTS])
 
 m4_define([STRIP_XIDS], [[sed 's/ (xid=0x[0-9a-fA-F]*)//']])
 m4_define([STRIP_DURATION], [[sed 's/\bduration=[0-9.]*s/duration=?s/']])
+m4_define([STRIP_USED], [[sed 's/used:[0-9]\.[0-9]*/used:0.0/']])
 m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m'])
 
 # OVS_VSWITCHD_START([vsctl-args], [vsctl-output])
index 042bcd5..d031ea4 100644 (file)
@@ -112,7 +112,8 @@ flow table.
 This command is primarily useful for debugging Open vSwitch.  The flow
 table entries that it displays are not
 OpenFlow flow entries.  Instead, they are different and considerably
-simpler flows maintained by the Open vSwitch kernel module.
+simpler flows maintained by the Open vSwitch kernel module.  If you wish
+to see the OpenFlow flow entries, use \fBovs\-ofctl dump\-flows\fR.
 .IP "\fBdel\-flows \fIdp\fR"
 Deletes all flow entries from datapath \fIdp\fR's flow table.
 .IP
index 5a959dd..6bb08c7 100644 (file)
@@ -204,6 +204,7 @@ information, and partner information.  If \fIport\fR is not specified,
 then displays detailed information about all interfaces with CFM
 enabled.
 .
+.so ofproto/ofproto-dpif-unixctl.man
 .so ofproto/ofproto-unixctl.man
 .so lib/vlog-unixctl.man
 .so lib/memory-unixctl.man