mininet / mininet / util.py @ 281f6e59
History | View | Annotate | Download (4.88 KB)
1 |
#!/usr/bin/env python
|
---|---|
2 |
"Utility functions for Mininet."
|
3 |
|
4 |
from time import sleep |
5 |
from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE |
6 |
import select |
7 |
from subprocess import call, check_call, Popen, PIPE, STDOUT |
8 |
|
9 |
from mininet.log import lg |
10 |
|
11 |
def run( cmd ): |
12 |
"""Simple interface to subprocess.call()
|
13 |
cmd: list of command params"""
|
14 |
return call( cmd.split( ' ' ) ) |
15 |
|
16 |
def checkRun( cmd ): |
17 |
"""Simple interface to subprocess.check_call()
|
18 |
cmd: list of command params"""
|
19 |
check_call( cmd.split( ' ' ) )
|
20 |
|
21 |
def quietRun( cmd ): |
22 |
"""Run a command, routing stderr to stdout, and return the output.
|
23 |
cmd: list of command params"""
|
24 |
if isinstance( cmd, str ): |
25 |
cmd = cmd.split( ' ' )
|
26 |
popen = Popen( cmd, stdout=PIPE, stderr=STDOUT ) |
27 |
# We can't use Popen.communicate() because it uses
|
28 |
# select(), which can't handle
|
29 |
# high file descriptor numbers! poll() can, however.
|
30 |
output = ''
|
31 |
readable = select.poll() |
32 |
readable.register( popen.stdout ) |
33 |
while True: |
34 |
while readable.poll():
|
35 |
data = popen.stdout.read( 1024 )
|
36 |
if len( data ) == 0: |
37 |
break
|
38 |
output += data |
39 |
popen.poll() |
40 |
if popen.returncode != None: |
41 |
break
|
42 |
return output
|
43 |
|
44 |
# Interface management
|
45 |
#
|
46 |
# Interfaces are managed as strings which are simply the
|
47 |
# interface names, of the form 'nodeN-ethM'.
|
48 |
#
|
49 |
# To connect nodes, we create a pair of veth interfaces, and then place them
|
50 |
# in the pair of nodes that we want to communicate. We then update the node's
|
51 |
# list of interfaces and connectivity map.
|
52 |
#
|
53 |
# For the kernel datapath, switch interfaces
|
54 |
# live in the root namespace and thus do not have to be
|
55 |
# explicitly moved.
|
56 |
|
57 |
def makeIntfPair( intf1, intf2 ): |
58 |
"""Make a veth pair connecting intf1 and intf2.
|
59 |
intf1: string, interface
|
60 |
intf2: string, interface
|
61 |
returns: success boolean"""
|
62 |
# Delete any old interfaces with the same names
|
63 |
quietRun( 'ip link del ' + intf1 )
|
64 |
quietRun( 'ip link del ' + intf2 )
|
65 |
# Create new pair
|
66 |
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 |
67 |
return checkRun( cmd )
|
68 |
|
69 |
def retry( retries, delaySecs, fn, *args, **keywords ): |
70 |
"""Try something several times before giving up.
|
71 |
n: number of times to retry
|
72 |
delaySecs: wait this long between tries
|
73 |
fn: function to call
|
74 |
args: args to apply to function call"""
|
75 |
tries = 0
|
76 |
while not fn( *args, **keywords ) and tries < retries: |
77 |
sleep( delaySecs ) |
78 |
tries += 1
|
79 |
if tries >= retries:
|
80 |
lg.error( "*** gave up after %i retries\n" % tries )
|
81 |
exit( 1 ) |
82 |
|
83 |
def moveIntfNoRetry( intf, node, printError=False ): |
84 |
"""Move interface to node, without retrying.
|
85 |
intf: string, interface
|
86 |
node: Node object
|
87 |
printError: if true, print error"""
|
88 |
cmd = 'ip link set ' + intf + ' netns ' + repr( node.pid ) |
89 |
quietRun( cmd ) |
90 |
links = node.cmd( 'ip link show' )
|
91 |
if not intf in links: |
92 |
if printError:
|
93 |
lg.error( '*** Error: moveIntf: ' + intf +
|
94 |
' not successfully moved to ' + node.name + '\n' ) |
95 |
return False |
96 |
return True |
97 |
|
98 |
def moveIntf( intf, node, printError=False, retries=3, delaySecs=0.001 ): |
99 |
"""Move interface to node, retrying on failure.
|
100 |
intf: string, interface
|
101 |
node: Node object
|
102 |
printError: if true, print error"""
|
103 |
retry( retries, delaySecs, moveIntfNoRetry, intf, node, printError ) |
104 |
|
105 |
def createLink( node1, node2, retries=10, delaySecs=0.001 ): |
106 |
"""Create a link between nodes, making an interface for each.
|
107 |
node1: Node object
|
108 |
node2: Node object"""
|
109 |
intf1 = node1.newIntf() |
110 |
intf2 = node2.newIntf() |
111 |
makeIntfPair( intf1, intf2 ) |
112 |
if node1.inNamespace:
|
113 |
retry( retries, delaySecs, moveIntf, intf1, node1 ) |
114 |
if node2.inNamespace:
|
115 |
retry( retries, delaySecs, moveIntf, intf2, node2 ) |
116 |
node1.connection[ intf1 ] = ( node2, intf2 ) |
117 |
node2.connection[ intf2 ] = ( node1, intf1 ) |
118 |
return intf1, intf2
|
119 |
|
120 |
def fixLimits(): |
121 |
"Fix ridiculously small resource limits."
|
122 |
setrlimit( RLIMIT_NPROC, ( 4096, 8192 ) ) |
123 |
setrlimit( RLIMIT_NOFILE, ( 16384, 32768 ) ) |
124 |
|
125 |
def _colonHex( val, bytes ): |
126 |
"""Generate colon-hex string.
|
127 |
val: input as unsigned int
|
128 |
bytes: number of bytes to convert
|
129 |
returns: chStr colon-hex string"""
|
130 |
pieces = [] |
131 |
for i in range( bytes - 1, -1, -1 ): |
132 |
piece = ( ( 0xff << ( i * 8 ) ) & val ) >> ( i * 8 ) |
133 |
pieces.append( '%02x' % piece )
|
134 |
chStr = ':'.join( pieces )
|
135 |
return chStr
|
136 |
|
137 |
def macColonHex( mac ): |
138 |
"""Generate MAC colon-hex string from unsigned int.
|
139 |
mac: MAC address as unsigned int
|
140 |
returns: macStr MAC colon-hex string"""
|
141 |
return _colonHex( mac, 6 ) |
142 |
|
143 |
def ipStr( ip ): |
144 |
"""Generate IP address string
|
145 |
returns: ip addr string"""
|
146 |
hi = ( ip & 0xff0000 ) >> 16 |
147 |
mid = ( ip & 0xff00 ) >> 8 |
148 |
lo = ip & 0xff
|
149 |
return "10.%i.%i.%i" % ( hi, mid, lo ) |