blob: 8ba17660914a6a93e520639c7db1ed9c7f74c79a [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
Rich Laneda3b5ad2012-10-03 09:05:32 -070020from oftest.testutils import *
Ken Chiang20a3da52012-07-11 09:13:57 -070021
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
Ken Chiang20a3da52012-07-11 09:13:57 -070025#@var serial_failover_config Local copy of global configuration data
26serial_failover_config = None
27
28test_prio = {}
29
30def test_set_init(config):
31 """
32 Set up function for serial failover test classes
33
34 @param config The configuration dictionary; see oft
35 """
36
37 global serial_failover_port_map
Ken Chiang20a3da52012-07-11 09:13:57 -070038 global serial_failover_config
39
Ken Chiang20a3da52012-07-11 09:13:57 -070040 serial_failover_port_map = config["port_map"]
41 serial_failover_config = config
42
43class SerialFailover(unittest.TestCase):
44 """
45 Opens a connection that the switch should use as its only controller,
46 as specified by controller_host and controller_port.
47 Then cause the connection to fail [fail method should be configurable].
48 Ultimately, the switch should connect to the next controller port,
49 as specified by
50 --test-params="controller_list=['ip2:port2','ip3:port3']".
51 Multiple test params are specified by
52 --test-params="param1=val1;param2=val2"
53 """
54
55 # populated by buildControllerList()
56 controller_list = []
57 controller_idx = 0
58 # populated by setUp()
59 test_timeout = 0
60 test_iterations = 0
61
62 def sig_handler(self, v1, v2):
Rich Lane9a003812012-10-04 17:17:59 -070063 logging.critical("Received interrupt signal; exiting")
Ken Chiang20a3da52012-07-11 09:13:57 -070064 print "Received interrupt signal; exiting"
65 self.clean_shutdown = False
66 self.tearDown()
67 sys.exit(1)
68
69 def controllerSetup(self, host, port):
70 self.controller = controller.Controller(host=host,port=port)
71
72 # clean_shutdown should be set to False to force quit app
73 self.clean_shutdown = True
74
75 self.controller.start()
76 #@todo Add an option to wait for a pkt transaction to ensure version
77 # compatibilty?
Dan Talayco907efa22012-09-19 11:30:06 -070078 self.controller.connect(timeout=10)
Ken Chiang20a3da52012-07-11 09:13:57 -070079 self.assertTrue(self.controller.active,
80 "Controller startup failed, not active")
81 self.assertTrue(self.controller.switch_addr is not None,
82 "Controller startup failed, no switch addr")
Dan Talayco907efa22012-09-19 11:30:06 -070083 request = message.features_request()
Dan Talaycoc689a792012-09-28 14:22:53 -070084 reply, pkt = self.controller.transact(request, timeout=20)
Dan Talayco907efa22012-09-19 11:30:06 -070085 self.assertTrue(reply is not None,
86 "Did not complete features_request for handshake")
Rich Lane9a003812012-10-04 17:17:59 -070087 logging.info("Connected " +
Ken Chiang20a3da52012-07-11 09:13:57 -070088 str(self.controller.switch_addr))
89
90 # send echo request and wait for reply
91 request = message.echo_request()
92 response, pkt = self.controller.transact(request)
93 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
94 'response is not echo_reply')
95 self.assertEqual(request.header.xid, response.header.xid,
96 'response xid != request xid')
97 self.assertEqual(len(response.data), 0, 'response data non-empty')
98
99 def connectionKill(self, kill_method):
100 if kill_method == 'controller_shutdown':
Rich Lane9a003812012-10-04 17:17:59 -0700101 logging.info("Shutting down controller")
Ken Chiang20a3da52012-07-11 09:13:57 -0700102 self.controller.shutdown()
103 elif kill_method == 'no_echo':
Rich Lane9a003812012-10-04 17:17:59 -0700104 logging.info("Disabling controller keep alive")
Ken Chiang20a3da52012-07-11 09:13:57 -0700105 self.controller.keep_alive = False
106
107 # wait for controller to die
108 count = 0
109 while self.controller.active and count < self.test_timeout:
110 time.sleep(1)
111 count = count + 1
112 else:
113 self.assertTrue(false, "Unknown controller kill method")
114
115 def buildControllerList(self):
116 # controller_list is list of ip/port tuples
117 partial_list = test_param_get(serial_failover_config,
118 'controller_list')
Rich Lane9a003812012-10-04 17:17:59 -0700119 logging.debug("ctrl list: " + str(partial_list))
Ken Chiang20a3da52012-07-11 09:13:57 -0700120 self.controller_list = [(serial_failover_config["controller_host"],
121 serial_failover_config["controller_port"])]
122 if partial_list is not None:
123 for controller in partial_list:
124 ip,portstr = controller.split(':')
125 try:
126 port = int(portstr)
127 except:
128 self.assertTrue(0, "failure converting port " +
129 portstr + " to integer")
130 self.controller_list.append( (ip, int(port)) )
131
132 def getController(self):
133 return self.controller_list[self.controller_idx]
134
135 def getNextController(self):
136 self.controller_idx = (self.controller_idx + 1) \
137 % len(self.controller_list)
138 return self.controller_list[self.controller_idx]
139
140 def setUp(self):
Ken Chiang20a3da52012-07-11 09:13:57 -0700141 self.config = serial_failover_config
Ed Swierk022d02e2012-08-22 06:26:36 -0700142 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
143 # to top-level oft
144 try:
145 signal.signal(signal.SIGINT, self.sig_handler)
146 except ValueError, e:
Rich Lane9a003812012-10-04 17:17:59 -0700147 logging.info("Could not set SIGINT handler: %s" % e)
148 logging.info("** START TEST CASE " + str(self))
Ken Chiang20a3da52012-07-11 09:13:57 -0700149
150 self.test_timeout = test_param_get(serial_failover_config,
151 'failover_timeout') or 60
152 self.test_iterations = test_param_get(serial_failover_config,
153 'failover_iterations') or 4
154
155 self.buildControllerList()
156 self.controller_idx = 0
157 controller = self.getController()
158 self.controllerSetup(controller[0], controller[1])
159
160 def inheritSetup(self, parent):
161 """
162 Inherit the setup of a parent
163
164 This allows running at test from within another test. Do the
165 following:
166
167 sub_test = SomeTestClass() # Create an instance of the test class
168 sub_test.inheritSetup(self) # Inherit setup of parent
169 sub_test.runTest() # Run the test
170
171 Normally, only the parent's setUp and tearDown are called and
172 the state after the sub_test is run must be taken into account
173 by subsequent operations.
174 """
Ken Chiang20a3da52012-07-11 09:13:57 -0700175 self.config = parent.config
Rich Lane9a003812012-10-04 17:17:59 -0700176 logging.info("** Setup " + str(self) +
Ken Chiang20a3da52012-07-11 09:13:57 -0700177 " inheriting from " + str(parent))
178 self.controller = parent.controller
179
180 def tearDown(self):
Rich Lane9a003812012-10-04 17:17:59 -0700181 logging.info("** END TEST CASE " + str(self))
Ken Chiang20a3da52012-07-11 09:13:57 -0700182 self.controller.shutdown()
183 if self.clean_shutdown:
184 self.controller.join()
185
186 def doFailover(self, killmethod):
Rich Lane9a003812012-10-04 17:17:59 -0700187 logging.info("Starting serial failover test")
Ken Chiang20a3da52012-07-11 09:13:57 -0700188 self.assertTrue(self.controller.switch_socket is not None,
189 str(self) + 'No connection to switch')
190 # kill controller connection
191 self.connectionKill(killmethod)
192 # establish new controller connection
193 controller = self.getNextController()
Rich Lane9a003812012-10-04 17:17:59 -0700194 logging.debug("** Next controller (%u/%u)%s:%u" %
Ken Chiang20a3da52012-07-11 09:13:57 -0700195 (self.controller_idx,
196 len(self.controller_list),
197 controller[0],
198 controller[1]))
199 self.controllerSetup(controller[0], controller[1])
200
201 def runTest(self):
202 for i in range(0,self.test_iterations):
203 self.doFailover('controller_shutdown')
204
205 def assertTrue(self, cond, msg):
206 if not cond:
Rich Lane9a003812012-10-04 17:17:59 -0700207 logging.error("** FAILED ASSERTION: " + msg)
Ken Chiang20a3da52012-07-11 09:13:57 -0700208 unittest.TestCase.assertTrue(self, cond, msg)
209
210test_prio["SerialFailover"] = -1
211
212
213class SerialFailoverNoEcho(SerialFailover):
214
215 def runTest(self):
216 for i in range(0,self.test_iterations):
217 self.doFailover('no_echo')
218
219test_prio["SerialFailoverNoEcho"] = -1
220