blob: 6da897979a7effc4204e800618fc316d703a5740 [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
Rich Laned7d32922012-12-24 15:03:20 -080097 self.killed = False
Dan Talayco1b3f6902010-02-15 14:14:19 -080098
Rich Lane5b5da2d2012-12-22 19:56:32 -080099 # Used to wake up the event loop in kill()
100 self.waker = EventDescriptor()
101
Dan Talayco3087a462010-02-13 14:01:47 -0800102 def interface_open(self, interface_name):
103 """
104 Open a socket in a promiscuous mode for a data connection.
105 @param interface_name port name as a string such as 'eth1'
106 @retval s socket
107 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800108 s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
Dan Talayco3087a462010-02-13 14:01:47 -0800109 socket.htons(ETH_P_ALL))
110 s.bind((interface_name, 0))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800111 netutils.set_promisc(s, interface_name)
Dan Talayco3087a462010-02-13 14:01:47 -0800112 s.settimeout(RCV_TIMEOUT)
113 return s
114
Dan Talayco3087a462010-02-13 14:01:47 -0800115 def run(self):
116 """
117 Activity function for class
118 """
Rich Lane5b5da2d2012-12-22 19:56:32 -0800119 self.socs = [self.socket, self.waker]
Dan Talayco710438c2010-02-18 15:16:07 -0800120 error_warned = False # Have we warned about error?
Rich Laned7d32922012-12-24 15:03:20 -0800121 while not self.killed:
Dan Talayco3087a462010-02-13 14:01:47 -0800122 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800123 sel_in, sel_out, sel_err = \
124 select.select(self.socs, [], [], 1)
125 except:
126 print sys.exc_info()
Dan Talayco48370102010-03-03 15:17:33 -0800127 self.logger.error("Select error, exiting")
128 break
Dan Talayco710438c2010-02-18 15:16:07 -0800129
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
Rich Lane5b5da2d2012-12-22 19:56:32 -0800133 if self.waker in sel_in:
134 self.waker.wait()
135 continue
136
Dan Talayco710438c2010-02-18 15:16:07 -0800137 try:
Dan Talayco48370102010-03-03 15:17:33 -0800138 rcvmsg = self.socket.recv(RCV_SIZE_DEFAULT)
Dan Talayco3087a462010-02-13 14:01:47 -0800139 except socket.error:
Dan Talayco710438c2010-02-18 15:16:07 -0800140 if not error_warned:
Dan Talayco48370102010-03-03 15:17:33 -0800141 self.logger.info("Socket error on recv")
Dan Talayco710438c2010-02-18 15:16:07 -0800142 error_warned = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800143 continue
Dan Talayco710438c2010-02-18 15:16:07 -0800144
Dan Talayco1b3f6902010-02-15 14:14:19 -0800145 if len(rcvmsg) == 0:
Dan Talayco48370102010-03-03 15:17:33 -0800146 self.logger.info("Zero len pkt rcvd")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800147 self.kill()
Dan Talayco3087a462010-02-13 14:01:47 -0800148 break
149
Rich Laned2e93aa2012-12-05 17:55:46 -0800150 rcvtime = time.time()
Dan Talayco48370102010-03-03 15:17:33 -0800151 self.logger.debug("Pkt len " + str(len(rcvmsg)) +
Ed Swierk4e200302012-03-19 14:53:31 -0700152 " in at " + str(rcvtime) + " on port " +
153 str(self.port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800154
Dan Talaycoe226eb12010-02-18 23:06:30 -0800155 # Enqueue packet
Rich Lanedb9d8662012-07-26 18:04:24 -0700156 with self.parent.pkt_sync:
157 if len(self.packets) >= self.max_pkts:
158 # Queue full, throw away oldest
159 self.packets.pop(0)
160 self.packets_discarded += 1
161 self.logger.debug("Discarding oldest packet to make room")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700162 self.packets.append((rcvmsg, rcvtime))
163 self.packets_total += 1
Rich Lanedb9d8662012-07-26 18:04:24 -0700164 self.parent.pkt_sync.notify_all()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800165
Rich Lanedb9d8662012-07-26 18:04:24 -0700166 self.logger.info("Thread exit")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800167
168 def kill(self):
169 """
170 Terminate the running thread
171 """
Dan Talayco48370102010-03-03 15:17:33 -0800172 self.logger.debug("Port monitor kill")
Rich Laned7d32922012-12-24 15:03:20 -0800173 self.killed = True
Rich Lane5b5da2d2012-12-22 19:56:32 -0800174 self.waker.notify()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800175 try:
176 self.socket.close()
177 except:
Dan Talayco48370102010-03-03 15:17:33 -0800178 self.logger.info("Ignoring dataplane soc shutdown error")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800179
Dan Talayco3087a462010-02-13 14:01:47 -0800180 def timestamp_head(self):
181 """
182 Return the timestamp of the head of queue or None if empty
183 """
Dan Talayco710438c2010-02-18 15:16:07 -0800184 rv = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800185 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800186 rv = self.packets[0][1]
Dan Talaycoe226eb12010-02-18 23:06:30 -0800187 except:
188 rv = None
Dan Talayco710438c2010-02-18 15:16:07 -0800189 return rv
Dan Talayco3087a462010-02-13 14:01:47 -0800190
191 def flush(self):
192 """
193 Clear the packet queue
194 """
Rich Lanedb9d8662012-07-26 18:04:24 -0700195 with self.parent.pkt_sync:
196 self.packets_discarded += len(self.packets)
197 self.packets = []
Dan Talayco3087a462010-02-13 14:01:47 -0800198
199 def send(self, packet):
200 """
201 Send a packet to the dataplane port
202 @param packet The packet data to send to the port
203 @retval The number of bytes sent
204 """
205 return self.socket.send(packet)
206
Dan Talayco3087a462010-02-13 14:01:47 -0800207 def register(self, handler):
208 """
209 Register a callback function to receive packets from this
Dan Talaycoe226eb12010-02-18 23:06:30 -0800210 port. The callback will be passed the packet, the
211 interface name and the port number (if set) on which the
Dan Talayco3087a462010-02-13 14:01:47 -0800212 packet was received.
213
214 To be implemented
215 """
216 pass
217
Dan Talayco1b3f6902010-02-15 14:14:19 -0800218 def show(self, prefix=''):
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500219
Dan Talayco1b3f6902010-02-15 14:14:19 -0800220 print prefix + "Name: " + self.interface_name
Dan Talayco710438c2010-02-18 15:16:07 -0800221 print prefix + "Pkts pending: " + str(len(self.packets))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800222 print prefix + "Pkts total: " + str(self.packets_total)
223 print prefix + "socket: " + str(self.socket)
Dan Talaycoe226eb12010-02-18 23:06:30 -0800224
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500225
226 def port_down(self,port_number,config):
227
228 """
229 Grabs a port from the dataplane ports and brings it down by
230 shutting the corresponding interface
231 @port_number The port number which has brought to be down
232 @interface_name The interface corresponding to the port that needs to
233 be brought down
234
235 """
236 interface_name = config["port_map"].get(port_number)
237 cmd = 'ifdown '+ interface_name
238 os.system(cmd)
239
240 def port_up(self,port_number,config):
241
242 """
243 Grabs a port from the dataplane ports and brings it up by
244 starting up the corresponding interface
245 @port_number The port number which has to brought up
246 @interface_name The interface corresponding to the port that has to
247 be brought up
248
249 """
250 interface_name = config["port_map"].get(port_number)
251 cmd = 'ifup '+ interface_name
252 os.system(cmd)
253
254
Rich Laneb42a31c2012-10-05 17:54:17 -0700255class DataPlanePortPcap(DataPlanePort):
256 """
257 Alternate port implementation using libpcap. This is required for recent
258 versions of Linux (such as Linux 3.2 included in Ubuntu 12.04) which
259 offload the VLAN tag, so it isn't in the data returned from a read on a raw
260 socket. libpcap understands how to read the VLAN tag from the kernel.
261 """
262
263 def __init__(self, interface_name, port_number, parent, max_pkts=1024):
264 DataPlanePort.__init__(self, interface_name, port_number, parent, max_pkts)
265
266 def interface_open(self, interface_name):
267 """
268 Open a PCAP interface.
269 """
270 self.pcap = pcap.pcap(interface_name)
271 self.pcap.setnonblock()
272 return self.pcap.fileno()
273
274 def run(self):
275 """
276 Activity function for class
277 """
Rich Laned7d32922012-12-24 15:03:20 -0800278 while not self.killed:
Rich Laneb42a31c2012-10-05 17:54:17 -0700279 try:
Rich Lane5b5da2d2012-12-22 19:56:32 -0800280 sel_in, sel_out, sel_err = select.select([self.socket, self.waker], [], [], 1)
Rich Laneb42a31c2012-10-05 17:54:17 -0700281 except:
282 print sys.exc_info()
283 self.logger.error("Select error, exiting")
284 break
285
Rich Laneb42a31c2012-10-05 17:54:17 -0700286 if (sel_in is None) or (len(sel_in) == 0):
287 continue
288
Rich Lane5b5da2d2012-12-22 19:56:32 -0800289 if self.waker in sel_in:
290 self.waker.wait()
291 continue
292
Rich Laneb42a31c2012-10-05 17:54:17 -0700293 # Enqueue packet
294 with self.parent.pkt_sync:
295 for (timestamp, rcvmsg) in self.pcap.readpkts():
Rich Laneb42a31c2012-10-05 17:54:17 -0700296 self.logger.debug("Pkt len " + str(len(rcvmsg)) +
Rich Laned2e93aa2012-12-05 17:55:46 -0800297 " in at " + str(timestamp) + " on port " +
Rich Laneb42a31c2012-10-05 17:54:17 -0700298 str(self.port_number))
299
300 if len(self.packets) >= self.max_pkts:
301 # Queue full, throw away oldest
302 self.packets.pop(0)
303 self.packets_discarded += 1
304 self.logger.debug("Discarding oldest packet to make room")
Rich Laned2e93aa2012-12-05 17:55:46 -0800305 self.packets.append((rcvmsg, timestamp))
Rich Laneb42a31c2012-10-05 17:54:17 -0700306 self.packets_total += 1
307 self.parent.pkt_sync.notify_all()
308
309 self.logger.info("Thread exit")
310
311 def kill(self):
312 """
313 Terminate the running thread
314 """
315 self.logger.debug("Port monitor kill")
Rich Laned7d32922012-12-24 15:03:20 -0800316 self.killed = True
Rich Lane5b5da2d2012-12-22 19:56:32 -0800317 self.waker.notify()
Rich Laneb42a31c2012-10-05 17:54:17 -0700318 # pcap object is closed on GC.
319
320 def send(self, packet):
321 """
322 Send a packet to the dataplane port
323 @param packet The packet data to send to the port
324 @retval The number of bytes sent
325 """
326 return self.pcap.inject(packet, len(packet))
Dan Talayco34089522010-02-07 23:07:41 -0800327
328class DataPlane:
329 """
330 Class defining access primitives to the data plane
331 Controls a list of DataPlanePort objects
332 """
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700333 def __init__(self, config=None):
Dan Talayco34089522010-02-07 23:07:41 -0800334 self.port_list = {}
Dan Talaycoe226eb12010-02-18 23:06:30 -0800335 # pkt_sync serves double duty as a regular top level lock and
336 # as a condition variable
337 self.pkt_sync = Condition()
338
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 #
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700348 # The platform/config can provide a custom DataPlanePort class
349 # here if you have a custom implementation with different
350 # behavior.
351 #
352 # Set config.dataplane.portclass = MyDataPlanePortClass
353 # where MyDataPlanePortClass has the same interface as the class
354 # DataPlanePort defined here.
355 #
Rich Lanefb718302012-12-26 21:02:31 -0800356 if "dataplane" in self.config and "portclass" in self.config["dataplane"]:
357 self.dppclass = self.config["dataplane"]["portclass"]
358 elif have_pypcap:
359 self.dppclass = DataPlanePortPcap
360 else:
361 self.logger.warning("Missing pypcap, VLAN tests may fail. See README for installation instructions.")
362 self.dppclass = DataPlanePort
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700363
Dan Talayco34089522010-02-07 23:07:41 -0800364 def port_add(self, interface_name, port_number):
365 """
366 Add a port to the dataplane
367 TBD: Max packets for queue?
368 @param interface_name The name of the physical interface like eth1
369 @param port_number The port number used to refer to the port
370 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800371
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700372 self.port_list[port_number] = self.dppclass(interface_name,
373 port_number, self);
374
Dan Talayco34089522010-02-07 23:07:41 -0800375 self.port_list[port_number].start()
376
377 def send(self, port_number, packet):
378 """
379 Send a packet to the given port
380 @param port_number The port to send the data to
381 @param packet Raw packet data to send to port
382 """
Dan Talayco11c26e72010-03-07 22:03:57 -0800383 self.logger.debug("Sending %d bytes to port %d" %
384 (len(packet), port_number))
Dan Talayco34089522010-02-07 23:07:41 -0800385 bytes = self.port_list[port_number].send(packet)
386 if bytes != len(packet):
Dan Talayco48370102010-03-03 15:17:33 -0800387 self.logger.error("Unhandled send error, length mismatch %d != %d" %
Dan Talayco1b3f6902010-02-15 14:14:19 -0800388 (bytes, len(packet)))
Dan Talayco34089522010-02-07 23:07:41 -0800389 return bytes
390
Rich Lanedb9d8662012-07-26 18:04:24 -0700391 # Returns the port with the oldest packet, or None if no packets are queued.
392 def oldest_port(self):
393 min_port = None
394 min_time = float('inf')
395 for port in self.port_list.values():
396 ptime = port.timestamp_head()
397 if ptime and ptime < min_time:
398 min_time = ptime
399 min_port = port
400 return min_port
401
402 # Dequeues and yields packets in the order they were received.
403 # Yields (port, packet, received time).
404 # If port_number is not specified yields packets from all ports.
405 def packets(self, port_number=None):
406 while True:
407 if port_number == None:
408 port = self.oldest_port()
409 else:
410 port = self.port_list[port_number]
411
412 if port == None or len(port.packets) == 0:
413 self.logger.debug("Out of packets for port %s" % str(port_number))
414 # Out of packets
415 break
416
417 pkt, time = port.packets.pop(0)
418 yield (port, pkt, time)
419
Rich Lane8806bc42012-07-26 19:18:37 -0700420 def poll(self, port_number=None, timeout=-1, exp_pkt=None):
Dan Talaycoe226eb12010-02-18 23:06:30 -0800421 """
422 Poll one or all dataplane ports for a packet
423
424 If port_number is given, get the oldest packet from that port.
425 Otherwise, find the port with the oldest packet and return
426 that packet.
Dan Talayco1729fdb2012-05-03 09:35:56 -0700427
428 If exp_pkt is true, discard all packets until that one is found
429
Dan Talaycoe226eb12010-02-18 23:06:30 -0800430 @param port_number If set, get packet from this port
Dan Talayco11c26e72010-03-07 22:03:57 -0800431 @param timeout If positive and no packet is available, block
Dan Talaycoe226eb12010-02-18 23:06:30 -0800432 until a packet is received or for this many seconds
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700433 @param exp_pkt If not None, look for this packet and ignore any
Dan Talayco1729fdb2012-05-03 09:35:56 -0700434 others received. Note that if port_number is None, all packets
435 from all ports will be discarded until the exp_pkt is found
Dan Talaycoe226eb12010-02-18 23:06:30 -0800436 @return The triple port_number, packet, pkt_time where packet
437 is received from port_number at time pkt_time. If a timeout
438 occurs, return None, None, None
439 """
440
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700441 if exp_pkt and not port_number:
Dan Talayco1729fdb2012-05-03 09:35:56 -0700442 self.logger.warn("Dataplane poll with exp_pkt but no port number")
443
Rich Lanedb9d8662012-07-26 18:04:24 -0700444 # Retrieve the packet. Returns (port number, packet, time).
445 def grab():
446 self.logger.debug("Grabbing packet")
447 for (port, pkt, time) in self.packets(port_number):
448 self.logger.debug("Checking packet from port %d" % port.port_number)
Dan Talayco1729fdb2012-05-03 09:35:56 -0700449 if not exp_pkt or match_exp_pkt(exp_pkt, pkt):
Rich Lanedb9d8662012-07-26 18:04:24 -0700450 return (port, pkt, time)
451 self.logger.debug("Did not find packet")
452 return None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800453
Rich Lanedb9d8662012-07-26 18:04:24 -0700454 with self.pkt_sync:
455 ret = timed_wait(self.pkt_sync, grab, timeout=timeout)
Dan Talayco34089522010-02-07 23:07:41 -0800456
Rich Lanedb9d8662012-07-26 18:04:24 -0700457 if ret != None:
458 (port, pkt, time) = ret
459 return (port.port_number, pkt, time)
460 else:
461 self.logger.debug("Poll time out, no packet from " + str(port_number))
462 return (None, None, None)
Dan Talayco34089522010-02-07 23:07:41 -0800463
Dan Talayco48370102010-03-03 15:17:33 -0800464 def kill(self, join_threads=True):
Dan Talayco1b3f6902010-02-15 14:14:19 -0800465 """
466 Close all sockets for dataplane
Dan Talayco710438c2010-02-18 15:16:07 -0800467 @param join_threads If True call join on each thread
Dan Talayco1b3f6902010-02-15 14:14:19 -0800468 """
Dan Talayco34089522010-02-07 23:07:41 -0800469 for port_number in self.port_list.keys():
470 self.port_list[port_number].kill()
Rich Lane4d46dbd2012-12-22 19:26:19 -0800471
472 if join_threads:
473 for port_number in self.port_list.keys():
Dan Talayco48370102010-03-03 15:17:33 -0800474 self.logger.debug("Joining " + str(port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800475 self.port_list[port_number].join()
Dan Talayco34089522010-02-07 23:07:41 -0800476
Rich Lanee7b0ecb2012-12-26 10:01:01 -0800477 self.port_list = None
478
Dan Talayco48370102010-03-03 15:17:33 -0800479 self.logger.info("DataPlane shutdown")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800480
481 def show(self, prefix=''):
482 print prefix + "Dataplane Controller"
483 for pnum, port in self.port_list.items():
484 print prefix + "OpenFlow Port Number " + str(pnum)
485 port.show(prefix + ' ')
486
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500487 def port_down(self,port_number):
488 """Brings the specified port down"""
489 self.port_list[port_number].port_down(port_number,self.config)
490
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500491 def port_up(self,port_number):
492 """Brings the specified port up"""
493 self.port_list[port_number].port_up(port_number,self.config)