blob: 8ae5a46194c9951e861877774fa967d461296364 [file] [log] [blame]
Ken Chiang20a3da52012-07-11 09:13:57 -07001"""
2Serial failover test cases
3
4"""
5
6import time
7import signal
8import sys
9import logging
10
11import unittest
12import random
13
14import oftest.controller as controller
15import oftest.cstruct as ofp
16import oftest.message as message
17import oftest.dataplane as dataplane
18import oftest.action as action
19
20from testutils import *
21
22#@var serial_failover_port_map Local copy of the configuration map from OF port
23# numbers to OS interfaces
24serial_failover_port_map = None
25#@var serial_failover_logger Local logger object
26serial_failover_logger = None
27#@var serial_failover_config Local copy of global configuration data
28serial_failover_config = None
29
30test_prio = {}
31
32def test_set_init(config):
33 """
34 Set up function for serial failover test classes
35
36 @param config The configuration dictionary; see oft
37 """
38
39 global serial_failover_port_map
40 global serial_failover_logger
41 global serial_failover_config
42
43 serial_failover_logger = logging.getLogger("serial_failover")
44 serial_failover_logger.info("Initializing test set")
45 serial_failover_port_map = config["port_map"]
46 serial_failover_config = config
47
48class SerialFailover(unittest.TestCase):
49 """
50 Opens a connection that the switch should use as its only controller,
51 as specified by controller_host and controller_port.
52 Then cause the connection to fail [fail method should be configurable].
53 Ultimately, the switch should connect to the next controller port,
54 as specified by
55 --test-params="controller_list=['ip2:port2','ip3:port3']".
56 Multiple test params are specified by
57 --test-params="param1=val1;param2=val2"
58 """
59
60 # populated by buildControllerList()
61 controller_list = []
62 controller_idx = 0
63 # populated by setUp()
64 test_timeout = 0
65 test_iterations = 0
66
67 def sig_handler(self, v1, v2):
68 serial_failover_logger.critical("Received interrupt signal; exiting")
69 print "Received interrupt signal; exiting"
70 self.clean_shutdown = False
71 self.tearDown()
72 sys.exit(1)
73
74 def controllerSetup(self, host, port):
75 self.controller = controller.Controller(host=host,port=port)
76
77 # clean_shutdown should be set to False to force quit app
78 self.clean_shutdown = True
79
80 self.controller.start()
81 #@todo Add an option to wait for a pkt transaction to ensure version
82 # compatibilty?
Dan Talayco907efa22012-09-19 11:30:06 -070083 self.controller.connect(timeout=10)
Ken Chiang20a3da52012-07-11 09:13:57 -070084 self.assertTrue(self.controller.active,
85 "Controller startup failed, not active")
86 self.assertTrue(self.controller.switch_addr is not None,
87 "Controller startup failed, no switch addr")
Dan Talayco907efa22012-09-19 11:30:06 -070088 request = message.features_request()
89 reply, pkt = self.controller.transact(request, timeout=10)
90 self.assertTrue(reply is not None,
91 "Did not complete features_request for handshake")
Ken Chiang20a3da52012-07-11 09:13:57 -070092 serial_failover_logger.info("Connected " +
93 str(self.controller.switch_addr))
94
95 # send echo request and wait for reply
96 request = message.echo_request()
97 response, pkt = self.controller.transact(request)
98 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
99 'response is not echo_reply')
100 self.assertEqual(request.header.xid, response.header.xid,
101 'response xid != request xid')
102 self.assertEqual(len(response.data), 0, 'response data non-empty')
103
104 def connectionKill(self, kill_method):
105 if kill_method == 'controller_shutdown':
106 serial_failover_logger.info("Shutting down controller")
107 self.controller.shutdown()
108 elif kill_method == 'no_echo':
109 serial_failover_logger.info("Disabling controller keep alive")
110 self.controller.keep_alive = False
111
112 # wait for controller to die
113 count = 0
114 while self.controller.active and count < self.test_timeout:
115 time.sleep(1)
116 count = count + 1
117 else:
118 self.assertTrue(false, "Unknown controller kill method")
119
120 def buildControllerList(self):
121 # controller_list is list of ip/port tuples
122 partial_list = test_param_get(serial_failover_config,
123 'controller_list')
Dan Talayco907efa22012-09-19 11:30:06 -0700124 serial_failover_logger.debug("ctrl list: " + str(partial_list))
Ken Chiang20a3da52012-07-11 09:13:57 -0700125 self.controller_list = [(serial_failover_config["controller_host"],
126 serial_failover_config["controller_port"])]
127 if partial_list is not None:
128 for controller in partial_list:
129 ip,portstr = controller.split(':')
130 try:
131 port = int(portstr)
132 except:
133 self.assertTrue(0, "failure converting port " +
134 portstr + " to integer")
135 self.controller_list.append( (ip, int(port)) )
136
137 def getController(self):
138 return self.controller_list[self.controller_idx]
139
140 def getNextController(self):
141 self.controller_idx = (self.controller_idx + 1) \
142 % len(self.controller_list)
143 return self.controller_list[self.controller_idx]
144
145 def setUp(self):
146 self.logger = serial_failover_logger
147 self.config = serial_failover_config
Ed Swierk022d02e2012-08-22 06:26:36 -0700148 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
149 # to top-level oft
150 try:
151 signal.signal(signal.SIGINT, self.sig_handler)
152 except ValueError, e:
153 serial_failover_logger.info("Could not set SIGINT handler: %s" % e)
Ken Chiang20a3da52012-07-11 09:13:57 -0700154 serial_failover_logger.info("** START TEST CASE " + str(self))
155
156 self.test_timeout = test_param_get(serial_failover_config,
157 'failover_timeout') or 60
158 self.test_iterations = test_param_get(serial_failover_config,
159 'failover_iterations') or 4
160
161 self.buildControllerList()
162 self.controller_idx = 0
163 controller = self.getController()
164 self.controllerSetup(controller[0], controller[1])
165
166 def inheritSetup(self, parent):
167 """
168 Inherit the setup of a parent
169
170 This allows running at test from within another test. Do the
171 following:
172
173 sub_test = SomeTestClass() # Create an instance of the test class
174 sub_test.inheritSetup(self) # Inherit setup of parent
175 sub_test.runTest() # Run the test
176
177 Normally, only the parent's setUp and tearDown are called and
178 the state after the sub_test is run must be taken into account
179 by subsequent operations.
180 """
181 self.logger = parent.logger
182 self.config = parent.config
183 serial_failover_logger.info("** Setup " + str(self) +
184 " inheriting from " + str(parent))
185 self.controller = parent.controller
186
187 def tearDown(self):
188 serial_failover_logger.info("** END TEST CASE " + str(self))
189 self.controller.shutdown()
190 if self.clean_shutdown:
191 self.controller.join()
192
193 def doFailover(self, killmethod):
194 serial_failover_logger.info("Starting serial failover test")
195 self.assertTrue(self.controller.switch_socket is not None,
196 str(self) + 'No connection to switch')
197 # kill controller connection
198 self.connectionKill(killmethod)
199 # establish new controller connection
200 controller = self.getNextController()
201 serial_failover_logger.debug("** Next controller (%u/%u)%s:%u" %
202 (self.controller_idx,
203 len(self.controller_list),
204 controller[0],
205 controller[1]))
206 self.controllerSetup(controller[0], controller[1])
207
208 def runTest(self):
209 for i in range(0,self.test_iterations):
210 self.doFailover('controller_shutdown')
211
212 def assertTrue(self, cond, msg):
213 if not cond:
214 serial_failover_logger.error("** FAILED ASSERTION: " + msg)
215 unittest.TestCase.assertTrue(self, cond, msg)
216
217test_prio["SerialFailover"] = -1
218
219
220class SerialFailoverNoEcho(SerialFailover):
221
222 def runTest(self):
223 for i in range(0,self.test_iterations):
224 self.doFailover('no_echo')
225
226test_prio["SerialFailoverNoEcho"] = -1
227