Revision 80a8fa62 mininet/node.py

View differences:

mininet/node.py
1 1
#!/usr/bin/env python
2
'''Node objects for Mininet.'''
2
"Node objects for Mininet."
3 3

  
4 4
from subprocess import Popen, PIPE, STDOUT
5 5
import os
......
13 13
from mininet.util import quietRun, macColonHex, ipStr
14 14

  
15 15

  
16
class Node(object):
17
    '''A virtual network node is simply a shell in a network namespace.
18
       We communicate with it using pipes.'''
16
class Node( object ):
17
    """A virtual network node is simply a shell in a network namespace.
18
       We communicate with it using pipes."""
19 19
    inToNode = {}
20 20
    outToNode = {}
21 21

  
22
    def __init__(self, name, inNamespace = True):
22
    def __init__( self, name, inNamespace=True ):
23 23
        self.name = name
24 24
        closeFds = False # speed vs. memory use
25
        # xpg_echo is needed so we can echo our sentinel in sendCmd
26
        cmd = ['/bin/bash', '-O', 'xpg_echo']
25
        # xpgEcho is needed so we can echo our sentinel in sendCmd
26
        cmd = [ '/bin/bash', '-O', 'xpg_echo' ]
27 27
        self.inNamespace = inNamespace
28 28
        if self.inNamespace:
29
            cmd = ['netns'] + cmd
30
        self.shell = Popen(cmd, stdin = PIPE, stdout = PIPE, stderr = STDOUT,
31
            close_fds = closeFds)
29
            cmd = [ 'netns' ] + cmd
30
        self.shell = Popen( cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT,
31
            closeFds=closeFds )
32 32
        self.stdin = self.shell.stdin
33 33
        self.stdout = self.shell.stdout
34 34
        self.pollOut = select.poll()
35
        self.pollOut.register(self.stdout)
35
        self.pollOut.register( self.stdout )
36 36
        # Maintain mapping between file descriptors and nodes
37 37
        # This could be useful for monitoring multiple nodes
38 38
        # using select.poll()
39
        self.outToNode[self.stdout.fileno()] = self
40
        self.inToNode[self.stdin.fileno()] = self
39
        self.outToNode[ self.stdout.fileno() ] = self
40
        self.inToNode[ self.stdin.fileno() ] = self
41 41
        self.pid = self.shell.pid
42 42
        self.intfCount = 0
43 43
        self.intfs = [] # list of interface names, as strings
......
48 48
        self.ports = {} # dict of ints to interface strings
49 49
                        # replace with Port object, eventually
50 50

  
51
    def fdToNode(self, f):
52
        '''Insert docstring.
51
    def fdToNode( self, f ):
52
        """Insert docstring.
53
           f: unknown
54
           returns: bool unknown"""
55
        node = self.outToNode.get( f )
56
        return node or self.inToNode.get( f )
53 57

  
54
        @param f unknown
55
        @return bool unknown
56
        '''
57
        node = self.outToNode.get(f)
58
        return node or self.inToNode.get(f)
59

  
60
    def cleanup(self):
61
        '''Help python collect its garbage.'''
58
    def cleanup( self ):
59
        "Help python collect its garbage."
62 60
        self.shell = None
63 61

  
64 62
    # Subshell I/O, commands and control
65
    def read(self, fileno_max):
66
        '''Insert docstring.
67

  
68
        @param fileno_max unknown
69
        '''
70
        return os.read(self.stdout.fileno(), fileno_max)
71

  
72
    def write(self, data):
73
        '''Write data to node.
74

  
75
        @param data string
76
        '''
77
        os.write(self.stdin.fileno(), data)
78

  
79
    def terminate(self):
80
        '''Send kill signal to Node and cleanup after it.'''
81
        os.kill(self.pid, signal.SIGKILL)
63
    def read( self, filenoMax ):
64
        """Insert docstring.
65
           filenoMax: unknown"""
66
        return os.read( self.stdout.fileno(), filenoMax )
67

  
68
    def write( self, data ):
69
        """Write data to node.
70
           data: string"""
