Statistics
| Branch: | Tag: | Revision:

mininet / mininet / net.py @ 9005ce32

History | View | Annotate | Download (22.4 KB)

1 281f6e59 Bob Lantz
"""
2

3 a6bcad8f Bob Lantz
    Mininet: A simple networking testbed for OpenFlow/SDN!
4 281f6e59 Bob Lantz

5
author: Bob Lantz (rlantz@cs.stanford.edu)
6
author: Brandon Heller (brandonh@stanford.edu)
7 98d4f189 Bob Lantz

8 08cef003 Bob Lantz
Mininet creates scalable OpenFlow test networks by using
9 723d068c Brandon Heller
process-based virtualization and network namespaces.
10 98d4f189 Bob Lantz

11 eddef947 Bob Lantz
Simulated hosts are created as processes in separate network
12
namespaces. This allows a complete OpenFlow network to be simulated on
13
top of a single Linux kernel.
14 98d4f189 Bob Lantz

15
Each host has:
16 7d4b7b7f Bob Lantz

17 281f6e59 Bob Lantz
A virtual console (pipes to a shell)
18
A virtual interfaces (half of a veth pair)
19
A parent shell (and possibly some child processes) in a namespace
20 723d068c Brandon Heller

21 eddef947 Bob Lantz
Hosts have a network interface which is configured via ifconfig/ip
22 8f20b95d Brandon Heller
link/etc.
23 98d4f189 Bob Lantz

24 55dd9368 Bob Lantz
This version supports both the kernel and user space datapaths
25 1bda2d21 Bob Lantz
from the OpenFlow reference implementation (openflowswitch.org)
26
as well as OpenVSwitch (openvswitch.org.)
27 08cef003 Bob Lantz

28 98d4f189 Bob Lantz
In kernel datapath mode, the controller and switches are simply
29
processes in the root namespace.
30

31 281f6e59 Bob Lantz
Kernel OpenFlow datapaths are instantiated using dpctl(8), and are
32 55dd9368 Bob Lantz
attached to the one side of a veth pair; the other side resides in the
33
host namespace. In this mode, switch processes can simply connect to the
34 eddef947 Bob Lantz
controller via the loopback interface.
35 98d4f189 Bob Lantz

36 31b43002 Bob Lantz
In user datapath mode, the controller and switches can be full-service
37 eddef947 Bob Lantz
nodes that live in their own network namespaces and have management
38 bbe5f8a3 Bob Lantz
interfaces and IP addresses on a control network (e.g. 192.168.123.1,
39 281f6e59 Bob Lantz
currently routed although it could be bridged.)
40 98d4f189 Bob Lantz

41
In addition to a management interface, user mode switches also have
42 55dd9368 Bob Lantz
several switch interfaces, halves of veth pairs whose other halves
43
reside in the host nodes that the switches are connected to.
44 98d4f189 Bob Lantz

45 31b43002 Bob Lantz
Consistent, straightforward naming is important in order to easily
46
identify hosts, switches and controllers, both from the CLI and
47
from program code. Interfaces are named to make it easy to identify
48
which interfaces belong to which node.
49

50
The basic naming scheme is as follows:
51 281f6e59 Bob Lantz

52 80be5642 Bob Lantz
    Host nodes are named h1-hN
53
    Switch nodes are named s1-sN
54 31b43002 Bob Lantz
    Controller nodes are named c0-cN
55
    Interfaces are named {nodename}-eth0 .. {nodename}-ethN
56

57 80be5642 Bob Lantz
Note: If the network topology is created using mininet.topo, then
58
node numbers are unique among hosts and switches (e.g. we have
59
h1..hN and SN..SN+M) and also correspond to their default IP addresses
60
of 10.x.y.z/8 where x.y.z is the base-256 representation of N for
61
hN. This mapping allows easy determination of a node's IP
62
address from its name, e.g. h1 -> 10.0.0.1, h257 -> 10.0.1.1.
63

64 bbe5f8a3 Bob Lantz
Note also that 10.0.0.1 can often be written as 10.1 for short, e.g.
65
"ping 10.1" is equivalent to "ping 10.0.0.1".
66

67 31b43002 Bob Lantz
Currently we wrap the entire network in a 'mininet' object, which
68
constructs a simulated network based on a network topology created
69 80be5642 Bob Lantz
using a topology object (e.g. LinearTopo) from mininet.topo or
70
mininet.topolib, and a Controller which the switches will connect
71
to. Several configuration options are provided for functions such as
72 31b43002 Bob Lantz
automatically setting MAC addresses, populating the ARP table, or
73 15b482e3 Brandon Heller
even running a set of terminals to allow direct interaction with nodes.
74 31b43002 Bob Lantz

75 80be5642 Bob Lantz
After the network is created, it can be started using start(), and a
76
variety of useful tasks maybe performed, including basic connectivity
77
and bandwidth tests and running the mininet CLI.
78 31b43002 Bob Lantz

79
Once the network is up and running, test code can easily get access
80 80be5642 Bob Lantz
to host and switch objects which can then be used for arbitrary
81
experiments, typically involving running a series of commands on the
82
hosts.
83 31b43002 Bob Lantz

84
After all desired tests or activities have been completed, the stop()
85
method may be called to shut down the network.
86 281f6e59 Bob Lantz

87
"""
88
89 8b5062a3 Brandon Heller
import os
90
import re
91 ec7b211c Bob Lantz
import select
92 8a034f4f Brandon Heller
import signal
93 98d4f189 Bob Lantz
from time import sleep
94
95 496b5f9e Bob Lantz
from mininet.cli import CLI
96 cdeaca86 Brandon Heller
from mininet.log import info, error, debug, output
97 84a91a14 Bob Lantz
from mininet.node import Host, OVSKernelSwitch, Controller
98 a6bcad8f Bob Lantz
from mininet.link import Link
99 8b5062a3 Brandon Heller
from mininet.util import quietRun, fixLimits
100 5a8bb489 Bob Lantz
from mininet.util import macColonHex, ipStr, ipParse, netParse, ipAdd
101 15b482e3 Brandon Heller
from mininet.term import cleanUpScreens, makeTerms
102 8b5062a3 Brandon Heller
103 80a8fa62 Bob Lantz
class Mininet( object ):
104
    "Network emulation with hosts spawned in network namespaces."
