[ovs-dev] [PATCH] ovs-l3ping: A new test utility that can detect L3 tunnel related issues
Ansis Atteka
aatteka at nicira.com
Thu Jun 28 22:39:14 UTC 2012
From: aatteka <aatteka at aatteka-dell.(none)>
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 at nicira.com>
---
NEWS | 5 +
debian/openvswitch-test.install | 1 +
debian/openvswitch-test.manpages | 1 +
manpages.mk | 8 ++
python/automake.mk | 1 +
python/ovstest/args.py | 74 ++++++++++
python/ovstest/rpcserver.py | 6 +
python/ovstest/tests.py | 230 +++++++++++++++++++++++++++++
python/ovstest/util.py | 82 +++++++++++
rhel/openvswitch.spec.in | 2 +
utilities/.gitignore | 2 +
utilities/automake.mk | 6 +
utilities/ovs-l3ping.8.in | 108 ++++++++++++++
utilities/ovs-l3ping.in | 76 ++++++++++
utilities/ovs-test.in | 286 ++-----------------------------------
xenserver/openvswitch-xen.spec.in | 6 +-
16 files changed, 617 insertions(+), 277 deletions(-)
create mode 100644 python/ovstest/tests.py
create mode 100644 utilities/ovs-l3ping.8.in
create mode 100644 utilities/ovs-l3ping.in
diff --git a/NEWS b/NEWS
index 28e3cf0..f8526b3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,11 @@
post-v1.7.0
------------------------
- 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. This utility does
+ not require administrator to open firewall hole for the XML/RPC
+ control channel.
- ovs-ofctl:
- "mod-port" command can now control all OpenFlow config flags.
- OpenFlow:
diff --git a/debian/openvswitch-test.install b/debian/openvswitch-test.install
index a36c828..2b0f8de 100644
--- a/debian/openvswitch-test.install
+++ b/debian/openvswitch-test.install
@@ -1,2 +1,3 @@
usr/share/openvswitch/python/ovstest usr/lib/python2.6/dist-packages/
usr/bin/ovs-test
+usr/bin/ovs-l3ping
\ No newline at end of file
diff --git a/debian/openvswitch-test.manpages b/debian/openvswitch-test.manpages
index 683c978..1a7f6d4 100644
--- a/debian/openvswitch-test.manpages
+++ b/debian/openvswitch-test.manpages
@@ -1 +1,2 @@
_debian/utilities/ovs-test.8
+_debian/utilities/ovs-l3ping.8
diff --git a/manpages.mk b/manpages.mk
index 1773263..f391fdf 100644
--- a/manpages.mk
+++ b/manpages.mk
@@ -120,6 +120,14 @@ utilities/ovs-dpctl.8.in:
lib/common.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.in \
lib/common.man \
diff --git a/python/automake.mk b/python/automake.mk
index 96869e3..b656f08 100644
--- a/python/automake.mk
+++ b/python/automake.mk
@@ -3,6 +3,7 @@ ovstest_pyfiles = \
python/ovstest/args.py \
python/ovstest/rpcserver.py \
python/ovstest/tcp.py \
+ python/ovstest/tests.py \
python/ovstest/udp.py \
python/ovstest/util.py \
python/ovstest/vswitch.py
diff --git a/python/ovstest/args.py b/python/ovstest/args.py
index 8e23a89..1dd97c1 100644
--- a/python/ovstest/args.py
+++ b/python/ovstest/args.py
@@ -78,6 +78,23 @@ def ip_optional_port(string, default_port, ip_callback):
"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("IP address from optional ports "
+ "must be colon-separated")
+
+
def vlan_tag(string):
"""
This function verifies whether given string is a correct VLAN tag.
@@ -154,6 +171,29 @@ def tunnel_types(string):
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.
+ """
+ remote_ip, me, he = string.split(',')
+ 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.
+ """
+ remote_ip, me = string.split(',')
+ return (ip_address(remote_ip),
+ ip_optional_port(me, CONTROL_PORT, ip_optional_mask))
+
+
def ovs_initialize_args():
"""
Initialize argument parsing for ovs-test utility.
@@ -197,3 +237,37 @@ def ovs_initialize_args():
'ovs-test server in the client mode by using 127.0.0.1 as '
'OuterIP.')
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()
diff --git a/python/ovstest/rpcserver.py b/python/ovstest/rpcserver.py
index 82bec3b..5c92014 100644
--- a/python/ovstest/rpcserver.py
+++ b/python/ovstest/rpcserver.py
@@ -343,6 +343,12 @@ class TestArena(xmlrpc.XMLRPC):
"""
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):
"""
diff --git a/python/ovstest/tests.py b/python/ovstest/tests.py
new file mode 100644
index 0000000..866c207
--- /dev/null
+++ b/python/ovstest/tests.py
@@ -0,0 +1,230 @@
+import math
+import time
+
+import ovstest.util as util
+
+DEFAULT_TEST_BRIDGE = "ovstestbr0"
+DEFAULT_TEST_PORT = "ovstestport0"
+DEFAULT_TEST_TUN = "ovstestport1"
+
+
+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 = -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(
+ (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 != -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 = 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 = -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(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 != -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 = 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.
+ """
+ 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
+ """
+ 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 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
diff --git a/python/ovstest/util.py b/python/ovstest/util.py
index ee2a878..138a8f4 100644
--- a/python/ovstest/util.py
+++ b/python/ovstest/util.py
@@ -19,11 +19,15 @@ import array
import exceptions
import fcntl
import os
+import select
import socket
import struct
+import signal
import subprocess
import re
+import xmlrpclib
+
def str_ip(ip_address):
"""
@@ -147,3 +151,81 @@ def move_routes(iface1, iface2):
for route in out.splitlines():
args = ["ip", "route", "replace", "dev", iface2] + route.split()
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"
diff --git a/rhel/openvswitch.spec.in b/rhel/openvswitch.spec.in
index 54442fb..de22c86 100644
--- a/rhel/openvswitch.spec.in
+++ b/rhel/openvswitch.spec.in
@@ -59,7 +59,9 @@ rm \
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
$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-l3ping.8 \
$RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8
diff --git a/utilities/.gitignore b/utilities/.gitignore
index 90f47ff..4f96a4f 100644
--- a/utilities/.gitignore
+++ b/utilities/.gitignore
@@ -13,6 +13,8 @@
/ovs-ctl
/ovs-dpctl
/ovs-dpctl.8
+/ovs-l3ping
+/ovs-l3ping.8
/ovs-lib
/ovs-ofctl
/ovs-ofctl.8
diff --git a/utilities/automake.mk b/utilities/automake.mk
index f3da1b1..04a7fdc 100644
--- a/utilities/automake.mk
+++ b/utilities/automake.mk
@@ -7,6 +7,7 @@ bin_PROGRAMS += \
bin_SCRIPTS += utilities/ovs-pki utilities/ovs-vsctl utilities/ovs-parse-leaks
if HAVE_PYTHON
bin_SCRIPTS += \
+ utilities/ovs-l3ping \
utilities/ovs-pcap \
utilities/ovs-tcpundump \
utilities/ovs-test \
@@ -22,6 +23,7 @@ scripts_DATA += utilities/ovs-lib
EXTRA_DIST += \
utilities/ovs-check-dead-ifs.in \
utilities/ovs-ctl.in \
+ utilities/ovs-l3ping.in \
utilities/ovs-lib.in \
utilities/ovs-parse-leaks.in \
utilities/ovs-pcap.in \
@@ -37,6 +39,7 @@ MAN_ROOTS += \
utilities/ovs-controller.8.in \
utilities/ovs-ctl.8 \
utilities/ovs-dpctl.8.in \
+ utilities/ovs-l3ping.8.in \
utilities/ovs-ofctl.8.in \
utilities/ovs-parse-leaks.8 \
utilities/ovs-pcap.1.in \
@@ -54,6 +57,8 @@ DISTCLEANFILES += \
utilities/ovs-check-dead-ifs \
utilities/ovs-controller.8 \
utilities/ovs-dpctl.8 \
+ utilities/ovs-l3ping \
+ utilities/ovs-l3ping.8 \
utilities/ovs-lib \
utilities/ovs-ofctl.8 \
utilities/ovs-parse-leaks \
@@ -76,6 +81,7 @@ man_MANS += \
utilities/ovs-benchmark.1 \
utilities/ovs-controller.8 \
utilities/ovs-dpctl.8 \
+ utilities/ovs-l3ping.8 \
utilities/ovs-ofctl.8 \
utilities/ovs-parse-leaks.8 \
utilities/ovs-pcap.1 \
diff --git a/utilities/ovs-l3ping.8.in b/utilities/ovs-l3ping.8.in
new file mode 100644
index 0000000..69da80f
--- /dev/null
+++ b/utilities/ovs-l3ping.8.in
@@ -0,0 +1,108 @@
+.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][:ControlPort]\fR
+.PP
+\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
+.
+.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\-\-fItunnel\-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)
\ No newline at end of file
diff --git a/utilities/ovs-l3ping.in b/utilities/ovs-l3ping.in
new file mode 100644
index 0000000..35e5fdd
--- /dev/null
+++ b/utilities/ovs-l3ping.in
@@ -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:
+ pass
+ 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()
diff --git a/utilities/ovs-test.in b/utilities/ovs-test.in
index b53302f..fb1f9ad 100644
--- a/utilities/ovs-test.in
+++ b/utilities/ovs-test.in
@@ -32,80 +32,18 @@ import twisted
import ovstest.args as args
import ovstest.rpcserver as rpcserver
+import ovstest.tests as tests
+import ovstest.util as util
DEFAULT_TEST_BRIDGE = "ovstestbr0"
DEFAULT_TEST_PORT = "ovstestport0"
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):
"""Print information about hosts that will do testing"""
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])
phys_iface = None
uname = server.uname()
@@ -140,210 +78,6 @@ def collect_information(node):
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__':
local_server = None
try:
@@ -360,10 +94,10 @@ if __name__ == '__main__':
# ovs-test server by looking at the first OuterIP. if it is a
# 127.0.0.1 then spawn local ovs-test server.
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
# 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])
node1 = (my_ip, node1[1], node1[2], node1[3])
@@ -372,7 +106,7 @@ if __name__ == '__main__':
bandwidth = ovs_args.targetBandwidth
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
vlan_tag = ovs_args.vlanTag
@@ -380,15 +114,17 @@ if __name__ == '__main__':
if direct is not None:
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:
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:
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:
pass
diff --git a/xenserver/openvswitch-xen.spec.in b/xenserver/openvswitch-xen.spec.in
index e051b31..3b9582e 100644
--- a/xenserver/openvswitch-xen.spec.in
+++ b/xenserver/openvswitch-xen.spec.in
@@ -125,13 +125,15 @@ rm \
$RPM_BUILD_ROOT/usr/bin/ovs-benchmark \
$RPM_BUILD_ROOT/usr/sbin/ovs-bugtool \
$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-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/man8/ovs-bugtool.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
--
1.7.9.5
More information about the dev
mailing list