Statistics
| Branch: | Tag: | Revision:

mininet / mininet / net.py @ 2e3258d2

History | View | Annotate | Download (36.3 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 eba13f0c cody burkard
import random
94 50774e40 Bob Lantz
95 98d4f189 Bob Lantz
from time import sleep
96 876e66e5 Rich Lane
from itertools import chain, groupby
97 ce781a18 cody burkard
from math import ceil
98 98d4f189 Bob Lantz
99 496b5f9e Bob Lantz
from mininet.cli import CLI
100 84ea8d7f Cody Burkard
from mininet.log import info, error, debug, output, warn
101 b1ec912d Bob Lantz
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
102 7a3159c9 Bob Lantz
                           Controller )
103 4015e066 Brian O'Connor
from mininet.nodelib import NAT
104 e8146dd1 Bob Lantz
from mininet.link import Link, Intf
105 b2fe0778 Bob Lantz
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
106
                           macColonHex, ipStr, ipParse, netParse, ipAdd,
107
                           waitListening )
108 15b482e3 Brandon Heller
from mininet.term import cleanUpScreens, makeTerms
109 8b5062a3 Brandon Heller
110 39128f8c Bob Lantz
# Mininet version: should be consistent with README and LICENSE
111 ab8c4e91 Bob Lantz
VERSION = "2.2.1"
112 39128f8c Bob Lantz
113 80a8fa62 Bob Lantz
class Mininet( object ):
114
    "Network emulation with hosts spawned in network namespaces."
115
116 eaf5888a Bob Lantz
    def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
117 a19cc915 Cody Burkard
                  controller=DefaultController, link=Link, intf=Intf,
118 edf60032 Brandon Heller
                  build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
119
                  inNamespace=False,
120
                  autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
121 84ea8d7f Cody Burkard
                  listenPort=None, waitConnected=False ):
122 80a8fa62 Bob Lantz
        """Create Mininet object.
123 019bff82 Bob Lantz
           topo: Topo (topology) object or None
124 a6bcad8f Bob Lantz
           switch: default Switch class
125
           host: default Host class/constructor
126
           controller: default Controller class/constructor
127
           link: default Link class/constructor
128 216a4b7c Bob Lantz
           intf: default Intf class/constructor
129 84a91a14 Bob Lantz
           ipBase: base IP address for hosts,
130 956bf4f2 Brandon Heller
           build: build now from topo?
131 80a8fa62 Bob Lantz
           xterms: if build now, spawn xterms?
132
           cleanup: if build now, cleanup before creating?
133
           inNamespace: spawn switches and controller in net namespaces?
134 197b083f Bob Lantz
           autoSetMacs: set MAC addrs automatically like IP addresses?
135 ccca871a Brandon Heller
           autoStaticArp: set all-pairs static MAC addrs?
136 197b083f Bob Lantz
           autoPinCpus: pin hosts to (real) cores (requires CPULimitedHost)?
137 ccca871a Brandon Heller
           listenPort: base listening port to open; will be incremented for
138 3f2355a3 Brian O'Connor
               each additional switch in the net if inNamespace=False"""
139 216a4b7c Bob Lantz
        self.topo = topo
140 8b5062a3 Brandon Heller
        self.switch = switch
141
        self.host = host
142
        self.controller = controller
143 a6bcad8f Bob Lantz
        self.link = link
144 216a4b7c Bob Lantz
        self.intf = intf
145 82f483f5 Bob Lantz
        self.ipBase = ipBase
146 5a8bb489 Bob Lantz
        self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
147
        self.nextIP = 1  # start for address allocation
148 80a8fa62 Bob Lantz
        self.inNamespace = inNamespace
149 376bcba4 Brandon Heller
        self.xterms = xterms
150
        self.cleanup = cleanup
151 80a8fa62 Bob Lantz
        self.autoSetMacs = autoSetMacs
152
        self.autoStaticArp = autoStaticArp
153 197b083f Bob Lantz
        self.autoPinCpus = autoPinCpus
154
        self.numCores = numCores()
155
        self.nextCore = 0  # next core for pinning hosts to CPUs
156 ccca871a Brandon Heller
        self.listenPort = listenPort
157 c23c992f Cody Burkard
        self.waitConn = waitConnected
158 8b5062a3 Brandon Heller
159 019bff82 Bob Lantz
        self.hosts = []
160
        self.switches = []
161
        self.controllers = []
162 c265deed Bob Lantz
        self.links = []
163 84a91a14 Bob Lantz
164 cf6f6704 Bob Lantz
        self.nameToNode = {}  # name to Node (Host/Switch) objects
165 84a91a14 Bob Lantz
166 cf6f6704 Bob Lantz
        self.terms = []  # list of spawned xterm processes
167 8a034f4f Brandon Heller
168 8e3699ec Bob Lantz
        Mininet.init()  # Initialize Mininet if necessary
169 b055728f Brandon Heller
170 99c035d9 Bob Lantz
        self.built = False
171
        if topo and build:
172 d44a5843 Bob Lantz
            self.build()
173 8b5062a3 Brandon Heller
174 4794871a Bob Lantz
    def waitConnected( self, timeout=None, delay=.5 ):
175 84ea8d7f Cody Burkard
        """wait for each switch to connect to a controller,
176
           up to 5 seconds
177 4797b420 Cody Burkard
           timeout: time to wait, or None to wait indefinitely
178 4794871a Bob Lantz
           delay: seconds to sleep per iteration
179 84ea8d7f Cody Burkard
           returns: True if all switches are connected"""
180 13d25b41 Bob Lantz
        info( '*** Waiting for switches to connect\n' )
181 84ea8d7f Cody Burkard
        time = 0
182 708b1843 Bob Lantz
        remaining = list( self.switches )
183 4794871a Bob Lantz
        while True:
184 708b1843 Bob Lantz
            for switch in tuple( remaining ):
185 4794871a Bob Lantz
                if switch.connected():
186
                    info( '%s ' % switch )
187 8e2443ad Cody Burkard
                    remaining.remove( switch )
188 4794871a Bob Lantz
            if not remaining:
