Statistics
| Branch: | Tag: | Revision:

mininet / examples / udpbwgraph.py @ c58bb42f

History | View | Annotate | Download (7.77 KB)

1
#!/usr/bin/python
2

    
3
"""
4
udpbwgraph: Plot network bandwidth over time
5

6
Bob Lantz
7
3/27/10
8
"""
9

    
10
from time import sleep
11

    
12
import os
13
import re
14
import sys
15
from time import time
16

    
17
from Tkinter import *
18

    
19
from mininet.log import setLogLevel
20
from mininet.net import init, Mininet
21
from mininet.node import KernelSwitch, UserSwitch, OVSKernelSwitch
22
from mininet.node import Controller, NOX
23
from mininet.topolib import TreeTopo
24
from mininet.util import quietRun
25

    
26
# bwtest support
27
    
28
class Graph( Frame ):
29

    
30
    "Graph that we can add bars to over time."
31
    
32
    def __init__( self, master=None,
33
        bg = 'white',
34
        gheight=200, gwidth=500,
35
        barwidth=10,
36
        ymax=3.5,):
37
        
38
        Frame.__init__( self, master )
39

    
40
        self.bg = bg
41
        self.gheight = gheight
42
        self.gwidth = gwidth
43
        self.barwidth = barwidth
44
        self.ymax = float( ymax )
45
        self.xpos = 0
46

    
47
        # Create everything
48
        self.title = self.graph = None
49
        self.createWidgets()
50
        self.updateScrollRegions()
51
        self.yview( 'moveto', '1.0' )
52
        
53
    def scale( self ):
54
        "Create a and return a new canvas with scale markers."
55
        height = float( self.gheight )
56
        width = 25
57
        ymax = self.ymax
58
        scale = Canvas( self, width=width, height=height, background=self.bg )
59
        fill = 'red'
60
        # Draw scale line
61
        scale.create_line( width - 1, height, width - 1, 0, fill=fill )
62
        # Draw ticks and numbers
63
        for y in range( 0, int( ymax + 1 ) ):
64
            ypos = height * (1 - float( y ) / ymax )
65
            scale.create_line( width, ypos, width - 10, ypos, fill=fill )
66
            scale.create_text( 10, ypos, text=str( y ), fill=fill )
67
            
68
        return scale
69
    
70
    def updateScrollRegions( self ):
71
        "Update graph and scale scroll regions."
72
        ofs = 20
73
        height = self.gheight + ofs
74
        self.graph.configure( scrollregion=( 0, -ofs, 
75
            self.xpos * self.barwidth, height ) )
76
        self.scale.configure( scrollregion=( 0, -ofs, 0, height ) )
77
        
78
    def yview( self, *args ):
79
            "Scroll both scale and graph."
80
            self.graph.yview( *args )
81
            self.scale.yview( *args )
82
                
83
    def createWidgets( self ):
84
        "Create initial widget set."
85

    
86
        # Objects
87
        title = Label( self, text="Bandwidth (Mb/s)", bg=self.bg )
88
        width = self.gwidth
89
        height = self.gheight
90
        scale = self.scale()
91
        graph = Canvas( self, width=width, height=height, background=self.bg)
92
        xbar = Scrollbar( self, orient='horizontal', command=graph.xview )
93
        ybar = Scrollbar( self, orient='vertical', command=self.yview )
94
        graph.configure( xscrollcommand=xbar.set, yscrollcommand=ybar.set,
95
            scrollregion=(0, 0, width, height ) )
96
        scale.configure( yscrollcommand=ybar.set )
97
        
98
        # Layout
99
        title.grid( row=0, columnspan=3, sticky=N+E+W)
100
        scale.grid( row=1, column=0, sticky=N+S+E+W )
101
        graph.grid( row=1, column=1, sticky=N+S+E+W )
102
        ybar.grid( row=1, column=2, sticky=N+S )
103
        xbar.grid( row=2, column=0, columnspan=2, sticky=E+W )
104
        self.rowconfigure( 1, weight=1 )
105
        self.columnconfigure( 1, weight=1 )
106

    
107
        # Save for future reference
108
        self.title = title
109
        self.scale = scale
110
        self.graph = graph
111
        return graph
112
            
113
    def addBar( self, yval ):
114
        "Add a new bar to our graph."
115
        percent = yval / self.ymax
116
        height = percent * self.gheight
117
        c = self.graph
118
        x0 = self.xpos * self.barwidth
119
        x1 = x0 + self.barwidth
120
        y0 = self.gheight
121
        y1 = ( 1 - percent ) * self.gheight
122
        c.create_rectangle( x0 , y0, x1, y1, fill='green' )
123
        self.xpos += 1
124
        self.updateScrollRegions()
125
        self.graph.xview( 'moveto', '1.0' )