105
106 eaf5888a Bob Lantz
    def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
107 14ff3ad3 Bob Lantz
                 controller=Controller, link=Link, intf=None,
108 82f483f5 Bob Lantz
                 build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
109 80a8fa62 Bob Lantz
                 inNamespace=False,
110 ccca871a Brandon Heller
                 autoSetMacs=False, autoStaticArp=False, listenPort=None ):
111 80a8fa62 Bob Lantz
        """Create Mininet object.
112 019bff82 Bob Lantz
           topo: Topo (topology) object or None
113 a6bcad8f Bob Lantz
           switch: default Switch class
114
           host: default Host class/constructor
115
           controller: default Controller class/constructor
116
           link: default Link class/constructor
117 216a4b7c Bob Lantz
           intf: default Intf class/constructor
118 84a91a14 Bob Lantz
           ipBase: base IP address for hosts,
119 956bf4f2 Brandon Heller
           build: build now from topo?
120 80a8fa62 Bob Lantz
           xterms: if build now, spawn xterms?
121
           cleanup: if build now, cleanup before creating?
122
           inNamespace: spawn switches and controller in net namespaces?
123 84a91a14 Bob Lantz
           autoSetMacs: set MAC addrs from topo dpid?
124 ccca871a Brandon Heller
           autoStaticArp: set all-pairs static MAC addrs?
125
           listenPort: base listening port to open; will be incremented for
126
               each additional switch in the net if inNamespace=False"""
127 216a4b7c Bob Lantz
        self.topo = topo
128 8b5062a3 Brandon Heller
        self.switch = switch
129
        self.host = host
130
        self.controller = controller
131 a6bcad8f Bob Lantz
        self.link = link
132 216a4b7c Bob Lantz
        self.intf = intf
133 82f483f5 Bob Lantz
        self.ipBase = ipBase
134 5a8bb489 Bob Lantz
        self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
135
        self.nextIP = 1  # start for address allocation
136 80a8fa62 Bob Lantz
        self.inNamespace = inNamespace
137 376bcba4 Brandon Heller
        self.xterms = xterms
138
        self.cleanup = cleanup
139 80a8fa62 Bob Lantz
        self.autoSetMacs = autoSetMacs
140
        self.autoStaticArp = autoStaticArp
141 ccca871a Brandon Heller
        self.listenPort = listenPort
