| """These tests fall under Conformance Test-Suite (OF-SWITCH-1.0.0 TestCases). |
| Refer Documentation -- Detailed testing methodology |
| <Some of test-cases are directly taken from oftest> """ |
| |
| "Test Suite 2 --> Openflow Protocol messages" |
| |
| |
| import logging |
| |
| import unittest |
| import random |
| |
| import oftest.controller as controller |
| import oftest.cstruct as ofp |
| import oftest.message as message |
| import oftest.dataplane as dataplane |
| import oftest.action as action |
| import oftest.parse as parse |
| import basic |
| |
| from oftest.testutils import * |
| from time import sleep |
| from FuncUtils import * |
| |
| |
| of_port_map = None |
| of_config = None |
| |
| def test_set_init(config): |
| |
| |
| basic.test_set_init(config) |
| |
| global of_port_map |
| global of_config |
| |
| of_port_map = config["port_map"] |
| of_config = config |
| |
| |
| class FeaturesRequest(basic.SimpleProtocol): |
| |
| """Verify Features_Request-Reply is implemented |
| a) Send OFPT_FEATURES_REQUEST |
| b) Verify OFPT_FEATURES_REPLY is received without errors""" |
| |
| def runTest(self): |
| logging.info("Running Features_Request test") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| |
| #Clear switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| logging.info("Sending Features_Request") |
| logging.info("Expecting Features_Reply") |
| |
| request = message.features_request() |
| rv = self.controller.message_send(request) |
| self.assertTrue(rv != -1, "Not able to send features request.") |
| |
| (response, pkt) = self.controller.poll(exp_msg=ofp.OFPT_FEATURES_REPLY, |
| timeout=2) |
| self.assertTrue(response is not None, |
| 'Did not receive Features Reply') |
| |
| |
| class ConfigurationRequest(basic.SimpleProtocol): |
| |
| """Check basic Get Config request is implemented |
| a) Send OFPT_GET_CONFIG_REQUEST |
| b) Verify OFPT_GET_CONFIG_REPLY is received without errors""" |
| |
| def runTest(self): |
| |
| logging.info("Running Configuration_Request test ") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| |
| #Clear switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| logging.info("Sending OFPT_GET_CONFIG_REQUEST ") |
| logging.info("Expecting OFPT_GET_CONFIG_REPLY ") |
| |
| request = message.get_config_request() |
| rv = self.controller.message_send(request) |
| self.assertTrue(rv != -1, " Not able to send get_config request.") |
| |
| (response, pkt) = self.controller.poll(exp_msg=ofp.OFPT_GET_CONFIG_REPLY, |
| timeout=2) |
| self.assertTrue(response is not None, |
| 'Did not receive OFPT_GET_CONFIG_REPLY') |
| |
| class ModifyStateAdd(basic.SimpleProtocol): |
| |
| """Check basic Flow Add request is implemented |
| a) Send OFPT_FLOW_MOD , command = OFPFC_ADD |
| c) Send ofp_table_stats request , verify active_count=1 in reply""" |
| |
| def runTest(self): |
| |
| logging.info("Running Modify_State_Add test") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| |
| #Clear switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| logging.info("Inserting a flow entry") |
| logging.info("Expecting active_count=1 in table_stats_reply") |
| |
| #Insert a flow entry matching on ingress_port |
| (Pkt,match) = Wildcard_All_Except_Ingress(self,of_ports) |
| |
| # Send Table_Stats_Request and verify flow gets inserted. |
| Verify_TableStats(self,active_entries=1) |
| |
| |
| class ModifyStateDelete(basic.SimpleProtocol): |
| |
| """Check Basic Flow Delete request is implemented |
| a) Send OFPT_FLOW_MOD, command = OFPFC_ADD |
| b) Send ofp_table_stats request , verify active_count=1 in reply |
| c) Send OFPT_FLOW_MOD, command = OFPFC_DELETE |
| c) Send ofp_table_stats request , verify active_count=0 in reply""" |
| |
| def runTest(self): |
| |
| logging.info("Running Modify_State_Delete test") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| |
| #Clear switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| logging.info("Inserting a flow entry and then deleting it") |
| logging.info("Expecting the active_count=0 in table_stats_reply") |
| |
| #Insert a flow matching on ingress_port |
| (Pkt,match) = Wildcard_All_Except_Ingress(self,of_ports) |
| |
| #Verify Flow inserted. |
| Verify_TableStats(self,active_entries=1) |
| |
| #Delete the flow |
| NonStrict_Delete(self,match) |
| |
| # Send Table_Stats_Request and verify flow deleted. |
| Verify_TableStats(self,active_entries=0) |
| |
| |
| |
| class ModifyStateModify(basic.SimpleDataPlane): |
| |
| """Verify basic Flow Modify request is implemented |
| a) Send OFPT_FLOW_MOD, command = OFPFC_ADD, Action A |
| b) Send OFPT_FLOW_MOD, command = OFPFC_MODIFY , with output action A' |
| c) Send a packet matching the flow, verify packet implements action A' """ |
| |
| def runTest(self): |
| |
| logging.info("Running Modify_State_Modify test") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| |
| #Clear switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| logging.info("Inserting a flow entry and then modifying it") |
| logging.info("Expecting the Test Packet to implement the modified action") |
| |
| # Insert a flow matching on ingress_port with action A (output to of_port[1]) |
| (pkt,match) = Wildcard_All_Except_Ingress(self,of_ports) |
| |
| # Modify the flow action (output to of_port[2]) |
| Modify_Flow_Action(self,of_ports,match) |
| |
| # Send the Test Packet and verify action implemented is A' (output to of_port[2]) |
| SendPacket(self,pkt,of_ports[0],of_ports[2]) |
| |
| |
| class ReadState(basic.SimpleProtocol): |
| |
| """Test that a basic Read state request (like flow_stats_get request) does not generate an error |
| a) Send OFPT_FLOW_MOD, command = OFPFC_ADD |
| b) Send ofp_flow_stats request |
| b) Verify switch replies without errors""" |
| |
| def runTest(self): |
| |
| logging.info("Running Read_State test") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| |
| #Clear switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| logging.info("Inserting a flow entry and then sending flow_stats request") |
| logging.info("Expecting the a flow_stats_reply without errors") |
| |
| # Insert a flow with match on ingress_port |
| (pkt,match ) = Wildcard_All_Except_Ingress(self,of_ports) |
| |
| #Verify Flow_Stats request does not generate errors |
| Verify_FlowStats(self,match) |
| |
| class PacketOut(basic.SimpleDataPlane): |
| |
| """Test packet out function |
| a) Send packet out message for each dataplane port. |
| b) Verify the packet appears on the appropriate dataplane port""" |
| |
| def runTest(self): |
| |
| logging.info("Running Packet_Out test") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| |
| #Clear Switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| logging.info("Sending a packet-out for each dataplane port") |
| logging.info("Expecting the packet on appropriate dataplane port") |
| |
| for dp_port in of_ports: |
| for outpkt, opt in [ |
| (simple_tcp_packet(), "simple TCP packet"), |
| (simple_eth_packet(), "simple Ethernet packet"), |
| (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]: |
| |
| msg = message.packet_out() |
| msg.data = str(outpkt) |
| act = action.action_output() |
| act.port = dp_port |
| self.assertTrue(msg.actions.add(act), 'Could not add action to msg') |
| |
| logging.info("PacketOut to: " + str(dp_port)) |
| rv = self.controller.message_send(msg) |
| self.assertTrue(rv == 0, "Error sending out message") |
| |
| exp_pkt_arg = None |
| exp_port = None |
| if of_config["relax"]: |
| exp_pkt_arg = outpkt |
| exp_port = dp_port |
| (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=2, |
| port_number=exp_port, |
| exp_pkt=exp_pkt_arg) |
| |
| self.assertTrue(pkt is not None, 'Packet not received') |
| logging.info("PacketOut: got pkt from " + str(of_port)) |
| if of_port is not None: |
| self.assertEqual(of_port, dp_port, "Unexpected receive port") |
| if not dataplane.match_exp_pkt(outpkt, pkt): |
| logging.debug("Sent %s" % format_packet(outpkt)) |
| logging.debug("Resp %s" % format_packet( |
| str(pkt)[:len(str(outpkt))])) |
| self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))], |
| 'Response packet does not match send packet') |
| |
| |
| class PacketIn(basic.SimpleDataPlane): |
| |
| """Test basic packet_in function |
| a) Send a simple tcp packet to a dataplane port, without any flow-entry |
| b) Verify that a packet_in event is sent to the controller""" |
| |
| def runTest(self): |
| |
| logging.info("Running Packet_In test") |
| |
| of_ports = of_port_map.keys() |
| of_ports.sort() |
| ingress_port = of_ports[0] |
| |
| #Clear Switch state |
| rc = delete_all_flows(self.controller) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| self.assertEqual(do_barrier(self.controller), 0, "Barrier failed") |
| |
| logging.info("Sending a Simple tcp packet a dataplane port") |
| logging.info("Expecting a packet_in event on the control plane") |
| |
| # Send packet on dataplane port and verify packet_in event gets generated. |
| pkt = simple_tcp_packet() |
| self.dataplane.send(ingress_port, str(pkt)) |
| logging.info("Sending packet to dp port " + str(ingress_port) + |
| ", expecting packet_in on control plane" ) |
| |
| (response, pkt) = self.controller.poll(exp_msg=ofp.OFPT_PACKET_IN, |
| timeout=2) |
| self.assertTrue(response is not None, |
| 'Packet in event is not sent to the controller') |
| |
| |
| class Hello(basic.SimpleDataPlane): |
| |
| """Test Hello messages are implemented |
| a) Create Hello messages from controller |
| b) Verify switch also exchanges hello message -- (Poll the control plane) |
| d) Verify the version field in the hello messages is openflow 1.0.0 """ |
| |
| def runTest(self): |
| |
| logging.info("Running Hello test") |
| |
| logging.info("Sending Hello") |
| logging.info("Expecting a Hello on the control plane with version--1.0.0") |
| |
| #Send Hello message |
| request = message.hello() |
| (response, pkt) = self.controller.poll(exp_msg=ofp.OFPT_HELLO, |
| timeout=1) |
| self.assertTrue(response is not None, |
| 'Switch did not exchange hello message in return') |
| self.assertTrue(response.header.version == 0x01, 'switch openflow-version field is not 1.0.0') |
| |
| |
| |
| class EchoWithoutBody(basic.SimpleProtocol): |
| |
| """Test basic echo-reply is implemented |
| a) Send echo-request from the controller side, note echo body is empty here. |
| b) Verify switch responds back with echo-reply with same xid """ |
| |
| def runTest(self): |
| |
| logging.info("Running Echo_Without_Body test") |
| |
| logging.info("Sending Echo Request") |
| logging.info("Expecting a Echo Reply with version--1.0.0 and same xid") |
| |
| # Send echo_request |
| request = message.echo_request() |
| (response, pkt) = self.controller.transact(request) |
| self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,'response is not echo_reply') |
| self.assertEqual(request.header.xid, response.header.xid, |
| 'response xid != request xid') |
| self.assertTrue(response.header.version == 0x01, 'switch openflow-version field is not 1.0.1') |
| self.assertEqual(len(response.data), 0, 'response data non-empty') |
| |
| |
| class BarrierRequestReply(basic.SimpleProtocol): |
| |
| """ Check basic Barrier request is implemented |
| a) Send OFPT_BARRIER_REQUEST |
| c) Verify OFPT_BARRIER_REPLY is recieved""" |
| |
| def runTest(self): |
| |
| logging.info("Running Barrier_Request_Reply test") |
| |
| logging.info("Sending Barrier Request") |
| logging.info("Expecting a Barrier Reply with same xid") |
| |
| #Send Barrier Request |
| request = message.barrier_request() |
| (response, pkt) = self.controller.transact(request) |
| self.assertEqual(response.header.type, ofp.OFPT_BARRIER_REPLY,'response is not barrier_reply') |
| self.assertEqual(request.header.xid, response.header.xid, |
| 'response xid != request xid') |
| |
| |
| |
| |
| |