blob: fff8534ec2f3b1a60ba9bb329cb1d3437b05a2e6 [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
Dan Talaycof8de5182012-04-12 22:38:41 -070043
44FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
45 for x in range(256)])
46
47def hex_dump_buffer(src, length=16):
48 """
49 Convert src to a hex dump string and return the string
50 @param src The source buffer
51 @param length The number of bytes shown in each line
52 @returns A string showing the hex dump
53 """
54 result = ["\n"]
55 for i in xrange(0, len(src), length):
56 chars = src[i:i+length]
57 hex = ' '.join(["%02x" % ord(x) for x in chars])
58 printable = ''.join(["%s" % ((ord(x) <= 127 and
59 FILTER[ord(x)]) or '.') for x in chars])
60 result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
61 return ''.join(result)
62
Dan Talayco48370102010-03-03 15:17:33 -080063##@todo Find a better home for these identifiers (controller)
Glen Gibb741b1182010-07-08 16:43:58 -070064RCV_SIZE_DEFAULT = 32768
Dan Talayco48370102010-03-03 15:17:33 -080065LISTEN_QUEUE_SIZE = 1
Dan Talayco34089522010-02-07 23:07:41 -080066
67class Controller(Thread):
68 """
69 Class abstracting the control interface to the switch.
70
71 For receiving messages, two mechanism will be implemented. First,
72 query the interface with poll. Second, register to have a
73 function called by message type. The callback is passed the
74 message type as well as the raw packet (or message object)
75
76 One of the main purposes of this object is to translate between network
77 and host byte order. 'Above' this object, things should be in host
78 byte order.
Dan Talayco21c75c72010-02-12 22:59:24 -080079
80 @todo Consider using SocketServer for listening socket
81 @todo Test transaction code
82
83 @var rcv_size The receive size to use for receive calls
84 @var max_pkts The max size of the receive queue
85 @var keep_alive If true, listen for echo requests and respond w/
86 echo replies
Dan Talayco710438c2010-02-18 15:16:07 -080087 @var initial_hello If true, will send a hello message immediately
88 upon connecting to the switch
Dan Talayco21c75c72010-02-12 22:59:24 -080089 @var host The host to use for connect
90 @var port The port to connect on
91 @var packets_total Total number of packets received
92 @var packets_expired Number of packets popped from queue as queue full
93 @var packets_handled Number of packets handled by something
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080094 @var dbg_state Debug indication of state
Dan Talayco34089522010-02-07 23:07:41 -080095 """
96
Dan Talayco48370102010-03-03 15:17:33 -080097 def __init__(self, host='127.0.0.1', port=6633, max_pkts=1024):
Dan Talayco21c75c72010-02-12 22:59:24 -080098 Thread.__init__(self)
Dan Talayco1b3f6902010-02-15 14:14:19 -080099 # Socket related
Dan Talayco21c75c72010-02-12 22:59:24 -0800100 self.rcv_size = RCV_SIZE_DEFAULT
Dan Talayco1b3f6902010-02-15 14:14:19 -0800101 self.listen_socket = None
102 self.switch_socket = None
103 self.switch_addr = None
Dan Talayco710438c2010-02-18 15:16:07 -0800104 self.socs = []
105 self.connect_cv = Condition()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800106 self.message_cv = Condition()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800107
108 # Counters
Dan Talayco21c75c72010-02-12 22:59:24 -0800109 self.socket_errors = 0
110 self.parse_errors = 0
Dan Talayco21c75c72010-02-12 22:59:24 -0800111 self.packets_total = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -0800112 self.packets_expired = 0
113 self.packets_handled = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -0800114 self.poll_discards = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -0800115
116 # State
Dan Talayco21c75c72010-02-12 22:59:24 -0800117 self.sync = Lock()
118 self.handlers = {}
119 self.keep_alive = False
Dan Talayco710438c2010-02-18 15:16:07 -0800120 self.active = True
121 self.initial_hello = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800122
Rich Lanec4f071b2012-07-11 17:25:57 -0700123 # OpenFlow message/packet queue
124 # Protected by the packets_cv lock / condition variable
125 self.packets = []
126 self.packets_cv = Condition()
127
Dan Talayco1b3f6902010-02-15 14:14:19 -0800128 # Settings
129 self.max_pkts = max_pkts
130 self.passive = True
Dan Talayco48370102010-03-03 15:17:33 -0800131 self.host = host
132 self.port = port
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800133 self.dbg_state = "init"
Dan Talayco48370102010-03-03 15:17:33 -0800134 self.logger = logging.getLogger("controller")
Dan Talaycof8de5182012-04-12 22:38:41 -0700135 self.filter_packet_in = False # Drop "excessive" packet ins
136 self.pkt_in_run = 0 # Count on run of packet ins
137 self.pkt_in_filter_limit = 50 # Count on run of packet ins
138 self.pkt_in_dropped = 0 # Total dropped packet ins
139 self.transact_to = 15 # Transact timeout default value; add to config
Dan Talayco1b3f6902010-02-15 14:14:19 -0800140
Dan Talaycoe226eb12010-02-18 23:06:30 -0800141 # Transaction and message type waiting variables
142 # xid_cv: Condition variable (semaphore) for packet waiters
Dan Talayco21c75c72010-02-12 22:59:24 -0800143 # xid: Transaction ID being waited on
144 # xid_response: Transaction response message
145 self.xid_cv = Condition()
146 self.xid = None
147 self.xid_response = None
148
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800149 self.buffered_input = ""
Dan Talaycoe226eb12010-02-18 23:06:30 -0800150
Dan Talaycof8de5182012-04-12 22:38:41 -0700151 def filter_packet(self, rawmsg, hdr):
152 """
153 Check if packet should be filtered
154
155 Currently filters packet in messages
156 @return Boolean, True if packet should be dropped
157 """
158 # Add check for packet in and rate limit
159 if self.filter_packet_in:
Rich Lanec4f071b2012-07-11 17:25:57 -0700160 # If we were dropping packets, report number dropped
161 # TODO dont drop expected packet ins
162 if self.pkt_in_run > self.pkt_in_filter_limit:
163 self.logger.debug("Dropped %d packet ins (%d total)"
164 % ((self.pkt_in_run -
165 self.pkt_in_filter_limit),
166 self.pkt_in_dropped))
167 self.pkt_in_run = 0
Dan Talaycof8de5182012-04-12 22:38:41 -0700168
169 return False
170
Dan Talaycod12b6612010-03-07 22:00:46 -0800171 def _pkt_handle(self, pkt):
172 """
173 Check for all packet handling conditions
174
175 Parse and verify message
176 Check if XID matches something waiting
177 Check if message is being expected for a poll operation
178 Check if keep alive is on and message is an echo request
179 Check if any registered handler wants the packet
180 Enqueue if none of those conditions is met
181
182 an echo request in case keep_alive is true, followed by
183 registered message handlers.
Glen Gibb6d467062010-07-08 16:15:08 -0700184 @param pkt The raw packet (string) which may contain multiple OF msgs
Dan Talaycod12b6612010-03-07 22:00:46 -0800185 """
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800186
187 # snag any left over data from last read()
188 pkt = self.buffered_input + pkt
189 self.buffered_input = ""
190
Glen Gibb6d467062010-07-08 16:15:08 -0700191 # Process each of the OF msgs inside the pkt
192 offset = 0
193 while offset < len(pkt):
194 # Parse the header to get type
195 hdr = of_header_parse(pkt[offset:])
Dan Talaycof8de5182012-04-12 22:38:41 -0700196 if not hdr or hdr.length == 0:
197 self.logger.error("Could not parse header")
198 self.logger.error("pkt len %d." % len(pkt))
199 if hdr:
200 self.logger.error("hdr len %d." % hdr.length)
201 self.logger.error("%s" % hex_dump_buffer(pkt[:200]))
202 self.kill()
Dan Talaycod12b6612010-03-07 22:00:46 -0800203 return
204
Glen Gibb6d467062010-07-08 16:15:08 -0700205 # Extract the raw message bytes
Ed Swierk836e5bd2012-03-20 11:08:53 -0700206 if (offset + hdr.length) > len(pkt):
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800207 break
Glen Gibb6d467062010-07-08 16:15:08 -0700208 rawmsg = pkt[offset : offset + hdr.length]
Dan Talayco4306d3e2011-09-07 09:42:26 -0700209 offset += hdr.length
Dan Talaycof8de5182012-04-12 22:38:41 -0700210
211 if self.filter_packet(rawmsg, hdr):
212 continue
213
214 self.logger.debug("Msg in: buf len %d. hdr.type %s. hdr.len %d" %
215 (len(pkt), ofp_type_map[hdr.type], hdr.length))
Glen Gibb6d467062010-07-08 16:15:08 -0700216 if hdr.version != OFP_VERSION:
217 self.logger.error("Version %d does not match OFTest version %d"
218 % (hdr.version, OFP_VERSION))
219 print "Version %d does not match OFTest version %d" % \
220 (hdr.version, OFP_VERSION)
Ken Chiangadc950f2012-10-05 13:50:03 -0700221 self.disconnect()
Dan Talaycof8de5182012-04-12 22:38:41 -0700222 return
Dan Talaycod12b6612010-03-07 22:00:46 -0800223
Glen Gibb6d467062010-07-08 16:15:08 -0700224 msg = of_message_parse(rawmsg)
225 if not msg:
226 self.parse_errors += 1
227 self.logger.warn("Could not parse message")
228 continue
229
Rich Lanec4f071b2012-07-11 17:25:57 -0700230 with self.sync:
231 # Check if transaction is waiting
232 with self.xid_cv:
233 if self.xid and hdr.xid == self.xid:
234 self.logger.debug("Matched expected XID " + str(hdr.xid))
235 self.xid_response = (msg, rawmsg)
236 self.xid = None
237 self.xid_cv.notify()
238 continue
Glen Gibb6d467062010-07-08 16:15:08 -0700239
Rich Lanec4f071b2012-07-11 17:25:57 -0700240 # Check if keep alive is set; if so, respond to echo requests
241 if self.keep_alive:
242 if hdr.type == OFPT_ECHO_REQUEST:
243 self.logger.debug("Responding to echo request")
244 rep = echo_reply()
245 rep.header.xid = hdr.xid
246 # Ignoring additional data
247 if self.message_send(rep.pack(), zero_xid=True) < 0:
248 self.logger.error("Error sending echo reply")
249 continue
Glen Gibb6d467062010-07-08 16:15:08 -0700250
Rich Lanec4f071b2012-07-11 17:25:57 -0700251 # Now check for message handlers; preference is given to
252 # handlers for a specific packet
253 handled = False
254 if hdr.type in self.handlers.keys():
255 handled = self.handlers[hdr.type](self, msg, rawmsg)
256 if not handled and ("all" in self.handlers.keys()):
257 handled = self.handlers["all"](self, msg, rawmsg)
Glen Gibb6d467062010-07-08 16:15:08 -0700258
Rich Lanec4f071b2012-07-11 17:25:57 -0700259 if not handled: # Not handled, enqueue
260 self.logger.debug("Enqueuing pkt type " + ofp_type_map[hdr.type])
261 with self.packets_cv:
262 if len(self.packets) >= self.max_pkts:
263 self.packets.pop(0)
264 self.packets_expired += 1
265 self.packets.append((msg, rawmsg))
266 self.packets_cv.notify_all()
267 self.packets_total += 1
268 else:
269 self.packets_handled += 1
270 self.logger.debug("Message handled by callback")
Glen Gibb6d467062010-07-08 16:15:08 -0700271
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800272 # end of 'while offset < len(pkt)'
273 # note that if offset = len(pkt), this is
274 # appends a harmless empty string
275 self.buffered_input += pkt[offset:]
Dan Talaycod12b6612010-03-07 22:00:46 -0800276
Dan Talayco710438c2010-02-18 15:16:07 -0800277 def _socket_ready_handle(self, s):
278 """
279 Handle an input-ready socket
Dan Talaycof8de5182012-04-12 22:38:41 -0700280
Dan Talayco710438c2010-02-18 15:16:07 -0800281 @param s The socket object that is ready
Dan Talaycof8de5182012-04-12 22:38:41 -0700282 @returns 0 on success, -1 on error
Dan Talayco710438c2010-02-18 15:16:07 -0800283 """
284
Dan Talaycof8de5182012-04-12 22:38:41 -0700285 if s and s == self.listen_socket:
Dan Talayco710438c2010-02-18 15:16:07 -0800286 if self.switch_socket:
Rich Lanee1da7ea2012-07-26 15:58:45 -0700287 self.logger.warning("Ignoring incoming connection; already connected to switch")
Rich Laneb4f8ecb2012-09-25 09:36:26 -0700288 (sock, addr) = self.listen_socket.accept()
289 sock.close()
Rich Lanee1da7ea2012-07-26 15:58:45 -0700290 return 0
Dan Talayco710438c2010-02-18 15:16:07 -0800291
Ken Chiange875baf2012-10-09 15:24:40 -0700292 try:
293 (sock, addr) = self.listen_socket.accept()
294 except:
295 self.logger.warning("Error on listen socket accept")
296 return -1
Rich Lanee1da7ea2012-07-26 15:58:45 -0700297 self.socs.append(sock)
Ken Chiang77173992012-10-30 15:44:39 -0700298 self.logger.info(self.host+":"+str(self.port)+": Incoming connection from "+str(addr))
Rich Lanee1da7ea2012-07-26 15:58:45 -0700299
Rich Laneee3586c2012-07-11 17:26:02 -0700300 with self.connect_cv:
Rich Lanee1da7ea2012-07-26 15:58:45 -0700301 (self.switch_socket, self.switch_addr) = (sock, addr)
Rich Lane1a8d5aa2012-10-08 15:40:03 -0700302 if self.initial_hello:
303 self.message_send(hello())
Rich Lanee1da7ea2012-07-26 15:58:45 -0700304 self.connect_cv.notify() # Notify anyone waiting
Dan Talaycof8de5182012-04-12 22:38:41 -0700305 elif s and s == self.switch_socket:
306 for idx in range(3): # debug: try a couple of times
307 try:
308 pkt = self.switch_socket.recv(self.rcv_size)
309 except:
310 self.logger.warning("Error on switch read")
311 return -1
312
313 if not self.active:
314 return 0
315
316 if len(pkt) == 0:
317 self.logger.warning("Zero-length switch read, %d" % idx)
318 else:
319 break
Dan Talayco710438c2010-02-18 15:16:07 -0800320
Dan Talaycof8de5182012-04-12 22:38:41 -0700321 if len(pkt) == 0: # Still no packet
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700322 self.logger.warning("Zero-length switch read; closing cxn")
Dan Talaycof8de5182012-04-12 22:38:41 -0700323 self.logger.info(str(self))
324 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800325
Dan Talaycod12b6612010-03-07 22:00:46 -0800326 self._pkt_handle(pkt)
Dan Talayco710438c2010-02-18 15:16:07 -0800327 else:
Dan Talayco48370102010-03-03 15:17:33 -0800328 self.logger.error("Unknown socket ready: " + str(s))
Dan Talaycof8de5182012-04-12 22:38:41 -0700329 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800330
Dan Talaycof8de5182012-04-12 22:38:41 -0700331 return 0
Dan Talayco710438c2010-02-18 15:16:07 -0800332
Dan Talayco1b3f6902010-02-15 14:14:19 -0800333 def run(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800334 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800335 Activity function for class
Dan Talayco21c75c72010-02-12 22:59:24 -0800336
Dan Talayco1b3f6902010-02-15 14:14:19 -0800337 Assumes connection to switch already exists. Listens on
338 switch_socket for messages until an error (or zero len pkt)
339 occurs.
Dan Talayco21c75c72010-02-12 22:59:24 -0800340
Dan Talayco1b3f6902010-02-15 14:14:19 -0800341 When there is a message on the socket, check for handlers; queue the
342 packet if no one handles the packet.
343
344 See note for controller describing the limitation of a single
345 connection for now.
346 """
347
Dan Talayco710438c2010-02-18 15:16:07 -0800348 self.dbg_state = "starting"
Dan Talayco1b3f6902010-02-15 14:14:19 -0800349
Dan Talayco710438c2010-02-18 15:16:07 -0800350 # Create listen socket
Dan Talayco48370102010-03-03 15:17:33 -0800351 self.logger.info("Create/listen at " + self.host + ":" +
Dan Talayco710438c2010-02-18 15:16:07 -0800352 str(self.port))
353 self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
354 self.listen_socket.setsockopt(socket.SOL_SOCKET,
355 socket.SO_REUSEADDR, 1)
356 self.listen_socket.bind((self.host, self.port))
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800357 self.dbg_state = "listening"
Dan Talayco21c75c72010-02-12 22:59:24 -0800358 self.listen_socket.listen(LISTEN_QUEUE_SIZE)
Dan Talayco21c75c72010-02-12 22:59:24 -0800359
Dan Talayco48370102010-03-03 15:17:33 -0800360 self.logger.info("Waiting for switch connection")
Dan Talayco710438c2010-02-18 15:16:07 -0800361 self.socs = [self.listen_socket]
362 self.dbg_state = "running"
Ken Chiangadc950f2012-10-05 13:50:03 -0700363
Dan Talayco710438c2010-02-18 15:16:07 -0800364 while self.active:
Dan Talayco710438c2010-02-18 15:16:07 -0800365 try:
366 sel_in, sel_out, sel_err = \
367 select.select(self.socs, [], self.socs, 1)
368 except:
369 print sys.exc_info()
Ken Chiangadc950f2012-10-05 13:50:03 -0700370 self.logger.error("Select error, disconnecting")
371 self.disconnect()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800372
Dan Talayco710438c2010-02-18 15:16:07 -0800373 for s in sel_err:
Ken Chiangadc950f2012-10-05 13:50:03 -0700374 self.logger.error("Got socket error on: " + str(s) + ", disconnecting")
375 self.disconnect()
Dan Talaycof8de5182012-04-12 22:38:41 -0700376
377 for s in sel_in:
378 if self._socket_ready_handle(s) == -1:
Ken Chiangadc950f2012-10-05 13:50:03 -0700379 self.disconnect()
Dan Talayco710438c2010-02-18 15:16:07 -0800380
Dan Talayco710438c2010-02-18 15:16:07 -0800381 # End of main loop
382 self.dbg_state = "closing"
Dan Talayco48370102010-03-03 15:17:33 -0800383 self.logger.info("Exiting controller thread")
Dan Talayco710438c2010-02-18 15:16:07 -0800384 self.shutdown()
385
Rich Lane8806bc42012-07-26 19:18:37 -0700386 def connect(self, timeout=-1):
Dan Talayco710438c2010-02-18 15:16:07 -0800387 """
388 Connect to the switch
389
Rich Lane8806bc42012-07-26 19:18:37 -0700390 @param timeout Block for up to timeout seconds. Pass -1 for the default.
Dan Talayco710438c2010-02-18 15:16:07 -0800391 @return Boolean, True if connected
392 """
393
Rich Lanee1da7ea2012-07-26 15:58:45 -0700394 with self.connect_cv:
395 timed_wait(self.connect_cv, lambda: self.switch_socket, timeout=timeout)
Dan Talayco710438c2010-02-18 15:16:07 -0800396 return self.switch_socket is not None
397
Ken Chiangadc950f2012-10-05 13:50:03 -0700398 def disconnect(self, timeout=-1):
399 """
400 If connected to a switch, disconnect.
401 """
402 if self.switch_socket:
403 self.socs.remove(self.switch_socket)
404 self.switch_socket.close()
405 self.switch_socket = None
406 self.switch_addr = None
Ken Chiange875baf2012-10-09 15:24:40 -0700407 with self.connect_cv:
408 self.connect_cv.notifyAll()
Ken Chiangadc950f2012-10-05 13:50:03 -0700409
410 def wait_disconnected(self, timeout=-1):
411 """
412 @param timeout Block for up to timeout seconds. Pass -1 for the default.
413 @return Boolean, True if disconnected
414 """
415
Ken Chiange875baf2012-10-09 15:24:40 -0700416 with self.connect_cv:
417 timed_wait(self.connect_cv,
Ken Chiangadc950f2012-10-05 13:50:03 -0700418 lambda: True if not self.switch_socket else None,
419 timeout=timeout)
420 return self.switch_socket is None
421
Dan Talayco710438c2010-02-18 15:16:07 -0800422 def kill(self):
423 """
424 Force the controller thread to quit
425
426 Just sets the active state variable to false and expects
427 the select timeout to kick in
428 """
429 self.active = False
Dan Talayco21c75c72010-02-12 22:59:24 -0800430
Dan Talayco1b3f6902010-02-15 14:14:19 -0800431 def shutdown(self):
Dan Talayco21c75c72010-02-12 22:59:24 -0800432 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800433 Shutdown the controller closing all sockets
Dan Talayco21c75c72010-02-12 22:59:24 -0800434
Dan Talayco1b3f6902010-02-15 14:14:19 -0800435 @todo Might want to synchronize shutdown with self.sync...
436 """
Dan Talaycof8de5182012-04-12 22:38:41 -0700437
Dan Talayco710438c2010-02-18 15:16:07 -0800438 self.active = False
439 try:
440 self.switch_socket.shutdown(socket.SHUT_RDWR)
441 except:
Dan Talayco48370102010-03-03 15:17:33 -0800442 self.logger.info("Ignoring switch soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800443 self.switch_socket = None
Dan Talayco1b3f6902010-02-15 14:14:19 -0800444
Dan Talayco710438c2010-02-18 15:16:07 -0800445 try:
446 self.listen_socket.shutdown(socket.SHUT_RDWR)
447 except:
Dan Talayco48370102010-03-03 15:17:33 -0800448 self.logger.info("Ignoring listen soc shutdown error")
Dan Talayco710438c2010-02-18 15:16:07 -0800449 self.listen_socket = None
Dan Talaycof8de5182012-04-12 22:38:41 -0700450
Rich Laneee3586c2012-07-11 17:26:02 -0700451 # Wakeup condition variables on which controller may be wait
452 with self.xid_cv:
453 self.xid_cv.notifyAll()
Dan Talaycof8de5182012-04-12 22:38:41 -0700454
Rich Laneee3586c2012-07-11 17:26:02 -0700455 with self.connect_cv:
456 self.connect_cv.notifyAll()
Dan Talaycof8de5182012-04-12 22:38:41 -0700457
Dan Talayco710438c2010-02-18 15:16:07 -0800458 self.dbg_state = "down"
459
Dan Talayco34089522010-02-07 23:07:41 -0800460 def register(self, msg_type, handler):
461 """
462 Register a callback to receive a specific message type.
463
464 Only one handler may be registered for a given message type.
Dan Talaycod12b6612010-03-07 22:00:46 -0800465
466 WARNING: A lock is held during the handler call back, so
467 the handler should not make any blocking calls
468
Dan Talayco34089522010-02-07 23:07:41 -0800469 @param msg_type The type of message to receive. May be DEFAULT
Dan Talayco21c75c72010-02-12 22:59:24 -0800470 for all non-handled packets. The special type, the string "all"
471 will send all packets to the handler.
Dan Talayco34089522010-02-07 23:07:41 -0800472 @param handler The function to call when a message of the given
473 type is received.
474 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800475 # Should check type is valid
476 if not handler and msg_type in self.handlers.keys():
477 del self.handlers[msg_type]
478 return
479 self.handlers[msg_type] = handler
Dan Talayco34089522010-02-07 23:07:41 -0800480
Rich Laneb64ce3d2012-07-26 15:37:57 -0700481 def poll(self, exp_msg=None, timeout=-1):
Dan Talayco34089522010-02-07 23:07:41 -0800482 """
483 Wait for the next OF message received from the switch.
484
485 @param exp_msg If set, return only when this type of message
Dan Talayco48370102010-03-03 15:17:33 -0800486 is received (unless timeout occurs).
Rich Laneb64ce3d2012-07-26 15:37:57 -0700487
488 @param timeout Maximum number of seconds to wait for the message.
489 Pass -1 for the default timeout.
Dan Talayco34089522010-02-07 23:07:41 -0800490
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800491 @retval A pair (msg, pkt) where msg is a message object and pkt
492 the string representing the packet as received from the socket.
493 This allows additional parsing by the receiver if necessary.
494
Dan Talayco34089522010-02-07 23:07:41 -0800495 The data members in the message are in host endian order.
Dan Talayco48370102010-03-03 15:17:33 -0800496 If an error occurs, (None, None) is returned
Dan Talayco34089522010-02-07 23:07:41 -0800497 """
Dan Talayco34089522010-02-07 23:07:41 -0800498
Ken Chiang77173992012-10-30 15:44:39 -0700499 if exp_msg is not None:
Ed Swierk9e55e282012-08-22 06:57:28 -0700500 self.logger.debug("Poll for %s" % ofp_type_map[exp_msg])
501 else:
502 self.logger.debug("Poll for any OF message")
Rich Laneb64ce3d2012-07-26 15:37:57 -0700503
504 # Take the packet from the queue
Rich Lanec4f071b2012-07-11 17:25:57 -0700505 def grab():
506 if len(self.packets) > 0:
Ken Chiang77173992012-10-30 15:44:39 -0700507 if exp_msg is None:
Rich Lanec4f071b2012-07-11 17:25:57 -0700508 self.logger.debug("Looking for any packet")
509 (msg, pkt) = self.packets.pop(0)
510 return (msg, pkt)
511 else:
512 self.logger.debug("Looking for %s" % ofp_type_map[exp_msg])
513 for i in range(len(self.packets)):
514 msg = self.packets[i][0]
515 self.logger.debug("Checking packets[%d] (%s)" % (i, ofp_type_map[msg.header.type]))
516 if msg.header.type == exp_msg:
517 (msg, pkt) = self.packets.pop(i)
518 return (msg, pkt)
519 # Not found
520 self.logger.debug("Packet not in queue")
Rich Laneb64ce3d2012-07-26 15:37:57 -0700521 return None
Dan Talayco21c75c72010-02-12 22:59:24 -0800522
Rich Lanec4f071b2012-07-11 17:25:57 -0700523 with self.packets_cv:
Rich Lane8806bc42012-07-26 19:18:37 -0700524 ret = timed_wait(self.packets_cv, grab, timeout=timeout)
Rich Lanec4f071b2012-07-11 17:25:57 -0700525
Rich Laneb64ce3d2012-07-26 15:37:57 -0700526 if ret != None:
527 (msg, pkt) = ret
528 self.logger.debug("Got message %s" % str(msg))
529 return (msg, pkt)
530 else:
531 return (None, None)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800532
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700533 def transact(self, msg, timeout=-1, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800534 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800535 Run a message transaction with the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800536
537 Send the message in msg and wait for a reply with a matching
Dan Talayco21c75c72010-02-12 22:59:24 -0800538 transaction id. Transactions have the highest priority in
539 received message handling.
Dan Talaycoe37999f2010-02-09 15:27:12 -0800540
Dan Talayco21c75c72010-02-12 22:59:24 -0800541 @param msg The message object to send; must not be a string
Rich Lanee1da7ea2012-07-26 15:58:45 -0700542 @param timeout The timeout in seconds; if -1 use default.
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800543 @param zero_xid Normally, if the XID is 0 an XID will be generated
Ed Swierk9e55e282012-08-22 06:57:28 -0700544 for the message. Set zero_xid to override this behavior
Dan Talayco21c75c72010-02-12 22:59:24 -0800545 @return The matching message object or None if unsuccessful
Dan Talaycoe37999f2010-02-09 15:27:12 -0800546
Dan Talayco34089522010-02-07 23:07:41 -0800547 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800548
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800549 if not zero_xid and msg.header.xid == 0:
550 msg.header.xid = gen_xid()
551
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700552 self.logger.debug("Running transaction %d" % msg.header.xid)
Dan Talayco21c75c72010-02-12 22:59:24 -0800553
Rich Lane9aca1992012-07-11 17:26:31 -0700554 with self.xid_cv:
555 if self.xid:
556 self.logger.error("Can only run one transaction at a time")
557 return (None, None)
Dan Talaycof8de5182012-04-12 22:38:41 -0700558
Rich Lane9aca1992012-07-11 17:26:31 -0700559 self.xid = msg.header.xid
Dan Talaycod12b6612010-03-07 22:00:46 -0800560 self.xid_response = None
Rich Lane9aca1992012-07-11 17:26:31 -0700561 if self.message_send(msg.pack()) < 0:
562 self.logger.error("Error sending pkt for transaction %d" %
563 msg.header.xid)
564 return (None, None)
565
Rich Lane8806bc42012-07-26 19:18:37 -0700566 self.logger.debug("Waiting for transaction %d" % msg.header.xid)
Rich Lanee1da7ea2012-07-26 15:58:45 -0700567 timed_wait(self.xid_cv, lambda: self.xid_response, timeout=timeout)
Rich Lane9aca1992012-07-11 17:26:31 -0700568
569 if self.xid_response:
570 (resp, pkt) = self.xid_response
571 self.xid_response = None
572 else:
573 (resp, pkt) = (None, None)
574
Dan Talayco09c2c592010-05-13 14:21:52 -0700575 if resp is None:
576 self.logger.warning("No response for xid " + str(self.xid))
577 return (resp, pkt)
Dan Talayco34089522010-02-07 23:07:41 -0800578
Dan Talayco710438c2010-02-18 15:16:07 -0800579 def message_send(self, msg, zero_xid=False):
Dan Talayco34089522010-02-07 23:07:41 -0800580 """
581 Send the message to the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800582
Dan Talayco11c26e72010-03-07 22:03:57 -0800583 @param msg A string or OpenFlow message object to be forwarded to
584 the switch.
585 @param zero_xid If msg is an OpenFlow object (not a string) and if
Dan Talayco710438c2010-02-18 15:16:07 -0800586 the XID in the header is 0, then an XID will be generated
Ed Swierk9e55e282012-08-22 06:57:28 -0700587 for the message. Set zero_xid to override this behavior (and keep an
Dan Talayco710438c2010-02-18 15:16:07 -0800588 existing 0 xid)
Dan Talaycoe37999f2010-02-09 15:27:12 -0800589
Ed Swierk9e55e282012-08-22 06:57:28 -0700590 @return 0 on success
Dan Talayco34089522010-02-07 23:07:41 -0800591
Dan Talayco21c75c72010-02-12 22:59:24 -0800592 """
593
Dan Talayco1b3f6902010-02-15 14:14:19 -0800594 if not self.switch_socket:
595 # Sending a string indicates the message is ready to go
Ed Swierk9e55e282012-08-22 06:57:28 -0700596 raise Exception("no socket")
Dan Talayco710438c2010-02-18 15:16:07 -0800597 #@todo If not string, try to pack
Dan Talayco21c75c72010-02-12 22:59:24 -0800598 if type(msg) != type(""):
Ed Swierk9e55e282012-08-22 06:57:28 -0700599 if msg.header.xid == 0 and not zero_xid:
600 msg.header.xid = gen_xid()
601 outpkt = msg.pack()
Dan Talayco710438c2010-02-18 15:16:07 -0800602 else:
603 outpkt = msg
Dan Talayco21c75c72010-02-12 22:59:24 -0800604
Dan Talayco48370102010-03-03 15:17:33 -0800605 self.logger.debug("Sending pkt of len " + str(len(outpkt)))
Ed Swierk9e55e282012-08-22 06:57:28 -0700606 if self.switch_socket.sendall(outpkt) is not None:
607 raise Exception("unknown error on sendall")
Dan Talayco710438c2010-02-18 15:16:07 -0800608
Ed Swierk9e55e282012-08-22 06:57:28 -0700609 return 0
Dan Talayco21c75c72010-02-12 22:59:24 -0800610
611 def __str__(self):
612 string = "Controller:\n"
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800613 string += " state " + self.dbg_state + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800614 string += " switch_addr " + str(self.switch_addr) + "\n"
615 string += " pending pkts " + str(len(self.packets)) + "\n"
616 string += " total pkts " + str(self.packets_total) + "\n"
617 string += " expired pkts " + str(self.packets_expired) + "\n"
618 string += " handled pkts " + str(self.packets_handled) + "\n"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800619 string += " poll discards " + str(self.poll_discards) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800620 string += " parse errors " + str(self.parse_errors) + "\n"
621 string += " sock errrors " + str(self.socket_errors) + "\n"
622 string += " max pkts " + str(self.max_pkts) + "\n"
623 string += " host " + str(self.host) + "\n"
624 string += " port " + str(self.port) + "\n"
625 string += " keep_alive " + str(self.keep_alive) + "\n"
Dan Talaycof8de5182012-04-12 22:38:41 -0700626 string += " pkt_in_run " + str(self.pkt_in_run) + "\n"
627 string += " pkt_in_dropped " + str(self.pkt_in_dropped) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800628 return string
629
630 def show(self):
631 print str(self)
632
633def sample_handler(controller, msg, pkt):
634 """
635 Sample message handler
636
637 This is the prototype for functions registered with the controller
638 class for packet reception
639
640 @param controller The controller calling the handler
641 @param msg The parsed message object
642 @param pkt The raw packet that was received on the socket. This is
643 in case the packet contains extra unparsed data.
644 @returns Boolean value indicating if the packet was handled. If
645 not handled, the packet is placed in the queue for pollers to received
646 """
647 pass