From: "Lukáš Doktor" <ldoktor@redhat.com>
To: qemu-block@nongnu.org
Cc: ldoktor@redhat.com, kwolf@redhat.com, qemu-devel@nongnu.org,
mreitz@redhat.com
Subject: [PATCH] tests/qemu_iotests: Minimize usage of used ports
Date: Mon, 3 Feb 2020 08:59:55 +0100 [thread overview]
Message-ID: <20200203075955.28861-1-ldoktor@redhat.com> (raw)
Using a range of ports from 32768 to 65538 is dangerous as some
application might already be listening there and interfere with the
testing. There is no way to reserve ports, but let's decrease the chance
of interactions by only using ports that were free at the time of
importing this module.
Without this patch CI occasionally fails with used ports. Additionally I
tried listening on the first port to be tried via "nc -l localhost
$port" and no matter how many other ports were available it always
hanged for infinity.
Signed-off-by: Lukáš Doktor <ldoktor@redhat.com>
---
tests/qemu-iotests/147 | 43 ++++++++++++++++-------
tests/qemu-iotests/iotests.py | 64 +++++++++++++++++++++++++++++++++++
2 files changed, 94 insertions(+), 13 deletions(-)
diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147
index 2b6f859a09..4d0e1418bb 100755
--- a/tests/qemu-iotests/147
+++ b/tests/qemu-iotests/147
@@ -26,10 +26,8 @@ import time
import iotests
from iotests import cachemode, aiomode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_early_pipe
-NBD_PORT_START = 32768
-NBD_PORT_END = NBD_PORT_START + 1024
-NBD_IPV6_PORT_START = NBD_PORT_END
-NBD_IPV6_PORT_END = NBD_IPV6_PORT_START + 1024
+NBD_PORTS = iotests.find_free_ports(32768, 65536, 1024)
+NBD_IPV6_PORTS = iotests.find_free_ports(NBD_PORTS[-1] + 1, 65536, 1024)
test_img = os.path.join(iotests.test_dir, 'test.img')
unix_socket = os.path.join(iotests.sock_dir, 'nbd.socket')
@@ -104,11 +102,15 @@ class QemuNBD(NBDBlockdevAddBase):
self.assertTrue(self._try_server_up(*args))
def test_inet(self):
- while True:
- nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
+ nbd_port = None
+ nbd_ports = NBD_PORTS[:]
+ random.shuffle(nbd_ports)
+ for nbd_port in nbd_ports:
if self._try_server_up('-b', 'localhost', '-p', str(nbd_port)):
break
-
+ else:
+ raise AssertionError("NBD not listening on any port: %s"
+ % NBD_PORTS)
address = { 'type': 'inet',
'data': {
'host': 'localhost',
@@ -178,8 +180,10 @@ class BuiltinNBD(NBDBlockdevAddBase):
self.assert_qmp(result, 'return', {})
def do_test_inet(self, export_name=None):
- while True:
- nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
+ nbd_port = None
+ nbd_ports = NBD_PORTS[:]
+ random.shuffle(nbd_ports)
+ for nbd_port in nbd_ports:
address = { 'type': 'inet',
'data': {
'host': 'localhost',
@@ -187,6 +191,9 @@ class BuiltinNBD(NBDBlockdevAddBase):
} }
if self._try_server_up(address, export_name):
break
+ else:
+ raise AssertionError("NBD not listening on any port: %s"
+ % NBD_PORTS)
export_name = export_name or 'nbd-export'
self.client_test('nbd://localhost:%i/%s' % (nbd_port, export_name),
@@ -203,8 +210,10 @@ class BuiltinNBD(NBDBlockdevAddBase):
self.do_test_inet('shadow')
def test_inet_two_exports(self):
- while True:
- nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
+ nbd_port = None
+ nbd_ports = NBD_PORTS[:]
+ random.shuffle(nbd_ports)
+ for nbd_port in nbd_ports:
address = { 'type': 'inet',
'data': {
'host': 'localhost',
@@ -212,6 +221,9 @@ class BuiltinNBD(NBDBlockdevAddBase):
} }
if self._try_server_up(address, 'exp1', 'exp2'):
break
+ else:
+ raise AssertionError("NBD not listening on any port: %s"
+ % NBD_PORTS)
self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp1'),
flatten_sock_addr(address), 'exp1', 'node1', False)
@@ -232,8 +244,10 @@ class BuiltinNBD(NBDBlockdevAddBase):
# IPv6 not available, skip
return
- while True:
- nbd_port = random.randrange(NBD_IPV6_PORT_START, NBD_IPV6_PORT_END)
+ nbd_port = None
+ nbd_ports = NBD_IPV6_PORTS[:]
+ random.shuffle(nbd_ports)
+ for nbd_port in nbd_ports:
address = { 'type': 'inet',
'data': {
'host': '::1',
@@ -243,6 +257,9 @@ class BuiltinNBD(NBDBlockdevAddBase):
} }
if self._try_server_up(address):
break
+ else:
+ raise AssertionError("NBD not listening on any port: %s"
+ % NBD_IPV6_PORTS)
filename = { 'driver': 'raw',
'file': {
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 89aa2df2f3..d5a3ce2d39 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -20,6 +20,7 @@ from __future__ import print_function
import errno
import os
import re
+import socket
import subprocess
import string
import unittest
@@ -75,6 +76,69 @@ luks_default_secret_object = 'secret,id=keysec0,data=' + \
luks_default_key_secret_opt = 'key-secret=keysec0'
+def is_port_free(port, address):
+ """
+ Return True if the given port is available for use.
+
+ Currently we only check for TCP/UDP connections on IPv4/6
+ Backported from avocado.utils.network
+
+ :param port: Port number
+ :param address: Socket address to bind or connect
+ """
+ families = (socket.AF_INET, socket.AF_INET6)
+ if address == "localhost" or not address:
+ localhost = True
+ protocols = (socket.SOCK_STREAM, socket.SOCK_DGRAM)
+ else:
+ localhost = False
+ # sock.connect always connects for UDP
+ protocols = (socket.SOCK_STREAM, )
+ sock = None
+ try:
+ for family in families:
+ for protocol in protocols:
+ try:
+ sock = socket.socket(family, protocol)
+ if localhost:
+ sock.bind(("", port))
+ else:
+ sock.connect((address, port))
+ return False
+ except socket.error as exc:
+ if exc.errno in (93, 94): # Unsupported combinations
+ continue
+ if localhost:
+ return False
+ sock.close()
+ return True
+ finally:
+ if sock is not None:
+ sock.close()
+
+
+def find_free_ports(start_port, end_port, count, address="localhost"):
+ """
+ Return count of host free ports in the range [start_port, end_port].
+
+ Backported from avocado.utils.network
+
+ :param start_port: header of candidate port range
+ :param end_port: ender of candidate port range
+ :param count: Initial number of ports known to be free in the range.
+ :param address: Socket address to bind or connect
+ """
+ ports = []
+ port_range = range(start_port, end_port)
+ for i in port_range:
+ if is_port_free(i, address):
+ ports.append(i)
+ if len(ports) >= count:
+ break
+
+ return ports
+
+
def qemu_img(*args):
'''Run qemu-img and return the exit code'''
devnull = open('/dev/null', 'r+')
--
2.21.1
next reply other threads:[~2020-02-03 8:01 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-02-03 7:59 Lukáš Doktor [this message]
2020-02-03 15:32 ` [PATCH] tests/qemu_iotests: Minimize usage of used ports Eric Blake
2020-02-06 15:03 ` Max Reitz
2020-02-06 16:27 ` Lukáš Doktor
2020-02-06 16:37 ` Max Reitz
2020-02-06 16:48 ` Eric Blake
2020-02-06 16:59 ` Max Reitz
2020-02-06 18:33 ` Lukáš Doktor
2020-02-07 8:24 ` Max Reitz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200203075955.28861-1-ldoktor@redhat.com \
--to=ldoktor@redhat.com \
--cc=kwolf@redhat.com \
--cc=mreitz@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).