Statistics
| Branch: | Tag: | Revision:

mininet / mininet / net.py @ b1ec912d

History | View | Annotate | Download (35.1 KB)

1 281f6e59 Bob Lantz
"""
2

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

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

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

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

15
Each host has:
16 7d4b7b7f Bob Lantz

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

87
"""
88
89 8b5062a3 Brandon Heller
import os
90
import re
91 ec7b211c Bob Lantz
import select
92 8a034f4f Brandon Heller
import signal
93 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
                          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 4219b229 Bob Lantz
VERSION = "2.2.0b2"
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 84ea8d7f Cody Burkard
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
            # pylint: disable=E1103
258 15146d90 Brian O'Connor
            name = controller_new.name
259 35029978 Bob Lantz
            # pylint: enable=E1103
260 15146d90 Brian O'Connor
        else:
261
            controller_new = controller( name, **params )
262 35029978 Bob Lantz
        # Add new controller to net
263 72fd120d Cody Burkard
        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 47b9466f Brian O'Connor
            self.addLink( nat, self.switches[ 0 ] )
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 2a2d6050 Bob Lantz
        return macColonHex( random.randint(1, 2**48 - 1) & 0xfeffffffffff  |
339
                            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 c92c4efb Bob Lantz
        # Set default MAC - this should probably be in Link
361
        options.setdefault( 'addr1', self.randMac() )
362
        options.setdefault( 'addr2', self.randMac() )
363 89fb0819 Bob Lantz
        cls = self.link if cls is None else cls
364 2a2d6050 Bob Lantz
        link = cls( node1, node2, **options )
365 c265deed Bob Lantz
        self.links.append( link )
366
        return link
367 5a8bb489 Bob Lantz
368 80be5642 Bob Lantz
    def configHosts( self ):
369 80a8fa62 Bob Lantz
        "Configure a set of hosts."
370 019bff82 Bob Lantz
        for host in self.hosts:
371 216a4b7c Bob Lantz
            info( host.name + ' ' )
372 e1ca7196 Bob Lantz
            intf = host.defaultIntf()
373
            if intf:
374 dd21df3c Bob Lantz
                host.configDefault()
375 e1ca7196 Bob Lantz
            else:
376
                # Don't configure nonexistent intf
377
                host.configDefault( ip=None, mac=None )
378 8b5062a3 Brandon Heller
            # You're low priority, dude!
379 84a91a14 Bob Lantz
            # BL: do we want to do this here or not?
380
            # May not make sense if we have CPU lmiting...
381
            # quietRun( 'renice +18 -p ' + repr( host.pid ) )
382 a9c28885 Bob Lantz
            # This may not be the right place to do this, but
383
            # it needs to be done somewhere.
384 d5886525 Bob Lantz
        info( '\n' )
385 80a8fa62 Bob Lantz
386 84a91a14 Bob Lantz
    def buildFromTopo( self, topo=None ):
387 019bff82 Bob Lantz
        """Build mininet from a topology object
388 80a8fa62 Bob Lantz
           At the end of this function, everything should be connected
389
           and up."""
390 d44a5843 Bob Lantz
391
        # Possibly we should clean up here and/or validate
392
        # the topo
393 376bcba4 Brandon Heller
        if self.cleanup:
394 d44a5843 Bob Lantz
            pass
395
396 d5886525 Bob Lantz
        info( '*** Creating network\n' )
397 84a91a14 Bob Lantz
398 15146d90 Brian O'Connor
        if not self.controllers and self.controller:
399 84a91a14 Bob Lantz
            # Add a default controller
400 aabbf295 Bob Lantz
            info( '*** Adding controller\n' )
401 f58f83c0 Bob Lantz
            classes = self.controller
402 9a8bdfd7 Bob Lantz
            if not isinstance( classes, list ):
403 f58f83c0 Bob Lantz
                classes = [ classes ]
404
            for i, cls in enumerate( classes ):
405 b7268856 Bob Lantz
                # Allow Controller objects because nobody understands currying
406 e183e699 Bob Lantz
                if isinstance( cls, Controller ):
407 b7268856 Bob Lantz
                    self.addController( cls )
408
                else:
409
                    self.addController( 'c%d' % i, cls )
410 84a91a14 Bob Lantz
411 d5886525 Bob Lantz
        info( '*** Adding hosts:\n' )
412 5a8bb489 Bob Lantz
        for hostName in topo.hosts():
413
            self.addHost( hostName, **topo.nodeInfo( hostName ) )
414
            info( hostName + ' ' )
415 84a91a14 Bob Lantz
416 d5886525 Bob Lantz
        info( '\n*** Adding switches:\n' )
417 5a8bb489 Bob Lantz
        for switchName in topo.switches():
418
            self.addSwitch( switchName, **topo.nodeInfo( switchName) )
419
            info( switchName + ' ' )
420 84a91a14 Bob Lantz
421 724f1144 Bob Lantz
        info( '\n*** Adding links:\n' )
422 38ce329e Bob Lantz
        for srcName, dstName, params in topo.links(
423
                sort=True, withInfo=True ):
424 2a2d6050 Bob Lantz
            self.addLink( **params )
425 38ce329e Bob Lantz
            info( '(%s, %s) ' % ( srcName, dstName ) )
426 84a91a14 Bob Lantz
427 d5886525 Bob Lantz
        info( '\n' )
428 80a8fa62 Bob Lantz
429 84a91a14 Bob Lantz
    def configureControlNetwork( self ):
430 14ff3ad3 Bob Lantz
        "Control net config hook: override in subclass"
431
        raise Exception( 'configureControlNetwork: '
432 edf60032 Brandon Heller
                         'should be overriden in subclass', self )
433 84a91a14 Bob Lantz
434 d44a5843 Bob Lantz
    def build( self ):
435
        "Build mininet."
436
        if self.topo:
437
            self.buildFromTopo( self.topo )
438 824afb84 Rémy Léone
        if self.inNamespace:
439 d44a5843 Bob Lantz
            self.configureControlNetwork()
440 d5886525 Bob Lantz
        info( '*** Configuring hosts\n' )
441 80be5642 Bob Lantz
        self.configHosts()
442 376bcba4 Brandon Heller
        if self.xterms:
443 15b482e3 Brandon Heller
            self.startTerms()
444 80a8fa62 Bob Lantz
        if self.autoStaticArp:
445
            self.staticArp()
446 99c035d9 Bob Lantz
        self.built = True
447 80a8fa62 Bob Lantz
448 15b482e3 Brandon Heller
    def startTerms( self ):
449
        "Start a terminal for each node."
450 4316be95 Brian O'Connor
        if 'DISPLAY' not in os.environ:
451
            error( "Error starting terms: Cannot connect to display\n" )
452
            return
453 15b482e3 Brandon Heller
        info( "*** Running terms on %s\n" % os.environ[ 'DISPLAY' ] )
454 8a034f4f Brandon Heller
        cleanUpScreens()
455 15b482e3 Brandon Heller
        self.terms += makeTerms( self.controllers, 'controller' )
456
        self.terms += makeTerms( self.switches, 'switch' )
457
        self.terms += makeTerms( self.hosts, 'host' )
458 8a034f4f Brandon Heller
459 80a8fa62 Bob Lantz
    def stopXterms( self ):
460
        "Kill each xterm."
461 8a034f4f Brandon Heller
        for term in self.terms:
462 80a8fa62 Bob Lantz
            os.kill( term.pid, signal.SIGKILL )
463 8a034f4f Brandon Heller
        cleanUpScreens()
464 8b5062a3 Brandon Heller
465 80a8fa62 Bob Lantz
    def staticArp( self ):
466
        "Add all-pairs ARP entries to remove the need to handle broadcast."
467 019bff82 Bob Lantz
        for src in self.hosts:
468
            for dst in self.hosts:
469 376bcba4 Brandon Heller
                if src != dst:
470 1bda2d21 Bob Lantz
                    src.setARP( ip=dst.IP(), mac=dst.MAC() )
471 376bcba4 Brandon Heller
472 80a8fa62 Bob Lantz
    def start( self ):
473 99c035d9 Bob Lantz
        "Start controller and switches."
474
        if not self.built:
475
            self.build()
476 d5886525 Bob Lantz
        info( '*** Starting controller\n' )
477 019bff82 Bob Lantz
        for controller in self.controllers:
478 6721f065 backb1
            info( controller.name + ' ')
479 019bff82 Bob Lantz
            controller.start()
480 6721f065 backb1
        info( '\n' )
481 019bff82 Bob Lantz
        info( '*** Starting %s switches\n' % len( self.switches ) )
482
        for switch in self.switches:
483 efc9a01c Bob Lantz
            info( switch.name + ' ')
484 80a8fa62 Bob Lantz
            switch.start( self.controllers )
485 d5886525 Bob Lantz
        info( '\n' )
486 c23c992f Cody Burkard
        if self.waitConn:
487 5a9c74be Cody Burkard
            self.waitConnected()
488 80a8fa62 Bob Lantz
489
    def stop( self ):
490 d5886525 Bob Lantz
        "Stop the controller(s), switches and hosts"
491 b7a112cb Cody Burkard
        info( '*** Stopping %i controllers\n' % len( self.controllers ) )
492
        for controller in self.controllers:
493
            info( controller.name + ' ' )
494
            controller.stop()
495
        info( '\n' )
496 8a034f4f Brandon Heller
        if self.terms:
497 d5886525 Bob Lantz
            info( '*** Stopping %i terms\n' % len( self.terms ) )
498 80a8fa62 Bob Lantz
            self.stopXterms()
499 019bff82 Bob Lantz
        info( '*** Stopping %i switches\n' % len( self.switches ) )
500 b1ec912d Bob Lantz
        for swclass, switches in groupby(
501
                sorted( self.switches, key=type ), type ):
502 a0bc1002 Bob Lantz
            if hasattr( swclass, 'batchShutdown' ):
503 876e66e5 Rich Lane
                swclass.batchShutdown( switches )
504 019bff82 Bob Lantz
        for switch in self.switches:
505 84a91a14 Bob Lantz
            info( switch.name + ' ' )
506 8b5062a3 Brandon Heller
            switch.stop()
507 c265deed Bob Lantz
            switch.terminate()
508
        info( '\n' )
509
        info( '*** Stopping %i links\n' % len( self.links ) )
510
        for link in self.links:
511
            link.stop()
512 d5886525 Bob Lantz
        info( '\n' )
513 10be691b Bob Lantz
        info( '*** Stopping %i hosts\n' % len( self.hosts ) )
514
        for host in self.hosts:
515
            info( host.name + ' ' )
516
            host.terminate()
517 84a91a14 Bob Lantz
        info( '\n*** Done\n' )
518 8b5062a3 Brandon Heller
519 67516aa4 Bob Lantz
    def run( self, test, *args, **kwargs ):
520 80a8fa62 Bob Lantz
        "Perform a complete start/test/stop cycle."
521 8b5062a3 Brandon Heller
        self.start()
522 d5886525 Bob Lantz
        info( '*** Running test\n' )
523 67516aa4 Bob Lantz
        result = test( *args, **kwargs )
524 8b5062a3 Brandon Heller
        self.stop()
525
        return result
526
527 259d7133 Bob Lantz
    def monitor( self, hosts=None, timeoutms=-1 ):
528 ec7b211c Bob Lantz
        """Monitor a set of hosts (or all hosts by default),
529
           and return their output, a line at a time.
530 259d7133 Bob Lantz
           hosts: (optional) set of hosts to monitor
531
           timeoutms: (optional) timeout value in ms
532
           returns: iterator which returns host, line"""
533 ec7b211c Bob Lantz
        if hosts is None:
534
            hosts = self.hosts
535
        poller = select.poll()
536 b1ec912d Bob Lantz
        h1 = hosts[ 0 ]  # so we can call class method fdToNode
537 ec7b211c Bob Lantz
        for host in hosts:
538
            poller.register( host.stdout )
539
        while True:
540 259d7133 Bob Lantz
            ready = poller.poll( timeoutms )
541 ec7b211c Bob Lantz
            for fd, event in ready:
542 b1ec912d Bob Lantz
                host = h1.fdToNode( fd )
543 ec7b211c Bob Lantz
                if event & select.POLLIN:
544
                    line = host.readline()
545 259d7133 Bob Lantz
                    if line is not None:
546 ec7b211c Bob Lantz
                        yield host, line
547 259d7133 Bob Lantz
            # Return if non-blocking
548
            if not ready and timeoutms >= 0:
549
                yield None, None
550 ec7b211c Bob Lantz
551 84a91a14 Bob Lantz
    # XXX These test methods should be moved out of this class.
552
    # Probably we should create a tests.py for them
553
554 8b5062a3 Brandon Heller
    @staticmethod
555 80a8fa62 Bob Lantz
    def _parsePing( pingOutput ):
556
        "Parse ping output and return packets sent, received."
557 3fac5a43 Brandon Heller
        # Check for downed link
558
        if 'connect: Network is unreachable' in pingOutput:
559 824afb84 Rémy Léone
            return 1, 0
560 8b5062a3 Brandon Heller
        r = r'(\d+) packets transmitted, (\d+) received'
561 80a8fa62 Bob Lantz
        m = re.search( r, pingOutput )
562 7a506047 Brandon Heller
        if m is None:
563 d5886525 Bob Lantz
            error( '*** Error: could not parse ping output: %s\n' %
564 2e089b5e Brandon Heller
                   pingOutput )
565 824afb84 Rémy Léone
            return 1, 0
566 80a8fa62 Bob Lantz
        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
567 8b5062a3 Brandon Heller
        return sent, received
568
569 1f1d590c Brandon Heller
    def ping( self, hosts=None, timeout=None ):
570 80a8fa62 Bob Lantz
        """Ping between all specified hosts.
571 019bff82 Bob Lantz
           hosts: list of hosts
572 1f1d590c Brandon Heller
           timeout: time to wait for a response, as string
573 80a8fa62 Bob Lantz
           returns: ploss packet loss percentage"""
574 d44a5843 Bob Lantz
        # should we check if running?
575 8b5062a3 Brandon Heller
        packets = 0
576
        lost = 0
577
        ploss = None
578
        if not hosts:
579 019bff82 Bob Lantz
            hosts = self.hosts
580 cdeaca86 Brandon Heller
            output( '*** Ping: testing ping reachability\n' )
581 019bff82 Bob Lantz
        for node in hosts:
582 cdeaca86 Brandon Heller
            output( '%s -> ' % node.name )
583 019bff82 Bob Lantz
            for dest in hosts:
584 8b5062a3 Brandon Heller
                if node != dest:
585 1f1d590c Brandon Heller
                    opts = ''
586
                    if timeout:
587
                        opts = '-W %s' % timeout
588 778267aa cody burkard
                    if dest.intfs:
589 b1ec912d Bob Lantz
                        result = node.cmd( 'ping -c1 %s %s' %
590
                                           (opts, dest.IP()) )
591 778267aa cody burkard
                        sent, received = self._parsePing( result )
592
                    else:
593
                        sent, received = 0, 0
594 8b5062a3 Brandon Heller
                    packets += sent
595
                    if received > sent:
596 d5886525 Bob Lantz
                        error( '*** Error: received too many packets' )
597
                        error( '%s' % result )
598 80a8fa62 Bob Lantz
                        node.cmdPrint( 'route' )
599
                        exit( 1 )
600 8b5062a3 Brandon Heller
                    lost += sent - received
601 cdeaca86 Brandon Heller
                    output( ( '%s ' % dest.name ) if received else 'X ' )
602
            output( '\n' )
603 c188bee3 Brian O'Connor
        if packets > 0:
604 92a28881 lantz
            ploss = 100.0 * lost / packets
605 fec98e27 Brian O'Connor
            received = packets - lost
606
            output( "*** Results: %i%% dropped (%d/%d received)\n" %
607
                    ( ploss, received, packets ) )
608 c188bee3 Brian O'Connor
        else:
609
            ploss = 0
610
            output( "*** Warning: No packets sent\n" )
611 8b5062a3 Brandon Heller
        return ploss
612
613 1f1d590c Brandon Heller
    @staticmethod
614
    def _parsePingFull( pingOutput ):
615
        "Parse ping output and return all data."
616 1ecc63df Brian O'Connor
        errorTuple = (1, 0, 0, 0, 0, 0)
617 1f1d590c Brandon Heller
        # Check for downed link
618 1ecc63df Brian O'Connor
        r = r'[uU]nreachable'
619
        m = re.search( r, pingOutput )
620
        if m is not None:
621
            return errorTuple
622 1f1d590c Brandon Heller
        r = r'(\d+) packets transmitted, (\d+) received'
623
        m = re.search( r, pingOutput )
624
        if m is None:
625
            error( '*** Error: could not parse ping output: %s\n' %
626
                   pingOutput )
627 1ecc63df Brian O'Connor
            return errorTuple
628 1f1d590c Brandon Heller
        sent, received = int( m.group( 1 ) ), int( m.group( 2 ) )
629
        r = r'rtt min/avg/max/mdev = '
630
        r += r'(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+)/(\d+\.\d+) ms'
631
        m = re.search( r, pingOutput )
632 1ecc63df Brian O'Connor
        if m is None:
633 00cbb348 cody burkard
            if received == 0:
634
                return errorTuple
635 1ecc63df Brian O'Connor
            error( '*** Error: could not parse ping output: %s\n' %
636
                   pingOutput )
637
            return errorTuple
638 1f1d590c Brandon Heller
        rttmin = float( m.group( 1 ) )
639
        rttavg = float( m.group( 2 ) )
640
        rttmax = float( m.group( 3 ) )
641
        rttdev = float( m.group( 4 ) )
642
        return sent, received, rttmin, rttavg, rttmax, rttdev
643
644
    def pingFull( self, hosts=None, timeout=None ):
645
        """Ping between all specified hosts and return all data.
646
           hosts: list of hosts
647
           timeout: time to wait for a response, as string
648
           returns: all ping data; see function body."""
649
        # should we check if running?
650
        # Each value is a tuple: (src, dsd, [all ping outputs])
651
        all_outputs = []
652
        if not hosts:
653
            hosts = self.hosts
654
            output( '*** Ping: testing ping reachability\n' )
655
        for node in hosts:
656
            output( '%s -> ' % node.name )
657
            for dest in hosts:
658
                if node != dest:
659
                    opts = ''
660
                    if timeout:
661
                        opts = '-W %s' % timeout
662
                    result = node.cmd( 'ping -c1 %s %s' % (opts, dest.IP()) )
663
                    outputs = self._parsePingFull( result )
664
                    sent, received, rttmin, rttavg, rttmax, rttdev = outputs
665
                    all_outputs.append( (node, dest, outputs) )
666
                    output( ( '%s ' % dest.name ) if received else 'X ' )
667
            output( '\n' )
668
        output( "*** Results: \n" )
669
        for outputs in all_outputs:
670
            src, dest, ping_outputs = outputs
671
            sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
672
            output( " %s->%s: %s/%s, " % (src, dest, sent, received ) )
673
            output( "rtt min/avg/max/mdev %0.3f/%0.3f/%0.3f/%0.3f ms\n" %
674
                    (rttmin, rttavg, rttmax, rttdev) )
675
        return all_outputs
676
677 4d1a9cdc Jon Hall
    def pingAll( self, timeout=None ):
678 80a8fa62 Bob Lantz
        """Ping between all hosts.
679
           returns: ploss packet loss percentage"""
680 4d1a9cdc Jon Hall
        return self.ping( timeout=timeout )
681 eeb9cb3c Brandon Heller
682 80a8fa62 Bob Lantz
    def pingPair( self ):
683
        """Ping between first two hosts, useful for testing.
684
           returns: ploss packet loss percentage"""
685 019bff82 Bob Lantz
        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
686 80a8fa62 Bob Lantz
        return self.ping( hosts=hosts )
687 eeb9cb3c Brandon Heller
688 1f1d590c Brandon Heller
    def pingAllFull( self ):
689
        """Ping between all hosts.
690
           returns: ploss packet loss percentage"""
691
        return self.pingFull()
692
693
    def pingPairFull( self ):
694
        """Ping between first two hosts, useful for testing.
695
           returns: ploss packet loss percentage"""
696
        hosts = [ self.hosts[ 0 ], self.hosts[ 1 ] ]
697
        return self.pingFull( hosts=hosts )
698
699 eeb9cb3c Brandon Heller
    @staticmethod
700 80a8fa62 Bob Lantz
    def _parseIperf( iperfOutput ):
701
        """Parse iperf output and return bandwidth.
702
           iperfOutput: string
703
           returns: result string"""
704 eeb9cb3c Brandon Heller
        r = r'([\d\.]+ \w+/sec)'
705 ad2fda25 Bob Lantz
        m = re.findall( r, iperfOutput )
706 eeb9cb3c Brandon Heller
        if m:
707 ad2fda25 Bob Lantz
            return m[-1]
708 eeb9cb3c Brandon Heller
        else:
709 ad2fda25 Bob Lantz
            # was: raise Exception(...)
710
            error( 'could not parse iperf output: ' + iperfOutput )
711
            return ''
712 80a8fa62 Bob Lantz
713 216a4b7c Bob Lantz
    # XXX This should be cleaned up
714
715 b1ec912d Bob Lantz
    def iperf( self, hosts=None, l4Type='TCP', udpBw='10M', fmt=None,
716 d3377bf9 Bob Lantz
               seconds=5):
717 80a8fa62 Bob Lantz
        """Run iperf between two hosts.
718 b1ec912d Bob Lantz
           hosts: list of hosts; if None, uses first and last hosts
719 80a8fa62 Bob Lantz
           l4Type: string, one of [ TCP, UDP ]
720 d3377bf9 Bob Lantz
           udpBw: bandwidth target for UDP test
721 b1ec912d Bob Lantz
           fmt: iperf format argument if any
722 d3377bf9 Bob Lantz
           seconds: iperf time to transmit
723
           returns: two-element array of [ server, client ] speeds
724 e1711f35 Bob Lantz
           note: send() is buffered, so client rate can be much higher than
725
           the actual transmission rate; on an unloaded system, server
726
           rate should be much closer to the actual receive rate"""
727 ad2fda25 Bob Lantz
        if not quietRun( 'which telnet' ):
728
            error( 'Cannot find telnet in $PATH - required for iperf test' )
729
            return
730 eeb9cb3c Brandon Heller
        if not hosts:
731 019bff82 Bob Lantz
            hosts = [ self.hosts[ 0 ], self.hosts[ -1 ] ]
732 eeb9cb3c Brandon Heller
        else:
733 80a8fa62 Bob Lantz
            assert len( hosts ) == 2
734 ec7b211c Bob Lantz
        client, server = hosts
735 cdeaca86 Brandon Heller
        output( '*** Iperf: testing ' + l4Type + ' bandwidth between ' )
736 ec7b211c Bob Lantz
        output( "%s and %s\n" % ( client.name, server.name ) )
737
        server.cmd( 'killall -9 iperf' )
738 80a8fa62 Bob Lantz
        iperfArgs = 'iperf '
739
        bwArgs = ''
740
        if l4Type == 'UDP':
741
            iperfArgs += '-u '
742
            bwArgs = '-b ' + udpBw + ' '
743
        elif l4Type != 'TCP':
744
            raise Exception( 'Unexpected l4 type: %s' % l4Type )
745 b1ec912d Bob Lantz
        if fmt:
746
            iperfArgs += '-f %s ' % fmt
747 ec7b211c Bob Lantz
        server.sendCmd( iperfArgs + '-s', printPid=True )
748
        servout = ''
749
        while server.lastPid is None:
750
            servout += server.monitor()
751 8856d284 Bob Lantz
        if l4Type == 'TCP':
752
            while 'Connected' not in client.cmd(
753 615ebb7a Brandon Heller
                    'sh -c "echo A | telnet -e A %s 5001"' % server.IP()):
754 13d25b41 Bob Lantz
                info( 'Waiting for iperf to start up...' )
755 8856d284 Bob Lantz
                sleep(.5)
756 d3377bf9 Bob Lantz
        cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
757
                             server.IP() + ' ' + bwArgs )
