ovs-l3ping: A new test utility that allows to detect L3 tunneling issues
[sliver-openvswitch.git] / python / ovstest / util.py
1 # Copyright (c) 2011, 2012 Nicira, Inc.
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 """
18 import array
19 import exceptions
20 import fcntl
21 import os
22 import select
23 import socket
24 import struct
25 import signal
26 import subprocess
27 import re
28 import xmlrpclib
29
30
31 def str_ip(ip_address):
32     """
33     Converts an IP address from binary format to a string.
34     """
35     (x1, x2, x3, x4) = struct.unpack("BBBB", ip_address)
36     return ("%u.%u.%u.%u") % (x1, x2, x3, x4)
37
38
39 def get_interface_mtu(iface):
40     """
41     Returns MTU of the given interface.
42     """
43     s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
44     indata = iface + ('\0' * (32 - len(iface)))
45     try:
46         outdata = fcntl.ioctl(s.fileno(), 0x8921, indata) #  socket.SIOCGIFMTU
47         mtu = struct.unpack("16si12x", outdata)[1]
48     except:
49         return 0
50
51     return mtu
52
53
54 def get_interface(address):
55     """
56     Finds first interface that has given address
57     """
58     bytes = 256 * 32
59     s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
60     names = array.array('B', '\0' * bytes)
61     outbytes = struct.unpack('iL', fcntl.ioctl(
62         s.fileno(),
63         0x8912, # SIOCGIFCONF
64         struct.pack('iL', bytes, names.buffer_info()[0])
65     ))[0]
66     namestr = names.tostring()
67
68     for i in range(0, outbytes, 40):
69         name = namestr[i:i + 16].split('\0', 1)[0]
70         if address == str_ip(namestr[i + 20:i + 24]):
71             return name
72     return None  # did not find interface we were looking for
73
74
75 def uname():
76     os_info = os.uname()
77     return os_info[2]  # return only the kernel version number
78
79
80 def start_process(args):
81     try:
82         p = subprocess.Popen(args,
83             stdin = subprocess.PIPE,
84             stdout = subprocess.PIPE,
85             stderr = subprocess.PIPE)
86         out, err = p.communicate()
87         return (p.returncode, out, err)
88     except exceptions.OSError:
89         return (-1, None, None)
90
91
92 def get_driver(iface):
93     ret, out, _err = start_process(["ethtool", "-i", iface])
94     if ret == 0:
95         lines = out.splitlines()
96         driver = "%s(%s)" % (lines[0], lines[1])  # driver name + version
97     else:
98         driver = None
99     return driver
100
101
102 def interface_up(iface):
103     """
104     This function brings given iface up.
105     """
106     ret, _out, _err = start_process(["ifconfig", iface, "up"])
107     return ret
108
109
110 def interface_assign_ip(iface, ip_addr, mask):
111     """
112     This function allows to assign IP address to an interface. If mask is an
113     empty string then ifconfig will decide what kind of mask to use. The
114     caller can also specify the mask by using CIDR notation in ip argument by
115     leaving the mask argument as an empty string. In case of success this
116     function returns 0.
117     """
118     args = ["ifconfig", iface, ip_addr]
119     if mask is not None:
120         args.append("netmask")
121         args.append(mask)
122     ret, _out, _err = start_process(args)
123     return ret
124
125
126 def interface_get_ip(iface):
127     """
128     This function returns tuple - ip and mask that was assigned to the
129     interface.
130     """
131     args = ["ifconfig", iface]
132     ret, out, _err = start_process(args)
133
134     if ret == 0:
135         ip = re.search(r'inet addr:(\S+)', out)
136         mask = re.search(r'Mask:(\S+)', out)
137         if ip is not None and mask is not None:
138             return (ip.group(1), mask.group(1))
139     else:
140         return ret
141
142
143 def move_routes(iface1, iface2):
144     """
145     This function moves routes from iface1 to iface2.
146     """
147     args = ["ip", "route", "show", "dev", iface1]
148     ret, out, _err = start_process(args)
149     if ret == 0:
150         for route in out.splitlines():
151             args = ["ip", "route", "replace", "dev", iface2] + route.split()
152             start_process(args)
153
154
155 def get_interface_from_routing_decision(ip):
156     """
157     This function returns the interface through which the given ip address
158     is reachable.
159     """
160     args = ["ip", "route", "get", ip]
161     ret, out, _err = start_process(args)
162     if ret == 0:
163         iface = re.search(r'dev (\S+)', out)
164         if iface:
165             return iface.group(1)
166     return None
167
168
169 def rpc_client(ip, port):
170     return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
171
172
173 def sigint_intercept():
174     """
175     Intercept SIGINT from child (the local ovs-test server process).
176     """
177     signal.signal(signal.SIGINT, signal.SIG_IGN)
178
179
180 def start_local_server(port):
181     """
182     This function spawns an ovs-test server that listens on specified port
183     and blocks till the spawned ovs-test server is ready to accept XML RPC
184     connections.
185     """
186     p = subprocess.Popen(["ovs-test", "-s", str(port)],
187                          stdout=subprocess.PIPE, stderr=subprocess.PIPE,
188                          preexec_fn=sigint_intercept)
189     fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
190         fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
191
192     while p.poll() is None:
193         fd = select.select([p.stdout.fileno()], [], [])[0]
194         if fd:
195             out = p.stdout.readline()
196             if out.startswith("Starting RPC server"):
197                 break
198     if p.poll() is not None:
199         raise RuntimeError("Couldn't start local instance of ovs-test server")
200     return p
201
202
203 def get_datagram_sizes(mtu1, mtu2):
204     """
205     This function calculates all the "interesting" datagram sizes so that
206     we test both - receive and send side with different packets sizes.
207     """
208     s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
209     s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
210     return sorted(s1.union(s2))
211
212
213 def ip_from_cidr(string):
214     """
215     This function removes the netmask (if present) from the given string and
216     returns the IP address.
217     """
218     token = string.split("/")
219     return token[0]
220
221
222 def bandwidth_to_string(bwidth):
223     """Convert bandwidth from long to string and add units."""
224     bwidth = bwidth * 8  # Convert back to bits/second
225     if bwidth >= 10000000:
226         return str(int(bwidth / 1000000)) + "Mbps"
227     elif bwidth > 10000:
228         return str(int(bwidth / 1000)) + "Kbps"
229     else:
230         return str(int(bwidth)) + "bps"