189
                info( '\n' )
190
                return True
191
            if time > timeout and timeout is not None:
192 84ea8d7f Cody Burkard
                break
193 4794871a Bob Lantz
            sleep( delay )
194
            time += delay
195
        warn( 'Timed out after %d seconds\n' % time )
196
        for switch in remaining:
197
            if not switch.connected():
198
                warn( 'Warning: %s is not connected to a controller\n'
199
                      % switch.name )
200
            else:
201
                remaining.remove( switch )
202
        return not remaining
203 84ea8d7f Cody Burkard
204 5a8bb489 Bob Lantz
    def addHost( self, name, cls=None, **params ):
205 80a8fa62 Bob Lantz
        """Add host.
206 019bff82 Bob Lantz
           name: name of host to add
207 5a8bb489 Bob Lantz
           cls: custom host class/constructor (optional)
208 84a91a14 Bob Lantz
           params: parameters for host
209 019bff82 Bob Lantz
           returns: added host"""
210 5a8bb489 Bob Lantz
        # Default IP and MAC addresses
211
        defaults = { 'ip': ipAdd( self.nextIP,
212
                                  ipBaseNum=self.ipBaseNum,
213 0f832c92 Bob Lantz
                                  prefixLen=self.prefixLen ) +
214
                                  '/%s' % self.prefixLen }
215 5a8bb489 Bob Lantz
        if self.autoSetMacs:
216 47b9466f Brian O'Connor
            defaults[ 'mac' ] = macColonHex( self.nextIP )
217 197b083f Bob Lantz
        if self.autoPinCpus:
218
            defaults[ 'cores' ] = self.nextCore
219
            self.nextCore = ( self.nextCore + 1 ) % self.numCores
220 5a8bb489 Bob Lantz
        self.nextIP += 1
221
        defaults.update( params )
222
        if not cls:
223 9005ce32 Bob Lantz
            cls = self.host
224 5a8bb489 Bob Lantz
        h = cls( name, **defaults )
225 a6bcad8f Bob Lantz
        self.hosts.append( h )
226
        self.nameToNode[ name ] = h
227
        return h
228
229 5a8bb489 Bob Lantz
    def addSwitch( self, name, cls=None, **params ):
230 80a8fa62 Bob Lantz
        """Add switch.
231 019bff82 Bob Lantz
           name: name of switch to add
232 5a8bb489 Bob Lantz
           cls: custom switch class/constructor (optional)
233 ccca871a Brandon Heller
           returns: added switch
234 84a91a14 Bob Lantz
           side effect: increments listenPort ivar ."""
235 14ff3ad3 Bob Lantz
        defaults = { 'listenPort': self.listenPort,
236 a6bcad8f Bob Lantz
                     'inNamespace': self.inNamespace }
237 84a91a14 Bob Lantz
        defaults.update( params )
238 5a8bb489 Bob Lantz
        if not cls:
239
            cls = self.switch
240
        sw = cls( name, **defaults )
241 0a9358c9 Brandon Heller
        if not self.inNamespace and self.listenPort:
242 ccca871a Brandon Heller
            self.listenPort += 1
243 019bff82 Bob Lantz
        self.switches.append( sw )
244
        self.nameToNode[ name ] = sw
245
        return sw
246 80a8fa62 Bob Lantz
247 a6bcad8f Bob Lantz
    def addController( self, name='c0', controller=None, **params ):
248 80a8fa62 Bob Lantz
        """Add controller.
249 e30f2c99 Bob Lantz
           controller: Controller class"""
250 35029978 Bob Lantz
        # Get controller class
251 e30f2c99 Bob Lantz
        if not controller:
252
            controller = self.controller
253 35029978 Bob Lantz
        # Construct new controller if one is not given
254 e183e699 Bob Lantz
        if isinstance( name, Controller ):
255 15146d90 Brian O'Connor
            controller_new = name
256 35029978 Bob Lantz
            # Pylint thinks controller is a str()
257 061598f0 Bob Lantz
            # pylint: disable=maybe-no-member
258 15146d90 Brian O'Connor
            name = controller_new.name
259 061598f0 Bob Lantz
            # pylint: enable=maybe-no-member
260 15146d90 Brian O'Connor
        else:
261
            controller_new = controller( name, **params )
262 35029978 Bob Lantz
        # Add new controller to net
263 7a3159c9 Bob Lantz
        if controller_new:  # allow controller-less setups
264 f32a5468 Brandon Heller
            self.controllers.append( controller_new )
265 82b72072 Bob Lantz
            self.nameToNode[ name ] = controller_new
266 eaf5888a Bob Lantz
        return controller_new
267 e8146dd1 Bob Lantz
268 b1ec912d Bob Lantz
    def addNAT( self, name='nat0', connect=True, inNamespace=False,
269
                **params):
270
        """Add a NAT to the Mininet network
271
           name: name of NAT node
272
           connect: switch to connect to | True (s1) | None
273
           inNamespace: create in a network namespace
274
           params: other NAT node params, notably:
275
               ip: used as default gateway address"""
276 aabbf295 Bob Lantz
        nat = self.addHost( name, cls=NAT, inNamespace=inNamespace,
277 ffeb16eb Brian O'Connor
                            subnet=self.ipBase, **params )
278 47b9466f Brian O'Connor
        # find first switch and create link
279
        if connect:
280 b1ec912d Bob Lantz
            if not isinstance( connect, Node ):
281
                # Use first switch if not specified
282
                connect = self.switches[ 0 ]
283
            # Connect the nat to the switch
284 0298e9be Rahman Pujianto
            self.addLink( nat, connect )
285 b1ec912d Bob Lantz
            # Set the default route on hosts
286 ffeb16eb Brian O'Connor
            natIP = nat.params[ 'ip' ].split('/')[ 0 ]
287
            for host in self.hosts:
288
                if host.inNamespace:
289
                    host.setDefaultRoute( 'via %s' % natIP )
290 47b9466f Brian O'Connor
        return nat
291
292 bd558875 Bob Lantz
    # BL: We now have four ways to look up nodes
