blob: 23d031b0f78d78b3d0625b18005862ce1121038a [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
Rich Lane720eaf22013-08-09 18:00:45 -070029import sys
Dan Talayco34089522010-02-07 23:07:41 -080030import os
31import socket
32import time
Rich Lanecd97d3d2013-01-07 18:50:06 -080033import struct
34import select
35import logging
Dan Talayco34089522010-02-07 23:07:41 -080036from threading import Thread
37from threading import Lock
Dan Talayco21c75c72010-02-12 22:59:24 -080038from threading import Condition
Dan Talayco48370102010-03-03 15:17:33 -080039
Wilson Ngc11a9182013-10-28 16:02:03 -070040import ofutils
41import loxi
42
43# Configured openflow version
44import ofp as cfg_ofp
Dan Talaycof8de5182012-04-12 22:38:41 -070045
Flavio Castrob01d0aa2016-07-20 16:14:48 -070046FILTER = ''.join( [ (len( repr( chr( x ) ) ) == 3) and chr( x ) or '.'
47 for x in range( 256 ) ] )
Dan Talaycof8de5182012-04-12 22:38:41 -070048
Flavio Castrob01d0aa2016-07-20 16:14:48 -070049
50def hex_dump_buffer( src, length=16 ):
Dan Talaycof8de5182012-04-12 22:38:41 -070051 """
52 Convert src to a hex dump string and return the string
53 @param src The source buffer
54 @param length The number of bytes shown in each line
55 @returns A string showing the hex dump
56 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -070057 result = [ "\n" ]
58 for i in xrange( 0, len( src ), length ):
59 chars = src[ i:i + length ]
60 hex = ' '.join( [ "%02x" % ord( x ) for x in chars ] )
61 printable = ''.join( [ "%s" % ((ord( x ) <= 127 and
62 FILTER[ ord( x ) ]) or '.') for x in
63 chars ] )
64 result.append( "%04x %-*s %s\n" % (i, length * 3, hex, printable) )
65 return ''.join( result )
66
Dan Talaycof8de5182012-04-12 22:38:41 -070067
Dan Talayco48370102010-03-03 15:17:33 -080068##@todo Find a better home for these identifiers (controller)
Glen Gibb741b1182010-07-08 16:43:58 -070069RCV_SIZE_DEFAULT = 32768
Dan Talayco48370102010-03-03 15:17:33 -080070LISTEN_QUEUE_SIZE = 1
Dan Talayco34089522010-02-07 23:07:41 -080071
Flavio Castrob01d0aa2016-07-20 16:14:48 -070072
73class Controller( Thread ):
Dan Talayco34089522010-02-07 23:07:41 -080074 """
75 Class abstracting the control interface to the switch.
76
77 For receiving messages, two mechanism will be implemented. First,
78 query the interface with poll. Second, register to have a
79 function called by message type. The callback is passed the
80 message type as well as the raw packet (or message object)
81
82 One of the main purposes of this object is to translate between network
83 and host byte order. 'Above' this object, things should be in host
84 byte order.
Dan Talayco21c75c72010-02-12 22:59:24 -080085
86 @todo Consider using SocketServer for listening socket
87 @todo Test transaction code
88
89 @var rcv_size The receive size to use for receive calls
90 @var max_pkts The max size of the receive queue
91 @var keep_alive If true, listen for echo requests and respond w/
92 echo replies
Dan Talayco710438c2010-02-18 15:16:07 -080093 @var initial_hello If true, will send a hello message immediately
94 upon connecting to the switch
Dan Talayco69ca4d62012-11-15 11:50:22 -080095 @var switch If not None, do an active connection to the switch
Dan Talayco21c75c72010-02-12 22:59:24 -080096 @var host The host to use for connect
97 @var port The port to connect on
98 @var packets_total Total number of packets received
99 @var packets_expired Number of packets popped from queue as queue full
100 @var packets_handled Number of packets handled by something
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800101 @var dbg_state Debug indication of state
Dan Talayco34089522010-02-07 23:07:41 -0800102 """
103
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700104 def __init__( self, switch=None, host='127.0.0.1', port=6653, max_pkts=1024,
105 force=False ):
106 Thread.__init__( self )
Dan Talayco1b3f6902010-02-15 14:14:19 -0800107 # Socket related
Dan Talayco21c75c72010-02-12 22:59:24 -0800108 self.rcv_size = RCV_SIZE_DEFAULT
Dan Talayco1b3f6902010-02-15 14:14:19 -0800109 self.listen_socket = None
110 self.switch_socket = None
111 self.switch_addr = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700112 self.connect_cv = Condition( )
113 self.message_cv = Condition( )
114 self.tx_lock = Lock( )
Dan Talayco1b3f6902010-02-15 14:14:19 -0800115
Rich Lane4dfd5e12012-12-22 19:48:01 -0800116 # Used to wake up the event loop from another thread
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700117 self.waker = ofutils.EventDescriptor( )
Rich Lane32797542012-12-22 17:46:05 -0800118
Dan Talayco1b3f6902010-02-15 14:14:19 -0800119 # Counters
Dan Talayco21c75c72010-02-12 22:59:24 -0800120 self.socket_errors = 0
121 self.parse_errors = 0
Dan Talayco21c75c72010-02-12 22:59:24 -0800122 self.packets_total = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -0800123 self.packets_expired = 0
124 self.packets_handled = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -0800125 self.poll_discards = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -0800126
127 # State
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700128 self.sync = Lock( )
129 self.handlers = { }
Dan Talayco21c75c72010-02-12 22:59:24 -0800130 self.keep_alive = False
Dan Talayco710438c2010-02-18 15:16:07 -0800131 self.active = True
132 self.initial_hello = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800133
Rich Lanec4f071b2012-07-11 17:25:57 -0700134 # OpenFlow message/packet queue
135 # Protected by the packets_cv lock / condition variable
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700136 self.packets = [ ]
137 self.packets_cv = Condition( )
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700138 self.packet_in_count = 0
Rich Lanec4f071b2012-07-11 17:25:57 -0700139
Dan Talayco1b3f6902010-02-15 14:14:19 -0800140 # Settings
141 self.max_pkts = max_pkts
Dan Talayco69ca4d62012-11-15 11:50:22 -0800142 self.switch = switch
143 self.passive = not self.switch
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700144 self.force = force
Dan Talayco48370102010-03-03 15:17:33 -0800145 self.host = host
146 self.port = port
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800147 self.dbg_state = "init"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700148 self.logger = logging.getLogger( "controller" )
149 self.filter_packet_in = False # Drop "excessive" packet ins
150 self.pkt_in_run = 0 # Count on run of packet ins
151 self.pkt_in_filter_limit = 50 # Count on run of packet ins
152 self.pkt_in_dropped = 0 # Total dropped packet ins
153 self.transact_to = 15 # Transact timeout default value; add to config
Dan Talayco1b3f6902010-02-15 14:14:19 -0800154
Dan Talaycoe226eb12010-02-18 23:06:30 -0800155 # Transaction and message type waiting variables
156 # xid_cv: Condition variable (semaphore) for packet waiters
Dan Talayco21c75c72010-02-12 22:59:24 -0800157 # xid: Transaction ID being waited on
158 # xid_response: Transaction response message
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700159 self.xid_cv = Condition( )
Dan Talayco21c75c72010-02-12 22:59:24 -0800160 self.xid = None
161 self.xid_response = None
162
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800163 self.buffered_input = ""
Dan Talaycoe226eb12010-02-18 23:06:30 -0800164
Rich Lane207502e2012-12-31 14:29:12 -0800165 # Create listen socket
166 if self.passive:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700167 self.logger.info( "Create/listen at " + self.host + ":" +
168 str( self.port ) )
169 ai = socket.getaddrinfo( self.host, self.port, socket.AF_UNSPEC,
170 socket.SOCK_STREAM, 0, socket.AI_PASSIVE )
Ken Chiangbf84c332015-01-27 12:52:27 -0800171 # Use first returned addrinfo
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700172 (family, socktype, proto, name, sockaddr) = ai[ 0 ]
173 self.listen_socket = socket.socket( family, socktype )
174 self.listen_socket.setsockopt( socket.SOL_SOCKET,
175 socket.SO_REUSEADDR, 1 )
176 self.listen_socket.bind( sockaddr )
177 self.listen_socket.listen( LISTEN_QUEUE_SIZE )
Rich Lane207502e2012-12-31 14:29:12 -0800178
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700179 def filter_packet( self, rawmsg, hdr ):
Dan Talaycof8de5182012-04-12 22:38:41 -0700180 """
181 Check if packet should be filtered
182
183 Currently filters packet in messages
184 @return Boolean, True if packet should be dropped
185 """
Rich Lane1622bbb2013-03-11 17:11:53 -0700186 # XXX didn't actually check for packet-in...
187 return False
Dan Talaycof8de5182012-04-12 22:38:41 -0700188 # Add check for packet in and rate limit
189 if self.filter_packet_in:
Rich Lanec4f071b2012-07-11 17:25:57 -0700190 # If we were dropping packets, report number dropped
191 # TODO dont drop expected packet ins
192 if self.pkt_in_run > self.pkt_in_filter_limit:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700193 self.logger.debug( "Dropped %d packet ins (%d total)"
194 % ((self.pkt_in_run -
195 self.pkt_in_filter_limit),
196 self.pkt_in_dropped) )
Rich Lanec4f071b2012-07-11 17:25:57 -0700197 self.pkt_in_run = 0
Dan Talaycof8de5182012-04-12 22:38:41 -0700198
199 return False
200
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700201 def _pkt_handle( self, pkt ):
Dan Talaycod12b6612010-03-07 22:00:46 -0800202 """
203 Check for all packet handling conditions
204
205 Parse and verify message
206 Check if XID matches something waiting
207 Check if message is being expected for a poll operation
208 Check if keep alive is on and message is an echo request
209 Check if any registered handler wants the packet
210 Enqueue if none of those conditions is met
211
212 an echo request in case keep_alive is true, followed by
213 registered message handlers.
Glen Gibb6d467062010-07-08 16:15:08 -0700214 @param pkt The raw packet (string) which may contain multiple OF msgs
Dan Talaycod12b6612010-03-07 22:00:46 -0800215 """
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800216
217 # snag any left over data from last read()
218 pkt = self.buffered_input + pkt
219 self.buffered_input = ""
220
Glen Gibb6d467062010-07-08 16:15:08 -0700221 # Process each of the OF msgs inside the pkt
222 offset = 0
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700223 while offset < len( pkt ):
224 if offset + 8 > len( pkt ):
Rich Lane1622bbb2013-03-11 17:11:53 -0700225 break
226
Glen Gibb6d467062010-07-08 16:15:08 -0700227 # Parse the header to get type
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700228 hdr_version, hdr_type, hdr_length, hdr_xid = cfg_ofp.message.parse_header(
229 pkt[ offset: ] )
Wilson Ngc11a9182013-10-28 16:02:03 -0700230
231 # Use loxi to resolve to ofp of matching version
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700232 ofp = loxi.protocol( hdr_version )
Dan Talaycod12b6612010-03-07 22:00:46 -0800233
Glen Gibb6d467062010-07-08 16:15:08 -0700234 # Extract the raw message bytes
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700235 if (offset + hdr_length) > len( pkt ):
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800236 break
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700237 rawmsg = pkt[ offset: offset + hdr_length ]
Rich Lane1622bbb2013-03-11 17:11:53 -0700238 offset += hdr_length
Dan Talaycof8de5182012-04-12 22:38:41 -0700239
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700240 # if self.filter_packet(rawmsg, hdr):
Rich Lane1622bbb2013-03-11 17:11:53 -0700241 # continue
Dan Talaycof8de5182012-04-12 22:38:41 -0700242
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700243 msg = ofp.message.parse_message( rawmsg )
Glen Gibb6d467062010-07-08 16:15:08 -0700244 if not msg:
245 self.parse_errors += 1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700246 self.logger.warn( "Could not parse message" )
Glen Gibb6d467062010-07-08 16:15:08 -0700247 continue
248
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700249 self.logger.debug( "Msg in: version %d class %s len %d xid %d",
250 hdr_version, type( msg ).__name__, hdr_length,
251 hdr_xid )
Rich Lane1fd43e32014-01-06 15:22:50 -0800252
Rich Lanec4f071b2012-07-11 17:25:57 -0700253 with self.sync:
254 # Check if transaction is waiting
255 with self.xid_cv:
Rich Lane1622bbb2013-03-11 17:11:53 -0700256 if self.xid and hdr_xid == self.xid:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700257 self.logger.debug(
258 "Matched expected XID " + str( hdr_xid ) )
Rich Lanec4f071b2012-07-11 17:25:57 -0700259 self.xid_response = (msg, rawmsg)
260 self.xid = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700261 self.xid_cv.notify( )
Rich Lanec4f071b2012-07-11 17:25:57 -0700262 continue
Glen Gibb6d467062010-07-08 16:15:08 -0700263
Rich Lanec4f071b2012-07-11 17:25:57 -0700264 # Check if keep alive is set; if so, respond to echo requests
265 if self.keep_alive:
Rich Lane1622bbb2013-03-11 17:11:53 -0700266 if hdr_type == ofp.OFPT_ECHO_REQUEST:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700267 self.logger.debug( "Responding to echo request" )
268 rep = ofp.message.echo_reply( )
Rich Lane1622bbb2013-03-11 17:11:53 -0700269 rep.xid = hdr_xid
Rich Lanec4f071b2012-07-11 17:25:57 -0700270 # Ignoring additional data
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700271 self.message_send( rep )
Rich Lanec4f071b2012-07-11 17:25:57 -0700272 continue
Glen Gibb6d467062010-07-08 16:15:08 -0700273
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700274 # Generalize to counters for all packet types?
275 if msg.type == ofp.OFPT_PACKET_IN:
276 self.packet_in_count += 1
277
Rich Lane5d63b9c2013-01-11 14:12:37 -0800278 # Log error messages
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700279 if isinstance( msg, ofp.message.error_msg ):
280 # pylint: disable=E1103
Rich Laneb73808c2013-03-11 15:22:23 -0700281 if msg.err_type in ofp.ofp_error_type_map:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700282 type_str = ofp.ofp_error_type_map[ msg.err_type ]
Rich Laneb73808c2013-03-11 15:22:23 -0700283 if msg.err_type == ofp.OFPET_HELLO_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800284 code_map = ofp.ofp_hello_failed_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700285 elif msg.err_type == ofp.OFPET_BAD_REQUEST:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800286 code_map = ofp.ofp_bad_request_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700287 elif msg.err_type == ofp.OFPET_BAD_ACTION:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800288 code_map = ofp.ofp_bad_action_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700289 elif msg.err_type == ofp.OFPET_FLOW_MOD_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800290 code_map = ofp.ofp_flow_mod_failed_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700291 elif msg.err_type == ofp.OFPET_PORT_MOD_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800292 code_map = ofp.ofp_port_mod_failed_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700293 elif msg.err_type == ofp.OFPET_QUEUE_OP_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800294 code_map = ofp.ofp_queue_op_failed_code_map
295 else:
296 code_map = None
297
298 if code_map and msg.code in code_map:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700299 code_str = code_map[ msg.code ]
Rich Lane5d63b9c2013-01-11 14:12:37 -0800300 else:
301 code_str = "unknown"
302 else:
303 type_str = "unknown"
Rich Lane1879dc72013-03-11 22:08:51 -0700304 code_str = "unknown"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700305 self.logger.warn(
306 "Received error message: xid=%d type=%s (%d) code=%s (%d)",
307 hdr_xid, type_str, msg.err_type, code_str,
308 msg.code )
Rich Lane5d63b9c2013-01-11 14:12:37 -0800309
Rich Lanec4f071b2012-07-11 17:25:57 -0700310 # Now check for message handlers; preference is given to
311 # handlers for a specific packet
312 handled = False
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700313 if hdr_type in self.handlers.keys( ):
314 handled = self.handlers[ hdr_type ]( self, msg, rawmsg )
315 if not handled and ("all" in self.handlers.keys( )):
316 handled = self.handlers[ "all" ]( self, msg, rawmsg )
Glen Gibb6d467062010-07-08 16:15:08 -0700317
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700318 if not handled: # Not handled, enqueue
Rich Lanec4f071b2012-07-11 17:25:57 -0700319 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700320 if len( self.packets ) >= self.max_pkts:
321 self.packets.pop( 0 )
Rich Lanec4f071b2012-07-11 17:25:57 -0700322 self.packets_expired += 1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700323 self.packets.append( (msg, rawmsg) )
324 self.packets_cv.notify_all( )
Rich Lanec4f071b2012-07-11 17:25:57 -0700325 self.packets_total += 1
326 else:
327 self.packets_handled += 1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700328 self.logger.debug( "Message handled by callback" )
Glen Gibb6d467062010-07-08 16:15:08 -0700329
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800330 # end of 'while offset < len(pkt)'
331 # note that if offset = len(pkt), this is
332 # appends a harmless empty string
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700333 self.buffered_input += pkt[ offset: ]
Dan Talaycod12b6612010-03-07 22:00:46 -0800334
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700335 def _socket_ready_handle( self, s ):
Dan Talayco710438c2010-02-18 15:16:07 -0800336 """
337 Handle an input-ready socket
Dan Talaycof8de5182012-04-12 22:38:41 -0700338
Dan Talayco710438c2010-02-18 15:16:07 -0800339 @param s The socket object that is ready
Dan Talaycof8de5182012-04-12 22:38:41 -0700340 @returns 0 on success, -1 on error
Dan Talayco710438c2010-02-18 15:16:07 -0800341 """
342
Dan Talayco69ca4d62012-11-15 11:50:22 -0800343 if self.passive and s and s == self.listen_socket:
Dan Talayco710438c2010-02-18 15:16:07 -0800344 if self.switch_socket:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700345 self.logger.warning(
346 "Ignoring incoming connection; already connected to switch" )
347 (sock, addr) = self.listen_socket.accept( )
348 sock.close( )
Rich Lanee1da7ea2012-07-26 15:58:45 -0700349 return 0
Dan Talayco710438c2010-02-18 15:16:07 -0800350
Ken Chiange875baf2012-10-09 15:24:40 -0700351 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700352 (sock, addr) = self.listen_socket.accept( )
Ken Chiange875baf2012-10-09 15:24:40 -0700353 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700354 self.logger.warning( "Error on listen socket accept" )
Ken Chiange875baf2012-10-09 15:24:40 -0700355 return -1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700356 self.logger.info( self.host + ":" + str(
357 self.port ) + ": Incoming connection from " + str( addr ) )
Rich Lanee1da7ea2012-07-26 15:58:45 -0700358
Rich Laneee3586c2012-07-11 17:26:02 -0700359 with self.connect_cv:
Rich Lanee1da7ea2012-07-26 15:58:45 -0700360 (self.switch_socket, self.switch_addr) = (sock, addr)
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700361 self.switch_socket.setsockopt( socket.IPPROTO_TCP,
362 socket.TCP_NODELAY, True )
Rich Lane1a8d5aa2012-10-08 15:40:03 -0700363 if self.initial_hello:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700364 self.message_send( cfg_ofp.message.hello( ) )
365 self.connect_cv.notify( ) # Notify anyone waiting
Rich Laned929b8d2013-04-15 15:59:14 -0700366
367 # Prevent further connections
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700368 self.listen_socket.close( )
Rich Laned929b8d2013-04-15 15:59:14 -0700369 self.listen_socket = None
Dan Talaycof8de5182012-04-12 22:38:41 -0700370 elif s and s == self.switch_socket:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700371 for idx in range( 3 ): # debug: try a couple of times
Dan Talaycof8de5182012-04-12 22:38:41 -0700372 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700373 pkt = self.switch_socket.recv( self.rcv_size )
Dan Talaycof8de5182012-04-12 22:38:41 -0700374 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700375 self.logger.warning( "Error on switch read" )
Dan Talaycof8de5182012-04-12 22:38:41 -0700376 return -1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700377
Dan Talaycof8de5182012-04-12 22:38:41 -0700378 if not self.active:
379 return 0
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700380
381 if len( pkt ) == 0:
382 self.logger.warning( "Zero-length switch read, %d" % idx )
Dan Talaycof8de5182012-04-12 22:38:41 -0700383 else:
384 break
Dan Talayco710438c2010-02-18 15:16:07 -0800385
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700386 if len( pkt ) == 0: # Still no packet
387 self.logger.warning( "Zero-length switch read; closing cxn" )
388 self.logger.info( str( self ) )
Dan Talaycof8de5182012-04-12 22:38:41 -0700389 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800390
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700391 self._pkt_handle( pkt )
Rich Lane4dfd5e12012-12-22 19:48:01 -0800392 elif s and s == self.waker:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700393 self.waker.wait( )
Dan Talayco710438c2010-02-18 15:16:07 -0800394 else:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700395 self.logger.error( "Unknown socket ready: " + str( s ) )
Dan Talaycof8de5182012-04-12 22:38:41 -0700396 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800397
Dan Talaycof8de5182012-04-12 22:38:41 -0700398 return 0
Dan Talayco710438c2010-02-18 15:16:07 -0800399
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700400 def active_connect( self ):
Dan Talayco69ca4d62012-11-15 11:50:22 -0800401 """
402 Actively connect to a switch IP addr
403 """
404 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700405 self.logger.info( "Trying active connection to %s" % self.switch )
406 soc = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
407 soc.connect( (self.switch, self.port) )
408 self.logger.info( "Connected to " + self.switch + " on " +
409 str( self.port ) )
410 soc.setsockopt( socket.IPPROTO_TCP, socket.TCP_NODELAY, True )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800411 self.switch_addr = (self.switch, self.port)
412 return soc
413 except (StandardError, socket.error), e:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700414 self.logger.error( "Could not connect to %s at %d:: %s" %
415 (self.switch, self.port, str( e )) )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800416 return None
417
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700418 def wakeup( self ):
Rich Lane32797542012-12-22 17:46:05 -0800419 """
420 Wake up the event loop, presumably from another thread.
421 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700422 self.waker.notify( )
Rich Lane32797542012-12-22 17:46:05 -0800423
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700424 def sockets( self ):
Rich Lane32797542012-12-22 17:46:05 -0800425 """
426 Return list of sockets to select on.
427 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700428 socs = [ self.listen_socket, self.switch_socket, self.waker ]
429 return [ x for x in socs if x ]
Rich Lane32797542012-12-22 17:46:05 -0800430
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700431 def run( self ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800432 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800433 Activity function for class
Dan Talayco21c75c72010-02-12 22:59:24 -0800434
Dan Talayco1b3f6902010-02-15 14:14:19 -0800435 Assumes connection to switch already exists. Listens on
436 switch_socket for messages until an error (or zero len pkt)
437 occurs.
Dan Talayco21c75c72010-02-12 22:59:24 -0800438
Dan Talayco1b3f6902010-02-15 14:14:19 -0800439 When there is a message on the socket, check for handlers; queue the
440 packet if no one handles the packet.
441
442 See note for controller describing the limitation of a single
443 connection for now.
444 """
445
Rich Lane207502e2012-12-31 14:29:12 -0800446 self.dbg_state = "running"
Ken Chiangadc950f2012-10-05 13:50:03 -0700447
Dan Talayco710438c2010-02-18 15:16:07 -0800448 while self.active:
Dan Talayco710438c2010-02-18 15:16:07 -0800449 try:
450 sel_in, sel_out, sel_err = \
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700451 select.select( self.sockets( ), [ ], self.sockets( ), 1 )
Dan Talayco710438c2010-02-18 15:16:07 -0800452 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700453 print sys.exc_info( )
454 self.logger.error( "Select error, disconnecting" )
455 self.disconnect( )
Dan Talayco1b3f6902010-02-15 14:14:19 -0800456
Dan Talayco710438c2010-02-18 15:16:07 -0800457 for s in sel_err:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700458 self.logger.error(
459 "Got socket error on: " + str( s ) + ", disconnecting" )
460 self.disconnect( )
Dan Talaycof8de5182012-04-12 22:38:41 -0700461
462 for s in sel_in:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700463 if self._socket_ready_handle( s ) == -1:
464 self.disconnect( )
Dan Talayco710438c2010-02-18 15:16:07 -0800465
Dan Talayco710438c2010-02-18 15:16:07 -0800466 # End of main loop
467 self.dbg_state = "closing"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700468 self.logger.info( "Exiting controller thread" )
469 self.shutdown( )
Dan Talayco710438c2010-02-18 15:16:07 -0800470
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700471 def connect( self, timeout=-1 ):
Dan Talayco710438c2010-02-18 15:16:07 -0800472 """
473 Connect to the switch
474
Rich Lane8806bc42012-07-26 19:18:37 -0700475 @param timeout Block for up to timeout seconds. Pass -1 for the default.
Dan Talayco710438c2010-02-18 15:16:07 -0800476 @return Boolean, True if connected
477 """
478
Dan Talayco69ca4d62012-11-15 11:50:22 -0800479 if not self.passive: # Do active connection now
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700480 self.logger.info( "Attempting to connect to %s on port %s" %
481 (self.switch, str( self.port )) )
482 soc = self.active_connect( )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800483 if soc:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700484 self.logger.info( "Connected to %s", self.switch )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800485 self.dbg_state = "running"
486 self.switch_socket = soc
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700487 self.wakeup( )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800488 with self.connect_cv:
489 if self.initial_hello:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700490 self.message_send( cfg_ofp.message.hello( ) )
491 self.connect_cv.notify( ) # Notify anyone waiting
Dan Talayco69ca4d62012-11-15 11:50:22 -0800492 else:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700493 self.logger.error( "Could not actively connect to switch %s",
494 self.switch )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800495 self.active = False
496 else:
497 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700498 ofutils.timed_wait( self.connect_cv, lambda: self.switch_socket,
499 timeout=timeout )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800500
Dan Talayco710438c2010-02-18 15:16:07 -0800501 return self.switch_socket is not None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700502
503 def disconnect( self, timeout=-1 ):
Ken Chiangadc950f2012-10-05 13:50:03 -0700504 """
505 If connected to a switch, disconnect.
506 """
507 if self.switch_socket:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700508 self.switch_socket.close( )
Ken Chiangadc950f2012-10-05 13:50:03 -0700509 self.switch_socket = None
510 self.switch_addr = None
Ken Chiang74be4722012-12-21 13:07:03 -0800511 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700512 self.packets = [ ]
Ken Chiange875baf2012-10-09 15:24:40 -0700513 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700514 self.connect_cv.notifyAll( )
Ken Chiangadc950f2012-10-05 13:50:03 -0700515
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700516 def wait_disconnected( self, timeout=-1 ):
Ken Chiangadc950f2012-10-05 13:50:03 -0700517 """
518 @param timeout Block for up to timeout seconds. Pass -1 for the default.
519 @return Boolean, True if disconnected
520 """
521
Ken Chiange875baf2012-10-09 15:24:40 -0700522 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700523 ofutils.timed_wait( self.connect_cv,
524 lambda: True if not self.switch_socket else None,
525 timeout=timeout )
Ken Chiangadc950f2012-10-05 13:50:03 -0700526 return self.switch_socket is None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700527
528 def kill( self ):
Dan Talayco710438c2010-02-18 15:16:07 -0800529 """
530 Force the controller thread to quit
Dan Talayco710438c2010-02-18 15:16:07 -0800531 """
532 self.active = False
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700533 self.wakeup( )
534 self.join( )
Dan Talayco21c75c72010-02-12 22:59:24 -0800535
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700536 def shutdown( self ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800537 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800538 Shutdown the controller closing all sockets
Dan Talayco21c75c72010-02-12 22:59:24 -0800539
Dan Talayco1b3f6902010-02-15 14:14:19 -0800540 @todo Might want to synchronize shutdown with self.sync...
541 """
Dan Talaycof8de5182012-04-12 22:38:41 -0700542
Dan Talayco710438c2010-02-18 15:16:07 -0800543 self.active = False
544 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700545 self.switch_socket.shutdown( socket.SHUT_RDWR )
Dan Talayco710438c2010-02-18 15:16:07 -0800546 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700547 self.logger.info( "Ignoring switch soc shutdown error" )
Dan Talayco710438c2010-02-18 15:16:07 -0800548 self.switch_socket = None
Dan Talayco1b3f6902010-02-15 14:14:19 -0800549
Dan Talayco710438c2010-02-18 15:16:07 -0800550 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700551 self.listen_socket.shutdown( socket.SHUT_RDWR )
Dan Talayco710438c2010-02-18 15:16:07 -0800552 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700553 self.logger.info( "Ignoring listen soc shutdown error" )
Dan Talayco710438c2010-02-18 15:16:07 -0800554 self.listen_socket = None
Dan Talaycof8de5182012-04-12 22:38:41 -0700555
Rich Laneee3586c2012-07-11 17:26:02 -0700556 # Wakeup condition variables on which controller may be wait
557 with self.xid_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700558 self.xid_cv.notifyAll( )
Dan Talaycof8de5182012-04-12 22:38:41 -0700559
Rich Laneee3586c2012-07-11 17:26:02 -0700560 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700561 self.connect_cv.notifyAll( )
Dan Talaycof8de5182012-04-12 22:38:41 -0700562
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700563 self.wakeup( )
Dan Talayco710438c2010-02-18 15:16:07 -0800564 self.dbg_state = "down"
565
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700566 def register( self, msg_type, handler ):
Dan Talayco34089522010-02-07 23:07:41 -0800567 """
568 Register a callback to receive a specific message type.
569
570 Only one handler may be registered for a given message type.
Dan Talaycod12b6612010-03-07 22:00:46 -0800571
572 WARNING: A lock is held during the handler call back, so
573 the handler should not make any blocking calls
574
Dan Talayco34089522010-02-07 23:07:41 -0800575 @param msg_type The type of message to receive. May be DEFAULT
Dan Talayco21c75c72010-02-12 22:59:24 -0800576 for all non-handled packets. The special type, the string "all"
577 will send all packets to the handler.
Dan Talayco34089522010-02-07 23:07:41 -0800578 @param handler The function to call when a message of the given
579 type is received.
580 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800581 # Should check type is valid
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700582 if not handler and msg_type in self.handlers.keys( ):
583 del self.handlers[ msg_type ]
Dan Talayco21c75c72010-02-12 22:59:24 -0800584 return
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700585 self.handlers[ msg_type ] = handler
Dan Talayco34089522010-02-07 23:07:41 -0800586
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700587 def poll( self, exp_msg=None, timeout=-1 ):
Dan Talayco34089522010-02-07 23:07:41 -0800588 """
589 Wait for the next OF message received from the switch.
590
591 @param exp_msg If set, return only when this type of message
Dan Talayco48370102010-03-03 15:17:33 -0800592 is received (unless timeout occurs).
Rich Laneb64ce3d2012-07-26 15:37:57 -0700593
594 @param timeout Maximum number of seconds to wait for the message.
595 Pass -1 for the default timeout.
Dan Talayco34089522010-02-07 23:07:41 -0800596
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800597 @retval A pair (msg, pkt) where msg is a message object and pkt
598 the string representing the packet as received from the socket.
599 This allows additional parsing by the receiver if necessary.
600
Dan Talayco34089522010-02-07 23:07:41 -0800601 The data members in the message are in host endian order.
Dan Talayco48370102010-03-03 15:17:33 -0800602 If an error occurs, (None, None) is returned
Dan Talayco34089522010-02-07 23:07:41 -0800603 """
Dan Talayco34089522010-02-07 23:07:41 -0800604
Rich Lanee9d36912014-01-31 12:46:05 -0800605 if exp_msg is None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700606 self.logger.warn( "DEPRECATED polling for any message class" )
Rich Lanee9d36912014-01-31 12:46:05 -0800607 klass = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700608 elif isinstance( exp_msg, int ):
609 klass = cfg_ofp.message.message.subtypes[ exp_msg ]
610 elif issubclass( exp_msg, loxi.OFObject ):
Rich Lanee9d36912014-01-31 12:46:05 -0800611 klass = exp_msg
Ed Swierk9e55e282012-08-22 06:57:28 -0700612 else:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700613 raise ValueError( "Unexpected exp_msg argument %r" % exp_msg )
Rich Lanee9d36912014-01-31 12:46:05 -0800614
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700615 self.logger.debug( "Polling for %s", klass.__name__ )
Rich Laneb64ce3d2012-07-26 15:37:57 -0700616
617 # Take the packet from the queue
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700618 def grab( ):
619 for i, (msg, pkt) in enumerate( self.packets ):
620 if klass is None or isinstance( msg, klass ):
621 self.logger.debug( "Got %s message",
622 msg.__class__.__name__ )
623 return self.packets.pop( i )
Rich Lanec4f071b2012-07-11 17:25:57 -0700624 # Not found
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700625 self.logger.debug( "%s message not in queue", klass.__name__ )
Rich Laneb64ce3d2012-07-26 15:37:57 -0700626 return None
Dan Talayco21c75c72010-02-12 22:59:24 -0800627
Rich Lanec4f071b2012-07-11 17:25:57 -0700628 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700629 ret = ofutils.timed_wait( self.packets_cv, grab, timeout=timeout )
Rich Lanec4f071b2012-07-11 17:25:57 -0700630
Rich Laneb64ce3d2012-07-26 15:37:57 -0700631 if ret != None:
632 (msg, pkt) = ret
Rich Laneb64ce3d2012-07-26 15:37:57 -0700633 return (msg, pkt)
634 else:
635 return (None, None)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800636
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700637 def transact( self, msg, timeout=-1 ):
Dan Talayco34089522010-02-07 23:07:41 -0800638 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800639 Run a message transaction with the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800640
641 Send the message in msg and wait for a reply with a matching
Dan Talayco21c75c72010-02-12 22:59:24 -0800642 transaction id. Transactions have the highest priority in
643 received message handling.
Dan Talaycoe37999f2010-02-09 15:27:12 -0800644
Dan Talayco21c75c72010-02-12 22:59:24 -0800645 @param msg The message object to send; must not be a string
Rich Lanee1da7ea2012-07-26 15:58:45 -0700646 @param timeout The timeout in seconds; if -1 use default.
Dan Talayco34089522010-02-07 23:07:41 -0800647 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800648
Rich Lane8fbfd662013-03-11 15:30:44 -0700649 if msg.xid == None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700650 msg.xid = ofutils.gen_xid( )
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800651
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700652 self.logger.debug( "Running transaction %d" % msg.xid )
Dan Talayco21c75c72010-02-12 22:59:24 -0800653
Rich Lane9aca1992012-07-11 17:26:31 -0700654 with self.xid_cv:
655 if self.xid:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700656 self.logger.error( "Can only run one transaction at a time" )
Rich Lane9aca1992012-07-11 17:26:31 -0700657 return (None, None)
Dan Talaycof8de5182012-04-12 22:38:41 -0700658
Rich Laneb73808c2013-03-11 15:22:23 -0700659 self.xid = msg.xid
Dan Talaycod12b6612010-03-07 22:00:46 -0800660 self.xid_response = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700661 self.message_send( msg )
Rich Lane9aca1992012-07-11 17:26:31 -0700662
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700663 self.logger.debug( "Waiting for transaction %d" % msg.xid )
664 ofutils.timed_wait( self.xid_cv, lambda: self.xid_response,
665 timeout=timeout )
Rich Lane9aca1992012-07-11 17:26:31 -0700666
667 if self.xid_response:
668 (resp, pkt) = self.xid_response
669 self.xid_response = None
670 else:
671 (resp, pkt) = (None, None)
672
Dan Talayco09c2c592010-05-13 14:21:52 -0700673 if resp is None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700674 self.logger.warning( "No response for xid " + str( self.xid ) )
Dan Talayco09c2c592010-05-13 14:21:52 -0700675 return (resp, pkt)
Dan Talayco34089522010-02-07 23:07:41 -0800676
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700677 def message_send( self, msg ):
Dan Talayco34089522010-02-07 23:07:41 -0800678 """
679 Send the message to the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800680
Dan Talayco11c26e72010-03-07 22:03:57 -0800681 @param msg A string or OpenFlow message object to be forwarded to
682 the switch.
Dan Talayco21c75c72010-02-12 22:59:24 -0800683 """
684
Dan Talayco1b3f6902010-02-15 14:14:19 -0800685 if not self.switch_socket:
686 # Sending a string indicates the message is ready to go
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700687 raise Exception( "no socket" )
Dan Talayco21c75c72010-02-12 22:59:24 -0800688
Rich Lane1fd43e32014-01-06 15:22:50 -0800689 if msg.xid == None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700690 msg.xid = ofutils.gen_xid( )
Rich Lane1fd43e32014-01-06 15:22:50 -0800691
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700692 outpkt = msg.pack( )
Rich Lane1fd43e32014-01-06 15:22:50 -0800693
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700694 self.logger.debug( "Msg out: version %d class %s len %d xid %d",
695 msg.version, type( msg ).__name__, len( outpkt ),
696 msg.xid )
Rich Lanec9d3edd2013-10-09 00:21:01 -0700697
698 with self.tx_lock:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700699 if self.switch_socket.sendall( outpkt ) is not None:
700 raise AssertionError( "failed to send message to switch" )
Dan Talayco710438c2010-02-18 15:16:07 -0800701
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700702 return 0 # for backwards compatibility
Dan Talayco21c75c72010-02-12 22:59:24 -0800703
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700704 def clear_queue( self ):
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700705 """
706 Clear the input queue and report the number of messages
707 that were in it
708 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700709 enqueued_pkt_count = len( self.packets )
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700710 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700711 self.packets = [ ]
Dan Talayco7071cf12013-04-16 11:02:13 -0700712 return enqueued_pkt_count
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700713
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700714 def __str__( self ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800715 string = "Controller:\n"
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800716 string += " state " + self.dbg_state + "\n"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700717 string += " switch_addr " + str( self.switch_addr ) + "\n"
718 string += " pending pkts " + str( len( self.packets ) ) + "\n"
719 string += " total pkts " + str( self.packets_total ) + "\n"
720 string += " expired pkts " + str( self.packets_expired ) + "\n"
721 string += " handled pkts " + str( self.packets_handled ) + "\n"
722 string += " poll discards " + str( self.poll_discards ) + "\n"
723 string += " parse errors " + str( self.parse_errors ) + "\n"
724 string += " sock errrors " + str( self.socket_errors ) + "\n"
725 string += " max pkts " + str( self.max_pkts ) + "\n"
726 string += " target switch " + str( self.switch ) + "\n"
727 string += " host " + str( self.host ) + "\n"
728 string += " port " + str( self.port ) + "\n"
729 string += " keep_alive " + str( self.keep_alive ) + "\n"
730 string += " pkt_in_run " + str( self.pkt_in_run ) + "\n"
731 string += " pkt_in_dropped " + str( self.pkt_in_dropped ) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800732 return string
733
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700734 def show( self ):
735 print str( self )
Dan Talayco21c75c72010-02-12 22:59:24 -0800736
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700737
738def sample_handler( controller, msg, pkt ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800739 """
740 Sample message handler
741
742 This is the prototype for functions registered with the controller
743 class for packet reception
744
745 @param controller The controller calling the handler
746 @param msg The parsed message object
747 @param pkt The raw packet that was received on the socket. This is
748 in case the packet contains extra unparsed data.
749 @returns Boolean value indicating if the packet was handled. If
750 not handled, the packet is placed in the queue for pollers to received
751 """
752 pass