142 8b5062a3 Brandon Heller
143 019bff82 Bob Lantz
        self.hosts = []
144
        self.switches = []
145
        self.controllers = []
146 84a91a14 Bob Lantz
147 cf6f6704 Bob Lantz
        self.nameToNode = {}  # name to Node (Host/Switch) objects
148 84a91a14 Bob Lantz
149 cf6f6704 Bob Lantz
        self.terms = []  # list of spawned xterm processes
150 8a034f4f Brandon Heller
151 8e3699ec Bob Lantz
        Mininet.init()  # Initialize Mininet if necessary
152 b055728f Brandon Heller
153 99c035d9 Bob Lantz
        self.built = False
154
        if topo and build:
155 d44a5843 Bob Lantz
            self.build()
156 8b5062a3 Brandon Heller
157 5a8bb489 Bob Lantz
    def addHost( self, name, cls=None, **params ):
158 80a8fa62 Bob Lantz
        """Add host.
159 019bff82 Bob Lantz
           name: name of host to add
160 5a8bb489 Bob Lantz
           cls: custom host class/constructor (optional)
161 84a91a14 Bob Lantz
           params: parameters for host
162 019bff82 Bob Lantz
           returns: added host"""
163 5a8bb489 Bob Lantz
        # Default IP and MAC addresses
164
        defaults = { 'ip': ipAdd( self.nextIP,
165
                                  ipBaseNum=self.ipBaseNum,
166
                                  prefixLen=self.prefixLen ) }
167
        if self.autoSetMacs:
168
            defaults[ 'mac'] = macColonHex( self.nextIP )
169
        self.nextIP += 1
170
        defaults.update( params )
171
        if not cls:
172 9005ce32 Bob Lantz
            cls = self.host
173 5a8bb489 Bob Lantz
        h = cls( name, **defaults )
174 a6bcad8f Bob Lantz
        self.hosts.append( h )
175
        self.nameToNode[ name ] = h
176
        return h
177
178 5a8bb489 Bob Lantz
    def addSwitch( self, name, cls=None, **params ):
179 80a8fa62 Bob Lantz
        """Add switch.
180 019bff82 Bob Lantz
           name: name of switch to add
181 5a8bb489 Bob Lantz
           cls: custom switch class/constructor (optional)
182 ccca871a Brandon Heller
           returns: added switch
183 84a91a14 Bob Lantz
           side effect: increments listenPort ivar ."""
184 14ff3ad3 Bob Lantz
        defaults = { 'listenPort': self.listenPort,
185 a6bcad8f Bob Lantz
                     'inNamespace': self.inNamespace }
186 84a91a14 Bob Lantz
        defaults.update( params )
187 5a8bb489 Bob Lantz
        if not cls:
188
            cls = self.switch
189
        sw = cls( name, **defaults )
190 0a9358c9 Brandon Heller
        if not self.inNamespace and self.listenPort:
191 ccca871a Brandon Heller
            self.listenPort += 1
192 019bff82 Bob Lantz
        self.switches.append( sw )
193
        self.nameToNode[ name ] = sw
194
        return sw
195 80a8fa62 Bob Lantz
196 a6bcad8f Bob Lantz
    def addController( self, name='c0', controller=None, **params ):
197 80a8fa62 Bob Lantz
        """Add controller.
198 e30f2c99 Bob Lantz
           controller: Controller class"""
199
        if not controller:
200
            controller = self.controller
201 a6bcad8f Bob Lantz
        controller_new = controller( name, **params )
202 f32a5468 Brandon Heller
        if controller_new:  # allow controller-less setups
203
            self.controllers.append( controller_new )
204 82b72072 Bob Lantz
            self.nameToNode[ name ] = controller_new
205 eaf5888a Bob Lantz
        return controller_new
206 8b5062a3 Brandon Heller
207 9005ce32 Bob Lantz
    def addLink( self, src, dst, srcPort=None, dstPort=None,
208 5a8bb489 Bob Lantz
                 cls=None, **params ):
209
        "Add a link from topo"
210
        if self.intf and not 'intf' in params:
211
            params[ 'intf' ] = self.intf
212
        if not cls:
213
            cls = self.link
214
        return cls( src, dst, srcPort, dstPort, **params )
215
216 80be5642 Bob Lantz
    def configHosts( self ):
217 80a8fa62 Bob Lantz
        "Configure a set of hosts."