758 ec7b211c Bob Lantz
        debug( 'Client output: %s\n' % cliout )
759
        server.sendInt()
760
        servout += server.waitOutput()
761
        debug( 'Server output: %s\n' % servout )
762
        result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ]
763 80a8fa62 Bob Lantz
        if l4Type == 'UDP':
764
            result.insert( 0, udpBw )
765 cdeaca86 Brandon Heller
        output( '*** Results: %s\n' % result )
766 eeb9cb3c Brandon Heller
        return result
767
768 fcd01592 Brandon Heller
    def runCpuLimitTest( self, cpu, duration=5 ):
769
        """run CPU limit test with 'while true' processes.
770
        cpu: desired CPU fraction of each host
771 b1ec912d Bob Lantz
        duration: test duration in seconds (integer)
772 fcd01592 Brandon Heller
        returns a single list of measured CPU fractions as floats.
773
        """
774 ce781a18 cody burkard
        cores = int( quietRun( 'nproc' ) )
775 fcd01592 Brandon Heller
        pct = cpu * 100
776 ce781a18 cody burkard
        info( '*** Testing CPU %.0f%% bandwidth limit\n' % pct )
777 fcd01592 Brandon Heller
        hosts = self.hosts
778 ce781a18 cody burkard
        cores = int( quietRun( 'nproc' ) )
