blob: 76aff75fe0900865bb3795323d4b838407b9255d [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
Rich Lanedb9d8662012-07-26 18:04:24 -070028from ofutils import *
Dan Talayco3087a462010-02-13 14:01:47 -080029
Rich Laneb42a31c2012-10-05 17:54:17 -070030have_pypcap = False
31try:
32 import pcap
Ed Swierkab0bab32012-11-30 13:31:00 -080033 if hasattr(pcap, "pcap"):
34 # the incompatible pylibpcap library masquerades as pcap
35 have_pypcap = True
Rich Laneb42a31c2012-10-05 17:54:17 -070036except:
37 pass
38
Dan Talayco48370102010-03-03 15:17:33 -080039##@todo Find a better home for these identifiers (dataplane)
40RCV_SIZE_DEFAULT = 4096
Dan Talayco3087a462010-02-13 14:01:47 -080041ETH_P_ALL = 0x03
42RCV_TIMEOUT = 10000
Dan Talayco3087a462010-02-13 14:01:47 -080043
Ed Swierk506614a2012-03-29 08:16:59 -070044def match_exp_pkt(exp_pkt, pkt):
45 """
46 Compare the string value of pkt with the string value of exp_pkt,
47 and return True iff they are identical. If the length of exp_pkt is
48 less than the minimum Ethernet frame size (60 bytes), then padding
49 bytes in pkt are ignored.
50 """
51 e = str(exp_pkt)
52 p = str(pkt)
53 if len(e) < 60:
54 p = p[:len(e)]
55 return e == p
56
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -070057
Dan Talayco3087a462010-02-13 14:01:47 -080058class DataPlanePort(Thread):
59 """
60 Class defining a port monitoring object.
61
62 Control a dataplane port connected to the switch under test.
63 Creates a promiscuous socket on a physical interface.
64 Queues the packets received on that interface with time stamps.
65 Inherits from Thread class as meant to run in background. Also
66 supports polling.
Dan Talaycoe226eb12010-02-18 23:06:30 -080067
68 Currently assumes a controlling 'parent' which maintains a
69 common Lock object and a total packet-pending count. May want
70 to decouple that some day.
Dan Talayco3087a462010-02-13 14:01:47 -080071 """
72
Dan Talaycoe226eb12010-02-18 23:06:30 -080073 def __init__(self, interface_name, port_number, parent, max_pkts=1024):
Dan Talayco3087a462010-02-13 14:01:47 -080074 """
75 Set up a port monitor object
76 @param interface_name The name of the physical interface like eth1
Dan Talayco4d065972010-02-18 23:11:32 -080077 @param port_number The port number associated with this port
Dan Talaycoe226eb12010-02-18 23:06:30 -080078 @param parent The controlling dataplane object; for pkt wait CV
Dan Talayco3087a462010-02-13 14:01:47 -080079 @param max_pkts Maximum number of pkts to keep in queue
80 """
81 Thread.__init__(self)
82 self.interface_name = interface_name
83 self.max_pkts = max_pkts
Dan Talayco3087a462010-02-13 14:01:47 -080084 self.packets_total = 0
85 self.packets = []
Dan Talayco1b3f6902010-02-15 14:14:19 -080086 self.packets_discarded = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -080087 self.port_number = port_number
Dan Talayco48370102010-03-03 15:17:33 -080088 logname = "dp-" + interface_name
89 self.logger = logging.getLogger(logname)
Dan Talayco0db53eb2010-03-10 14:00:02 -080090 try:
91 self.socket = self.interface_open(interface_name)
92 except:
93 self.logger.info("Could not open socket")
Rich Laneb42a31c2012-10-05 17:54:17 -070094 raise
95 self.logger.info("Opened port monitor (class %s)", type(self).__name__)
Dan Talaycoe226eb12010-02-18 23:06:30 -080096 self.parent = parent
Dan Talayco1b3f6902010-02-15 14:14:19 -080097
Dan Talayco3087a462010-02-13 14:01:47 -080098 def interface_open(self, interface_name):
99 """
100 Open a socket in a promiscuous mode for a data connection.
101 @param interface_name port name as a string such as 'eth1'
102 @retval s socket
103 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800104 s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
Dan Talayco3087a462010-02-13 14:01:47 -0800105 socket.htons(ETH_P_ALL))
106 s.bind((interface_name, 0))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800107 netutils.set_promisc(s, interface_name)
Dan Talayco3087a462010-02-13 14:01:47 -0800108 s.settimeout(RCV_TIMEOUT)
109 return s
110
Dan Talayco3087a462010-02-13 14:01:47 -0800111 def run(self):
112 """
113 Activity function for class
114 """
115 self.running = True
Dan Talayco710438c2010-02-18 15:16:07 -0800116 self.socs = [self.socket]
117 error_warned = False # Have we warned about error?
Dan Talayco3087a462010-02-13 14:01:47 -0800118 while self.running:
119 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800120 sel_in, sel_out, sel_err = \
121 select.select(self.socs, [], [], 1)
122 except:
123 print sys.exc_info()
Dan Talayco48370102010-03-03 15:17:33 -0800124 self.logger.error("Select error, exiting")
125 break
Dan Talayco710438c2010-02-18 15:16:07 -0800126
127 if not self.running:
128 break
129
Dan Talayco48370102010-03-03 15:17:33 -0800130 if (sel_in is None) or (len(sel_in) == 0):
Dan Talayco710438c2010-02-18 15:16:07 -0800131 continue
132
133 try:
Dan Talayco48370102010-03-03 15:17:33 -0800134 rcvmsg = self.socket.recv(RCV_SIZE_DEFAULT)
Dan Talayco3087a462010-02-13 14:01:47 -0800135 except socket.error:
Dan Talayco710438c2010-02-18 15:16:07 -0800136 if not error_warned:
Dan Talayco48370102010-03-03 15:17:33 -0800137 self.logger.info("Socket error on recv")
Dan Talayco710438c2010-02-18 15:16:07 -0800138 error_warned = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800139 continue
Dan Talayco710438c2010-02-18 15:16:07 -0800140
Dan Talayco1b3f6902010-02-15 14:14:19 -0800141 if len(rcvmsg) == 0:
Dan Talayco48370102010-03-03 15:17:33 -0800142 self.logger.info("Zero len pkt rcvd")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800143 self.kill()
Dan Talayco3087a462010-02-13 14:01:47 -0800144 break
145
Rich Laned2e93aa2012-12-05 17:55:46 -0800146 rcvtime = time.time()
Dan Talayco48370102010-03-03 15:17:33 -0800147 self.logger.debug("Pkt len " + str(len(rcvmsg)) +
Ed Swierk4e200302012-03-19 14:53:31 -0700148 " in at " + str(rcvtime) + " on port " +
149 str(self.port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800150
Dan Talaycoe226eb12010-02-18 23:06:30 -0800151 # Enqueue packet
Rich Lanedb9d8662012-07-26 18:04:24 -0700152 with self.parent.pkt_sync:
153 if len(self.packets) >= self.max_pkts:
154 # Queue full, throw away oldest
155 self.packets.pop(0)
156 self.packets_discarded += 1
157 self.logger.debug("Discarding oldest packet to make room")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700158 self.packets.append((rcvmsg, rcvtime))
159 self.packets_total += 1
Rich Lanedb9d8662012-07-26 18:04:24 -0700160 self.parent.pkt_sync.notify_all()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800161
Rich Lanedb9d8662012-07-26 18:04:24 -0700162 self.logger.info("Thread exit")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800163
164 def kill(self):
165 """
166 Terminate the running thread
167 """
Dan Talayco48370102010-03-03 15:17:33 -0800168 self.logger.debug("Port monitor kill")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800169 self.running = False
170 try:
171 self.socket.close()
172 except:
Dan Talayco48370102010-03-03 15:17:33 -0800173 self.logger.info("Ignoring dataplane soc shutdown error")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800174
Dan Talayco3087a462010-02-13 14:01:47 -0800175 def timestamp_head(self):
176 """
177 Return the timestamp of the head of queue or None if empty
178 """
Dan Talayco710438c2010-02-18 15:16:07 -0800179 rv = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800180 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800181 rv = self.packets[0][1]
Dan Talaycoe226eb12010-02-18 23:06:30 -0800182 except:
183 rv = None
Dan Talayco710438c2010-02-18 15:16:07 -0800184 return rv
Dan Talayco3087a462010-02-13 14:01:47 -0800185
186 def flush(self):
187 """
188 Clear the packet queue
189 """
Rich Lanedb9d8662012-07-26 18:04:24 -0700190 with self.parent.pkt_sync:
191 self.packets_discarded += len(self.packets)
192 self.packets = []
Dan Talayco3087a462010-02-13 14:01:47 -0800193
194 def send(self, packet):
195 """
196 Send a packet to the dataplane port
197 @param packet The packet data to send to the port
198 @retval The number of bytes sent
199 """
200 return self.socket.send(packet)
201
Dan Talayco3087a462010-02-13 14:01:47 -0800202 def register(self, handler):
203 """
204 Register a callback function to receive packets from this
Dan Talaycoe226eb12010-02-18 23:06:30 -0800205 port. The callback will be passed the packet, the
206 interface name and the port number (if set) on which the
Dan Talayco3087a462010-02-13 14:01:47 -0800207 packet was received.
208
209 To be implemented
210 """
211 pass
212
Dan Talayco1b3f6902010-02-15 14:14:19 -0800213 def show(self, prefix=''):
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500214
Dan Talayco1b3f6902010-02-15 14:14:19 -0800215 print prefix + "Name: " + self.interface_name
Dan Talayco710438c2010-02-18 15:16:07 -0800216 print prefix + "Pkts pending: " + str(len(self.packets))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800217 print prefix + "Pkts total: " + str(self.packets_total)
218 print prefix + "socket: " + str(self.socket)
Dan Talaycoe226eb12010-02-18 23:06:30 -0800219
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500220
221 def port_down(self,port_number,config):
222
223 """
224 Grabs a port from the dataplane ports and brings it down by
225 shutting the corresponding interface
226 @port_number The port number which has brought to be down
227 @interface_name The interface corresponding to the port that needs to
228 be brought down
229
230 """
231 interface_name = config["port_map"].get(port_number)
232 cmd = 'ifdown '+ interface_name
233 os.system(cmd)
234
235 def port_up(self,port_number,config):
236
237 """
238 Grabs a port from the dataplane ports and brings it up by
239 starting up the corresponding interface
240 @port_number The port number which has to brought up
241 @interface_name The interface corresponding to the port that has to
242 be brought up
243
244 """
245 interface_name = config["port_map"].get(port_number)
246 cmd = 'ifup '+ interface_name
247 os.system(cmd)
248
249
Rich Laneb42a31c2012-10-05 17:54:17 -0700250class DataPlanePortPcap(DataPlanePort):
251 """
252 Alternate port implementation using libpcap. This is required for recent
253 versions of Linux (such as Linux 3.2 included in Ubuntu 12.04) which
254 offload the VLAN tag, so it isn't in the data returned from a read on a raw
255 socket. libpcap understands how to read the VLAN tag from the kernel.
256 """
257
258 def __init__(self, interface_name, port_number, parent, max_pkts=1024):
259 DataPlanePort.__init__(self, interface_name, port_number, parent, max_pkts)
260
261 def interface_open(self, interface_name):
262 """
263 Open a PCAP interface.
264 """
265 self.pcap = pcap.pcap(interface_name)
266 self.pcap.setnonblock()
267 return self.pcap.fileno()
268
269 def run(self):
270 """
271 Activity function for class
272 """
273 self.running = True
274 while self.running:
275 try:
276 sel_in, sel_out, sel_err = select.select([self.socket], [], [], 1)
277 except:
278 print sys.exc_info()
279 self.logger.error("Select error, exiting")
280 break
281
282 if not self.running:
283 break
284
285 if (sel_in is None) or (len(sel_in) == 0):
286 continue
287
288 # Enqueue packet
289 with self.parent.pkt_sync:
290 for (timestamp, rcvmsg) in self.pcap.readpkts():
Rich Laneb42a31c2012-10-05 17:54:17 -0700291 self.logger.debug("Pkt len " + str(len(rcvmsg)) +
Rich Laned2e93aa2012-12-05 17:55:46 -0800292 " in at " + str(timestamp) + " on port " +
Rich Laneb42a31c2012-10-05 17:54:17 -0700293 str(self.port_number))
294
295 if len(self.packets) >= self.max_pkts:
296 # Queue full, throw away oldest
297 self.packets.pop(0)
298 self.packets_discarded += 1
299 self.logger.debug("Discarding oldest packet to make room")
Rich Laned2e93aa2012-12-05 17:55:46 -0800300 self.packets.append((rcvmsg, timestamp))
Rich Laneb42a31c2012-10-05 17:54:17 -0700301 self.packets_total += 1
302 self.parent.pkt_sync.notify_all()
303
304 self.logger.info("Thread exit")
305
306 def kill(self):
307 """
308 Terminate the running thread
309 """
310 self.logger.debug("Port monitor kill")
311 self.running = False
312 # pcap object is closed on GC.
313
314 def send(self, packet):
315 """
316 Send a packet to the dataplane port
317 @param packet The packet data to send to the port
318 @retval The number of bytes sent
319 """
320 return self.pcap.inject(packet, len(packet))
Dan Talayco34089522010-02-07 23:07:41 -0800321
322class DataPlane:
323 """
324 Class defining access primitives to the data plane
325 Controls a list of DataPlanePort objects
326 """
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700327 def __init__(self, config=None):
Dan Talayco34089522010-02-07 23:07:41 -0800328 self.port_list = {}
Dan Talaycoe226eb12010-02-18 23:06:30 -0800329 # pkt_sync serves double duty as a regular top level lock and
330 # as a condition variable
331 self.pkt_sync = Condition()
332
333 # These are used to signal async pkt arrival for polling
334 self.want_pkt = False
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700335 self.exp_pkt = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800336 self.want_pkt_port = None # What port required (or None)
337 self.got_pkt_port = None # On what port received?
338 self.packets_pending = 0 # Total pkts in all port queues
Dan Talayco48370102010-03-03 15:17:33 -0800339 self.logger = logging.getLogger("dataplane")
Dan Talayco34089522010-02-07 23:07:41 -0800340
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700341 if config is None:
342 self.config = {}
343 else:
344 self.config = config;
345
346 ############################################################
347 #
348 # We use the DataPlanePort class defined here by
349 # default for all port traffic:
350 #
Rich Laneb42a31c2012-10-05 17:54:17 -0700351 if have_pypcap:
352 self.dppclass = DataPlanePortPcap
353 else:
354 self.logger.warning("Missing pypcap, VLAN tests may fail. See README for installation instructions.")
355 self.dppclass = DataPlanePort
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700356
357 ############################################################
358 #
359 # The platform/config can provide a custom DataPlanePort class
360 # here if you have a custom implementation with different
361 # behavior.
362 #
363 # Set config.dataplane.portclass = MyDataPlanePortClass
364 # where MyDataPlanePortClass has the same interface as the class
365 # DataPlanePort defined here.
366 #
367 if "dataplane" in self.config:
368 if "portclass" in self.config["dataplane"]:
369 self.dppclass = self.config["dataplane"]["portclass"]
370
371 if self.dppclass == None:
372 raise Exception("Problem determining DataPlanePort class.")
373
374
Dan Talayco34089522010-02-07 23:07:41 -0800375 def port_add(self, interface_name, port_number):
376 """
377 Add a port to the dataplane
378 TBD: Max packets for queue?
379 @param interface_name The name of the physical interface like eth1
380 @param port_number The port number used to refer to the port
381 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800382
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700383 self.port_list[port_number] = self.dppclass(interface_name,
384 port_number, self);
385
Dan Talayco34089522010-02-07 23:07:41 -0800386 self.port_list[port_number].start()
387
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700388
389
Dan Talayco34089522010-02-07 23:07:41 -0800390 def send(self, port_number, packet):
391 """
392 Send a packet to the given port
393 @param port_number The port to send the data to
394 @param packet Raw packet data to send to port
395 """
Dan Talayco11c26e72010-03-07 22:03:57 -0800396 self.logger.debug("Sending %d bytes to port %d" %
397 (len(packet), port_number))
Dan Talayco34089522010-02-07 23:07:41 -0800398 bytes = self.port_list[port_number].send(packet)
399 if bytes != len(packet):
Dan Talayco48370102010-03-03 15:17:33 -0800400 self.logger.error("Unhandled send error, length mismatch %d != %d" %
Dan Talayco1b3f6902010-02-15 14:14:19 -0800401 (bytes, len(packet)))
Dan Talayco34089522010-02-07 23:07:41 -0800402 return bytes
403
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500404
405
406
Dan Talayco34089522010-02-07 23:07:41 -0800407 def flood(self, packet):
408 """
409 Send a packet to all ports
410 @param packet Raw packet data to send to port
411 """
412 for port_number in self.port_list.keys():
413 bytes = self.port_list[port_number].send(packet)
414 if bytes != len(packet):
Dan Talayco48370102010-03-03 15:17:33 -0800415 self.logger.error("Unhandled send error" +
Dan Talayco1b3f6902010-02-15 14:14:19 -0800416 ", port %d, length mismatch %d != %d" %
417 (port_number, bytes, len(packet)))
Dan Talayco34089522010-02-07 23:07:41 -0800418
Rich Lanedb9d8662012-07-26 18:04:24 -0700419 # Returns the port with the oldest packet, or None if no packets are queued.
420 def oldest_port(self):
421 min_port = None
422 min_time = float('inf')
423 for port in self.port_list.values():
424 ptime = port.timestamp_head()
425 if ptime and ptime < min_time:
426 min_time = ptime
427 min_port = port
428 return min_port
429
430 # Dequeues and yields packets in the order they were received.
431 # Yields (port, packet, received time).
432 # If port_number is not specified yields packets from all ports.
433 def packets(self, port_number=None):
434 while True:
435 if port_number == None:
436 port = self.oldest_port()
437 else:
438 port = self.port_list[port_number]
439
440 if port == None or len(port.packets) == 0:
441 self.logger.debug("Out of packets for port %s" % str(port_number))
442 # Out of packets
443 break
444
445 pkt, time = port.packets.pop(0)
446 yield (port, pkt, time)
447
Rich Lane8806bc42012-07-26 19:18:37 -0700448 def poll(self, port_number=None, timeout=-1, exp_pkt=None):
Dan Talaycoe226eb12010-02-18 23:06:30 -0800449 """
450 Poll one or all dataplane ports for a packet
451
452 If port_number is given, get the oldest packet from that port.
453 Otherwise, find the port with the oldest packet and return
454 that packet.
Dan Talayco1729fdb2012-05-03 09:35:56 -0700455
456 If exp_pkt is true, discard all packets until that one is found
457
Dan Talaycoe226eb12010-02-18 23:06:30 -0800458 @param port_number If set, get packet from this port
Dan Talayco11c26e72010-03-07 22:03:57 -0800459 @param timeout If positive and no packet is available, block
Dan Talaycoe226eb12010-02-18 23:06:30 -0800460 until a packet is received or for this many seconds
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700461 @param exp_pkt If not None, look for this packet and ignore any
Dan Talayco1729fdb2012-05-03 09:35:56 -0700462 others received. Note that if port_number is None, all packets
463 from all ports will be discarded until the exp_pkt is found
Dan Talaycoe226eb12010-02-18 23:06:30 -0800464 @return The triple port_number, packet, pkt_time where packet
465 is received from port_number at time pkt_time. If a timeout
466 occurs, return None, None, None
467 """
468
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700469 if exp_pkt and not port_number:
Dan Talayco1729fdb2012-05-03 09:35:56 -0700470 self.logger.warn("Dataplane poll with exp_pkt but no port number")
471
Rich Lanedb9d8662012-07-26 18:04:24 -0700472 # Retrieve the packet. Returns (port number, packet, time).
473 def grab():
474 self.logger.debug("Grabbing packet")
475 for (port, pkt, time) in self.packets(port_number):
476 self.logger.debug("Checking packet from port %d" % port.port_number)
Dan Talayco1729fdb2012-05-03 09:35:56 -0700477 if not exp_pkt or match_exp_pkt(exp_pkt, pkt):
Rich Lanedb9d8662012-07-26 18:04:24 -0700478 return (port, pkt, time)
479 self.logger.debug("Did not find packet")
480 return None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800481
Rich Lanedb9d8662012-07-26 18:04:24 -0700482 with self.pkt_sync:
483 ret = timed_wait(self.pkt_sync, grab, timeout=timeout)
Dan Talayco34089522010-02-07 23:07:41 -0800484
Rich Lanedb9d8662012-07-26 18:04:24 -0700485 if ret != None:
486 (port, pkt, time) = ret
487 return (port.port_number, pkt, time)
488 else:
489 self.logger.debug("Poll time out, no packet from " + str(port_number))
490 return (None, None, None)
Dan Talayco34089522010-02-07 23:07:41 -0800491
Dan Talayco48370102010-03-03 15:17:33 -0800492 def kill(self, join_threads=True):
Dan Talayco1b3f6902010-02-15 14:14:19 -0800493 """
494 Close all sockets for dataplane
Dan Talayco710438c2010-02-18 15:16:07 -0800495 @param join_threads If True call join on each thread
Dan Talayco1b3f6902010-02-15 14:14:19 -0800496 """
Dan Talayco34089522010-02-07 23:07:41 -0800497 for port_number in self.port_list.keys():
498 self.port_list[port_number].kill()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800499 if join_threads:
Dan Talayco48370102010-03-03 15:17:33 -0800500 self.logger.debug("Joining " + str(port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800501 self.port_list[port_number].join()
Dan Talayco34089522010-02-07 23:07:41 -0800502
Dan Talayco48370102010-03-03 15:17:33 -0800503 self.logger.info("DataPlane shutdown")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800504
505 def show(self, prefix=''):
506 print prefix + "Dataplane Controller"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800507 print prefix + "Packets pending" + str(self.packets_pending)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800508 for pnum, port in self.port_list.items():
509 print prefix + "OpenFlow Port Number " + str(pnum)
510 port.show(prefix + ' ')
511
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500512
513 def port_down(self,port_number):
514 """Brings the specified port down"""
515 self.port_list[port_number].port_down(port_number,self.config)
516
517
518 def port_up(self,port_number):
519 """Brings the specified port up"""
520 self.port_list[port_number].port_up(port_number,self.config)