218 019bff82 Bob Lantz
        for host in self.hosts:
219 216a4b7c Bob Lantz
            info( host.name + ' ' )
220 84a91a14 Bob Lantz
            host.configDefault( defaultRoute=host.defaultIntf )
221 8b5062a3 Brandon Heller
            # You're low priority, dude!
222 84a91a14 Bob Lantz
            # BL: do we want to do this here or not?
223
            # May not make sense if we have CPU lmiting...
224
            # quietRun( 'renice +18 -p ' + repr( host.pid ) )
225 d5886525 Bob Lantz
        info( '\n' )
226 80a8fa62 Bob Lantz
227 84a91a14 Bob Lantz
    def buildFromTopo( self, topo=None ):
228 019bff82 Bob Lantz
        """Build mininet from a topology object
229 80a8fa62 Bob Lantz
           At the end of this function, everything should be connected
230
           and up."""
231 d44a5843 Bob Lantz
232
        # Possibly we should clean up here and/or validate
233
        # the topo
234 376bcba4 Brandon Heller
        if self.cleanup:
235 d44a5843 Bob Lantz
            pass
236
237 d5886525 Bob Lantz
        info( '*** Creating network\n' )
238 84a91a14 Bob Lantz
239
        if not self.controllers:
240
            # Add a default controller
241
            info( '*** Adding controller\n' )
242
            self.addController( 'c0' )
243
244 d5886525 Bob Lantz
        info( '*** Adding hosts:\n' )
245 5a8bb489 Bob Lantz
        for hostName in topo.hosts():
246
            self.addHost( hostName, **topo.nodeInfo( hostName ) )
247
            info( hostName + ' ' )
248 84a91a14 Bob Lantz
249 d5886525 Bob Lantz
        info( '\n*** Adding switches:\n' )
250 5a8bb489 Bob Lantz
        for switchName in topo.switches():
251
            self.addSwitch( switchName, **topo.nodeInfo( switchName) )
252
            info( switchName + ' ' )
253 84a91a14 Bob Lantz
254 724f1144 Bob Lantz
        info( '\n*** Adding links:\n' )
255 5a8bb489 Bob Lantz
        for srcName, dstName in topo.links(sort=True):
256
            src, dst = self.nameToNode[ srcName ], self.nameToNode[ dstName ]
257
            srcPort, dstPort = topo.port( srcName, dstName )
258
            self.addLink( src, dst, srcPort, dstPort,
259
                          **topo.linkInfo( srcName, dstName ) )
260
            info( '(%s, %s) ' % ( src.name, dst.name ) )
261 84a91a14 Bob Lantz
262 d5886525 Bob Lantz
        info( '\n' )
263 80a8fa62 Bob Lantz
264 84a91a14 Bob Lantz
    def configureControlNetwork( self ):
265 14ff3ad3 Bob Lantz
        "Control net config hook: override in subclass"
266
        raise Exception( 'configureControlNetwork: '
267
               'should be overriden in subclass', self )
268 84a91a14 Bob Lantz
269 d44a5843 Bob Lantz
    def build( self ):
270
        "Build mininet."
271
        if self.topo:
272
            self.buildFromTopo( self.topo )
273 14ff3ad3 Bob Lantz
        if ( self.inNamespace ):
274 d44a5843 Bob Lantz
            self.configureControlNetwork()
275 d5886525 Bob Lantz
        info( '*** Configuring hosts\n' )
276 80be5642 Bob Lantz
        self.configHosts()
277 376bcba4 Brandon Heller
        if self.xterms:
278 15b482e3 Brandon Heller
            self.startTerms()
279 80a8fa62 Bob Lantz
        if self.autoStaticArp:
280
            self.staticArp()
281 99c035d9 Bob Lantz
        self.built = True
282 80a8fa62 Bob Lantz
283 15b482e3 Brandon Heller
    def startTerms( self ):
284
        "Start a terminal for each node."
285
        info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
286 8a034f4f Brandon Heller
        cleanUpScreens()
287 15b482e3 Brandon Heller
        self.terms += makeTerms( self.controllers, 'controller' )
288
        self.terms += makeTerms( self.switches, 'switch' )
289
        self.terms += makeTerms( self.hosts, 'host' )
290 8a034f4f Brandon Heller
291 80a8fa62 Bob Lantz
    def stopXterms( self ):
