]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-test.in
ovs-test: Enhancements to the ovs-test tool
[mirror_ovs.git] / utilities / ovs-test.in
1 #! @PYTHON@
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 ovs test utility that allows to do tests between remote hosts
17 """
18
19 import fcntl
20 import math
21 import os
22 import select
23 import signal
24 import socket
25 import subprocess
26 import sys
27 import time
28 import xmlrpclib
29
30 import argparse
31 import twisted
32
33 import ovstest.args as args
34 import ovstest.rpcserver as rpcserver
35
36 DEFAULT_TEST_BRIDGE = "ovstestbr0"
37 DEFAULT_TEST_PORT = "ovstestport0"
38 DEFAULT_TEST_TUN = "ovstestport1"
39
40
41 def rpc_client(ip, port):
42 return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
43
44
45 def sigint_intercept():
46 """
47 Intercept SIGINT from child (the local ovs-test server process).
48 """
49 signal.signal(signal.SIGINT, signal.SIG_IGN)
50
51
52 def start_local_server(port):
53 """
54 This function spawns an ovs-test server that listens on specified port
55 and blocks till the spawned ovs-test server is ready to accept XML RPC
56 connections.
57 """
58 p = subprocess.Popen(["ovs-test", "-s", str(port)],
59 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
60 preexec_fn = sigint_intercept)
61 fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
62 fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
63
64 while p.poll() is None:
65 fd = select.select([p.stdout.fileno()], [], [])[0]
66 if fd:
67 out = p.stdout.readline()
68 if out.startswith("Starting RPC server"):
69 break
70 if p.poll() is not None:
71 raise RuntimeError("Couldn't start local instance of ovs-test server")
72 return p
73
74
75 def get_datagram_sizes(mtu1, mtu2):
76 """
77 This function calculates all the "interesting" datagram sizes so that
78 we test both - receive and send side with different packets sizes.
79 """
80 s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
81 s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
82 return sorted(s1.union(s2))
83
84
85 def ip_from_cidr(string):
86 """
87 This function removes the netmask (if present) from the given string and
88 returns the IP address.
89 """
90 token = string.split("/")
91 return token[0]
92
93
94 def bandwidth_to_string(bwidth):
95 """Convert bandwidth from long to string and add units."""
96 bwidth = bwidth * 8 # Convert back to bits/second
97 if bwidth >= 10000000:
98 return str(int(bwidth / 1000000)) + "Mbps"
99 elif bwidth > 10000:
100 return str(int(bwidth / 1000)) + "Kbps"
101 else:
102 return str(int(bwidth)) + "bps"
103
104
105 def collect_information(node):
106 """Print information about hosts that will do testing"""
107 print "Node %s:%u " % (node[0], node[1])
108 server = rpc_client(node[0], node[1])
109 interface_name = server.get_interface(node[0])
110 phys_iface = None
111 uname = server.uname()
112 mtu = 1500
113
114 if not interface_name:
115 print ("Could not find interface that has %s IP address."
116 "Make sure that you specified correct Outer IP." % (node[0]))
117 else:
118 if server.is_ovs_bridge(interface_name):
119 phys_iface = server.get_iface_from_bridge(interface_name)
120 else:
121 phys_iface = interface_name
122
123 if phys_iface:
124 driver = server.get_driver(phys_iface)
125 mtu = server.get_interface_mtu(phys_iface)
126
127 print "Will be using %s (%s) with MTU %u" % (phys_iface, node[0],
128 mtu)
129 if not driver:
130 print "Unable to get driver information from ethtool."
131 else:
132 print "On this host %s has %s." % (phys_iface, driver)
133
134 if not uname:
135 print "Unable to retrieve kernel information. Is this Linux?"
136 else:
137 print "Running kernel %s." % uname
138 print "\n"
139
140 return mtu
141
142
143 def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes):
144 """Schedule UDP tests between receiver and sender"""
145 server1 = rpc_client(receiver[0], receiver[1])
146 server2 = rpc_client(sender[0], sender[1])
147
148 udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}'
149
150 print ("UDP test from %s:%u to %s:%u with target bandwidth %s" %
151 (sender[0], sender[1], receiver[0], receiver[1],
152 bandwidth_to_string(tbwidth)))
153 print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams",
154 "Datagram Loss", "Bandwidth")
155
156 for size in port_sizes:
157 listen_handle = -1
158 send_handle = -1
159 try:
160 packetcnt = (tbwidth * duration) / size
161
162 listen_handle = server1.create_udp_listener(receiver[3])
163 if listen_handle == -1:
164 print ("Server could not open UDP listening socket on port"
165 " %u. Try to restart the server.\n" % receiver[3])
166 return
167 send_handle = server2.create_udp_sender(
168 (ip_from_cidr(receiver[2]),
169 receiver[3]), packetcnt, size,
170 duration)
171
172 # Using sleep here because there is no other synchronization source
173 # that would notify us when all sent packets were received
174 time.sleep(duration + 1)
175
176 rcv_packets = server1.get_udp_listener_results(listen_handle)
177 snt_packets = server2.get_udp_sender_results(send_handle)
178
179 loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) /
180 snt_packets) / 100
181 bwidth = (rcv_packets * size) / duration
182
183 print udpformat.format(size, snt_packets, rcv_packets,
184 '%.2f%%' % loss, bandwidth_to_string(bwidth))
185 finally:
186 if listen_handle != -1:
187 server1.close_udp_listener(listen_handle)
188 if send_handle != -1:
189 server2.close_udp_sender(send_handle)
190 print "\n"
191
192
193 def do_tcp_tests(receiver, sender, duration):
194 """Schedule TCP tests between receiver and sender"""
195 server1 = rpc_client(receiver[0], receiver[1])
196 server2 = rpc_client(sender[0], sender[1])
197
198 tcpformat = '{0:>15} {1:>15} {2:>15}'
199 print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1],
200 receiver[0], receiver[1])
201 print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")
202
203 listen_handle = -1
204 send_handle = -1
205 try:
206 listen_handle = server1.create_tcp_listener(receiver[3])
207 if listen_handle == -1:
208 print ("Server was unable to open TCP listening socket on port"
209 " %u. Try to restart the server.\n" % receiver[3])
210 return
211 send_handle = server2.create_tcp_sender(ip_from_cidr(receiver[2]),
212 receiver[3], duration)
213
214 time.sleep(duration + 1)
215
216 rcv_bytes = long(server1.get_tcp_listener_results(listen_handle))
217 snt_bytes = long(server2.get_tcp_sender_results(send_handle))
218
219 bwidth = rcv_bytes / duration
220
221 print tcpformat.format(snt_bytes, rcv_bytes,
222 bandwidth_to_string(bwidth))
223 finally:
224 if listen_handle != -1:
225 server1.close_tcp_listener(listen_handle)
226 if send_handle != -1:
227 server2.close_tcp_sender(send_handle)
228 print "\n"
229
230
231 def do_l3_tests(node1, node2, bandwidth, duration, ps, type):
232 """
233 Do L3 tunneling tests.
234 """
235 server1 = rpc_client(node1[0], node1[1])
236 server2 = rpc_client(node2[0], node2[1])
237 servers_with_bridges = []
238 try:
239 server1.create_bridge(DEFAULT_TEST_BRIDGE)
240 servers_with_bridges.append(server1)
241 server2.create_bridge(DEFAULT_TEST_BRIDGE)
242 servers_with_bridges.append(server2)
243
244 server1.interface_up(DEFAULT_TEST_BRIDGE)
245 server2.interface_up(DEFAULT_TEST_BRIDGE)
246
247 server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None)
248 server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None)
249
250 server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
251 server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
252
253 server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
254 None, type)
255 server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
256 None, type)
257 server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
258 "remote_ip", node2[0])
259 server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
260 "remote_ip", node1[0])
261
262 do_udp_tests(node1, node2, bandwidth, duration, ps)
263 do_udp_tests(node2, node1, bandwidth, duration, ps)
264 do_tcp_tests(node1, node2, duration)
265 do_tcp_tests(node2, node1, duration)
266
267 finally:
268 for server in servers_with_bridges:
269 server.del_bridge(DEFAULT_TEST_BRIDGE)
270
271
272
273 def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag):
274 """
275 Do VLAN tests between node1 and node2.
276 """
277 server1 = rpc_client(node1[0], node1[1])
278 server2 = rpc_client(node2[0], node2[1])
279
280 br_name1 = None
281 br_name2 = None
282
283 servers_with_test_ports = []
284
285 try:
286 interface_node1 = server1.get_interface(node1[0])
287 interface_node2 = server2.get_interface(node2[0])
288
289 if server1.is_ovs_bridge(interface_node1):
290 br_name1 = interface_node1
291 else:
292 br_name1 = DEFAULT_TEST_BRIDGE
293 server1.create_test_bridge(br_name1, interface_node1)
294
295 if server2.is_ovs_bridge(interface_node2):
296 br_name2 = interface_node2
297 else:
298 br_name2 = DEFAULT_TEST_BRIDGE
299 server2.create_test_bridge(br_name2, interface_node2)
300
301 server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT)
302 servers_with_test_ports.append(server1)
303 server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT)
304 servers_with_test_ports.append(server2)
305
306 server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
307 server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
308
309 server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
310 "internal")
311 server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
312 "internal")
313
314 server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None)
315 server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None)
316
317 server1.interface_up(DEFAULT_TEST_PORT)
318 server2.interface_up(DEFAULT_TEST_PORT)
319
320 do_udp_tests(node1, node2, bandwidth, duration, ps)
321 do_udp_tests(node2, node1, bandwidth, duration, ps)
322 do_tcp_tests(node1, node2, duration)
323 do_tcp_tests(node2, node1, duration)
324
325 finally:
326 for server in servers_with_test_ports:
327 server.del_port_from_bridge(DEFAULT_TEST_PORT)
328 if br_name1 == DEFAULT_TEST_BRIDGE:
329 server1.del_test_bridge(br_name1, interface_node1)
330 if br_name2 == DEFAULT_TEST_BRIDGE:
331 server2.del_test_bridge(br_name2, interface_node2)
332
333
334 def do_direct_tests(node1, node2, bandwidth, duration, ps):
335 """
336 Do tests between outer IPs without involving Open vSwitch
337 """
338 n1 = (node1[0], node1[1], node1[0], node1[3])
339 n2 = (node2[0], node2[1], node2[0], node2[3])
340
341 do_udp_tests(n1, n2, bandwidth, duration, ps)
342 do_udp_tests(n2, n1, bandwidth, duration, ps)
343 do_tcp_tests(n1, n2, duration)
344 do_tcp_tests(n2, n1, duration)
345
346
347 if __name__ == '__main__':
348 local_server = None
349 try:
350 ovs_args = args.ovs_initialize_args()
351
352 if ovs_args.port is not None: # Start in pure server mode
353 rpcserver.start_rpc_server(ovs_args.port)
354
355 elif ovs_args.servers is not None: # Run in client mode
356 node1 = ovs_args.servers[0]
357 node2 = ovs_args.servers[1]
358
359 # Verify whether client will need to spawn a local instance of
360 # ovs-test server by looking at the first OuterIP. if it is a
361 # 127.0.0.1 then spawn local ovs-test server.
362 if node1[0] == "127.0.0.1":
363 local_server = start_local_server(node1[1])
364 # We must determine the IP address that local ovs-test server
365 # will use:
366 me = rpc_client(node1[0], node1[1])
367 my_ip = me.get_my_address_from(node2[0], node2[1])
368 node1 = (my_ip, node1[1], node1[2], node1[3])
369
370 mtu_node2 = collect_information(node2)
371 mtu_node1 = collect_information(node1)
372
373 bandwidth = ovs_args.targetBandwidth
374 interval = ovs_args.testInterval
375 ps = get_datagram_sizes(mtu_node1, mtu_node2)
376
377 direct = ovs_args.direct
378 vlan_tag = ovs_args.vlanTag
379 tunnel_modes = ovs_args.tunnelModes
380
381 if direct is not None:
382 print "Performing direct tests"
383 do_direct_tests(node2, node1, bandwidth, interval, ps)
384
385 if vlan_tag is not None:
386 print "Performing VLAN tests"
387 do_vlan_tests(node2, node1, bandwidth, interval, ps, vlan_tag)
388
389 for tmode in tunnel_modes:
390 print "Performing", tmode, "tests"
391 do_l3_tests(node2, node1, bandwidth, interval, ps, tmode)
392
393 except KeyboardInterrupt:
394 pass
395 except xmlrpclib.Fault:
396 print "Couldn't establish XMLRPC control channel"
397 except socket.error:
398 print "Couldn't establish XMLRPC control channel"
399 except xmlrpclib.ProtocolError:
400 print "XMLRPC control channel was abruptly terminated"
401 except twisted.internet.error.CannotListenError:
402 print "Couldn't start XMLRPC server on port %u" % ovs_args.port
403 finally:
404 if local_server is not None:
405 local_server.terminate()