From 2702241699d0539d6ff4a2d44c5bc0ab9d7f7fc8 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Tue, 25 Sep 2012 18:47:02 -0700 Subject: [PATCH] ofproto-dpif: Add ovs-appctl commands for ovs-dpctl functions. 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 --- NEWS | 3 + manpages.mk | 2 + ofproto/automake.mk | 2 +- ofproto/ofproto-dpif-unixctl.man | 34 +++++ ofproto/ofproto-dpif.c | 211 +++++++++++++++++++++++++++++++ tests/ofproto-dpif.at | 100 +++++++++++++++ tests/ofproto-macros.at | 1 + utilities/ovs-dpctl.8.in | 3 +- vswitchd/ovs-vswitchd.8.in | 1 + 9 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 ofproto/ofproto-dpif-unixctl.man diff --git a/NEWS b/NEWS index a052f225..ea412be2 100644 --- 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 diff --git a/manpages.mk b/manpages.mk index 01700c32..c878144e 100644 --- a/manpages.mk +++ b/manpages.mk @@ -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: diff --git a/ofproto/automake.mk b/ofproto/automake.mk index ab889af1..90882924 100644 --- a/ofproto/automake.mk +++ b/ofproto/automake.mk @@ -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 index 00000000..daacd3ac --- /dev/null +++ b/ofproto/ofproto-dpif-unixctl.man @@ -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. diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index e5767169..65e560ea 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -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); } /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index a49e04d8..fb4cb734 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -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 diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at index 8b8de155..ee0d853f 100644 --- a/tests/ofproto-macros.at +++ b/tests/ofproto-macros.at @@ -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]) diff --git a/utilities/ovs-dpctl.8.in b/utilities/ovs-dpctl.8.in index 042bcd55..d031ea41 100644 --- a/utilities/ovs-dpctl.8.in +++ b/utilities/ovs-dpctl.8.in @@ -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 diff --git a/vswitchd/ovs-vswitchd.8.in b/vswitchd/ovs-vswitchd.8.in index 5a959ddf..6bb08c71 100644 --- a/vswitchd/ovs-vswitchd.8.in +++ b/vswitchd/ovs-vswitchd.8.in @@ -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 -- 2.30.2