292
        "Kill each xterm."
293 8a034f4f Brandon Heller
        for term in self.terms:
294 80a8fa62 Bob Lantz
            os.kill( term.pid, signal.SIGKILL )
295 8a034f4f Brandon Heller
        cleanUpScreens()
296 8b5062a3 Brandon Heller
297 80a8fa62 Bob Lantz
    def staticArp( self ):
298
        "Add all-pairs ARP entries to remove the need to handle broadcast."
299 019bff82 Bob Lantz
        for src in self.hosts:
300
            for dst in self.hosts:
301 376bcba4 Brandon Heller
                if src != dst:
302 1bda2d21 Bob Lantz
                    src.setARP( ip=dst.IP(), mac=dst.MAC() )
303 376bcba4 Brandon Heller
304 80a8fa62 Bob Lantz
    def start( self ):
305 99c035d9 Bob Lantz
        "Start controller and switches."
306
        if not self.built:
307
            self.build()
308 d5886525 Bob Lantz
        info( '*** Starting controller\n' )
309 019bff82 Bob Lantz
        for controller in self.controllers:
310
            controller.start()
311
        info( '*** Starting %s switches\n' % len( self.switches ) )
312
        for switch in self.switches:
313 efc9a01c Bob Lantz
            info( switch.name + ' ')
314 80a8fa62 Bob Lantz
            switch.start( self.controllers )
315 d5886525 Bob Lantz
        info( '\n' )
316 80a8fa62 Bob Lantz
317
    def stop( self ):
318 d5886525 Bob Lantz
        "Stop the controller(s), switches and hosts"
319 8a034f4f Brandon Heller
        if self.terms:
320 d5886525 Bob Lantz
            info( '*** Stopping %i terms\n' % len( self.terms ) )
321 80a8fa62 Bob Lantz
            self.stopXterms()
322 019bff82 Bob Lantz
        info( '*** Stopping %i hosts\n' % len( self.hosts ) )
323
        for host in self.hosts:
324 84a91a14 Bob Lantz
            info( host.name + ' ' )
325 8b5062a3 Brandon Heller
            host.terminate()
326 d5886525 Bob Lantz
        info( '\n' )
327 019bff82 Bob Lantz
        info( '*** Stopping %i switches\n' % len( self.switches ) )
328
        for switch in self.switches:
329 84a91a14 Bob Lantz
            info( switch.name + ' ' )
330 8b5062a3 Brandon Heller
            switch.stop()
331 d5886525 Bob Lantz
        info( '\n' )
332 019bff82 Bob Lantz
        info( '*** Stopping %i controllers\n' % len( self.controllers ) )
333
        for controller in self.controllers:
334 84a91a14 Bob Lantz
            info( controller.name + ' ' )
335 019bff82 Bob Lantz
            controller.stop()
336 84a91a14 Bob Lantz
        info( '\n*** Done\n' )
337 8b5062a3 Brandon Heller
338 67516aa4 Bob Lantz
    def run( self, test, *args, **kwargs ):
339 80a8fa62 Bob Lantz
        "Perform a complete start/test/stop cycle."
340 8b5062a3 Brandon Heller
        self.start()
341 d5886525 Bob Lantz
        info( '*** Running test\n' )
342 67516aa4 Bob Lantz
        result = test( *args, **kwargs )
343 8b5062a3 Brandon Heller
        self.stop()
344
        return result
345
346 259d7133 Bob Lantz
    def monitor( self, hosts=None, timeoutms=-1 ):
347 ec7b211c Bob Lantz
        """Monitor a set of hosts (or all hosts by default),
348
           and return their output, a line at a time.
349 259d7133 Bob Lantz
           hosts: (optional) set of hosts to monitor
350
           timeoutms: (optional) timeout value in ms
351
           returns: iterator which returns host, line"""
352 ec7b211c Bob Lantz
        if hosts is None:
353
            hosts = self.hosts
354
        poller = select.poll()
355 cf6f6704 Bob Lantz
        Node = hosts[ 0 ]  # so we can call class method fdToNode
356 ec7b211c Bob Lantz
        for host in hosts:
357
            poller.register( host.stdout )
358
        while True:
359 259d7133 Bob Lantz
            ready = poller.poll( timeoutms )
360 ec7b211c Bob Lantz
            for fd, event in ready:
