Merge into master from pull request #83:
OpenFlow 1.3 role request tests (https://github.com/floodlight/oftest/pull/83)
diff --git a/tests-1.3/role_request.py b/tests-1.3/role_request.py
new file mode 100644
index 0000000..f265bca
--- /dev/null
+++ b/tests-1.3/role_request.py
@@ -0,0 +1,363 @@
+"""
+Test the role request message
+"""
+import struct
+import unittest
+import logging
+
+import oftest
+from oftest import config
+import oftest.controller as controller
+import ofp
+import oftest.base_tests as base_tests
+
+from oftest.testutils import *
+
+def add_mod64(a, b):
+ return (a + b) & (2**64-1)
+
+def simple_role_request(test, role, gen=None, con=None):
+ """
+ Send a role request we expect to succeed
+ """
+ if con == None:
+ con = test.controller
+ request = ofp.message.role_request(role=role, generation_id=gen)
+ response, _ = con.transact(request)
+ test.assertTrue(isinstance(response, ofp.message.role_reply), "Expected a role reply")
+ if role != ofp.OFPCR_ROLE_NOCHANGE:
+ test.assertEquals(response.role, role)
+ if gen != None:
+ test.assertEquals(response.generation_id, gen)
+ return response.role, response.generation_id
+
+def failed_role_request(test, role, gen, code, con=None):
+ """
+ Send a role request we expect to fail
+ """
+ if con == None:
+ con = test.controller
+ request = ofp.message.role_request(role=role, generation_id=gen)
+ response, _ = con.transact(request)
+ test.assertIsInstance(response, ofp.message.role_request_failed_error_msg)
+ test.assertEqual(response.code, code)
+
+class RoleRequestNochange(base_tests.SimpleDataPlane):
+ """
+ Verify that we can query the switch for our current role and generation ID
+
+ The role should default to OFPCR_ROLE_EQUAL.
+ """
+ def runTest(self):
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+
+ # Make sure the generation ID is still the same
+ role, new_gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+ self.assertEqual(new_gen, gen)
+
+class RoleRequestEqualToSlave(base_tests.SimpleDataPlane):
+ """
+ Transition between equal and slave roles and back
+ """
+ def runTest(self):
+ role, gen0 = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+
+ # Unchanged generation ID
+ role, gen1 = simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen0)
+
+ # Back to equal
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL)
+
+ # Smallest greater generation ID
+ role, gen2 = simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, add_mod64(gen1, 1))
+
+ # Back to equal
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL)
+
+ # Largest greater generation ID
+ role, gen3 = simple_role_request(self, ofp.OFPCR_ROLE_SLAVE,
+ add_mod64(gen2, 2**63-1))
+
+ # Back to equal
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL)
+
+ # Send least stale generation ID
+ failed_role_request(self, ofp.OFPCR_ROLE_SLAVE,
+ add_mod64(gen3, -1),
+ ofp.OFPRRFC_STALE)
+
+ # Check that our role is unchanged
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+ self.assertEqual(gen, gen3)
+
+ # Send most stale generation ID
+ failed_role_request(self, ofp.OFPCR_ROLE_SLAVE,
+ add_mod64(gen3, -(2**63)),
+ ofp.OFPRRFC_STALE)
+
+ # Check that our role is unchanged
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+ self.assertEqual(gen, gen3)
+
+class RoleRequestEqualToMaster(base_tests.SimpleDataPlane):
+ """
+ Transition between equal and master roles and back
+ """
+ def runTest(self):
+ role, gen0 = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+
+ # Unchanged generation ID
+ role, gen1 = simple_role_request(self, ofp.OFPCR_ROLE_MASTER, gen0)
+
+ # Back to equal
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL)
+
+ # Smallest greater generation ID
+ role, gen2 = simple_role_request(self, ofp.OFPCR_ROLE_MASTER, add_mod64(gen1, 1))
+
+ # Back to equal
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL)
+
+ # Largest greater generation ID
+ role, gen3 = simple_role_request(self, ofp.OFPCR_ROLE_MASTER,
+ add_mod64(gen2, 2**63-1))
+
+ # Back to equal
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL)
+
+ # Send least stale generation ID
+ failed_role_request(self, ofp.OFPCR_ROLE_MASTER,
+ add_mod64(gen3, -1),
+ ofp.OFPRRFC_STALE)
+
+ # Check that our role is unchanged
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+ self.assertEqual(gen, gen3)
+
+ # Send most stale generation ID
+ failed_role_request(self, ofp.OFPCR_ROLE_MASTER,
+ add_mod64(gen3, -(2**63)),
+ ofp.OFPRRFC_STALE)
+
+ # Check that our role is unchanged
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+ self.assertEqual(gen, gen3)
+
+class RoleRequestSlaveToMaster(base_tests.SimpleDataPlane):
+ """
+ Transition between slave and master roles and back
+ """
+ def runTest(self):
+ role, gen0 = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+
+ # Initial transition to slave
+ role, gen1 = simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen0)
+
+ # Unchanged generation ID
+ role, gen2 = simple_role_request(self, ofp.OFPCR_ROLE_MASTER, gen1)
+
+ # Back to slave
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen2)
+
+ # Smallest greater generation ID
+ role, gen3 = simple_role_request(self, ofp.OFPCR_ROLE_MASTER,
+ add_mod64(gen2, 1))
+
+ # Back to slave
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen3)
+
+ # Largest greater generation ID
+ role, gen4 = simple_role_request(self, ofp.OFPCR_ROLE_MASTER,
+ add_mod64(gen3, 2**63-1))
+
+ # Back to slave
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen4)
+
+ # Send least stale generation ID
+ failed_role_request(self, ofp.OFPCR_ROLE_MASTER,
+ add_mod64(gen4, -1),
+ ofp.OFPRRFC_STALE)
+
+ # Check that our role is unchanged
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_SLAVE)
+ self.assertEqual(gen, gen4)
+
+ # Send most stale generation ID
+ failed_role_request(self, ofp.OFPCR_ROLE_MASTER,
+ add_mod64(gen4, -(2**63)),
+ ofp.OFPRRFC_STALE)
+
+ # Check that our role is unchanged
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+ self.assertEqual(role, ofp.OFPCR_ROLE_SLAVE)
+ self.assertEqual(gen, gen4)
+
+class RolePermissions(base_tests.SimpleDataPlane):
+ """
+ Verify that a slave connection cannot modify switch state, but
+ a master or equal can.
+ """
+ def runTest(self):
+ delete_all_flows(self.controller)
+ self.verify_permission(True)
+
+ role, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+
+ simple_role_request(self, ofp.OFPCR_ROLE_MASTER, gen)
+ self.verify_permission(True)
+
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen)
+ self.verify_permission(False)
+
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL)
+ self.verify_permission(True)
+
+ def verify_permission(self, perm):
+ self.controller.message_send(
+ ofp.message.packet_out(buffer_id=ofp.OFP_NO_BUFFER))
+
+ self.controller.message_send(
+ ofp.message.flow_delete(
+ buffer_id=ofp.OFP_NO_BUFFER,
+ out_port=ofp.OFPP_ANY,
+ out_group=ofp.OFPG_ANY))
+
+ self.controller.message_send(
+ ofp.message.group_mod(
+ command=ofp.OFPGC_DELETE,
+ group_id=ofp.OFPG_ALL))
+
+ # TODO OFPT_PORT_MOD
+ # TODO OFPT_TABLE_MOD
+
+ do_barrier(self.controller)
+
+ err_count = 0
+ while self.controller.packets:
+ msg = self.controller.packets.pop(0)[0]
+ if msg.type == ofp.OFPT_ERROR:
+ self.assertEquals(msg.err_type, ofp.OFPET_BAD_REQUEST)
+ self.assertEquals(msg.code, ofp.OFPBRC_IS_SLAVE)
+ err_count += 1
+
+ if perm:
+ self.assertEquals(err_count, 0, "Expected no errors")
+ else:
+ self.assertEquals(err_count, 3, "Expected errors for each message")
+
+class SlaveNoPacketIn(base_tests.SimpleDataPlane):
+ """
+ Verify that slave connections do not receive OFPT_PACKET_IN messages but other roles do.
+ """
+ def runTest(self):
+ delete_all_flows(self.controller)
+ ingress_port, = openflow_ports(1)
+ pkt = str(simple_tcp_packet())
+
+ logging.info("Inserting table-miss flow sending all packets to controller")
+ request = ofp.message.flow_add(
+ table_id=test_param_get("table", 0),
+ instructions=[
+ ofp.instruction.apply_actions(
+ actions=[
+ ofp.action.output(
+ port=ofp.OFPP_CONTROLLER,
+ max_len=ofp.OFPCML_NO_BUFFER)])],
+ buffer_id=ofp.OFP_NO_BUFFER,
+ priority=0)
+ self.controller.message_send(request)
+ do_barrier(self.controller)
+
+ _, gen = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE)
+
+ simple_role_request(self, ofp.OFPCR_ROLE_MASTER, gen)
+ self.dataplane.send(ingress_port, pkt)
+ verify_packet_in(self, pkt, ingress_port, ofp.OFPR_NO_MATCH)
+
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen)
+ self.dataplane.send(ingress_port, pkt)
+ verify_no_packet_in(self, pkt, ingress_port)
+
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL, gen)
+ self.dataplane.send(ingress_port, pkt)
+ verify_packet_in(self, pkt, ingress_port, ofp.OFPR_NO_MATCH)
+
+@disabled
+class RoleSwitch(unittest.TestCase):
+ """
+ Verify that when a connection becomes a master the existing master is
+ downgraded to slave.
+
+ Requires the switch to attempt to connect in parallel to ports 6653
+ and 6753 on the configured IP.
+ """
+
+ def setUp(self):
+ host = config["controller_host"]
+ self.controllers = [
+ controller.Controller(host=host,port=6653),
+ controller.Controller(host=host,port=6753)
+ ]
+
+ def runTest(self):
+ # Connect and handshake with both controllers
+ for con in self.controllers:
+ con.start()
+ if not con.connect():
+ raise AssertionError("failed to connect controller %s" % str(con))
+ reply, _ = con.transact(ofp.message.features_request())
+ self.assertTrue(isinstance(reply, ofp.message.features_reply))
+
+ # Assert initial role and get generation IDs
+ role, gen0 = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE, con=self.controllers[0])
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+ role, gen1 = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE, con=self.controllers[1])
+ self.assertEqual(role, ofp.OFPCR_ROLE_EQUAL)
+
+ # Initial role assignment: controller 0 is master, controller 1 is slave
+ simple_role_request(self, ofp.OFPCR_ROLE_MASTER, gen0, con=self.controllers[0])
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen1, con=self.controllers[1])
+ self.verify_role(self.controllers[0], ofp.OFPCR_ROLE_MASTER)
+ self.verify_role(self.controllers[1], ofp.OFPCR_ROLE_SLAVE)
+
+ # Controller 1 requests master
+ # Controller 0 becomes slave
+ simple_role_request(self, ofp.OFPCR_ROLE_MASTER, gen1, con=self.controllers[1])
+ self.verify_role(self.controllers[0], ofp.OFPCR_ROLE_SLAVE)
+ self.verify_role(self.controllers[1], ofp.OFPCR_ROLE_MASTER)
+
+ # Controller 0 requests master
+ # Controller 1 becomes slave
+ simple_role_request(self, ofp.OFPCR_ROLE_MASTER, gen0, con=self.controllers[0])
+ self.verify_role(self.controllers[0], ofp.OFPCR_ROLE_MASTER)
+ self.verify_role(self.controllers[1], ofp.OFPCR_ROLE_SLAVE)
+
+ # Controller 1 requests equal
+ # Controller 0 remains master
+ simple_role_request(self, ofp.OFPCR_ROLE_EQUAL, gen1, con=self.controllers[1])
+ self.verify_role(self.controllers[0], ofp.OFPCR_ROLE_MASTER)
+ self.verify_role(self.controllers[1], ofp.OFPCR_ROLE_EQUAL)
+
+ # Both controllers request slave
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen0, con=self.controllers[0])
+ simple_role_request(self, ofp.OFPCR_ROLE_SLAVE, gen1, con=self.controllers[1])
+ self.verify_role(self.controllers[0], ofp.OFPCR_ROLE_SLAVE)
+ self.verify_role(self.controllers[1], ofp.OFPCR_ROLE_SLAVE)
+
+ def verify_role(self, con, role):
+ rcv_role, _ = simple_role_request(self, ofp.OFPCR_ROLE_NOCHANGE, con=con)
+ self.assertEqual(rcv_role, role)
+
+ def tearDown(self):
+ for con in self.controllers:
+ con.shutdown()