293
    # This may (should?) be cleaned up in the future.
294 548580d8 Bob Lantz
    def getNodeByName( self, *args ):
295
        "Return node(s) with given name(s)"
296
        if len( args ) == 1:
297
            return self.nameToNode[ args[ 0 ] ]
298
        return [ self.nameToNode[ n ] for n in args ]
299 8b5062a3 Brandon Heller
300 089e8130 Bob Lantz
    def get( self, *args ):
301
        "Convenience alias for getNodeByName"
302
        return self.getNodeByName( *args )
303
304 bd558875 Bob Lantz
    # Even more convenient syntax for node lookup and iteration
305 15146d90 Brian O'Connor
    def __getitem__( self, key ):
306 bd558875 Bob Lantz
        """net [ name ] operator: Return node(s) with given name(s)"""
307 15146d90 Brian O'Connor
        return self.nameToNode[ key ]
308 bd558875 Bob Lantz
309
    def __iter__( self ):
310 09b12391 Brian O'Connor
        "return iterator over node names"
311 9281719d Brian O'Connor
        for node in chain( self.hosts, self.switches, self.controllers ):
312
            yield node.name
313 bd558875 Bob Lantz
314 8e04a9f8 Brian O'Connor
    def __len__( self ):
315 9281719d Brian O'Connor
        "returns number of nodes in net"
316 35029978 Bob Lantz
        return ( len( self.hosts ) + len( self.switches ) +
317
                 len( self.controllers ) )
318 8e04a9f8 Brian O'Connor
319
    def __contains__( self, item ):
320 9281719d Brian O'Connor
        "returns True if net contains named node"
321 15146d90 Brian O'Connor
        return item in self.nameToNode
322 8e04a9f8 Brian O'Connor
323
    def keys( self ):
324 9281719d Brian O'Connor
        "return a list of all node names or net's keys"
325 15146d90 Brian O'Connor
        return list( self )
326 8e04a9f8 Brian O'Connor
327
    def values( self ):
328 9281719d Brian O'Connor
        "return a list of all nodes or net's values"
329 15146d90 Brian O'Connor
        return [ self[name] for name in self ]
330 8e04a9f8 Brian O'Connor
331
    def items( self ):
332 9281719d Brian O'Connor
        "return (key,value) tuple list for every node in net"
333 8e04a9f8 Brian O'Connor
        return zip( self.keys(), self.values() )
334
335 89fb0819 Bob Lantz
    @staticmethod
336
    def randMac():
337
        "Return a random, non-multicast MAC address"
338 7a3159c9 Bob Lantz
        return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff |
339 2a2d6050 Bob Lantz
                            0x020000000000 )
340 aabbf295 Bob Lantz
341 e8146dd1 Bob Lantz
    def addLink( self, node1, node2, port1=None, port2=None,
342 2a2d6050 Bob Lantz
                 cls=None, **params ):
343 e8146dd1 Bob Lantz
        """"Add a link from node1 to node2
344 2a2d6050 Bob Lantz
            node1: source node (or name)
345
            node2: dest node (or name)
346 89fb0819 Bob Lantz
            port1: source port (optional)
347
            port2: dest port (optional)
348
            cls: link class (optional)
349
            params: additional link params (optional)
350 e8146dd1 Bob Lantz
            returns: link object"""
351 c92c4efb Bob Lantz
        # Accept node objects or names
352 9a8bdfd7 Bob Lantz
        node1 = node1 if not isinstance( node1, basestring ) else self[ node1 ]
353
        node2 = node2 if not isinstance( node2, basestring ) else self[ node2 ]
354 2a2d6050 Bob Lantz
        options = dict( params )
355 c92c4efb Bob Lantz
        # Port is optional
356 39203484 Bob Lantz
        if port1 is not None:
357
            options.setdefault( 'port1', port1 )
358
        if port2 is not None:
359
            options.setdefault( 'port2', port2 )
360 c5f6d0ff Bob Lantz
        if self.intf is not None:
361
            options.setdefault( 'intf', self.intf )
362 c92c4efb Bob Lantz
        # Set default MAC - this should probably be in Link
363
        options.setdefault( 'addr1', self.randMac() )
364
        options.setdefault( 'addr2', self.randMac() )
365 89fb0819 Bob Lantz
        cls = self.link if cls is None else cls
366 2a2d6050 Bob Lantz
        link = cls( node1, node2, **options )
367 c265deed Bob Lantz
        self.links.append( link )
368
        return link
369 5a8bb489 Bob Lantz
370 80be5642 Bob Lantz
    def configHosts( self ):
371 80a8fa62 Bob Lantz
        "Configure a set of hosts."
372 019bff82 Bob Lantz
        for host in self.hosts:
373 216a4b7c Bob Lantz
            info( host.name + ' ' )
374 e1ca7196 Bob Lantz
            intf = host.defaultIntf()
375
            if intf:
376 2e3258d2 Luca Baldesi
                if intf.prefixLen:
377
                    host.configDefault(ip=intf.ip+'/'+intf.prefixLen,mac=intf.mac)
378
                else:
379
                    if intf.ip :
380
                        host.configDefault(ip=intf.ip,mac=intf.mac)
381
                    else:
382
                        host.configDefault()
383 e1ca7196 Bob Lantz
            else:
384
                # Don't configure nonexistent intf
385
                host.configDefault( ip=None, mac=None )
386 8b5062a3 Brandon Heller
            # You're low priority, dude!
387 84a91a14 Bob Lantz
            # BL: do we want to do this here or not?
388
            # May not make sense if we have CPU lmiting...
389
            # quietRun( 'renice +18 -p ' + repr( host.pid ) )
390 a9c28885 Bob Lantz
            # This may not be the right place to do this, but
391
            # it needs to be done somewhere.
392 d5886525 Bob Lantz
        info( '\n' )
393 80a8fa62 Bob Lantz
394 84a91a14 Bob Lantz
    def buildFromTopo( self, topo=None ):
395 019bff82 Bob Lantz
        """Build mininet from a topology object
396 80a8fa62 Bob Lantz
           At the end of this function, everything should be connected
397
           and up."""
