]> git.proxmox.com Git - ovs.git/commitdiff
tests: Avoid race conditions, by letting the kernel choose ports to bind.
authorBen Pfaff <blp@nicira.com>
Wed, 3 Apr 2013 18:30:18 +0000 (13:30 -0500)
committerBen Pfaff <blp@nicira.com>
Thu, 18 Apr 2013 23:43:15 +0000 (16:43 -0700)
An occasionally occurring problem with "make check", especially when
parallel tests are enabled, is that multiple tests try to bind the same
TCP port and, of course, fail.  This happens because the code to select
a TCP port to bind just generates random numbers until it finds a port that
is not currently in use and uses the first one, which is of course prone
to races.

This commit changes the tests to let the kernel directly choose an
available port, which should avoid this type of failure.

Also, some of the tests that generated a random free TCP port actually
used the port number to bind a UDP socket, which of course doesn't work
well.  This commit fixes that problem too as a side effect.

Signed-off-by: Ben Pfaff <blp@nicira.com>
tests/automake.mk
tests/choose-port.pl [deleted file]
tests/ofproto-dpif.at
tests/ofproto-macros.at
tests/ovsdb-idl.at
tests/ovsdb-server.at

index b9dbf3bd7d0e853cb87d53bdfc30a630d4879114..4442eb50955e4c0e882a76c746750002b4b43663 100644 (file)
@@ -301,8 +301,6 @@ noinst_PROGRAMS += tests/test-byte-order
 tests_test_byte_order_SOURCES = tests/test-byte-order.c
 tests_test_byte_order_LDADD = lib/libopenvswitch.a
 
-EXTRA_DIST += tests/choose-port.pl
-
 # Python tests.
 CHECK_PYFILES = \
        tests/appctl.py \
diff --git a/tests/choose-port.pl b/tests/choose-port.pl
deleted file mode 100644 (file)
index 46c8db5..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- perl -*-
-
-# Picks a random TCP port and attempts to bind it, retrying a few
-# times if the chosen port is in use.  This is better than just
-# picking a random number without checking whether it is in use (but
-# of course a race window still exists).
-#
-# On success, prints a port number on stdout and exits with status 0.
-# On failure, prints an error on stderr and exits with a nonzero status.
-
-use warnings;
-use strict;
-use Socket;
-
-socket(SOCK, PF_INET, SOCK_STREAM, 0) || die "socket: $!\n";
-for (my ($i) = 0; ; $i++) {
-    my ($port) = int(rand(16383)) + 49152;
-    if (bind(SOCK, sockaddr_in($port, INADDR_ANY))) {
-        print "$port\n";
-        exit 0;
-    } elsif ($i < 10 && $!{EADDRINUSE}) {
-        # Address already in use.  Try again.
-    } else {
-        die "bind: $!\n";
-    }
-}
index b1d2f2600ea8103572c9f5e85964af8ff2919413..0de941ea2d0f5b6e0808878086d28d9870d9afd7 100644 (file)
@@ -1208,10 +1208,12 @@ AT_CLEANUP
 
 dnl Test that sFlow samples packets correctly.
 AT_SETUP([ofproto-dpif - sFlow packet sampling])
-AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-SFLOW_PORT=`cat stdout`
 OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
 
+AT_CHECK([test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore])
+AT_CAPTURE_FILE([sflow.log])
+SFLOW_PORT=`parse_listening_port < test-sflow.log`
+
 ovs-appctl time/stop
 
 ADD_OF_PORTS([br0], 1, 2)
@@ -1223,8 +1225,6 @@ ovs-vsctl \
    --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
      header=128 sampling=1 polling=1
 ON_EXIT([kill `cat test-sflow.pid`])
-AT_CHECK([test-sflow --detach --no-chdir --pidfile $SFLOW_PORT:127.0.0.1 > sflow.log])
-AT_CAPTURE_FILE([sflow.log])
 
 dnl open with ARP packets to seed the bridge-learning.  The output
 dnl ifIndex numbers should be reported predictably after that.
