blob: 2a21e685b02917ccaf6e64d20234ec02ecf8ef15 [file] [log] [blame]
Rich Lane207599b2014-03-06 14:24:16 -08001# Distributed under the OpenFlow Software License (see LICENSE)
2# Copyright (c) 2014 Big Switch Networks, Inc.
3"""
4BSN flow checksum extension test cases
5"""
6
7import logging
8import math
9import random
10
11from oftest import config
12import oftest.base_tests as base_tests
13import ofp
14
15from oftest.testutils import *
16
17TABLE_ID = 0
18
19def make_checksum(hi, lo):
20 """
21 Place 'hi' in the upper 8 bits and 'lo' in the lower bits.
22 """
23 return ((hi & 0xff) << 56) | lo
24
25assert make_checksum(0xab, 0xcd) == 0xab000000000000cd
26
Rich Lanea7331722014-03-11 13:28:25 -070027def shuffled(seq):
28 l = list(seq)[:]
29 random.shuffle(l)
30 return l
31
Rich Lane4ef81fd2014-06-30 17:16:07 -070032def add_checksum(a, b):
33 return (a + b) % 2**64
34
35def subtract_checksum(a, b):
36 return (a - b) % 2**64
37
38def bucket_index(num_buckets, checksum):
39 """
40 Use the top bits of the checksum to select a bucket index
41 """
42 return checksum >> (64 - int(math.log(num_buckets, 2)))
43
44def add_bucket_checksum(buckets, checksum):
45 """
46 Add the checksum to the correct bucket
47 """
48 index = bucket_index(len(buckets), checksum)
49 buckets[index] = add_checksum(buckets[index], checksum)
50
51def subtract_bucket_checksum(buckets, checksum):
52 """
53 Subtract the checksum from the correct bucket
54 """
55 index = bucket_index(len(buckets), checksum)
56 buckets[index] = subtract_checksum(buckets[index], checksum)
57
Rich Lane207599b2014-03-06 14:24:16 -080058class FlowChecksumBase(base_tests.SimpleProtocol):
59 """
60 Base class that maintains the expected table and bucket checksums
61 """
62 checksum_buckets = None
63 table_checksum = None
64 all_checksums = []
65
66 def get_table_checksum(self):
67 for entry in get_stats(self, ofp.message.bsn_table_checksum_stats_request()):
68 if entry.table_id == TABLE_ID:
69 return entry.checksum
70 return None
71
72 def get_checksum_buckets(self):
73 stats = get_stats(self,
74 ofp.message.bsn_flow_checksum_bucket_stats_request(table_id=TABLE_ID))
75 return [x.checksum for x in stats]
76
77 def verify_checksums(self):
78 self.assertEquals(self.get_table_checksum(), self.table_checksum)
79 self.assertEquals(self.get_checksum_buckets(), self.checksum_buckets)
80
Rich Lane207599b2014-03-06 14:24:16 -080081 def insert_checksum(self, checksum):
Rich Lane4ef81fd2014-06-30 17:16:07 -070082 self.table_checksum = add_checksum(self.table_checksum, checksum)
83 add_bucket_checksum(self.checksum_buckets, checksum)
Rich Lane207599b2014-03-06 14:24:16 -080084 self.all_checksums.append(checksum)
85
86 def remove_checksum(self, checksum):
Rich Lane4ef81fd2014-06-30 17:16:07 -070087 self.table_checksum = subtract_checksum(self.table_checksum, checksum)
88 subtract_bucket_checksum(self.checksum_buckets, checksum)
Rich Lane207599b2014-03-06 14:24:16 -080089 self.all_checksums.remove(checksum)
90
91 def set_buckets_size(self, buckets_size):
92 self.controller.message_send(
93 ofp.message.bsn_table_set_buckets_size(
94 table_id=TABLE_ID, buckets_size=buckets_size))
95 do_barrier(self.controller)
96 verify_no_errors(self.controller)
97
Rich Lane4ef81fd2014-06-30 17:16:07 -070098 old_checksums = self.all_checksums
99 self.all_checksums = []
Rich Lane207599b2014-03-06 14:24:16 -0800100 self.checksum_buckets = [0] * buckets_size
101 self.table_checksum = 0
Rich Lane4ef81fd2014-06-30 17:16:07 -0700102 for checksum in old_checksums:
103 self.insert_checksum(checksum)
Rich Lane207599b2014-03-06 14:24:16 -0800104
105class FlowChecksum(FlowChecksumBase):
106 """
107 Test flow checksum buckets and table checksums
108 """
109 def runTest(self):
110 delete_all_flows(self.controller)
111
112 # Deleted all flows, table checksum should be 0
113 self.assertEquals(self.get_table_checksum(), 0)
114
115 self.set_buckets_size(8)
116 self.verify_checksums()
117
118 # Interesting checksums
119 checksums = [
120 make_checksum(0, 1),
121 make_checksum(0, 2),
122 make_checksum(1, 0xab),
123 make_checksum(1, 0xab),
124 make_checksum(7, 0xff),
125 make_checksum(7, 0xaa),
126 ]
127
128 # Random checksums
129 for _ in xrange(0, 8):
130 checksums.append(random.randint(0, 2**64-1))
131
132 # Add flows in random order
Rich Lanea7331722014-03-11 13:28:25 -0700133 for i, checksum in shuffled(enumerate(checksums)):
Rich Lane207599b2014-03-06 14:24:16 -0800134 self.insert_checksum(checksum)
135 request = ofp.message.flow_add(
136 table_id=TABLE_ID,
137 cookie=checksum,
138 buffer_id=ofp.OFP_NO_BUFFER,
139 priority=i)
140 self.controller.message_send(request)
Rich Lanea7331722014-03-11 13:28:25 -0700141 do_barrier(self.controller)
142 verify_no_errors(self.controller)
143 self.verify_checksums()
Rich Lane207599b2014-03-06 14:24:16 -0800144
145 # Delete flows in random order
Rich Lanea7331722014-03-11 13:28:25 -0700146 for i, checksum in shuffled(enumerate(checksums)):
Rich Lane207599b2014-03-06 14:24:16 -0800147 self.remove_checksum(checksum)
148 request = ofp.message.flow_delete_strict(
149 table_id=TABLE_ID,
150 priority=i,
151 out_port=ofp.OFPP_ANY,
152 out_group=ofp.OFPG_ANY)
153 self.controller.message_send(request)
Rich Lanea7331722014-03-11 13:28:25 -0700154 do_barrier(self.controller)
155 verify_no_errors(self.controller)
156 self.verify_checksums()
Rich Lane207599b2014-03-06 14:24:16 -0800157
158 # Deleted all flows, table checksum should be 0
159 self.assertEquals(self.get_table_checksum(), 0)
160
161class Resize(FlowChecksumBase):
162 """
163 Resize the checksum buckets, checking limits and redistribution
164 """
165 def runTest(self):
166 delete_all_flows(self.controller)
167
168 self.assertEquals(self.get_table_checksum(), 0)
169
170 self.set_buckets_size(128)
171 self.verify_checksums()
172
173 checksums = [random.randint(0, 2**64-1) for _ in xrange(0, 128)]
174
175 # Add flows
176 for i, checksum in enumerate(checksums):
177 self.insert_checksum(checksum)
178 request = ofp.message.flow_add(
179 table_id=TABLE_ID,
180 cookie=checksum,
181 buffer_id=ofp.OFP_NO_BUFFER,
182 priority=i)
183 self.controller.message_send(request)
Rich Lanea7331722014-03-11 13:28:25 -0700184 if i % 17 == 0:
185 do_barrier(self.controller)
186 verify_no_errors(self.controller)
187 self.verify_checksums()
188
Rich Lane207599b2014-03-06 14:24:16 -0800189 do_barrier(self.controller)
190 verify_no_errors(self.controller)
191 self.verify_checksums()
192
193 # Shrink checksum buckets
194 self.set_buckets_size(64)
195 self.verify_checksums()
196
197 # Shrink checksum buckets to minimum
198 self.set_buckets_size(1)
199 self.verify_checksums()
200
201 # Grow checksum buckets
202 self.set_buckets_size(2)
203 self.verify_checksums()
204
205 # Grow checksum buckets
206 self.set_buckets_size(256)
207 self.verify_checksums()
208
209 # Grow checksum buckets to maximum
210 self.set_buckets_size(65536)
211 self.verify_checksums()
212
213 # Delete flows
214 for i, checksum in enumerate(checksums):
215 self.remove_checksum(checksum)
216 request = ofp.message.flow_delete_strict(
217 table_id=TABLE_ID,
218 priority=i,
219 out_port=ofp.OFPP_ANY,
220 out_group=ofp.OFPG_ANY)
221 self.controller.message_send(request)
Rich Lanea7331722014-03-11 13:28:25 -0700222 if i % 17 == 0:
223 do_barrier(self.controller)
224 verify_no_errors(self.controller)
225 self.verify_checksums()
226
Rich Lane207599b2014-03-06 14:24:16 -0800227 do_barrier(self.controller)
228 verify_no_errors(self.controller)
229 self.verify_checksums()
230
231 # Deleted all flows, table checksum should be 0
232 self.assertEquals(self.get_table_checksum(), 0)
233
234class ResizeError(FlowChecksumBase):
235 """
236 Check that the switch rejects invalid checksum buckets sizes
237 """
238 def runTest(self):
239 # buckets_size = 0
240 self.controller.message_send(
241 ofp.message.bsn_table_set_buckets_size(
242 table_id=TABLE_ID, buckets_size=0))
243 do_barrier(self.controller)
244 error, _ = self.controller.poll(ofp.OFPT_ERROR)
245 self.assertIsInstance(error, ofp.message.error_msg)
246
247 # buckets_size = 3
248 self.controller.message_send(
249 ofp.message.bsn_table_set_buckets_size(
250 table_id=TABLE_ID, buckets_size=3))
251 do_barrier(self.controller)
252 error, _ = self.controller.poll(ofp.OFPT_ERROR)
253 self.assertIsInstance(error, ofp.message.error_msg)
254
255 # buckets_size = 100
256 self.controller.message_send(
257 ofp.message.bsn_table_set_buckets_size(
258 table_id=TABLE_ID, buckets_size=100))
259 do_barrier(self.controller)
260 error, _ = self.controller.poll(ofp.OFPT_ERROR)
261 self.assertIsInstance(error, ofp.message.error_msg)
262
263 # buckets_size = 2**32 - 1
264 self.controller.message_send(
265 ofp.message.bsn_table_set_buckets_size(
266 table_id=TABLE_ID, buckets_size=2**32-1))
267 do_barrier(self.controller)
268 error, _ = self.controller.poll(ofp.OFPT_ERROR)
269 self.assertIsInstance(error, ofp.message.error_msg)
270
271 # buckets_size = 2**31
272 self.controller.message_send(
273 ofp.message.bsn_table_set_buckets_size(
274 table_id=TABLE_ID, buckets_size=2**31))
275 do_barrier(self.controller)
276 error, _ = self.controller.poll(ofp.OFPT_ERROR)
277 self.assertIsInstance(error, ofp.message.error_msg)
Rich Lane506673a2014-05-30 15:26:29 -0700278
279class TableChecksumIds(FlowChecksumBase):
280 """
281 Check that each table is represented in the table checksum stats reply
282 """
283 def runTest(self):
284 table_checksum_stats_ids = [x.table_id for x in get_stats(self, ofp.message.bsn_table_checksum_stats_request())]
285 table_stats_ids = [x.table_id for x in get_stats(self, ofp.message.table_stats_request())]
286 self.assertEquals(sorted(table_checksum_stats_ids), sorted(table_stats_ids))