398 d44a5843 Bob Lantz
399
        # Possibly we should clean up here and/or validate
400
        # the topo
401 376bcba4 Brandon Heller
        if self.cleanup:
402 d44a5843 Bob Lantz
            pass
403
404 d5886525 Bob Lantz
        info( '*** Creating network\n' )
405 84a91a14 Bob Lantz
406 15146d90 Brian O'Connor
        if not self.controllers and self.controller:
407 84a91a14 Bob Lantz
            # Add a default controller
408 aabbf295 Bob Lantz
            info( '*** Adding controller\n' )
409 f58f83c0 Bob Lantz
            classes = self.controller
410 9a8bdfd7 Bob Lantz
            if not isinstance( classes, list ):
411 f58f83c0 Bob Lantz
                classes = [ classes ]
412
            for i, cls in enumerate( classes ):
413 125e6697 Bob Lantz
                # Allow Controller objects because nobody understands partial()
414 e183e699 Bob Lantz
                if isinstance( cls, Controller ):
415 b7268856 Bob Lantz
                    self.addController( cls )
416
                else:
417
                    self.addController( 'c%d' % i, cls )
418 84a91a14 Bob Lantz
419 d5886525 Bob Lantz
        info( '*** Adding hosts:\n' )
420 5a8bb489 Bob Lantz
        for hostName in topo.hosts():
421
            self.addHost( hostName, **topo.nodeInfo( hostName ) )
422
            info( hostName + ' ' )
423 84a91a14 Bob Lantz
424 d5886525 Bob Lantz
        info( '\n*** Adding switches:\n' )
425 5a8bb489 Bob Lantz
        for switchName in topo.switches():
426 a4e93368 Bob Lantz
            # A bit ugly: add batch parameter if appropriate
427
            params = topo.nodeInfo( switchName)
428
            cls = params.get( 'cls', self.switch )
429
            if hasattr( cls, 'batchStartup' ):
430
                params.setdefault( 'batch', True )
431
            self.addSwitch( switchName, **params )
432 5a8bb489 Bob Lantz
            info( switchName + ' ' )
433 84a91a14 Bob Lantz
434 724f1144 Bob Lantz
        info( '\n*** Adding links:\n' )
435 38ce329e Bob Lantz
        for srcName, dstName, params in topo.links(
436
                sort=True, withInfo=True ):
437 2a2d6050 Bob Lantz
            self.addLink( **params )
438 38ce329e Bob Lantz
            info( '(%s, %s) ' % ( srcName, dstName ) )
439 84a91a14 Bob Lantz
440 d5886525 Bob Lantz
        info( '\n' )
441 80a8fa62 Bob Lantz
442 84a91a14 Bob Lantz
    def configureControlNetwork( self ):
443 14ff3ad3 Bob Lantz
        "Control net config hook: override in subclass"
444
        raise Exception( 'configureControlNetwork: '
445 edf60032 Brandon Heller
                         'should be overriden in subclass', self )
446 84a91a14 Bob Lantz
447 d44a5843 Bob Lantz
    def build( self ):
448
        "Build mininet."
449
        if self.topo:
450
            self.buildFromTopo( self.topo )
451 824afb84 Rémy Léone
        if self.inNamespace:
452 d44a5843 Bob Lantz
            self.configureControlNetwork()
453 d5886525 Bob Lantz
        info( '*** Configuring hosts\n' )
454 80be5642 Bob Lantz
        self.configHosts()
455 376bcba4 Brandon Heller
        if self.xterms:
456 15b482e3 Brandon Heller
            self.startTerms()
457 80a8fa62 Bob Lantz
        if self.autoStaticArp:
458
            self.staticArp()
459 99c035d9 Bob Lantz
        self.built = True
460 80a8fa62 Bob Lantz
461 15b482e3 Brandon Heller
    def startTerms( self ):
462
        "Start a terminal for each node."
463 4316be95 Brian O'Connor
        if 'DISPLAY' not in os.environ:
464
            error( "Error starting terms: Cannot connect to display\n" )
465
            return
466 15b482e3 Brandon Heller
        info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
467 8a034f4f Brandon Heller
        cleanUpScreens()
468 15b482e3 Brandon Heller
        self.terms += makeTerms( self.controllers, 'controller' )
469
        self.terms += makeTerms( self.switches, 'switch' )
470
        self.terms += makeTerms( self.hosts, 'host' )
471 8a034f4f Brandon Heller
472 80a8fa62 Bob Lantz
    def stopXterms( self ):
473
        "Kill each xterm."
474 8a034f4f Brandon Heller
        for term in self.terms:
475 80a8fa62 Bob Lantz
            os.kill( term.pid, signal.SIGKILL )
476 8a034f4f Brandon Heller
        cleanUpScreens()
477 8b5062a3 Brandon Heller
478 80a8fa62 Bob Lantz
    def staticArp( self ):
479
        "Add all-pairs ARP entries to remove the need to handle broadcast."
480 019bff82 Bob Lantz
        for src in self.hosts:
481
            for dst in self.hosts:
482 376bcba4 Brandon Heller
                if src != dst:
483 1bda2d21 Bob Lantz
                    src.setARP( ip=dst.IP(), mac=dst.MAC() )
484 376bcba4 Brandon Heller
485 80a8fa62 Bob Lantz
    def start( self ):
486 99c035d9 Bob Lantz
        "Start controller and switches."
487
        if not self.built:
488
            self.build()
489 d5886525 Bob Lantz
        info( '*** Starting controller\n' )
490 019bff82 Bob Lantz
        for controller in self.controllers:
491 6721f065 backb1
            info( controller.name + ' ')
492 019bff82 Bob Lantz
            controller.start()
493 6721f065 backb1
        info( '\n' )
494 019bff82 Bob Lantz
        info( '*** Starting %s switches\n' % len( self.switches ) )
495
        for switch in self.switches:
496 efc9a01c Bob Lantz
            info( switch.name + ' ')
497 80a8fa62 Bob Lantz
            switch.start( self.controllers )
