[ovs-dev] [PATCH] ovs-test: A new tool that allows to diagnose connectivity and performance issues

Ansis Atteka aatteka at nicira.com
Tue Nov 15 01:16:02 UTC 2011


This patch contains MTU detection and also uses ethtool to retrieve test
interface driver information.

On Mon, Nov 14, 2011 at 5:14 PM, Ansis Atteka <aatteka at nicira.com> wrote:

> This tool will be a replacement for the current ovs-vlan-test
> utility. Besides from connectivity issues it will also be able
> to detect performance related issues in Open vSwitch setups.
> Currently it uses UDP and TCP protocols for stressing.
>
> Issue #6976
> ---
>  Makefile.am                       |    2 +-
>  NEWS                              |    6 +-
>  debian/automake.mk                |    3 +
>  debian/control                    |    9 ++
>  debian/openvswitch-test.dirs      |    1 +
>  debian/openvswitch-test.install   |    2 +
>  debian/openvswitch-test.manpages  |    1 +
>  debian/python-openvswitch.install |    2 +-
>  manpages.mk                       |   10 ++
>  python/automake.mk                |   56 ++++++++++
>  python/ovs/automake.mk            |   48 ---------
>  python/ovstest/__init__.py        |    1 +
>  python/ovstest/args.py            |  115 +++++++++++++++++++++
>  python/ovstest/rpcserver.py       |  203
> +++++++++++++++++++++++++++++++++++++
>  python/ovstest/tcp.py             |  139 +++++++++++++++++++++++++
>  python/ovstest/udp.py             |   90 ++++++++++++++++
>  python/ovstest/util.py            |   71 +++++++++++++
>  utilities/automake.mk             |    6 +
>  utilities/ovs-test.8.in           |  117 +++++++++++++++++++++
>  utilities/ovs-test.in             |  175 ++++++++++++++++++++++++++++++++
>  utilities/ovs-vlan-test.8.in      |    7 ++
>  21 files changed, 1013 insertions(+), 51 deletions(-)
>  create mode 100644 debian/openvswitch-test.dirs
>  create mode 100644 debian/openvswitch-test.install
>  create mode 100644 debian/openvswitch-test.manpages
>  create mode 100644 python/automake.mk
>  delete mode 100644 python/ovs/automake.mk
>  create mode 100644 python/ovstest/__init__.py
>  create mode 100644 python/ovstest/args.py
>  create mode 100644 python/ovstest/rpcserver.py
>  create mode 100644 python/ovstest/tcp.py
>  create mode 100644 python/ovstest/udp.py
>  create mode 100644 python/ovstest/util.py
>  create mode 100644 utilities/ovs-test.8.in
>  create mode 100644 utilities/ovs-test.in
>
> diff --git a/Makefile.am b/Makefile.am
> index 401d23a..c0a7ade 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -194,5 +194,5 @@ include vswitchd/automake.mk
>  include ovsdb/automake.mk
>  include rhel/automake.mk
>  include xenserver/automake.mk
> -include python/ovs/automake.mk
> +include python/automake.mk
>  include python/compat/automake.mk
> diff --git a/NEWS b/NEWS
> index 30714c5..01f4575 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -8,6 +8,10 @@ post-v1.3.0
>        - Added ability to modify TTL in IPv4.
>     - ovs-appctl:
>       - New "fdb/flush" command to flush bridge's MAC learning table.
> +    - ovs-test:
> +      - A new distributed testing tool that allows one to diagnose
> performance
> +        and connectivity issues. This tool currently is not included in
> RH or
> +        Xen packages.
>
>  v1.3.0 - xx xxx xxxx
>  ------------------------
> @@ -87,7 +91,7 @@ v1.2.0 - 03 Aug 2011
>       datapath/linux-2.6/compat-2.6 directories.
>     - Feature removals:
>       - Dropped support for "tun_id_from_cookie" OpenFlow extension.
> -           Please use the extensible match extensions instead.
> +           Please use the extensible match extensions instead.
>       - Removed the Maintenance_Point and Monitor tables in an effort
>         to simplify 802.1ag configuration.
>     - Performance and scalability improvements
> diff --git a/debian/automake.mk b/debian/automake.mk
> index d289830..755d727 100644
> --- a/debian/automake.mk
> +++ b/debian/automake.mk
> @@ -40,6 +40,9 @@ EXTRA_DIST += \
>        debian/openvswitch-switch.postinst \
>        debian/openvswitch-switch.postrm \
>        debian/openvswitch-switch.template \
> +       debian/openvswitch-test.dirs \
> +       debian/openvswitch-test.install \
> +       debian/openvswitch-test.manpages \
>        debian/ovsdbmonitor.install \
>        debian/ovsdbmonitor.manpages \
>        debian/ovs-monitor-ipsec \
> diff --git a/debian/control b/debian/control
> index 1f3387a..c350fac 100644
> --- a/debian/control
> +++ b/debian/control
> @@ -138,3 +138,12 @@ Description: Open vSwitch graphical monitoring tool
>  to "ovs-vsctl list <table>").
>  .
>  Open vSwitch is a full-featured software-based Ethernet switch.
> +
> +Package: openvswitch-test
> +Architecture: all
> +Depends: python-twisted-web, python-argparse
> +Description: Open vSwitch test package
> + This package contains utilities that are useful to diagnose
> + performance and connectivity issues in Open vSwitch setup.
> + .
> + Open vSwitch is a full-featured software-based Ethernet switch.
> diff --git a/debian/openvswitch-test.dirs b/debian/openvswitch-test.dirs
> new file mode 100644
> index 0000000..daaae31
> --- /dev/null
> +++ b/debian/openvswitch-test.dirs
> @@ -0,0 +1 @@
> +usr/share/pyshared/ovstest/
> diff --git a/debian/openvswitch-test.install
> b/debian/openvswitch-test.install
> new file mode 100644
> index 0000000..a152aff
> --- /dev/null
> +++ b/debian/openvswitch-test.install
> @@ -0,0 +1,2 @@
> +usr/share/openvswitch/python/ovstest usr/lib/python2.4/site-packages/
> +usr/bin/ovs-test
> diff --git a/debian/openvswitch-test.manpages
> b/debian/openvswitch-test.manpages
> new file mode 100644
> index 0000000..683c978
> --- /dev/null
> +++ b/debian/openvswitch-test.manpages
> @@ -0,0 +1 @@
> +_debian/utilities/ovs-test.8
> diff --git a/debian/python-openvswitch.install
> b/debian/python-openvswitch.install
> index ef84d2b..6779298 100644
> --- a/debian/python-openvswitch.install
> +++ b/debian/python-openvswitch.install
> @@ -1 +1 @@
> -usr/share/openvswitch/python/* usr/lib/python2.4/site-packages/
> +usr/share/openvswitch/python/ovs usr/lib/python2.4/site-packages/
> diff --git a/manpages.mk b/manpages.mk
> index c722d5d..48f2db5 100644
> --- a/manpages.mk
> +++ b/manpages.mk
> @@ -150,6 +150,16 @@ utilities/ovs-tcpundump.1.in:
>  lib/common-syn.man:
>  lib/common.man:
>
> +utilities/ovs-test.8: \
> +       utilities/ovs-test.8.in \
> +       lib/common-syn.man \
> +       lib/common.man \
> +       utilities/ovs-vlan-bugs.man
> +utilities/ovs-test.8.in:
> +lib/common-syn.man:
> +lib/common.man:
> +utilities/ovs-vlan-bugs.man:
> +
>  utilities/ovs-vlan-bug-workaround.8: \
>        utilities/ovs-vlan-bug-workaround.8.in \
>        lib/common.man \
> diff --git a/python/automake.mk b/python/automake.mk
> new file mode 100644
> index 0000000..089ef36
> --- /dev/null
> +++ b/python/automake.mk
> @@ -0,0 +1,56 @@
> +run_python = PYTHONPATH=$(top_srcdir)/python:$$PYTHON_PATH $(PYTHON)
> +
> +ovstest_pyfiles = \
> +       python/ovstest/__init__.py \
> +       python/ovstest/args.py \
> +       python/ovstest/rpcserver.py \
> +       python/ovstest/tcp.py \
> +       python/ovstest/udp.py \
> +       python/ovstest/util.py
> +
> +ovs_pyfiles = \
> +       python/ovs/__init__.py \
> +       python/ovs/daemon.py \
> +       python/ovs/db/__init__.py \
> +       python/ovs/db/data.py \
> +       python/ovs/db/error.py \
> +       python/ovs/db/idl.py \
> +       python/ovs/db/parser.py \
> +       python/ovs/db/schema.py \
> +       python/ovs/db/types.py \
> +       python/ovs/fatal_signal.py \
> +       python/ovs/json.py \
> +       python/ovs/jsonrpc.py \
> +       python/ovs/ovsuuid.py \
> +       python/ovs/poller.py \
> +       python/ovs/process.py \
> +       python/ovs/reconnect.py \
> +       python/ovs/socket_util.py \
> +       python/ovs/stream.py \
> +       python/ovs/timeval.py \
> +       python/ovs/vlog.py \
> +       python/ovs/util.py
> +EXTRA_DIST += $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles)
> +
> +if HAVE_PYTHON
> +nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles)
> +ovs-install-data-local:
> +       $(MKDIR_P) python/ovs
> +       (echo "import os" && \
> +        echo 'PKGDATADIR = os.environ.get("OVS_PKGDATADIR",
> """$(pkgdatadir)""")' && \
> +        echo 'RUNDIR = os.environ.get("OVS_RUNDIR", """@RUNDIR@""")' && \
> +        echo 'LOGDIR = os.environ.get("OVS_LOGDIR", """@LOGDIR@""")' && \
> +        echo 'BINDIR = os.environ.get("OVS_BINDIR", """$(bindir)""")') \
> +               > python/ovs/dirs.py.tmp
> +       $(MKDIR_P) $(DESTDIR)$(pkgdatadir)/python/ovs
> +       $(INSTALL_DATA) python/ovs/dirs.py.tmp
> $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py
> +       rm python/ovs/dirs.py.tmp
> +else
> +ovs-install-data-local:
> +       @:
> +endif
> +install-data-local: ovs-install-data-local
> +
> +UNINSTALL_LOCAL += ovs-uninstall-local
> +ovs-uninstall-local:
> +       rm -f $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py
> diff --git a/python/ovs/automake.mk b/python/ovs/automake.mk
> deleted file mode 100644
> index 2247328..0000000
> --- a/python/ovs/automake.mk
> +++ /dev/null
> @@ -1,48 +0,0 @@
> -run_python = PYTHONPATH=$(top_srcdir)/python:$$PYTHON_PATH $(PYTHON)
> -
> -ovs_pyfiles = \
> -       python/ovs/__init__.py \
> -       python/ovs/daemon.py \
> -       python/ovs/db/__init__.py \
> -       python/ovs/db/data.py \
> -       python/ovs/db/error.py \
> -       python/ovs/db/idl.py \
> -       python/ovs/db/parser.py \
> -       python/ovs/db/schema.py \
> -       python/ovs/db/types.py \
> -       python/ovs/fatal_signal.py \
> -       python/ovs/json.py \
> -       python/ovs/jsonrpc.py \
> -       python/ovs/ovsuuid.py \
> -       python/ovs/poller.py \
> -       python/ovs/process.py \
> -       python/ovs/reconnect.py \
> -       python/ovs/socket_util.py \
> -       python/ovs/stream.py \
> -       python/ovs/timeval.py \
> -       python/ovs/vlog.py \
> -       python/ovs/util.py
> -EXTRA_DIST += $(ovs_pyfiles) python/ovs/dirs.py
> -
> -if HAVE_PYTHON
> -nobase_pkgdata_DATA = $(ovs_pyfiles)
> -ovs-install-data-local:
> -       $(MKDIR_P) python/ovs
> -       (echo "import os" && \
> -        echo 'PKGDATADIR = os.environ.get("OVS_PKGDATADIR",
> """$(pkgdatadir)""")' && \
> -        echo 'RUNDIR = os.environ.get("OVS_RUNDIR", """@RUNDIR@""")' && \
> -        echo 'LOGDIR = os.environ.get("OVS_LOGDIR", """@LOGDIR@""")' && \
> -        echo 'BINDIR = os.environ.get("OVS_BINDIR", """$(bindir)""")') \
> -               > python/ovs/dirs.py.tmp
> -       $(MKDIR_P) $(DESTDIR)$(pkgdatadir)/python/ovs
> -       $(INSTALL_DATA) python/ovs/dirs.py.tmp
> $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py
> -       rm python/ovs/dirs.py.tmp
> -else
> -ovs-install-data-local:
> -       @:
> -endif
> -install-data-local: ovs-install-data-local
> -
> -UNINSTALL_LOCAL += ovs-uninstall-local
> -ovs-uninstall-local:
> -       rm -f $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py
> diff --git a/python/ovstest/__init__.py b/python/ovstest/__init__.py
> new file mode 100644
> index 0000000..218d892
> --- /dev/null
> +++ b/python/ovstest/__init__.py
> @@ -0,0 +1 @@
> +# This file intentionally left blank.
> diff --git a/python/ovstest/args.py b/python/ovstest/args.py
> new file mode 100644
> index 0000000..d6b4756
> --- /dev/null
> +++ b/python/ovstest/args.py
> @@ -0,0 +1,115 @@
> +# Copyright (c) 2011 Nicira Networks
> +#
> +# 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.
> +
> +"""
> +ovsargs provide argument parsing for ovs-test utility
> +"""
> +
> +import argparse
> +import socket
> +import re
> +
> +
> +def ip(string):
> +    """Verifies if string is a valid IP address"""
> +    try:
> +        socket.inet_aton(string)
> +    except socket.error:
> +        raise argparse.ArgumentTypeError("Not a valid IPv4 address")
> +    return string
> +
> +
> +def port(string):
> +    """Convert a string into a Port (integer)"""
> +    try:
> +        port_number = int(string)
> +        if port_number < 1 or port_number > 65535:
> +            raise argparse.ArgumentTypeError("Port is out of range")
> +    except ValueError:
> +        raise argparse.ArgumentTypeError("Port is not an integer")
> +    return port_number
> +
> +
> +def ip_optional_port(string, default_port):
> +    """Convert a string into IP and Port pair. If port was absent then use
> +    default_port as the port"""
> +    value = string.split(':')
> +    if len(value) == 1:
> +        return (ip(value[0]), default_port)
> +    elif len(value) == 2:
> +        return (ip(value[0]), port(value[1]))
> +    else:
> +        raise argparse.ArgumentTypeError("IP address from the optional
> Port "
> +                                         "must be colon-separated")
> +
> +
> +
> +def server_endpoint(string):
> +    """Converts a string in ControlIP[:ControlPort][,TestIP[:TestPort]]
> format
> +    into a 4-tuple, where:
> +    1. First element is ControlIP
> +    2. Second element is ControlPort (if omitted will use default value
> 15531)
> +    3  Third element is TestIP (if omitted will be the same as ControlIP)
> +    4. Fourth element is TestPort (if omitted will use default value
> 15532)"""
> +    value = string.split(',')
> +    if len(value) == 1: #  TestIP and TestPort are not present
> +        ret = ip_optional_port(value[0], 15531)
> +        return (ret[0], ret[1], ret[0], 15532)
> +    elif len(value) == 2:
> +        ret1 = ip_optional_port(value[0], 15531)
> +        ret2 = ip_optional_port(value[1], 15532)
> +        return (ret1[0], ret1[1], ret2[0], ret2[1])
> +    else:
> +        raise argparse.ArgumentTypeError("ControlIP:ControlPort and
> TestIP:"
> +                                         "TestPort must be comma "
> +                                         "separated")
> +
> +
> +def bandwidth(string):
> +    """Convert a string (given in bits/second with optional magnitude for
> +    units) into a long (bytes/second)"""
> +    if re.match("^[1-9][0-9]*[MK]?$", string) == None:
> +        raise argparse.ArgumentTypeError("Not a valid target bandwidth")
> +    bwidth = string.replace("M", "000000")
> +    bwidth = bwidth.replace("K", "000")
> +    return long(bwidth) / 8 #  Convert from bits to bytes
> +
> +
> +def ovs_initialize_args():
> +    """Initialize args for ovstest utility"""
> +    parser = argparse.ArgumentParser(description = 'Test ovs
> connectivity')
> +    parser.add_argument('-v', '--version', action = 'version',
> +                version = 'ovs-test (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.')
> +    group = parser.add_mutually_exclusive_group(required = True)
> +    group.add_argument("-s", "--server", action = "store", dest = "port",
> +                type = port,
> +                help = 'run in server mode and wait client to connect to
> this '
> +                'port')
> +    group.add_argument('-c', "--client", action = "store", nargs = 2,
> +                dest = "servers", type = server_endpoint,
> +                metavar = ("SERVER1", "SERVER2"),
> +                help = 'run in client mode and do tests between these '
> +                'two servers. Each server must be specified in following '
> +                'format - ControlIP[:ControlPort][,TestIP[:TestPort]]. If
> '
> +                'TestIP is omitted then ovs-test server will also use the
> '
> +                'ControlIP for testing purposes. ControlPort is TCP port '
> +                'where server will listen for incoming XML/RPC control '
> +                'connections to schedule tests (by default 15531).
> TestPort '
> +                'is port which will be used by server to send test
> traffic '
> +                '(by default 15532)')
> +    return parser.parse_args()
> diff --git a/python/ovstest/rpcserver.py b/python/ovstest/rpcserver.py
> new file mode 100644
> index 0000000..41d2569
> --- /dev/null
> +++ b/python/ovstest/rpcserver.py
> @@ -0,0 +1,203 @@
> +# Copyright (c) 2011 Nicira Networks
> +#
> +# 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.
> +
> +"""
> +rpcserver is an XML RPC server that allows RPC client to initiate tests
> +"""
> +
> +from twisted.internet import reactor
> +from twisted.web import xmlrpc, server
> +from twisted.internet.error import CannotListenError
> +import udp
> +import tcp
> +import args
> +import util
> +
> +
> +class TestArena(xmlrpc.XMLRPC):
> +    """
> +    This class contains all the functions that ovstest will call
> +    remotely. The caller is responsible to use designated handleIds
> +    for designated methods (e.g. do not mix UDP and TCP handles).
> +    """
> +
> +    def __init__(self):
> +        xmlrpc.XMLRPC.__init__(self)
> +        self.handle_id = 1
> +        self.handle_map = {}
> +
> +    def __acquire_handle(self, value):
> +        """
> +        Allocates new handle and assigns value object to it
> +        """
> +        handle = self.handle_id
> +        self.handle_map[handle] = value
> +        self.handle_id += 1
> +        return handle
> +
> +    def __get_handle_resources(self, handle):
> +        """
> +        Return resources that were assigned to handle
> +        """
> +        return self.handle_map[handle]
> +
> +    def __delete_handle(self, handle):
> +        """
> +        Releases handle from handle_map
> +        """
> +        del self.handle_map[handle]
> +
> +
> +    def xmlrpc_create_udp_listener(self, port):
> +        """
> +        Creates a UDP listener that will receive packets from UDP sender
> +        """
> +        try:
> +            listener = udp.UdpListener()
> +            reactor.listenUDP(port, listener)
> +            handle_id = self.__acquire_handle(listener)
> +        except CannotListenError:
> +            return -1
> +        return handle_id
> +
> +    def xmlrpc_create_udp_sender(self, host, count, size, duration):
> +        """
> +        Send UDP datagrams to UDP listener
> +        """
> +        sender = udp.UdpSender(tuple(host), count, size, duration)
> +        reactor.listenUDP(0, sender)
> +        handle_id = self.__acquire_handle(sender)
> +        return handle_id
> +
> +    def xmlrpc_get_udp_listener_results(self, handle):
> +        """
> +        Returns number of datagrams that were received
> +        """
> +        listener = self.__get_handle_resources(handle)
> +        return listener.getResults()
> +
> +    def xmlrpc_get_udp_sender_results(self, handle):
> +        """
> +        Returns number of datagrams that were sent
> +        """
> +        sender = self.__get_handle_resources(handle)
> +        return sender.getResults()
> +
> +    def xmlrpc_close_udp_listener(self, handle):
> +        """
> +        Releases UdpListener and all its resources
> +        """
> +        listener = self.__get_handle_resources(handle)
> +        listener.transport.stopListening()
> +        self.__delete_handle(handle)
> +        return 0
> +
> +    def xmlrpc_close_udp_sender(self, handle):
> +        """
> +        Releases UdpSender and all its resources
> +        """
> +        sender = self.__get_handle_resources(handle)
> +        sender.transport.stopListening()
> +        self.__delete_handle(handle)
> +        return 0
> +
> +    def xmlrpc_create_tcp_listener(self, port):
> +        """
> +        Creates a TcpListener that will accept connection from TcpSender
> +        """
> +        try:
> +            listener = tcp.TcpListenerFactory()
> +            port = reactor.listenTCP(port, listener)
> +            handle_id = self.__acquire_handle((listener, port))
> +            return handle_id
> +        except CannotListenError:
> +            return -1
> +
> +    def xmlrpc_create_tcp_sender(self, his_ip, his_port, duration):
> +        """
> +        Creates a TcpSender that will connect to TcpListener
> +        """
> +        sender = tcp.TcpSenderFactory(duration)
> +        connector = reactor.connectTCP(his_ip, his_port, sender)
> +        handle_id = self.__acquire_handle((sender, connector))
> +        return handle_id
> +
> +    def xmlrpc_get_tcp_listener_results(self, handle):
> +        """
> +        Returns number of bytes received
> +        """
> +        (listener, _) = self.__get_handle_resources(handle)
> +        return listener.getResults()
> +
> +    def xmlrpc_get_tcp_sender_results(self, handle):
> +        """
> +        Returns number of bytes sent
> +        """
> +        (sender, _) = self.__get_handle_resources(handle)
> +        return sender.getResults()
> +
> +    def xmlrpc_close_tcp_listener(self, handle):
> +        """
> +        Releases TcpListener and all its resources
> +        """
> +        try:
> +            (_, port) = self.__get_handle_resources(handle)
> +            port.loseConnection()
> +            self.__delete_handle(handle)
> +        except exceptions.KeyError:
> +            return -1
> +        return 0
> +
> +    def xmlrpc_close_tcp_sender(self, handle):
> +        """
> +        Releases TcpSender and all its resources
> +        """
> +        try:
> +            (_, connector) = self.__get_handle_resources(handle)
> +            connector.disconnect()
> +            self.__delete_handle(handle)
> +        except exceptions.KeyError:
> +            return -1
> +        return 0
> +
> +
> +    def xmlrpc_get_interface(self, address):
> +        """
> +        Finds first interface that has given address
> +        """
> +        return util.get_interface(address)
> +
> +    def xmlrpc_get_interface_mtu(self, iface):
> +        """
> +        Returns MTU of the given interface
> +        """
> +        return util.get_interface_mtu(iface)
> +
> +    def xmlrpc_uname(self):
> +        """
> +        Return information about running kernel
> +        """
> +        return util.uname()
> +
> +    def xmlrpc_get_driver(self, iface):
> +        """
> +        Returns driver version
> +        """
> +        return util.get_driver(iface)
> +
> +
> +def start_rpc_server(port):
> +    RPC_SERVER = TestArena()
> +    reactor.listenTCP(port, server.Site(RPC_SERVER))
> +    reactor.run()
> diff --git a/python/ovstest/tcp.py b/python/ovstest/tcp.py
> new file mode 100644
> index 0000000..33dc719
> --- /dev/null
> +++ b/python/ovstest/tcp.py
> @@ -0,0 +1,139 @@
> +# Copyright (c) 2011 Nicira Networks
> +#
> +# 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.
> +
> +"""
> +tcp module contains listener and sender classes for TCP protocol
> +"""
> +
> +from twisted.internet.protocol import Factory, ClientFactory, Protocol
> +from twisted.internet import interfaces
> +from zope.interface import implements
> +import time
> +
> +
> +class TcpListenerConnection(Protocol):
> +    """
> +    This per-connection class is instantiated each time sender connects
> +    """
> +    def __init__(self):
> +        self.stats = 0
> +
> +    def connectionMade(self):
> +        print "Started TCP Listener connection"
> +
> +    def dataReceived(self, data):
> +        self.stats += len(data)
> +
> +    def connectionLost(self, reason):
> +        print "Stopped TCP Listener connection"
> +        self.factory.stats += self.stats
> +
> +
> +class TcpListenerFactory(Factory):
> +    """
> +    This per-listening socket class is used to
> +    instantiate TcpListenerConnections
> +    """
> +    protocol = TcpListenerConnection
> +
> +    def __init__(self):
> +        self.stats = 0
> +
> +    def startFactory(self):
> +        print "Starting TCP listener factory"
> +
> +    def stopFactory(self):
> +        print "Stopping TCP listener factory"
> +
> +    def getResults(self):
> +        """ returns the number of bytes received as string"""
> +        #XML RPC does not support 64bit int (
> http://bugs.python.org/issue2985)
> +        #so we have to convert the amount of bytes into a string
> +        return str(self.stats)
> +
> +
> +class Producer(object):
> +    implements(interfaces.IPushProducer)
> +    """
> +    This producer class generates infinite byte stream for a specified
> time
> +    duration
> +    """
> +    def __init__(self, proto, duration):
> +        self.proto = proto
> +        self.start = time.time()
> +        self.produced = 0
> +        self.paused = False
> +        self.data = "X" * 65535
> +        self.duration = duration
> +
> +    def pauseProducing(self):
> +        """This function is called whenever write() to socket would
> block"""
> +        self.paused = True
> +
> +    def resumeProducing(self):
> +        """This function is called whenever socket becomes writable"""
> +        self.paused = False
> +        current = time.time()
> +        while (not self.paused) and (current < self.start +
> self.duration):
> +            self.proto.transport.write(self.data)
> +            self.produced += len(self.data)
> +            current = time.time()
> +        if current >= self.start + self.duration:
> +            self.proto.factory.stats += self.produced
> +            self.proto.transport.unregisterProducer()
> +            self.proto.transport.loseConnection()
> +
> +    def stopProducing(self):
> +        pass
> +
> +
> +class TcpSenderConnection(Protocol):
> +    """
> +    TCP connection instance class that sends all traffic at full speed.
> +    """
> +
> +    def connectionMade(self):
> +        print "Started TCP sender connection"
> +        producer = Producer(self, self.factory.duration)
> +        self.transport.registerProducer(producer, True)
> +        producer.resumeProducing()
> +
> +    def dataReceived(self, data):
> +        print "Sender received data!", data
> +        self.transport.loseConnection()
> +
> +    def connectionLost(self, reason):
> +        print "Stopped TCP sender connection"
> +
> +
> +class TcpSenderFactory(ClientFactory):
> +    """
> +    This factory is responsible to instantiate TcpSenderConnection classes
> +    each time sender initiates connection
> +    """
> +    protocol = TcpSenderConnection
> +
> +    def __init__(self, duration):
> +        self.duration = duration
> +        self.stats = 0
> +
> +    def startFactory(self):
> +        print "Starting TCP sender factory"
> +
> +    def stopFactory(self):
> +        print "Stopping TCP sender factory"
> +
> +    def getResults(self):
> +        """Returns amount of bytes sent to the Listener (as a string)"""
> +        return str(self.stats)
> diff --git a/python/ovstest/udp.py b/python/ovstest/udp.py
> new file mode 100644
> index 0000000..e09569d
> --- /dev/null
> +++ b/python/ovstest/udp.py
> @@ -0,0 +1,90 @@
> +# Copyright (c) 2011 Nicira Networks
> +#
> +# 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.
> +
> +"""
> +ovsudp contains listener and sender classes for UDP protocol
> +"""
> +
> +from twisted.internet.protocol import DatagramProtocol
> +from twisted.internet.task import LoopingCall
> +import array, struct, time
> +
> +
> +class UdpListener(DatagramProtocol):
> +    """
> +    Class that will listen for incoming UDP packets
> +    """
> +    def __init__(self):
> +        self.stats = []
> +
> +    def startProtocol(self):
> +        print "Starting UDP listener"
> +
> +    def stopProtocol(self):
> +        print "Stopping UDP listener"
> +
> +    def datagramReceived(self, data, (_1, _2)):
> +        """This function is called each time datagram is received"""
> +        try:
> +            self.stats.append(struct.unpack_from("Q", data, 0))
> +        except struct.error:
> +            pass #ignore packets that are less than 8 bytes of size
> +
> +    def getResults(self):
> +        """Returns number of packets that were actually received"""
> +        return len(self.stats)
> +
> +
> +class UdpSender(DatagramProtocol):
> +    """
> +    Class that will send UDP packets to UDP Listener
> +    """
> +    def __init__(self, host, count, size, duration):
> +        #LoopingCall does not know whether UDP socket is actually writable
> +        self.looper = None
> +        self.host = host
> +        self.count = count
> +        self.duration = duration
> +        self.start = time.time()
> +        self.sent = 0
> +        self.data = array.array('c', 'X' * size)
> +
> +    def startProtocol(self):
> +        print "Starting UDP sender"
> +        self.looper = LoopingCall(self.sendData)
> +        period = self.duration / float(self.count)
> +        self.looper.start(period , now = False)
> +
> +    def stopProtocol(self):
> +        print "Stopping UDP sender"
> +        if (self.looper is not None):
> +            self.looper.stop()
> +            self.looper = None
> +
> +    def datagramReceived(self, data, (host, port)):
> +        pass
> +
> +    def sendData(self):
> +        """This function is called from LoopingCall"""
> +        if self.start + self.duration < time.time():
> +            self.looper.stop()
> +            self.looper = None
> +
> +        self.sent += 1
> +        struct.pack_into('Q', self.data, 0, self.sent)
> +        self.transport.write(self.data, self.host)
> +
> +    def getResults(self):
> +        """Returns number of packets that were sent"""
> +        return self.sent
> diff --git a/python/ovstest/util.py b/python/ovstest/util.py
> new file mode 100644
> index 0000000..8c7c7a3
> --- /dev/null
> +++ b/python/ovstest/util.py
> @@ -0,0 +1,71 @@
> +# Copyright (c) 2011 Nicira Networks
> +#
> +# 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.
> +
> +"""
> +util module contains some helper function
> +"""
> +import socket, struct, fcntl, array, os, subprocess, exceptions
> +
> +def str_ip(ip):
> +    (x1, x2, x3, x4) = struct.unpack("BBBB", ip)
> +    return ("%u.%u.%u.%u") % (x1, x2, x3, x4)
> +
> +def get_interface_mtu(iface):
> +    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
> +    indata = iface + ('\0' * (32 - len(iface)))
> +    try:
> +        outdata = fcntl.ioctl(s.fileno(), 0x8921, indata) #
>  socket.SIOCGIFMTU
> +        mtu = struct.unpack("16si12x", outdata)[1]
> +    except:
> +        return 0
> +
> +    return mtu
> +
> +def get_interface(address):
> +    """
> +    Finds first interface that has given address
> +    """
> +    bytes = 256 * 32
> +    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
> +    names = array.array('B', '\0' * bytes)
> +    outbytes = struct.unpack('iL', fcntl.ioctl(
> +        s.fileno(),
> +        0x8912, # SIOCGIFCONF
> +        struct.pack('iL', bytes, names.buffer_info()[0])
> +    ))[0]
> +    namestr = names.tostring()
> +
> +    for i in range(0, outbytes, 40):
> +        name = namestr[i:i + 16].split('\0', 1)[0]
> +        if address == str_ip(namestr[i + 20:i + 24]):
> +            return name
> +    return "" #  did not find interface we were looking for
> +
> +def uname():
> +    os_info = os.uname()
> +    return os_info[2] #return only the kernel version number
> +
> +def get_driver(iface):
> +    try:
> +        p = subprocess.Popen(
> +            ["ethtool", "-i", iface],
> +            stdin = subprocess.PIPE,
> +            stdout = subprocess.PIPE,
> +            stderr = subprocess.PIPE)
> +        out, err = p.communicate()
> +        lines = out.split("\n")
> +        driver = "%s(%s)" % (lines[0], lines[1]) #driver name + driver
> version
> +    except exceptions.OSError:
> +        driver = ""
> +    return driver
> diff --git a/utilities/automake.mk b/utilities/automake.mk
> index 420d5fc..df94dd1 100644
> --- a/utilities/automake.mk
> +++ b/utilities/automake.mk
> @@ -9,6 +9,7 @@ if HAVE_PYTHON
>  bin_SCRIPTS += \
>        utilities/ovs-pcap \
>        utilities/ovs-tcpundump \
> +       utilities/ovs-test \
>        utilities/ovs-vlan-test
>  endif
>  noinst_SCRIPTS += utilities/ovs-pki-cgi
> @@ -23,6 +24,7 @@ EXTRA_DIST += \
>        utilities/ovs-pki.in \
>        utilities/ovs-save \
>        utilities/ovs-tcpundump.in \
> +       utilities/ovs-test.in \
>        utilities/ovs-vlan-test.in
>  MAN_ROOTS += \
>        utilities/ovs-appctl.8.in \
> @@ -36,6 +38,7 @@ MAN_ROOTS += \
>        utilities/ovs-pki.8.in \
>        utilities/ovs-tcpundump.1.in \
>        utilities/ovs-vlan-bug-workaround.8.in \
> +       utilities/ovs-test.8.in \
>        utilities/ovs-vlan-test.8.in \
>        utilities/ovs-vsctl.8.in
>  MAN_FRAGMENTS += utilities/ovs-vlan-bugs.man
> @@ -55,6 +58,8 @@ DISTCLEANFILES += \
>        utilities/ovs-pki.8 \
>        utilities/ovs-tcpundump \
>        utilities/ovs-tcpundump.1 \
> +       utilities/ovs-test \
> +       utilities/ovs-test.8 \
>        utilities/ovs-vlan-test \
>        utilities/ovs-vlan-test.8 \
>        utilities/ovs-vlan-bug-workaround.8 \
> @@ -71,6 +76,7 @@ man_MANS += \
>        utilities/ovs-pki.8 \
>        utilities/ovs-tcpundump.1 \
>        utilities/ovs-vlan-bug-workaround.8 \
> +       utilities/ovs-test.8 \
>        utilities/ovs-vlan-test.8 \
>        utilities/ovs-vsctl.8
>  dist_man_MANS += utilities/ovs-ctl.8
> diff --git a/utilities/ovs-test.8.in b/utilities/ovs-test.8.in
> new file mode 100644
> index 0000000..afc8221
> --- /dev/null
> +++ b/utilities/ovs-test.8.in
> @@ -0,0 +1,117 @@
> +.TH ovs\-test 1 "October 2011" "Open vSwitch" "Open vSwitch Manual"
> +.
> +.SH NAME
> +\fBovs\-test\fR \- check Linux drivers for performance and vlan problems
> +.
> +.SH SYNOPSIS
> +\fBovs\-test\fR \fB\-s\fR \fIport\fR
> +.PP
> +\fBovs\-test\fR \fB\-c\fR \fIserver1\fR
> +\fIserver2\fR [\fB\-b\fR \fIbandwidth\fR]
> +.so lib/common-syn.man
> +.
> +.SH DESCRIPTION
> +The \fBovs\-test\fR program may be used to check for problems sending
> +802.1Q traffic that Open vSwitch may uncover. These problems can
> +occur when Open vSwitch is used to send 802.1Q traffic through physical
> +interfaces running certain drivers of certain Linux kernel versions. To
> run a
> +test, configure Open vSwitch to tag traffic originating from
> \fIserver1\fR and
> +forward it to the \fIserver2\fR. On both servers run \fBovs\-test\fR
> +in server mode. Then, on any other host, run the \fBovs\-test\fR in client
> +mode. The client will connect to both \fBovs\-test\fR servers and schedule
> +tests between them. \fBovs\-test\fR will perform UDP and TCP tests.
> +.PP
> +UDP tests can report packet loss and achieved bandwidth, because UDP flow
> +control is done inside \fBovs\-test\fR. It is also possible to specify
> target
> +bandwidth for UDP. By default it is 1Mbit/s.
> +.PP
> +TCP tests report only achieved bandwidth, because kernel TCP stack
> +takes care of flow control and packet loss. TCP tests are essential to
> detect
> +potential TSO related VLAN issues.
> +.PP
> +To determine whether Open vSwitch is encountering any 802.1Q related
> problems,
> +the user must compare packet loss and achieved bandwidth in a setup where
> +traffic is being tagged against one where it is not. If in the tagged
> setup
> +both servers are unable to communicate or the achieved bandwidth is lower,
> +then, most likely, Open vSwitch has encountered a pre-existing kernel or
> +driver bug.
> +.PP
> +Some examples of the types of problems that may be encountered are:
> +.so utilities/ovs-vlan-bugs.man
> +.
> +.SS "Client Mode"
> +An \fBovs\-test\fR client will connect to two \fBovs\-test\fR servers and
> +will ask them to exchange traffic.
> +.
> +.SS "Server Mode"
> +To conduct tests, two \fBovs\-test\fR servers must be running on two
> different
> +hosts where client can connect. The actual test traffic is exchanged only
> +between both \fBovs\-test\fR server test IP addresses. It is recommended
> that
> +both servers have their test IP addresses in the same subnet, otherwise
> one
> +will need to change routing so that the test traffic actually goes
> through the
> +interface that he originally intended to test.
> +.
> +.SH OPTIONS
> +.
> +.TP
> +\fB\-s\fR, \fB\-\-server\fR \fIport\fR
> +Run in server mode and wait for a client to establish XML RPC Control
> +Connection on TCP \fIport\fR. It is recommended to have ethtool installed
> on
> +the server so that it could retrieve information about NIC driver.
> +.TP
> +\fB\-c\fR, \fB\-\-client\fR \fIserver1\fR \fIserver2\fR
> +Run in client mode and schedule tests between \fIserver1\fR and
> \fIserver2\fR,
> +where each \fIserver\fR must be given in following format -
> +ControlIP[:ControlPort][,TestIP[:TestPort]]. If TestIP is omitted then
> +ovs-test server will use the ControlIP for testing purposes. ControlPort
> is
> +TCP port where server will listen for incoming XML/RPC control
> +connections to schedule tests (by default it is 15531). TestPort
> +is port which will be used by server to listen for test traffic
> +(by default it is 15532).
> +.TP
> +\fB\-b\fR, \fB\-\-bandwidth\fR \fIbandwidth\fR
> +Target bandwidth for UDP tests. The \fIbandwidth\fR must be given in bits
> per
> +second. It is possible to use postfix M or K to alter the target bandwidth
> +magnitude.
> +.
> +.so lib/common.man
> +.SH EXAMPLES
> +.PP
> +Set up a bridge which forwards traffic originating from \fB1.2.3.4\fR out
> +\fBeth1\fR with VLAN tag 10.
> +.IP
> +.B ovs\-vsctl \-\- add\-br vlan\-br \(rs
> +.IP
> +.B \-\- add\-port vlan\-br eth1 \(rs
> +.IP
> +.B \-\- add\-port vlan\-br vlan\-br\-tag tag=10 \(rs
> +.IP
> +.B \-\- set Interface vlan\-br\-tag type=internal
> +.IP
> +.B ifconfig vlan\-br\-tag up 1.2.3.4
> +.
> +.PP
> +On two different hosts start \fBovs\-test\fR in server mode and tell them
> to
> +listen on port 15531 for incoming client control connections:
> +.IP
> +.B 1.2.3.4: ovs\-test \-s 15531
> +.IP
> +.B 1.2.3.5: ovs\-test \-s 15531
> +.
> +.PP
> +On any other host start \fBovs\-test\fR in client mode and ask it to
> connect
> +to those two servers - one at 1.2.3.4 and another at 1.2.3.5 (by default
> +client will use TCP port 15531 to establish control channel).
> +.IP
> +.B ovs\-test -c 1.2.3.4 1.2.3.5
> +.
> +.TP
> +
> +.SH SEE ALSO
> +.
> +.BR ovs\-vswitchd (8),
> +.BR ovs\-ofctl (8),
> +.BR ovs\-vsctl (8),
> +.BR ovs\-vlan\-test (8),
> +.BR ethtool (8),
> +.BR uname (1)
> diff --git a/utilities/ovs-test.in b/utilities/ovs-test.in
> new file mode 100644
> index 0000000..3210196
> --- /dev/null
> +++ b/utilities/ovs-test.in
> @@ -0,0 +1,175 @@
> +#! @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 test utility that allows to do tests between remote hosts
> +"""
> +
> +import xmlrpclib
> +import time
> +import socket
> +import math
> +from ovstest import args, rpcserver
> +
> +
> +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])
> +    server1 = xmlrpclib.Server("http://%s:%u/" % (node[0], node[1]))
> +    interface_name = server1.get_interface(node[2])
> +    uname = server1.uname()
> +    mtu = 1500
> +
> +    if interface_name == "":
> +        print ("Could not find interface that has %s IP address."
> +               "Make sure that you specified correct Test IP." %
> (node[2]))
> +    else:
> +        mtu = server1.get_interface_mtu(interface_name)
> +        driver = server1.get_driver(interface_name)
> +        print "Will be using %s(%s) with MTU %u" % (interface_name,
> node[2],
> +                                                    mtu)
> +        if driver == "":
> +            print "Install ethtool on this host to get NIC driver
> information"
> +        else:
> +            print "On this host %s has %s." % (interface_name, driver)
> +
> +    if uname == "":
> +        print "Unable to retrieve kernel information. Is this Linux?"
> +    else:
> +        print "Running kernel %s." % uname
> +    print "\n"
> +    return mtu
> +
> +
> +def do_udp_tests(receiver, sender, tbwidth, duration, sender_mtu):
> +    """Schedule UDP tests between receiver and sender"""
> +    server1 = xmlrpclib.Server("http://%s:%u/" % (receiver[0],
> receiver[1]))
> +    server2 = xmlrpclib.Server("http://%s:%u/" % (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 [8, sender_mtu - 28, sender_mtu]:
> +        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(
> +                                            (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 = xmlrpclib.Server("http://%s:%u/" % (receiver[0],
> receiver[1]))
> +    server2 = xmlrpclib.Server("http://%s:%u/" % (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(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"
> +
> +
> +if __name__ == '__main__':
> +    try:
> +        ovs_args = args.ovs_initialize_args()
> +
> +        if ovs_args.port is not None: #  Start in server mode
> +            print "Starting RPC server"
> +            rpcserver.start_rpc_server(ovs_args.port)
> +        elif ovs_args.servers is not None: #  Run in client mode
> +            node1 = ovs_args.servers[0]
> +            node2 = ovs_args.servers[1]
> +            bandwidth = ovs_args.targetBandwidth
> +
> +            mtu_node1 = collect_information(node1)
> +            mtu_node2 = collect_information(node2)
> +
> +            do_udp_tests(node1, node2, bandwidth, 5, mtu_node1)
> +            do_udp_tests(node2, node1, bandwidth, 5, mtu_node2)
> +            do_tcp_tests(node1, node2, 5)
> +            do_tcp_tests(node2, node1, 5)
> +    except KeyboardInterrupt:
> +        pass
> +    except socket.error:
> +        print "Couldn't establish XMLRPC control channel"
> diff --git a/utilities/ovs-vlan-test.8.in b/utilities/ovs-vlan-test.8.in
> index 602d785..549dcad 100644
> --- a/utilities/ovs-vlan-test.8.in
> +++ b/utilities/ovs-vlan-test.8.in
> @@ -8,6 +8,12 @@
>  .so lib/common-syn.man
>  .
>  .SH DESCRIPTION
> +The \fBovs\-vlan\-test\fR utility has some limitations, for example, it
> does
> +not use TCP in its tests. Also it does not take into account MTU to detect
> +potential edge cases. To overcome those limitations a new tool was
> +developed \- \fBovs\-test\fR. \fBovs\-test\fR is currently supported only
> +on Debian so, if possible try to use that on instead of
> \fBovs\-vlan\-test\fR.
> +.PP
>  The \fBovs\-vlan\-test\fR program may be used to check for problems
> sending
>  802.1Q traffic which may occur when running Open vSwitch. These problems
> can
>  occur when Open vSwitch is used to send 802.1Q traffic through physical
> @@ -82,5 +88,6 @@ Run an \fBovs\-vlan\-test\fR client with a control
> server located at
>  .BR ovs\-vswitchd (8),
>  .BR ovs\-ofctl (8),
>  .BR ovs\-vsctl (8),
> +.BR ovs\-test (8),
>  .BR ethtool (8),
>  .BR uname (1)
> --
> 1.7.4.1
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openvswitch.org/pipermail/ovs-dev/attachments/20111114/ea7bd22f/attachment-0003.html>


More information about the dev mailing list