361
                host = Node.fdToNode( fd )
362
                if event & select.POLLIN:
363
                    line = host.readline()
364 259d7133 Bob Lantz
                    if line is not None:
365 ec7b211c Bob Lantz
                        yield host, line
366 259d7133 Bob Lantz
            # Return if non-blocking
367
            if not ready and timeoutms >= 0:
368
                yield None, None
369 ec7b211c Bob Lantz
370 84a91a14 Bob Lantz
    # XXX These test methods should be moved out of this class.
371
    # Probably we should create a tests.py for them
372
373 8b5062a3 Brandon Heller
    @staticmethod
374 80a8fa62 Bob Lantz
    def _parsePing( pingOutput ):
375
        "Parse ping output and return packets sent, received."
376 3fac5a43 Brandon Heller
        # Check for downed link
377
        if 'connect: Network is unreachable' in pingOutput:
378
            return (1, 0)
379 8b5062a3 Brandon Heller
        r = r'(\d+) packets transmitted, (\d+) received'
380 80a8fa62 Bob Lantz
        m = re.search( r, pingOutput )
381 8b5062a3 Brandon Heller
        if m == None:
382 d5886525 Bob Lantz
            error( '*** Error: could not parse ping output: %s\n' %
383 80a8fa62 Bob Lantz
                     pingOutput )
384 3fac5a43 Brandon Heller
            return (1, 0)
385 80a8fa62 Bob Lantz
        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
386 8b5062a3 Brandon Heller
        return sent, received
387
388 80a8fa62 Bob Lantz
    def ping( self, hosts=None ):
389
        """Ping between all specified hosts.
390 019bff82 Bob Lantz
           hosts: list of hosts
391 80a8fa62 Bob Lantz
           returns: ploss packet loss percentage"""
392 d44a5843 Bob Lantz
        # should we check if running?
393 8b5062a3 Brandon Heller
        packets = 0
394
        lost = 0
395
        ploss = None
396
        if not hosts:
397 019bff82 Bob Lantz
            hosts = self.hosts
398 cdeaca86 Brandon Heller
            output( '*** Ping: testing ping reachability\n' )
399 019bff82 Bob Lantz
        for node in hosts:
400 cdeaca86 Brandon Heller
            output( '%s -> ' % node.name )
401 019bff82 Bob Lantz
            for dest in hosts:
402 8b5062a3 Brandon Heller
                if node != dest:
403 80a8fa62 Bob Lantz
                    result = node.cmd( 'ping -c1 ' + dest.IP() )
404
                    sent, received = self._parsePing( result )
405 8b5062a3 Brandon Heller
                    packets += sent
406
                    if received > sent:
407 d5886525 Bob Lantz
                        error( '*** Error: received too many packets' )
408
                        error( '%s' % result )
409 80a8fa62 Bob Lantz
                        node.cmdPrint( 'route' )
410
                        exit( 1 )
411 8b5062a3 Brandon Heller
                    lost += sent - received
412 cdeaca86 Brandon Heller
                    output( ( '%s ' % dest.name ) if received else 'X ' )
413
            output( '\n' )
414 8b5062a3 Brandon Heller
            ploss = 100 * lost / packets
415 cdeaca86 Brandon Heller
        output( "*** Results: %i%% dropped (%d/%d lost)\n" %
416 80a8fa62 Bob Lantz
                ( ploss, lost, packets ) )
417 8b5062a3 Brandon Heller
        return ploss
418
419 80a8fa62 Bob Lantz
    def pingAll( self ):
420
        """Ping between all hosts.
421
           returns: ploss packet loss percentage"""
422 1bb4412f Brandon Heller
        return self.ping()
423 eeb9cb3c Brandon Heller
424 80a8fa62 Bob Lantz
    def pingPair( self ):
425
        """Ping between first two hosts, useful for testing.
426
           returns: ploss packet loss percentage"""
427 019bff82 Bob Lantz
        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
428 80a8fa62 Bob Lantz
        return self.ping( hosts=hosts )
429 eeb9cb3c Brandon Heller
430
    @staticmethod
431 80a8fa62 Bob Lantz
    def _parseIperf( iperfOutput ):
432
        """Parse iperf output and return bandwidth.
433
           iperfOutput: string
434
           returns: result string"""
435 eeb9cb3c Brandon Heller
        r = r'([\d\.]+ \w+/sec)'
