blob: 717a85f795a1d8642267c8b5e1f92f5bc572caee [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
33 have_pypcap = True
34except:
35 pass
36
Dan Talayco48370102010-03-03 15:17:33 -080037##@todo Find a better home for these identifiers (dataplane)
38RCV_SIZE_DEFAULT = 4096
Dan Talayco3087a462010-02-13 14:01:47 -080039ETH_P_ALL = 0x03
40RCV_TIMEOUT = 10000
Dan Talayco3087a462010-02-13 14:01:47 -080041
Ed Swierk506614a2012-03-29 08:16:59 -070042def match_exp_pkt(exp_pkt, pkt):
43 """
44 Compare the string value of pkt with the string value of exp_pkt,
45 and return True iff they are identical. If the length of exp_pkt is
46 less than the minimum Ethernet frame size (60 bytes), then padding
47 bytes in pkt are ignored.
48 """
49 e = str(exp_pkt)
50 p = str(pkt)
51 if len(e) < 60:
52 p = p[:len(e)]
53 return e == p
54
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -070055
Dan Talayco3087a462010-02-13 14:01:47 -080056class DataPlanePort(Thread):
57 """
58 Class defining a port monitoring object.
59
60 Control a dataplane port connected to the switch under test.
61 Creates a promiscuous socket on a physical interface.
62 Queues the packets received on that interface with time stamps.
63 Inherits from Thread class as meant to run in background. Also
64 supports polling.
Dan Talaycoe226eb12010-02-18 23:06:30 -080065
66 Currently assumes a controlling 'parent' which maintains a
67 common Lock object and a total packet-pending count. May want
68 to decouple that some day.
Dan Talayco3087a462010-02-13 14:01:47 -080069 """
70
Dan Talaycoe226eb12010-02-18 23:06:30 -080071 def __init__(self, interface_name, port_number, parent, max_pkts=1024):
Dan Talayco3087a462010-02-13 14:01:47 -080072 """
73 Set up a port monitor object
74 @param interface_name The name of the physical interface like eth1
Dan Talayco4d065972010-02-18 23:11:32 -080075 @param port_number The port number associated with this port
Dan Talaycoe226eb12010-02-18 23:06:30 -080076 @param parent The controlling dataplane object; for pkt wait CV
Dan Talayco3087a462010-02-13 14:01:47 -080077 @param max_pkts Maximum number of pkts to keep in queue
78 """
79 Thread.__init__(self)
80 self.interface_name = interface_name
81 self.max_pkts = max_pkts
Dan Talayco3087a462010-02-13 14:01:47 -080082 self.packets_total = 0
83 self.packets = []
Dan Talayco1b3f6902010-02-15 14:14:19 -080084 self.packets_discarded = 0
Dan Talaycoe226eb12010-02-18 23:06:30 -080085 self.port_number = port_number
Dan Talayco48370102010-03-03 15:17:33 -080086 logname = "dp-" + interface_name
87 self.logger = logging.getLogger(logname)
Dan Talayco0db53eb2010-03-10 14:00:02 -080088 try:
89 self.socket = self.interface_open(interface_name)
90 except:
91 self.logger.info("Could not open socket")
Rich Laneb42a31c2012-10-05 17:54:17 -070092 raise
93 self.logger.info("Opened port monitor (class %s)", type(self).__name__)
Dan Talaycoe226eb12010-02-18 23:06:30 -080094 self.parent = parent
Dan Talayco1b3f6902010-02-15 14:14:19 -080095
Dan Talayco3087a462010-02-13 14:01:47 -080096 def interface_open(self, interface_name):
97 """
98 Open a socket in a promiscuous mode for a data connection.
99 @param interface_name port name as a string such as 'eth1'
100 @retval s socket
101 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800102 s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
Dan Talayco3087a462010-02-13 14:01:47 -0800103 socket.htons(ETH_P_ALL))
104 s.bind((interface_name, 0))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800105 netutils.set_promisc(s, interface_name)
Dan Talayco3087a462010-02-13 14:01:47 -0800106 s.settimeout(RCV_TIMEOUT)
107 return s
108
Dan Talayco3087a462010-02-13 14:01:47 -0800109 def run(self):
110 """
111 Activity function for class
112 """
113 self.running = True
Dan Talayco710438c2010-02-18 15:16:07 -0800114 self.socs = [self.socket]
115 error_warned = False # Have we warned about error?
Dan Talayco3087a462010-02-13 14:01:47 -0800116 while self.running:
117 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800118 sel_in, sel_out, sel_err = \
119 select.select(self.socs, [], [], 1)
120 except:
121 print sys.exc_info()
Dan Talayco48370102010-03-03 15:17:33 -0800122 self.logger.error("Select error, exiting")
123 break
Dan Talayco710438c2010-02-18 15:16:07 -0800124
125 if not self.running:
126 break
127
Dan Talayco48370102010-03-03 15:17:33 -0800128 if (sel_in is None) or (len(sel_in) == 0):
Dan Talayco710438c2010-02-18 15:16:07 -0800129 continue
130
131 try:
Dan Talayco48370102010-03-03 15:17:33 -0800132 rcvmsg = self.socket.recv(RCV_SIZE_DEFAULT)
Dan Talayco3087a462010-02-13 14:01:47 -0800133 except socket.error:
Dan Talayco710438c2010-02-18 15:16:07 -0800134 if not error_warned:
Dan Talayco48370102010-03-03 15:17:33 -0800135 self.logger.info("Socket error on recv")
Dan Talayco710438c2010-02-18 15:16:07 -0800136 error_warned = True
Dan Talayco1b3f6902010-02-15 14:14:19 -0800137 continue
Dan Talayco710438c2010-02-18 15:16:07 -0800138
Dan Talayco1b3f6902010-02-15 14:14:19 -0800139 if len(rcvmsg) == 0:
Dan Talayco48370102010-03-03 15:17:33 -0800140 self.logger.info("Zero len pkt rcvd")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800141 self.kill()
Dan Talayco3087a462010-02-13 14:01:47 -0800142 break
143
Rich Laned2e93aa2012-12-05 17:55:46 -0800144 rcvtime = time.time()
Dan Talayco48370102010-03-03 15:17:33 -0800145 self.logger.debug("Pkt len " + str(len(rcvmsg)) +
Ed Swierk4e200302012-03-19 14:53:31 -0700146 " in at " + str(rcvtime) + " on port " +
147 str(self.port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800148
Dan Talaycoe226eb12010-02-18 23:06:30 -0800149 # Enqueue packet
Rich Lanedb9d8662012-07-26 18:04:24 -0700150 with self.parent.pkt_sync:
151 if len(self.packets) >= self.max_pkts:
152 # Queue full, throw away oldest
153 self.packets.pop(0)
154 self.packets_discarded += 1
155 self.logger.debug("Discarding oldest packet to make room")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700156 self.packets.append((rcvmsg, rcvtime))
157 self.packets_total += 1
Rich Lanedb9d8662012-07-26 18:04:24 -0700158 self.parent.pkt_sync.notify_all()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800159
Rich Lanedb9d8662012-07-26 18:04:24 -0700160 self.logger.info("Thread exit")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800161
162 def kill(self):
163 """
164 Terminate the running thread
165 """
Dan Talayco48370102010-03-03 15:17:33 -0800166 self.logger.debug("Port monitor kill")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800167 self.running = False
168 try:
169 self.socket.close()
170 except:
Dan Talayco48370102010-03-03 15:17:33 -0800171 self.logger.info("Ignoring dataplane soc shutdown error")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800172
Dan Talayco3087a462010-02-13 14:01:47 -0800173 def timestamp_head(self):
174 """
175 Return the timestamp of the head of queue or None if empty
176 """
Dan Talayco710438c2010-02-18 15:16:07 -0800177 rv = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800178 try:
Dan Talayco710438c2010-02-18 15:16:07 -0800179 rv = self.packets[0][1]
Dan Talaycoe226eb12010-02-18 23:06:30 -0800180 except:
181 rv = None
Dan Talayco710438c2010-02-18 15:16:07 -0800182 return rv
Dan Talayco3087a462010-02-13 14:01:47 -0800183
184 def flush(self):
185 """
186 Clear the packet queue
187 """
Rich Lanedb9d8662012-07-26 18:04:24 -0700188 with self.parent.pkt_sync:
189 self.packets_discarded += len(self.packets)
190 self.packets = []
Dan Talayco3087a462010-02-13 14:01:47 -0800191
192 def send(self, packet):
193 """
194 Send a packet to the dataplane port
195 @param packet The packet data to send to the port
196 @retval The number of bytes sent
197 """
198 return self.socket.send(packet)
199
Dan Talayco3087a462010-02-13 14:01:47 -0800200 def register(self, handler):
201 """
202 Register a callback function to receive packets from this
Dan Talaycoe226eb12010-02-18 23:06:30 -0800203 port. The callback will be passed the packet, the
204 interface name and the port number (if set) on which the
Dan Talayco3087a462010-02-13 14:01:47 -0800205 packet was received.
206
207 To be implemented
208 """
209 pass
210
Dan Talayco1b3f6902010-02-15 14:14:19 -0800211 def show(self, prefix=''):
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500212
Dan Talayco1b3f6902010-02-15 14:14:19 -0800213 print prefix + "Name: " + self.interface_name
Dan Talayco710438c2010-02-18 15:16:07 -0800214 print prefix + "Pkts pending: " + str(len(self.packets))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800215 print prefix + "Pkts total: " + str(self.packets_total)
216 print prefix + "socket: " + str(self.socket)
Dan Talaycoe226eb12010-02-18 23:06:30 -0800217
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500218
219 def port_down(self,port_number,config):
220
221 """
222 Grabs a port from the dataplane ports and brings it down by
223 shutting the corresponding interface
224 @port_number The port number which has brought to be down
225 @interface_name The interface corresponding to the port that needs to
226 be brought down
227
228 """
229 interface_name = config["port_map"].get(port_number)
230 cmd = 'ifdown '+ interface_name
231 os.system(cmd)
232
233 def port_up(self,port_number,config):
234
235 """
236 Grabs a port from the dataplane ports and brings it up by
237 starting up the corresponding interface
238 @port_number The port number which has to brought up
239 @interface_name The interface corresponding to the port that has to
240 be brought up
241
242 """
243 interface_name = config["port_map"].get(port_number)
244 cmd = 'ifup '+ interface_name
245 os.system(cmd)
246
247
Rich Laneb42a31c2012-10-05 17:54:17 -0700248class DataPlanePortPcap(DataPlanePort):
249 """
250 Alternate port implementation using libpcap. This is required for recent
251 versions of Linux (such as Linux 3.2 included in Ubuntu 12.04) which
252 offload the VLAN tag, so it isn't in the data returned from a read on a raw
253 socket. libpcap understands how to read the VLAN tag from the kernel.
254 """
255
256 def __init__(self, interface_name, port_number, parent, max_pkts=1024):
257 DataPlanePort.__init__(self, interface_name, port_number, parent, max_pkts)
258
259 def interface_open(self, interface_name):
260 """
261 Open a PCAP interface.
262 """
263 self.pcap = pcap.pcap(interface_name)
264 self.pcap.setnonblock()
265 return self.pcap.fileno()
266
267 def run(self):
268 """
269 Activity function for class
270 """
271 self.running = True
272 while self.running:
273 try:
274 sel_in, sel_out, sel_err = select.select([self.socket], [], [], 1)
275 except:
276 print sys.exc_info()
277 self.logger.error("Select error, exiting")
278 break
279
280 if not self.running:
281 break
282
283 if (sel_in is None) or (len(sel_in) == 0):
284 continue
285
286 # Enqueue packet
287 with self.parent.pkt_sync:
288 for (timestamp, rcvmsg) in self.pcap.readpkts():
Rich Laneb42a31c2012-10-05 17:54:17 -0700289 self.logger.debug("Pkt len " + str(len(rcvmsg)) +
Rich Laned2e93aa2012-12-05 17:55:46 -0800290 " in at " + str(timestamp) + " on port " +
Rich Laneb42a31c2012-10-05 17:54:17 -0700291 str(self.port_number))
292
293 if len(self.packets) >= self.max_pkts:
294 # Queue full, throw away oldest
295 self.packets.pop(0)
296 self.packets_discarded += 1
297 self.logger.debug("Discarding oldest packet to make room")
Rich Laned2e93aa2012-12-05 17:55:46 -0800298 self.packets.append((rcvmsg, timestamp))
Rich Laneb42a31c2012-10-05 17:54:17 -0700299 self.packets_total += 1
300 self.parent.pkt_sync.notify_all()
301
302 self.logger.info("Thread exit")
303
304 def kill(self):
305 """
306 Terminate the running thread
307 """
308 self.logger.debug("Port monitor kill")
309 self.running = False
310 # pcap object is closed on GC.
311
312 def send(self, packet):
313 """
314 Send a packet to the dataplane port
315 @param packet The packet data to send to the port
316 @retval The number of bytes sent
317 """
318 return self.pcap.inject(packet, len(packet))
Dan Talayco34089522010-02-07 23:07:41 -0800319
320class DataPlane:
321 """
322 Class defining access primitives to the data plane
323 Controls a list of DataPlanePort objects
324 """
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700325 def __init__(self, config=None):
Dan Talayco34089522010-02-07 23:07:41 -0800326 self.port_list = {}
Dan Talaycoe226eb12010-02-18 23:06:30 -0800327 # pkt_sync serves double duty as a regular top level lock and
328 # as a condition variable
329 self.pkt_sync = Condition()
330
331 # These are used to signal async pkt arrival for polling
332 self.want_pkt = False
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700333 self.exp_pkt = None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800334 self.want_pkt_port = None # What port required (or None)
335 self.got_pkt_port = None # On what port received?
336 self.packets_pending = 0 # Total pkts in all port queues
Dan Talayco48370102010-03-03 15:17:33 -0800337 self.logger = logging.getLogger("dataplane")
Dan Talayco34089522010-02-07 23:07:41 -0800338
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700339 if config is None:
340 self.config = {}
341 else:
342 self.config = config;
343
344 ############################################################
345 #
346 # We use the DataPlanePort class defined here by
347 # default for all port traffic:
348 #
Rich Laneb42a31c2012-10-05 17:54:17 -0700349 if have_pypcap:
350 self.dppclass = DataPlanePortPcap
351 else:
352 self.logger.warning("Missing pypcap, VLAN tests may fail. See README for installation instructions.")
353 self.dppclass = DataPlanePort
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700354
355 ############################################################
356 #
357 # The platform/config can provide a custom DataPlanePort class
358 # here if you have a custom implementation with different
359 # behavior.
360 #
361 # Set config.dataplane.portclass = MyDataPlanePortClass
362 # where MyDataPlanePortClass has the same interface as the class
363 # DataPlanePort defined here.
364 #
365 if "dataplane" in self.config:
366 if "portclass" in self.config["dataplane"]:
367 self.dppclass = self.config["dataplane"]["portclass"]
368
369 if self.dppclass == None:
370 raise Exception("Problem determining DataPlanePort class.")
371
372
Dan Talayco34089522010-02-07 23:07:41 -0800373 def port_add(self, interface_name, port_number):
374 """
375 Add a port to the dataplane
376 TBD: Max packets for queue?
377 @param interface_name The name of the physical interface like eth1
378 @param port_number The port number used to refer to the port
379 """
Dan Talaycoe226eb12010-02-18 23:06:30 -0800380
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700381 self.port_list[port_number] = self.dppclass(interface_name,
382 port_number, self);
383
Dan Talayco34089522010-02-07 23:07:41 -0800384 self.port_list[port_number].start()
385
Jeffrey Townsend0e8b0922012-07-11 11:37:46 -0700386
387
Dan Talayco34089522010-02-07 23:07:41 -0800388 def send(self, port_number, packet):
389 """
390 Send a packet to the given port
391 @param port_number The port to send the data to
392 @param packet Raw packet data to send to port
393 """
Dan Talayco11c26e72010-03-07 22:03:57 -0800394 self.logger.debug("Sending %d bytes to port %d" %
395 (len(packet), port_number))
Dan Talayco34089522010-02-07 23:07:41 -0800396 bytes = self.port_list[port_number].send(packet)
397 if bytes != len(packet):
Dan Talayco48370102010-03-03 15:17:33 -0800398 self.logger.error("Unhandled send error, length mismatch %d != %d" %
Dan Talayco1b3f6902010-02-15 14:14:19 -0800399 (bytes, len(packet)))
Dan Talayco34089522010-02-07 23:07:41 -0800400 return bytes
401
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500402
403
404
Dan Talayco34089522010-02-07 23:07:41 -0800405 def flood(self, packet):
406 """
407 Send a packet to all ports
408 @param packet Raw packet data to send to port
409 """
410 for port_number in self.port_list.keys():
411 bytes = self.port_list[port_number].send(packet)
412 if bytes != len(packet):
Dan Talayco48370102010-03-03 15:17:33 -0800413 self.logger.error("Unhandled send error" +
Dan Talayco1b3f6902010-02-15 14:14:19 -0800414 ", port %d, length mismatch %d != %d" %
415 (port_number, bytes, len(packet)))
Dan Talayco34089522010-02-07 23:07:41 -0800416
Rich Lanedb9d8662012-07-26 18:04:24 -0700417 # Returns the port with the oldest packet, or None if no packets are queued.
418 def oldest_port(self):
419 min_port = None
420 min_time = float('inf')
421 for port in self.port_list.values():
422 ptime = port.timestamp_head()
423 if ptime and ptime < min_time:
424 min_time = ptime
425 min_port = port
426 return min_port
427
428 # Dequeues and yields packets in the order they were received.
429 # Yields (port, packet, received time).
430 # If port_number is not specified yields packets from all ports.
431 def packets(self, port_number=None):
432 while True:
433 if port_number == None:
434 port = self.oldest_port()
435 else:
436 port = self.port_list[port_number]
437
438 if port == None or len(port.packets) == 0:
439 self.logger.debug("Out of packets for port %s" % str(port_number))
440 # Out of packets
441 break
442
443 pkt, time = port.packets.pop(0)
444 yield (port, pkt, time)
445
Rich Lane8806bc42012-07-26 19:18:37 -0700446 def poll(self, port_number=None, timeout=-1, exp_pkt=None):
Dan Talaycoe226eb12010-02-18 23:06:30 -0800447 """
448 Poll one or all dataplane ports for a packet
449
450 If port_number is given, get the oldest packet from that port.
451 Otherwise, find the port with the oldest packet and return
452 that packet.
Dan Talayco1729fdb2012-05-03 09:35:56 -0700453
454 If exp_pkt is true, discard all packets until that one is found
455
Dan Talaycoe226eb12010-02-18 23:06:30 -0800456 @param port_number If set, get packet from this port
Dan Talayco11c26e72010-03-07 22:03:57 -0800457 @param timeout If positive and no packet is available, block
Dan Talaycoe226eb12010-02-18 23:06:30 -0800458 until a packet is received or for this many seconds
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700459 @param exp_pkt If not None, look for this packet and ignore any
Dan Talayco1729fdb2012-05-03 09:35:56 -0700460 others received. Note that if port_number is None, all packets
461 from all ports will be discarded until the exp_pkt is found
Dan Talaycoe226eb12010-02-18 23:06:30 -0800462 @return The triple port_number, packet, pkt_time where packet
463 is received from port_number at time pkt_time. If a timeout
464 occurs, return None, None, None
465 """
466
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700467 if exp_pkt and not port_number:
Dan Talayco1729fdb2012-05-03 09:35:56 -0700468 self.logger.warn("Dataplane poll with exp_pkt but no port number")
469
Rich Lanedb9d8662012-07-26 18:04:24 -0700470 # Retrieve the packet. Returns (port number, packet, time).
471 def grab():
472 self.logger.debug("Grabbing packet")
473 for (port, pkt, time) in self.packets(port_number):
474 self.logger.debug("Checking packet from port %d" % port.port_number)
Dan Talayco1729fdb2012-05-03 09:35:56 -0700475 if not exp_pkt or match_exp_pkt(exp_pkt, pkt):
Rich Lanedb9d8662012-07-26 18:04:24 -0700476 return (port, pkt, time)
477 self.logger.debug("Did not find packet")
478 return None
Dan Talaycoe226eb12010-02-18 23:06:30 -0800479
Rich Lanedb9d8662012-07-26 18:04:24 -0700480 with self.pkt_sync:
481 ret = timed_wait(self.pkt_sync, grab, timeout=timeout)
Dan Talayco34089522010-02-07 23:07:41 -0800482
Rich Lanedb9d8662012-07-26 18:04:24 -0700483 if ret != None:
484 (port, pkt, time) = ret
485 return (port.port_number, pkt, time)
486 else:
487 self.logger.debug("Poll time out, no packet from " + str(port_number))
488 return (None, None, None)
Dan Talayco34089522010-02-07 23:07:41 -0800489
Dan Talayco48370102010-03-03 15:17:33 -0800490 def kill(self, join_threads=True):
Dan Talayco1b3f6902010-02-15 14:14:19 -0800491 """
492 Close all sockets for dataplane
Dan Talayco710438c2010-02-18 15:16:07 -0800493 @param join_threads If True call join on each thread
Dan Talayco1b3f6902010-02-15 14:14:19 -0800494 """
Dan Talayco34089522010-02-07 23:07:41 -0800495 for port_number in self.port_list.keys():
496 self.port_list[port_number].kill()
Dan Talayco1b3f6902010-02-15 14:14:19 -0800497 if join_threads:
Dan Talayco48370102010-03-03 15:17:33 -0800498 self.logger.debug("Joining " + str(port_number))
Dan Talayco1b3f6902010-02-15 14:14:19 -0800499 self.port_list[port_number].join()
Dan Talayco34089522010-02-07 23:07:41 -0800500
Dan Talayco48370102010-03-03 15:17:33 -0800501 self.logger.info("DataPlane shutdown")
Dan Talayco1b3f6902010-02-15 14:14:19 -0800502
503 def show(self, prefix=''):
504 print prefix + "Dataplane Controller"
Dan Talaycoe226eb12010-02-18 23:06:30 -0800505 print prefix + "Packets pending" + str(self.packets_pending)
Dan Talayco1b3f6902010-02-15 14:14:19 -0800506 for pnum, port in self.port_list.items():
507 print prefix + "OpenFlow Port Number " + str(pnum)
508 port.show(prefix + ' ')
509
ShreyaPanditacd8e1cf2012-11-28 11:44:42 -0500510
511 def port_down(self,port_number):
512 """Brings the specified port down"""
513 self.port_list[port_number].port_down(port_number,self.config)
514
515
516 def port_up(self,port_number):
517 """Brings the specified port up"""
518 self.port_list[port_number].port_up(port_number,self.config)