blob: 7f4196bdb50d0b68d8f373d0ea9ed113a0b9e4a7 [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 Talayco21c75c72010-02-12 22:59:24 -080019@todo Set up reasonable logging facility
Dan Talayco1b3f6902010-02-15 14:14:19 -080020@todo Support select and listen on an administrative socket (or
21use a timeout to support clean shutdown).
22
23Currently only one connection is accepted during the life of
24the controller. There seems
25to be no clean way to interrupt an accept call. Using select that also listens
26on an administrative socket and can shut down the socket might work.
27
Dan Talayco34089522010-02-07 23:07:41 -080028"""
29
Dan Talayco21c75c72010-02-12 22:59:24 -080030from oft_config import *
Dan Talayco34089522010-02-07 23:07:41 -080031import os
32import socket
33import time
Dan Talayco34089522010-02-07 23:07:41 -080034from threading import Thread
35from threading import Lock
Dan Talayco21c75c72010-02-12 22:59:24 -080036from threading import Condition
Dan Talayco34089522010-02-07 23:07:41 -080037from message import *
Dan Talaycoe37999f2010-02-09 15:27:12 -080038from parse import *
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080039from ofutils import *
Dan Talayco710438c2010-02-18 15:16:07 -080040# For some reason, it seems select to be last (or later).
41# Otherwise get an attribute error when calling select.select
42import select
Dan Talayco34089522010-02-07 23:07:41 -080043
44class Controller(Thread):
45 """
46 Class abstracting the control interface to the switch.
47
48 For receiving messages, two mechanism will be implemented. First,
49 query the interface with poll. Second, register to have a
50 function called by message type. The callback is passed the
51 message type as well as the raw packet (or message object)
52
53 One of the main purposes of this object is to translate between network
54 and host byte order. 'Above' this object, things should be in host
55 byte order.
Dan Talayco21c75c72010-02-12 22:59:24 -080056
57 @todo Consider using SocketServer for listening socket
58 @todo Test transaction code
59
60 @var rcv_size The receive size to use for receive calls
61 @var max_pkts The max size of the receive queue
62 @var keep_alive If true, listen for echo requests and respond w/
63 echo replies
Dan Talayco710438c2010-02-18 15:16:07 -080064 @var initial_hello If true, will send a hello message immediately
65 upon connecting to the switch
Dan Talayco21c75c72010-02-12 22:59:24 -080066 @var host The host to use for connect
67 @var port The port to connect on
68 @var packets_total Total number of packets received
69 @var packets_expired Number of packets popped from queue as queue full
70 @var packets_handled Number of packets handled by something
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080071 @var dbg_state Debug indication of state
Dan Talayco34089522010-02-07 23:07:41 -080072 """
73
Dan Talayco21c75c72010-02-12 22:59:24 -080074 def __init__(self, max_pkts=1024):
75 Thread.__init__(self)
Dan Talayco1b3f6902010-02-15 14:14:19 -080076 # Socket related
Dan Talayco21c75c72010-02-12 22:59:24 -080077 self.rcv_size = RCV_SIZE_DEFAULT
Dan Talayco1b3f6902010-02-15 14:14:19 -080078 self.listen_socket = None
79 self.switch_socket = None
80 self.switch_addr = None
Dan Talayco710438c2010-02-18 15:16:07 -080081 self.socs = []
82 self.connect_cv = Condition()
Dan Talaycoe226eb12010-02-18 23:06:30 -080083 self.message_cv = Condition()
Dan Talayco1b3f6902010-02-15 14:14:19 -080084
85 # Counters
Dan Talayco21c75c72010-02-12 22:59:24 -080086 self.socket_errors = 0
87 self.parse_errors = 0
Dan Talayco21c75c72010-02-12 22:59:24 -080088 self.packets_total = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -080089 self.packets_expired = 0
90 self.packets_handled = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -080091 self.poll_discards = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -080092
93 # State
Dan Talayco21c75c72010-02-12 22:59:24 -080094 self.packets = []
95 self.sync = Lock()
96 self.handlers = {}
97 self.keep_alive = False
Dan Talayco710438c2010-02-18 15:16:07 -080098 self.active = True
99 self.initial_hello = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800100
101 # Settings
102 self.max_pkts = max_pkts
103 self.passive = True
Dan Talayco21c75c72010-02-12 22:59:24 -0800104 self.host = controller_host
105 self.port = controller_port
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800106 self.dbg_state = "init"
Dan Talayco1b3f6902010-02-15 14:14:19 -0800107 self.debug_level = debug_level_default
Dan Talayco710438c2010-02-18 15:16:07 -0800108 # self.debug_level = DEBUG_VERBOSE
Dan Talayco1b3f6902010-02-15 14:14:19 -0800109
Dan Talaycoe226eb12010-02-18 23:06:30 -0800110 # Transaction and message type waiting variables
111 # xid_cv: Condition variable (semaphore) for packet waiters
Dan Talayco21c75c72010-02-12 22:59:24 -0800112 # xid: Transaction ID being waited on
113 # xid_response: Transaction response message
Dan Talaycoe226eb12010-02-18 23:06:30 -0800114 # expect_msg: Is a message being waited on
115 # expect_msg_cv: Semaphore for waiters
116 # expect_msg_type: Type of message expected
117 # expect_msg_response: Result passed through here
118
Dan Talayco21c75c72010-02-12 22:59:24 -0800119 self.xid_cv = Condition()
120 self.xid = None
121 self.xid_response = None
122
Dan Talaycoe226eb12010-02-18 23:06:30 -0800123 self.expect_msg = False
124 self.expect_msg_cv = Condition()
125 self.expect_msg_type = None
126 self.expect_msg_response = None
127
Dan Talayco21c75c72010-02-12 22:59:24 -0800128 def dbg(self, level, string):
129 debug_log("CTRL", self.debug_level, level, string)
130
Dan Talayco710438c2010-02-18 15:16:07 -0800131 def _socket_ready_handle(self, s):
132 """
133 Handle an input-ready socket
134 @param s The socket object that is ready
135 @retval True, reset the switch connection
136 """
137
138 if s == self.listen_socket:
139 if self.switch_socket:
140 self.dbg(DEBUG_ERROR, "Multiple switch cxns not supported")
141 sys.exit(1)
142
143 (self.switch_socket, self.switch_addr) = \
144 self.listen_socket.accept()
145 self.dbg(DEBUG_INFO, "Got cxn to " + str(self.switch_addr))
146 # Notify anyone waiting
147 self.connect_cv.acquire()
148 self.connect_cv.notify()
149 self.connect_cv.release()
150 self.socs.append(self.switch_socket)
151 if self.initial_hello:
152 self.message_send(hello())
153 elif s == self.switch_socket:
154 try:
155 pkt = self.switch_socket.recv(self.rcv_size)
156 except:
157 self.dbg(DEBUG_INFO, "error on switch read")
158 return True
159
160 if not self.active:
161 return False
162
163 if len(pkt) == 0:
164 self.dbg(DEBUG_INFO, "zero-len pkt in")
165 return True
166
167 (handled, msg) = self._pkt_handler_check(pkt)
168 if handled:
169 self.packets_handled += 1
170 return False
171
172 # Not handled, enqueue
173 self.sync.acquire()
174 if len(self.packets) >= self.max_pkts:
175 self.packets.pop(0)
176 self.packets_expired += 1
177 self.packets.append((msg, pkt))
178 self.packets_total += 1
179 self.sync.release()
180 else:
181 self.dbg(DEBUG_ERROR, "Unknown socket ready: " + str(s))
182 return True
183
184 return False
185
Dan Talayco1b3f6902010-02-15 14:14:19 -0800186 def run(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800187 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800188 Activity function for class
Dan Talayco21c75c72010-02-12 22:59:24 -0800189
Dan Talayco1b3f6902010-02-15 14:14:19 -0800190 Assumes connection to switch already exists. Listens on
191 switch_socket for messages until an error (or zero len pkt)
192 occurs.
Dan Talayco21c75c72010-02-12 22:59:24 -0800193
Dan Talayco1b3f6902010-02-15 14:14:19 -0800194 When there is a message on the socket, check for handlers; queue the
195 packet if no one handles the packet.
196
197 See note for controller describing the limitation of a single
198 connection for now.
199 """
200
Dan Talayco710438c2010-02-18 15:16:07 -0800201 self.dbg_state = "starting"
Dan Talayco1b3f6902010-02-15 14:14:19 -0800202
Dan Talayco710438c2010-02-18 15:16:07 -0800203 # Create listen socket
204 self.dbg(DEBUG_INFO, "Create/listen at " + self.host + ":" +
205 str(self.port))
206 self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
207 self.listen_socket.setsockopt(socket.SOL_SOCKET,
208 socket.SO_REUSEADDR, 1)
209 self.listen_socket.bind((self.host, self.port))
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800210 self.dbg_state = "listening"
Dan Talayco21c75c72010-02-12 22:59:24 -0800211 self.listen_socket.listen(LISTEN_QUEUE_SIZE)
Dan Talayco21c75c72010-02-12 22:59:24 -0800212
Dan Talayco710438c2010-02-18 15:16:07 -0800213 self.dbg(DEBUG_INFO, "Waiting for switch connection")
214 self.socs = [self.listen_socket]
215 self.dbg_state = "running"
216 while self.active:
217 reset_switch_cxn = False
218 try:
219 sel_in, sel_out, sel_err = \
220 select.select(self.socs, [], self.socs, 1)
221 except:
222 print sys.exc_info()
223 self.dbg(DEBUG_ERROR, "Select error, exiting")
224 sys.exit(1)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800225
Dan Talayco710438c2010-02-18 15:16:07 -0800226 if not self.active:
227 break
Dan Talayco1b3f6902010-02-15 14:14:19 -0800228
Dan Talayco710438c2010-02-18 15:16:07 -0800229 for s in sel_in:
230 reset_switch_cxn = self._socket_ready_handle(s)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800231
Dan Talayco710438c2010-02-18 15:16:07 -0800232 for s in sel_err:
233 self.dbg(DEBUG_ERROR, "Got socket error on: " + str(s))
234 if s == self.switch_socket:
235 reset_switch_cxn = True
236 else:
237 self.dbg(DEBUG_ERROR, "Socket error; exiting")
238 self.active = False
239 break
240
241 if self.active and reset_switch_cxn:
242 self.dbg(DEBUG_INFO, "Closing switch cxn")
243 try:
244 self.switch_socket.close()
245 except:
246 pass
247 self.switch_socket = None
248 self.socs = self.socs[0:1]
249
250 # End of main loop
251 self.dbg_state = "closing"
252 self.dbg(DEBUG_INFO, "Exiting controller thread")
253 self.shutdown()
254
255 def connect(self, timeout=None):
256 """
257 Connect to the switch
258
259 @param timeout If None, block until connected. If 0, return
260 immedidately. Otherwise, block for up to timeout seconds
261 @return Boolean, True if connected
262 """
263
264 if timeout == 0:
265 return self.switch_socket is not None
266 if self.switch_socket is not None:
267 return True
268 self.connect_cv.acquire()
269 self.connect_cv.wait(timeout)
270 self.connect_cv.release()
271
272 return self.switch_socket is not None
273
274 def kill(self):
275 """
276 Force the controller thread to quit
277
278 Just sets the active state variable to false and expects
279 the select timeout to kick in
280 """
281 self.active = False
Dan Talayco21c75c72010-02-12 22:59:24 -0800282
Dan Talayco1b3f6902010-02-15 14:14:19 -0800283 def shutdown(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800284 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800285 Shutdown the controller closing all sockets
Dan Talayco21c75c72010-02-12 22:59:24 -0800286
Dan Talayco1b3f6902010-02-15 14:14:19 -0800287 @todo Might want to synchronize shutdown with self.sync...
288 """
Dan Talayco710438c2010-02-18 15:16:07 -0800289 self.active = False
290 try:
291 self.switch_socket.shutdown(socket.SHUT_RDWR)
292 except:
293 self.dbg(DEBUG_INFO, "Ignoring switch soc shutdown error")
294 self.switch_socket = None
Dan Talayco1b3f6902010-02-15 14:14:19 -0800295
Dan Talayco710438c2010-02-18 15:16:07 -0800296 try:
297 self.listen_socket.shutdown(socket.SHUT_RDWR)
298 except:
299 self.dbg(DEBUG_INFO, "Ignoring listen soc shutdown error")
300 self.listen_socket = None
301 self.dbg_state = "down"
302
Dan Talayco21c75c72010-02-12 22:59:24 -0800303 def _pkt_handler_check(self, pkt):
304 """
305 Check for packet handling before being enqueued
306
307 This includes checking for an ongoing transaction (see transact())
308 an echo request in case keep_alive is true, followed by
309 registered message handlers.
310 @param pkt The raw packet (string)
311 @return (handled, msg) where handled is a boolean indicating
312 the message was handled; msg if None is the parsed message
313 """
314 # Parse the header to get type
315 hdr = of_header_parse(pkt)
316 if not hdr:
317 self.dbg(DEBUG_INFO, "Could not parse header, pkt len", len(pkt))
318 self.parse_errors += 1
319 return (True, None)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800320 # if self.debug_level <= DEBUG_VERBOSE:
321 # hdr.show()
Dan Talayco21c75c72010-02-12 22:59:24 -0800322
323 self.dbg(DEBUG_VERBOSE, "message: len %d. type %s. hdr.len %d" %
324 (len(pkt), ofp_type_map[hdr.type], hdr.length))
325 msg = of_message_parse(pkt)
326 if not msg:
327 self.parse_errors += 1
328 self.dbg(DEBUG_WARN, "Could not parse message")
329 return (True, None)
330
331 # Check if transaction is waiting
332 self.xid_cv.acquire()
333 if self.xid:
334 if hdr.xid == self.xid:
Dan Talaycoe226eb12010-02-18 23:06:30 -0800335 self.xid_response = (msg, pkt)
336 self.xid = None
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800337 self.xid_cv.notify()
Dan Talayco21c75c72010-02-12 22:59:24 -0800338 self.xid_cv.release()
339 return (True, None)
340 self.xid_cv.release()
341
Dan Talaycoe226eb12010-02-18 23:06:30 -0800342 # Check if anyone waiting on this type of message
343 self.expect_msg_cv.acquire()
344 if self.expect_msg:
345 if not self.expect_msg_type or self.expect_msg_type == hdr.type:
346 self.expect_msg_response = (msg, pkt)
347 self.expect_msg = False
348 self.expect_msg_cv.notify()
349 self.expect_msg_cv.release()
350 return (True, None)
351 self.expect_msg_cv.release()
352
Dan Talayco21c75c72010-02-12 22:59:24 -0800353 # Check if keep alive is set; if so, respond to echo requests
354 if self.keep_alive:
355 if hdr.type == OFPT_ECHO_REQUEST:
356 rep = echo_reply()
357 rep.header.xid = hdr.xid
358 # Ignoring additional data
Dan Talayco710438c2010-02-18 15:16:07 -0800359 self.message_send(rep.pack(), zero_xid=True)
Dan Talayco21c75c72010-02-12 22:59:24 -0800360 return (True, None)
361
362 # Now check for message handlers; preference is given to
363 # handlers for a specific packet
364 handled = False
365 if hdr.type in self.handlers.keys():
366 handled = self.handlers[hdr.type](self, msg, pkt)
367 if not handled and ("all" in self.handlers.keys()):
368 handled = self.handlers["all"](self, msg, pkt)
369
370 return (handled, msg)
371
Dan Talayco34089522010-02-07 23:07:41 -0800372 def register(self, msg_type, handler):
373 """
374 Register a callback to receive a specific message type.
375
376 Only one handler may be registered for a given message type.
377 @param msg_type The type of message to receive. May be DEFAULT
Dan Talayco21c75c72010-02-12 22:59:24 -0800378 for all non-handled packets. The special type, the string "all"
379 will send all packets to the handler.
Dan Talayco34089522010-02-07 23:07:41 -0800380 @param handler The function to call when a message of the given
381 type is received.
382 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800383 # Should check type is valid
384 if not handler and msg_type in self.handlers.keys():
385 del self.handlers[msg_type]
386 return
387 self.handlers[msg_type] = handler
Dan Talayco34089522010-02-07 23:07:41 -0800388
Dan Talayco21c75c72010-02-12 22:59:24 -0800389 def poll(self, exp_msg=None, timeout=None):
Dan Talayco34089522010-02-07 23:07:41 -0800390 """
391 Wait for the next OF message received from the switch.
392
393 @param exp_msg If set, return only when this type of message
394 is received.
Dan Talaycoe226eb12010-02-18 23:06:30 -0800395 @param timeout If None, do not block. Otherwise, sleep in
396 intervals of 1 second until
Dan Talayco34089522010-02-07 23:07:41 -0800397
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800398 @retval A pair (msg, pkt) where msg is a message object and pkt
399 the string representing the packet as received from the socket.
400 This allows additional parsing by the receiver if necessary.
401
Dan Talayco34089522010-02-07 23:07:41 -0800402 The data members in the message are in host endian order.
Dan Talayco21c75c72010-02-12 22:59:24 -0800403 If an error occurs, None is returned
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 Talaycoe226eb12010-02-18 23:06:30 -0800408 # First check the current queue
409 self.sync.acquire()
410 if len(self.packets) > 0:
411 if not exp_msg:
412 (msg, pkt) = self.packets.pop(0)
413 self.sync.release()
414 return (msg, pkt)
415 else:
416 for i in range(len(self.packets)):
417 msg = self.packets[i][0]
418 if msg.header.type == exp_msg:
419 (msg, pkt) = self.packets.pop(i)
420 self.sync.release()
421 return (msg, pkt)
422
423 # Okay, not currently in the queue
424 if timeout is None or timeout <= 0:
Dan Talayco21c75c72010-02-12 22:59:24 -0800425 self.sync.release()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800426 return (None, None)
Dan Talayco21c75c72010-02-12 22:59:24 -0800427
Dan Talaycoe226eb12010-02-18 23:06:30 -0800428 # Careful of race condition releasing sync before message cv
429 self.expect_msg_cv.acquire()
430 self.sync.release()
431 self.expect_msg = True
432 self.expect_msg_type = exp_msg
433 self.expect_msg_cv.wait(timeout)
434 if self.expect_msg_response is not None:
435 (msg, pkt) = self.expect_msg_response
436 self.expect_msg_response = None
437 self.expect_msg_cv.release()
438
439 if msg is None:
440 self.dbg(DEBUG_VERBOSE, "poll time out")
441
442 return (msg, pkt)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800443
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800444 def transact(self, msg, timeout=None, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800445 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800446 Run a message transaction with the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800447
448 Send the message in msg and wait for a reply with a matching
Dan Talayco21c75c72010-02-12 22:59:24 -0800449 transaction id. Transactions have the highest priority in
450 received message handling.
Dan Talaycoe37999f2010-02-09 15:27:12 -0800451
Dan Talayco21c75c72010-02-12 22:59:24 -0800452 @param msg The message object to send; must not be a string
453 @param timeout The timeout in seconds (?)
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800454 @param zero_xid Normally, if the XID is 0 an XID will be generated
455 for the message. Set xero_xid to override this behavior
Dan Talayco21c75c72010-02-12 22:59:24 -0800456 @return The matching message object or None if unsuccessful
Dan Talaycoe37999f2010-02-09 15:27:12 -0800457
Dan Talayco34089522010-02-07 23:07:41 -0800458 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800459
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800460 if not zero_xid and msg.header.xid == 0:
461 msg.header.xid = gen_xid()
462
Dan Talayco21c75c72010-02-12 22:59:24 -0800463 self.xid_cv.acquire()
464 if self.xid:
465 self.xid_cv.release()
466 self.dbg(DEBUG_ERROR,
467 "Can only run one transaction at a time")
468 return None
469
470 self.xid = msg.header.xid
471 self.xid_response = None
472 self.message_send(msg.pack())
473 self.xid_cv.wait(timeout)
Dan Talaycoe226eb12010-02-18 23:06:30 -0800474 (msg, pkt) = self.xid_response
Dan Talayco21c75c72010-02-12 22:59:24 -0800475 self.xid_response = None
476 self.xid_cv.release()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800477 return (msg, pkt)
Dan Talayco34089522010-02-07 23:07:41 -0800478
Dan Talayco710438c2010-02-18 15:16:07 -0800479 def message_send(self, msg, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800480 """
481 Send the message to the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800482
Dan Talayco710438c2010-02-18 15:16:07 -0800483 @param msg A string or OpenFlow message object to be forwarded to
484 the switch.
485 @param zero_xid If msg is an OpenFlow object (not a string) and if
486 the XID in the header is 0, then an XID will be generated
487 for the message. Set xero_xid to override this behavior (and keep an
488 existing 0 xid)
Dan Talaycoe37999f2010-02-09 15:27:12 -0800489
Dan Talayco710438c2010-02-18 15:16:07 -0800490 @return -1 if error, 0 on success
Dan Talayco34089522010-02-07 23:07:41 -0800491
Dan Talayco21c75c72010-02-12 22:59:24 -0800492 """
493
Dan Talayco1b3f6902010-02-15 14:14:19 -0800494 if not self.switch_socket:
495 # Sending a string indicates the message is ready to go
496 self.dbg(DEBUG_INFO, "message_send: no socket")
497 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800498 #@todo If not string, try to pack
Dan Talayco21c75c72010-02-12 22:59:24 -0800499 if type(msg) != type(""):
Dan Talayco710438c2010-02-18 15:16:07 -0800500 try:
501 if msg.header.xid == 0 and not zero_xid:
502 msg.header.xid = gen_xid()
503 outpkt = msg.pack()
504 except:
505 self.dbg(DEBUG_INFO,
506 "message_send: not an OF message or string?")
507 return -1
508 else:
509 outpkt = msg
Dan Talayco21c75c72010-02-12 22:59:24 -0800510
Dan Talayco710438c2010-02-18 15:16:07 -0800511 self.dbg(DEBUG_INFO, "Sending pkt of len " + str(len(outpkt)))
512 if self.switch_socket.sendall(outpkt) is None:
513 return 0
514
515 self.dbg(DEBUG_ERROR, "Unknown error on sendall")
516 return -1
Dan Talayco21c75c72010-02-12 22:59:24 -0800517
518 def __str__(self):
519 string = "Controller:\n"
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800520 string += " state " + self.dbg_state + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800521 string += " switch_addr " + str(self.switch_addr) + "\n"
522 string += " pending pkts " + str(len(self.packets)) + "\n"
523 string += " total pkts " + str(self.packets_total) + "\n"
524 string += " expired pkts " + str(self.packets_expired) + "\n"
525 string += " handled pkts " + str(self.packets_handled) + "\n"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800526 string += " poll discards " + str(self.poll_discards) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800527 string += " parse errors " + str(self.parse_errors) + "\n"
528 string += " sock errrors " + str(self.socket_errors) + "\n"
529 string += " max pkts " + str(self.max_pkts) + "\n"
530 string += " host " + str(self.host) + "\n"
531 string += " port " + str(self.port) + "\n"
532 string += " keep_alive " + str(self.keep_alive) + "\n"
533 return string
534
535 def show(self):
536 print str(self)
537
538def sample_handler(controller, msg, pkt):
539 """
540 Sample message handler
541
542 This is the prototype for functions registered with the controller
543 class for packet reception
544
545 @param controller The controller calling the handler
546 @param msg The parsed message object
547 @param pkt The raw packet that was received on the socket. This is
548 in case the packet contains extra unparsed data.
549 @returns Boolean value indicating if the packet was handled. If
550 not handled, the packet is placed in the queue for pollers to received
551 """
552 pass