436 ad2fda25 Bob Lantz
        m = re.findall( r, iperfOutput )
437 eeb9cb3c Brandon Heller
        if m:
438 ad2fda25 Bob Lantz
            return m[-1]
439 eeb9cb3c Brandon Heller
        else:
440 ad2fda25 Bob Lantz
            # was: raise Exception(...)
441
            error( 'could not parse iperf output: ' + iperfOutput )
442
            return ''
443 80a8fa62 Bob Lantz
444 216a4b7c Bob Lantz
    # XXX This should be cleaned up
445
446 1a40cd04 Brandon Heller
    def iperf( self, hosts=None, l4Type='TCP', udpBw='10M' ):
447 80a8fa62 Bob Lantz
        """Run iperf between two hosts.
448 019bff82 Bob Lantz
           hosts: list of hosts; if None, uses opposite hosts
449 80a8fa62 Bob Lantz
           l4Type: string, one of [ TCP, UDP ]
450
           returns: results two-element array of server and client speeds"""
451 ad2fda25 Bob Lantz
        if not quietRun( 'which telnet' ):
452
            error( 'Cannot find telnet in $PATH - required for iperf test' )
453
            return
454 eeb9cb3c Brandon Heller
        if not hosts:
455 019bff82 Bob Lantz
            hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
456 eeb9cb3c Brandon Heller
        else:
457 80a8fa62 Bob Lantz
            assert len( hosts ) == 2
458 ec7b211c Bob Lantz
        client, server = hosts
459 cdeaca86 Brandon Heller
        output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
460 ec7b211c Bob Lantz
        output( "%s and %s\n" % ( client.name, server.name ) )
461
        server.cmd( 'killall -9 iperf' )
462 80a8fa62 Bob Lantz
        iperfArgs = 'iperf '
463
        bwArgs = ''
464
        if l4Type == 'UDP':
465
            iperfArgs += '-u '
466
            bwArgs = '-b ' + udpBw + ' '
467
        elif l4Type != 'TCP':
468
            raise Exception( 'Unexpected l4 type: %s' % l4Type )
469 ec7b211c Bob Lantz
        server.sendCmd( iperfArgs + '-s', printPid=True )
470
        servout = ''
471
        while server.lastPid is None:
472
            servout += server.monitor()
473 8856d284 Bob Lantz
        if l4Type == 'TCP':