779
        # number of processes to run a while loop on per host
780
        num_procs = int( ceil( cores * cpu ) )
781
        pids = {}
782 fcd01592 Brandon Heller
        for h in hosts:
783 ce781a18 cody burkard
            pids[ h ] = []
784
            for _core in range( num_procs ):
785
                h.cmd( 'while true; do a=1; done &' )
786
                pids[ h ].append( h.cmd( 'echo $!' ).strip() )
787
        outputs = {}
788
        time = {}
789
        # get the initial cpu time for each host
790
        for host in hosts:
791
            outputs[ host ] = []
792 b1ec912d Bob Lantz
            with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
793
                       host, 'r' ) as f:
794 ce781a18 cody burkard
                time[ host ] = float( f.read() )
795 b1ec912d Bob Lantz
        for _ in range( duration ):
796 fcd01592 Brandon Heller
            sleep( 1 )
797 ce781a18 cody burkard
            for host in hosts:
798 b1ec912d Bob Lantz
                with open( '/sys/fs/cgroup/cpuacct/%s/cpuacct.usage' %
799
                           host, 'r' ) as f:
800 ce781a18 cody burkard
                    readTime = float( f.read() )
801
                outputs[ host ].append( ( ( readTime - time[ host ] )
802
                                        / 1000000000 ) / cores * 100 )
