Statistics
| Branch: | Tag: | Revision:

mininet / mininet / net.py @ 21b2c2c4

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

588
       Create an explicit control network. Currently this is only
589
       used/usable with the user datapath.
590

591
       Notes:
592

593
       1. If the controller and switches are in the same (e.g. root)
594
          namespace, they can just use the loopback connection.
595

596
       2. If we can get unix domain sockets to work, we can use them
597
          instead of an explicit control network.
598

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

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

605
       5. Basically nobody ever used this code, so it has been moved
606 14ff3ad3 Bob Lantz
          into its own class.
607

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