blob: 4611aa2c950e1d2f22903f13f9368770b93439fa [file] [log] [blame]
Dan Talayco34089522010-02-07 23:07:41 -08001"""
2OpenFlow Test Framework
3
Dan Talayco3087a462010-02-13 14:01:47 -08004DataPlane and DataPlanePort classes
Dan Talayco34089522010-02-07 23:07:41 -08005
6Provide the interface to the control the set of ports being used
7to stimulate the switch under test.
8
9See the class dataplaneport for more details. This class wraps
Dan Talaycoe226eb12010-02-18 23:06:30 -080010a set of those objects allowing general calls and parsing
Dan Talayco34089522010-02-07 23:07:41 -080011configuration.
12
Dan Talayco3087a462010-02-13 14:01:47 -080013@todo Add "filters" for matching packets. Actions supported
14for filters should include a callback or a counter
Dan Talayco34089522010-02-07 23:07:41 -080015"""
16
Dan Talayco3087a462010-02-13 14:01:47 -080017import sys
18import os
19import socket
20import time
Dan Talaycod7e2dbe2010-02-13 21:51:15 -080021import netutils
Dan Talayco3087a462010-02-13 14:01:47 -080022from threading import Thread
23from threading import Lock
Dan Talaycoe226eb12010-02-18 23:06:30 -080024from threading import Condition
Dan Talayco710438c2010-02-18 15:16:07 -080025import select
Dan Talayco48370102010-03-03 15:17:33 -080026import logging
27from oft_assert import oft_assert
Dan Talayco3087a462010-02-13 14:01:47 -080028
Dan Talayco48370102010-03-03 15:17:33 -080029##@todo Find a better home for these identifiers (dataplane)
30RCV_SIZE_DEFAULT = 4096
Dan Talayco3087a462010-02-13 14:01:47 -080031ETH_P_ALL = 0x03
32RCV_TIMEOUT = 10000
Dan Talayco3087a462010-02-13 14:01:47 -080033
Ed Swierk506614a2012-03-29 08:16:59 -070034def match_exp_pkt(exp_pkt, pkt):
35 """
36 Compare the string value of pkt with the string value of exp_pkt,
37 and return True iff they are identical. If the length of exp_pkt is
38 less than the minimum Ethernet frame size (60 bytes), then padding
39 bytes in pkt are ignored.
40 """
41 e = str(exp_pkt)
42 p = str(pkt)
43 if len(e) < 60:
44 p = p[:len(e)]
45 return e == p
46
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -070047
Dan Talayco3087a462010-02-13 14:01:47 -080048class DataPlanePort(Thread):
49 """
50 Class defining a port monitoring object.
51
52 Control a dataplane port connected to the switch under test.
53 Creates a promiscuous socket on a physical interface.
54 Queues the packets received on that interface with time stamps.
55 Inherits from Thread class as meant to run in background. Also
56 supports polling.
57 Use accessors to dequeue packets for proper synchronization.
Dan Talaycoe226eb12010-02-18 23:06:30 -080058
59 Currently assumes a controlling 'parent' which maintains a
60 common Lock object and a total packet-pending count. May want
61 to decouple that some day.
Dan Talayco3087a462010-02-13 14:01:47 -080062 """
63
Dan Talaycoe226eb12010-02-18 23:06:30 -080064 def __init__(self, interface_name, port_number, parent, max_pkts=1024):
Dan Talayco3087a462010-02-13 14:01:47 -080065 """
66 Set up a port monitor object
67 @param interface_name The name of the physical interface like eth1
Dan Talayco4d065972010-02-18 23:11:32 -080068 @param port_number The port number associated with this port
Dan Talaycoe226eb12010-02-18 23:06:30 -080069 @param parent The controlling dataplane object; for pkt wait CV
Dan Talayco3087a462010-02-13 14:01:47 -080070 @param max_pkts Maximum number of pkts to keep in queue
71 """
72 Thread.__init__(self)
73 self.interface_name = interface_name
74 self.max_pkts = max_pkts
Dan Talayco3087a462010-02-13 14:01:47 -080075 self.packets_total = 0
76 self.packets = []
Dan Talayco1b3f6902010-02-15 14:14:19 -080077 self.packets_discarded = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -080078 self.port_number = port_number
Dan Talayco48370102010-03-03 15:17:33 -080079 logname = "dp-" + interface_name
80 self.logger = logging.getLogger(logname)
Dan Talayco0db53eb2010-03-10 14:00:02 -080081 try:
82 self.socket = self.interface_open(interface_name)
83 except:
84 self.logger.info("Could not open socket")
85 sys.exit(1)
Dan Talayco48370102010-03-03 15:17:33 -080086 self.logger.info("Openned port monitor socket")
Dan Talaycoe226eb12010-02-18 23:06:30 -080087 self.parent = parent
88 self.pkt_sync = self.parent.pkt_sync
Dan Talayco1b3f6902010-02-15 14:14:19 -080089
Dan Talayco3087a462010-02-13 14:01:47 -080090 def interface_open(self, interface_name):
91 """
92 Open a socket in a promiscuous mode for a data connection.
93 @param interface_name port name as a string such as 'eth1'
94 @retval s socket
95 """
Dan Talaycoe226eb12010-02-18 23:06:30 -080096 s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
Dan Talayco3087a462010-02-13 14:01:47 -080097 socket.htons(ETH_P_ALL))
98 s.bind((interface_name, 0))
Dan Talayco1b3f6902010-02-15 14:14:19 -080099 netutils.set_promisc(s, interface_name)
Dan Talayco3087a462010-02-13 14:01:47 -0800100 s.settimeout(RCV_TIMEOUT)
101 return s
102
Dan Talayco3087a462010-02-13 14:01:47 -0800103 def run(self):
104 """
105 Activity function for class
106 """
107 self.running = True
Dan Talayco710438c2010-02-18 15:16:07 -0800108 self.socs = [self.socket]
109 error_warned = False # Have we warned about error?
Dan Talayco3087a462010-02-13 14:01:47 -0800110 while self.running:
111 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800112 sel_in, sel_out, sel_err = \
113 select.select(self.socs, [], [], 1)
114 except:
115 print sys.exc_info()
Dan Talayco48370102010-03-03 15:17:33 -0800116 self.logger.error("Select error, exiting")
117 break
Dan Talayco710438c2010-02-18 15:16:07 -0800118
119 if not self.running:
120 break
121
Dan Talayco48370102010-03-03 15:17:33 -0800122 if (sel_in is None) or (len(sel_in) == 0):
Dan Talayco710438c2010-02-18 15:16:07 -0800123 continue
124
125 try:
Dan Talayco48370102010-03-03 15:17:33 -0800126 rcvmsg = self.socket.recv(RCV_SIZE_DEFAULT)
Dan Talayco3087a462010-02-13 14:01:47 -0800127 except socket.error:
Dan Talayco710438c2010-02-18 15:16:07 -0800128 if not error_warned:
Dan Talayco48370102010-03-03 15:17:33 -0800129 self.logger.info("Socket error on recv")
Dan Talayco710438c2010-02-18 15:16:07 -0800130 error_warned = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800131 continue
Dan Talayco710438c2010-02-18 15:16:07 -0800132
Dan Talayco1b3f6902010-02-15 14:14:19 -0800133 if len(rcvmsg) == 0:
Dan Talayco48370102010-03-03 15:17:33 -0800134 self.logger.info("Zero len pkt rcvd")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800135 self.kill()
Dan Talayco3087a462010-02-13 14:01:47 -0800136 break
137
Dan Talayco1b3f6902010-02-15 14:14:19 -0800138 rcvtime = time.clock()
Dan Talayco48370102010-03-03 15:17:33 -0800139 self.logger.debug("Pkt len " + str(len(rcvmsg)) +
Ed Swierk4e200302012-03-19 14:53:31 -0700140 " in at " + str(rcvtime) + " on port " +
141 str(self.port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800142
Dan Talaycoe226eb12010-02-18 23:06:30 -0800143 # Enqueue packet
144 self.pkt_sync.acquire()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800145 if len(self.packets) >= self.max_pkts:
Dan Talaycoe226eb12010-02-18 23:06:30 -0800146 # Queue full, throw away oldest
Dan Talayco1b3f6902010-02-15 14:14:19 -0800147 self.packets.pop(0)
148 self.packets_discarded += 1
Dan Talaycoe226eb12010-02-18 23:06:30 -0800149 else:
150 self.parent.packets_pending += 1
Dan Talayco48370102010-03-03 15:17:33 -0800151 # Check if parent is waiting on this (or any) port
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700152 drop_pkt = False
Dan Talayco48370102010-03-03 15:17:33 -0800153 if self.parent.want_pkt:
154 if (not self.parent.want_pkt_port or
Dan Talaycoe226eb12010-02-18 23:06:30 -0800155 self.parent.want_pkt_port == self.port_number):
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700156 if self.parent.exp_pkt:
Ed Swierk506614a2012-03-29 08:16:59 -0700157 if not match_exp_pkt(self.parent.exp_pkt, rcvmsg):
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700158 drop_pkt = True
159 if not drop_pkt:
160 self.parent.got_pkt_port = self.port_number
161 self.parent.want_pkt = False
162 self.parent.pkt_sync.notify()
163 if not drop_pkt:
164 self.packets.append((rcvmsg, rcvtime))
165 self.packets_total += 1
Dan Talaycoe226eb12010-02-18 23:06:30 -0800166 self.pkt_sync.release()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800167
Dan Talayco48370102010-03-03 15:17:33 -0800168 self.logger.info("Thread exit ")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800169
170 def kill(self):
171 """
172 Terminate the running thread
173 """
Dan Talayco48370102010-03-03 15:17:33 -0800174 self.logger.debug("Port monitor kill")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800175 self.running = False
176 try:
177 self.socket.close()
178 except:
Dan Talayco48370102010-03-03 15:17:33 -0800179 self.logger.info("Ignoring dataplane soc shutdown error")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800180
Dan Talaycoe226eb12010-02-18 23:06:30 -0800181 def dequeue(self, use_lock=True):
Dan Talayco3087a462010-02-13 14:01:47 -0800182 """
183 Get the oldest packet in the queue
Dan Talaycoe226eb12010-02-18 23:06:30 -0800184 @param use_lock If True, acquires the packet sync lock (which is
185 really the parent's lock)
186 @return The pair packet, packet time-stamp
Dan Talayco3087a462010-02-13 14:01:47 -0800187 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800188 if use_lock:
189 self.pkt_sync.acquire()
190 if len(self.packets) > 0:
191 pkt, pkt_time = self.packets.pop(0)
192 self.parent.packets_pending -= 1
193 else:
194 pkt = pkt_time = None
195 if use_lock:
196 self.pkt_sync.release()
Dan Talayco3087a462010-02-13 14:01:47 -0800197 return pkt, pkt_time
198
199 def timestamp_head(self):
200 """
201 Return the timestamp of the head of queue or None if empty
202 """
Dan Talayco710438c2010-02-18 15:16:07 -0800203 rv = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800204 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800205 rv = self.packets[0][1]
Dan Talaycoe226eb12010-02-18 23:06:30 -0800206 except:
207 rv = None
Dan Talayco710438c2010-02-18 15:16:07 -0800208 return rv
Dan Talayco3087a462010-02-13 14:01:47 -0800209
210 def flush(self):
211 """
212 Clear the packet queue
213 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800214 self.pkt_sync.acquire()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800215 self.packets_discarded += len(self.packets)
Dan Talaycoe226eb12010-02-18 23:06:30 -0800216 self.parent.packets_pending -= len(self.packets)
Dan Talayco3087a462010-02-13 14:01:47 -0800217 self.packets = []
218 self.packet_times = []
Dan Talaycoe226eb12010-02-18 23:06:30 -0800219 self.pkt_sync.release()
Dan Talayco3087a462010-02-13 14:01:47 -0800220
221
222 def send(self, packet):
223 """
224 Send a packet to the dataplane port
225 @param packet The packet data to send to the port
226 @retval The number of bytes sent
227 """
228 return self.socket.send(packet)
229
230
231 def register(self, handler):
232 """
233 Register a callback function to receive packets from this
Dan Talaycoe226eb12010-02-18 23:06:30 -0800234 port. The callback will be passed the packet, the
235 interface name and the port number (if set) on which the
Dan Talayco3087a462010-02-13 14:01:47 -0800236 packet was received.
237
238 To be implemented
239 """
240 pass
241
Dan Talayco1b3f6902010-02-15 14:14:19 -0800242 def show(self, prefix=''):
243 print prefix + "Name: " + self.interface_name
Dan Talayco710438c2010-02-18 15:16:07 -0800244 print prefix + "Pkts pending: " + str(len(self.packets))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800245 print prefix + "Pkts total: " + str(self.packets_total)
246 print prefix + "socket: " + str(self.socket)
Dan Talaycoe226eb12010-02-18 23:06:30 -0800247
Dan Talayco34089522010-02-07 23:07:41 -0800248
249class DataPlane:
250 """
251 Class defining access primitives to the data plane
252 Controls a list of DataPlanePort objects
253 """
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700254 def __init__(self, config=None):
Dan Talayco34089522010-02-07 23:07:41 -0800255 self.port_list = {}
Dan Talaycoe226eb12010-02-18 23:06:30 -0800256 # pkt_sync serves double duty as a regular top level lock and
257 # as a condition variable
258 self.pkt_sync = Condition()
259
260 # These are used to signal async pkt arrival for polling
261 self.want_pkt = False
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700262 self.exp_pkt = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800263 self.want_pkt_port = None # What port required (or None)
264 self.got_pkt_port = None # On what port received?
265 self.packets_pending = 0 # Total pkts in all port queues
Dan Talayco48370102010-03-03 15:17:33 -0800266 self.logger = logging.getLogger("dataplane")
Dan Talayco34089522010-02-07 23:07:41 -0800267
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700268 if config is None:
269 self.config = {}
270 else:
271 self.config = config;
272
273 ############################################################
274 #
275 # We use the DataPlanePort class defined here by
276 # default for all port traffic:
277 #
278 self.dppclass = DataPlanePort
279
280 ############################################################
281 #
282 # The platform/config can provide a custom DataPlanePort class
283 # here if you have a custom implementation with different
284 # behavior.
285 #
286 # Set config.dataplane.portclass = MyDataPlanePortClass
287 # where MyDataPlanePortClass has the same interface as the class
288 # DataPlanePort defined here.
289 #
290 if "dataplane" in self.config:
291 if "portclass" in self.config["dataplane"]:
292 self.dppclass = self.config["dataplane"]["portclass"]
293
294 if self.dppclass == None:
295 raise Exception("Problem determining DataPlanePort class.")
296
297
Dan Talayco34089522010-02-07 23:07:41 -0800298 def port_add(self, interface_name, port_number):
299 """
300 Add a port to the dataplane
301 TBD: Max packets for queue?
302 @param interface_name The name of the physical interface like eth1
303 @param port_number The port number used to refer to the port
304 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800305
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700306 self.port_list[port_number] = self.dppclass(interface_name,
307 port_number, self);
308
Dan Talayco34089522010-02-07 23:07:41 -0800309 self.port_list[port_number].start()
310
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700311
312
Dan Talayco34089522010-02-07 23:07:41 -0800313 def send(self, port_number, packet):
314 """
315 Send a packet to the given port
316 @param port_number The port to send the data to
317 @param packet Raw packet data to send to port
318 """
Dan Talayco11c26e72010-03-07 22:03:57 -0800319 self.logger.debug("Sending %d bytes to port %d" %
320 (len(packet), port_number))
Dan Talayco34089522010-02-07 23:07:41 -0800321 bytes = self.port_list[port_number].send(packet)
322 if bytes != len(packet):
Dan Talayco48370102010-03-03 15:17:33 -0800323 self.logger.error("Unhandled send error, length mismatch %d != %d" %
Dan Talayco1b3f6902010-02-15 14:14:19 -0800324 (bytes, len(packet)))
Dan Talayco34089522010-02-07 23:07:41 -0800325 return bytes
326
327 def flood(self, packet):
328 """
329 Send a packet to all ports
330 @param packet Raw packet data to send to port
331 """
332 for port_number in self.port_list.keys():
333 bytes = self.port_list[port_number].send(packet)
334 if bytes != len(packet):
Dan Talayco48370102010-03-03 15:17:33 -0800335 self.logger.error("Unhandled send error" +
Dan Talayco1b3f6902010-02-15 14:14:19 -0800336 ", port %d, length mismatch %d != %d" %
337 (port_number, bytes, len(packet)))
Dan Talayco34089522010-02-07 23:07:41 -0800338
Dan Talaycoe226eb12010-02-18 23:06:30 -0800339 def _oldest_packet_find(self):
Dan Talayco34089522010-02-07 23:07:41 -0800340 # Find port with oldest packet
341 min_time = 0
342 min_port = -1
343 for port_number in self.port_list.keys():
344 ptime = self.port_list[port_number].timestamp_head()
345 if ptime:
346 if (min_port == -1) or (ptime < min_time):
Dan Talaycoe226eb12010-02-18 23:06:30 -0800347 min_time = ptime
Dan Talayco34089522010-02-07 23:07:41 -0800348 min_port = port_number
Dan Talaycoe226eb12010-02-18 23:06:30 -0800349 oft_assert(min_port != -1, "Could not find port when pkts pending")
Dan Talayco34089522010-02-07 23:07:41 -0800350
Dan Talaycoe226eb12010-02-18 23:06:30 -0800351 return min_port
352
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700353 def poll(self, port_number=None, timeout=None, exp_pkt=None):
Dan Talaycoe226eb12010-02-18 23:06:30 -0800354 """
355 Poll one or all dataplane ports for a packet
356
357 If port_number is given, get the oldest packet from that port.
358 Otherwise, find the port with the oldest packet and return
359 that packet.
Dan Talayco1729fdb2012-05-03 09:35:56 -0700360
361 If exp_pkt is true, discard all packets until that one is found
362
Dan Talaycoe226eb12010-02-18 23:06:30 -0800363 @param port_number If set, get packet from this port
Dan Talayco11c26e72010-03-07 22:03:57 -0800364 @param timeout If positive and no packet is available, block
Dan Talaycoe226eb12010-02-18 23:06:30 -0800365 until a packet is received or for this many seconds
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700366 @param exp_pkt If not None, look for this packet and ignore any
Dan Talayco1729fdb2012-05-03 09:35:56 -0700367 others received. Note that if port_number is None, all packets
368 from all ports will be discarded until the exp_pkt is found
Dan Talaycoe226eb12010-02-18 23:06:30 -0800369 @return The triple port_number, packet, pkt_time where packet
370 is received from port_number at time pkt_time. If a timeout
371 occurs, return None, None, None
372 """
373
Dan Talaycoe226eb12010-02-18 23:06:30 -0800374
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700375 if exp_pkt and not port_number:
Dan Talayco1729fdb2012-05-03 09:35:56 -0700376 self.logger.warn("Dataplane poll with exp_pkt but no port number")
377
378 self.pkt_sync.acquire()
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700379
Dan Talaycoe226eb12010-02-18 23:06:30 -0800380 # Check if requested specific port and it has a packet
381 if port_number and len(self.port_list[port_number].packets) != 0:
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700382 while len(self.port_list[port_number].packets) != 0:
383 pkt, time = self.port_list[port_number].dequeue(use_lock=False)
384 if not exp_pkt:
385 break
Ed Swierk506614a2012-03-29 08:16:59 -0700386 if match_exp_pkt(exp_pkt, pkt):
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700387 break
388 pkt = None # Discard silently
389 if pkt:
390 self.pkt_sync.release()
391 oft_assert(pkt, "Poll: packet not found on port " +
392 str(port_number))
393 return port_number, pkt, time
Dan Talaycoe226eb12010-02-18 23:06:30 -0800394
395 # Check if requested any port and some packet pending
Dan Talayco1729fdb2012-05-03 09:35:56 -0700396 if not port_number:
397 while self.packets_pending != 0:
Ken Chiang3978f242012-06-13 14:14:09 -0700398 port = self._oldest_packet_find()
Dan Talayco1729fdb2012-05-03 09:35:56 -0700399 pkt, time = self.port_list[port].dequeue(use_lock=False)
400 self.pkt_sync.release()
401 oft_assert(pkt, "Poll: oldest packet not found")
402 if not exp_pkt or match_exp_pkt(exp_pkt, pkt):
403 return port, pkt, time
Dan Talaycoe226eb12010-02-18 23:06:30 -0800404
405 # No packet pending; blocking call requested?
406 if not timeout:
407 self.pkt_sync.release()
Dan Talayco34089522010-02-07 23:07:41 -0800408 return None, None, None
409
Dan Talaycoe226eb12010-02-18 23:06:30 -0800410 # Desired packet isn't available and timeout is specified
411 # Already holding pkt_sync; wait on pkt_sync variable
412 self.want_pkt = True
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700413 self.exp_pkt = exp_pkt
Dan Talaycoe226eb12010-02-18 23:06:30 -0800414 self.want_pkt_port = port_number
415 self.got_pkt_port = None
416 self.pkt_sync.wait(timeout)
417 self.want_pkt = False
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700418 self.exp_pkt = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800419 if self.got_pkt_port:
420 pkt, time = \
421 self.port_list[self.got_pkt_port].dequeue(use_lock=False)
422 self.pkt_sync.release()
423 oft_assert(pkt, "Poll: pkt reported, but not found at " +
Dan Talayco48370102010-03-03 15:17:33 -0800424 str(self.got_pkt_port))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800425 return self.got_pkt_port, pkt, time
426
427 self.pkt_sync.release()
Dan Talaycoa99c21a2010-05-07 09:23:34 -0700428 self.logger.debug("Poll time out, no packet from " + str(port_number))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800429
430 return None, None, None
Dan Talayco34089522010-02-07 23:07:41 -0800431
Dan Talayco48370102010-03-03 15:17:33 -0800432 def kill(self, join_threads=True):
Dan Talayco1b3f6902010-02-15 14:14:19 -0800433 """
434 Close all sockets for dataplane
Dan Talayco710438c2010-02-18 15:16:07 -0800435 @param join_threads If True call join on each thread
Dan Talayco1b3f6902010-02-15 14:14:19 -0800436 """
Dan Talayco34089522010-02-07 23:07:41 -0800437 for port_number in self.port_list.keys():
438 self.port_list[port_number].kill()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800439 if join_threads:
Dan Talayco48370102010-03-03 15:17:33 -0800440 self.logger.debug("Joining " + str(port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800441 self.port_list[port_number].join()
Dan Talayco34089522010-02-07 23:07:41 -0800442
Dan Talayco48370102010-03-03 15:17:33 -0800443 self.logger.info("DataPlane shutdown")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800444
445 def show(self, prefix=''):
446 print prefix + "Dataplane Controller"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800447 print prefix + "Packets pending" + str(self.packets_pending)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800448 for pnum, port in self.port_list.items():
449 print prefix + "OpenFlow Port Number " + str(pnum)
450 port.show(prefix + ' ')
451