71
        os.write( self.stdin.fileno(), data )
72

  
73
    def terminate( self ):
74
        "Send kill signal to Node and cleanup after it."
75
        os.kill( self.pid, signal.SIGKILL )
82 76
        self.cleanup()
83 77

  
84
    def stop(self):
85
        '''Stop node.'''
78
    def stop( self ):
79
        "Stop node."
86 80
        self.terminate()
87 81

  
88
    def waitReadable(self):
89
        '''Poll on node.'''
82
    def waitReadable( self ):
83
        "Poll on node."
90 84
        self.pollOut.poll()
91 85

  
92
    def sendCmd(self, cmd):
93
        '''Send a command, followed by a command to echo a sentinel,
94
           and return without waiting for the command to complete.'''
86
    def sendCmd( self, cmd ):
87
        """Send a command, followed by a command to echo a sentinel,
88
           and return without waiting for the command to complete."""
95 89
        assert not self.waiting
96
        if cmd[-1] == '&':
90
        if cmd[ -1 ] == '&':
97 91
            separator = '&'
98
            cmd = cmd[:-1]
92
            cmd = cmd[ :-1 ]
99 93
        else:
100 94
            separator = ';'
101
        if isinstance(cmd, list):
102
            cmd = ' '.join(cmd)
103
        self.write(cmd + separator + ' echo -n "\\0177" \n')
95
        if isinstance( cmd, list ):
96
            cmd = ' '.join( cmd )
97
        self.write( cmd + separator + ' echo -n "\\0177" \n' )
104 98
        self.waiting = True
105 99

  
106
    def monitor(self):
107
        '''Monitor the output of a command, returning (done, data).'''
100
    def monitor( self ):
101
        "Monitor the output of a command, returning (done, data)."
108 102
        assert self.waiting
109 103
        self.waitReadable()
110
        data = self.read(1024)
111
        if len(data) > 0 and data[-1] == chr(0177):
104
        data = self.read( 1024 )
105
        if len( data ) > 0 and data[ -1 ] == chr( 0177 ):
112 106
            self.waiting = False
113
            return True, data[:-1]
107
            return True, data[ :-1 ]
114 108
        else:
115 109
            return False, data
116 110

  
117
    def sendInt(self):
118
        '''Send ^C, hopefully interrupting a running subprocess.'''
119
        self.write(chr(3))
120

  
121
    def waitOutput(self):
122
        '''Wait for a command to complete.
111
    def sendInt( self ):
112
        "Send ^C, hopefully interrupting a running subprocess."
113
        self.write( chr( 3 ) )
123 114

  
124
        Completion is signaled by a sentinel character, ASCII(127) appearing in
125
        the output stream.  Wait for the sentinel and return the output,
126
        including trailing newline.
127
        '''
115
    def waitOutput( self ):
116
        """Wait for a command to complete.
117
           Completion is signaled by a sentinel character, ASCII( 127 )
118
           appearing in the output stream.  Wait for the sentinel and return
119
           the output, including trailing newline."""
128 120
        assert self.waiting
129 121
        output = ''
130 122
        while True:
131 123
            self.waitReadable()
132
            data = self.read(1024)
133
            if len(data) > 0  and data[-1] == chr(0177):
134
                output += data[:-1]
124
            data = self.read( 1024 )
125
            if len( data ) > 0  and data[ -1 ] == chr( 0177 ):
126
                output += data[ :-1 ]
135 127
                break
136 128
            else:
137 129
                output += data
138 130
        self.waiting = False
139 131
        return output
140 132

  
141
    def cmd(self, cmd):
142
        '''Send a command, wait for output, and return it.
143

  
144
        @param cmd string
145
        '''
146
        self.sendCmd(cmd)
133
    def cmd( self, cmd ):
134
        """Send a command, wait for output, and return it.
135
           cmd: string"""
136
        self.sendCmd( cmd )
147 137
        return self.waitOutput()
148 138

  
149
    def cmdPrint(self, cmd):
150
        '''Call cmd and printing its output
151

  
152
        @param cmd string
153
        '''
154
        #lg.info('*** %s : %s', self.name, cmd)