498 9bda9848 Bob Lantz
        started = {}
499
        for swclass, switches in groupby(
500 09e9c055 Bob Lantz
                sorted( self.switches, key=type ), type ):
501 9bda9848 Bob Lantz
            switches = tuple( switches )
502 bdad3e8c Bob Lantz
            if hasattr( swclass, 'batchStartup' ):
503
                success = swclass.batchStartup( switches )
504
                started.update( { s: s for s in success } )
505 d5886525 Bob Lantz
        info( '\n' )
506 c23c992f Cody Burkard
        if self.waitConn:
507 5a9c74be Cody Burkard
            self.waitConnected()
508 80a8fa62 Bob Lantz
509
    def stop( self ):
510 d5886525 Bob Lantz
        "Stop the controller(s), switches and hosts"
511 b7a112cb Cody Burkard
        info( '*** Stopping %i controllers\n' % len( self.controllers ) )
512
        for controller in self.controllers:
513
            info( controller.name + ' ' )
514
            controller.stop()
515
        info( '\n' )
516 8a034f4f Brandon Heller
        if self.terms:
517 d5886525 Bob Lantz
            info( '*** Stopping %i terms\n' % len( self.terms ) )
518 80a8fa62 Bob Lantz
            self.stopXterms()
519 d66b9626 Bob Lantz
        info( '*** Stopping %i links\n' % len( self.links ) )
520
        for link in self.links:
521
            info( '.' )
522
            link.stop()
523
        info( '\n' )
524 019bff82 Bob Lantz
        info( '*** Stopping %i switches\n' % len( self.switches ) )
525 d66b9626 Bob Lantz
        stopped = {}
526 b1ec912d Bob Lantz
        for swclass, switches in groupby(
527
                sorted( self.switches, key=type ), type ):
528 d66b9626 Bob Lantz
            switches = tuple( switches )
529 bdad3e8c Bob Lantz
            if hasattr( swclass, 'batchShutdown' ):
530
                success = swclass.batchShutdown( switches )
531
                stopped.update( { s: s for s in success } )
532 019bff82 Bob Lantz
        for switch in self.switches:
533 84a91a14 Bob Lantz
            info( switch.name + ' ' )
534 d66b9626 Bob Lantz
            if switch not in stopped:
535
                switch.stop()
536 c265deed Bob Lantz
            switch.terminate()
537
        info( '\n' )
538 10be691b Bob Lantz
        info( '*** Stopping %i hosts\n' % len( self.hosts ) )
539
        for host in self.hosts:
540
            info( host.name + ' ' )
541
            host.terminate()
542 84a91a14 Bob Lantz
        info( '\n*** Done\n' )
543 8b5062a3 Brandon Heller
544 67516aa4 Bob Lantz
    def run( self, test, *args, **kwargs ):
545 80a8fa62 Bob Lantz
        "Perform a complete start/test/stop cycle."
546 8b5062a3 Brandon Heller
        self.start()
547 d5886525 Bob Lantz
        info( '*** Running test\n' )
548 67516aa4 Bob Lantz
        result = test( *args, **kwargs )
549 8b5062a3 Brandon Heller
        self.stop()
550
        return result
551
552 259d7133 Bob Lantz
    def monitor( self, hosts=None, timeoutms=-1 ):
553 ec7b211c Bob Lantz
        """Monitor a set of hosts (or all hosts by default),
554
           and return their output, a line at a time.
555 259d7133 Bob Lantz
           hosts: (optional) set of hosts to monitor
556
           timeoutms: (optional) timeout value in ms
557
           returns: iterator which returns host, line"""
558 ec7b211c Bob Lantz
        if hosts is None:
559
            hosts = self.hosts
560
        poller = select.poll()
561 b1ec912d Bob Lantz
        h1 = hosts[ 0 ]  # so we can call class method fdToNode
562 ec7b211c Bob Lantz
        for host in hosts:
563
            poller.register( host.stdout )
564
        while True:
565 259d7133 Bob Lantz
            ready = poller.poll( timeoutms )
566 ec7b211c Bob Lantz
            for fd, event in ready:
567 b1ec912d Bob Lantz
                host = h1.fdToNode( fd )
568 ec7b211c Bob Lantz
                if event & select.POLLIN:
569
                    line = host.readline()
570 259d7133 Bob Lantz
                    if line is not None:
571 ec7b211c Bob Lantz
                        yield host, line
572 259d7133 Bob Lantz
            # Return if non-blocking
573
            if not ready and timeoutms >= 0:
574
                yield None, None
575 ec7b211c Bob Lantz
576 84a91a14 Bob Lantz
    # XXX These test methods should be moved out of this class.
577
    # Probably we should create a tests.py for them
578
579 8b5062a3 Brandon Heller
    @staticmethod
580 80a8fa62 Bob Lantz
    def _parsePing( pingOutput ):
581
        "Parse ping output and return packets sent, received."
582 3fac5a43 Brandon Heller
        # Check for downed link
583
        if 'connect: Network is unreachable' in pingOutput:
584 824afb84 Rémy Léone
            return 1, 0
585 8b5062a3 Brandon Heller
        r = r'(\d+) packets transmitted, (\d+) received'
586 80a8fa62 Bob Lantz
        m = re.search( r, pingOutput )
587 7a506047 Brandon Heller
        if m is None:
588 d5886525 Bob Lantz
            error( '*** Error: could not parse ping output: %s\n' %
589 2e089b5e Brandon Heller
                   pingOutput )
590 824afb84 Rémy Léone
            return 1, 0
591 80a8fa62 Bob Lantz
        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
592 8b5062a3 Brandon Heller
        return sent, received
593
594 1f1d590c Brandon Heller
    def ping( self, hosts=None, timeout=None ):
595 80a8fa62 Bob Lantz
        """Ping between all specified hosts.
596 019bff82 Bob Lantz
           hosts: list of hosts
597 1f1d590c Brandon Heller
           timeout: time to wait for a response, as string
598 80a8fa62 Bob Lantz
           returns: ploss packet loss percentage"""
