mirror of
https://github.com/openvswitch/ovs
synced 2025-08-30 05:47:55 +00:00
ovs-l3ping: A new test utility that allows to detect L3 tunneling issues
ovs-l3ping is similar to ovs-test, but the main difference is that it does not require administrator to open firewall holes for the XML/RPC control connection. This is achieved by encapsulating the Control Connection over the L3 tunnel itself. This tool is not intended as a replacement for ovs-test, because ovs-test covers much broader set of test cases. Sample usage: Node1: ovs-l3ping -s 192.168.122.236,10.1.1.1 -t gre Node2: ovs-l3ping -c 192.168.122.220,10.1.1.2,10.1.1.1 -t gre Issue#11791 Signed-off-by: Ansis Atteka <aatteka@nicira.com>
This commit is contained in:
parent
969e46a224
commit
2d8bdd8f22
3
NEWS
3
NEWS
@ -1,6 +1,9 @@
|
|||||||
post-v1.7.0
|
post-v1.7.0
|
||||||
------------------------
|
------------------------
|
||||||
- New FAQ. Please send updates and additions!
|
- New FAQ. Please send updates and additions!
|
||||||
|
- ovs-l3ping:
|
||||||
|
- A new test utility that can create L3 tunnel between two Open
|
||||||
|
vSwitches and detect connectivity issues.
|
||||||
- ovs-ofctl:
|
- ovs-ofctl:
|
||||||
- "mod-port" command can now control all OpenFlow config flags.
|
- "mod-port" command can now control all OpenFlow config flags.
|
||||||
- OpenFlow:
|
- OpenFlow:
|
||||||
|
1
debian/openvswitch-test.install
vendored
1
debian/openvswitch-test.install
vendored
@ -1,2 +1,3 @@
|
|||||||
usr/share/openvswitch/python/ovstest usr/lib/python2.6/dist-packages/
|
usr/share/openvswitch/python/ovstest usr/lib/python2.6/dist-packages/
|
||||||
usr/bin/ovs-test
|
usr/bin/ovs-test
|
||||||
|
usr/bin/ovs-l3ping
|
||||||
|
1
debian/openvswitch-test.manpages
vendored
1
debian/openvswitch-test.manpages
vendored
@ -1 +1,2 @@
|
|||||||
_debian/utilities/ovs-test.8
|
_debian/utilities/ovs-test.8
|
||||||
|
_debian/utilities/ovs-l3ping.8
|
||||||
|
@ -120,6 +120,14 @@ utilities/ovs-dpctl.8.in:
|
|||||||
lib/common.man:
|
lib/common.man:
|
||||||
lib/vlog.man:
|
lib/vlog.man:
|
||||||
|
|
||||||
|
utilities/ovs-l3ping.8: \
|
||||||
|
utilities/ovs-l3ping.8.in \
|
||||||
|
lib/common-syn.man \
|
||||||
|
lib/common.man
|
||||||
|
utilities/ovs-l3ping.8.in:
|
||||||
|
lib/common-syn.man:
|
||||||
|
lib/common.man:
|
||||||
|
|
||||||
utilities/ovs-ofctl.8: \
|
utilities/ovs-ofctl.8: \
|
||||||
utilities/ovs-ofctl.8.in \
|
utilities/ovs-ofctl.8.in \
|
||||||
lib/common.man \
|
lib/common.man \
|
||||||
|
@ -3,6 +3,7 @@ ovstest_pyfiles = \
|
|||||||
python/ovstest/args.py \
|
python/ovstest/args.py \
|
||||||
python/ovstest/rpcserver.py \
|
python/ovstest/rpcserver.py \
|
||||||
python/ovstest/tcp.py \
|
python/ovstest/tcp.py \
|
||||||
|
python/ovstest/tests.py \
|
||||||
python/ovstest/udp.py \
|
python/ovstest/udp.py \
|
||||||
python/ovstest/util.py \
|
python/ovstest/util.py \
|
||||||
python/ovstest/vswitch.py
|
python/ovstest/vswitch.py
|
||||||
|
@ -78,6 +78,23 @@ def ip_optional_port(string, default_port, ip_callback):
|
|||||||
"must be colon-separated")
|
"must be colon-separated")
|
||||||
|
|
||||||
|
|
||||||
|
def ip_optional_port_port(string, default_port1, default_port2, ip_callback):
|
||||||
|
"""Convert a string into IP, Port1, Port2 tuple. If any of ports were
|
||||||
|
missing, then default ports will be used. The fourth argument is a
|
||||||
|
callback that verifies whether IP address is given in the expected
|
||||||
|
format."""
|
||||||
|
value = string.split(':')
|
||||||
|
if len(value) == 1:
|
||||||
|
return (ip_callback(value[0]), default_port1, default_port2)
|
||||||
|
elif len(value) == 2:
|
||||||
|
return (ip_callback(value[0]), port(value[1]), default_port2)
|
||||||
|
elif len(value) == 3:
|
||||||
|
return (ip_callback(value[0]), port(value[1]), port(value[2]))
|
||||||
|
else:
|
||||||
|
raise argparse.ArgumentTypeError("Expected IP address and at most "
|
||||||
|
"two colon-separated ports")
|
||||||
|
|
||||||
|
|
||||||
def vlan_tag(string):
|
def vlan_tag(string):
|
||||||
"""
|
"""
|
||||||
This function verifies whether given string is a correct VLAN tag.
|
This function verifies whether given string is a correct VLAN tag.
|
||||||
@ -154,6 +171,37 @@ def tunnel_types(string):
|
|||||||
return string.split(',')
|
return string.split(',')
|
||||||
|
|
||||||
|
|
||||||
|
def l3_endpoint_client(string):
|
||||||
|
"""
|
||||||
|
This function parses command line argument string in
|
||||||
|
remoteIP,localInnerIP[/mask][:ControlPort[:TestPort]],remoteInnerIP[:
|
||||||
|
ControlPort[:TestPort]] format.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
remote_ip, me, he = string.split(',')
|
||||||
|
except ValueError:
|
||||||
|
raise argparse.ArgumentTypeError("All 3 IP addresses must be comma "
|
||||||
|
"separated.")
|
||||||
|
r = (ip_address(remote_ip),
|
||||||
|
ip_optional_port_port(me, CONTROL_PORT, DATA_PORT, ip_optional_mask),
|
||||||
|
ip_optional_port_port(he, CONTROL_PORT, DATA_PORT, ip_address))
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def l3_endpoint_server(string):
|
||||||
|
"""
|
||||||
|
This function parses a command line argument string in
|
||||||
|
remoteIP,localInnerIP[/mask][:ControlPort] format.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
remote_ip, me = string.split(',')
|
||||||
|
except ValueError:
|
||||||
|
raise argparse.ArgumentTypeError("Both IP addresses must be comma "
|
||||||
|
"separated.")
|
||||||
|
return (ip_address(remote_ip),
|
||||||
|
ip_optional_port(me, CONTROL_PORT, ip_optional_mask))
|
||||||
|
|
||||||
|
|
||||||
def ovs_initialize_args():
|
def ovs_initialize_args():
|
||||||
"""
|
"""
|
||||||
Initialize argument parsing for ovs-test utility.
|
Initialize argument parsing for ovs-test utility.
|
||||||
@ -197,3 +245,37 @@ def ovs_initialize_args():
|
|||||||
'ovs-test server in the client mode by using 127.0.0.1 as '
|
'ovs-test server in the client mode by using 127.0.0.1 as '
|
||||||
'OuterIP.')
|
'OuterIP.')
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def l3_initialize_args():
|
||||||
|
"""
|
||||||
|
Initialize argument parsing for ovs-l3ping utility.
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(description='Test L3 tunnel '
|
||||||
|
'connectivity between two Open vSwitch instances.')
|
||||||
|
|
||||||
|
parser.add_argument('-v', '--version', action='version',
|
||||||
|
version='ovs-l3ping (Open vSwitch) @VERSION@')
|
||||||
|
|
||||||
|
parser.add_argument("-b", "--bandwidth", action='store',
|
||||||
|
dest="targetBandwidth", default="1M", type=bandwidth,
|
||||||
|
help='Target bandwidth for UDP tests in bits/second. Use '
|
||||||
|
'postfix M or K to alter unit magnitude.')
|
||||||
|
parser.add_argument("-i", "--interval", action='store',
|
||||||
|
dest="testInterval", default=5, type=int,
|
||||||
|
help='Interval for how long to run each test in seconds.')
|
||||||
|
|
||||||
|
parser.add_argument("-t", "--tunnel-mode", action='store',
|
||||||
|
dest="tunnelMode", required=True,
|
||||||
|
help='Do L3 tests with this tunnel type.')
|
||||||
|
|
||||||
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
group.add_argument("-s", "--server", action="store", dest="server",
|
||||||
|
metavar="TUNNELIP,SERVER",
|
||||||
|
type=l3_endpoint_server,
|
||||||
|
help='Run in server mode and wait for the client to '
|
||||||
|
'connect.')
|
||||||
|
group.add_argument('-c', "--client", action="store", dest="client",
|
||||||
|
metavar="TUNNELIP,CLIENT,SERVER",
|
||||||
|
type=l3_endpoint_client,
|
||||||
|
help='Run in client mode and connect to the server.')
|
||||||
|
return parser.parse_args()
|
||||||
|
@ -343,6 +343,12 @@ class TestArena(xmlrpc.XMLRPC):
|
|||||||
"""
|
"""
|
||||||
return util.get_driver(iface)
|
return util.get_driver(iface)
|
||||||
|
|
||||||
|
def xmlrpc_get_interface_from_routing_decision(self, ip):
|
||||||
|
"""
|
||||||
|
Returns driver version
|
||||||
|
"""
|
||||||
|
return util.get_interface_from_routing_decision(ip)
|
||||||
|
|
||||||
|
|
||||||
def start_rpc_server(port):
|
def start_rpc_server(port):
|
||||||
"""
|
"""
|
||||||
|
237
python/ovstest/tests.py
Normal file
237
python/ovstest/tests.py
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
import math
|
||||||
|
import time
|
||||||
|
|
||||||
|
import ovstest.util as util
|
||||||
|
|
||||||
|
DEFAULT_TEST_BRIDGE = "ovstestbr0"
|
||||||
|
DEFAULT_TEST_PORT = "ovstestport0"
|
||||||
|
DEFAULT_TEST_TUN = "ovstestport1"
|
||||||
|
NO_HANDLE = -1
|
||||||
|
|
||||||
|
|
||||||
|
def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes):
|
||||||
|
"""Schedule UDP tests between receiver and sender"""
|
||||||
|
server1 = util.rpc_client(receiver[0], receiver[1])
|
||||||
|
server2 = util.rpc_client(sender[0], sender[1])
|
||||||
|
|
||||||
|
udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}'
|
||||||
|
|
||||||
|
print ("UDP test from %s:%u to %s:%u with target bandwidth %s" %
|
||||||
|
(sender[0], sender[1], receiver[0], receiver[1],
|
||||||
|
util.bandwidth_to_string(tbwidth)))
|
||||||
|
print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams",
|
||||||
|
"Datagram Loss", "Bandwidth")
|
||||||
|
|
||||||
|
for size in port_sizes:
|
||||||
|
listen_handle = NO_HANDLE
|
||||||
|
send_handle = NO_HANDLE
|
||||||
|
try:
|
||||||
|
packetcnt = (tbwidth * duration) / size
|
||||||
|
|
||||||
|
listen_handle = server1.create_udp_listener(receiver[3])
|
||||||
|
if listen_handle == NO_HANDLE:
|
||||||
|
print ("Server could not open UDP listening socket on port"
|
||||||
|
" %u. Try to restart the server.\n" % receiver[3])
|
||||||
|
return
|
||||||
|
send_handle = server2.create_udp_sender(
|
||||||
|
(util.ip_from_cidr(receiver[2]),
|
||||||
|
receiver[3]), packetcnt, size,
|
||||||
|
duration)
|
||||||
|
|
||||||
|
# Using sleep here because there is no other synchronization
|
||||||
|
# source that would notify us when all sent packets were received
|
||||||
|
time.sleep(duration + 1)
|
||||||
|
|
||||||
|
rcv_packets = server1.get_udp_listener_results(listen_handle)
|
||||||
|
snt_packets = server2.get_udp_sender_results(send_handle)
|
||||||
|
|
||||||
|
loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) /
|
||||||
|
snt_packets) / 100
|
||||||
|
bwidth = (rcv_packets * size) / duration
|
||||||
|
|
||||||
|
print udpformat.format(size, snt_packets, rcv_packets,
|
||||||
|
'%.2f%%' % loss, util.bandwidth_to_string(bwidth))
|
||||||
|
finally:
|
||||||
|
if listen_handle != NO_HANDLE:
|
||||||
|
server1.close_udp_listener(listen_handle)
|
||||||
|
if send_handle != NO_HANDLE:
|
||||||
|
server2.close_udp_sender(send_handle)
|
||||||
|
print "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def do_tcp_tests(receiver, sender, duration):
|
||||||
|
"""Schedule TCP tests between receiver and sender"""
|
||||||
|
server1 = util.rpc_client(receiver[0], receiver[1])
|
||||||
|
server2 = util.rpc_client(sender[0], sender[1])
|
||||||
|
|
||||||
|
tcpformat = '{0:>15} {1:>15} {2:>15}'
|
||||||
|
print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1],
|
||||||
|
receiver[0], receiver[1])
|
||||||
|
print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")
|
||||||
|
|
||||||
|
listen_handle = NO_HANDLE
|
||||||
|
send_handle = NO_HANDLE
|
||||||
|
try:
|
||||||
|
listen_handle = server1.create_tcp_listener(receiver[3])
|
||||||
|
if listen_handle == NO_HANDLE:
|
||||||
|
print ("Server was unable to open TCP listening socket on port"
|
||||||
|
" %u. Try to restart the server.\n" % receiver[3])
|
||||||
|
return
|
||||||
|
send_handle = server2.create_tcp_sender(util.ip_from_cidr(receiver[2]),
|
||||||
|
receiver[3], duration)
|
||||||
|
|
||||||
|
time.sleep(duration + 1)
|
||||||
|
|
||||||
|
rcv_bytes = long(server1.get_tcp_listener_results(listen_handle))
|
||||||
|
snt_bytes = long(server2.get_tcp_sender_results(send_handle))
|
||||||
|
|
||||||
|
bwidth = rcv_bytes / duration
|
||||||
|
|
||||||
|
print tcpformat.format(snt_bytes, rcv_bytes,
|
||||||
|
util.bandwidth_to_string(bwidth))
|
||||||
|
finally:
|
||||||
|
if listen_handle != NO_HANDLE:
|
||||||
|
server1.close_tcp_listener(listen_handle)
|
||||||
|
if send_handle != NO_HANDLE:
|
||||||
|
server2.close_tcp_sender(send_handle)
|
||||||
|
print "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def do_l3_tests(node1, node2, bandwidth, duration, ps, type):
|
||||||
|
"""
|
||||||
|
Do L3 tunneling tests. Each node is given as 4 tuple - physical
|
||||||
|
interface IP, control port, test IP and test port.
|
||||||
|
"""
|
||||||
|
server1 = util.rpc_client(node1[0], node1[1])
|
||||||
|
server2 = util.rpc_client(node2[0], node2[1])
|
||||||
|
servers_with_bridges = []
|
||||||
|
try:
|
||||||
|
server1.create_bridge(DEFAULT_TEST_BRIDGE)
|
||||||
|
servers_with_bridges.append(server1)
|
||||||
|
server2.create_bridge(DEFAULT_TEST_BRIDGE)
|
||||||
|
servers_with_bridges.append(server2)
|
||||||
|
|
||||||
|
server1.interface_up(DEFAULT_TEST_BRIDGE)
|
||||||
|
server2.interface_up(DEFAULT_TEST_BRIDGE)
|
||||||
|
|
||||||
|
server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None)
|
||||||
|
server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None)
|
||||||
|
|
||||||
|
server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
|
||||||
|
server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
|
||||||
|
|
||||||
|
server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
|
||||||
|
None, type)
|
||||||
|
server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
|
||||||
|
None, type)
|
||||||
|
server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
|
||||||
|
"remote_ip", node2[0])
|
||||||
|
server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
|
||||||
|
"remote_ip", node1[0])
|
||||||
|
|
||||||
|
do_udp_tests(node1, node2, bandwidth, duration, ps)
|
||||||
|
do_udp_tests(node2, node1, bandwidth, duration, ps)
|
||||||
|
do_tcp_tests(node1, node2, duration)
|
||||||
|
do_tcp_tests(node2, node1, duration)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
for server in servers_with_bridges:
|
||||||
|
server.del_bridge(DEFAULT_TEST_BRIDGE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag):
|
||||||
|
"""
|
||||||
|
Do VLAN tests between node1 and node2. Each node is given
|
||||||
|
as 4 tuple - physical interface IP, control port, test IP and
|
||||||
|
test port.
|
||||||
|
"""
|
||||||
|
server1 = util.rpc_client(node1[0], node1[1])
|
||||||
|
server2 = util.rpc_client(node2[0], node2[1])
|
||||||
|
|
||||||
|
br_name1 = None
|
||||||
|
br_name2 = None
|
||||||
|
|
||||||
|
servers_with_test_ports = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
interface_node1 = server1.get_interface(node1[0])
|
||||||
|
interface_node2 = server2.get_interface(node2[0])
|
||||||
|
|
||||||
|
if server1.is_ovs_bridge(interface_node1):
|
||||||
|
br_name1 = interface_node1
|
||||||
|
else:
|
||||||
|
br_name1 = DEFAULT_TEST_BRIDGE
|
||||||
|
server1.create_test_bridge(br_name1, interface_node1)
|
||||||
|
|
||||||
|
if server2.is_ovs_bridge(interface_node2):
|
||||||
|
br_name2 = interface_node2
|
||||||
|
else:
|
||||||
|
br_name2 = DEFAULT_TEST_BRIDGE
|
||||||
|
server2.create_test_bridge(br_name2, interface_node2)
|
||||||
|
|
||||||
|
server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT)
|
||||||
|
servers_with_test_ports.append(server1)
|
||||||
|
server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT)
|
||||||
|
servers_with_test_ports.append(server2)
|
||||||
|
|
||||||
|
server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
|
||||||
|
server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
|
||||||
|
|
||||||
|
server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
|
||||||
|
"internal")
|
||||||
|
server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
|
||||||
|
"internal")
|
||||||
|
|
||||||
|
server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None)
|
||||||
|
server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None)
|
||||||
|
|
||||||
|
server1.interface_up(DEFAULT_TEST_PORT)
|
||||||
|
server2.interface_up(DEFAULT_TEST_PORT)
|
||||||
|
|
||||||
|
do_udp_tests(node1, node2, bandwidth, duration, ps)
|
||||||
|
do_udp_tests(node2, node1, bandwidth, duration, ps)
|
||||||
|
do_tcp_tests(node1, node2, duration)
|
||||||
|
do_tcp_tests(node2, node1, duration)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
for server in servers_with_test_ports:
|
||||||
|
server.del_port_from_bridge(DEFAULT_TEST_PORT)
|
||||||
|
if br_name1 == DEFAULT_TEST_BRIDGE:
|
||||||
|
server1.del_test_bridge(br_name1, interface_node1)
|
||||||
|
if br_name2 == DEFAULT_TEST_BRIDGE:
|
||||||
|
server2.del_test_bridge(br_name2, interface_node2)
|
||||||
|
|
||||||
|
|
||||||
|
def do_direct_tests(node1, node2, bandwidth, duration, ps):
|
||||||
|
"""
|
||||||
|
Do tests between outer IPs without involving Open vSwitch. Each
|
||||||
|
node is given as 4 tuple - physical interface IP, control port,
|
||||||
|
test IP and test port. Direct tests will use physical interface
|
||||||
|
IP as the test IP address.
|
||||||
|
"""
|
||||||
|
n1 = (node1[0], node1[1], node1[0], node1[3])
|
||||||
|
n2 = (node2[0], node2[1], node2[0], node2[3])
|
||||||
|
|
||||||
|
do_udp_tests(n1, n2, bandwidth, duration, ps)
|
||||||
|
do_udp_tests(n2, n1, bandwidth, duration, ps)
|
||||||
|
do_tcp_tests(n1, n2, duration)
|
||||||
|
do_tcp_tests(n2, n1, duration)
|
||||||
|
|
||||||
|
|
||||||
|
def configure_l3(conf, tunnel_mode):
|
||||||
|
"""
|
||||||
|
This function creates a temporary test bridge and adds an L3 tunnel.
|
||||||
|
"""
|
||||||
|
s = util.start_local_server(conf[1][1])
|
||||||
|
server = util.rpc_client("127.0.0.1", conf[1][1])
|
||||||
|
server.create_bridge(DEFAULT_TEST_BRIDGE)
|
||||||
|
server.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_PORT)
|
||||||
|
server.interface_up(DEFAULT_TEST_BRIDGE)
|
||||||
|
server.interface_assign_ip(DEFAULT_TEST_BRIDGE, conf[1][0],
|
||||||
|
None)
|
||||||
|
server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type",
|
||||||
|
None, tunnel_mode)
|
||||||
|
server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "options",
|
||||||
|
"remote_ip", conf[0])
|
||||||
|
return s
|
@ -19,10 +19,13 @@ import array
|
|||||||
import exceptions
|
import exceptions
|
||||||
import fcntl
|
import fcntl
|
||||||
import os
|
import os
|
||||||
|
import select
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
|
import xmlrpclib
|
||||||
|
|
||||||
|
|
||||||
def str_ip(ip_address):
|
def str_ip(ip_address):
|
||||||
@ -147,3 +150,81 @@ def move_routes(iface1, iface2):
|
|||||||
for route in out.splitlines():
|
for route in out.splitlines():
|
||||||
args = ["ip", "route", "replace", "dev", iface2] + route.split()
|
args = ["ip", "route", "replace", "dev", iface2] + route.split()
|
||||||
start_process(args)
|
start_process(args)
|
||||||
|
|
||||||
|
|
||||||
|
def get_interface_from_routing_decision(ip):
|
||||||
|
"""
|
||||||
|
This function returns the interface through which the given ip address
|
||||||
|
is reachable.
|
||||||
|
"""
|
||||||
|
args = ["ip", "route", "get", ip]
|
||||||
|
ret, out, _err = start_process(args)
|
||||||
|
if ret == 0:
|
||||||
|
iface = re.search(r'dev (\S+)', out)
|
||||||
|
if iface:
|
||||||
|
return iface.group(1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def rpc_client(ip, port):
|
||||||
|
return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
|
||||||
|
|
||||||
|
|
||||||
|
def sigint_intercept():
|
||||||
|
"""
|
||||||
|
Intercept SIGINT from child (the local ovs-test server process).
|
||||||
|
"""
|
||||||
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
|
|
||||||
|
|
||||||
|
def start_local_server(port):
|
||||||
|
"""
|
||||||
|
This function spawns an ovs-test server that listens on specified port
|
||||||
|
and blocks till the spawned ovs-test server is ready to accept XML RPC
|
||||||
|
connections.
|
||||||
|
"""
|
||||||
|
p = subprocess.Popen(["ovs-test", "-s", str(port)],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
|
preexec_fn=sigint_intercept)
|
||||||
|
fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
|
||||||
|
fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||||
|
|
||||||
|
while p.poll() is None:
|
||||||
|
fd = select.select([p.stdout.fileno()], [], [])[0]
|
||||||
|
if fd:
|
||||||
|
out = p.stdout.readline()
|
||||||
|
if out.startswith("Starting RPC server"):
|
||||||
|
break
|
||||||
|
if p.poll() is not None:
|
||||||
|
raise RuntimeError("Couldn't start local instance of ovs-test server")
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def get_datagram_sizes(mtu1, mtu2):
|
||||||
|
"""
|
||||||
|
This function calculates all the "interesting" datagram sizes so that
|
||||||
|
we test both - receive and send side with different packets sizes.
|
||||||
|
"""
|
||||||
|
s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
|
||||||
|
s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
|
||||||
|
return sorted(s1.union(s2))
|
||||||
|
|
||||||
|
|
||||||
|
def ip_from_cidr(string):
|
||||||
|
"""
|
||||||
|
This function removes the netmask (if present) from the given string and
|
||||||
|
returns the IP address.
|
||||||
|
"""
|
||||||
|
token = string.split("/")
|
||||||
|
return token[0]
|
||||||
|
|
||||||
|
|
||||||
|
def bandwidth_to_string(bwidth):
|
||||||
|
"""Convert bandwidth from long to string and add units."""
|
||||||
|
bwidth = bwidth * 8 # Convert back to bits/second
|
||||||
|
if bwidth >= 10000000:
|
||||||
|
return str(int(bwidth / 1000000)) + "Mbps"
|
||||||
|
elif bwidth > 10000:
|
||||||
|
return str(int(bwidth / 1000)) + "Kbps"
|
||||||
|
else:
|
||||||
|
return str(int(bwidth)) + "bps"
|
||||||
|
@ -59,7 +59,9 @@ rm \
|
|||||||
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
|
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
|
||||||
$RPM_BUILD_ROOT/usr/bin/ovs-test \
|
$RPM_BUILD_ROOT/usr/bin/ovs-test \
|
||||||
|
$RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
|
||||||
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
|
||||||
$RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \
|
$RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8
|
||||||
|
|
||||||
|
2
utilities/.gitignore
vendored
2
utilities/.gitignore
vendored
@ -13,6 +13,8 @@
|
|||||||
/ovs-ctl
|
/ovs-ctl
|
||||||
/ovs-dpctl
|
/ovs-dpctl
|
||||||
/ovs-dpctl.8
|
/ovs-dpctl.8
|
||||||
|
/ovs-l3ping
|
||||||
|
/ovs-l3ping.8
|
||||||
/ovs-lib
|
/ovs-lib
|
||||||
/ovs-ofctl
|
/ovs-ofctl
|
||||||
/ovs-ofctl.8
|
/ovs-ofctl.8
|
||||||
|
@ -7,6 +7,7 @@ bin_PROGRAMS += \
|
|||||||
bin_SCRIPTS += utilities/ovs-pki utilities/ovs-vsctl utilities/ovs-parse-leaks
|
bin_SCRIPTS += utilities/ovs-pki utilities/ovs-vsctl utilities/ovs-parse-leaks
|
||||||
if HAVE_PYTHON
|
if HAVE_PYTHON
|
||||||
bin_SCRIPTS += \
|
bin_SCRIPTS += \
|
||||||
|
utilities/ovs-l3ping \
|
||||||
utilities/ovs-pcap \
|
utilities/ovs-pcap \
|
||||||
utilities/ovs-tcpundump \
|
utilities/ovs-tcpundump \
|
||||||
utilities/ovs-test \
|
utilities/ovs-test \
|
||||||
@ -22,6 +23,7 @@ scripts_DATA += utilities/ovs-lib
|
|||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
utilities/ovs-check-dead-ifs.in \
|
utilities/ovs-check-dead-ifs.in \
|
||||||
utilities/ovs-ctl.in \
|
utilities/ovs-ctl.in \
|
||||||
|
utilities/ovs-l3ping.in \
|
||||||
utilities/ovs-lib.in \
|
utilities/ovs-lib.in \
|
||||||
utilities/ovs-parse-leaks.in \
|
utilities/ovs-parse-leaks.in \
|
||||||
utilities/ovs-pcap.in \
|
utilities/ovs-pcap.in \
|
||||||
@ -37,6 +39,7 @@ MAN_ROOTS += \
|
|||||||
utilities/ovs-controller.8.in \
|
utilities/ovs-controller.8.in \
|
||||||
utilities/ovs-ctl.8 \
|
utilities/ovs-ctl.8 \
|
||||||
utilities/ovs-dpctl.8.in \
|
utilities/ovs-dpctl.8.in \
|
||||||
|
utilities/ovs-l3ping.8.in \
|
||||||
utilities/ovs-ofctl.8.in \
|
utilities/ovs-ofctl.8.in \
|
||||||
utilities/ovs-parse-leaks.8 \
|
utilities/ovs-parse-leaks.8 \
|
||||||
utilities/ovs-pcap.1.in \
|
utilities/ovs-pcap.1.in \
|
||||||
@ -54,6 +57,8 @@ DISTCLEANFILES += \
|
|||||||
utilities/ovs-check-dead-ifs \
|
utilities/ovs-check-dead-ifs \
|
||||||
utilities/ovs-controller.8 \
|
utilities/ovs-controller.8 \
|
||||||
utilities/ovs-dpctl.8 \
|
utilities/ovs-dpctl.8 \
|
||||||
|
utilities/ovs-l3ping \
|
||||||
|
utilities/ovs-l3ping.8 \
|
||||||
utilities/ovs-lib \
|
utilities/ovs-lib \
|
||||||
utilities/ovs-ofctl.8 \
|
utilities/ovs-ofctl.8 \
|
||||||
utilities/ovs-parse-leaks \
|
utilities/ovs-parse-leaks \
|
||||||
@ -76,6 +81,7 @@ man_MANS += \
|
|||||||
utilities/ovs-benchmark.1 \
|
utilities/ovs-benchmark.1 \
|
||||||
utilities/ovs-controller.8 \
|
utilities/ovs-controller.8 \
|
||||||
utilities/ovs-dpctl.8 \
|
utilities/ovs-dpctl.8 \
|
||||||
|
utilities/ovs-l3ping.8 \
|
||||||
utilities/ovs-ofctl.8 \
|
utilities/ovs-ofctl.8 \
|
||||||
utilities/ovs-parse-leaks.8 \
|
utilities/ovs-parse-leaks.8 \
|
||||||
utilities/ovs-pcap.1 \
|
utilities/ovs-pcap.1 \
|
||||||
|
114
utilities/ovs-l3ping.8.in
Normal file
114
utilities/ovs-l3ping.8.in
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
.de IQ
|
||||||
|
. br
|
||||||
|
. ns
|
||||||
|
. IP "\\$1"
|
||||||
|
..
|
||||||
|
.TH ovs\-l3ping 1 "June 2012" "Open vSwitch" "Open vSwitch Manual"
|
||||||
|
.
|
||||||
|
.SH NAME
|
||||||
|
\fBovs\-l3ping\fR \- check network deployment for L3 tunneling
|
||||||
|
problems
|
||||||
|
.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
\fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask]\fR \fB\-t\fR \fItunnelmode\fR
|
||||||
|
.br
|
||||||
|
\fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR \fB\-t\fR \fItunnelmode\fR
|
||||||
|
.PP
|
||||||
|
\fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask],RemoteInnerIP\fR \fB\-t\fR \fItunnelmode\fR
|
||||||
|
.br
|
||||||
|
\fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
|
||||||
|
[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR
|
||||||
|
[\fB\-b\fR \fItargetbandwidth\fR] [\fB\-i\fR \fItestinterval\fR]
|
||||||
|
\fB\-t\fR \fItunnelmode\fR
|
||||||
|
.so lib/common-syn.man
|
||||||
|
.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The \fBovs\-l3ping\fR program may be used to check for problems that could
|
||||||
|
be caused by invalid routing policy, misconfigured firewall in the tunnel
|
||||||
|
path or a bad NIC driver. On one of the nodes, run \fBovs\-l3ping\fR in
|
||||||
|
server mode and on the other node run it in client mode. The client and
|
||||||
|
server will establish L3 tunnel, over which client will give further testing
|
||||||
|
instructions. The \fBovs\-l3ping\fR client will perform UDP and TCP tests.
|
||||||
|
This tool is different from \fBovs\-test\fR that it encapsulates XML/RPC
|
||||||
|
control connection over the tunnel, so there is no need to open special holes
|
||||||
|
in firewall.
|
||||||
|
.PP
|
||||||
|
UDP tests can report packet loss and achieved bandwidth for various
|
||||||
|
datagram sizes. By default target bandwidth for UDP tests is 1Mbit/s.
|
||||||
|
.PP
|
||||||
|
TCP tests report only achieved bandwidth, because kernel TCP stack
|
||||||
|
takes care of flow control and packet loss.
|
||||||
|
.
|
||||||
|
.SS "Client Mode"
|
||||||
|
An \fBovs\-l3ping\fR client will create a L3 tunnel and connect over it to the
|
||||||
|
\fBovs\-l3ping\fR server to schedule the tests. \fITunnelRemoteIP\fR is the
|
||||||
|
peer's IP address, where tunnel will be terminated. \fIInnerIP\fR is the
|
||||||
|
address that will be temporarily assigned during testing. All test traffic
|
||||||
|
originating from this IP address to the \fIRemoteInnerIP\fR will be tunneled.
|
||||||
|
It is possible to override default \fIControlPort\fR and \fIDataPort\fR, if
|
||||||
|
there is any other application that already listens on those two ports.
|
||||||
|
.
|
||||||
|
.SS "Server Mode"
|
||||||
|
To conduct tests, \fBovs\-l3ping\fR server must be running. It is required
|
||||||
|
that both client and server \fIInnerIP\fR addresses are in the same subnet.
|
||||||
|
It is possible to specify \fIInnerIP\fR with netmask in CIDR format.
|
||||||
|
.
|
||||||
|
.SH OPTIONS
|
||||||
|
One of \fB\-s\fR or \fB\-c\fR is required. The \fB\-t\fR option is
|
||||||
|
also required.
|
||||||
|
.
|
||||||
|
.IP "\fB\-s \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR"
|
||||||
|
.IQ "\fB\-\-server\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR"
|
||||||
|
Run in server mode and create L3 tunnel with the client that will be
|
||||||
|
accepting tunnel at \fITunnelRemoteIP\fR address. The socket on
|
||||||
|
\fIInnerIP[:ControlPort]\fR will be used to receive further instructions
|
||||||
|
from the client.
|
||||||
|
.
|
||||||
|
.IP "\fB\-c \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
|
||||||
|
[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR"
|
||||||
|
.IQ "\fB\-\-client \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
|
||||||
|
[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR"
|
||||||
|
Run in client mode and create L3 tunnel with the server on
|
||||||
|
\fITunnelRemoteIP\fR. The client will use \fIInnerIP\fR to generate test
|
||||||
|
traffic with the server's \fIRemoteInnerIP\fR.
|
||||||
|
.
|
||||||
|
.IP "\fB\-b \fItargetbandwidth\fR"
|
||||||
|
.IQ "\fB\-\-bandwidth\fR \fItargetbandwidth\fR"
|
||||||
|
Target bandwidth for UDP tests. The \fItargetbandwidth\fR must be given in
|
||||||
|
bits per second. It is possible to use postfix M or K to alter the target
|
||||||
|
bandwidth magnitude.
|
||||||
|
.
|
||||||
|
.IP "\fB\-i \fItestinterval\fR"
|
||||||
|
.IQ "\fB\-\-interval\fR \fItestinterval\fR"
|
||||||
|
How long each test should run. By default 5 seconds.
|
||||||
|
.
|
||||||
|
.IP "\fB\-t \fItunnelmode\fR"
|
||||||
|
.IQ "\fB\-\-tunnel\-mode\fR \fItunnelmode\fR"
|
||||||
|
Specify the tunnel type. This option must match on server and client.
|
||||||
|
.
|
||||||
|
.so lib/common.man
|
||||||
|
.
|
||||||
|
.SH EXAMPLES
|
||||||
|
.PP
|
||||||
|
On host 192.168.122.220 start \fBovs\-l3ping\fR in server mode. This command
|
||||||
|
will create a temporary GRE tunnel with the host 192.168.122.236 and assign
|
||||||
|
10.1.1.1/28 as the inner IP address, where client will have to connect:
|
||||||
|
.IP
|
||||||
|
.B ovs\-l3ping -s 192.168.122.236,10.1.1.1/28 -t gre
|
||||||
|
.
|
||||||
|
.PP
|
||||||
|
On host 192.168.122.236 start \fBovs\-l3ping\fR in client mode. This command
|
||||||
|
will use 10.1.1.2/28 as the local inner IP address and will connect over the
|
||||||
|
L3 tunnel to the server's inner IP address at 10.1.1.1.
|
||||||
|
.IP
|
||||||
|
.B ovs\-l3ping -c 192.168.122.220,10.1.1.2/28,10.1.1.1 -t gre
|
||||||
|
.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.
|
||||||
|
.BR ovs\-vswitchd (8),
|
||||||
|
.BR ovs\-ofctl (8),
|
||||||
|
.BR ovs\-vsctl (8),
|
||||||
|
.BR ovs\-vlan\-test (8),
|
||||||
|
.BR ovs\-test (8),
|
||||||
|
.BR ethtool (8),
|
||||||
|
.BR uname (1)
|
76
utilities/ovs-l3ping.in
Normal file
76
utilities/ovs-l3ping.in
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#! @PYTHON@
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at:
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
ovs L3 ping utility allows to do tests between two remote hosts without
|
||||||
|
opening holes in the firewall for the XML RPC control connection. This is
|
||||||
|
achieved by tunneling the control connection inside the tunnel itself.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import xmlrpclib
|
||||||
|
|
||||||
|
import ovstest.args as args
|
||||||
|
import ovstest.tests as tests
|
||||||
|
import ovstest.util as util
|
||||||
|
|
||||||
|
|
||||||
|
def get_packet_sizes(me, he, remote_ip):
|
||||||
|
"""
|
||||||
|
This function retrieves MTUs from both hosts and returns a list of
|
||||||
|
packet sizes, that are more likely to uncover possible configuration
|
||||||
|
issues.
|
||||||
|
"""
|
||||||
|
mtu_node1 = 1500
|
||||||
|
mtu_node2 = 1500
|
||||||
|
server1 = util.rpc_client(me[0], me[1])
|
||||||
|
server2 = util.rpc_client(he[0], he[1])
|
||||||
|
iface1 = server2.get_interface(remote_ip)
|
||||||
|
iface2 = server1.get_interface_from_routing_decision(remote_ip)
|
||||||
|
if iface1:
|
||||||
|
mtu_node1 = server2.get_interface_mtu(iface1)
|
||||||
|
if iface2:
|
||||||
|
mtu_node2 = server1.get_interface_mtu(iface2)
|
||||||
|
return util.get_datagram_sizes(mtu_node1, mtu_node2)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
local_server = None
|
||||||
|
try:
|
||||||
|
args = args.l3_initialize_args()
|
||||||
|
tunnel_mode = args.tunnelMode
|
||||||
|
if args.server is not None: # Start in server mode
|
||||||
|
local_server = tests.configure_l3(args.server, tunnel_mode)
|
||||||
|
local_server.wait()
|
||||||
|
elif args.client is not None: # Run in client mode
|
||||||
|
bandwidth = args.targetBandwidth
|
||||||
|
interval = args.testInterval
|
||||||
|
me = (util.ip_from_cidr(args.client[1][0]), args.client[1][1],
|
||||||
|
args.client[1][0], args.client[1][2])
|
||||||
|
he = (args.client[2][0], args.client[2][1],
|
||||||
|
args.client[2][0], args.client[2][2])
|
||||||
|
local_server = tests. configure_l3(args.client, tunnel_mode)
|
||||||
|
ps = get_packet_sizes(me, he, args.client[0])
|
||||||
|
tests.do_direct_tests(me, he, bandwidth, interval, ps)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print "Terminating"
|
||||||
|
except xmlrpclib.Fault:
|
||||||
|
print "Couldn't contact peer"
|
||||||
|
except socket.error:
|
||||||
|
print "Couldn't contact peer"
|
||||||
|
except xmlrpclib.ProtocolError:
|
||||||
|
print "XMLRPC control channel was abruptly terminated"
|
||||||
|
finally:
|
||||||
|
if local_server is not None:
|
||||||
|
local_server.terminate()
|
@ -32,80 +32,18 @@ import twisted
|
|||||||
|
|
||||||
import ovstest.args as args
|
import ovstest.args as args
|
||||||
import ovstest.rpcserver as rpcserver
|
import ovstest.rpcserver as rpcserver
|
||||||
|
import ovstest.tests as tests
|
||||||
|
import ovstest.util as util
|
||||||
|
|
||||||
DEFAULT_TEST_BRIDGE = "ovstestbr0"
|
DEFAULT_TEST_BRIDGE = "ovstestbr0"
|
||||||
DEFAULT_TEST_PORT = "ovstestport0"
|
DEFAULT_TEST_PORT = "ovstestport0"
|
||||||
DEFAULT_TEST_TUN = "ovstestport1"
|
DEFAULT_TEST_TUN = "ovstestport1"
|
||||||
|
|
||||||
|
|
||||||
def rpc_client(ip, port):
|
|
||||||
return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
|
|
||||||
|
|
||||||
|
|
||||||
def sigint_intercept():
|
|
||||||
"""
|
|
||||||
Intercept SIGINT from child (the local ovs-test server process).
|
|
||||||
"""
|
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
||||||
|
|
||||||
|
|
||||||
def start_local_server(port):
|
|
||||||
"""
|
|
||||||
This function spawns an ovs-test server that listens on specified port
|
|
||||||
and blocks till the spawned ovs-test server is ready to accept XML RPC
|
|
||||||
connections.
|
|
||||||
"""
|
|
||||||
p = subprocess.Popen(["ovs-test", "-s", str(port)],
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
||||||
preexec_fn = sigint_intercept)
|
|
||||||
fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
|
|
||||||
fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
|
|
||||||
|
|
||||||
while p.poll() is None:
|
|
||||||
fd = select.select([p.stdout.fileno()], [], [])[0]
|
|
||||||
if fd:
|
|
||||||
out = p.stdout.readline()
|
|
||||||
if out.startswith("Starting RPC server"):
|
|
||||||
break
|
|
||||||
if p.poll() is not None:
|
|
||||||
raise RuntimeError("Couldn't start local instance of ovs-test server")
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def get_datagram_sizes(mtu1, mtu2):
|
|
||||||
"""
|
|
||||||
This function calculates all the "interesting" datagram sizes so that
|
|
||||||
we test both - receive and send side with different packets sizes.
|
|
||||||
"""
|
|
||||||
s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
|
|
||||||
s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
|
|
||||||
return sorted(s1.union(s2))
|
|
||||||
|
|
||||||
|
|
||||||
def ip_from_cidr(string):
|
|
||||||
"""
|
|
||||||
This function removes the netmask (if present) from the given string and
|
|
||||||
returns the IP address.
|
|
||||||
"""
|
|
||||||
token = string.split("/")
|
|
||||||
return token[0]
|
|
||||||
|
|
||||||
|
|
||||||
def bandwidth_to_string(bwidth):
|
|
||||||
"""Convert bandwidth from long to string and add units."""
|
|
||||||
bwidth = bwidth * 8 # Convert back to bits/second
|
|
||||||
if bwidth >= 10000000:
|
|
||||||
return str(int(bwidth / 1000000)) + "Mbps"
|
|
||||||
elif bwidth > 10000:
|
|
||||||
return str(int(bwidth / 1000)) + "Kbps"
|
|
||||||
else:
|
|
||||||
return str(int(bwidth)) + "bps"
|
|
||||||
|
|
||||||
|
|
||||||
def collect_information(node):
|
def collect_information(node):
|
||||||
"""Print information about hosts that will do testing"""
|
"""Print information about hosts that will do testing"""
|
||||||
print "Node %s:%u " % (node[0], node[1])
|
print "Node %s:%u " % (node[0], node[1])
|
||||||
server = rpc_client(node[0], node[1])
|
server = util.rpc_client(node[0], node[1])
|
||||||
interface_name = server.get_interface(node[0])
|
interface_name = server.get_interface(node[0])
|
||||||
phys_iface = None
|
phys_iface = None
|
||||||
uname = server.uname()
|
uname = server.uname()
|
||||||
@ -140,210 +78,6 @@ def collect_information(node):
|
|||||||
return mtu
|
return mtu
|
||||||
|
|
||||||
|
|
||||||
def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes):
|
|
||||||
"""Schedule UDP tests between receiver and sender"""
|
|
||||||
server1 = rpc_client(receiver[0], receiver[1])
|
|
||||||
server2 = rpc_client(sender[0], sender[1])
|
|
||||||
|
|
||||||
udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}'
|
|
||||||
|
|
||||||
print ("UDP test from %s:%u to %s:%u with target bandwidth %s" %
|
|
||||||
(sender[0], sender[1], receiver[0], receiver[1],
|
|
||||||
bandwidth_to_string(tbwidth)))
|
|
||||||
print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams",
|
|
||||||
"Datagram Loss", "Bandwidth")
|
|
||||||
|
|
||||||
for size in port_sizes:
|
|
||||||
listen_handle = -1
|
|
||||||
send_handle = -1
|
|
||||||
try:
|
|
||||||
packetcnt = (tbwidth * duration) / size
|
|
||||||
|
|
||||||
listen_handle = server1.create_udp_listener(receiver[3])
|
|
||||||
if listen_handle == -1:
|
|
||||||
print ("Server could not open UDP listening socket on port"
|
|
||||||
" %u. Try to restart the server.\n" % receiver[3])
|
|
||||||
return
|
|
||||||
send_handle = server2.create_udp_sender(
|
|
||||||
(ip_from_cidr(receiver[2]),
|
|
||||||
receiver[3]), packetcnt, size,
|
|
||||||
duration)
|
|
||||||
|
|
||||||
# Using sleep here because there is no other synchronization source
|
|
||||||
# that would notify us when all sent packets were received
|
|
||||||
time.sleep(duration + 1)
|
|
||||||
|
|
||||||
rcv_packets = server1.get_udp_listener_results(listen_handle)
|
|
||||||
snt_packets = server2.get_udp_sender_results(send_handle)
|
|
||||||
|
|
||||||
loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) /
|
|
||||||
snt_packets) / 100
|
|
||||||
bwidth = (rcv_packets * size) / duration
|
|
||||||
|
|
||||||
print udpformat.format(size, snt_packets, rcv_packets,
|
|
||||||
'%.2f%%' % loss, bandwidth_to_string(bwidth))
|
|
||||||
finally:
|
|
||||||
if listen_handle != -1:
|
|
||||||
server1.close_udp_listener(listen_handle)
|
|
||||||
if send_handle != -1:
|
|
||||||
server2.close_udp_sender(send_handle)
|
|
||||||
print "\n"
|
|
||||||
|
|
||||||
|
|
||||||
def do_tcp_tests(receiver, sender, duration):
|
|
||||||
"""Schedule TCP tests between receiver and sender"""
|
|
||||||
server1 = rpc_client(receiver[0], receiver[1])
|
|
||||||
server2 = rpc_client(sender[0], sender[1])
|
|
||||||
|
|
||||||
tcpformat = '{0:>15} {1:>15} {2:>15}'
|
|
||||||
print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1],
|
|
||||||
receiver[0], receiver[1])
|
|
||||||
print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")
|
|
||||||
|
|
||||||
listen_handle = -1
|
|
||||||
send_handle = -1
|
|
||||||
try:
|
|
||||||
listen_handle = server1.create_tcp_listener(receiver[3])
|
|
||||||
if listen_handle == -1:
|
|
||||||
print ("Server was unable to open TCP listening socket on port"
|
|
||||||
" %u. Try to restart the server.\n" % receiver[3])
|
|
||||||
return
|
|
||||||
send_handle = server2.create_tcp_sender(ip_from_cidr(receiver[2]),
|
|
||||||
receiver[3], duration)
|
|
||||||
|
|
||||||
time.sleep(duration + 1)
|
|
||||||
|
|
||||||
rcv_bytes = long(server1.get_tcp_listener_results(listen_handle))
|
|
||||||
snt_bytes = long(server2.get_tcp_sender_results(send_handle))
|
|
||||||
|
|
||||||
bwidth = rcv_bytes / duration
|
|
||||||
|
|
||||||
print tcpformat.format(snt_bytes, rcv_bytes,
|
|
||||||
bandwidth_to_string(bwidth))
|
|
||||||
finally:
|
|
||||||
if listen_handle != -1:
|
|
||||||
server1.close_tcp_listener(listen_handle)
|
|
||||||
if send_handle != -1:
|
|
||||||
server2.close_tcp_sender(send_handle)
|
|
||||||
print "\n"
|
|
||||||
|
|
||||||
|
|
||||||
def do_l3_tests(node1, node2, bandwidth, duration, ps, type):
|
|
||||||
"""
|
|
||||||
Do L3 tunneling tests.
|
|
||||||
"""
|
|
||||||
server1 = rpc_client(node1[0], node1[1])
|
|
||||||
server2 = rpc_client(node2[0], node2[1])
|
|
||||||
servers_with_bridges = []
|
|
||||||
try:
|
|
||||||
server1.create_bridge(DEFAULT_TEST_BRIDGE)
|
|
||||||
servers_with_bridges.append(server1)
|
|
||||||
server2.create_bridge(DEFAULT_TEST_BRIDGE)
|
|
||||||
servers_with_bridges.append(server2)
|
|
||||||
|
|
||||||
server1.interface_up(DEFAULT_TEST_BRIDGE)
|
|
||||||
server2.interface_up(DEFAULT_TEST_BRIDGE)
|
|
||||||
|
|
||||||
server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None)
|
|
||||||
server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None)
|
|
||||||
|
|
||||||
server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
|
|
||||||
server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
|
|
||||||
|
|
||||||
server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
|
|
||||||
None, type)
|
|
||||||
server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
|
|
||||||
None, type)
|
|
||||||
server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
|
|
||||||
"remote_ip", node2[0])
|
|
||||||
server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
|
|
||||||
"remote_ip", node1[0])
|
|
||||||
|
|
||||||
do_udp_tests(node1, node2, bandwidth, duration, ps)
|
|
||||||
do_udp_tests(node2, node1, bandwidth, duration, ps)
|
|
||||||
do_tcp_tests(node1, node2, duration)
|
|
||||||
do_tcp_tests(node2, node1, duration)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
for server in servers_with_bridges:
|
|
||||||
server.del_bridge(DEFAULT_TEST_BRIDGE)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag):
|
|
||||||
"""
|
|
||||||
Do VLAN tests between node1 and node2.
|
|
||||||
"""
|
|
||||||
server1 = rpc_client(node1[0], node1[1])
|
|
||||||
server2 = rpc_client(node2[0], node2[1])
|
|
||||||
|
|
||||||
br_name1 = None
|
|
||||||
br_name2 = None
|
|
||||||
|
|
||||||
servers_with_test_ports = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
interface_node1 = server1.get_interface(node1[0])
|
|
||||||
interface_node2 = server2.get_interface(node2[0])
|
|
||||||
|
|
||||||
if server1.is_ovs_bridge(interface_node1):
|
|
||||||
br_name1 = interface_node1
|
|
||||||
else:
|
|
||||||
br_name1 = DEFAULT_TEST_BRIDGE
|
|
||||||
server1.create_test_bridge(br_name1, interface_node1)
|
|
||||||
|
|
||||||
if server2.is_ovs_bridge(interface_node2):
|
|
||||||
br_name2 = interface_node2
|
|
||||||
else:
|
|
||||||
br_name2 = DEFAULT_TEST_BRIDGE
|
|
||||||
server2.create_test_bridge(br_name2, interface_node2)
|
|
||||||
|
|
||||||
server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT)
|
|
||||||
servers_with_test_ports.append(server1)
|
|
||||||
server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT)
|
|
||||||
servers_with_test_ports.append(server2)
|
|
||||||
|
|
||||||
server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
|
|
||||||
server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
|
|
||||||
|
|
||||||
server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
|
|
||||||
"internal")
|
|
||||||
server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
|
|
||||||
"internal")
|
|
||||||
|
|
||||||
server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None)
|
|
||||||
server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None)
|
|
||||||
|
|
||||||
server1.interface_up(DEFAULT_TEST_PORT)
|
|
||||||
server2.interface_up(DEFAULT_TEST_PORT)
|
|
||||||
|
|
||||||
do_udp_tests(node1, node2, bandwidth, duration, ps)
|
|
||||||
do_udp_tests(node2, node1, bandwidth, duration, ps)
|
|
||||||
do_tcp_tests(node1, node2, duration)
|
|
||||||
do_tcp_tests(node2, node1, duration)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
for server in servers_with_test_ports:
|
|
||||||
server.del_port_from_bridge(DEFAULT_TEST_PORT)
|
|
||||||
if br_name1 == DEFAULT_TEST_BRIDGE:
|
|
||||||
server1.del_test_bridge(br_name1, interface_node1)
|
|
||||||
if br_name2 == DEFAULT_TEST_BRIDGE:
|
|
||||||
server2.del_test_bridge(br_name2, interface_node2)
|
|
||||||
|
|
||||||
|
|
||||||
def do_direct_tests(node1, node2, bandwidth, duration, ps):
|
|
||||||
"""
|
|
||||||
Do tests between outer IPs without involving Open vSwitch
|
|
||||||
"""
|
|
||||||
n1 = (node1[0], node1[1], node1[0], node1[3])
|
|
||||||
n2 = (node2[0], node2[1], node2[0], node2[3])
|
|
||||||
|
|
||||||
do_udp_tests(n1, n2, bandwidth, duration, ps)
|
|
||||||
do_udp_tests(n2, n1, bandwidth, duration, ps)
|
|
||||||
do_tcp_tests(n1, n2, duration)
|
|
||||||
do_tcp_tests(n2, n1, duration)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
local_server = None
|
local_server = None
|
||||||
try:
|
try:
|
||||||
@ -360,10 +94,10 @@ if __name__ == '__main__':
|
|||||||
# ovs-test server by looking at the first OuterIP. if it is a
|
# ovs-test server by looking at the first OuterIP. if it is a
|
||||||
# 127.0.0.1 then spawn local ovs-test server.
|
# 127.0.0.1 then spawn local ovs-test server.
|
||||||
if node1[0] == "127.0.0.1":
|
if node1[0] == "127.0.0.1":
|
||||||
local_server = start_local_server(node1[1])
|
local_server = util.start_local_server(node1[1])
|
||||||
# We must determine the IP address that local ovs-test server
|
# We must determine the IP address that local ovs-test server
|
||||||
# will use:
|
# will use:
|
||||||
me = rpc_client(node1[0], node1[1])
|
me = util.rpc_client(node1[0], node1[1])
|
||||||
my_ip = me.get_my_address_from(node2[0], node2[1])
|
my_ip = me.get_my_address_from(node2[0], node2[1])
|
||||||
node1 = (my_ip, node1[1], node1[2], node1[3])
|
node1 = (my_ip, node1[1], node1[2], node1[3])
|
||||||
|
|
||||||
@ -372,7 +106,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
bandwidth = ovs_args.targetBandwidth
|
bandwidth = ovs_args.targetBandwidth
|
||||||
interval = ovs_args.testInterval
|
interval = ovs_args.testInterval
|
||||||
ps = get_datagram_sizes(mtu_node1, mtu_node2)
|
ps = util.get_datagram_sizes(mtu_node1, mtu_node2)
|
||||||
|
|
||||||
direct = ovs_args.direct
|
direct = ovs_args.direct
|
||||||
vlan_tag = ovs_args.vlanTag
|
vlan_tag = ovs_args.vlanTag
|
||||||
@ -380,15 +114,17 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
if direct is not None:
|
if direct is not None:
|
||||||
print "Performing direct tests"
|
print "Performing direct tests"
|
||||||
do_direct_tests(node2, node1, bandwidth, interval, ps)
|
tests.do_direct_tests(node2, node1, bandwidth, interval, ps)
|
||||||
|
|
||||||
if vlan_tag is not None:
|
if vlan_tag is not None:
|
||||||
print "Performing VLAN tests"
|
print "Performing VLAN tests"
|
||||||
do_vlan_tests(node2, node1, bandwidth, interval, ps, vlan_tag)
|
tests.do_vlan_tests(node2, node1, bandwidth, interval, ps,
|
||||||
|
vlan_tag)
|
||||||
|
|
||||||
for tmode in tunnel_modes:
|
for tmode in tunnel_modes:
|
||||||
print "Performing", tmode, "tests"
|
print "Performing", tmode, "tests"
|
||||||
do_l3_tests(node2, node1, bandwidth, interval, ps, tmode)
|
tests.do_l3_tests(node2, node1, bandwidth, interval, ps,
|
||||||
|
tmode)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
@ -125,13 +125,15 @@ rm \
|
|||||||
$RPM_BUILD_ROOT/usr/bin/ovs-benchmark \
|
$RPM_BUILD_ROOT/usr/bin/ovs-benchmark \
|
||||||
$RPM_BUILD_ROOT/usr/sbin/ovs-bugtool \
|
$RPM_BUILD_ROOT/usr/sbin/ovs-bugtool \
|
||||||
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
|
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
|
||||||
|
$RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
|
||||||
$RPM_BUILD_ROOT/usr/bin/ovs-pki \
|
$RPM_BUILD_ROOT/usr/bin/ovs-pki \
|
||||||
$RPM_BUILD_ROOT/usr/bin/ovs-test \
|
$RPM_BUILD_ROOT/usr/bin/ovs-test \
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
|
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man1/ovs-benchmark.1 \
|
$RPM_BUILD_ROOT/usr/share/man/man1/ovs-benchmark.1 \
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-bugtool.8 \
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-bugtool.8 \
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
|
||||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8
|
||||||
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \
|
||||||
|
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8
|
||||||
|
|
||||||
install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
|
install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user