155
        result = self.cmd(cmd)
156
        #lg.info('%s\n', result)
139
    def cmdPrint( self, cmd ):
140
        """Call cmd and printing its output
141
           cmd: string"""
142
        #lg.info( '*** %s : %s', self.name, cmd )
143
        result = self.cmd( cmd )
144
        #lg.info( '%s\n', result )
157 145
        return result
158 146

  
159 147
    # Interface management, configuration, and routing
160
    def intfName(self, n):
161
        '''Construct a canonical interface name node-intf for interface N.'''
162
        return self.name + '-eth' + repr(n)
148
    def intfName( self, n ):
149
        "Construct a canonical interface name node-intf for interface N."
150
        return self.name + '-eth' + repr( n )
163 151

  
164
    def newIntf(self):
165
        '''Reserve and return a new interface name.'''
166
        intfName = self.intfName(self.intfCount)
152
    def newIntf( self ):
153
        "Reserve and return a new interface name."
154
        intfName = self.intfName( self.intfCount )
167 155
        self.intfCount += 1
168
        self.intfs += [intfName]
156
        self.intfs += [ intfName ]
169 157
        return intfName
170 158

  
171
    def setMAC(self, intf, mac):
172
        '''Set the MAC address for an interface.
173

  
174
        @param mac MAC address as unsigned int
175
        '''
176
        mac_str = macColonHex(mac)
177
        result = self.cmd(['ifconfig', intf, 'down'])
178
        result += self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
179
        result += self.cmd(['ifconfig', intf, 'up'])
159
    def setMAC( self, intf, mac ):
160
        """Set the MAC address for an interface.
161
           mac: MAC address as unsigned int"""
162
        macStr = macColonHex( mac )
163
        result = self.cmd( [ 'ifconfig', intf, 'down' ] )
164
        result += self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
165
        result += self.cmd( [ 'ifconfig', intf, 'up' ] )
180 166
        return result
181 167

  
182
    def setARP(self, ip, mac):
183
        '''Add an ARP entry.
184

  
185
        @param ip IP address as unsigned int
186
        @param mac MAC address as unsigned int
187
        '''
188
        ip_str = ipStr(ip)
189
        mac_str = macColonHex(mac)
190
        result = self.cmd(['arp', '-s', ip_str, mac_str])
168
    def setARP( self, ip, mac ):
169
        """Add an ARP entry.
170
           ip: IP address as unsigned int
171
           mac: MAC address as unsigned int"""
172
        ip = ipStr( ip )
173
        mac = macColonHex( mac )
174
        result = self.cmd( [ 'arp', '-s', ip, mac ] )
191 175
        return result
192 176

  
193
    def setIP(self, intf, ip, bits):
194
        '''Set the IP address for an interface.
195

  
196
        @param intf string, interface name
197
        @param ip IP address as a string
198
        @param bits
199
        '''
200
        result = self.cmd(['ifconfig', intf, ip + bits, 'up'])
201
        self.ips[intf] = ip
177
    def setIP( self, intf, ip, bits ):
178
        """Set the IP address for an interface.
179
           intf: string, interface name
180
           ip: IP address as a string
181
           bits:"""
182
        result = self.cmd( [ 'ifconfig', intf, ip + bits, 'up' ] )
183
        self.ips[ intf ] = ip
202 184
        return result
203 185

  
204
    def setHostRoute(self, ip, intf):
205
        '''Add route to host.
206

  
207
        @param ip IP address as dotted decimal
208
        @param intf string, interface name
209
        '''
210
        return self.cmd('route add -host ' + ip + ' dev ' + intf)
186
    def setHostRoute( self, ip, intf ):
187
        """Add route to host.
188
           ip: IP address as dotted decimal
189
           intf: string, interface name"""
190
        return self.cmd( 'route add -host ' + ip + ' dev ' + intf )
211 191

  
212
    def setDefaultRoute(self, intf):
