mininet / mininet / util.py @ 723d068c
History | View | Annotate | Download (5.65 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.logging_mod import lg |
10 |
|
11 |
|
12 |
def run(cmd): |
13 |
'''Simple interface to subprocess.call()
|
14 |
|
15 |
@param cmd list of command params
|
16 |
'''
|
17 |
return call(cmd.split(' ')) |
18 |
|
19 |
|
20 |
def checkRun(cmd): |
21 |
'''Simple interface to subprocess.check_call()
|
22 |
|
23 |
@param cmd list of command params
|
24 |
'''
|
25 |
check_call(cmd.split(' '))
|
26 |
|
27 |
|
28 |
def quietRun(cmd): |
29 |
'''Run a command, routing stderr to stdout, and return the output.
|
30 |
|
31 |
@param cmd list of command params
|
32 |
'''
|
33 |
if isinstance(cmd, str): |
34 |
cmd = cmd.split(' ')
|
35 |
popen = Popen(cmd, stdout=PIPE, stderr=STDOUT) |
36 |
# We can't use Popen.communicate() because it uses
|
37 |
# select(), which can't handle
|
38 |
# high file descriptor numbers! poll() can, however.
|
39 |
output = ''
|
40 |
readable = select.poll() |
41 |
readable.register(popen.stdout) |
42 |
while True: |
43 |
while readable.poll():
|
44 |
data = popen.stdout.read(1024)
|
45 |
if len(data) == 0: |
46 |
break
|
47 |
output += data |
48 |
popen.poll() |
49 |
if popen.returncode != None: |
50 |
break
|
51 |
return output
|
52 |
|
53 |
|
54 |
def make_veth_pair(intf1, intf2): |
55 |
'''Create a veth pair connecting intf1 and intf2.
|
56 |
|
57 |
@param intf1 string, interface name
|
58 |
@param intf2 string, interface name
|
59 |
'''
|
60 |
# Delete any old interfaces with the same names
|
61 |
quietRun('ip link del ' + intf1)
|
62 |
quietRun('ip link del ' + intf2)
|
63 |
# Create new pair
|
64 |
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 |
65 |
#lg.info('running command: %s\n' % cmd)
|
66 |
return checkRun(cmd)
|
67 |
|
68 |
|
69 |
def move_intf(intf, node): |
70 |
'''Move interface to node.
|
71 |
|
72 |
@param intf string interface name
|
73 |
@param node Node object
|
74 |
|
75 |
@return success boolean, did operation complete?
|
76 |
'''
|
77 |
cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid) |
78 |
#lg.info('running command: %s\n' % cmd)
|
79 |
quietRun(cmd) |
80 |
#lg.info(' output: %s\n' % output)
|
81 |
links = node.cmd('ip link show')
|
82 |
if not intf in links: |
83 |
lg.error('*** Error: move_intf: %s not successfully moved to %s:\n' %
|
84 |
(intf, node.name)) |
85 |
return False |
86 |
return True |
87 |
|
88 |
|
89 |
# Interface management
|
90 |
#
|
91 |
# Interfaces are managed as strings which are simply the
|
92 |
# interface names, of the form 'nodeN-ethM'.
|
93 |
#
|
94 |
# To connect nodes, we create a pair of veth interfaces, and then place them
|
95 |
# in the pair of nodes that we want to communicate. We then update the node's
|
96 |
# list of interfaces and connectivity map.
|
97 |
#
|
98 |
# For the kernel datapath, switch interfaces
|
99 |
# live in the root namespace and thus do not have to be
|
100 |
# explicitly moved.
|
101 |
|
102 |
|
103 |
def makeIntfPair(intf1, intf2): |
104 |
'''Make a veth pair.
|
105 |
|
106 |
@param intf1 string, interface
|
107 |
@param intf2 string, interface
|
108 |
@return success boolean
|
109 |
'''
|
110 |
# Delete any old interfaces with the same names
|
111 |
quietRun('ip link del ' + intf1)
|
112 |
quietRun('ip link del ' + intf2)
|
113 |
# Create new pair
|
114 |
cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2 |
115 |
return checkRun(cmd)
|
116 |
|
117 |
|
118 |
def moveIntf(intf, node, print_error = False): |
119 |
'''Move interface to node.
|
120 |
|
121 |
@param intf string, interface
|
122 |
@param node Node object
|
123 |
@param print_error if true, print error
|
124 |
'''
|
125 |
cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid) |
126 |
quietRun(cmd) |
127 |
links = node.cmd('ip link show')
|
128 |
if not intf in links: |
129 |
if print_error:
|
130 |
lg.error('*** Error: moveIntf: % not successfully moved to %s:\n' %
|
131 |
(intf, node.name)) |
132 |
return False |
133 |
return True |
134 |
|
135 |
|
136 |
def retry(n, retry_delay, fn, *args, **keywords): |
137 |
'''Try something N times before giving up.
|
138 |
|
139 |
@param n number of times to retry
|
140 |
@param retry_delay seconds wait this long between tries
|
141 |
@param fn function to call
|
142 |
@param args args to apply to function call
|
143 |
'''
|
144 |
tries = 0
|
145 |
while not fn(*args, **keywords) and tries < n: |
146 |
sleep(retry_delay) |
147 |
tries += 1
|
148 |
if tries >= n:
|
149 |
lg.error("*** gave up after %i retries\n" % tries)
|
150 |
exit(1) |
151 |
|
152 |
|
153 |
# delay between interface move checks in seconds
|
154 |
MOVEINTF_DELAY = 0.0001
|
155 |
|
156 |
CREATE_LINK_RETRIES = 10
|
157 |
|
158 |
|
159 |
def createLink(node1, node2): |
160 |
'''Create a link between nodes, making an interface for each.
|
161 |
|
162 |
@param node1 Node object
|
163 |
@param node2 Node object
|
164 |
'''
|
165 |
intf1 = node1.newIntf() |
166 |
intf2 = node2.newIntf() |
167 |
makeIntfPair(intf1, intf2) |
168 |
if node1.inNamespace:
|
169 |
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf1, node1) |
170 |
if node2.inNamespace:
|
171 |
retry(CREATE_LINK_RETRIES, MOVEINTF_DELAY, moveIntf, intf2, node2) |
172 |
node1.connection[intf1] = (node2, intf2) |
173 |
node2.connection[intf2] = (node1, intf1) |
174 |
return intf1, intf2
|
175 |
|
176 |
|
177 |
def fixLimits(): |
178 |
'''Fix ridiculously small resource limits.'''
|
179 |
setrlimit(RLIMIT_NPROC, (4096, 8192)) |
180 |
setrlimit(RLIMIT_NOFILE, (16384, 32768)) |
181 |
|
182 |
|
183 |
def _colonHex(val, bytes): |
184 |
'''Generate colon-hex string.
|
185 |
|
186 |
@param val input as unsigned int
|
187 |
@param bytes number of bytes to convert
|
188 |
@return ch_str colon-hex string
|
189 |
'''
|
190 |
pieces = [] |
191 |
for i in range(bytes - 1, -1, -1): |
192 |
pieces.append('%02x' % (((0xff << (i * 8)) & val) >> (i * 8))) |
193 |
ch_str = ':'.join(pieces)
|
194 |
return ch_str
|
195 |
|
196 |
|
197 |
def macColonHex(mac): |
198 |
'''Generate MAC colon-hex string from unsigned int.
|
199 |
|
200 |
@param mac MAC address as unsigned int
|
201 |
@return mac_str MAC colon-hex string
|
202 |
'''
|
203 |
return _colonHex(mac, 6) |
204 |
|
205 |
|
206 |
def ipStr(ip): |
207 |
'''Generate IP address string
|
208 |
|
209 |
@return ip addr string
|
210 |
'''
|
211 |
hi = (ip & 0xff0000) >> 16 |
212 |
mid = (ip & 0xff00) >> 8 |
213 |
lo = ip & 0xff
|
214 |
return "10.%i.%i.%i" % (hi, mid, lo) |