blob: 99a6ffb2628f4dfce3968f84387b6d8846b36995 [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))
Dan Talaycob2d1d042010-05-07 09:23:11 -0700157 if hdr.version != OFP_VERSION:
158 self.logger.error("Version %d does not match OFTest version %d"
159 % (hdr.version, OFP_VERSION))
160 print "Version %d does not match OFTest version %d" % \
161 (hdr.version, OFP_VERSION)
162 self.active = False
163 self.switch_socket = None
164 self.kill()
165
Dan Talaycod12b6612010-03-07 22:00:46 -0800166 msg = of_message_parse(pkt)
167 if not msg:
168 self.parse_errors += 1
169 self.logger.warn("Could not parse message")
170 return
171
172 self.sync.acquire()
173
174 # Check if transaction is waiting
175 self.xid_cv.acquire()
176 if self.xid:
177 if hdr.xid == self.xid:
178 self.logger.debug("Matched expected XID " + str(hdr.xid))
179 self.xid_response = (msg, pkt)
180 self.xid = None
181 self.xid_cv.notify()
182 self.xid_cv.release()
183 self.sync.release()
184 return
185 self.xid_cv.release()
186
187 # PREVENT QUEUE ACCESS AT THIS POINT?
188 # Check if anyone waiting on this type of message
189 self.expect_msg_cv.acquire()
190 if self.expect_msg:
191 if not self.expect_msg_type or (self.expect_msg_type == hdr.type):
192 self.logger.debug("Matched expected msg type "
193 + ofp_type_map[hdr.type])
194 self.expect_msg_response = (msg, pkt)
195 self.expect_msg = False
196 self.expect_msg_cv.notify()
197 self.expect_msg_cv.release()
198 self.sync.release()
199 return
200 self.expect_msg_cv.release()
201
202 # Check if keep alive is set; if so, respond to echo requests
203 if self.keep_alive:
204 if hdr.type == OFPT_ECHO_REQUEST:
205 self.sync.release()
206 self.logger.debug("Responding to echo request")
207 rep = echo_reply()
208 rep.header.xid = hdr.xid
209 # Ignoring additional data
210 self.message_send(rep.pack(), zero_xid=True)
211 return
212
213 # Now check for message handlers; preference is given to
214 # handlers for a specific packet
215 handled = False
216 if hdr.type in self.handlers.keys():
217 handled = self.handlers[hdr.type](self, msg, pkt)
218 if not handled and ("all" in self.handlers.keys()):
219 handled = self.handlers["all"](self, msg, pkt)
220
221 if not handled: # Not handled, enqueue
222 self.logger.debug("Enqueuing pkt type " + ofp_type_map[hdr.type])
223 if len(self.packets) >= self.max_pkts:
224 self.packets.pop(0)
225 self.packets_expired += 1
226 self.packets.append((msg, pkt))
227 self.packets_total += 1
228 else:
229 self.packets_handled += 1
230 self.logger.debug("Message handled by callback")
231
232 self.sync.release()
233
Dan Talayco710438c2010-02-18 15:16:07 -0800234 def _socket_ready_handle(self, s):
235 """
236 Handle an input-ready socket
237 @param s The socket object that is ready
238 @retval True, reset the switch connection
239 """
240
241 if s == self.listen_socket:
242 if self.switch_socket:
Dan Talayco48370102010-03-03 15:17:33 -0800243 self.logger.error("Multiple switch cxns not supported")
Dan Talayco710438c2010-02-18 15:16:07 -0800244 sys.exit(1)
245
246 (self.switch_socket, self.switch_addr) = \
247 self.listen_socket.accept()
Dan Talayco48370102010-03-03 15:17:33 -0800248 self.logger.info("Got cxn to " + str(self.switch_addr))
Dan Talayco710438c2010-02-18 15:16:07 -0800249 # Notify anyone waiting
250 self.connect_cv.acquire()
251 self.connect_cv.notify()
252 self.connect_cv.release()
253 self.socs.append(self.switch_socket)
254 if self.initial_hello:
255 self.message_send(hello())
256 elif s == self.switch_socket:
257 try:
258 pkt = self.switch_socket.recv(self.rcv_size)
259 except:
Dan Talaycod12b6612010-03-07 22:00:46 -0800260 self.logger.warning("Error on switch read")
Dan Talayco710438c2010-02-18 15:16:07 -0800261 return True
262
263 if not self.active:
264 return False
265
266 if len(pkt) == 0:
Dan Talayco48370102010-03-03 15:17:33 -0800267 self.logger.info("zero-len pkt in")
Dan Talayco710438c2010-02-18 15:16:07 -0800268 return True
269
Dan Talaycod12b6612010-03-07 22:00:46 -0800270 self._pkt_handle(pkt)
Dan Talayco710438c2010-02-18 15:16:07 -0800271 else:
Dan Talayco48370102010-03-03 15:17:33 -0800272 self.logger.error("Unknown socket ready: " + str(s))
Dan Talayco710438c2010-02-18 15:16:07 -0800273 return True
274
275 return False
276
Dan Talayco1b3f6902010-02-15 14:14:19 -0800277 def run(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800278 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800279 Activity function for class
Dan Talayco21c75c72010-02-12 22:59:24 -0800280
Dan Talayco1b3f6902010-02-15 14:14:19 -0800281 Assumes connection to switch already exists. Listens on
282 switch_socket for messages until an error (or zero len pkt)
283 occurs.
Dan Talayco21c75c72010-02-12 22:59:24 -0800284
Dan Talayco1b3f6902010-02-15 14:14:19 -0800285 When there is a message on the socket, check for handlers; queue the
286 packet if no one handles the packet.
287
288 See note for controller describing the limitation of a single
289 connection for now.
290 """
291
Dan Talayco710438c2010-02-18 15:16:07 -0800292 self.dbg_state = "starting"
Dan Talayco1b3f6902010-02-15 14:14:19 -0800293
Dan Talayco710438c2010-02-18 15:16:07 -0800294 # Create listen socket
Dan Talayco48370102010-03-03 15:17:33 -0800295 self.logger.info("Create/listen at " + self.host + ":" +
Dan Talayco710438c2010-02-18 15:16:07 -0800296 str(self.port))
297 self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
298 self.listen_socket.setsockopt(socket.SOL_SOCKET,
299 socket.SO_REUSEADDR, 1)
300 self.listen_socket.bind((self.host, self.port))
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800301 self.dbg_state = "listening"
Dan Talayco21c75c72010-02-12 22:59:24 -0800302 self.listen_socket.listen(LISTEN_QUEUE_SIZE)
Dan Talayco21c75c72010-02-12 22:59:24 -0800303
Dan Talayco48370102010-03-03 15:17:33 -0800304 self.logger.info("Waiting for switch connection")
Dan Talayco710438c2010-02-18 15:16:07 -0800305 self.socs = [self.listen_socket]
306 self.dbg_state = "running"
307 while self.active:
308 reset_switch_cxn = False
309 try:
310 sel_in, sel_out, sel_err = \
311 select.select(self.socs, [], self.socs, 1)
312 except:
313 print sys.exc_info()
Dan Talayco48370102010-03-03 15:17:33 -0800314 self.logger.error("Select error, exiting")
Dan Talayco710438c2010-02-18 15:16:07 -0800315 sys.exit(1)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800316
Dan Talayco710438c2010-02-18 15:16:07 -0800317 if not self.active:
318 break
Dan Talayco1b3f6902010-02-15 14:14:19 -0800319
Dan Talayco710438c2010-02-18 15:16:07 -0800320 for s in sel_in:
321 reset_switch_cxn = self._socket_ready_handle(s)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800322
Dan Talayco710438c2010-02-18 15:16:07 -0800323 for s in sel_err:
Dan Talayco48370102010-03-03 15:17:33 -0800324 self.logger.error("Got socket error on: " + str(s))
Dan Talayco710438c2010-02-18 15:16:07 -0800325 if s == self.switch_socket:
326 reset_switch_cxn = True
327 else:
Dan Talayco48370102010-03-03 15:17:33 -0800328 self.logger.error("Socket error; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -0800329 self.active = False
330 break
331
332 if self.active and reset_switch_cxn:
Dan Talaycod12b6612010-03-07 22:00:46 -0800333 if self.exit_on_reset:
334 self.kill()
335 else:
336 self.logger.warning("Closing switch cxn")
337 try:
338 self.switch_socket.close()
339 except:
340 pass
341 self.switch_socket = None
342 self.socs = self.socs[0:1]
Dan Talayco710438c2010-02-18 15:16:07 -0800343
344 # End of main loop
345 self.dbg_state = "closing"
Dan Talayco48370102010-03-03 15:17:33 -0800346 self.logger.info("Exiting controller thread")
Dan Talayco710438c2010-02-18 15:16:07 -0800347 self.shutdown()
348
349 def connect(self, timeout=None):
350 """
351 Connect to the switch
352
353 @param timeout If None, block until connected. If 0, return
354 immedidately. Otherwise, block for up to timeout seconds
355 @return Boolean, True if connected
356 """
357
358 if timeout == 0:
359 return self.switch_socket is not None
360 if self.switch_socket is not None:
361 return True
362 self.connect_cv.acquire()
363 self.connect_cv.wait(timeout)
364 self.connect_cv.release()
365
366 return self.switch_socket is not None
367
368 def kill(self):
369 """
370 Force the controller thread to quit
371
372 Just sets the active state variable to false and expects
373 the select timeout to kick in
374 """
375 self.active = False
Dan Talayco21c75c72010-02-12 22:59:24 -0800376
Dan Talayco1b3f6902010-02-15 14:14:19 -0800377 def shutdown(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800378 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800379 Shutdown the controller closing all sockets
Dan Talayco21c75c72010-02-12 22:59:24 -0800380
Dan Talayco1b3f6902010-02-15 14:14:19 -0800381 @todo Might want to synchronize shutdown with self.sync...
382 """
Dan Talayco710438c2010-02-18 15:16:07 -0800383 self.active = False
384 try:
385 self.switch_socket.shutdown(socket.SHUT_RDWR)
386 except:
Dan Talayco48370102010-03-03 15:17:33 -0800387 self.logger.info("Ignoring switch soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800388 self.switch_socket = None
Dan Talayco1b3f6902010-02-15 14:14:19 -0800389
Dan Talayco710438c2010-02-18 15:16:07 -0800390 try:
391 self.listen_socket.shutdown(socket.SHUT_RDWR)
392 except:
Dan Talayco48370102010-03-03 15:17:33 -0800393 self.logger.info("Ignoring listen soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800394 self.listen_socket = None
395 self.dbg_state = "down"
396
Dan Talayco34089522010-02-07 23:07:41 -0800397 def register(self, msg_type, handler):
398 """
399 Register a callback to receive a specific message type.
400
401 Only one handler may be registered for a given message type.
Dan Talaycod12b6612010-03-07 22:00:46 -0800402
403 WARNING: A lock is held during the handler call back, so
404 the handler should not make any blocking calls
405
Dan Talayco34089522010-02-07 23:07:41 -0800406 @param msg_type The type of message to receive. May be DEFAULT
Dan Talayco21c75c72010-02-12 22:59:24 -0800407 for all non-handled packets. The special type, the string "all"
408 will send all packets to the handler.
Dan Talayco34089522010-02-07 23:07:41 -0800409 @param handler The function to call when a message of the given
410 type is received.
411 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800412 # Should check type is valid
413 if not handler and msg_type in self.handlers.keys():
414 del self.handlers[msg_type]
415 return
416 self.handlers[msg_type] = handler
Dan Talayco34089522010-02-07 23:07:41 -0800417
Dan Talayco21c75c72010-02-12 22:59:24 -0800418 def poll(self, exp_msg=None, timeout=None):
Dan Talayco34089522010-02-07 23:07:41 -0800419 """
420 Wait for the next OF message received from the switch.
421
422 @param exp_msg If set, return only when this type of message
Dan Talayco48370102010-03-03 15:17:33 -0800423 is received (unless timeout occurs).
Dan Talaycoe226eb12010-02-18 23:06:30 -0800424 @param timeout If None, do not block. Otherwise, sleep in
Dan Talayco48370102010-03-03 15:17:33 -0800425 intervals of 1 second until message is received.
Dan Talayco34089522010-02-07 23:07:41 -0800426
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800427 @retval A pair (msg, pkt) where msg is a message object and pkt
428 the string representing the packet as received from the socket.
429 This allows additional parsing by the receiver if necessary.
430
Dan Talayco34089522010-02-07 23:07:41 -0800431 The data members in the message are in host endian order.
Dan Talayco48370102010-03-03 15:17:33 -0800432 If an error occurs, (None, None) is returned
433
434 The current queue is searched for a message of the desired type
435 before sleeping on message in events.
Dan Talayco34089522010-02-07 23:07:41 -0800436 """
Dan Talayco34089522010-02-07 23:07:41 -0800437
Dan Talaycoe226eb12010-02-18 23:06:30 -0800438 msg = pkt = None
Dan Talayco34089522010-02-07 23:07:41 -0800439
Dan Talayco48370102010-03-03 15:17:33 -0800440 self.logger.debug("Poll for " + ofp_type_map[exp_msg])
Dan Talaycoe226eb12010-02-18 23:06:30 -0800441 # First check the current queue
442 self.sync.acquire()
443 if len(self.packets) > 0:
444 if not exp_msg:
445 (msg, pkt) = self.packets.pop(0)
446 self.sync.release()
447 return (msg, pkt)
448 else:
449 for i in range(len(self.packets)):
450 msg = self.packets[i][0]
451 if msg.header.type == exp_msg:
452 (msg, pkt) = self.packets.pop(i)
453 self.sync.release()
454 return (msg, pkt)
455
456 # Okay, not currently in the queue
457 if timeout is None or timeout <= 0:
Dan Talayco21c75c72010-02-12 22:59:24 -0800458 self.sync.release()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800459 return (None, None)
Dan Talayco21c75c72010-02-12 22:59:24 -0800460
Dan Talayco48370102010-03-03 15:17:33 -0800461 msg = pkt = None
462 self.logger.debug("Entering timeout")
Dan Talaycoe226eb12010-02-18 23:06:30 -0800463 # Careful of race condition releasing sync before message cv
Dan Talayco90576bd2010-02-19 10:59:02 -0800464 # Also, this style is ripe for a lockup.
Dan Talaycoe226eb12010-02-18 23:06:30 -0800465 self.expect_msg_cv.acquire()
466 self.sync.release()
Dan Talayco48370102010-03-03 15:17:33 -0800467 self.expect_msg_response = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800468 self.expect_msg = True
469 self.expect_msg_type = exp_msg
470 self.expect_msg_cv.wait(timeout)
471 if self.expect_msg_response is not None:
472 (msg, pkt) = self.expect_msg_response
Dan Talaycoe226eb12010-02-18 23:06:30 -0800473 self.expect_msg_cv.release()
474
475 if msg is None:
Dan Talayco48370102010-03-03 15:17:33 -0800476 self.logger.debug("Poll time out")
477 else:
478 self.logger.debug("Got msg " + str(msg))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800479
480 return (msg, pkt)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800481
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800482 def transact(self, msg, timeout=None, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800483 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800484 Run a message transaction with the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800485
486 Send the message in msg and wait for a reply with a matching
Dan Talayco21c75c72010-02-12 22:59:24 -0800487 transaction id. Transactions have the highest priority in
488 received message handling.
Dan Talaycoe37999f2010-02-09 15:27:12 -0800489
Dan Talayco21c75c72010-02-12 22:59:24 -0800490 @param msg The message object to send; must not be a string
491 @param timeout The timeout in seconds (?)
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800492 @param zero_xid Normally, if the XID is 0 an XID will be generated
493 for the message. Set xero_xid to override this behavior
Dan Talayco21c75c72010-02-12 22:59:24 -0800494 @return The matching message object or None if unsuccessful
Dan Talaycoe37999f2010-02-09 15:27:12 -0800495
Dan Talayco34089522010-02-07 23:07:41 -0800496 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800497
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800498 if not zero_xid and msg.header.xid == 0:
499 msg.header.xid = gen_xid()
500
Dan Talayco21c75c72010-02-12 22:59:24 -0800501 self.xid_cv.acquire()
502 if self.xid:
503 self.xid_cv.release()
Dan Talayco48370102010-03-03 15:17:33 -0800504 self.logger.error("Can only run one transaction at a time")
Dan Talayco21c75c72010-02-12 22:59:24 -0800505 return None
506
507 self.xid = msg.header.xid
508 self.xid_response = None
509 self.message_send(msg.pack())
510 self.xid_cv.wait(timeout)
Dan Talaycod12b6612010-03-07 22:00:46 -0800511 if self.xid_response:
Dan Talayco09c2c592010-05-13 14:21:52 -0700512 (resp, pkt) = self.xid_response
Dan Talaycod12b6612010-03-07 22:00:46 -0800513 self.xid_response = None
514 else:
Dan Talayco09c2c592010-05-13 14:21:52 -0700515 (resp, pkt) = (None, None)
Dan Talayco21c75c72010-02-12 22:59:24 -0800516 self.xid_cv.release()
Dan Talayco09c2c592010-05-13 14:21:52 -0700517 if resp is None:
518 self.logger.warning("No response for xid " + str(self.xid))
519 return (resp, pkt)
Dan Talayco34089522010-02-07 23:07:41 -0800520
Dan Talayco710438c2010-02-18 15:16:07 -0800521 def message_send(self, msg, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800522 """
523 Send the message to the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800524
Dan Talayco11c26e72010-03-07 22:03:57 -0800525 @param msg A string or OpenFlow message object to be forwarded to
526 the switch.
527 @param zero_xid If msg is an OpenFlow object (not a string) and if
Dan Talayco710438c2010-02-18 15:16:07 -0800528 the XID in the header is 0, then an XID will be generated
529 for the message. Set xero_xid to override this behavior (and keep an
530 existing 0 xid)
Dan Talaycoe37999f2010-02-09 15:27:12 -0800531
Dan Talayco710438c2010-02-18 15:16:07 -0800532 @return -1 if error, 0 on success
Dan Talayco34089522010-02-07 23:07:41 -0800533
Dan Talayco21c75c72010-02-12 22:59:24 -0800534 """
535
Dan Talayco1b3f6902010-02-15 14:14:19 -0800536 if not self.switch_socket:
537 # Sending a string indicates the message is ready to go
Dan Talayco48370102010-03-03 15:17:33 -0800538 self.logger.info("message_send: no socket")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800539 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800540 #@todo If not string, try to pack
Dan Talayco21c75c72010-02-12 22:59:24 -0800541 if type(msg) != type(""):
Dan Talayco710438c2010-02-18 15:16:07 -0800542 try:
543 if msg.header.xid == 0 and not zero_xid:
544 msg.header.xid = gen_xid()
545 outpkt = msg.pack()
546 except:
Dan Talayco48370102010-03-03 15:17:33 -0800547 self.logger.error(
Dan Talayco710438c2010-02-18 15:16:07 -0800548 "message_send: not an OF message or string?")
549 return -1
550 else:
551 outpkt = msg
Dan Talayco21c75c72010-02-12 22:59:24 -0800552
Dan Talayco48370102010-03-03 15:17:33 -0800553 self.logger.debug("Sending pkt of len " + str(len(outpkt)))
Dan Talayco710438c2010-02-18 15:16:07 -0800554 if self.switch_socket.sendall(outpkt) is None:
555 return 0
556
Dan Talayco48370102010-03-03 15:17:33 -0800557 self.logger.error("Unknown error on sendall")
Dan Talayco710438c2010-02-18 15:16:07 -0800558 return -1
Dan Talayco21c75c72010-02-12 22:59:24 -0800559
560 def __str__(self):
561 string = "Controller:\n"
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800562 string += " state " + self.dbg_state + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800563 string += " switch_addr " + str(self.switch_addr) + "\n"
564 string += " pending pkts " + str(len(self.packets)) + "\n"
565 string += " total pkts " + str(self.packets_total) + "\n"
566 string += " expired pkts " + str(self.packets_expired) + "\n"
567 string += " handled pkts " + str(self.packets_handled) + "\n"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800568 string += " poll discards " + str(self.poll_discards) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800569 string += " parse errors " + str(self.parse_errors) + "\n"
570 string += " sock errrors " + str(self.socket_errors) + "\n"
571 string += " max pkts " + str(self.max_pkts) + "\n"
572 string += " host " + str(self.host) + "\n"
573 string += " port " + str(self.port) + "\n"
574 string += " keep_alive " + str(self.keep_alive) + "\n"
575 return string
576
577 def show(self):
578 print str(self)
579
580def sample_handler(controller, msg, pkt):
581 """
582 Sample message handler
583
584 This is the prototype for functions registered with the controller
585 class for packet reception
586
587 @param controller The controller calling the handler
588 @param msg The parsed message object
589 @param pkt The raw packet that was received on the socket. This is
590 in case the packet contains extra unparsed data.
591 @returns Boolean value indicating if the packet was handled. If
592 not handled, the packet is placed in the queue for pollers to received
593 """
594 pass