@@ -1504,20 +1504,19 @@ dnl - Flow actions changing (in this case, due to MAC learning)
 dnl   cause a record to be sent.
 AT_SETUP([ofproto-dpif - NetFlow flow expiration])
 
-AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-NETFLOW_PORT=`cat stdout`
-
 OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
 ADD_OF_PORTS([br0], 1, 2)
+
+ON_EXIT([kill `cat test-netflow.pid`])
+AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > netflow.log], [0], [], [ignore])
+AT_CAPTURE_FILE([netflow.log])
+NETFLOW_PORT=`parse_listening_port < test-netflow.log`
+
 ovs-vsctl \
    set Bridge br0 netflow=@nf -- \
    --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \
      engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false
 
-ON_EXIT([kill `cat test-netflow.pid`])
-AT_CHECK([test-netflow --detach --no-chdir --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])
-AT_CAPTURE_FILE([netflow.log])
-
 for delay in 1000 30000; do
     ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
     ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
@@ -1546,19 +1545,19 @@ AT_CLEANUP
 dnl Test that basic NetFlow reports active expirations correctly.
 AT_SETUP([ofproto-dpif - NetFlow active expiration])
 
-AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-NETFLOW_PORT=`cat stdout`
-
 OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
 ADD_OF_PORTS([br0], 1, 2)
+
+ON_EXIT([kill `cat test-netflow.pid`])
+AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > netflow.log], [0], [], [ignore])
+AT_CAPTURE_FILE([netflow.log])
+NETFLOW_PORT=`parse_listening_port < test-netflow.log`
+
 ovs-vsctl \
    set Bridge br0 netflow=@nf -- \
    --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \
      engine_id=1 engine_type=2 active_timeout=10 add-id-to-interface=false
 
-ON_EXIT([kill `cat test-netflow.pid`])
-AT_CHECK([test-netflow --detach --no-chdir --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])AT_CAPTURE_FILE([netflow.log])
-
 AT_CHECK([ovs-appctl time/stop])
 n=1
 while test $n -le 60; do
index 9a6d5ab213d2c3ce7c35ad110bcc2ee0e196472c..cad00804a32b76c303b8d84ad1ff03cf4b4c5964 100644 (file)
@@ -13,6 +13,23 @@ s/ n_bytes=0,//
 s/ idle_age=[0-9]*,//
 s/ hard_age=[0-9]*,//
 '
+}
+
+# parse_listening_port [SERVER]
+#
+# Parses the TCP or SSL port on which a server is listening from the log,
+# given that the server was told to listen on a kernel-chosen port,
+# file provided on stdin, and prints the port number on stdout.
+#
+# Here's an example of how to use this with ovsdb-server:
+#
+#    OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+#    ovsdb-server --log-file --remote=ptcp:0:127.0.0.1 ...
+#    TCP_PORT=`parse_listening_port < ovsdb-server.log`
+#
+# (Also works with pssl: in place of ptcp:.)
+parse_listening_port () {
+    sed -n 's/.*0:127\.0\.0\.1: listening on port \([0-9]*\)$/\1/p'
 }]
 m4_divert_pop([PREPARE_TESTS])
 
index 3c32e2f56d66fca2be69add49581b23a0ed4c8d4..21a22dbb8d110e865c59ff4359f59ba6d09801fa 100644 (file)
@@ -57,11 +57,12 @@ m4_define([OVSDB_CHECK_IDL_TCP_PY],
    AT_SKIP_IF([test $HAVE_PYTHON = no])
    AT_KEYWORDS([ovsdb server idl positive Python with tcp socket $5])
    OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+   OVS_LOGDIR=`pwd`; export OVS_LOGDIR
    AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
                   [0], [stdout], [ignore])