599 d44a5843 Bob Lantz
        # should we check if running?
600 8b5062a3 Brandon Heller
        packets = 0
601
        lost = 0
602
        ploss = None
603
        if not hosts:
604 019bff82 Bob Lantz
            hosts = self.hosts
605 cdeaca86 Brandon Heller
            output( '*** Ping: testing ping reachability\n' )
606 019bff82 Bob Lantz
        for node in hosts:
607 cdeaca86 Brandon Heller
            output( '%s -> ' % node.name )
608 019bff82 Bob Lantz
            for dest in hosts:
609 8b5062a3 Brandon Heller
                if node != dest:
610 1f1d590c Brandon Heller
                    opts = ''
611
                    if timeout:
612
                        opts = '-W %s' % timeout
613 778267aa cody burkard
                    if dest.intfs:
614 b1ec912d Bob Lantz
                        result = node.cmd( 'ping -c1 %s %s' %
615
                                           (opts, dest.IP()) )
616 778267aa cody burkard
                        sent, received = self._parsePing( result )
617
                    else:
618
                        sent, received = 0, 0
619 8b5062a3 Brandon Heller
                    packets += sent
620
                    if received > sent:
621 d5886525 Bob Lantz
                        error( '*** Error: received too many packets' )
622
                        error( '%s' % result )
623 80a8fa62 Bob Lantz
                        node.cmdPrint( 'route' )
624
                        exit( 1 )
625 8b5062a3 Brandon Heller
                    lost += sent - received
626 cdeaca86 Brandon Heller
                    output( ( '%s ' % dest.name ) if received else 'X ' )
627
            output( '\n' )
628 c188bee3 Brian O'Connor
        if packets > 0:
629 92a28881 lantz
            ploss = 100.0 * lost / packets
630 fec98e27 Brian O'Connor
            received = packets - lost
631
            output( "*** Results: %i%% dropped (%d/%d received)\n" %
632
                    ( ploss, received, packets ) )
633 c188bee3 Brian O'Connor
        else:
634
            ploss = 0
635
            output( "*** Warning: No packets sent\n" )
636 8b5062a3 Brandon Heller
        return ploss
637
638 1f1d590c Brandon Heller
    @staticmethod
639
    def _parsePingFull( pingOutput ):
640
        "Parse ping output and return all data."
641 1ecc63df Brian O'Connor
        errorTuple = (1, 0, 0, 0, 0, 0)
642 1f1d590c Brandon Heller
        # Check for downed link
643 1ecc63df Brian O'Connor
        r = r'[uU]nreachable'
644
        m = re.search( r, pingOutput )
645
        if m is not None:
646
            return errorTuple
647 1f1d590c Brandon Heller
        r = r'(\d+) packets transmitted, (\d+) received'
648
        m = re.search( r, pingOutput )
649
        if m is None:
650
            error( '*** Error: could not parse ping output: %s\n' %
651
                   pingOutput )
652 1ecc63df Brian O'Connor
            return errorTuple
653 1f1d590c Brandon Heller
        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
654
        r = r'rtt min/avg/max/mdev = '
655
        r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
656
        m = re.search( r, pingOutput )
657 1ecc63df Brian O'Connor
        if m is None:
658 00cbb348 cody burkard
            if received == 0:
659
                return errorTuple
660 1ecc63df Brian O'Connor
            error( '*** Error: could not parse ping output: %s\n' %
661
                   pingOutput )
662
            return errorTuple
663 1f1d590c Brandon Heller
        rttmin = float( m.group( 1 ) )
664
        rttavg = float( m.group( 2 ) )
665
        rttmax = float( m.group( 3 ) )
666
        rttdev = float( m.group( 4 ) )
667
        return sent, received, rttmin, rttavg, rttmax, rttdev
668
669
    def pingFull( self, hosts=None, timeout=None ):
670
        """Ping between all specified hosts and return all data.
671
           hosts: list of hosts
672
           timeout: time to wait for a response, as string
673
           returns: all ping data; see function body."""
674
        # should we check if running?
675
        # Each value is a tuple: (src, dsd, [all ping outputs])
676
        all_outputs = []
677
        if not hosts:
678
            hosts = self.hosts
679
            output( '*** Ping: testing ping reachability\n' )
680
        for node in hosts:
681
            output( '%s -> ' % node.name )
682
            for dest in hosts:
683
                if node != dest:
684
                    opts = ''
685
                    if timeout:
686
                        opts = '-W %s' % timeout
687
                    result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
688
                    outputs = self._parsePingFull( result )
689
                    sent, received, rttmin, rttavg, rttmax, rttdev = outputs
690
                    all_outputs.append( (node, dest, outputs) )
691
                    output( ( '%s ' % dest.name ) if received else 'X ' )
692
            output( '\n' )
693
        output( "*** Results: \n" )
694
        for outputs in all_outputs:
695
            src, dest, ping_outputs = outputs
696
            sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
697
            output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
698
            output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
699
                    (rttmin, rttavg, rttmax, rttdev) )
700
        return all_outputs
701
702 4d1a9cdc Jon Hall
    def pingAll( self, timeout=None ):
703 80a8fa62 Bob Lantz
        """Ping between all hosts.
704
           returns: ploss packet loss percentage"""
705 4d1a9cdc Jon Hall
        return self.ping( timeout=timeout )
706 eeb9cb3c Brandon Heller
707 80a8fa62 Bob Lantz
    def pingPair( self ):
708
        """Ping between first two hosts, useful for testing.
709
           returns: ploss packet loss percentage"""
710 019bff82 Bob Lantz
        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
711 80a8fa62 Bob Lantz
        return self.ping( hosts=hosts )
712 eeb9cb3c Brandon Heller
713 1f1d590c Brandon Heller
    def pingAllFull( self ):
714
        """Ping between all hosts.
715
           returns: ploss packet loss percentage"""
716
        return self.pingFull()
717
718
    def pingPairFull( self ):
719
        """Ping between first two hosts, useful for testing.
720
           returns: ploss packet loss percentage"""
721
        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
722
        return self.pingFull( hosts=hosts )
