Statistics
| Branch: | Tag: | Revision:

mininet / mininet / net.py @ 089e8130

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

575
       Create an explicit control network. Currently this is only
576
       used/usable with the user datapath.
577

578
       Notes:
579

580
       1. If the controller and switches are in the same (e.g. root)
581
          namespace, they can just use the loopback connection.
582

583
       2. If we can get unix domain sockets to work, we can use them
584
          instead of an explicit control network.
585

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

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

592
       5. Basically nobody ever used this code, so it has been moved
593 14ff3ad3 Bob Lantz
          into its own class.
594

595
       6. Ultimately we may wish to extend this to allow us to create a
596
          control network which every node's control interface is
597
          attached to."""
598 84a91a14 Bob Lantz
599
    def configureControlNetwork( self ):
600
        "Configure control network."
601
        self.configureRoutedControlNetwork()
602
603
    # We still need to figure out the right way to pass
604
    # in the control network location.
605
606
    def configureRoutedControlNetwork( self, ip='192.168.123.1',
607
        prefixLen=16 ):
608
        """Configure a routed control network on controller and switches.
609
           For use with the user datapath only right now."""
610
        controller = self.controllers[ 0 ]
611
        info( controller.name + ' <->' )
612
        cip = ip
613
        snum = ipParse( ip )
614
        for switch in self.switches:
615
            info( ' ' + switch.name )
616 14ff3ad3 Bob Lantz
            link = self.link( switch, controller, port1=0 )
617
            sintf, cintf = link.intf1, link.intf2
618
            switch.controlIntf = sintf
619 84a91a14 Bob Lantz
            snum += 1
620
            while snum & 0xff in [ 0, 255 ]:
621
                snum += 1
622
            sip = ipStr( snum )
623 14ff3ad3 Bob Lantz
            cintf.setIP( cip, prefixLen )
624
            sintf.setIP( sip, prefixLen )
625 84a91a14 Bob Lantz
            controller.setHostRoute( sip, cintf )
626
            switch.setHostRoute( cip, sintf )
627
        info( '\n' )
628
        info( '*** Testing control network\n' )
629 14ff3ad3 Bob Lantz
        while not cintf.isUp():
630 84a91a14 Bob Lantz
            info( '*** Waiting for', cintf, 'to come up\n' )
631
            sleep( 1 )
632
        for switch in self.switches:
633 14ff3ad3 Bob Lantz
            while not sintf.isUp():
634 84a91a14 Bob Lantz
                info( '*** Waiting for', sintf, 'to come up\n' )
635
                sleep( 1 )
636
            if self.ping( hosts=[ switch, controller ] ) != 0:
637
                error( '*** Error: control network test failed\n' )
638
                exit( 1 )
639
        info( '\n' )