-   AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-   TCP_PORT=`cat stdout`
-   AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --no-chdir --pidfile="`pwd`"/pid --remote=ptcp:$TCP_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   AT_CHECK([ovsdb-server --log-file '-vPATTERN:console:ovsdb-server|%c|%m' --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --remote=ptcp:0:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   TCP_PORT=`parse_listening_port < ovsdb-server.log`
+
    m4_if([$2], [], [],
      [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT $2], [0], [ignore], [ignore], [kill `cat pid`])])
    AT_CHECK([$PYTHON $srcdir/test-ovsdb.py  -t10 idl $srcdir/idltest.ovsschema tcp:127.0.0.1:$TCP_PORT $3],
index 50f95bda2492121ea1a8e3db2e8ad1ebcaed0736..1f0deca638aeda3d34287331bc1273c1b1e48c15 100644 (file)
@@ -296,15 +296,15 @@ AT_CHECK(
                 "certificate": "'"$PKIDIR/testpki-cert2.pem"'",
                 "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'"}}]']],
   [0], [ignore], [ignore])
-AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-SSL_PORT=`cat stdout`
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
 AT_CHECK(
-  [ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid \
+  [ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid \
         --private-key=db:SSL,private_key \
         --certificate=db:SSL,certificate \
         --ca-cert=db:SSL,ca_cert \
-         --remote=pssl:$SSL_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db],
+        --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db],
   [0], [ignore], [ignore])
+SSL_PORT=`parse_listening_port < ovsdb-server.log`
 AT_CHECK(
   [[ovsdb-client \
         --private-key=$PKIDIR/testpki-privkey.pem \
@@ -479,12 +479,12 @@ m4_define([OVSDB_CHECK_EXECUTION],
    AT_KEYWORDS([ovsdb server positive ssl $5])
    AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
    OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+   OVS_LOGDIR=`pwd`; export OVS_LOGDIR
    $2 > schema
-   AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-   SSL_PORT=`cat stdout`
    PKIDIR=$abs_top_builddir/tests
    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
-   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:$SSL_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   SSL_PORT=`parse_listening_port < ovsdb-server.log`
    m4_foreach([txn], [$3], 
      [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:127.0.0.1:$SSL_PORT 'txn'], [0], [stdout], [ignore],
      [test ! -e pid || kill `cat pid`])
@@ -502,10 +502,10 @@ AT_BANNER([OVSDB -- ovsdb-server transactions (TCP sockets)])
 AT_SETUP([ovsdb-client get-schema-version - tcp socket])
 AT_KEYWORDS([ovsdb server positive tcp])
 ordinal_schema > schema
-AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-TCP_PORT=`cat stdout`
 AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=ptcp:$TCP_PORT:127.0.0.1 db], [0], [ignore], [ignore])
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=ptcp:0:127.0.0.1 db], [0], [ignore], [ignore])
+TCP_PORT=`parse_listening_port < ovsdb-server.log`
 AT_CHECK([ovsdb-client get-schema-version tcp:127.0.0.1:$TCP_PORT ordinals], [0], [5.1.3
 ])
 OVSDB_SERVER_SHUTDOWN
@@ -529,12 +529,12 @@ m4_define([OVSDB_CHECK_EXECUTION],
   [AT_SETUP([$1])
    AT_KEYWORDS([ovsdb server positive tcp $5])
    OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+   OVS_LOGDIR=`pwd`; export OVS_LOGDIR
    $2 > schema
-   AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
-   TCP_PORT=`cat stdout`
    PKIDIR=$abs_top_builddir/tests
    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
-   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=ptcp:$TCP_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid --remote=ptcp:0:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   TCP_PORT=`parse_listening_port < ovsdb-server.log`
    m4_foreach([txn], [$3],
      [AT_CHECK([ovsdb-client transact tcp:127.0.0.1:$TCP_PORT 'txn'], [0], [stdout], [ignore],
      [test ! -e pid || kill `cat pid`])