723
724 eeb9cb3c Brandon Heller
    @staticmethod
725 80a8fa62 Bob Lantz
    def _parseIperf( iperfOutput ):
726
        """Parse iperf output and return bandwidth.
727
           iperfOutput: string
728
           returns: result string"""
729 eeb9cb3c Brandon Heller
        r = r'([\d\.]+ \w+/sec)'
730 ad2fda25 Bob Lantz
        m = re.findall( r, iperfOutput )
731 eeb9cb3c Brandon Heller
        if m:
732 ad2fda25 Bob Lantz
            return m[-1]
733 eeb9cb3c Brandon Heller
        else:
734 ad2fda25 Bob Lantz
            # was: raise Exception(...)
735
            error( 'could not parse iperf output: ' + iperfOutput )
736
            return ''
737 80a8fa62 Bob Lantz
738 216a4b7c Bob Lantz
    # XXX This should be cleaned up
739
740 b1ec912d Bob Lantz
    def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
741 b2fe0778 Bob Lantz
               seconds=5, port=5001):
742 80a8fa62 Bob Lantz
        """Run iperf between two hosts.
743 b1ec912d Bob Lantz
           hosts: list of hosts; if None, uses first and last hosts
744 80a8fa62 Bob Lantz
           l4Type: string, one of [ TCP, UDP ]
745 d3377bf9 Bob Lantz
           udpBw: bandwidth target for UDP test
746 b1ec912d Bob Lantz
           fmt: iperf format argument if any
747 d3377bf9 Bob Lantz
           seconds: iperf time to transmit
748 b2fe0778 Bob Lantz
           port: iperf port
749 d3377bf9 Bob Lantz
           returns: two-element array of [ server, client ] speeds
750 e1711f35 Bob Lantz
           note: send() is buffered, so client rate can be much higher than
751
           the actual transmission rate; on an unloaded system, server
752
           rate should be much closer to the actual receive rate"""
753 18aab5b7 Bob Lantz
        hosts = hosts or [ self.hosts[ 0 ], self.hosts[ -1 ] ]
754
        assert len( hosts ) == 2
755 ec7b211c Bob Lantz
        client, server = hosts
756 b2fe0778 Bob Lantz
        output( '*** Iperf: testing', l4Type, 'bandwidth between',
757
                client, 'and', server, '\n' )
758 ec7b211c Bob Lantz
        server.cmd( 'killall -9 iperf' )
759 b2fe0778 Bob Lantz
        iperfArgs = 'iperf -p %d ' % port
760 80a8fa62 Bob Lantz
        bwArgs = ''
761
        if l4Type == 'UDP':
762
            iperfArgs += '-u '
763
            bwArgs = '-b ' + udpBw + ' '
764
        elif l4Type != 'TCP':
765
            raise Exception( 'Unexpected l4 type: %s' % l4Type )
766 b1ec912d Bob Lantz
        if fmt:
767
            iperfArgs += '-f %s ' % fmt
768 b2fe0778 Bob Lantz
        server.sendCmd( iperfArgs + '-s' )
769 8856d284 Bob Lantz
        if l4Type == 'TCP':
770 b2fe0778 Bob Lantz
            if not waitListening( client, server.IP(), port ):
771
                raise Exception( 'Could not connect to iperf on port %d'
772
                                 % port )
773 d3377bf9 Bob Lantz
        cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
774
                             server.IP() + ' ' + bwArgs )
775 ec7b211c Bob Lantz
        debug( 'Client output: %s\n' % cliout )
776 327af97c Bob Lantz
        servout = ''
777 6a69c3c7 Bob Lantz
        # We want the last *b/sec from the iperf server output
778
        # for TCP, there are two fo them because of waitListening
779
        count = 2 if l4Type == 'TCP' else 1
780
        while len( re.findall( '/sec', servout ) ) < count:
781 327af97c Bob Lantz
            servout += server.monitor( timeoutms=5000 )
782 ec7b211c Bob Lantz
        server.sendInt()
783 04897513 Bob Lantz
        servout += server.waitOutput()
784 ec7b211c Bob Lantz
        debug( 'Server output: %s\n' % servout )
785
        result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
786 80a8fa62 Bob Lantz
        if l4Type == 'UDP':
787
            result.insert( 0, udpBw )
788 cdeaca86 Brandon Heller
        output( '*** Results: %s\n' % result )
789 eeb9cb3c Brandon Heller
        return result
790
791 fcd01592 Brandon Heller
    def runCpuLimitTest( self, cpu, duration=5 ):
792
        """run CPU limit test with 'while true' processes.
793
        cpu: desired CPU fraction of each host
794 b1ec912d Bob Lantz
        duration: test duration in seconds (integer)
795 fcd01592 Brandon Heller
        returns a single list of measured CPU fractions as floats.
796
        """
797 ce781a18 cody burkard
        cores = int( quietRun( 'nproc' ) )
798 fcd01592 Brandon Heller
        pct = cpu * 100
799 ce781a18 cody burkard
        info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
800 fcd01592 Brandon Heller
        hosts = self.hosts
801 ce781a18 cody burkard
        cores = int( quietRun( 'nproc' ) )
802
        # number of processes to run a while loop on per host
803
        num_procs = int( ceil( cores * cpu ) )
804
        pids = {}
805 fcd01592 Brandon Heller
        for h in hosts:
806 ce781a18 cody burkard
            pids[ h ] = []
807
            for _core in range( num_procs ):
808
                h.cmd( 'while true; do a=1; done &' )
809
                pids[ h ].append( h.cmd( 'echo $!' ).strip() )
810
        outputs = {}
811
        time = {}
812
        # get the initial cpu time for each host
813
        for host in hosts:
814
            outputs[ host ] = []
815 b1ec912d Bob Lantz
            with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
816
                       host, 'r' ) as f:
817 ce781a18 cody burkard
                time[ host ] = float( f.read() )
818 b1ec912d Bob Lantz
        for _ in range( duration ):
819 fcd01592 Brandon Heller
            sleep( 1 )
820 ce781a18 cody burkard
            for host in hosts:
