blob: a10d8b839899a188d7d850afe91ce3759644fa8b [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)
43 elif isinstance(response, ofp.message.error_msg):
44 logging.info("Error message received")
45 logging.info(response.show())
46 self.assertEquals(response.err_type, ofp.OFPET_BAD_REQUEST)
47 self.assertEquals(response.code, ofp.OFPBRC_BAD_VENDOR)
48 else:
49 raise AssertionError("Unexpected reply type")
Rich Lanef41eea62013-03-27 16:05:43 -070050
51@nonstandard
52class RolePermissions(base_tests.SimpleDataPlane):
53 """
54 Verify that a slave connection cannot modify switch state, but
55 a master or equal can.
56 """
57 def runTest(self):
58 self.features_reply, _ = self.controller.transact(ofp.message.features_request())
59 delete_all_flows(self.controller)
60 self.verify_permission(True)
61
62 set_role(self, NX_ROLE_MASTER)
63 self.verify_permission(True)
64
65 set_role(self, NX_ROLE_SLAVE)
66 self.verify_permission(False)
67
68 set_role(self, NX_ROLE_OTHER)
69 self.verify_permission(True)
70
71 def verify_permission(self, perm):
72 port = self.features_reply.ports[0]
73
74 self.controller.message_send(ofp.message.port_mod(port_no=port.port_no, hw_addr=port.hw_addr))
75 self.controller.message_send(ofp.message.packet_out(buffer_id=0xffffffff))
76 self.controller.message_send(ofp.message.flow_add(buffer_id=0xffffffff))
77 do_barrier(self.controller)
78
79 err_count = 0
80 while self.controller.packets:
81 msg = self.controller.packets.pop(0)[0]
82 if isinstance(msg, ofp.message.error_msg):
83 self.assertEquals(msg.err_type, ofp.OFPET_BAD_REQUEST)
84 self.assertEquals(msg.code, ofp.OFPBRC_EPERM)
85 err_count += 1
86
87 if perm:
88 self.assertEquals(err_count, 0, "Expected no errors")
89 else:
90 self.assertEquals(err_count, 3, "Expected errors for each message")
Rich Lane13c91592013-03-27 17:03:56 -070091
92@nonstandard
93class SlaveNoPacketIn(base_tests.SimpleDataPlane):
94 """
95 Verify that slave connections do not receive OFPT_PACKET_IN messages but other roles do.
96 """
97 def runTest(self):
98 delete_all_flows(self.controller)
99
100 set_role(self, NX_ROLE_MASTER)
101 self.verify_packetin(True)
102
103 set_role(self, NX_ROLE_SLAVE)
104 self.verify_packetin(False)
105
106 set_role(self, NX_ROLE_OTHER)
107 self.verify_packetin(True)
108
109 def verify_packetin(self, enabled):
110 ingress_port = config["port_map"].keys()[0]
111 self.dataplane.send(ingress_port, str(simple_tcp_packet()))
112
113 if enabled:
114 timeout = -1
115 else:
116 timeout = 0.5
117 msg, _ = self.controller.poll(exp_msg=ofp.OFPT_PACKET_IN, timeout=timeout)
118
119 if enabled:
120 self.assertTrue(msg != None, "Expected a packet-in message")
121 else:
122 self.assertTrue(msg == None, "Did not expect a packet-in message")
Rich Lanecf5dbde2013-03-28 13:53:05 -0700123
124@nonstandard
125@disabled
126class RoleSwitch(unittest.TestCase):
127 """
128 Verify that when a connection becomes a master the existing master is
129 downgraded to slave.
130
131 Requires the switch to attempt to connect in parallel to ports 6633
132 and 6634 on the configured IP.
133 """
134
135 def setUp(self):
136 host = config["controller_host"]
137 self.controllers = [
138 controller.Controller(host=host,port=6633),
139 controller.Controller(host=host,port=6634)
140 ]
141
142 def runTest(self):
143 # Connect and handshake with both controllers
144 for con in self.controllers:
145 con.start()
146 if not con.connect():
147 raise AssertionError("failed to connect controller %s" % str(con))
148 reply, _ = con.transact(ofp.message.features_request())
149 self.assertTrue(isinstance(reply, ofp.message.features_reply))
150
151 # Initial role assignment, controller 0 is master
152 set_role(self, NX_ROLE_MASTER, con=self.controllers[0])
153 set_role(self, NX_ROLE_SLAVE, con=self.controllers[1])
154 self.verify_role(self.controllers[0], True)
155 self.verify_role(self.controllers[1], False)
156
157 # Controller 1 becomes master
158 set_role(self, NX_ROLE_MASTER, con=self.controllers[1])
159 self.verify_role(self.controllers[0], False)
160 self.verify_role(self.controllers[1], True)
161
162 # Controller 0 becomes master
163 set_role(self, NX_ROLE_MASTER, con=self.controllers[0])
164 self.verify_role(self.controllers[0], True)
165 self.verify_role(self.controllers[1], False)
166
167 # Controller 1 becomes equal
168 set_role(self, NX_ROLE_OTHER, con=self.controllers[1])
169 self.verify_role(self.controllers[0], True)
170 self.verify_role(self.controllers[1], True)
171
172 # Both controllers become slaves
173 set_role(self, NX_ROLE_SLAVE, con=self.controllers[0])
174 set_role(self, NX_ROLE_SLAVE, con=self.controllers[1])
175 self.verify_role(self.controllers[0], False)
176 self.verify_role(self.controllers[1], False)
177
178 def verify_role(self, con, master):
179 con.message_send(ofp.message.flow_add(buffer_id=0xffffffff))
180 do_barrier(con)
181
182 err_count = 0
183 while con.packets:
184 msg = con.packets.pop(0)[0]
185 if isinstance(msg, ofp.message.error_msg):
186 self.assertEquals(msg.err_type, ofp.OFPET_BAD_REQUEST)
187 self.assertEquals(msg.code, ofp.OFPBRC_EPERM)
188 err_count += 1
189
190 if master:
191 self.assertEquals(err_count, 0, "Expected no errors")
192 else:
193 self.assertEquals(err_count, 1, "Expected errors for each message")
194
195 def tearDown(self):
196 for con in self.controllers:
197 con.shutdown()
Rich Lane2c232ca2013-03-28 16:53:09 -0700198
199@nonstandard
200@disabled
201class EqualAsyncMessages(unittest.TestCase):
202 """
203 Verify that 'equal' controllers all get async events.
204
205 Requires the switch to attempt to connect in parallel to ports 6633
206 and 6634 on the configured IP.
207 """
208
209 def setUp(self):
210 host = config["controller_host"]
211 self.controllers = [
212 controller.Controller(host=host,port=6633),
213 controller.Controller(host=host,port=6634)
214 ]
215 self.dataplane = oftest.dataplane_instance
216 self.dataplane.flush()
217
218 def runTest(self):
219 # Connect and handshake with both controllers
220 for con in self.controllers:
221 con.start()
222 if not con.connect():
223 raise AssertionError("failed to connect controller %s" % str(con))
224 reply, _ = con.transact(ofp.message.features_request())
225 self.assertTrue(isinstance(reply, ofp.message.features_reply))
226
227 delete_all_flows(self.controllers[0])
228 do_barrier(self.controllers[0])
229
230 pkt = str(simple_tcp_packet())
231 ingress_port = config["port_map"].keys()[0]
232 self.dataplane.send(ingress_port, pkt)
233
234 for con in self.controllers:
235 msg, _ = con.poll(ofp.OFPT_PACKET_IN)
236 self.assertTrue(msg != None)
237 self.assertEquals(msg.data, pkt)
238
239 def tearDown(self):
240 for con in self.controllers:
241 con.shutdown()
242 con.join()
243 del self.controllers