blob: 31d81e22151a07787fa853c92858212e48d89c46 [file] [log] [blame]
Matteo Scandoloa229eca2017-08-08 13:05:28 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Rich Lane629393f2013-01-10 15:37:33 -080017"""
18Basic protocol and dataplane test cases
19
20It is recommended that these definitions be kept in their own
21namespace as different groups of tests will likely define
22similar identifiers.
23"""
24
Rich Lane629393f2013-01-10 15:37:33 -080025import logging
Rich Lane629393f2013-01-10 15:37:33 -080026
27from oftest import config
Rich Lane02eb6b02013-01-11 08:08:37 -080028import ofp
Rich Lane629393f2013-01-10 15:37:33 -080029import oftest.base_tests as base_tests
Rich Lane629393f2013-01-10 15:37:33 -080030import oftest.oft12.testutils as testutils
Rich Lane629393f2013-01-10 15:37:33 -080031
32class Echo(base_tests.SimpleProtocol):
33 """
34 Test echo response with no data
35 """
36 def runTest(self):
37 testutils.do_echo_request_reply_test(self, self.controller)
38
39class EchoWithData(base_tests.SimpleProtocol):
40 """
41 Test echo response with short string data
42 """
43 def runTest(self):
Rich Lane02eb6b02013-01-11 08:08:37 -080044 request = ofp.message.echo_request()
Rich Lane629393f2013-01-10 15:37:33 -080045 request.data = 'OpenFlow Will Rule The World'
46 response, _ = self.controller.transact(request)
Rich Lane4b5537f2013-05-07 15:23:26 -070047 self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
Rich Lane629393f2013-01-10 15:37:33 -080048 'response is not echo_reply')
Rich Lane4b5537f2013-05-07 15:23:26 -070049 self.assertEqual(request.xid, response.xid,
Rich Lane629393f2013-01-10 15:37:33 -080050 'response xid != request xid')
51 self.assertEqual(request.data, response.data,
52 'response data does not match request')
53
54class FeaturesRequest(base_tests.SimpleProtocol):
55 """
56 Test features_request to make sure we get a response
57
58 Does NOT test the contents; just that we get a response
59 """
60 def runTest(self):
Rich Lane02eb6b02013-01-11 08:08:37 -080061 request = ofp.message.features_request()
Rich Lane629393f2013-01-10 15:37:33 -080062 response,_ = self.controller.transact(request)
63 self.assertTrue(response,"Got no features_reply to features_request")
Rich Lane4b5537f2013-05-07 15:23:26 -070064 self.assertEqual(response.type, ofp.OFPT_FEATURES_REPLY,
65 'response is not features_reply')
Rich Lane629393f2013-01-10 15:37:33 -080066
67class PacketIn(base_tests.SimpleDataPlane):
68 """
69 Test packet in function
70
71 Send a packet to each dataplane port and verify that a packet
72 in message is received from the controller for each
73 """
74 def runTest(self):
75 # Construct packet to send to dataplane
76 # Send packet to dataplane, once to each port
77 # Poll controller with expect message type packet in
78
79 rc = testutils.delete_all_flows(self.controller, logging)
80 self.assertEqual(rc, 0, "Failed to delete all flows")
81
82 for of_port in config["port_map"].keys():
83 logging.info("PKT IN test, port " + str(of_port))
84 pkt = testutils.simple_tcp_packet()
85 self.dataplane.send(of_port, str(pkt))
86 #@todo Check for unexpected messages?
Rich Lane4b5537f2013-05-07 15:23:26 -070087 (response, _) = self.controller.poll(ofp.OFPT_PACKET_IN)
Rich Lane629393f2013-01-10 15:37:33 -080088
89 self.assertTrue(response is not None,
90 'Packet in message not received on port ' +
91 str(of_port))
92 if str(pkt) != response.data:
93 logging.debug("pkt len " + str(len(str(pkt))) +
94 ": " + str(pkt))
95 logging.debug("resp len " +
96 str(len(str(response.data))) +
97 ": " + str(response.data))
98
99 self.assertEqual(str(pkt), response.data,
100 'Response packet does not match send packet' +
101 ' for port ' + str(of_port))
102
103class PacketOut(base_tests.SimpleDataPlane):
104 """
105 Test packet out function
106
107 Send packet out message to controller for each dataplane port and
108 verify the packet appears on the appropriate dataplane port
109 """
110 def runTest(self):
111 # Construct packet to send to dataplane
112 # Send packet to dataplane
113 # Poll controller with expect message type packet in
114
115 rc = testutils.delete_all_flows(self.controller, logging)
116 self.assertEqual(rc, 0, "Failed to delete all flows")
117
118 # These will get put into function
119 outpkt = testutils.simple_tcp_packet()
120 of_ports = config["port_map"].keys()
121 of_ports.sort()
122 for dp_port in of_ports:
Rich Lane02eb6b02013-01-11 08:08:37 -0800123 msg = ofp.message.packet_out()
Rich Lane629393f2013-01-10 15:37:33 -0800124 msg.in_port = ofp.OFPP_CONTROLLER
Rich Lane4b5537f2013-05-07 15:23:26 -0700125 msg.buffer_id = 0xffffffff
Rich Lane629393f2013-01-10 15:37:33 -0800126 msg.data = str(outpkt)
Rich Lane63393492013-01-11 09:21:12 -0800127 act = ofp.action.output()
Rich Lane629393f2013-01-10 15:37:33 -0800128 act.port = dp_port
Rich Lane4b5537f2013-05-07 15:23:26 -0700129 msg.actions.append(act)
Rich Lane629393f2013-01-10 15:37:33 -0800130
131 logging.info("PacketOut to: " + str(dp_port))
132 rv = self.controller.message_send(msg)
133 self.assertTrue(rv == 0, "Error sending out message")
134
135 (of_port, pkt, _) = self.dataplane.poll(timeout=1)
136
137 self.assertTrue(pkt is not None, 'Packet not received')
138 logging.info("PacketOut: got pkt from " + str(of_port))
139 if of_port is not None:
140 self.assertEqual(of_port, dp_port, "Unexpected receive port")
141 self.assertEqual(str(outpkt), str(pkt),
142 'Response packet does not match send packet')
143
144class FlowRemoveAll(base_tests.SimpleProtocol):
145 """
146 Remove all flows; required for almost all tests
147
148 Add a bunch of flows, remove them, and then make sure there are no flows left
149 This is an intentionally naive test to see if the baseline functionality works
150 and should be a precondition to any more complicated deletion test (e.g.,
151 delete_strict vs. delete)
152 """
153 def runTest(self):
154 logging.info("Running StatsGet")
155 logging.info("Inserting trial flow")
Rich Lane4b5537f2013-05-07 15:23:26 -0700156 request = ofp.message.flow_add()
Rich Lane629393f2013-01-10 15:37:33 -0800157 request.buffer_id = 0xffffffff
158 for i in range(1,5):
159 request.priority = i*1000
160 logging.debug("Adding flow %d" % i)
161 rv = self.controller.message_send(request)
162 self.assertTrue(rv != -1, "Failed to insert test flow %d" % i)
163 logging.info("Removing all flows")
164 testutils.delete_all_flows(self.controller, logging)
165 logging.info("Sending flow request")
Rich Lane02eb6b02013-01-11 08:08:37 -0800166 request = ofp.message.flow_stats_request()
Rich Lane629393f2013-01-10 15:37:33 -0800167 request.out_port = ofp.OFPP_ANY
168 request.out_group = ofp.OFPG_ANY
169 request.table_id = 0xff
170 response, _ = self.controller.transact(request, timeout=2)
171 self.assertTrue(response is not None, "Did not get response")
Rich Lane02eb6b02013-01-11 08:08:37 -0800172 self.assertTrue(isinstance(response,ofp.message.flow_stats_reply),"Not a flow_stats_reply")
Rich Lane4b5537f2013-05-07 15:23:26 -0700173 self.assertEqual(len(response.entries),0)
Rich Lane629393f2013-01-10 15:37:33 -0800174 logging.debug(response.show())
175
176
177
178class FlowStatsGet(base_tests.SimpleProtocol):
179 """
180 Get stats
181
182 Simply verify stats get transaction
183 """
184 def runTest(self):
185 logging.info("Running StatsGet")
186 logging.info("Inserting trial flow")
Rich Lane4b5537f2013-05-07 15:23:26 -0700187 request = ofp.message.flow_add()
Rich Lane629393f2013-01-10 15:37:33 -0800188 request.buffer_id = 0xffffffff
189 rv = self.controller.message_send(request)
190 self.assertTrue(rv != -1, "Failed to insert test flow")
191
192 logging.info("Sending flow request")
193 response = testutils.flow_stats_get(self)
194 logging.debug(response.show())
195
196class TableStatsGet(base_tests.SimpleProtocol):
197 """
198 Get table stats
199
200 Naively verify that we get a reply
201 do better sanity check of data in stats.TableStats test
202 """
203 def runTest(self):
204 logging.info("Running TableStatsGet")
205 logging.info("Sending table stats request")
Rich Lane02eb6b02013-01-11 08:08:37 -0800206 request = ofp.message.table_stats_request()
Rich Lane629393f2013-01-10 15:37:33 -0800207 response, _ = self.controller.transact(request, timeout=2)
208 self.assertTrue(response is not None, "Did not get response")
209 logging.debug(response.show())
210
211class FlowMod(base_tests.SimpleProtocol):
212 """
213 Insert a flow
214
215 Simple verification of a flow mod transaction
216 """
217
218 def runTest(self):
219 logging.info("Running " + str(self))
Rich Lane4b5537f2013-05-07 15:23:26 -0700220 request = ofp.message.flow_add()
Rich Lane629393f2013-01-10 15:37:33 -0800221 request.buffer_id = 0xffffffff
222 rv = self.controller.message_send(request)
223 self.assertTrue(rv != -1, "Error installing flow mod")
224
225class PortConfigMod(base_tests.SimpleProtocol):
226 """
227 Modify a bit in port config and verify changed
228
229 Get the switch configuration, modify the port configuration
230 and write it back; get the config again and verify changed.
231 Then set it back to the way it was.
232 """
233
234 def runTest(self):
235 logging.info("Running " + str(self))
236 for of_port, _ in config["port_map"].items(): # Grab first port
237 break
238
239 (_, port_config, _) = \
240 testutils.port_config_get(self.controller, of_port, logging)
241 self.assertTrue(port_config is not None, "Did not get port config")
242
243 logging.debug("No flood bit port " + str(of_port) + " is now " +
244 str(port_config & ofp.OFPPC_NO_PACKET_IN))
245
246 rv = testutils.port_config_set(self.controller, of_port,
247 port_config ^ ofp.OFPPC_NO_PACKET_IN, ofp.OFPPC_NO_PACKET_IN,
248 logging)
249 self.assertTrue(rv != -1, "Error sending port mod")
250
251 # Verify change took place with same feature request
252 (_, port_config2, _) = \
253 testutils.port_config_get(self.controller, of_port, logging)
254 logging.debug("No packet_in bit port " + str(of_port) + " is now " +
255 str(port_config2 & ofp.OFPPC_NO_PACKET_IN))
256 self.assertTrue(port_config2 is not None, "Did not get port config2")
257 self.assertTrue(port_config2 & ofp.OFPPC_NO_PACKET_IN !=
258 port_config & ofp.OFPPC_NO_PACKET_IN,
259 "Bit change did not take")
260 # Set it back
261 rv = testutils.port_config_set(self.controller, of_port, port_config,
262 ofp.OFPPC_NO_PACKET_IN, logging)
263 self.assertTrue(rv != -1, "Error sending port mod")
264
265class TableModConfig(base_tests.SimpleProtocol):
266 """ Simple table modification
267
268 Mostly to make sure the switch correctly responds to these messages.
269 More complicated tests in the multi-tables.py tests
270 """
271 def runTest(self):
272 logging.info("Running " + str(self))
Rich Lane02eb6b02013-01-11 08:08:37 -0800273 table_mod = ofp.message.table_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800274 table_mod.table_id = 0 # first table should always exist
275 table_mod.config = ofp.OFPTC_TABLE_MISS_CONTROLLER
276
277 rv = self.controller.message_send(table_mod)
278 self.assertTrue(rv != -1, "Error sending table_mod")
279 testutils.do_echo_request_reply_test(self, self.controller)
280
281
282if __name__ == "__main__":
283 print "Please run through oft script: ./oft --test_spec=basic"