821 b1ec912d Bob Lantz
                with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
822
                           host, 'r' ) as f:
823 ce781a18 cody burkard
                    readTime = float( f.read() )
824
                outputs[ host ].append( ( ( readTime - time[ host ] )
825
                                        / 1000000000 ) / cores * 100 )
826
                time[ host ] = readTime
827
        for h, pids in pids.items():
828
            for pid in pids:
829
                h.cmd( 'kill -9 %s' % pid )
830 fcd01592 Brandon Heller
        cpu_fractions = []
831 ce781a18 cody burkard
        for _host, outputs in outputs.items():
832
            for pct in outputs:
833
                cpu_fractions.append( pct )
834 fcd01592 Brandon Heller
        output( '*** Results: %s\n' % cpu_fractions )
835
        return cpu_fractions
836
837 84a91a14 Bob Lantz
    # BL: I think this can be rewritten now that we have
838
    # a real link class.
839 c70aab0a Bob Lantz
    def configLinkStatus( self, src, dst, status ):
840
        """Change status of src <-> dst links.
841
           src: node name
842
           dst: node name
843
           status: string {up, down}"""
844 8d3c2859 Brandon Heller
        if src not in self.nameToNode:
845
            error( 'src not in network: %s\n' % src )
846
        elif dst not in self.nameToNode:
847
            error( 'dst not in network: %s\n' % dst )
848
        else:
849 9a8bdfd7 Bob Lantz
            if isinstance( src, basestring ):
850 8856d284 Bob Lantz
                src = self.nameToNode[ src ]
851 9a8bdfd7 Bob Lantz
            if isinstance( dst, basestring ):
852 8856d284 Bob Lantz
                dst = self.nameToNode[ dst ]
853
            connections = src.connectionsTo( dst )
854 fb2f6523 Bob Lantz
            if len( connections ) == 0:
855 8d3c2859 Brandon Heller
                error( 'src and dst not connected: %s %s\n' % ( src, dst) )
856 fb2f6523 Bob Lantz
            for srcIntf, dstIntf in connections:
857 8856d284 Bob Lantz
                result = srcIntf.ifconfig( status )
858 8d3c2859 Brandon Heller
                if result:
859
                    error( 'link src status change failed: %s\n' % result )
860 8856d284 Bob Lantz
                result = dstIntf.ifconfig( status )
861 8d3c2859 Brandon Heller
                if result:
862
                    error( 'link dst status change failed: %s\n' % result )
863
864 80a8fa62 Bob Lantz
    def interact( self ):
865
        "Start network and run our simple CLI."
866 eeb9cb3c Brandon Heller
        self.start()
867 496b5f9e Bob Lantz
        result = CLI( self )
868 eeb9cb3c Brandon Heller
        self.stop()
869
        return result
870 82b72072 Bob Lantz
871 8e3699ec Bob Lantz
    inited = False
872 14ff3ad3 Bob Lantz
873 8e3699ec Bob Lantz
    @classmethod
874
    def init( cls ):
875
        "Initialize Mininet"
876
        if cls.inited:
877
            return
878 bcfb3009 Brandon Heller
        ensureRoot()
879 8e3699ec Bob Lantz
        fixLimits()
880
        cls.inited = True
881
882 82b72072 Bob Lantz
883 84a91a14 Bob Lantz
class MininetWithControlNet( Mininet ):
884
885
    """Control network support:
886

887
       Create an explicit control network. Currently this is only
888
       used/usable with the user datapath.
889

890
       Notes:
891

892
       1. If the controller and switches are in the same (e.g. root)
893
          namespace, they can just use the loopback connection.
894

895
       2. If we can get unix domain sockets to work, we can use them
896
          instead of an explicit control network.
897

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

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

904
       5. Basically nobody ever used this code, so it has been moved
905 14ff3ad3 Bob Lantz
          into its own class.
906

907
       6. Ultimately we may wish to extend this to allow us to create a
908
          control network which every node's control interface is
909
          attached to."""
910 84a91a14 Bob Lantz
911
    def configureControlNetwork( self ):
912
        "Configure control network."
913
        self.configureRoutedControlNetwork()
914
915
    # We still need to figure out the right way to pass
916
    # in the control network location.
917
918
    def configureRoutedControlNetwork( self, ip='192.168.123.1',
919 edf60032 Brandon Heller
                                       prefixLen=16 ):
920 84a91a14 Bob Lantz
        """Configure a routed control network on controller and switches.
921
           For use with the user datapath only right now."""
922
        controller = self.controllers[ 0 ]
923
        info( controller.name + ' <->' )
924
        cip = ip
925
        snum = ipParse( ip )
926
        for switch in self.switches:
927
            info( ' ' + switch.name )
928 14ff3ad3 Bob Lantz
            link = self.link( switch, controller, port1=0 )
929
            sintf, cintf = link.intf1, link.intf2
930
            switch.controlIntf = sintf
931 84a91a14 Bob Lantz
            snum += 1
932
            while snum & 0xff in [ 0, 255 ]:
933
                snum += 1
934
            sip = ipStr( snum )
935 14ff3ad3 Bob Lantz
            cintf.setIP( cip, prefixLen )
936
            sintf.setIP( sip, prefixLen )
937 84a91a14 Bob Lantz
            controller.setHostRoute( sip, cintf )
938
            switch.setHostRoute( cip, sintf )
939
        info( '\n' )
940
        info( '*** Testing control network\n' )
941 14ff3ad3 Bob Lantz
        while not cintf.isUp():
942 84a91a14 Bob Lantz
            info( '*** Waiting for', cintf, 'to come up\n' )
943
            sleep( 1 )
944
        for switch in self.switches:
945 14ff3ad3 Bob Lantz
            while not sintf.isUp():
946 84a91a14 Bob Lantz
                info( '*** Waiting for', sintf, 'to come up\n' )
947
                sleep( 1 )
948
            if self.ping( hosts=[ switch, controller ] ) != 0:
949
                error( '*** Error: control network test failed\n' )
950
                exit( 1 )
951
        info( '\n' )