blob: 479a35b31d407481f449dbcff4eefafedd299592 [file] [log] [blame]
Dan Talayco34089522010-02-07 23:07:41 -08001"""
2OpenFlow Test Framework
3
4Controller class
5
6Provide the interface to the control channel to the switch under test.
7
8Class inherits from thread so as to run in background allowing
9asynchronous callbacks (if needed, not required). Also supports
10polling.
11
12The controller thread maintains a queue. Incoming messages that
13are not handled by a callback function are placed in this queue for
14poll calls.
15
16Callbacks and polling support specifying the message type
17
18@todo Support transaction semantics via xid
Dan Talayco1b3f6902010-02-15 14:14:19 -080019@todo Support select and listen on an administrative socket (or
20use a timeout to support clean shutdown).
21
22Currently only one connection is accepted during the life of
23the controller. There seems
24to be no clean way to interrupt an accept call. Using select that also listens
25on an administrative socket and can shut down the socket might work.
26
Dan Talayco34089522010-02-07 23:07:41 -080027"""
28
Dan Talayco34089522010-02-07 23:07:41 -080029import os
30import socket
31import time
Dan Talayco34089522010-02-07 23:07:41 -080032from threading import Thread
33from threading import Lock
Dan Talayco21c75c72010-02-12 22:59:24 -080034from threading import Condition
Dan Talayco34089522010-02-07 23:07:41 -080035from message import *
Dan Talaycoe37999f2010-02-09 15:27:12 -080036from parse import *
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080037from ofutils import *
Dan Talayco710438c2010-02-18 15:16:07 -080038# For some reason, it seems select to be last (or later).
39# Otherwise get an attribute error when calling select.select
40import select
Dan Talayco48370102010-03-03 15:17:33 -080041import logging
42
43##@todo Find a better home for these identifiers (controller)
44RCV_SIZE_DEFAULT = 4096
45LISTEN_QUEUE_SIZE = 1
Dan Talayco34089522010-02-07 23:07:41 -080046
47class Controller(Thread):
48 """
49 Class abstracting the control interface to the switch.
50
51 For receiving messages, two mechanism will be implemented. First,
52 query the interface with poll. Second, register to have a
53 function called by message type. The callback is passed the
54 message type as well as the raw packet (or message object)
55
56 One of the main purposes of this object is to translate between network
57 and host byte order. 'Above' this object, things should be in host
58 byte order.
Dan Talayco21c75c72010-02-12 22:59:24 -080059
60 @todo Consider using SocketServer for listening socket
61 @todo Test transaction code
62
63 @var rcv_size The receive size to use for receive calls
64 @var max_pkts The max size of the receive queue
65 @var keep_alive If true, listen for echo requests and respond w/
66 echo replies
Dan Talayco710438c2010-02-18 15:16:07 -080067 @var initial_hello If true, will send a hello message immediately
68 upon connecting to the switch
Dan Talayco21c75c72010-02-12 22:59:24 -080069 @var host The host to use for connect
70 @var port The port to connect on
71 @var packets_total Total number of packets received
72 @var packets_expired Number of packets popped from queue as queue full
73 @var packets_handled Number of packets handled by something
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080074 @var dbg_state Debug indication of state
Dan Talayco34089522010-02-07 23:07:41 -080075 """
76
Dan Talayco48370102010-03-03 15:17:33 -080077 def __init__(self, host='127.0.0.1', port=6633, max_pkts=1024):
Dan Talayco21c75c72010-02-12 22:59:24 -080078 Thread.__init__(self)
Dan Talayco1b3f6902010-02-15 14:14:19 -080079 # Socket related
Dan Talayco21c75c72010-02-12 22:59:24 -080080 self.rcv_size = RCV_SIZE_DEFAULT
Dan Talayco1b3f6902010-02-15 14:14:19 -080081 self.listen_socket = None
82 self.switch_socket = None
83 self.switch_addr = None
Dan Talayco710438c2010-02-18 15:16:07 -080084 self.socs = []
85 self.connect_cv = Condition()
Dan Talaycoe226eb12010-02-18 23:06:30 -080086 self.message_cv = Condition()
Dan Talayco1b3f6902010-02-15 14:14:19 -080087
88 # Counters
Dan Talayco21c75c72010-02-12 22:59:24 -080089 self.socket_errors = 0
90 self.parse_errors = 0
Dan Talayco21c75c72010-02-12 22:59:24 -080091 self.packets_total = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -080092 self.packets_expired = 0
93 self.packets_handled = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -080094 self.poll_discards = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -080095
96 # State
Dan Talayco21c75c72010-02-12 22:59:24 -080097 self.packets = []
98 self.sync = Lock()
99 self.handlers = {}
100 self.keep_alive = False
Dan Talayco710438c2010-02-18 15:16:07 -0800101 self.active = True
102 self.initial_hello = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800103
104 # Settings
105 self.max_pkts = max_pkts
106 self.passive = True
Dan Talayco48370102010-03-03 15:17:33 -0800107 self.host = host
108 self.port = port
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800109 self.dbg_state = "init"
Dan Talayco48370102010-03-03 15:17:33 -0800110 self.logger = logging.getLogger("controller")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800111
Dan Talaycoe226eb12010-02-18 23:06:30 -0800112 # Transaction and message type waiting variables
113 # xid_cv: Condition variable (semaphore) for packet waiters
Dan Talayco21c75c72010-02-12 22:59:24 -0800114 # xid: Transaction ID being waited on
115 # xid_response: Transaction response message
Dan Talaycoe226eb12010-02-18 23:06:30 -0800116 # expect_msg: Is a message being waited on
117 # expect_msg_cv: Semaphore for waiters
118 # expect_msg_type: Type of message expected
119 # expect_msg_response: Result passed through here
120
Dan Talayco21c75c72010-02-12 22:59:24 -0800121 self.xid_cv = Condition()
122 self.xid = None
123 self.xid_response = None
124
Dan Talaycoe226eb12010-02-18 23:06:30 -0800125 self.expect_msg = False
126 self.expect_msg_cv = Condition()
127 self.expect_msg_type = None
128 self.expect_msg_response = None
129
Dan Talayco710438c2010-02-18 15:16:07 -0800130 def _socket_ready_handle(self, s):
131 """
132 Handle an input-ready socket
133 @param s The socket object that is ready
134 @retval True, reset the switch connection
135 """
136
137 if s == self.listen_socket:
138 if self.switch_socket:
Dan Talayco48370102010-03-03 15:17:33 -0800139 self.logger.error("Multiple switch cxns not supported")
Dan Talayco710438c2010-02-18 15:16:07 -0800140 sys.exit(1)
141
142 (self.switch_socket, self.switch_addr) = \
143 self.listen_socket.accept()
Dan Talayco48370102010-03-03 15:17:33 -0800144 self.logger.info("Got cxn to " + str(self.switch_addr))
Dan Talayco710438c2010-02-18 15:16:07 -0800145 # Notify anyone waiting
146 self.connect_cv.acquire()
147 self.connect_cv.notify()
148 self.connect_cv.release()
149 self.socs.append(self.switch_socket)
150 if self.initial_hello:
151 self.message_send(hello())
152 elif s == self.switch_socket:
153 try:
154 pkt = self.switch_socket.recv(self.rcv_size)
155 except:
Dan Talayco48370102010-03-03 15:17:33 -0800156 self.logger.info("Error on switch read")
Dan Talayco710438c2010-02-18 15:16:07 -0800157 return True
158
159 if not self.active:
160 return False
161
162 if len(pkt) == 0:
Dan Talayco48370102010-03-03 15:17:33 -0800163 self.logger.info("zero-len pkt in")
Dan Talayco710438c2010-02-18 15:16:07 -0800164 return True
165
166 (handled, msg) = self._pkt_handler_check(pkt)
167 if handled:
168 self.packets_handled += 1
169 return False
170
171 # Not handled, enqueue
172 self.sync.acquire()
173 if len(self.packets) >= self.max_pkts:
174 self.packets.pop(0)
175 self.packets_expired += 1
176 self.packets.append((msg, pkt))
177 self.packets_total += 1
178 self.sync.release()
179 else:
Dan Talayco48370102010-03-03 15:17:33 -0800180 self.logger.error("Unknown socket ready: " + str(s))
Dan Talayco710438c2010-02-18 15:16:07 -0800181 return True
182
183 return False
184
Dan Talayco1b3f6902010-02-15 14:14:19 -0800185 def run(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800186 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800187 Activity function for class
Dan Talayco21c75c72010-02-12 22:59:24 -0800188
Dan Talayco1b3f6902010-02-15 14:14:19 -0800189 Assumes connection to switch already exists. Listens on
190 switch_socket for messages until an error (or zero len pkt)
191 occurs.
Dan Talayco21c75c72010-02-12 22:59:24 -0800192
Dan Talayco1b3f6902010-02-15 14:14:19 -0800193 When there is a message on the socket, check for handlers; queue the
194 packet if no one handles the packet.
195
196 See note for controller describing the limitation of a single
197 connection for now.
198 """
199
Dan Talayco710438c2010-02-18 15:16:07 -0800200 self.dbg_state = "starting"
Dan Talayco1b3f6902010-02-15 14:14:19 -0800201
Dan Talayco710438c2010-02-18 15:16:07 -0800202 # Create listen socket
Dan Talayco48370102010-03-03 15:17:33 -0800203 self.logger.info("Create/listen at " + self.host + ":" +
Dan Talayco710438c2010-02-18 15:16:07 -0800204 str(self.port))
205 self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
206 self.listen_socket.setsockopt(socket.SOL_SOCKET,
207 socket.SO_REUSEADDR, 1)
208 self.listen_socket.bind((self.host, self.port))
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800209 self.dbg_state = "listening"
Dan Talayco21c75c72010-02-12 22:59:24 -0800210 self.listen_socket.listen(LISTEN_QUEUE_SIZE)
Dan Talayco21c75c72010-02-12 22:59:24 -0800211
Dan Talayco48370102010-03-03 15:17:33 -0800212 self.logger.info("Waiting for switch connection")
Dan Talayco710438c2010-02-18 15:16:07 -0800213 self.socs = [self.listen_socket]
214 self.dbg_state = "running"
215 while self.active:
216 reset_switch_cxn = False
217 try:
218 sel_in, sel_out, sel_err = \
219 select.select(self.socs, [], self.socs, 1)
220 except:
221 print sys.exc_info()
Dan Talayco48370102010-03-03 15:17:33 -0800222 self.logger.error("Select error, exiting")
Dan Talayco710438c2010-02-18 15:16:07 -0800223 sys.exit(1)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800224
Dan Talayco710438c2010-02-18 15:16:07 -0800225 if not self.active:
226 break
Dan Talayco1b3f6902010-02-15 14:14:19 -0800227
Dan Talayco710438c2010-02-18 15:16:07 -0800228 for s in sel_in:
229 reset_switch_cxn = self._socket_ready_handle(s)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800230
Dan Talayco710438c2010-02-18 15:16:07 -0800231 for s in sel_err:
Dan Talayco48370102010-03-03 15:17:33 -0800232 self.logger.error("Got socket error on: " + str(s))
Dan Talayco710438c2010-02-18 15:16:07 -0800233 if s == self.switch_socket:
234 reset_switch_cxn = True
235 else:
Dan Talayco48370102010-03-03 15:17:33 -0800236 self.logger.error("Socket error; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -0800237 self.active = False
238 break
239
240 if self.active and reset_switch_cxn:
Dan Talayco48370102010-03-03 15:17:33 -0800241 self.logger.info("Closing switch cxn")
Dan Talayco710438c2010-02-18 15:16:07 -0800242 try:
243 self.switch_socket.close()
244 except:
245 pass
246 self.switch_socket = None
247 self.socs = self.socs[0:1]
248
249 # End of main loop
250 self.dbg_state = "closing"
Dan Talayco48370102010-03-03 15:17:33 -0800251 self.logger.info("Exiting controller thread")
Dan Talayco710438c2010-02-18 15:16:07 -0800252 self.shutdown()
253
254 def connect(self, timeout=None):
255 """
256 Connect to the switch
257
258 @param timeout If None, block until connected. If 0, return
259 immedidately. Otherwise, block for up to timeout seconds
260 @return Boolean, True if connected
261 """
262
263 if timeout == 0:
264 return self.switch_socket is not None
265 if self.switch_socket is not None:
266 return True
267 self.connect_cv.acquire()
268 self.connect_cv.wait(timeout)
269 self.connect_cv.release()
270
271 return self.switch_socket is not None
272
273 def kill(self):
274 """
275 Force the controller thread to quit
276
277 Just sets the active state variable to false and expects
278 the select timeout to kick in
279 """
280 self.active = False
Dan Talayco21c75c72010-02-12 22:59:24 -0800281
Dan Talayco1b3f6902010-02-15 14:14:19 -0800282 def shutdown(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800283 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800284 Shutdown the controller closing all sockets
Dan Talayco21c75c72010-02-12 22:59:24 -0800285
Dan Talayco1b3f6902010-02-15 14:14:19 -0800286 @todo Might want to synchronize shutdown with self.sync...
287 """
Dan Talayco710438c2010-02-18 15:16:07 -0800288 self.active = False
289 try:
290 self.switch_socket.shutdown(socket.SHUT_RDWR)
291 except:
Dan Talayco48370102010-03-03 15:17:33 -0800292 self.logger.info("Ignoring switch soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800293 self.switch_socket = None
Dan Talayco1b3f6902010-02-15 14:14:19 -0800294
Dan Talayco710438c2010-02-18 15:16:07 -0800295 try:
296 self.listen_socket.shutdown(socket.SHUT_RDWR)
297 except:
Dan Talayco48370102010-03-03 15:17:33 -0800298 self.logger.info("Ignoring listen soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800299 self.listen_socket = None
300 self.dbg_state = "down"
301
Dan Talayco21c75c72010-02-12 22:59:24 -0800302 def _pkt_handler_check(self, pkt):
303 """
304 Check for packet handling before being enqueued
305
306 This includes checking for an ongoing transaction (see transact())
307 an echo request in case keep_alive is true, followed by
308 registered message handlers.
309 @param pkt The raw packet (string)
310 @return (handled, msg) where handled is a boolean indicating
311 the message was handled; msg if None is the parsed message
312 """
313 # Parse the header to get type
314 hdr = of_header_parse(pkt)
315 if not hdr:
Dan Talayco48370102010-03-03 15:17:33 -0800316 self.logger.info("Could not parse header, pkt len", len(pkt))
Dan Talayco21c75c72010-02-12 22:59:24 -0800317 self.parse_errors += 1
318 return (True, None)
Dan Talayco21c75c72010-02-12 22:59:24 -0800319
Dan Talayco48370102010-03-03 15:17:33 -0800320 self.logger.debug("message: len %d. type %s. hdr.len %d" %
Dan Talayco21c75c72010-02-12 22:59:24 -0800321 (len(pkt), ofp_type_map[hdr.type], hdr.length))
322 msg = of_message_parse(pkt)
323 if not msg:
324 self.parse_errors += 1
Dan Talayco48370102010-03-03 15:17:33 -0800325 self.logger.warn("Could not parse message")
Dan Talayco21c75c72010-02-12 22:59:24 -0800326 return (True, None)
327
328 # Check if transaction is waiting
329 self.xid_cv.acquire()
330 if self.xid:
331 if hdr.xid == self.xid:
Dan Talaycoe226eb12010-02-18 23:06:30 -0800332 self.xid_response = (msg, pkt)
333 self.xid = None
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800334 self.xid_cv.notify()
Dan Talayco21c75c72010-02-12 22:59:24 -0800335 self.xid_cv.release()
336 return (True, None)
337 self.xid_cv.release()
338
Dan Talaycoe226eb12010-02-18 23:06:30 -0800339 # Check if anyone waiting on this type of message
340 self.expect_msg_cv.acquire()
341 if self.expect_msg:
Dan Talayco48370102010-03-03 15:17:33 -0800342 if not self.expect_msg_type or (self.expect_msg_type == hdr.type):
Dan Talaycoe226eb12010-02-18 23:06:30 -0800343 self.expect_msg_response = (msg, pkt)
344 self.expect_msg = False
345 self.expect_msg_cv.notify()
346 self.expect_msg_cv.release()
347 return (True, None)
348 self.expect_msg_cv.release()
349
Dan Talayco21c75c72010-02-12 22:59:24 -0800350 # Check if keep alive is set; if so, respond to echo requests
351 if self.keep_alive:
352 if hdr.type == OFPT_ECHO_REQUEST:
353 rep = echo_reply()
354 rep.header.xid = hdr.xid
355 # Ignoring additional data
Dan Talayco710438c2010-02-18 15:16:07 -0800356 self.message_send(rep.pack(), zero_xid=True)
Dan Talayco21c75c72010-02-12 22:59:24 -0800357 return (True, None)
358
359 # Now check for message handlers; preference is given to
360 # handlers for a specific packet
361 handled = False
362 if hdr.type in self.handlers.keys():
363 handled = self.handlers[hdr.type](self, msg, pkt)
364 if not handled and ("all" in self.handlers.keys()):
365 handled = self.handlers["all"](self, msg, pkt)
366
367 return (handled, msg)
368
Dan Talayco34089522010-02-07 23:07:41 -0800369 def register(self, msg_type, handler):
370 """
371 Register a callback to receive a specific message type.
372
373 Only one handler may be registered for a given message type.
374 @param msg_type The type of message to receive. May be DEFAULT
Dan Talayco21c75c72010-02-12 22:59:24 -0800375 for all non-handled packets. The special type, the string "all"
376 will send all packets to the handler.
Dan Talayco34089522010-02-07 23:07:41 -0800377 @param handler The function to call when a message of the given
378 type is received.
379 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800380 # Should check type is valid
381 if not handler and msg_type in self.handlers.keys():
382 del self.handlers[msg_type]
383 return
384 self.handlers[msg_type] = handler
Dan Talayco34089522010-02-07 23:07:41 -0800385
Dan Talayco21c75c72010-02-12 22:59:24 -0800386 def poll(self, exp_msg=None, timeout=None):
Dan Talayco34089522010-02-07 23:07:41 -0800387 """
388 Wait for the next OF message received from the switch.
389
390 @param exp_msg If set, return only when this type of message
Dan Talayco48370102010-03-03 15:17:33 -0800391 is received (unless timeout occurs).
Dan Talaycoe226eb12010-02-18 23:06:30 -0800392 @param timeout If None, do not block. Otherwise, sleep in
Dan Talayco48370102010-03-03 15:17:33 -0800393 intervals of 1 second until message is received.
Dan Talayco34089522010-02-07 23:07:41 -0800394
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800395 @retval A pair (msg, pkt) where msg is a message object and pkt
396 the string representing the packet as received from the socket.
397 This allows additional parsing by the receiver if necessary.
398
Dan Talayco34089522010-02-07 23:07:41 -0800399 The data members in the message are in host endian order.
Dan Talayco48370102010-03-03 15:17:33 -0800400 If an error occurs, (None, None) is returned
401
402 The current queue is searched for a message of the desired type
403 before sleeping on message in events.
Dan Talayco34089522010-02-07 23:07:41 -0800404 """
Dan Talayco34089522010-02-07 23:07:41 -0800405
Dan Talaycoe226eb12010-02-18 23:06:30 -0800406 msg = pkt = None
Dan Talayco34089522010-02-07 23:07:41 -0800407
Dan Talayco48370102010-03-03 15:17:33 -0800408 self.logger.debug("Poll for " + ofp_type_map[exp_msg])
Dan Talaycoe226eb12010-02-18 23:06:30 -0800409 # First check the current queue
410 self.sync.acquire()
411 if len(self.packets) > 0:
412 if not exp_msg:
413 (msg, pkt) = self.packets.pop(0)
414 self.sync.release()
415 return (msg, pkt)
416 else:
417 for i in range(len(self.packets)):
418 msg = self.packets[i][0]
419 if msg.header.type == exp_msg:
420 (msg, pkt) = self.packets.pop(i)
421 self.sync.release()
422 return (msg, pkt)
423
424 # Okay, not currently in the queue
425 if timeout is None or timeout <= 0:
Dan Talayco21c75c72010-02-12 22:59:24 -0800426 self.sync.release()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800427 return (None, None)
Dan Talayco21c75c72010-02-12 22:59:24 -0800428
Dan Talayco48370102010-03-03 15:17:33 -0800429 msg = pkt = None
430 self.logger.debug("Entering timeout")
Dan Talaycoe226eb12010-02-18 23:06:30 -0800431 # Careful of race condition releasing sync before message cv
Dan Talayco90576bd2010-02-19 10:59:02 -0800432 # Also, this style is ripe for a lockup.
Dan Talaycoe226eb12010-02-18 23:06:30 -0800433 self.expect_msg_cv.acquire()
434 self.sync.release()
Dan Talayco48370102010-03-03 15:17:33 -0800435 self.expect_msg_response = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800436 self.expect_msg = True
437 self.expect_msg_type = exp_msg
438 self.expect_msg_cv.wait(timeout)
439 if self.expect_msg_response is not None:
440 (msg, pkt) = self.expect_msg_response
Dan Talaycoe226eb12010-02-18 23:06:30 -0800441 self.expect_msg_cv.release()
442
443 if msg is None:
Dan Talayco48370102010-03-03 15:17:33 -0800444 self.logger.debug("Poll time out")
445 else:
446 self.logger.debug("Got msg " + str(msg))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800447
448 return (msg, pkt)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800449
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800450 def transact(self, msg, timeout=None, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800451 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800452 Run a message transaction with the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800453
454 Send the message in msg and wait for a reply with a matching
Dan Talayco21c75c72010-02-12 22:59:24 -0800455 transaction id. Transactions have the highest priority in
456 received message handling.
Dan Talaycoe37999f2010-02-09 15:27:12 -0800457
Dan Talayco21c75c72010-02-12 22:59:24 -0800458 @param msg The message object to send; must not be a string
459 @param timeout The timeout in seconds (?)
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800460 @param zero_xid Normally, if the XID is 0 an XID will be generated
461 for the message. Set xero_xid to override this behavior
Dan Talayco21c75c72010-02-12 22:59:24 -0800462 @return The matching message object or None if unsuccessful
Dan Talaycoe37999f2010-02-09 15:27:12 -0800463
Dan Talayco34089522010-02-07 23:07:41 -0800464 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800465
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800466 if not zero_xid and msg.header.xid == 0:
467 msg.header.xid = gen_xid()
468
Dan Talayco21c75c72010-02-12 22:59:24 -0800469 self.xid_cv.acquire()
470 if self.xid:
471 self.xid_cv.release()
Dan Talayco48370102010-03-03 15:17:33 -0800472 self.logger.error("Can only run one transaction at a time")
Dan Talayco21c75c72010-02-12 22:59:24 -0800473 return None
474
475 self.xid = msg.header.xid
476 self.xid_response = None
477 self.message_send(msg.pack())
478 self.xid_cv.wait(timeout)
Dan Talaycoe226eb12010-02-18 23:06:30 -0800479 (msg, pkt) = self.xid_response
Dan Talayco21c75c72010-02-12 22:59:24 -0800480 self.xid_response = None
481 self.xid_cv.release()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800482 return (msg, pkt)
Dan Talayco34089522010-02-07 23:07:41 -0800483
Dan Talayco710438c2010-02-18 15:16:07 -0800484 def message_send(self, msg, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800485 """
486 Send the message to the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800487
Dan Talayco710438c2010-02-18 15:16:07 -0800488 @param msg A string or OpenFlow message object to be forwarded to
489 the switch.
490 @param zero_xid If msg is an OpenFlow object (not a string) and if
491 the XID in the header is 0, then an XID will be generated
492 for the message. Set xero_xid to override this behavior (and keep an
493 existing 0 xid)
Dan Talaycoe37999f2010-02-09 15:27:12 -0800494
Dan Talayco710438c2010-02-18 15:16:07 -0800495 @return -1 if error, 0 on success
Dan Talayco34089522010-02-07 23:07:41 -0800496
Dan Talayco21c75c72010-02-12 22:59:24 -0800497 """
498
Dan Talayco1b3f6902010-02-15 14:14:19 -0800499 if not self.switch_socket:
500 # Sending a string indicates the message is ready to go
Dan Talayco48370102010-03-03 15:17:33 -0800501 self.logger.info("message_send: no socket")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800502 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800503 #@todo If not string, try to pack
Dan Talayco21c75c72010-02-12 22:59:24 -0800504 if type(msg) != type(""):
Dan Talayco710438c2010-02-18 15:16:07 -0800505 try:
506 if msg.header.xid == 0 and not zero_xid:
507 msg.header.xid = gen_xid()
508 outpkt = msg.pack()
509 except:
Dan Talayco48370102010-03-03 15:17:33 -0800510 self.logger.error(
Dan Talayco710438c2010-02-18 15:16:07 -0800511 "message_send: not an OF message or string?")
512 return -1
513 else:
514 outpkt = msg
Dan Talayco21c75c72010-02-12 22:59:24 -0800515
Dan Talayco48370102010-03-03 15:17:33 -0800516 self.logger.debug("Sending pkt of len " + str(len(outpkt)))
Dan Talayco710438c2010-02-18 15:16:07 -0800517 if self.switch_socket.sendall(outpkt) is None:
518 return 0
519
Dan Talayco48370102010-03-03 15:17:33 -0800520 self.logger.error("Unknown error on sendall")
Dan Talayco710438c2010-02-18 15:16:07 -0800521 return -1
Dan Talayco21c75c72010-02-12 22:59:24 -0800522
523 def __str__(self):
524 string = "Controller:\n"
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800525 string += " state " + self.dbg_state + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800526 string += " switch_addr " + str(self.switch_addr) + "\n"
527 string += " pending pkts " + str(len(self.packets)) + "\n"
528 string += " total pkts " + str(self.packets_total) + "\n"
529 string += " expired pkts " + str(self.packets_expired) + "\n"
530 string += " handled pkts " + str(self.packets_handled) + "\n"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800531 string += " poll discards " + str(self.poll_discards) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800532 string += " parse errors " + str(self.parse_errors) + "\n"
533 string += " sock errrors " + str(self.socket_errors) + "\n"
534 string += " max pkts " + str(self.max_pkts) + "\n"
535 string += " host " + str(self.host) + "\n"
536 string += " port " + str(self.port) + "\n"
537 string += " keep_alive " + str(self.keep_alive) + "\n"
538 return string
539
540 def show(self):
541 print str(self)
542
543def sample_handler(controller, msg, pkt):
544 """
545 Sample message handler
546
547 This is the prototype for functions registered with the controller
548 class for packet reception
549
550 @param controller The controller calling the handler
551 @param msg The parsed message object
552 @param pkt The raw packet that was received on the socket. This is
553 in case the packet contains extra unparsed data.
554 @returns Boolean value indicating if the packet was handled. If
555 not handled, the packet is placed in the queue for pollers to received
556 """
557 pass