blob: 079f7efe0c39835bf38fb8b375afbe736557dff1 [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/
Dan Talaycod12b6612010-03-07 22:00:46 -080066 @var keep_alive If true, listen for echo requests and respond w/
Dan Talayco21c75c72010-02-12 22:59:24 -080067 echo replies
Dan Talayco710438c2010-02-18 15:16:07 -080068 @var initial_hello If true, will send a hello message immediately
69 upon connecting to the switch
Dan Talaycod12b6612010-03-07 22:00:46 -080070 @var exit_on_reset If true, terminate controller on connection reset
Dan Talayco21c75c72010-02-12 22:59:24 -080071 @var host The host to use for connect
72 @var port The port to connect on
73 @var packets_total Total number of packets received
74 @var packets_expired Number of packets popped from queue as queue full
75 @var packets_handled Number of packets handled by something
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080076 @var dbg_state Debug indication of state
Dan Talayco34089522010-02-07 23:07:41 -080077 """
78
Dan Talayco48370102010-03-03 15:17:33 -080079 def __init__(self, host='127.0.0.1', port=6633, max_pkts=1024):
Dan Talayco21c75c72010-02-12 22:59:24 -080080 Thread.__init__(self)
Dan Talayco1b3f6902010-02-15 14:14:19 -080081 # Socket related
Dan Talayco21c75c72010-02-12 22:59:24 -080082 self.rcv_size = RCV_SIZE_DEFAULT
Dan Talayco1b3f6902010-02-15 14:14:19 -080083 self.listen_socket = None
84 self.switch_socket = None
85 self.switch_addr = None
Dan Talayco710438c2010-02-18 15:16:07 -080086 self.socs = []
87 self.connect_cv = Condition()
Dan Talaycoe226eb12010-02-18 23:06:30 -080088 self.message_cv = Condition()
Dan Talayco1b3f6902010-02-15 14:14:19 -080089
90 # Counters
Dan Talayco21c75c72010-02-12 22:59:24 -080091 self.socket_errors = 0
92 self.parse_errors = 0
Dan Talayco21c75c72010-02-12 22:59:24 -080093 self.packets_total = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -080094 self.packets_expired = 0
95 self.packets_handled = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -080096 self.poll_discards = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -080097
98 # State
Dan Talayco21c75c72010-02-12 22:59:24 -080099 self.packets = []
100 self.sync = Lock()
101 self.handlers = {}
102 self.keep_alive = False
Dan Talayco710438c2010-02-18 15:16:07 -0800103 self.active = True
104 self.initial_hello = True
Dan Talaycod12b6612010-03-07 22:00:46 -0800105 self.exit_on_reset = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800106
107 # Settings
108 self.max_pkts = max_pkts
109 self.passive = True
Dan Talayco48370102010-03-03 15:17:33 -0800110 self.host = host
111 self.port = port
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800112 self.dbg_state = "init"
Dan Talayco48370102010-03-03 15:17:33 -0800113 self.logger = logging.getLogger("controller")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800114
Dan Talaycoe226eb12010-02-18 23:06:30 -0800115 # Transaction and message type waiting variables
116 # xid_cv: Condition variable (semaphore) for packet waiters
Dan Talayco21c75c72010-02-12 22:59:24 -0800117 # xid: Transaction ID being waited on
118 # xid_response: Transaction response message
Dan Talaycoe226eb12010-02-18 23:06:30 -0800119 # expect_msg: Is a message being waited on
120 # expect_msg_cv: Semaphore for waiters
121 # expect_msg_type: Type of message expected
122 # expect_msg_response: Result passed through here
123
Dan Talayco21c75c72010-02-12 22:59:24 -0800124 self.xid_cv = Condition()
125 self.xid = None
126 self.xid_response = None
127
Dan Talaycoe226eb12010-02-18 23:06:30 -0800128 self.expect_msg = False
129 self.expect_msg_cv = Condition()
130 self.expect_msg_type = None
131 self.expect_msg_response = None
132
Dan Talaycod12b6612010-03-07 22:00:46 -0800133 def _pkt_handle(self, pkt):
134 """
135 Check for all packet handling conditions
136
137 Parse and verify message
138 Check if XID matches something waiting
139 Check if message is being expected for a poll operation
140 Check if keep alive is on and message is an echo request
141 Check if any registered handler wants the packet
142 Enqueue if none of those conditions is met
143
144 an echo request in case keep_alive is true, followed by
145 registered message handlers.
146 @param pkt The raw packet (string)
147 """
148 # Parse the header to get type
149 hdr = of_header_parse(pkt)
150 if not hdr:
151 self.logger.info("Could not parse header, pkt len", len(pkt))
152 self.parse_errors += 1
153 return
154
155 self.logger.debug("Msg in: len %d. type %s. hdr.len %d" %
156 (len(pkt), ofp_type_map[hdr.type], hdr.length))
157 msg = of_message_parse(pkt)
158 if not msg:
159 self.parse_errors += 1
160 self.logger.warn("Could not parse message")
161 return
162
163 self.sync.acquire()
164
165 # Check if transaction is waiting
166 self.xid_cv.acquire()
167 if self.xid:
168 if hdr.xid == self.xid:
169 self.logger.debug("Matched expected XID " + str(hdr.xid))
170 self.xid_response = (msg, pkt)
171 self.xid = None
172 self.xid_cv.notify()
173 self.xid_cv.release()
174 self.sync.release()
175 return
176 self.xid_cv.release()
177
178 # PREVENT QUEUE ACCESS AT THIS POINT?
179 # Check if anyone waiting on this type of message
180 self.expect_msg_cv.acquire()
181 if self.expect_msg:
182 if not self.expect_msg_type or (self.expect_msg_type == hdr.type):
183 self.logger.debug("Matched expected msg type "
184 + ofp_type_map[hdr.type])
185 self.expect_msg_response = (msg, pkt)
186 self.expect_msg = False
187 self.expect_msg_cv.notify()
188 self.expect_msg_cv.release()
189 self.sync.release()
190 return
191 self.expect_msg_cv.release()
192
193 # Check if keep alive is set; if so, respond to echo requests
194 if self.keep_alive:
195 if hdr.type == OFPT_ECHO_REQUEST:
196 self.sync.release()
197 self.logger.debug("Responding to echo request")
198 rep = echo_reply()
199 rep.header.xid = hdr.xid
200 # Ignoring additional data
201 self.message_send(rep.pack(), zero_xid=True)
202 return
203
204 # Now check for message handlers; preference is given to
205 # handlers for a specific packet
206 handled = False
207 if hdr.type in self.handlers.keys():
208 handled = self.handlers[hdr.type](self, msg, pkt)
209 if not handled and ("all" in self.handlers.keys()):
210 handled = self.handlers["all"](self, msg, pkt)
211
212 if not handled: # Not handled, enqueue
213 self.logger.debug("Enqueuing pkt type " + ofp_type_map[hdr.type])
214 if len(self.packets) >= self.max_pkts:
215 self.packets.pop(0)
216 self.packets_expired += 1
217 self.packets.append((msg, pkt))
218 self.packets_total += 1
219 else:
220 self.packets_handled += 1
221 self.logger.debug("Message handled by callback")
222
223 self.sync.release()
224
Dan Talayco710438c2010-02-18 15:16:07 -0800225 def _socket_ready_handle(self, s):
226 """
227 Handle an input-ready socket
228 @param s The socket object that is ready
229 @retval True, reset the switch connection
230 """
231
232 if s == self.listen_socket:
233 if self.switch_socket:
Dan Talayco48370102010-03-03 15:17:33 -0800234 self.logger.error("Multiple switch cxns not supported")
Dan Talayco710438c2010-02-18 15:16:07 -0800235 sys.exit(1)
236
237 (self.switch_socket, self.switch_addr) = \
238 self.listen_socket.accept()
Dan Talayco48370102010-03-03 15:17:33 -0800239 self.logger.info("Got cxn to " + str(self.switch_addr))
Dan Talayco710438c2010-02-18 15:16:07 -0800240 # Notify anyone waiting
241 self.connect_cv.acquire()
242 self.connect_cv.notify()
243 self.connect_cv.release()
244 self.socs.append(self.switch_socket)
245 if self.initial_hello:
246 self.message_send(hello())
247 elif s == self.switch_socket:
248 try:
249 pkt = self.switch_socket.recv(self.rcv_size)
250 except:
Dan Talaycod12b6612010-03-07 22:00:46 -0800251 self.logger.warning("Error on switch read")
Dan Talayco710438c2010-02-18 15:16:07 -0800252 return True
253
254 if not self.active:
255 return False
256
257 if len(pkt) == 0:
Dan Talayco48370102010-03-03 15:17:33 -0800258 self.logger.info("zero-len pkt in")
Dan Talayco710438c2010-02-18 15:16:07 -0800259 return True
260
Dan Talaycod12b6612010-03-07 22:00:46 -0800261 self._pkt_handle(pkt)
Dan Talayco710438c2010-02-18 15:16:07 -0800262 else:
Dan Talayco48370102010-03-03 15:17:33 -0800263 self.logger.error("Unknown socket ready: " + str(s))
Dan Talayco710438c2010-02-18 15:16:07 -0800264 return True
265
266 return False
267
Dan Talayco1b3f6902010-02-15 14:14:19 -0800268 def run(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800269 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800270 Activity function for class
Dan Talayco21c75c72010-02-12 22:59:24 -0800271
Dan Talayco1b3f6902010-02-15 14:14:19 -0800272 Assumes connection to switch already exists. Listens on
273 switch_socket for messages until an error (or zero len pkt)
274 occurs.
Dan Talayco21c75c72010-02-12 22:59:24 -0800275
Dan Talayco1b3f6902010-02-15 14:14:19 -0800276 When there is a message on the socket, check for handlers; queue the
277 packet if no one handles the packet.
278
279 See note for controller describing the limitation of a single
280 connection for now.
281 """
282
Dan Talayco710438c2010-02-18 15:16:07 -0800283 self.dbg_state = "starting"
Dan Talayco1b3f6902010-02-15 14:14:19 -0800284
Dan Talayco710438c2010-02-18 15:16:07 -0800285 # Create listen socket
Dan Talayco48370102010-03-03 15:17:33 -0800286 self.logger.info("Create/listen at " + self.host + ":" +
Dan Talayco710438c2010-02-18 15:16:07 -0800287 str(self.port))
288 self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
289 self.listen_socket.setsockopt(socket.SOL_SOCKET,
290 socket.SO_REUSEADDR, 1)
291 self.listen_socket.bind((self.host, self.port))
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800292 self.dbg_state = "listening"
Dan Talayco21c75c72010-02-12 22:59:24 -0800293 self.listen_socket.listen(LISTEN_QUEUE_SIZE)
Dan Talayco21c75c72010-02-12 22:59:24 -0800294
Dan Talayco48370102010-03-03 15:17:33 -0800295 self.logger.info("Waiting for switch connection")
Dan Talayco710438c2010-02-18 15:16:07 -0800296 self.socs = [self.listen_socket]
297 self.dbg_state = "running"
298 while self.active:
299 reset_switch_cxn = False
300 try:
301 sel_in, sel_out, sel_err = \
302 select.select(self.socs, [], self.socs, 1)
303 except:
304 print sys.exc_info()
Dan Talayco48370102010-03-03 15:17:33 -0800305 self.logger.error("Select error, exiting")
Dan Talayco710438c2010-02-18 15:16:07 -0800306 sys.exit(1)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800307
Dan Talayco710438c2010-02-18 15:16:07 -0800308 if not self.active:
309 break
Dan Talayco1b3f6902010-02-15 14:14:19 -0800310
Dan Talayco710438c2010-02-18 15:16:07 -0800311 for s in sel_in:
312 reset_switch_cxn = self._socket_ready_handle(s)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800313
Dan Talayco710438c2010-02-18 15:16:07 -0800314 for s in sel_err:
Dan Talayco48370102010-03-03 15:17:33 -0800315 self.logger.error("Got socket error on: " + str(s))
Dan Talayco710438c2010-02-18 15:16:07 -0800316 if s == self.switch_socket:
317 reset_switch_cxn = True
318 else:
Dan Talayco48370102010-03-03 15:17:33 -0800319 self.logger.error("Socket error; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -0800320 self.active = False
321 break
322
323 if self.active and reset_switch_cxn:
Dan Talaycod12b6612010-03-07 22:00:46 -0800324 if self.exit_on_reset:
325 self.kill()
326 else:
327 self.logger.warning("Closing switch cxn")
328 try:
329 self.switch_socket.close()
330 except:
331 pass
332 self.switch_socket = None
333 self.socs = self.socs[0:1]
Dan Talayco710438c2010-02-18 15:16:07 -0800334
335 # End of main loop
336 self.dbg_state = "closing"
Dan Talayco48370102010-03-03 15:17:33 -0800337 self.logger.info("Exiting controller thread")
Dan Talayco710438c2010-02-18 15:16:07 -0800338 self.shutdown()
339
340 def connect(self, timeout=None):
341 """
342 Connect to the switch
343
344 @param timeout If None, block until connected. If 0, return
345 immedidately. Otherwise, block for up to timeout seconds
346 @return Boolean, True if connected
347 """
348
349 if timeout == 0:
350 return self.switch_socket is not None
351 if self.switch_socket is not None:
352 return True
353 self.connect_cv.acquire()
354 self.connect_cv.wait(timeout)
355 self.connect_cv.release()
356
357 return self.switch_socket is not None
358
359 def kill(self):
360 """
361 Force the controller thread to quit
362
363 Just sets the active state variable to false and expects
364 the select timeout to kick in
365 """
366 self.active = False
Dan Talayco21c75c72010-02-12 22:59:24 -0800367
Dan Talayco1b3f6902010-02-15 14:14:19 -0800368 def shutdown(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800369 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800370 Shutdown the controller closing all sockets
Dan Talayco21c75c72010-02-12 22:59:24 -0800371
Dan Talayco1b3f6902010-02-15 14:14:19 -0800372 @todo Might want to synchronize shutdown with self.sync...
373 """
Dan Talayco710438c2010-02-18 15:16:07 -0800374 self.active = False
375 try:
376 self.switch_socket.shutdown(socket.SHUT_RDWR)
377 except:
Dan Talayco48370102010-03-03 15:17:33 -0800378 self.logger.info("Ignoring switch soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800379 self.switch_socket = None
Dan Talayco1b3f6902010-02-15 14:14:19 -0800380
Dan Talayco710438c2010-02-18 15:16:07 -0800381 try:
382 self.listen_socket.shutdown(socket.SHUT_RDWR)
383 except:
Dan Talayco48370102010-03-03 15:17:33 -0800384 self.logger.info("Ignoring listen soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800385 self.listen_socket = None
386 self.dbg_state = "down"
387
Dan Talayco34089522010-02-07 23:07:41 -0800388 def register(self, msg_type, handler):
389 """
390 Register a callback to receive a specific message type.
391
392 Only one handler may be registered for a given message type.
Dan Talaycod12b6612010-03-07 22:00:46 -0800393
394 WARNING: A lock is held during the handler call back, so
395 the handler should not make any blocking calls
396
Dan Talayco34089522010-02-07 23:07:41 -0800397 @param msg_type The type of message to receive. May be DEFAULT
Dan Talayco21c75c72010-02-12 22:59:24 -0800398 for all non-handled packets. The special type, the string "all"
399 will send all packets to the handler.
Dan Talayco34089522010-02-07 23:07:41 -0800400 @param handler The function to call when a message of the given
401 type is received.
402 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800403 # Should check type is valid
404 if not handler and msg_type in self.handlers.keys():
405 del self.handlers[msg_type]
406 return
407 self.handlers[msg_type] = handler
Dan Talayco34089522010-02-07 23:07:41 -0800408
Dan Talayco21c75c72010-02-12 22:59:24 -0800409 def poll(self, exp_msg=None, timeout=None):
Dan Talayco34089522010-02-07 23:07:41 -0800410 """
411 Wait for the next OF message received from the switch.
412
413 @param exp_msg If set, return only when this type of message
Dan Talayco48370102010-03-03 15:17:33 -0800414 is received (unless timeout occurs).
Dan Talaycoe226eb12010-02-18 23:06:30 -0800415 @param timeout If None, do not block. Otherwise, sleep in
Dan Talayco48370102010-03-03 15:17:33 -0800416 intervals of 1 second until message is received.
Dan Talayco34089522010-02-07 23:07:41 -0800417
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800418 @retval A pair (msg, pkt) where msg is a message object and pkt
419 the string representing the packet as received from the socket.
420 This allows additional parsing by the receiver if necessary.
421
Dan Talayco34089522010-02-07 23:07:41 -0800422 The data members in the message are in host endian order.
Dan Talayco48370102010-03-03 15:17:33 -0800423 If an error occurs, (None, None) is returned
424
425 The current queue is searched for a message of the desired type
426 before sleeping on message in events.
Dan Talayco34089522010-02-07 23:07:41 -0800427 """
Dan Talayco34089522010-02-07 23:07:41 -0800428
Dan Talaycoe226eb12010-02-18 23:06:30 -0800429 msg = pkt = None
Dan Talayco34089522010-02-07 23:07:41 -0800430
Dan Talayco48370102010-03-03 15:17:33 -0800431 self.logger.debug("Poll for " + ofp_type_map[exp_msg])
Dan Talaycoe226eb12010-02-18 23:06:30 -0800432 # First check the current queue
433 self.sync.acquire()
434 if len(self.packets) > 0:
435 if not exp_msg:
436 (msg, pkt) = self.packets.pop(0)
437 self.sync.release()
438 return (msg, pkt)
439 else:
440 for i in range(len(self.packets)):
441 msg = self.packets[i][0]
442 if msg.header.type == exp_msg:
443 (msg, pkt) = self.packets.pop(i)
444 self.sync.release()
445 return (msg, pkt)
446
447 # Okay, not currently in the queue
448 if timeout is None or timeout <= 0:
Dan Talayco21c75c72010-02-12 22:59:24 -0800449 self.sync.release()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800450 return (None, None)
Dan Talayco21c75c72010-02-12 22:59:24 -0800451
Dan Talayco48370102010-03-03 15:17:33 -0800452 msg = pkt = None
453 self.logger.debug("Entering timeout")
Dan Talaycoe226eb12010-02-18 23:06:30 -0800454 # Careful of race condition releasing sync before message cv
Dan Talayco90576bd2010-02-19 10:59:02 -0800455 # Also, this style is ripe for a lockup.
Dan Talaycoe226eb12010-02-18 23:06:30 -0800456 self.expect_msg_cv.acquire()
457 self.sync.release()
Dan Talayco48370102010-03-03 15:17:33 -0800458 self.expect_msg_response = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800459 self.expect_msg = True
460 self.expect_msg_type = exp_msg
461 self.expect_msg_cv.wait(timeout)
462 if self.expect_msg_response is not None:
463 (msg, pkt) = self.expect_msg_response
Dan Talaycoe226eb12010-02-18 23:06:30 -0800464 self.expect_msg_cv.release()
465
466 if msg is None:
Dan Talayco48370102010-03-03 15:17:33 -0800467 self.logger.debug("Poll time out")
468 else:
469 self.logger.debug("Got msg " + str(msg))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800470
471 return (msg, pkt)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800472
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800473 def transact(self, msg, timeout=None, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800474 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800475 Run a message transaction with the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800476
477 Send the message in msg and wait for a reply with a matching
Dan Talayco21c75c72010-02-12 22:59:24 -0800478 transaction id. Transactions have the highest priority in
479 received message handling.
Dan Talaycoe37999f2010-02-09 15:27:12 -0800480
Dan Talayco21c75c72010-02-12 22:59:24 -0800481 @param msg The message object to send; must not be a string
482 @param timeout The timeout in seconds (?)
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800483 @param zero_xid Normally, if the XID is 0 an XID will be generated
484 for the message. Set xero_xid to override this behavior
Dan Talayco21c75c72010-02-12 22:59:24 -0800485 @return The matching message object or None if unsuccessful
Dan Talaycoe37999f2010-02-09 15:27:12 -0800486
Dan Talayco34089522010-02-07 23:07:41 -0800487 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800488
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800489 if not zero_xid and msg.header.xid == 0:
490 msg.header.xid = gen_xid()
491
Dan Talayco21c75c72010-02-12 22:59:24 -0800492 self.xid_cv.acquire()
493 if self.xid:
494 self.xid_cv.release()
Dan Talayco48370102010-03-03 15:17:33 -0800495 self.logger.error("Can only run one transaction at a time")
Dan Talayco21c75c72010-02-12 22:59:24 -0800496 return None
497
498 self.xid = msg.header.xid
499 self.xid_response = None
500 self.message_send(msg.pack())
501 self.xid_cv.wait(timeout)
Dan Talaycod12b6612010-03-07 22:00:46 -0800502 if self.xid_response:
503 (msg, pkt) = self.xid_response
504 self.xid_response = None
505 else:
506 (msg, pkt) = (None, None)
Dan Talayco21c75c72010-02-12 22:59:24 -0800507 self.xid_cv.release()
Dan Talaycod12b6612010-03-07 22:00:46 -0800508 if msg is None:
509 self.logger.warning("No response for xid " + str(msg.header.xid))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800510 return (msg, pkt)
Dan Talayco34089522010-02-07 23:07:41 -0800511
Dan Talayco710438c2010-02-18 15:16:07 -0800512 def message_send(self, msg, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800513 """
514 Send the message to the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800515
Dan Talayco710438c2010-02-18 15:16:07 -0800516 @param msg A string or OpenFlow message object to be forwarded to
517 the switch.
518 @param zero_xid If msg is an OpenFlow object (not a string) and if
519 the XID in the header is 0, then an XID will be generated
520 for the message. Set xero_xid to override this behavior (and keep an
521 existing 0 xid)
Dan Talaycoe37999f2010-02-09 15:27:12 -0800522
Dan Talayco710438c2010-02-18 15:16:07 -0800523 @return -1 if error, 0 on success
Dan Talayco34089522010-02-07 23:07:41 -0800524
Dan Talayco21c75c72010-02-12 22:59:24 -0800525 """
526
Dan Talayco1b3f6902010-02-15 14:14:19 -0800527 if not self.switch_socket:
528 # Sending a string indicates the message is ready to go
Dan Talayco48370102010-03-03 15:17:33 -0800529 self.logger.info("message_send: no socket")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800530 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800531 #@todo If not string, try to pack
Dan Talayco21c75c72010-02-12 22:59:24 -0800532 if type(msg) != type(""):
Dan Talayco710438c2010-02-18 15:16:07 -0800533 try:
534 if msg.header.xid == 0 and not zero_xid:
535 msg.header.xid = gen_xid()
536 outpkt = msg.pack()
537 except:
Dan Talayco48370102010-03-03 15:17:33 -0800538 self.logger.error(
Dan Talayco710438c2010-02-18 15:16:07 -0800539 "message_send: not an OF message or string?")
540 return -1
541 else:
542 outpkt = msg
Dan Talayco21c75c72010-02-12 22:59:24 -0800543
Dan Talayco48370102010-03-03 15:17:33 -0800544 self.logger.debug("Sending pkt of len " + str(len(outpkt)))
Dan Talayco710438c2010-02-18 15:16:07 -0800545 if self.switch_socket.sendall(outpkt) is None:
546 return 0
547
Dan Talayco48370102010-03-03 15:17:33 -0800548 self.logger.error("Unknown error on sendall")
Dan Talayco710438c2010-02-18 15:16:07 -0800549 return -1
Dan Talayco21c75c72010-02-12 22:59:24 -0800550
551 def __str__(self):
552 string = "Controller:\n"
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800553 string += " state " + self.dbg_state + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800554 string += " switch_addr " + str(self.switch_addr) + "\n"
555 string += " pending pkts " + str(len(self.packets)) + "\n"
556 string += " total pkts " + str(self.packets_total) + "\n"
557 string += " expired pkts " + str(self.packets_expired) + "\n"
558 string += " handled pkts " + str(self.packets_handled) + "\n"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800559 string += " poll discards " + str(self.poll_discards) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800560 string += " parse errors " + str(self.parse_errors) + "\n"
561 string += " sock errrors " + str(self.socket_errors) + "\n"
562 string += " max pkts " + str(self.max_pkts) + "\n"
563 string += " host " + str(self.host) + "\n"
564 string += " port " + str(self.port) + "\n"
565 string += " keep_alive " + str(self.keep_alive) + "\n"
566 return string
567
568 def show(self):
569 print str(self)
570
571def sample_handler(controller, msg, pkt):
572 """
573 Sample message handler
574
575 This is the prototype for functions registered with the controller
576 class for packet reception
577
578 @param controller The controller calling the handler
579 @param msg The parsed message object
580 @param pkt The raw packet that was received on the socket. This is
581 in case the packet contains extra unparsed data.
582 @returns Boolean value indicating if the packet was handled. If
583 not handled, the packet is placed in the queue for pollers to received
584 """
585 pass