803
                time[ host ] = readTime
804
        for h, pids in pids.items():
805
            for pid in pids:
806
                h.cmd( 'kill -9 %s' % pid )
807 fcd01592 Brandon Heller
        cpu_fractions = []
808 ce781a18 cody burkard
        for _host, outputs in outputs.items():
809
            for pct in outputs:
810
                cpu_fractions.append( pct )
811 fcd01592 Brandon Heller
        output( '*** Results: %s\n' % cpu_fractions )
812
        return cpu_fractions
813
814 84a91a14 Bob Lantz
    # BL: I think this can be rewritten now that we have
815
    # a real link class.
816 c70aab0a Bob Lantz
    def configLinkStatus( self, src, dst, status ):
817
        """Change status of src <-> dst links.
818
           src: node name
819
           dst: node name
820
           status: string {up, down}"""
821 8d3c2859 Brandon Heller
        if src not in self.nameToNode:
822
            error( 'src not in network: %s\n' % src )
823
        elif dst not in self.nameToNode:
824
            error( 'dst not in network: %s\n' % dst )
825
        else:
826 9a8bdfd7 Bob Lantz
            if isinstance( src, basestring ):
827 8856d284 Bob Lantz
                src = self.nameToNode[ src ]
828 9a8bdfd7 Bob Lantz
            if isinstance( dst, basestring ):
