Statistics
| Branch: | Tag: | Revision:

mininet / mininet / net.py @ a4e93368

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

878
       Create an explicit control network. Currently this is only
879
       used/usable with the user datapath.
880

881
       Notes:
882

883
       1. If the controller and switches are in the same (e.g. root)
884
          namespace, they can just use the loopback connection.
885

886
       2. If we can get unix domain sockets to work, we can use them
887
          instead of an explicit control network.
888

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

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

895
       5. Basically nobody ever used this code, so it has been moved
896 14ff3ad3 Bob Lantz
          into its own class.
897

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