126

    
127
    def test( self ):
128
        "Add a bar for testing purposes."
129
        ms = 1000
130
        if self.xpos < 10:
131
            self.addBar( self.xpos/10 * self.ymax  )
132
            self.after( ms, self.test )
133

    
134
    def setTitle( self, text ):
135
        "Set graph title"
136
        self.title.configure( text=text, font='Helvetica 9 bold' )
137

    
138

    
139
class Controls( Frame ):
140

    
141
    "Handy controls for configuring test."
142
    
143
    switches = { 
144
        'Kernel Switch': KernelSwitch,
145
        'User Switch': UserSwitch,
146
        'Open vSwitch': OVSKernelSwitch
147
    }
148
    
149
    controllers = {
150
        'Reference Controller': Controller,
151
        'NOX': NOX
152
    }
153
    
154
    
155
    def __init__( self, master=None ):
156
    
157
        Frame.__init__( self, master )
158
        
159
        self.switch = StringVar()
160
        self.switch.set( 'Kernel Switch' )
161
        self.switchMenu = OptionMenu( self, self.switch, 
162
            *( switches.keys() ) )
163
        
164
        self.controller = StringVar()
165
        self.controller.set( 'Reference Controller' )
166
        self.controllerMenu = OpetionMenu( self, self.controller,
167
            *( controllers.keys() ) )
168

    
169
def App( Frame ):
170

    
171
           
172
def parsebwtest( line,
173
    r=re.compile( r'(\d+) s: in ([\d\.]+) Mbps, out ([\d\.]+) Mbps' ) ):
174
    "Parse udpbwtest.c output, returning seconds, inbw, outbw."
175
    match = r.match( line )
176
    if match:
177
        seconds, inbw, outbw = match.group( 1, 2, 3 )
178
        return int( seconds ), float( inbw ), float( outbw )
179
    return None, None, None
180

    
181

    
182
class UdpBwTest( object ):
183
    "Test and plot UDP bandwidth over time"
184

    
185
    def __init__( self, graph, net, seconds=60 ):
186
        "Start up and monitor udpbwtest on each of our hosts."
187
        
188
        hosts = net.hosts
189
        self.graph = graph
190
        self.hostCount = len( hosts )
191
        
192
        print "*** Starting udpbwtest on hosts"
193
        for host in hosts:
194
            ips = [ h.IP() for h in hosts if h != host ]
195
            host.cmdPrint( './udpbwtest ' + ' '.join( ips ) + ' &' )
196
        
197
        print "*** Monitoring hosts"
198
        self.output = net.monitor( hosts, timeoutms=0 )
199
        self.results = {}
200
        self.quitTime = time() + seconds
201
        self.updateGraph()
202

    
203
    # Pylint isn't smart enough to understand iterator.next()
204
    # pylint: disable-msg=E1101
205
    
206
    def updateGraph( self ):
207
        "Graph input bandwidth."
208
        while True:
209
            host, line = self.output.next()
210
            if host is None or len( line ) == 0:
211
                break
212
            seconds, inbw, outbw = parsebwtest( line )
213
            if seconds is None:
214
                break
215
            result = self.results.get( seconds, [] ) + [ ( host, inbw, outbw ) ]
216
            self.results[ seconds ] = result
217
            if len( result ) == self.hostCount:
218
                # Calculate total and update graph
219
                # We report input bandwidth, i.e. packets that made it
220
                totalin = 0
221
                for host, inbw, outbw in result:
222
                    totalin += inbw
223
                self.graph.addBar( totalin / 1000.0 )
224
                print totalin
225
        if time() < self.quitTime:
226
            # Fileevent would be better, but for now we just poll every 500ms
227
            self.graph.after( 10, self.updateGraph )
228
        else:
229
            self.shutdown()
230

    
231
    def shutdown( self ):
232
        "Stop udpbwtest proceses."
233
        print "*** Stopping udpbwtest processes"
234
        # We *really* don't want these things hanging around!
235
        quietRun( 'killall -9 udpbwtest' )
236

    
237
    # pylint: enable-msg=E1101
238

    
239
if __name__ == '__main__':
240
    setLogLevel( 'info' )
241
    app = Graph()
242
    app.master.title( "Mininet Bandwidth" )
243
    depth, fanout = 1, 2
244
    net = Mininet( topo=TreeTopo( depth=depth, fanout=fanout), 
245
        switch=KernelSwitch )
246
    title = "Bandwidth (Mb/s), (%i hosts, %i switches, depth=%d, fanout=%d)" % (
247
        len( net.hosts ), len( net.switches), depth, fanout )
248
    app.setTitle( title )
249
    net.start()
250
    test = UdpBwTest( app, net )
251
    app.mainloop()
252
    net.stop()
253
    test.shutdown()  # just in case!
254