Brian O'Connor
Committed by Gerrit Code Review

Several improvements to onosnet.py

Replace arping and ping with custom gratuitous arp code
Don't block forever on switch.waitConnected()
Allow backgrounding bgIperf

Change-Id: I6cb10c3c8971e29336a6c6e2deffb5231900d463
1 +#!/usr/bin/env python
2 +import sys
3 +import os
4 +import fcntl
5 +import socket
6 +from struct import pack
7 +
8 +def getIPAddress(intf):
9 + #Borrowed from:
10 + #http://stackoverflow.com/questions/24196932/how-can-i-get-the-ip-address-of-eth0-in-python
11 + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
12 + return socket.inet_ntoa(fcntl.ioctl(
13 + s.fileno(),
14 + 0x8915, # SIOCGIFADDR
15 + pack('256s', intf[:15])
16 + )[20:24])
17 +
18 +def gratuitousArp(intf, ip=None, mac=None):
19 + #Adapted from:
20 + #https://github.com/krig/send_arp.py/blob/master/send_arp.py
21 + sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
22 + try:
23 + sock.bind((intf, socket.SOCK_RAW))
24 + except:
25 + print 'Device does not exist: %s' % intf
26 + return
27 +
28 + if not ip:
29 + try:
30 + ip = getIPAddress(intf)
31 + except IOError:
32 + print 'No IP for %s' % intf
33 + return
34 + packed_ip = pack('!4B', *[int(x) for x in ip.split('.')])
35 +
36 + if mac:
37 + packed_mac = pack('!6B', *[int(x,16) for x in mac.split(':')])
38 + else:
39 + packed_mac = sock.getsockname()[4]
40 +
41 + bcast_mac = pack('!6B', *(0xFF,)*6)
42 + zero_mac = pack('!6B', *(0x00,)*6)
43 + eth_arp = pack('!H', 0x0806)
44 + arp_proto = pack('!HHBBH', 0x0001, 0x0800, 0x0006, 0x0004, 0x0001)
45 + arpframe = [
46 + ## ETHERNET
47 + # destination MAC addr
48 + bcast_mac,
49 + # source MAC addr
50 + packed_mac,
51 + # eth proto
52 + eth_arp,
53 +
54 + ## ARP
55 + arp_proto,
56 + # sender MAC addr
57 + packed_mac,
58 + # sender IP addr
59 + packed_ip,
60 + # target hardware addr
61 + bcast_mac,
62 + # target IP addr
63 + packed_ip
64 + ]
65 +
66 + # send the ARP packet
67 + sock.send(''.join(arpframe))
68 +
69 +if __name__ == "__main__":
70 + if len(sys.argv) > 1:
71 + intfs = sys.argv[1:]
72 + else:
73 + intfs = os.listdir('/sys/class/net/')
74 +
75 + for intf in intfs:
76 + gratuitousArp(intf)
...\ No newline at end of file ...\ No newline at end of file
...@@ -2,21 +2,22 @@ ...@@ -2,21 +2,22 @@
2 2
3 import sys 3 import sys
4 import itertools 4 import itertools
5 +import signal
5 from time import sleep 6 from time import sleep
7 +from threading import Thread
6 8
7 from mininet.net import Mininet 9 from mininet.net import Mininet
8 from mininet.log import setLogLevel 10 from mininet.log import setLogLevel
9 -from mininet.node import RemoteController 11 +from mininet.node import RemoteController, Node
10 -from mininet.log import info, debug, output 12 +from mininet.log import info, debug, output, error
11 -from mininet.util import quietRun
12 from mininet.link import TCLink 13 from mininet.link import TCLink
13 from mininet.cli import CLI 14 from mininet.cli import CLI
14 15
15 -class ONOSMininet( Mininet ): 16 +# This is the program that each host will call
17 +import gratuitousArp
18 +ARP_PATH = gratuitousArp.__file__.replace('.pyc', '.py')
16 19
17 - @classmethod 20 +class ONOSMininet( Mininet ):
18 - def setup( cls ):
19 - cls.useArping = True if quietRun( 'which arping' ) else False
20 21
21 def __init__( self, controllers=[], gratuitousArp=True, build=True, *args, **kwargs ): 22 def __init__( self, controllers=[], gratuitousArp=True, build=True, *args, **kwargs ):
22 """Create Mininet object for ONOS. 23 """Create Mininet object for ONOS.
...@@ -32,7 +33,6 @@ class ONOSMininet( Mininet ): ...@@ -32,7 +33,6 @@ class ONOSMininet( Mininet ):
32 Mininet.__init__(self, *args, **kwargs ) 33 Mininet.__init__(self, *args, **kwargs )
33 34
34 self.gratArp = gratuitousArp 35 self.gratArp = gratuitousArp
35 - self.useArping = ONOSMininet.useArping
36 36
37 info ( '*** Adding controllers\n' ) 37 info ( '*** Adding controllers\n' )
38 ctrl_count = 0 38 ctrl_count = 0
...@@ -47,31 +47,30 @@ class ONOSMininet( Mininet ): ...@@ -47,31 +47,30 @@ class ONOSMininet( Mininet ):
47 def start( self ): 47 def start( self ):
48 Mininet.start( self ) 48 Mininet.start( self )
49 if self.gratArp: 49 if self.gratArp:
50 - self.waitConnected() 50 + self.waitConnected( timeout=5 )
51 info ( '*** Sending a gratuitious ARP from each host\n' ) 51 info ( '*** Sending a gratuitious ARP from each host\n' )
52 self.gratuitousArp() 52 self.gratuitousArp()
53 53
54 + def verifyHosts( self, hosts ):
55 + for i in range( len( hosts ) ):
56 + if isinstance( hosts[i], str):
57 + if hosts[i] in self:
58 + hosts[i] = self[ hosts[i] ]
59 + else:
60 + info( '*** ERROR: %s is not a host\n' % hosts[i] )
61 + del hosts[i]
62 + elif not isinstance( hosts[i], Node):
63 + del hosts[i]
54 64
55 - def gratuitousArp( self ): 65 + def gratuitousArp( self, hosts=[] ):
56 "Send an ARP from each host to aid controller's host discovery; fallback to ping if necessary" 66 "Send an ARP from each host to aid controller's host discovery; fallback to ping if necessary"
57 - if self.useArping: 67 + if not hosts:
58 - for host in self.hosts: 68 + hosts = self.hosts
69 + self.verifyHosts( hosts )
70 +
71 + for host in hosts:
59 info( '%s ' % host.name ) 72 info( '%s ' % host.name )
60 - debug( host.cmd( 'arping -U -c 1 ' + host.IP() ) ) 73 + info( host.cmd( ARP_PATH ) )
61 - info ( '\n' )
62 - else:
63 - info( '\nWARNING: arping is not found, using ping instead.\n'
64 - 'For higher performance, install arping: sudo apt-get install iputils-arping\n\n' )
65 -
66 - procs = [ s.popen( 'ping -w 0.1 -W 0.1 -c1 %s > /dev/null; printf "%s "'
67 - % ( d.IP(), s.name ), shell=True )
68 - for (s, d) in zip( self.hosts, self.hosts[1:] + self.hosts[0:1] ) ]
69 - for t in procs:
70 - out, err = t.communicate()
71 - if err:
72 - info ( err )
73 - else:
74 - info ( out )
75 info ( '\n' ) 74 info ( '\n' )
76 75
77 def pingloop( self ): 76 def pingloop( self ):
...@@ -84,28 +83,51 @@ class ONOSMininet( Mininet ): ...@@ -84,28 +83,51 @@ class ONOSMininet( Mininet ):
84 setLogLevel( 'info' ) 83 setLogLevel( 'info' )
85 84
86 def bgIperf( self, hosts=[], seconds=10 ): 85 def bgIperf( self, hosts=[], seconds=10 ):
87 - #TODO check if the hosts are strings or objects 86 + self.verifyHosts( hosts )
88 - # h1 = net.getNodeByName('h1')
89 servers = [ host.popen("iperf -s") for host in hosts ] 87 servers = [ host.popen("iperf -s") for host in hosts ]
90 88
91 clients = [] 89 clients = []
92 - for pair in itertools.combinations(hosts, 2): 90 + for s, d in itertools.combinations(hosts, 2):
93 - info ( '%s <--> %s\n' % ( pair[0].name, pair[1].name )) 91 + info ( '%s <--> %s\n' % ( s.name, d.name ))
94 - cmd = "iperf -c %s -t %s" % (pair[1].IP(), seconds) 92 + cmd = 'iperf -c %s -t %s -y csv' % (d.IP(), seconds)
95 - clients.append(pair[0].popen(cmd)) 93 + p = s.popen(cmd)
96 - 94 + p.s = s.name
97 - progress( seconds ) 95 + p.d = d.name
98 - 96 + clients.append(p)
97 +
98 + def handler (_signum, _frame):
99 + raise BackgroundException()
100 + oldSignal = signal.getsignal(signal.SIGTSTP)
101 + signal.signal(signal.SIGTSTP, handler)
102 +
103 + def finish( verbose=True ):
99 for c in clients: 104 for c in clients:
100 out, err = c.communicate() 105 out, err = c.communicate()
106 + if verbose:
101 if err: 107 if err:
102 info( err ) 108 info( err )
103 else: 109 else:
104 - debug( out ) 110 + bw = out.split( ',' )[8]
105 - #TODO parse output and print summary 111 + info( '%s <--> %s: %s\n' % ( c.s, c.d, formatBw(bw) ) )
112 + for s in servers:
113 + s.terminate()
106 114
115 + try:
116 + info ( 'Press ^Z to continue in background or ^C to abort\n')
117 + progress( seconds )
118 + finish()
119 + except KeyboardInterrupt:
120 + for c in clients:
121 + c.terminate()
107 for s in servers: 122 for s in servers:
108 s.terminate() 123 s.terminate()
124 + except BackgroundException:
125 + info( '\n*** Continuing in background...\n' )
126 + t = Thread( target=finish, args=[ False ] )
127 + t.start()
128 + finally:
129 + #Disable custom background signal
130 + signal.signal(signal.SIGTSTP, oldSignal)
109 131
110 def progress(t): 132 def progress(t):
111 while t > 0: 133 while t > 0:
...@@ -115,13 +137,39 @@ def progress(t): ...@@ -115,13 +137,39 @@ def progress(t):
115 sleep(1) 137 sleep(1)
116 print 138 print
117 139
118 -# Initialize ONOSMininet the first time that the class is loaded 140 +def formatBw( bw ):
119 -ONOSMininet.setup() 141 + bw = float(bw)
120 - 142 + if bw > 1000:
121 -def do_iperf( self, line ): 143 + bw /= 1000
144 + if bw > 1000:
145 + bw /= 1000
146 + if bw > 1000:
147 + bw /= 1000
148 + return '%.2f Gbps' % bw
149 + return '%.2f Mbps' % bw
150 + return '%.2f Kbps' % bw
151 + return '%.2f bps' % bw
152 +
153 +class BackgroundException( Exception ):
154 + pass
155 +
156 +def do_bgIperf( self, line ):
122 args = line.split() 157 args = line.split()
123 if not args: 158 if not args:
124 output( 'Provide a list of hosts.\n' ) 159 output( 'Provide a list of hosts.\n' )
160 +
161 + #Try to parse the '-t' argument as the number of seconds
162 + seconds = 10
163 + for i, arg in enumerate(args):
164 + if arg == '-t':
165 + if i + 1 < len(args):
166 + try:
167 + seconds = int(args[i + 1])
168 + except ValueError:
169 + error( 'Could not parse number of seconds: %s', args[i+1] )
170 + del(args[i+1])
171 + del args[i]
172 +
125 hosts = [] 173 hosts = []
126 err = False 174 err = False
127 for arg in args: 175 for arg in args:
...@@ -131,15 +179,16 @@ def do_iperf( self, line ): ...@@ -131,15 +179,16 @@ def do_iperf( self, line ):
131 else: 179 else:
132 hosts.append( self.mn[ arg ] ) 180 hosts.append( self.mn[ arg ] )
133 if "bgIperf" in dir(self.mn) and not err: 181 if "bgIperf" in dir(self.mn) and not err:
134 - self.mn.bgIperf( hosts ) 182 + self.mn.bgIperf( hosts, seconds=seconds )
135 183
136 -def do_gratuitousArp( self, _line ): 184 +def do_gratuitousArp( self, line ):
137 - if "gratuitousArp" in dir(self.mn): 185 + args = line.split()
138 - self.mn.gratuitousArp() 186 + if "gratuitousArp" in dir( self.mn ):
187 + self.mn.gratuitousArp( args )
139 else: 188 else:
140 - output( 'Gratuitous ARP is not support.\n' ) 189 + output( 'Gratuitous ARP is not supported.\n' )
141 190
142 -CLI.do_bgIperf = do_iperf 191 +CLI.do_bgIperf = do_bgIperf
143 CLI.do_gratuitousArp = do_gratuitousArp 192 CLI.do_gratuitousArp = do_gratuitousArp
144 193
145 def run( topo, controllers=None, link=TCLink, autoSetMacs=True ): 194 def run( topo, controllers=None, link=TCLink, autoSetMacs=True ):
......