]>
Commit | Line | Data |
---|---|---|
0b2c7e69 | 1 | # Copyright (c) 2011, 2012, 2017 Nicira, Inc. |
0be6140a AA |
2 | # |
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | # you may not use this file except in compliance with the License. | |
5 | # You may obtain a copy of the License at: | |
6 | # | |
7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
8 | # | |
9 | # Unless required by applicable law or agreed to in writing, software | |
10 | # distributed under the License is distributed on an "AS IS" BASIS, | |
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | # See the License for the specific language governing permissions and | |
13 | # limitations under the License. | |
14 | ||
15 | """ | |
16 | util module contains some helper function | |
17 | """ | |
8d25d9a2 | 18 | import array |
8d25d9a2 | 19 | import fcntl |
6c7050b5 | 20 | |
8d25d9a2 | 21 | import os |
6c7050b5 | 22 | import re |
2d8bdd8f | 23 | import select |
6c7050b5 | 24 | import signal |
8d25d9a2 AA |
25 | import socket |
26 | import struct | |
27 | import subprocess | |
73eb682e | 28 | |
6c7050b5 | 29 | import exceptions |
30 | ||
0c4d144a | 31 | import xmlrpc.client |
0be6140a | 32 | |
8d25d9a2 AA |
33 | |
34 | def str_ip(ip_address): | |
35 | """ | |
36 | Converts an IP address from binary format to a string. | |
37 | """ | |
38 | (x1, x2, x3, x4) = struct.unpack("BBBB", ip_address) | |
0be6140a AA |
39 | return ("%u.%u.%u.%u") % (x1, x2, x3, x4) |
40 | ||
8d25d9a2 | 41 | |
0be6140a | 42 | def get_interface_mtu(iface): |
8d25d9a2 AA |
43 | """ |
44 | Returns MTU of the given interface. | |
45 | """ | |
0be6140a AA |
46 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
47 | indata = iface + ('\0' * (32 - len(iface))) | |
48 | try: | |
a0631d92 | 49 | outdata = fcntl.ioctl(s.fileno(), 0x8921, indata) # socket.SIOCGIFMTU |
0be6140a AA |
50 | mtu = struct.unpack("16si12x", outdata)[1] |
51 | except: | |
52 | return 0 | |
53 | ||
54 | return mtu | |
55 | ||
8d25d9a2 | 56 | |
0be6140a AA |
57 | def get_interface(address): |
58 | """ | |
59 | Finds first interface that has given address | |
60 | """ | |
61 | bytes = 256 * 32 | |
62 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
63 | names = array.array('B', '\0' * bytes) | |
64 | outbytes = struct.unpack('iL', fcntl.ioctl( | |
65 | s.fileno(), | |
a0631d92 | 66 | 0x8912, # SIOCGIFCONF |
0be6140a AA |
67 | struct.pack('iL', bytes, names.buffer_info()[0]) |
68 | ))[0] | |
69 | namestr = names.tostring() | |
70 | ||
71 | for i in range(0, outbytes, 40): | |
72 | name = namestr[i:i + 16].split('\0', 1)[0] | |
73 | if address == str_ip(namestr[i + 20:i + 24]): | |
74 | return name | |
8d25d9a2 AA |
75 | return None # did not find interface we were looking for |
76 | ||
0be6140a AA |
77 | |
78 | def uname(): | |
79 | os_info = os.uname() | |
8d25d9a2 | 80 | return os_info[2] # return only the kernel version number |
0be6140a | 81 | |
8d25d9a2 AA |
82 | |
83 | def start_process(args): | |
0be6140a | 84 | try: |
8d25d9a2 | 85 | p = subprocess.Popen(args, |
a0631d92 RB |
86 | stdin=subprocess.PIPE, |
87 | stdout=subprocess.PIPE, | |
88 | stderr=subprocess.PIPE) | |
0be6140a | 89 | out, err = p.communicate() |
8d25d9a2 | 90 | return (p.returncode, out, err) |
0be6140a | 91 | except exceptions.OSError: |
8d25d9a2 AA |
92 | return (-1, None, None) |
93 | ||
94 | ||
95 | def get_driver(iface): | |
96 | ret, out, _err = start_process(["ethtool", "-i", iface]) | |
97 | if ret == 0: | |
98 | lines = out.splitlines() | |
99 | driver = "%s(%s)" % (lines[0], lines[1]) # driver name + version | |
100 | else: | |
101 | driver = None | |
0be6140a | 102 | return driver |
8d25d9a2 AA |
103 | |
104 | ||
105 | def interface_up(iface): | |
106 | """ | |
107 | This function brings given iface up. | |
108 | """ | |
0b2c7e69 | 109 | ret, _out, _err = start_process(["ip", "link", "set", iface, "up"]) |
8d25d9a2 AA |
110 | return ret |
111 | ||
112 | ||
113 | def interface_assign_ip(iface, ip_addr, mask): | |
114 | """ | |
0b2c7e69 BP |
115 | This function adds an IP address to an interface. If mask is None |
116 | then a mask will be selected automatically. In case of success | |
117 | this function returns 0. | |
8d25d9a2 | 118 | """ |
0b2c7e69 BP |
119 | interface_ip_op(iface, ip_addr, mask, "add") |
120 | ||
121 | ||
122 | def interface_remove_ip(iface, ip_addr, mask): | |
123 | """ | |
124 | This function removes an IP address from an interface. If mask is | |
125 | None then a mask will be selected automatically. In case of | |
126 | success this function returns 0. | |
127 | """ | |
128 | interface_ip_op(iface, ip_addr, mask, "del") | |
129 | ||
130 | ||
131 | def interface_ip_op(iface, ip_addr, mask, op): | |
8d25d9a2 | 132 | if mask is not None: |
0b2c7e69 BP |
133 | arg = "%s/%s" % (ip_addr, mask) |
134 | elif '/' in ip_addr: | |
135 | arg = ip_addr | |
136 | else: | |
137 | (x1, x2, x3, x4) = struct.unpack("BBBB", socket.inet_aton(ip_addr)) | |
138 | if x1 < 128: | |
139 | arg = "%s/8" % ip_addr | |
140 | elif x1 < 192: | |
141 | arg = "%s/16" % ip_addr | |
142 | else: | |
143 | arg = "%s/24" % ip_addr | |
144 | ret, _out, _err = start_process(["ip", "addr", op, arg, "dev", iface]) | |
8d25d9a2 AA |
145 | return ret |
146 | ||
147 | ||
148 | def interface_get_ip(iface): | |
149 | """ | |
150 | This function returns tuple - ip and mask that was assigned to the | |
151 | interface. | |
152 | """ | |
0b2c7e69 | 153 | args = ["ip", "addr", "show", iface] |
8d25d9a2 AA |
154 | ret, out, _err = start_process(args) |
155 | ||
156 | if ret == 0: | |
0b2c7e69 BP |
157 | ip = re.search(r'inet (\S+)/(\S+)', out) |
158 | if ip is not None: | |
159 | return (ip.group(1), ip.group(2)) | |
8d25d9a2 AA |
160 | else: |
161 | return ret | |
162 | ||
163 | ||
164 | def move_routes(iface1, iface2): | |
165 | """ | |
166 | This function moves routes from iface1 to iface2. | |
167 | """ | |
168 | args = ["ip", "route", "show", "dev", iface1] | |
169 | ret, out, _err = start_process(args) | |
170 | if ret == 0: | |
171 | for route in out.splitlines(): | |
172 | args = ["ip", "route", "replace", "dev", iface2] + route.split() | |
173 | start_process(args) | |
2d8bdd8f AA |
174 | |
175 | ||
176 | def get_interface_from_routing_decision(ip): | |
177 | """ | |
178 | This function returns the interface through which the given ip address | |
179 | is reachable. | |
180 | """ | |
181 | args = ["ip", "route", "get", ip] | |
182 | ret, out, _err = start_process(args) | |
183 | if ret == 0: | |
184 | iface = re.search(r'dev (\S+)', out) | |
185 | if iface: | |
186 | return iface.group(1) | |
187 | return None | |
188 | ||
189 | ||
190 | def rpc_client(ip, port): | |
0c4d144a | 191 | return xmlrpc.client.Server("http://%s:%u/" % (ip, port), allow_none=True) |
2d8bdd8f AA |
192 | |
193 | ||
194 | def sigint_intercept(): | |
195 | """ | |
196 | Intercept SIGINT from child (the local ovs-test server process). | |
197 | """ | |
198 | signal.signal(signal.SIGINT, signal.SIG_IGN) | |
199 | ||
200 | ||
201 | def start_local_server(port): | |
202 | """ | |
203 | This function spawns an ovs-test server that listens on specified port | |
204 | and blocks till the spawned ovs-test server is ready to accept XML RPC | |
205 | connections. | |
206 | """ | |
207 | p = subprocess.Popen(["ovs-test", "-s", str(port)], | |
208 | stdout=subprocess.PIPE, stderr=subprocess.PIPE, | |
209 | preexec_fn=sigint_intercept) | |
a0631d92 | 210 | fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, |
2d8bdd8f AA |
211 | fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK) |
212 | ||
213 | while p.poll() is None: | |
214 | fd = select.select([p.stdout.fileno()], [], [])[0] | |
215 | if fd: | |
216 | out = p.stdout.readline() | |
217 | if out.startswith("Starting RPC server"): | |
218 | break | |
219 | if p.poll() is not None: | |
220 | raise RuntimeError("Couldn't start local instance of ovs-test server") | |
221 | return p | |
222 | ||
223 | ||
224 | def get_datagram_sizes(mtu1, mtu2): | |
225 | """ | |
226 | This function calculates all the "interesting" datagram sizes so that | |
227 | we test both - receive and send side with different packets sizes. | |
228 | """ | |
229 | s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1]) | |
230 | s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2]) | |
231 | return sorted(s1.union(s2)) | |
232 | ||
233 | ||
234 | def ip_from_cidr(string): | |
235 | """ | |
236 | This function removes the netmask (if present) from the given string and | |
237 | returns the IP address. | |
238 | """ | |
239 | token = string.split("/") | |
240 | return token[0] | |
241 | ||
242 | ||
243 | def bandwidth_to_string(bwidth): | |
244 | """Convert bandwidth from long to string and add units.""" | |
245 | bwidth = bwidth * 8 # Convert back to bits/second | |
246 | if bwidth >= 10000000: | |
247 | return str(int(bwidth / 1000000)) + "Mbps" | |
248 | elif bwidth > 10000: | |
249 | return str(int(bwidth / 1000)) + "Kbps" | |
250 | else: | |
251 | return str(int(bwidth)) + "bps" |