Skip to content

Commit 42448ff

Browse files
authored
Merge pull request #32 from digitalocean/mdl-ovs-flowstats14
ovs: extend FlowStats parsing for OpenFlow 1.4
2 parents 28d9f09 + 5fd1889 commit 42448ff

File tree

2 files changed

+77
-56
lines changed

2 files changed

+77
-56
lines changed

ovs/flowstats.go

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,55 +34,63 @@ type FlowStats struct {
3434
ByteCount uint64
3535
}
3636

37-
// UnmarshalText unmarshals a FlowStats from textual form as output by
38-
// 'ovs-ofctl dump-aggregate <bridge> table=<tablename>,cookie=<cookie>/-1':
39-
// NXST_AGGREGATE reply (xid=0x4): packet_count=642800 byte_count=141379644 flow_count=3
37+
// UnmarshalText unmarshals a FlowStats from textual form.
4038
func (f *FlowStats) UnmarshalText(b []byte) error {
4139
// Make a copy per documentation for encoding.TextUnmarshaler.
4240
s := string(b)
4341

4442
// Constants only needed within this method, to avoid polluting the
4543
// package namespace with generic names
4644
const (
47-
nxstAggregate = "NXST_AGGREGATE"
48-
packetCount = "packet_count"
49-
byteCount = "byte_count"
45+
packetCount = "packet_count"
46+
byteCount = "byte_count"
47+
flowCount = "flow_count"
5048
)
5149

52-
ss := strings.Fields(s)
53-
// Assumption here is that string s should have 6 fields of text separated by
54-
// (5) spaces
55-
if len(ss) != 6 {
50+
// Find the index of packet count to find stats.
51+
idx := strings.Index(s, packetCount)
52+
if idx == -1 {
5653
return ErrInvalidFlowStats
5754
}
5855

59-
if ss[0] != nxstAggregate {
60-
return ErrInvalidFlowStats
56+
// Assume the last three fields are packets, bytes, and flows, in that order.
57+
ss := strings.Fields(s[idx:])
58+
fields := []string{
59+
packetCount,
60+
byteCount,
61+
flowCount,
6162
}
6263

63-
packetCountPair := strings.Split(ss[3], "=")
64-
if len(packetCountPair) != 2 || packetCountPair[0] != packetCount {
64+
if len(ss) != len(fields) {
6565
return ErrInvalidFlowStats
6666
}
6767

68-
n, err := strconv.ParseUint(packetCountPair[1], 10, 64)
69-
if err != nil {
70-
return ErrInvalidFlowStats
71-
}
68+
var values []uint64
69+
for i := range ss {
70+
// Split key from its integer value.
71+
kv := strings.Split(ss[i], "=")
72+
if len(kv) != 2 {
73+
return ErrInvalidFlowStats
74+
}
7275

73-
f.PacketCount = n
76+
// Verify keys appear in expected order.
77+
if kv[0] != fields[i] {
78+
return ErrInvalidFlowStats
79+
}
7480

75-
byteCountPair := strings.Split(ss[4], "=")
76-
if len(byteCountPair) != 2 || byteCountPair[0] != byteCount {
77-
return ErrInvalidFlowStats
78-
}
81+
n, err := strconv.ParseUint(kv[1], 10, 64)
82+
if err != nil {
83+
return err
84+
}
7985

80-
n, err = strconv.ParseUint(byteCountPair[1], 10, 64)
81-
if err != nil {
82-
return ErrInvalidFlowStats
86+
values = append(values, n)
8387
}
8488

85-
f.ByteCount = n
89+
*f = FlowStats{
90+
PacketCount: values[0],
91+
ByteCount: values[1],
92+
// Flow count unused.
93+
}
8694

8795
return nil
8896
}

ovs/flowstats_test.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,77 +15,90 @@
1515
package ovs
1616

1717
import (
18-
"reflect"
1918
"testing"
19+
20+
"github.com/google/go-cmp/cmp"
2021
)
2122

2223
func TestFlowStatsUnmarshalText(t *testing.T) {
2324
var tests = []struct {
24-
desc string
25-
s string
26-
p *FlowStats
27-
err error
25+
desc string
26+
s string
27+
stats *FlowStats
28+
ok bool
2829
}{
2930
{
3031
desc: "empty string",
31-
err: ErrInvalidFlowStats,
3232
},
3333
{
34-
desc: "incorrect number of fields",
35-
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=642800 byte_count=141379644 flow_count=2, flow_count=3",
36-
err: ErrInvalidFlowStats,
34+
desc: "too few fields",
35+
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=642800 byte_count=141379644",
3736
},
3837
{
39-
desc: "first field is not NXST_AGGREGATE",
40-
s: "NXST_REPLY reply (xid=0x4): packet_count=642800 byte_count=141379644 flow_count=2",
41-
err: ErrInvalidFlowStats,
38+
desc: "too many fields",
39+
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=642800 byte_count=141379644 flow_count=2, flow_count=3",
4240
},
4341
{
44-
desc: "packet_count string is missing",
42+
desc: "packet_count missing",
4543
s: "NXST_AGGREGATE reply (xid=0x4): frame_count=642800 byte_count=141379644 flow_count=2",
46-
err: ErrInvalidFlowStats,
4744
},
4845
{
49-
desc: "byte_count string is missing",
46+
desc: "byte_count missing",
5047
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=642800 bits*8_count=141379644 flow_count=2",
51-
err: ErrInvalidFlowStats,
5248
},
5349
{
54-
desc: "broken packet_count=value pair",
50+
desc: "bad key=value",
51+
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=1=foo byte_count=141379644 flow_count=2",
52+
},
53+
{
54+
desc: "bad packet count",
5555
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=toosmall byte_count=141379644 flow_count=2",
56-
err: ErrInvalidFlowStats,
5756
},
5857
{
59-
desc: "broken byte_count=value pair",
58+
desc: "bad byte count",
6059
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=642800 byte_count=toolarge flow_count=2",
61-
err: ErrInvalidFlowStats,
60+
},
61+
{
62+
desc: "bad flow count",
63+
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=642800 byte_count=1 FLOW_count=2",
6264
},
6365
{
6466
desc: "OK",
6567
s: "NXST_AGGREGATE reply (xid=0x4): packet_count=642800 byte_count=141379644 flow_count=2",
66-
p: &FlowStats{
68+
stats: &FlowStats{
6769
PacketCount: 642800,
6870
ByteCount: 141379644,
6971
},
72+
ok: true,
73+
},
74+
{
75+
desc: "OK, OpenFlow 1.4",
76+
s: "OFPST_AGGREGATE reply (OF1.4) (xid=0x2): packet_count=1207 byte_count=101673 flow_count=1",
77+
stats: &FlowStats{
78+
PacketCount: 1207,
79+
ByteCount: 101673,
80+
},
81+
ok: true,
7082
},
7183
}
7284

7385
for _, tt := range tests {
7486
t.Run(tt.desc, func(t *testing.T) {
75-
p := new(FlowStats)
76-
err := p.UnmarshalText([]byte(tt.s))
87+
stats := new(FlowStats)
88+
err := stats.UnmarshalText([]byte(tt.s))
7789

78-
if want, got := errStr(tt.err), errStr(err); want != got {
79-
t.Fatalf("unexpected error:\n- want: %v\n- got: %v",
80-
want, got)
90+
if err != nil && tt.ok {
91+
t.Fatalf("unexpected error: %v", err)
92+
}
93+
if err == nil && !tt.ok {
94+
t.Fatal("expected an error, but none occurred")
8195
}
8296
if err != nil {
8397
return
8498
}
8599

86-
if want, got := tt.p, p; !reflect.DeepEqual(want, got) {
87-
t.Fatalf("unexpected PortStats:\n- want: %#v\n- got: %#v",
88-
want, got)
100+
if diff := cmp.Diff(tt.stats, stats); diff != "" {
101+
t.Fatalf("unexpected FlowStats (-want +got):\n%s", diff)
89102
}
90103
})
91104
}

0 commit comments

Comments
 (0)