blob: 7415fd007b69540de63c882555d119e9d3f3cb34 [file] [log] [blame]
Matteo Scandoloa229eca2017-08-08 13:05:28 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Dan Talayco34089522010-02-07 23:07:41 -080017"""
18OpenFlow Test Framework
19
20Controller class
21
22Provide the interface to the control channel to the switch under test.
23
24Class inherits from thread so as to run in background allowing
25asynchronous callbacks (if needed, not required). Also supports
26polling.
27
28The controller thread maintains a queue. Incoming messages that
29are not handled by a callback function are placed in this queue for
30poll calls.
31
32Callbacks and polling support specifying the message type
33
34@todo Support transaction semantics via xid
Dan Talayco1b3f6902010-02-15 14:14:19 -080035@todo Support select and listen on an administrative socket (or
36use a timeout to support clean shutdown).
37
38Currently only one connection is accepted during the life of
39the controller. There seems
40to be no clean way to interrupt an accept call. Using select that also listens
41on an administrative socket and can shut down the socket might work.
42
Dan Talayco34089522010-02-07 23:07:41 -080043"""
44
Rich Lane720eaf22013-08-09 18:00:45 -070045import sys
Dan Talayco34089522010-02-07 23:07:41 -080046import os
47import socket
48import time
Rich Lanecd97d3d2013-01-07 18:50:06 -080049import struct
50import select
51import logging
Dan Talayco34089522010-02-07 23:07:41 -080052from threading import Thread
53from threading import Lock
Dan Talayco21c75c72010-02-12 22:59:24 -080054from threading import Condition
Dan Talayco48370102010-03-03 15:17:33 -080055
Wilson Ngc11a9182013-10-28 16:02:03 -070056import ofutils
57import loxi
58
59# Configured openflow version
60import ofp as cfg_ofp
Dan Talaycof8de5182012-04-12 22:38:41 -070061
Flavio Castrob01d0aa2016-07-20 16:14:48 -070062FILTER = ''.join( [ (len( repr( chr( x ) ) ) == 3) and chr( x ) or '.'
63 for x in range( 256 ) ] )
Dan Talaycof8de5182012-04-12 22:38:41 -070064
Flavio Castrob01d0aa2016-07-20 16:14:48 -070065
66def hex_dump_buffer( src, length=16 ):
Dan Talaycof8de5182012-04-12 22:38:41 -070067 """
68 Convert src to a hex dump string and return the string
69 @param src The source buffer
70 @param length The number of bytes shown in each line
71 @returns A string showing the hex dump
72 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -070073 result = [ "\n" ]
74 for i in xrange( 0, len( src ), length ):
75 chars = src[ i:i + length ]
76 hex = ' '.join( [ "%02x" % ord( x ) for x in chars ] )
77 printable = ''.join( [ "%s" % ((ord( x ) <= 127 and
78 FILTER[ ord( x ) ]) or '.') for x in
79 chars ] )
80 result.append( "%04x %-*s %s\n" % (i, length * 3, hex, printable) )
81 return ''.join( result )
82
Dan Talaycof8de5182012-04-12 22:38:41 -070083
Dan Talayco48370102010-03-03 15:17:33 -080084##@todo Find a better home for these identifiers (controller)
Glen Gibb741b1182010-07-08 16:43:58 -070085RCV_SIZE_DEFAULT = 32768
Dan Talayco48370102010-03-03 15:17:33 -080086LISTEN_QUEUE_SIZE = 1
Dan Talayco34089522010-02-07 23:07:41 -080087
Flavio Castrob01d0aa2016-07-20 16:14:48 -070088
89class Controller( Thread ):
Dan Talayco34089522010-02-07 23:07:41 -080090 """
91 Class abstracting the control interface to the switch.
92
93 For receiving messages, two mechanism will be implemented. First,
94 query the interface with poll. Second, register to have a
95 function called by message type. The callback is passed the
96 message type as well as the raw packet (or message object)
97
98 One of the main purposes of this object is to translate between network
99 and host byte order. 'Above' this object, things should be in host
100 byte order.
Dan Talayco21c75c72010-02-12 22:59:24 -0800101
102 @todo Consider using SocketServer for listening socket
103 @todo Test transaction code
104
105 @var rcv_size The receive size to use for receive calls
106 @var max_pkts The max size of the receive queue
107 @var keep_alive If true, listen for echo requests and respond w/
108 echo replies
Dan Talayco710438c2010-02-18 15:16:07 -0800109 @var initial_hello If true, will send a hello message immediately
110 upon connecting to the switch
Dan Talayco69ca4d62012-11-15 11:50:22 -0800111 @var switch If not None, do an active connection to the switch
Dan Talayco21c75c72010-02-12 22:59:24 -0800112 @var host The host to use for connect
113 @var port The port to connect on
114 @var packets_total Total number of packets received
115 @var packets_expired Number of packets popped from queue as queue full
116 @var packets_handled Number of packets handled by something
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800117 @var dbg_state Debug indication of state
Dan Talayco34089522010-02-07 23:07:41 -0800118 """
119
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700120 def __init__( self, switch=None, host='127.0.0.1', port=6653, max_pkts=1024,
121 force=False ):
122 Thread.__init__( self )
Dan Talayco1b3f6902010-02-15 14:14:19 -0800123 # Socket related
Dan Talayco21c75c72010-02-12 22:59:24 -0800124 self.rcv_size = RCV_SIZE_DEFAULT
Dan Talayco1b3f6902010-02-15 14:14:19 -0800125 self.listen_socket = None
126 self.switch_socket = None
127 self.switch_addr = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700128 self.connect_cv = Condition( )
129 self.message_cv = Condition( )
130 self.tx_lock = Lock( )
Dan Talayco1b3f6902010-02-15 14:14:19 -0800131
Rich Lane4dfd5e12012-12-22 19:48:01 -0800132 # Used to wake up the event loop from another thread
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700133 self.waker = ofutils.EventDescriptor( )
Rich Lane32797542012-12-22 17:46:05 -0800134
Dan Talayco1b3f6902010-02-15 14:14:19 -0800135 # Counters
Dan Talayco21c75c72010-02-12 22:59:24 -0800136 self.socket_errors = 0
137 self.parse_errors = 0
Dan Talayco21c75c72010-02-12 22:59:24 -0800138 self.packets_total = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -0800139 self.packets_expired = 0
140 self.packets_handled = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -0800141 self.poll_discards = 0
Dan Talayco1b3f6902010-02-15 14:14:19 -0800142
143 # State
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700144 self.sync = Lock( )
145 self.handlers = { }
Dan Talayco21c75c72010-02-12 22:59:24 -0800146 self.keep_alive = False
Dan Talayco710438c2010-02-18 15:16:07 -0800147 self.active = True
148 self.initial_hello = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800149
Rich Lanec4f071b2012-07-11 17:25:57 -0700150 # OpenFlow message/packet queue
151 # Protected by the packets_cv lock / condition variable
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700152 self.packets = [ ]
153 self.packets_cv = Condition( )
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700154 self.packet_in_count = 0
Rich Lanec4f071b2012-07-11 17:25:57 -0700155
Dan Talayco1b3f6902010-02-15 14:14:19 -0800156 # Settings
157 self.max_pkts = max_pkts
Dan Talayco69ca4d62012-11-15 11:50:22 -0800158 self.switch = switch
159 self.passive = not self.switch
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700160 self.force = force
Dan Talayco48370102010-03-03 15:17:33 -0800161 self.host = host
162 self.port = port
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800163 self.dbg_state = "init"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700164 self.logger = logging.getLogger( "controller" )
165 self.filter_packet_in = False # Drop "excessive" packet ins
166 self.pkt_in_run = 0 # Count on run of packet ins
167 self.pkt_in_filter_limit = 50 # Count on run of packet ins
168 self.pkt_in_dropped = 0 # Total dropped packet ins
169 self.transact_to = 15 # Transact timeout default value; add to config
Dan Talayco1b3f6902010-02-15 14:14:19 -0800170
Dan Talaycoe226eb12010-02-18 23:06:30 -0800171 # Transaction and message type waiting variables
172 # xid_cv: Condition variable (semaphore) for packet waiters
Dan Talayco21c75c72010-02-12 22:59:24 -0800173 # xid: Transaction ID being waited on
174 # xid_response: Transaction response message
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700175 self.xid_cv = Condition( )
Dan Talayco21c75c72010-02-12 22:59:24 -0800176 self.xid = None
177 self.xid_response = None
178
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800179 self.buffered_input = ""
Dan Talaycoe226eb12010-02-18 23:06:30 -0800180
Rich Lane207502e2012-12-31 14:29:12 -0800181 # Create listen socket
182 if self.passive:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700183 self.logger.info( "Create/listen at " + self.host + ":" +
184 str( self.port ) )
185 ai = socket.getaddrinfo( self.host, self.port, socket.AF_UNSPEC,
186 socket.SOCK_STREAM, 0, socket.AI_PASSIVE )
Ken Chiangbf84c332015-01-27 12:52:27 -0800187 # Use first returned addrinfo
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700188 (family, socktype, proto, name, sockaddr) = ai[ 0 ]
189 self.listen_socket = socket.socket( family, socktype )
190 self.listen_socket.setsockopt( socket.SOL_SOCKET,
191 socket.SO_REUSEADDR, 1 )
192 self.listen_socket.bind( sockaddr )
193 self.listen_socket.listen( LISTEN_QUEUE_SIZE )
Rich Lane207502e2012-12-31 14:29:12 -0800194
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700195 def filter_packet( self, rawmsg, hdr ):
Dan Talaycof8de5182012-04-12 22:38:41 -0700196 """
197 Check if packet should be filtered
198
199 Currently filters packet in messages
200 @return Boolean, True if packet should be dropped
201 """
Rich Lane1622bbb2013-03-11 17:11:53 -0700202 # XXX didn't actually check for packet-in...
203 return False
Dan Talaycof8de5182012-04-12 22:38:41 -0700204 # Add check for packet in and rate limit
205 if self.filter_packet_in:
Rich Lanec4f071b2012-07-11 17:25:57 -0700206 # If we were dropping packets, report number dropped
207 # TODO dont drop expected packet ins
208 if self.pkt_in_run > self.pkt_in_filter_limit:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700209 self.logger.debug( "Dropped %d packet ins (%d total)"
210 % ((self.pkt_in_run -
211 self.pkt_in_filter_limit),
212 self.pkt_in_dropped) )
Rich Lanec4f071b2012-07-11 17:25:57 -0700213 self.pkt_in_run = 0
Dan Talaycof8de5182012-04-12 22:38:41 -0700214
215 return False
216
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700217 def _pkt_handle( self, pkt ):
Dan Talaycod12b6612010-03-07 22:00:46 -0800218 """
219 Check for all packet handling conditions
220
221 Parse and verify message
222 Check if XID matches something waiting
223 Check if message is being expected for a poll operation
224 Check if keep alive is on and message is an echo request
225 Check if any registered handler wants the packet
226 Enqueue if none of those conditions is met
227
228 an echo request in case keep_alive is true, followed by
229 registered message handlers.
Glen Gibb6d467062010-07-08 16:15:08 -0700230 @param pkt The raw packet (string) which may contain multiple OF msgs
Dan Talaycod12b6612010-03-07 22:00:46 -0800231 """
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800232
233 # snag any left over data from last read()
234 pkt = self.buffered_input + pkt
235 self.buffered_input = ""
236
Glen Gibb6d467062010-07-08 16:15:08 -0700237 # Process each of the OF msgs inside the pkt
238 offset = 0
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700239 while offset < len( pkt ):
240 if offset + 8 > len( pkt ):
Rich Lane1622bbb2013-03-11 17:11:53 -0700241 break
242
Glen Gibb6d467062010-07-08 16:15:08 -0700243 # Parse the header to get type
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700244 hdr_version, hdr_type, hdr_length, hdr_xid = cfg_ofp.message.parse_header(
245 pkt[ offset: ] )
Wilson Ngc11a9182013-10-28 16:02:03 -0700246
247 # Use loxi to resolve to ofp of matching version
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700248 ofp = loxi.protocol( hdr_version )
Dan Talaycod12b6612010-03-07 22:00:46 -0800249
Glen Gibb6d467062010-07-08 16:15:08 -0700250 # Extract the raw message bytes
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700251 if (offset + hdr_length) > len( pkt ):
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800252 break
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700253 rawmsg = pkt[ offset: offset + hdr_length ]
Rich Lane1622bbb2013-03-11 17:11:53 -0700254 offset += hdr_length
Dan Talaycof8de5182012-04-12 22:38:41 -0700255
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700256 # if self.filter_packet(rawmsg, hdr):
Rich Lane1622bbb2013-03-11 17:11:53 -0700257 # continue
Dan Talaycof8de5182012-04-12 22:38:41 -0700258
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700259 msg = ofp.message.parse_message( rawmsg )
Glen Gibb6d467062010-07-08 16:15:08 -0700260 if not msg:
261 self.parse_errors += 1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700262 self.logger.warn( "Could not parse message" )
Glen Gibb6d467062010-07-08 16:15:08 -0700263 continue
264
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700265 self.logger.debug( "Msg in: version %d class %s len %d xid %d",
266 hdr_version, type( msg ).__name__, hdr_length,
267 hdr_xid )
Rich Lane1fd43e32014-01-06 15:22:50 -0800268
Rich Lanec4f071b2012-07-11 17:25:57 -0700269 with self.sync:
270 # Check if transaction is waiting
271 with self.xid_cv:
Rich Lane1622bbb2013-03-11 17:11:53 -0700272 if self.xid and hdr_xid == self.xid:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700273 self.logger.debug(
274 "Matched expected XID " + str( hdr_xid ) )
Rich Lanec4f071b2012-07-11 17:25:57 -0700275 self.xid_response = (msg, rawmsg)
276 self.xid = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700277 self.xid_cv.notify( )
Rich Lanec4f071b2012-07-11 17:25:57 -0700278 continue
Glen Gibb6d467062010-07-08 16:15:08 -0700279
Rich Lanec4f071b2012-07-11 17:25:57 -0700280 # Check if keep alive is set; if so, respond to echo requests
281 if self.keep_alive:
Rich Lane1622bbb2013-03-11 17:11:53 -0700282 if hdr_type == ofp.OFPT_ECHO_REQUEST:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700283 self.logger.debug( "Responding to echo request" )
284 rep = ofp.message.echo_reply( )
Rich Lane1622bbb2013-03-11 17:11:53 -0700285 rep.xid = hdr_xid
Rich Lanec4f071b2012-07-11 17:25:57 -0700286 # Ignoring additional data
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700287 self.message_send( rep )
Rich Lanec4f071b2012-07-11 17:25:57 -0700288 continue
Glen Gibb6d467062010-07-08 16:15:08 -0700289
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700290 # Generalize to counters for all packet types?
291 if msg.type == ofp.OFPT_PACKET_IN:
292 self.packet_in_count += 1
293
Rich Lane5d63b9c2013-01-11 14:12:37 -0800294 # Log error messages
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700295 if isinstance( msg, ofp.message.error_msg ):
296 # pylint: disable=E1103
Rich Laneb73808c2013-03-11 15:22:23 -0700297 if msg.err_type in ofp.ofp_error_type_map:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700298 type_str = ofp.ofp_error_type_map[ msg.err_type ]
Rich Laneb73808c2013-03-11 15:22:23 -0700299 if msg.err_type == ofp.OFPET_HELLO_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800300 code_map = ofp.ofp_hello_failed_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700301 elif msg.err_type == ofp.OFPET_BAD_REQUEST:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800302 code_map = ofp.ofp_bad_request_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700303 elif msg.err_type == ofp.OFPET_BAD_ACTION:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800304 code_map = ofp.ofp_bad_action_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700305 elif msg.err_type == ofp.OFPET_FLOW_MOD_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800306 code_map = ofp.ofp_flow_mod_failed_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700307 elif msg.err_type == ofp.OFPET_PORT_MOD_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800308 code_map = ofp.ofp_port_mod_failed_code_map
Rich Laneb73808c2013-03-11 15:22:23 -0700309 elif msg.err_type == ofp.OFPET_QUEUE_OP_FAILED:
Rich Lane5d63b9c2013-01-11 14:12:37 -0800310 code_map = ofp.ofp_queue_op_failed_code_map
311 else:
312 code_map = None
313
314 if code_map and msg.code in code_map:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700315 code_str = code_map[ msg.code ]
Rich Lane5d63b9c2013-01-11 14:12:37 -0800316 else:
317 code_str = "unknown"
318 else:
319 type_str = "unknown"
Rich Lane1879dc72013-03-11 22:08:51 -0700320 code_str = "unknown"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700321 self.logger.warn(
322 "Received error message: xid=%d type=%s (%d) code=%s (%d)",
323 hdr_xid, type_str, msg.err_type, code_str,
324 msg.code )
Rich Lane5d63b9c2013-01-11 14:12:37 -0800325
Rich Lanec4f071b2012-07-11 17:25:57 -0700326 # Now check for message handlers; preference is given to
327 # handlers for a specific packet
328 handled = False
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700329 if hdr_type in self.handlers.keys( ):
330 handled = self.handlers[ hdr_type ]( self, msg, rawmsg )
331 if not handled and ("all" in self.handlers.keys( )):
332 handled = self.handlers[ "all" ]( self, msg, rawmsg )
Glen Gibb6d467062010-07-08 16:15:08 -0700333
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700334 if not handled: # Not handled, enqueue
Rich Lanec4f071b2012-07-11 17:25:57 -0700335 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700336 if len( self.packets ) >= self.max_pkts:
337 self.packets.pop( 0 )
Rich Lanec4f071b2012-07-11 17:25:57 -0700338 self.packets_expired += 1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700339 self.packets.append( (msg, rawmsg) )
340 self.packets_cv.notify_all( )
Rich Lanec4f071b2012-07-11 17:25:57 -0700341 self.packets_total += 1
342 else:
343 self.packets_handled += 1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700344 self.logger.debug( "Message handled by callback" )
Glen Gibb6d467062010-07-08 16:15:08 -0700345
Rob Sherwoode3e452a2012-03-06 09:24:26 -0800346 # end of 'while offset < len(pkt)'
347 # note that if offset = len(pkt), this is
348 # appends a harmless empty string
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700349 self.buffered_input += pkt[ offset: ]
Dan Talaycod12b6612010-03-07 22:00:46 -0800350
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700351 def _socket_ready_handle( self, s ):
Dan Talayco710438c2010-02-18 15:16:07 -0800352 """
353 Handle an input-ready socket
Dan Talaycof8de5182012-04-12 22:38:41 -0700354
Dan Talayco710438c2010-02-18 15:16:07 -0800355 @param s The socket object that is ready
Dan Talaycof8de5182012-04-12 22:38:41 -0700356 @returns 0 on success, -1 on error
Dan Talayco710438c2010-02-18 15:16:07 -0800357 """
358
Dan Talayco69ca4d62012-11-15 11:50:22 -0800359 if self.passive and s and s == self.listen_socket:
Dan Talayco710438c2010-02-18 15:16:07 -0800360 if self.switch_socket:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700361 self.logger.warning(
362 "Ignoring incoming connection; already connected to switch" )
363 (sock, addr) = self.listen_socket.accept( )
364 sock.close( )
Rich Lanee1da7ea2012-07-26 15:58:45 -0700365 return 0
Dan Talayco710438c2010-02-18 15:16:07 -0800366
Ken Chiange875baf2012-10-09 15:24:40 -0700367 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700368 (sock, addr) = self.listen_socket.accept( )
Ken Chiange875baf2012-10-09 15:24:40 -0700369 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700370 self.logger.warning( "Error on listen socket accept" )
Ken Chiange875baf2012-10-09 15:24:40 -0700371 return -1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700372 self.logger.info( self.host + ":" + str(
373 self.port ) + ": Incoming connection from " + str( addr ) )
Rich Lanee1da7ea2012-07-26 15:58:45 -0700374
Rich Laneee3586c2012-07-11 17:26:02 -0700375 with self.connect_cv:
Rich Lanee1da7ea2012-07-26 15:58:45 -0700376 (self.switch_socket, self.switch_addr) = (sock, addr)
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700377 self.switch_socket.setsockopt( socket.IPPROTO_TCP,
378 socket.TCP_NODELAY, True )
Rich Lane1a8d5aa2012-10-08 15:40:03 -0700379 if self.initial_hello:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700380 self.message_send( cfg_ofp.message.hello( ) )
381 self.connect_cv.notify( ) # Notify anyone waiting
Rich Laned929b8d2013-04-15 15:59:14 -0700382
383 # Prevent further connections
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700384 self.listen_socket.close( )
Rich Laned929b8d2013-04-15 15:59:14 -0700385 self.listen_socket = None
Dan Talaycof8de5182012-04-12 22:38:41 -0700386 elif s and s == self.switch_socket:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700387 for idx in range( 3 ): # debug: try a couple of times
Dan Talaycof8de5182012-04-12 22:38:41 -0700388 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700389 pkt = self.switch_socket.recv( self.rcv_size )
Dan Talaycof8de5182012-04-12 22:38:41 -0700390 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700391 self.logger.warning( "Error on switch read" )
Dan Talaycof8de5182012-04-12 22:38:41 -0700392 return -1
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700393
Dan Talaycof8de5182012-04-12 22:38:41 -0700394 if not self.active:
395 return 0
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700396
397 if len( pkt ) == 0:
398 self.logger.warning( "Zero-length switch read, %d" % idx )
Dan Talaycof8de5182012-04-12 22:38:41 -0700399 else:
400 break
Dan Talayco710438c2010-02-18 15:16:07 -0800401
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700402 if len( pkt ) == 0: # Still no packet
403 self.logger.warning( "Zero-length switch read; closing cxn" )
404 self.logger.info( str( self ) )
Dan Talaycof8de5182012-04-12 22:38:41 -0700405 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800406
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700407 self._pkt_handle( pkt )
Rich Lane4dfd5e12012-12-22 19:48:01 -0800408 elif s and s == self.waker:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700409 self.waker.wait( )
Dan Talayco710438c2010-02-18 15:16:07 -0800410 else:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700411 self.logger.error( "Unknown socket ready: " + str( s ) )
Dan Talaycof8de5182012-04-12 22:38:41 -0700412 return -1
Dan Talayco710438c2010-02-18 15:16:07 -0800413
Dan Talaycof8de5182012-04-12 22:38:41 -0700414 return 0
Dan Talayco710438c2010-02-18 15:16:07 -0800415
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700416 def active_connect( self ):
Dan Talayco69ca4d62012-11-15 11:50:22 -0800417 """
418 Actively connect to a switch IP addr
419 """
420 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700421 self.logger.info( "Trying active connection to %s" % self.switch )
422 soc = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
423 soc.connect( (self.switch, self.port) )
424 self.logger.info( "Connected to " + self.switch + " on " +
425 str( self.port ) )
426 soc.setsockopt( socket.IPPROTO_TCP, socket.TCP_NODELAY, True )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800427 self.switch_addr = (self.switch, self.port)
428 return soc
429 except (StandardError, socket.error), e:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700430 self.logger.error( "Could not connect to %s at %d:: %s" %
431 (self.switch, self.port, str( e )) )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800432 return None
433
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700434 def wakeup( self ):
Rich Lane32797542012-12-22 17:46:05 -0800435 """
436 Wake up the event loop, presumably from another thread.
437 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700438 self.waker.notify( )
Rich Lane32797542012-12-22 17:46:05 -0800439
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700440 def sockets( self ):
Rich Lane32797542012-12-22 17:46:05 -0800441 """
442 Return list of sockets to select on.
443 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700444 socs = [ self.listen_socket, self.switch_socket, self.waker ]
445 return [ x for x in socs if x ]
Rich Lane32797542012-12-22 17:46:05 -0800446
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700447 def run( self ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800448 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800449 Activity function for class
Dan Talayco21c75c72010-02-12 22:59:24 -0800450
Dan Talayco1b3f6902010-02-15 14:14:19 -0800451 Assumes connection to switch already exists. Listens on
452 switch_socket for messages until an error (or zero len pkt)
453 occurs.
Dan Talayco21c75c72010-02-12 22:59:24 -0800454
Dan Talayco1b3f6902010-02-15 14:14:19 -0800455 When there is a message on the socket, check for handlers; queue the
456 packet if no one handles the packet.
457
458 See note for controller describing the limitation of a single
459 connection for now.
460 """
461
Rich Lane207502e2012-12-31 14:29:12 -0800462 self.dbg_state = "running"
Ken Chiangadc950f2012-10-05 13:50:03 -0700463
Dan Talayco710438c2010-02-18 15:16:07 -0800464 while self.active:
Dan Talayco710438c2010-02-18 15:16:07 -0800465 try:
466 sel_in, sel_out, sel_err = \
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700467 select.select( self.sockets( ), [ ], self.sockets( ), 1 )
Dan Talayco710438c2010-02-18 15:16:07 -0800468 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700469 print sys.exc_info( )
470 self.logger.error( "Select error, disconnecting" )
471 self.disconnect( )
Dan Talayco1b3f6902010-02-15 14:14:19 -0800472
Dan Talayco710438c2010-02-18 15:16:07 -0800473 for s in sel_err:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700474 self.logger.error(
475 "Got socket error on: " + str( s ) + ", disconnecting" )
476 self.disconnect( )
Dan Talaycof8de5182012-04-12 22:38:41 -0700477
478 for s in sel_in:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700479 if self._socket_ready_handle( s ) == -1:
480 self.disconnect( )
Dan Talayco710438c2010-02-18 15:16:07 -0800481
Dan Talayco710438c2010-02-18 15:16:07 -0800482 # End of main loop
483 self.dbg_state = "closing"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700484 self.logger.info( "Exiting controller thread" )
485 self.shutdown( )
Dan Talayco710438c2010-02-18 15:16:07 -0800486
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700487 def connect( self, timeout=-1 ):
Dan Talayco710438c2010-02-18 15:16:07 -0800488 """
489 Connect to the switch
490
Rich Lane8806bc42012-07-26 19:18:37 -0700491 @param timeout Block for up to timeout seconds. Pass -1 for the default.
Dan Talayco710438c2010-02-18 15:16:07 -0800492 @return Boolean, True if connected
493 """
494
Dan Talayco69ca4d62012-11-15 11:50:22 -0800495 if not self.passive: # Do active connection now
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700496 self.logger.info( "Attempting to connect to %s on port %s" %
497 (self.switch, str( self.port )) )
498 soc = self.active_connect( )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800499 if soc:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700500 self.logger.info( "Connected to %s", self.switch )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800501 self.dbg_state = "running"
502 self.switch_socket = soc
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700503 self.wakeup( )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800504 with self.connect_cv:
505 if self.initial_hello:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700506 self.message_send( cfg_ofp.message.hello( ) )
507 self.connect_cv.notify( ) # Notify anyone waiting
Dan Talayco69ca4d62012-11-15 11:50:22 -0800508 else:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700509 self.logger.error( "Could not actively connect to switch %s",
510 self.switch )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800511 self.active = False
512 else:
513 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700514 ofutils.timed_wait( self.connect_cv, lambda: self.switch_socket,
515 timeout=timeout )
Dan Talayco69ca4d62012-11-15 11:50:22 -0800516
Dan Talayco710438c2010-02-18 15:16:07 -0800517 return self.switch_socket is not None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700518
519 def disconnect( self, timeout=-1 ):
Ken Chiangadc950f2012-10-05 13:50:03 -0700520 """
521 If connected to a switch, disconnect.
522 """
523 if self.switch_socket:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700524 self.switch_socket.close( )
Ken Chiangadc950f2012-10-05 13:50:03 -0700525 self.switch_socket = None
526 self.switch_addr = None
Ken Chiang74be4722012-12-21 13:07:03 -0800527 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700528 self.packets = [ ]
Ken Chiange875baf2012-10-09 15:24:40 -0700529 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700530 self.connect_cv.notifyAll( )
Ken Chiangadc950f2012-10-05 13:50:03 -0700531
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700532 def wait_disconnected( self, timeout=-1 ):
Ken Chiangadc950f2012-10-05 13:50:03 -0700533 """
534 @param timeout Block for up to timeout seconds. Pass -1 for the default.
535 @return Boolean, True if disconnected
536 """
537
Ken Chiange875baf2012-10-09 15:24:40 -0700538 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700539 ofutils.timed_wait( self.connect_cv,
540 lambda: True if not self.switch_socket else None,
541 timeout=timeout )
Ken Chiangadc950f2012-10-05 13:50:03 -0700542 return self.switch_socket is None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700543
544 def kill( self ):
Dan Talayco710438c2010-02-18 15:16:07 -0800545 """
546 Force the controller thread to quit
Dan Talayco710438c2010-02-18 15:16:07 -0800547 """
548 self.active = False
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700549 self.wakeup( )
550 self.join( )
Dan Talayco21c75c72010-02-12 22:59:24 -0800551
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700552 def shutdown( self ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800553 """
Dan Talayco1b3f6902010-02-15 14:14:19 -0800554 Shutdown the controller closing all sockets
Dan Talayco21c75c72010-02-12 22:59:24 -0800555
Dan Talayco1b3f6902010-02-15 14:14:19 -0800556 @todo Might want to synchronize shutdown with self.sync...
557 """
Dan Talaycof8de5182012-04-12 22:38:41 -0700558
Dan Talayco710438c2010-02-18 15:16:07 -0800559 self.active = False
560 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700561 self.switch_socket.shutdown( socket.SHUT_RDWR )
Dan Talayco710438c2010-02-18 15:16:07 -0800562 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700563 self.logger.info( "Ignoring switch soc shutdown error" )
Dan Talayco710438c2010-02-18 15:16:07 -0800564 self.switch_socket = None
Dan Talayco1b3f6902010-02-15 14:14:19 -0800565
Dan Talayco710438c2010-02-18 15:16:07 -0800566 try:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700567 self.listen_socket.shutdown( socket.SHUT_RDWR )
Dan Talayco710438c2010-02-18 15:16:07 -0800568 except:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700569 self.logger.info( "Ignoring listen soc shutdown error" )
Dan Talayco710438c2010-02-18 15:16:07 -0800570 self.listen_socket = None
Dan Talaycof8de5182012-04-12 22:38:41 -0700571
Rich Laneee3586c2012-07-11 17:26:02 -0700572 # Wakeup condition variables on which controller may be wait
573 with self.xid_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700574 self.xid_cv.notifyAll( )
Dan Talaycof8de5182012-04-12 22:38:41 -0700575
Rich Laneee3586c2012-07-11 17:26:02 -0700576 with self.connect_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700577 self.connect_cv.notifyAll( )
Dan Talaycof8de5182012-04-12 22:38:41 -0700578
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700579 self.wakeup( )
Dan Talayco710438c2010-02-18 15:16:07 -0800580 self.dbg_state = "down"
581
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700582 def register( self, msg_type, handler ):
Dan Talayco34089522010-02-07 23:07:41 -0800583 """
584 Register a callback to receive a specific message type.
585
586 Only one handler may be registered for a given message type.
Dan Talaycod12b6612010-03-07 22:00:46 -0800587
588 WARNING: A lock is held during the handler call back, so
589 the handler should not make any blocking calls
590
Dan Talayco34089522010-02-07 23:07:41 -0800591 @param msg_type The type of message to receive. May be DEFAULT
Dan Talayco21c75c72010-02-12 22:59:24 -0800592 for all non-handled packets. The special type, the string "all"
593 will send all packets to the handler.
Dan Talayco34089522010-02-07 23:07:41 -0800594 @param handler The function to call when a message of the given
595 type is received.
596 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800597 # Should check type is valid
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700598 if not handler and msg_type in self.handlers.keys( ):
599 del self.handlers[ msg_type ]
Dan Talayco21c75c72010-02-12 22:59:24 -0800600 return
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700601 self.handlers[ msg_type ] = handler
Dan Talayco34089522010-02-07 23:07:41 -0800602
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700603 def poll( self, exp_msg=None, timeout=-1 ):
Dan Talayco34089522010-02-07 23:07:41 -0800604 """
605 Wait for the next OF message received from the switch.
606
607 @param exp_msg If set, return only when this type of message
Dan Talayco48370102010-03-03 15:17:33 -0800608 is received (unless timeout occurs).
Rich Laneb64ce3d2012-07-26 15:37:57 -0700609
610 @param timeout Maximum number of seconds to wait for the message.
611 Pass -1 for the default timeout.
Dan Talayco34089522010-02-07 23:07:41 -0800612
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800613 @retval A pair (msg, pkt) where msg is a message object and pkt
614 the string representing the packet as received from the socket.
615 This allows additional parsing by the receiver if necessary.
616
Dan Talayco34089522010-02-07 23:07:41 -0800617 The data members in the message are in host endian order.
Dan Talayco48370102010-03-03 15:17:33 -0800618 If an error occurs, (None, None) is returned
Dan Talayco34089522010-02-07 23:07:41 -0800619 """
Dan Talayco34089522010-02-07 23:07:41 -0800620
Rich Lanee9d36912014-01-31 12:46:05 -0800621 if exp_msg is None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700622 self.logger.warn( "DEPRECATED polling for any message class" )
Rich Lanee9d36912014-01-31 12:46:05 -0800623 klass = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700624 elif isinstance( exp_msg, int ):
625 klass = cfg_ofp.message.message.subtypes[ exp_msg ]
626 elif issubclass( exp_msg, loxi.OFObject ):
Rich Lanee9d36912014-01-31 12:46:05 -0800627 klass = exp_msg
Ed Swierk9e55e282012-08-22 06:57:28 -0700628 else:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700629 raise ValueError( "Unexpected exp_msg argument %r" % exp_msg )
Rich Lanee9d36912014-01-31 12:46:05 -0800630
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700631 self.logger.debug( "Polling for %s", klass.__name__ )
Rich Laneb64ce3d2012-07-26 15:37:57 -0700632
633 # Take the packet from the queue
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700634 def grab( ):
635 for i, (msg, pkt) in enumerate( self.packets ):
636 if klass is None or isinstance( msg, klass ):
637 self.logger.debug( "Got %s message",
638 msg.__class__.__name__ )
639 return self.packets.pop( i )
Rich Lanec4f071b2012-07-11 17:25:57 -0700640 # Not found
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700641 self.logger.debug( "%s message not in queue", klass.__name__ )
Rich Laneb64ce3d2012-07-26 15:37:57 -0700642 return None
Dan Talayco21c75c72010-02-12 22:59:24 -0800643
Rich Lanec4f071b2012-07-11 17:25:57 -0700644 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700645 ret = ofutils.timed_wait( self.packets_cv, grab, timeout=timeout )
Rich Lanec4f071b2012-07-11 17:25:57 -0700646
Rich Laneb64ce3d2012-07-26 15:37:57 -0700647 if ret != None:
648 (msg, pkt) = ret
Rich Laneb64ce3d2012-07-26 15:37:57 -0700649 return (msg, pkt)
650 else:
651 return (None, None)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800652
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700653 def transact( self, msg, timeout=-1 ):
Dan Talayco34089522010-02-07 23:07:41 -0800654 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800655 Run a message transaction with the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800656
657 Send the message in msg and wait for a reply with a matching
Dan Talayco21c75c72010-02-12 22:59:24 -0800658 transaction id. Transactions have the highest priority in
659 received message handling.
Dan Talaycoe37999f2010-02-09 15:27:12 -0800660
Dan Talayco21c75c72010-02-12 22:59:24 -0800661 @param msg The message object to send; must not be a string
Rich Lanee1da7ea2012-07-26 15:58:45 -0700662 @param timeout The timeout in seconds; if -1 use default.
Dan Talayco34089522010-02-07 23:07:41 -0800663 """
Dan Talayco21c75c72010-02-12 22:59:24 -0800664
Rich Lane8fbfd662013-03-11 15:30:44 -0700665 if msg.xid == None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700666 msg.xid = ofutils.gen_xid( )
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800667
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700668 self.logger.debug( "Running transaction %d" % msg.xid )
Dan Talayco21c75c72010-02-12 22:59:24 -0800669
Rich Lane9aca1992012-07-11 17:26:31 -0700670 with self.xid_cv:
671 if self.xid:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700672 self.logger.error( "Can only run one transaction at a time" )
Rich Lane9aca1992012-07-11 17:26:31 -0700673 return (None, None)
Dan Talaycof8de5182012-04-12 22:38:41 -0700674
Rich Laneb73808c2013-03-11 15:22:23 -0700675 self.xid = msg.xid
Dan Talaycod12b6612010-03-07 22:00:46 -0800676 self.xid_response = None
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700677 self.message_send( msg )
Rich Lane9aca1992012-07-11 17:26:31 -0700678
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700679 self.logger.debug( "Waiting for transaction %d" % msg.xid )
680 ofutils.timed_wait( self.xid_cv, lambda: self.xid_response,
681 timeout=timeout )
Rich Lane9aca1992012-07-11 17:26:31 -0700682
683 if self.xid_response:
684 (resp, pkt) = self.xid_response
685 self.xid_response = None
686 else:
687 (resp, pkt) = (None, None)
688
Dan Talayco09c2c592010-05-13 14:21:52 -0700689 if resp is None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700690 self.logger.warning( "No response for xid " + str( self.xid ) )
Dan Talayco09c2c592010-05-13 14:21:52 -0700691 return (resp, pkt)
Dan Talayco34089522010-02-07 23:07:41 -0800692
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700693 def message_send( self, msg ):
Dan Talayco34089522010-02-07 23:07:41 -0800694 """
695 Send the message to the switch
Dan Talaycoe37999f2010-02-09 15:27:12 -0800696
Dan Talayco11c26e72010-03-07 22:03:57 -0800697 @param msg A string or OpenFlow message object to be forwarded to
698 the switch.
Dan Talayco21c75c72010-02-12 22:59:24 -0800699 """
700
Dan Talayco1b3f6902010-02-15 14:14:19 -0800701 if not self.switch_socket:
702 # Sending a string indicates the message is ready to go
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700703 raise Exception( "no socket" )
Dan Talayco21c75c72010-02-12 22:59:24 -0800704
Rich Lane1fd43e32014-01-06 15:22:50 -0800705 if msg.xid == None:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700706 msg.xid = ofutils.gen_xid( )
Rich Lane1fd43e32014-01-06 15:22:50 -0800707
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700708 outpkt = msg.pack( )
Rich Lane1fd43e32014-01-06 15:22:50 -0800709
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700710 self.logger.debug( "Msg out: version %d class %s len %d xid %d",
711 msg.version, type( msg ).__name__, len( outpkt ),
712 msg.xid )
Rich Lanec9d3edd2013-10-09 00:21:01 -0700713
714 with self.tx_lock:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700715 if self.switch_socket.sendall( outpkt ) is not None:
716 raise AssertionError( "failed to send message to switch" )
Dan Talayco710438c2010-02-18 15:16:07 -0800717
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700718 return 0 # for backwards compatibility
Dan Talayco21c75c72010-02-12 22:59:24 -0800719
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700720 def clear_queue( self ):
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700721 """
722 Clear the input queue and report the number of messages
723 that were in it
724 """
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700725 enqueued_pkt_count = len( self.packets )
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700726 with self.packets_cv:
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700727 self.packets = [ ]
Dan Talayco7071cf12013-04-16 11:02:13 -0700728 return enqueued_pkt_count
Dan Talaycodd6b6ff2013-04-12 08:20:18 -0700729
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700730 def __str__( self ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800731 string = "Controller:\n"
Dan Talaycod7e2dbe2010-02-13 21:51:15 -0800732 string += " state " + self.dbg_state + "\n"
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700733 string += " switch_addr " + str( self.switch_addr ) + "\n"
734 string += " pending pkts " + str( len( self.packets ) ) + "\n"
735 string += " total pkts " + str( self.packets_total ) + "\n"
736 string += " expired pkts " + str( self.packets_expired ) + "\n"
737 string += " handled pkts " + str( self.packets_handled ) + "\n"
738 string += " poll discards " + str( self.poll_discards ) + "\n"
739 string += " parse errors " + str( self.parse_errors ) + "\n"
740 string += " sock errrors " + str( self.socket_errors ) + "\n"
741 string += " max pkts " + str( self.max_pkts ) + "\n"
742 string += " target switch " + str( self.switch ) + "\n"
743 string += " host " + str( self.host ) + "\n"
744 string += " port " + str( self.port ) + "\n"
745 string += " keep_alive " + str( self.keep_alive ) + "\n"
746 string += " pkt_in_run " + str( self.pkt_in_run ) + "\n"
747 string += " pkt_in_dropped " + str( self.pkt_in_dropped ) + "\n"
Dan Talayco21c75c72010-02-12 22:59:24 -0800748 return string
749
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700750 def show( self ):
751 print str( self )
Dan Talayco21c75c72010-02-12 22:59:24 -0800752
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700753
754def sample_handler( controller, msg, pkt ):
Dan Talayco21c75c72010-02-12 22:59:24 -0800755 """
756 Sample message handler
757
758 This is the prototype for functions registered with the controller
759 class for packet reception
760
761 @param controller The controller calling the handler
762 @param msg The parsed message object
763 @param pkt The raw packet that was received on the socket. This is
764 in case the packet contains extra unparsed data.
765 @returns Boolean value indicating if the packet was handled. If
766 not handled, the packet is placed in the queue for pollers to received
767 """
768 pass