213
        '''Set the default route to go through intf.
192
    def setDefaultRoute( self, intf ):
193
        """Set the default route to go through intf.
194
           intf: string, interface name"""
195
        self.cmd( 'ip route flush' )
196
        return self.cmd( 'route add default ' + intf )
214 197

  
215
        @param intf string, interface name
216
        '''
217
        self.cmd('ip route flush')
218
        return self.cmd('route add default ' + intf)
198
    def IP( self ):
199
        "Return IP address of first interface"
200
        if len( self.intfs ) > 0:
201
            return self.ips.get( self.intfs[ 0 ], None )
219 202

  
220
    def IP(self):
221
        '''Return IP address of first interface'''
222
        if len(self.intfs) > 0:
223
            return self.ips.get(self.intfs[0], None)
224

  
225
    def intfIsUp(self):
226
        '''Check if one of our interfaces is up.'''
227
        return 'UP' in self.cmd('ifconfig ' + self.intfs[0])
203
    def intfIsUp( self ):
204
        "Check if one of our interfaces is up."
205
        return 'UP' in self.cmd( 'ifconfig ' + self.intfs[ 0 ] )
228 206

  
229 207
    # Other methods
230
    def __str__(self):
208
    def __str__( self ):
231 209
        result = self.name + ':'
232 210
        if self.IP():
233 211
            result += ' IP=' + self.IP()
234
        result += ' intfs=' + ','.join(self.intfs)
235
        result += ' waiting=' + repr(self.waiting)
212
        result += ' intfs=' + ','.join( self.intfs )
213
        result += ' waiting=' + repr( self.waiting )
236 214
        return result
237 215

  
238 216

  
239
class Host(Node):
240
    '''A host is simply a Node.'''
217
class Host( Node ):
218
    "A host is simply a Node."
241 219
    pass
242 220

  
243 221

  
244
class Switch(Node):
245
    '''A Switch is a Node that is running (or has execed)
246
       an OpenFlow switch.'''
247

  
248
    def sendCmd(self, cmd):
249
        '''Send command to Node.
222
class Switch( Node ):
223
    """A Switch is a Node that is running ( or has execed )
224
       an OpenFlow switch."""
250 225

  
251
        @param cmd string
252
        '''
226
    def sendCmd( self, cmd ):
227
        """Send command to Node.
228
           cmd: string"""
253 229
        if not self.execed:
254
            return Node.sendCmd(self, cmd)
230
            return Node.sendCmd( self, cmd )
255 231
        else:
256
            lg.error('*** Error: %s has execed and cannot accept commands' %
257
                     self.name)
232
            lg.error( '*** Error: %s has execed and cannot accept commands' %
233
                     self.name )
258 234

  
259
    def monitor(self):
260
        '''Monitor node.'''
235
    def monitor( self ):
236
        "Monitor node."
261 237
        if not self.execed:
262
            return Node.monitor(self)
238
            return Node.monitor( self )
263 239
        else:
264 240
            return True, ''
265 241

  
266 242

  
267
class UserSwitch(Switch):
268
    '''User-space switch.
269

  
270
    Currently only works in the root namespace.
271
    '''
272

  
273
    def __init__(self, name):
274
        '''Init.
275

  
276
        @param name
277
        '''
278
        Switch.__init__(self, name, inNamespace = False)
279

  
280
    def start(self, controllers):
281
        '''Start OpenFlow reference user datapath.
243
class UserSwitch( Switch ):
244
    """User-space switch.
245
       Currently only works in the root namespace."""
282 246

  
283
        Log to /tmp/sN-{ofd,ofp}.log.
247
    def __init__( self, name ):
248
        """Init.
249
           name: name for the switch"""
250
        Switch.__init__( self, name, inNamespace=False )
284 251

  
285
        @param controllers dict of controller names to objects
286
        '''
252
    def start( self, controllers ):
253
        """Start OpenFlow reference user datapath.
254
           Log to /tmp/sN-{ ofd,ofp }.log.
255
           controllers: dict of controller names to objects"""
287 256
        if 'c0' not in controllers:
288
            raise Exception('User datapath start() requires controller c0')
289
        controller = controllers['c0']
257
            raise Exception( 'User datapath start() requires controller c0' )