474
            while 'Connected' not in client.cmd(
475
                'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
476
                output('waiting for iperf to start up...')
477
                sleep(.5)
478 ec7b211c Bob Lantz
        cliout = client.cmd( iperfArgs + '-t 5 -c ' + server.IP() + ' ' +
479 80a8fa62 Bob Lantz
                           bwArgs )
480 ec7b211c Bob Lantz
        debug( 'Client output: %s\n' % cliout )
481
        server.sendInt()
482
        servout += server.waitOutput()
483
        debug( 'Server output: %s\n' % servout )
484
        result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
485 80a8fa62 Bob Lantz
        if l4Type == 'UDP':
486
            result.insert( 0, udpBw )
487 cdeaca86 Brandon Heller
        output( '*** Results: %s\n' % result )
488 eeb9cb3c Brandon Heller
        return result
489
490 84a91a14 Bob Lantz
    # BL: I think this can be rewritten now that we have
491
    # a real link class.
492 c70aab0a Bob Lantz
    def configLinkStatus( self, src, dst, status ):
493
        """Change status of src <-> dst links.
494
           src: node name
495
           dst: node name
496
           status: string {up, down}"""
497 8d3c2859 Brandon Heller
        if src not in self.nameToNode:
498
            error( 'src not in network: %s\n' % src )
499
        elif dst not in self.nameToNode:
500
            error( 'dst not in network: %s\n' % dst )
501
        else:
502 8856d284 Bob Lantz
            if type( src ) is str:
503
                src = self.nameToNode[ src ]
504
            if type( dst ) is str:
505
                dst = self.nameToNode[ dst ]
506
            connections = src.connectionsTo( dst )
507 fb2f6523 Bob Lantz
            if len( connections ) == 0:
508 8d3c2859 Brandon Heller
                error( 'src and dst not connected: %s %s\n' % ( src, dst) )
509 fb2f6523 Bob Lantz
            for srcIntf, dstIntf in connections:
510 8856d284 Bob Lantz
                result = srcIntf.ifconfig( status )
511 8d3c2859 Brandon Heller
                if result:
512
                    error( 'link src status change failed: %s\n' % result )
513 8856d284 Bob Lantz
                result = dstIntf.ifconfig( status )
514 8d3c2859 Brandon Heller
                if result:
515
                    error( 'link dst status change failed: %s\n' % result )
516
517 80a8fa62 Bob Lantz
    def interact( self ):
518
        "Start network and run our simple CLI."
519 eeb9cb3c Brandon Heller
        self.start()
520 496b5f9e Bob Lantz
        result = CLI( self )
521 eeb9cb3c Brandon Heller
        self.stop()
522
        return result
523 82b72072 Bob Lantz
524 8e3699ec Bob Lantz
    inited = False
525 14ff3ad3 Bob Lantz
526 8e3699ec Bob Lantz
    @classmethod
527
    def init( cls ):
528
        "Initialize Mininet"
529
        if cls.inited:
530
            return
531
        if os.getuid() != 0:
532
            # Note: this script must be run as root
533 14ff3ad3 Bob Lantz
            # Probably we should only sudo when we need
534
            # to as per Big Switch's patch
535 8e3699ec Bob Lantz
            print "*** Mininet must run as root."
536
            exit( 1 )
537
        fixLimits()
538
        cls.inited = True
539
540 82b72072 Bob Lantz
541 84a91a14 Bob Lantz
class MininetWithControlNet( Mininet ):
542
543
    """Control network support:
544

545
       Create an explicit control network. Currently this is only
546
       used/usable with the user datapath.
547

548
       Notes:
549

550
       1. If the controller and switches are in the same (e.g. root)
551
          namespace, they can just use the loopback connection.
552

553
       2. If we can get unix domain sockets to work, we can use them
554
          instead of an explicit control network.
555

556
       3. Instead of routing, we could bridge or use 'in-band' control.
557

558
       4. Even if we dispense with this in general, it could still be
559
          useful for people who wish to simulate a separate control
560
          network (since real networks may need one!)
561

562
       5. Basically nobody ever used this code, so it has been moved
563 14ff3ad3 Bob Lantz
          into its own class.
564

565
       6. Ultimately we may wish to extend this to allow us to create a
566
          control network which every node's control interface is
567
          attached to."""
568 84a91a14 Bob Lantz
569
    def configureControlNetwork( self ):
570
        "Configure control network."
571
        self.configureRoutedControlNetwork()
572
573
    # We still need to figure out the right way to pass
574
    # in the control network location.
575
576
    def configureRoutedControlNetwork( self, ip='192.168.123.1',
577
        prefixLen=16 ):
578
        """Configure a routed control network on controller and switches.
579
           For use with the user datapath only right now."""
580
        controller = self.controllers[ 0 ]
581
        info( controller.name + ' <->' )
582
        cip = ip
583
        snum = ipParse( ip )
584
        for switch in self.switches:
585
            info( ' ' + switch.name )
586 14ff3ad3 Bob Lantz
            link = self.link( switch, controller, port1=0 )
587
            sintf, cintf = link.intf1, link.intf2
588
            switch.controlIntf = sintf
589 84a91a14 Bob Lantz
            snum += 1
590
            while snum & 0xff in [ 0, 255 ]:
591
                snum += 1
592
            sip = ipStr( snum )
593 14ff3ad3 Bob Lantz
            cintf.setIP( cip, prefixLen )
594
            sintf.setIP( sip, prefixLen )
595 84a91a14 Bob Lantz
            controller.setHostRoute( sip, cintf )
596
            switch.setHostRoute( cip, sintf )
597
        info( '\n' )
598
        info( '*** Testing control network\n' )
599 14ff3ad3 Bob Lantz
        while not cintf.isUp():
600 84a91a14 Bob Lantz
            info( '*** Waiting for', cintf, 'to come up\n' )
601
            sleep( 1 )
602
        for switch in self.switches:
603 14ff3ad3 Bob Lantz
            while not sintf.isUp():
604 84a91a14 Bob Lantz
                info( '*** Waiting for', sintf, 'to come up\n' )
605
                sleep( 1 )
606
            if self.ping( hosts=[ switch, controller ] ) != 0:
607
                error( '*** Error: control network test failed\n' )
608
                exit( 1 )
609
        info( '\n' )