829 8856d284 Bob Lantz
                dst = self.nameToNode[ dst ]
830
            connections = src.connectionsTo( dst )
831 fb2f6523 Bob Lantz
            if len( connections ) == 0:
832 8d3c2859 Brandon Heller
                error( 'src and dst not connected: %s %s\n' % ( src, dst) )
833 fb2f6523 Bob Lantz
            for srcIntf, dstIntf in connections:
834 8856d284 Bob Lantz
                result = srcIntf.ifconfig( status )
835 8d3c2859 Brandon Heller
                if result:
836
                    error( 'link src status change failed: %s\n' % result )
837 8856d284 Bob Lantz
                result = dstIntf.ifconfig( status )
838 8d3c2859 Brandon Heller
                if result:
839
                    error( 'link dst status change failed: %s\n' % result )
840
841 80a8fa62 Bob Lantz
    def interact( self ):
842
        "Start network and run our simple CLI."
843 eeb9cb3c Brandon Heller
        self.start()
844 496b5f9e Bob Lantz
        result = CLI( self )
845 eeb9cb3c Brandon Heller
        self.stop()
846
        return result
847 82b72072 Bob Lantz
848 8e3699ec Bob Lantz
    inited = False
849 14ff3ad3 Bob Lantz
850 8e3699ec Bob Lantz
    @classmethod
851
    def init( cls ):
852
        "Initialize Mininet"
853
        if cls.inited:
854
            return
855 bcfb3009 Brandon Heller
        ensureRoot()
856 8e3699ec Bob Lantz
        fixLimits()
857
        cls.inited = True
858
859 82b72072 Bob Lantz
860 84a91a14 Bob Lantz
class MininetWithControlNet( Mininet ):
861
862
    """Control network support:
863

864
       Create an explicit control network. Currently this is only
865
       used/usable with the user datapath.
866

867
       Notes:
868

869
       1. If the controller and switches are in the same (e.g. root)
870
          namespace, they can just use the loopback connection.
871

872
       2. If we can get unix domain sockets to work, we can use them
873
          instead of an explicit control network.
874

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

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

881
       5. Basically nobody ever used this code, so it has been moved
882 14ff3ad3 Bob Lantz
          into its own class.
883

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