258
        controller = controllers[ 'c0' ]
290 259
        ofdlog = '/tmp/' + self.name + '-ofd.log'
291 260
        ofplog = '/tmp/' + self.name + '-ofp.log'
292
        self.cmd('ifconfig lo up')
261
        self.cmd( 'ifconfig lo up' )
293 262
        intfs = self.intfs
294
        self.cmdPrint('ofdatapath -i ' + ','.join(intfs) + ' punix:/tmp/' +
295
                      self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &')
296
        self.cmdPrint('ofprotocol unix:/tmp/' + self.name + ' tcp:' +
263
        self.cmdPrint( 'ofdatapath -i ' + ','.join( intfs ) + ' punix:/tmp/' +
264
                      self.name + ' 1> ' + ofdlog + ' 2> ' + ofdlog + ' &' )
265
        self.cmdPrint( 'ofprotocol unix:/tmp/' + self.name + ' tcp:' +
297 266
                      controller.IP() + ' --fail=closed 1> ' + ofplog + ' 2>' +
298
                      ofplog + ' &')
267
                      ofplog + ' &' )
299 268

  
300
    def stop(self):
301
        '''Stop OpenFlow reference user datapath.'''
302
        self.cmd('kill %ofdatapath')
303
        self.cmd('kill %ofprotocol')
269
    def stop( self ):
270
        "Stop OpenFlow reference user datapath."
271
        self.cmd( 'kill %ofdatapath' )
272
        self.cmd( 'kill %ofprotocol' )
304 273

  
305 274

  
306
class KernelSwitch(Switch):
307
    '''Kernel-space switch.
275
class KernelSwitch( Switch ):
276
    """Kernel-space switch.
277
       Currently only works in the root namespace."""
308 278

  
309
    Currently only works in the root namespace.
310
    '''
311

  
312
    def __init__(self, name, dp = None, dpid = None):
313
        '''Init.
314

  
315
        @param name
316
        @param dp netlink id (0, 1, 2, ...)
317
        @param dpid datapath ID as unsigned int; random value if None
318
        '''
319
        Switch.__init__(self, name, inNamespace = False)
279
    def __init__( self, name, dp=None, dpid=None ):
280
        """Init.
281
           name:
282
           dp: netlink id ( 0, 1, 2, ... )
283
           dpid: datapath ID as unsigned int; random value if None"""
284
        Switch.__init__( self, name, inNamespace=False )
320 285
        self.dp = dp
321 286
        self.dpid = dpid
322 287

  
323
    def start(self, controllers):
324
        '''Start up reference kernel datapath.'''
288
    def start( self, controllers ):
289
        "Start up reference kernel datapath."
325 290
        ofplog = '/tmp/' + self.name + '-ofp.log'
326
        quietRun('ifconfig lo up')
291
        quietRun( 'ifconfig lo up' )
327 292
        # Delete local datapath if it exists;
328 293
        # then create a new one monitoring the given interfaces
329
        quietRun('dpctl deldp nl:%i' % self.dp)
330
        self.cmdPrint('dpctl adddp nl:%i' % self.dp)
294
        quietRun( 'dpctl deldp nl:%i' % self.dp )
295
        self.cmdPrint( 'dpctl adddp nl:%i' % self.dp )
331 296
        if self.dpid:
332 297
            intf = 'of%i' % self.dp
333
            mac_str = macColonHex(self.dpid)
334
            self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
335

  
336
        if len(self.ports) != max(self.ports.keys()) + 1:
337
            raise Exception('only contiguous, zero-indexed port ranges'
338
                            'supported: %s' % self.ports)
339
        intfs = [self.ports[port] for port in self.ports.keys()]
340
        self.cmdPrint('dpctl addif nl:' + str(self.dp) + ' ' + ' '.join(intfs))
298
            macStr = macColonHex( self.dpid )
299
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
300

  
301
        if len( self.ports ) != max( self.ports.keys() ) + 1:
302
            raise Exception( 'only contiguous, zero-indexed port ranges'
303
                            'supported: %s' % self.ports )
304
        intfs = [ self.ports[ port ] for port in self.ports.keys() ]
305
        self.cmdPrint( 'dpctl addif nl:' + str( self.dp ) + ' ' +
306
            ' '.join( intfs ) )
341 307
        # Run protocol daemon
342
        self.cmdPrint('ofprotocol nl:' + str(self.dp) + ' tcp:' +
343
                      controllers['c0'].IP() + ':' +
344
                      str(controllers['c0'].port) +
345
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &')
308
        self.cmdPrint( 'ofprotocol nl:' + str( self.dp ) + ' tcp:' +
309
                      controllers[ 'c0' ].IP() + ':' +
310
                      str( controllers[ 'c0' ].port ) +
311
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
346 312
        self.execed = False
347 313

  
348
    def stop(self):
349
        '''Terminate kernel datapath.'''
350
        quietRun('dpctl deldp nl:%i' % self.dp)
314
    def stop( self ):
315
        "Terminate kernel datapath."
316
        quietRun( 'dpctl deldp nl:%i' % self.dp )
351 317
        # In theory the interfaces should go away after we shut down.
352 318
        # However, this takes time, so we're better off to remove them
353 319
        # explicitly so that we won't get errors if we run before they
354 320
        # have been removed by the kernel. Unfortunately this is very slow.
355
        self.cmd('kill %ofprotocol')
321
        self.cmd( 'kill %ofprotocol' )
356 322
        for intf in self.intfs:
357
            quietRun('ip link del ' + intf)
358
            lg.info('.')
359

  
323
            quietRun( 'ip link del ' + intf )
324
            lg.info( '.' )
360 325

  
361
class OVSKernelSwitch(Switch):
362
    '''Open VSwitch kernel-space switch.
363 326

  
364
    Currently only works in the root namespace.
365
    '''
327
class OVSKernelSwitch( Switch ):
328
    """Open VSwitch kernel-space switch.
329
       Currently only works in the root namespace."""
366 330

  
367
    def __init__(self, name, dp = None, dpid = None):
368
        '''Init.
369

  
370
        @param name
371
        @param dp netlink id (0, 1, 2, ...)
372
        @param dpid datapath ID as unsigned int; random value if None
373
        '''
374
        Switch.__init__(self, name, inNamespace = False)
331
    def __init__( self, name, dp=None, dpid=None ):
332
        """Init.
333
           name:
334
           dp: netlink id ( 0, 1, 2, ... )
335
           dpid: datapath ID as unsigned int; random value if None"""
336
        Switch.__init__( self, name, inNamespace=False )
375 337
        self.dp = dp
376 338
        self.dpid = dpid
377 339

  
378
    def start(self, controllers):
379
        '''Start up kernel datapath.'''
340
    def start( self, controllers ):
341
        "Start up kernel datapath."
380 342
        ofplog = '/tmp/' + self.name + '-ofp.log'
381
        quietRun('ifconfig lo up')
343
        quietRun( 'ifconfig lo up' )
382 344
        # Delete local datapath if it exists;
383 345
        # then create a new one monitoring the given interfaces
384
        quietRun('ovs-dpctl del-dp dp%i' % self.dp)
385
        self.cmdPrint('ovs-dpctl add-dp dp%i' % self.dp)
346
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
347
        self.cmdPrint( 'ovs-dpctl add-dp dp%i' % self.dp )
386 348
        if self.dpid:
387 349
            intf = 'dp' % self.dp
388
            mac_str = macColonHex(self.dpid)
389
            self.cmd(['ifconfig', intf, 'hw', 'ether', mac_str])
390

  
391
        if len(self.ports) != max(self.ports.keys()) + 1:
392
            raise Exception('only contiguous, zero-indexed port ranges'
393
                            'supported: %s' % self.ports)
394
        intfs = [self.ports[port] for port in self.ports.keys()]
395
        self.cmdPrint('ovs-dpctl add-if dp' + str(self.dp) + ' ' +
396
                      ' '.join(intfs))
350
            macStr = macColonHex( self.dpid )
351
            self.cmd( [ 'ifconfig', intf, 'hw', 'ether', macStr ] )
352

  
353
        if len( self.ports ) != max( self.ports.keys() ) + 1:
354
            raise Exception( 'only contiguous, zero-indexed port ranges'
355
                            'supported: %s' % self.ports )
356
        intfs = [ self.ports[ port ] for port in self.ports.keys() ]
357
        self.cmdPrint( 'ovs-dpctl add-if dp' + str( self.dp ) + ' ' +
358
                      ' '.join( intfs ) )
397 359
        # Run protocol daemon
398
        self.cmdPrint('ovs-openflowd dp' + str(self.dp) + ' tcp:' +
399
                      controllers['c0'].IP() + ':' +
400
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &')
360
        self.cmdPrint( 'ovs-openflowd dp' + str( self.dp ) + ' tcp:' +
361
                      controllers[ 'c0' ].IP() + ':' +
362
                      ' --fail=closed 1> ' + ofplog + ' 2>' + ofplog + ' &' )
401 363
        self.execed = False
402 364

  
403
    def stop(self):
404
        '''Terminate kernel datapath.'''
405
        quietRun('ovs-dpctl del-dp dp%i' % self.dp)
365
    def stop( self ):
366
        "Terminate kernel datapath."
367
        quietRun( 'ovs-dpctl del-dp dp%i' % self.dp )
406 368
        # In theory the interfaces should go away after we shut down.
407 369
        # However, this takes time, so we're better off to remove them
408 370
        # explicitly so that we won't get errors if we run before they
409 371
        # have been removed by the kernel. Unfortunately this is very slow.
410
        self.cmd('kill %ovs-openflowd')
372
        self.cmd( 'kill %ovs-openflowd' )
411 373
        for intf in self.intfs:
412
            quietRun('ip link del ' + intf)
413
            lg.info('.')
374
            quietRun( 'ip link del ' + intf )
375
            lg.info( '.' )
414 376

  
415 377

  
416
class Controller(Node):
417
    '''A Controller is a Node that is running (or has execed) an
418
      OpenFlow controller.'''
378
class Controller( Node ):
379
    """A Controller is a Node that is running ( or has execed ) an
380
       OpenFlow controller."""
419 381

  
420
    def __init__(self, name, inNamespace = False, controller = 'controller',
421
                 cargs = '-v ptcp:', cdir = None, ip_address="127.0.0.1",
422
                 port = 6633):
382
    def __init__( self, name, inNamespace=False, controller='controller',
383
                 cargs='-v ptcp:', cdir=None, ipAddress="127.0.0.1",
384
                 port=6633 ):
423 385
        self.controller = controller
424 386
        self.cargs = cargs
425 387
        self.cdir = cdir
426
        self.ip_address = ip_address
388
        self.ipAddress = ipAddress
427 389
        self.port = port
428
        Node.__init__(self, name, inNamespace = inNamespace)
429

  
430
    def start(self):
431
        '''Start <controller> <args> on controller.
390
        Node.__init__( self, name, inNamespace=inNamespace )
432 391

  
433
        Log to /tmp/cN.log
434
        '''
392
    def start( self ):
393
        """Start <controller> <args> on controller.
394
           Log to /tmp/cN.log"""
435 395
        cout = '/tmp/' + self.name + '.log'
436 396
        if self.cdir is not None:
437
            self.cmdPrint('cd ' + self.cdir)
438
        self.cmdPrint(self.controller + ' ' + self.cargs +
439
            ' 1> ' + cout + ' 2> ' + cout + ' &')
397
            self.cmdPrint( 'cd ' + self.cdir )
398
        self.cmdPrint( self.controller + ' ' + self.cargs +
399
            ' 1> ' + cout + ' 2> ' + cout + ' &' )
440 400
        self.execed = False
441 401

  
442
    def stop(self):
443
        '''Stop controller.'''
444
        self.cmd('kill %' + self.controller)
402
    def stop( self ):
403
        "Stop controller."
404
        self.cmd( 'kill %' + self.controller )
445 405
        self.terminate()
446 406

  
447
    def IP(self):
448
        '''Return IP address of the Controller'''
449
        return self.ip_address
450

  
407
    def IP( self ):
408
        "Return IP address of the Controller"
409
        return self.ipAddress
451 410

  
452
class ControllerParams(object):
453
    '''Container for controller IP parameters.'''
454 411

  
455
    def __init__(self, ip, subnet_size):
456
        '''Init.
412
class ControllerParams( object ):
413
    "Container for controller IP parameters."
457 414

  
458
        @param ip integer, controller IP
459
        @param subnet_size integer, ex 8 for slash-8, covering 17M
460
        '''
415
    def __init__( self, ip, subnetSize ):
416
        """Init.
417
           ip: integer, controller IP
418
            subnetSize: integer, ex 8 for slash-8, covering 17M"""
461 419
        self.ip = ip
462
        self.subnet_size = subnet_size
463

  
464

  
465
class NOX(Controller):
466
    '''Controller to run a NOX application.'''
467

  
468
    def __init__(self, name, inNamespace = False, nox_args = None, **kwargs):
469
        '''Init.
470

  
471
        @param name name to give controller
472
        @param nox_args list of args, or single arg, to pass to NOX
473
        '''
474
        if type(nox_args) != list:
475
            nox_args = [nox_args]
476
        if not nox_args:
477
            nox_args = ['packetdump']
478
        nox_core_dir = os.environ['NOX_CORE_DIR']
479
        if not nox_core_dir:
480
            raise Exception('please set NOX_CORE_DIR env var\n')
481
        Controller.__init__(self, name,
482
            controller = nox_core_dir + '/nox_core',
483
            cargs = '--libdir=/usr/local/lib -v -i ptcp: ' + \
484
                    ' '.join(nox_args),
485
            cdir = nox_core_dir, **kwargs)
486

  
487

  
488
class RemoteController(Controller):
489
    '''Controller running outside of Mininet's control.'''
490

  
491
    def __init__(self, name, inNamespace = False, ip_address = '127.0.0.1',
492
                 port = 6633):
493
        '''Init.
494

  
495
        @param name name to give controller
496
        @param ip_address the IP address where the remote controller is
497
            listening
498
        @param port the port where the remote controller is listening
499
        '''
500
        Controller.__init__(self, name, ip_address = ip_address, port = port)
501

  
502
    def start(self):
503
        '''Overridden to do nothing.'''
420
        self.subnetSize = subnetSize
421

  
422

  
423
class NOX( Controller ):
424
    "Controller to run a NOX application."
425

  
426
    def __init__( self, name, inNamespace=False, noxArgs=None, **kwargs ):
427
        """Init.
428
           name: name to give controller
429
           noxArgs: list of args, or single arg, to pass to NOX"""
430
        if type( noxArgs ) != list:
431
            noxArgs = [ noxArgs ]
432
        if not noxArgs:
433
            noxArgs = [ 'packetdump' ]
434
        noxCoreDir = os.environ[ 'NOX_CORE_DIR' ]
435
        if not noxCoreDir:
436
            raise Exception( 'please set NOX_CORE_DIR env var\n' )
437
        Controller.__init__( self, name,
438
            controller=noxCoreDir + '/nox_core',
439
            cargs='--libdir=/usr/local/lib -v -i ptcp: ' + \
440
                    ' '.join( noxArgs ),
441
            cdir = noxCoreDir, **kwargs )
442

  
443

  
444
class RemoteController( Controller ):
445
    "Controller running outside of Mininet's control."
446

  
447
    def __init__( self, name, inNamespace=False, ipAddress='127.0.0.1',
448
                 port=6633 ):
449
        """Init.
450
           name: name to give controller
451
           ipAddress: the IP address where the remote controller is
452
           listening
453
           port: the port where the remote controller is listening"""
454
        Controller.__init__( self, name, ipAddress=ipAddress, port=port )
455

  
456
    def start( self ):
457
        "Overridden to do nothing."
504 458
        return
505 459

  
506
    def stop(self):
507
        '''Overridden to do nothing.'''
460
    def stop( self ):
461
        "Overridden to do nothing."
508 462
        return

Also available in: Unified diff