Matteo Scandolo | a229eca | 2017-08-08 13:05:28 -0700 | [diff] [blame^] | 1 | |
| 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 Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 17 | """ |
| 18 | Basic protocol and dataplane test cases |
| 19 | |
| 20 | It is recommended that these definitions be kept in their own |
| 21 | namespace as different groups of tests will likely define |
| 22 | similar identifiers. |
| 23 | """ |
| 24 | |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 25 | import logging |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 26 | |
| 27 | from oftest import config |
Rich Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 28 | import ofp |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 29 | import oftest.base_tests as base_tests |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 30 | import oftest.oft12.testutils as testutils |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 31 | |
| 32 | class 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 | |
| 39 | class EchoWithData(base_tests.SimpleProtocol): |
| 40 | """ |
| 41 | Test echo response with short string data |
| 42 | """ |
| 43 | def runTest(self): |
Rich Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 44 | request = ofp.message.echo_request() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 45 | request.data = 'OpenFlow Will Rule The World' |
| 46 | response, _ = self.controller.transact(request) |
Rich Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 47 | self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY, |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 48 | 'response is not echo_reply') |
Rich Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 49 | self.assertEqual(request.xid, response.xid, |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 50 | 'response xid != request xid') |
| 51 | self.assertEqual(request.data, response.data, |
| 52 | 'response data does not match request') |
| 53 | |
| 54 | class 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 Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 61 | request = ofp.message.features_request() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 62 | response,_ = self.controller.transact(request) |
| 63 | self.assertTrue(response,"Got no features_reply to features_request") |
Rich Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 64 | self.assertEqual(response.type, ofp.OFPT_FEATURES_REPLY, |
| 65 | 'response is not features_reply') |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 66 | |
| 67 | class 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 Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 87 | (response, _) = self.controller.poll(ofp.OFPT_PACKET_IN) |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 88 | |
| 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 | |
| 103 | class 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 Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 123 | msg = ofp.message.packet_out() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 124 | msg.in_port = ofp.OFPP_CONTROLLER |
Rich Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 125 | msg.buffer_id = 0xffffffff |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 126 | msg.data = str(outpkt) |
Rich Lane | 6339349 | 2013-01-11 09:21:12 -0800 | [diff] [blame] | 127 | act = ofp.action.output() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 128 | act.port = dp_port |
Rich Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 129 | msg.actions.append(act) |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 130 | |
| 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 | |
| 144 | class 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 Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 156 | request = ofp.message.flow_add() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 157 | 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 Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 166 | request = ofp.message.flow_stats_request() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 167 | 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 Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 172 | self.assertTrue(isinstance(response,ofp.message.flow_stats_reply),"Not a flow_stats_reply") |
Rich Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 173 | self.assertEqual(len(response.entries),0) |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 174 | logging.debug(response.show()) |
| 175 | |
| 176 | |
| 177 | |
| 178 | class 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 Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 187 | request = ofp.message.flow_add() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 188 | 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 | |
| 196 | class 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 Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 206 | request = ofp.message.table_stats_request() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 207 | response, _ = self.controller.transact(request, timeout=2) |
| 208 | self.assertTrue(response is not None, "Did not get response") |
| 209 | logging.debug(response.show()) |
| 210 | |
| 211 | class 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 Lane | 4b5537f | 2013-05-07 15:23:26 -0700 | [diff] [blame] | 220 | request = ofp.message.flow_add() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 221 | request.buffer_id = 0xffffffff |
| 222 | rv = self.controller.message_send(request) |
| 223 | self.assertTrue(rv != -1, "Error installing flow mod") |
| 224 | |
| 225 | class 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 | |
| 265 | class 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 Lane | 02eb6b0 | 2013-01-11 08:08:37 -0800 | [diff] [blame] | 273 | table_mod = ofp.message.table_mod() |
Rich Lane | 629393f | 2013-01-10 15:37:33 -0800 | [diff] [blame] | 274 | 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 | |
| 282 | if __name__ == "__main__": |
| 283 | print "Please run through oft script: ./oft --test_spec=basic" |