blob: 7f2c5c8ef66f4d2991c030689659d66efe3b8b9a [file] [log] [blame]
Shudong Zhoudc276ae2012-10-15 16:02:56 -07001"""
Rich Lane5252a512013-04-04 13:19:22 -07002Test the Nicira controller role extension
Shudong Zhoudc276ae2012-10-15 16:02:56 -07003"""
4import struct
Rich Lanecf5dbde2013-03-28 13:53:05 -07005import unittest
Shudong Zhoudc276ae2012-10-15 16:02:56 -07006import logging
7
Rich Lane2c232ca2013-03-28 16:53:09 -07008import oftest
Shudong Zhoudc276ae2012-10-15 16:02:56 -07009from oftest import config
10import oftest.controller as controller
Rich Laned7b0ffa2013-03-08 15:53:42 -080011import ofp
Shudong Zhoudc276ae2012-10-15 16:02:56 -070012import oftest.base_tests as base_tests
13
14from oftest.testutils import *
15
Rich Lanecb816fd2013-03-26 21:53:47 -070016NX_ROLE_OTHER = 0
17NX_ROLE_MASTER = 1
18NX_ROLE_SLAVE = 2
Shudong Zhoudc276ae2012-10-15 16:02:56 -070019
Rich Lanecf5dbde2013-03-28 13:53:05 -070020def set_role(test, role, con=None):
21 if con == None:
22 con = test.controller
Rich Lanef41eea62013-03-27 16:05:43 -070023 request = ofp.message.nicira_controller_role_request(role=role)
Rich Lanecf5dbde2013-03-28 13:53:05 -070024 response, _ = con.transact(request)
Rich Lanef41eea62013-03-27 16:05:43 -070025 test.assertTrue(isinstance(response, ofp.message.nicira_controller_role_reply), "Expected a role reply")
26 test.assertEquals(response.role, role)
27
Rich Lanecb816fd2013-03-26 21:53:47 -070028class AnyReply(base_tests.SimpleDataPlane):
Shudong Zhoudc276ae2012-10-15 16:02:56 -070029 """
Rich Lanecb816fd2013-03-26 21:53:47 -070030 Verify that a role request gets either a role reply or an error.
Shudong Zhoudc276ae2012-10-15 16:02:56 -070031
Rich Lanecb816fd2013-03-26 21:53:47 -070032 This test should pass on any switch, no matter whether it implements
33 the extension.
34 """
Shudong Zhoudc276ae2012-10-15 16:02:56 -070035 def runTest(self):
Rich Lane4b601452013-03-11 23:37:06 -070036 request = ofp.message.nicira_controller_role_request(role=NX_ROLE_MASTER)
Shudong Zhoudc276ae2012-10-15 16:02:56 -070037 response, pkt = self.controller.transact(request)
38 self.assertTrue(response is not None, "No reply to Nicira role request")
Rich Lanecb816fd2013-03-26 21:53:47 -070039 if isinstance(response, ofp.message.nicira_controller_role_reply):
40 logging.info("Role reply received")
41 logging.info(response.show())
42 self.assertEquals(response.role, NX_ROLE_MASTER)
Rich Lanee226f042013-10-21 06:22:03 -070043 elif isinstance(response, ofp.message.bad_request_error_msg):
Rich Lanecb816fd2013-03-26 21:53:47 -070044 logging.info("Error message received")
45 logging.info(response.show())
Rich Lanecb816fd2013-03-26 21:53:47 -070046 self.assertEquals(response.code, ofp.OFPBRC_BAD_VENDOR)
47 else:
48 raise AssertionError("Unexpected reply type")
Rich Lanef41eea62013-03-27 16:05:43 -070049
50@nonstandard
51class RolePermissions(base_tests.SimpleDataPlane):
52 """
53 Verify that a slave connection cannot modify switch state, but
54 a master or equal can.
55 """
56 def runTest(self):
57 self.features_reply, _ = self.controller.transact(ofp.message.features_request())
58 delete_all_flows(self.controller)
59 self.verify_permission(True)
60
61 set_role(self, NX_ROLE_MASTER)
62 self.verify_permission(True)
63
64 set_role(self, NX_ROLE_SLAVE)
65 self.verify_permission(False)
66
67 set_role(self, NX_ROLE_OTHER)
68 self.verify_permission(True)
69
70 def verify_permission(self, perm):
71 port = self.features_reply.ports[0]
72
73 self.controller.message_send(ofp.message.port_mod(port_no=port.port_no, hw_addr=port.hw_addr))
74 self.controller.message_send(ofp.message.packet_out(buffer_id=0xffffffff))
75 self.controller.message_send(ofp.message.flow_add(buffer_id=0xffffffff))
76 do_barrier(self.controller)
77
78 err_count = 0
79 while self.controller.packets:
80 msg = self.controller.packets.pop(0)[0]
Rich Lanee226f042013-10-21 06:22:03 -070081 if msg.type == ofp.OFPT_ERROR:
Rich Lanef41eea62013-03-27 16:05:43 -070082 self.assertEquals(msg.err_type, ofp.OFPET_BAD_REQUEST)
83 self.assertEquals(msg.code, ofp.OFPBRC_EPERM)
84 err_count += 1
85
86 if perm:
87 self.assertEquals(err_count, 0, "Expected no errors")
88 else:
89 self.assertEquals(err_count, 3, "Expected errors for each message")
Rich Lane13c91592013-03-27 17:03:56 -070090
91@nonstandard
92class SlaveNoPacketIn(base_tests.SimpleDataPlane):
93 """
94 Verify that slave connections do not receive OFPT_PACKET_IN messages but other roles do.
95 """
96 def runTest(self):
97 delete_all_flows(self.controller)
Rich Lane4c504f32013-06-07 17:24:14 -070098 ingress_port = config["port_map"].keys()[0]
99 pkt = str(simple_tcp_packet())
Rich Lane13c91592013-03-27 17:03:56 -0700100
101 set_role(self, NX_ROLE_MASTER)
Rich Lane4c504f32013-06-07 17:24:14 -0700102 self.dataplane.send(ingress_port, pkt)
103 verify_packet_in(self, pkt, ingress_port, ofp.OFPR_NO_MATCH)
Rich Lane13c91592013-03-27 17:03:56 -0700104
105 set_role(self, NX_ROLE_SLAVE)
Rich Lane4c504f32013-06-07 17:24:14 -0700106 self.dataplane.send(ingress_port, pkt)
107 verify_no_packet_in(self, pkt, ingress_port)
Rich Lane13c91592013-03-27 17:03:56 -0700108
109 set_role(self, NX_ROLE_OTHER)
Rich Lane4c504f32013-06-07 17:24:14 -0700110 self.dataplane.send(ingress_port, pkt)
111 verify_packet_in(self, pkt, ingress_port, ofp.OFPR_NO_MATCH)
Rich Lanecf5dbde2013-03-28 13:53:05 -0700112
113@nonstandard
114@disabled
115class RoleSwitch(unittest.TestCase):
116 """
117 Verify that when a connection becomes a master the existing master is
118 downgraded to slave.
119
120 Requires the switch to attempt to connect in parallel to ports 6633
121 and 6634 on the configured IP.
122 """
123
124 def setUp(self):
125 host = config["controller_host"]
126 self.controllers = [
127 controller.Controller(host=host,port=6633),
128 controller.Controller(host=host,port=6634)
129 ]
130
131 def runTest(self):
132 # Connect and handshake with both controllers
133 for con in self.controllers:
134 con.start()
135 if not con.connect():
136 raise AssertionError("failed to connect controller %s" % str(con))
137 reply, _ = con.transact(ofp.message.features_request())
138 self.assertTrue(isinstance(reply, ofp.message.features_reply))
139
140 # Initial role assignment, controller 0 is master
141 set_role(self, NX_ROLE_MASTER, con=self.controllers[0])
142 set_role(self, NX_ROLE_SLAVE, con=self.controllers[1])
143 self.verify_role(self.controllers[0], True)
144 self.verify_role(self.controllers[1], False)
145
146 # Controller 1 becomes master
147 set_role(self, NX_ROLE_MASTER, con=self.controllers[1])
148 self.verify_role(self.controllers[0], False)
149 self.verify_role(self.controllers[1], True)
150
151 # Controller 0 becomes master
152 set_role(self, NX_ROLE_MASTER, con=self.controllers[0])
153 self.verify_role(self.controllers[0], True)
154 self.verify_role(self.controllers[1], False)
155
156 # Controller 1 becomes equal
157 set_role(self, NX_ROLE_OTHER, con=self.controllers[1])
158 self.verify_role(self.controllers[0], True)
159 self.verify_role(self.controllers[1], True)
160
161 # Both controllers become slaves
162 set_role(self, NX_ROLE_SLAVE, con=self.controllers[0])
163 set_role(self, NX_ROLE_SLAVE, con=self.controllers[1])
164 self.verify_role(self.controllers[0], False)
165 self.verify_role(self.controllers[1], False)
166
167 def verify_role(self, con, master):
168 con.message_send(ofp.message.flow_add(buffer_id=0xffffffff))
169 do_barrier(con)
170
171 err_count = 0
172 while con.packets:
173 msg = con.packets.pop(0)[0]
Rich Lanee226f042013-10-21 06:22:03 -0700174 if msg.type == ofp.OFPT_ERROR:
Rich Lanecf5dbde2013-03-28 13:53:05 -0700175 self.assertEquals(msg.err_type, ofp.OFPET_BAD_REQUEST)
176 self.assertEquals(msg.code, ofp.OFPBRC_EPERM)
177 err_count += 1
178
179 if master:
180 self.assertEquals(err_count, 0, "Expected no errors")
181 else:
182 self.assertEquals(err_count, 1, "Expected errors for each message")
183
184 def tearDown(self):
185 for con in self.controllers:
186 con.shutdown()
Rich Lane2c232ca2013-03-28 16:53:09 -0700187
188@nonstandard
189@disabled
190class EqualAsyncMessages(unittest.TestCase):
191 """
192 Verify that 'equal' controllers all get async events.
193
194 Requires the switch to attempt to connect in parallel to ports 6633
195 and 6634 on the configured IP.
196 """
197
198 def setUp(self):
199 host = config["controller_host"]
200 self.controllers = [
201 controller.Controller(host=host,port=6633),
202 controller.Controller(host=host,port=6634)
203 ]
204 self.dataplane = oftest.dataplane_instance
205 self.dataplane.flush()
206
207 def runTest(self):
208 # Connect and handshake with both controllers
209 for con in self.controllers:
210 con.start()
211 if not con.connect():
212 raise AssertionError("failed to connect controller %s" % str(con))
213 reply, _ = con.transact(ofp.message.features_request())
214 self.assertTrue(isinstance(reply, ofp.message.features_reply))
215
216 delete_all_flows(self.controllers[0])
217 do_barrier(self.controllers[0])
218
219 pkt = str(simple_tcp_packet())
220 ingress_port = config["port_map"].keys()[0]
221 self.dataplane.send(ingress_port, pkt)
222
223 for con in self.controllers:
Rich Lane4c504f32013-06-07 17:24:14 -0700224 verify_packet_in(self, pkt, ingress_port, ofp.OFPR_NO_MATCH, controller=con)
Rich Lane2c232ca2013-03-28 16:53:09 -0700225
226 def tearDown(self):
227 for con in self.controllers:
228 con.shutdown()
229 con.join()
230 del self.controllers