* [PATCH 1/5] tests/functional: Convert the vnc test
2024-12-04 7:19 [PATCH for-10.0 0/5] tests/functional: Convert tests with find_free_ports() Thomas Huth
@ 2024-12-04 7:19 ` Thomas Huth
2024-12-04 7:19 ` [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file Thomas Huth
` (3 subsequent siblings)
4 siblings, 0 replies; 12+ messages in thread
From: Thomas Huth @ 2024-12-04 7:19 UTC (permalink / raw)
To: qemu-devel; +Cc: Daniel P. Berrangé, Philippe Mathieu-Daudé
Nothing thrilling in here, it's just a straight forward conversion.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
tests/functional/meson.build | 1 +
tests/{avocado/vnc.py => functional/test_vnc.py} | 12 +++++++-----
2 files changed, 8 insertions(+), 5 deletions(-)
rename tests/{avocado/vnc.py => functional/test_vnc.py} (97%)
mode change 100644 => 100755
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index f50bf61a50..3d29b8245a 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -206,6 +206,7 @@ tests_x86_64_system_quick = [
'pc_cpu_hotplug_props',
'virtio_version',
'x86_cpu_model_versions',
+ 'vnc',
]
tests_x86_64_system_thorough = [
diff --git a/tests/avocado/vnc.py b/tests/functional/test_vnc.py
old mode 100644
new mode 100755
similarity index 97%
rename from tests/avocado/vnc.py
rename to tests/functional/test_vnc.py
index 862c8996a8..b769d3b268
--- a/tests/avocado/vnc.py
+++ b/tests/functional/test_vnc.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+#
# Simple functional tests for VNC functionality
#
# Copyright (c) 2018 Red Hat, Inc.
@@ -11,7 +13,7 @@
import socket
from typing import List
-from avocado_qemu import QemuSystemTest
+from qemu_test import QemuSystemTest
VNC_ADDR = '127.0.0.1'
@@ -51,10 +53,7 @@ def find_free_ports(count: int) -> List[int]:
class Vnc(QemuSystemTest):
- """
- :avocado: tags=vnc,quick
- :avocado: tags=machine:none
- """
+
def test_no_vnc(self):
self.vm.add_args('-nodefaults', '-S')
self.vm.launch()
@@ -113,3 +112,6 @@ def test_change_listen(self):
self.assertFalse(check_connect(a))
self.assertTrue(check_connect(b))
self.assertTrue(check_connect(c))
+
+if __name__ == '__main__':
+ QemuSystemTest.main()
--
2.47.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file
2024-12-04 7:19 [PATCH for-10.0 0/5] tests/functional: Convert tests with find_free_ports() Thomas Huth
2024-12-04 7:19 ` [PATCH 1/5] tests/functional: Convert the vnc test Thomas Huth
@ 2024-12-04 7:19 ` Thomas Huth
2024-12-04 10:41 ` Daniel P. Berrangé
2024-12-04 10:46 ` Daniel P. Berrangé
2024-12-04 7:19 ` [PATCH 3/5] tests/functional/test_vnc: Do not use a hard-coded VNC port Thomas Huth
` (2 subsequent siblings)
4 siblings, 2 replies; 12+ messages in thread
From: Thomas Huth @ 2024-12-04 7:19 UTC (permalink / raw)
To: qemu-devel; +Cc: Daniel P. Berrangé, Philippe Mathieu-Daudé
We'll need this functionality in other functional tests, too, so
let's extract it into the qemu_test module.
Also add an __enter__ and __exit__ function that can be used for
using this functionality in a locked context, so that tests that
are running in parallel don't try to compete for the same ports
later.
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
tests/functional/qemu_test/ports.py | 53 +++++++++++++++++++++++++++++
tests/functional/test_vnc.py | 36 +++++---------------
2 files changed, 61 insertions(+), 28 deletions(-)
create mode 100644 tests/functional/qemu_test/ports.py
diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py
new file mode 100644
index 0000000000..d235d3432b
--- /dev/null
+++ b/tests/functional/qemu_test/ports.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# Simple functional tests for VNC functionality
+#
+# Copyright 2018, 2024 Red Hat, Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import fcntl
+import os
+import socket
+import sys
+import tempfile
+from typing import List
+
+class Ports():
+
+ PORTS_ADDR = '127.0.0.1'
+ PORTS_START = 32768
+ PORTS_END = PORTS_START + 1024
+
+ def __enter__(self):
+ lock_file = os.path.join(tempfile.gettempdir(), "qemu_port_lock")
+ self.lock_fh = os.open(lock_file, os.O_CREAT)
+ fcntl.flock(self.lock_fh, fcntl.LOCK_EX)
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ fcntl.flock(self.lock_fh, fcntl.LOCK_UN)
+ os.close(self.lock_fh)
+
+ def check_bind(self, port: int) -> bool:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ try:
+ sock.bind((self.PORTS_ADDR, port))
+ except OSError:
+ return False
+
+ return True
+
+ def find_free_ports(self, count: int) -> List[int]:
+ result = []
+ for port in range(self.PORTS_START, self.PORTS_END):
+ if self.check_bind(port):
+ result.append(port)
+ if len(result) >= count:
+ break
+ assert len(result) == count
+ return result
+
+ def find_free_port(self) -> int:
+ return self.find_free_ports(1)[0]
diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py
index b769d3b268..32a81259e4 100755
--- a/tests/functional/test_vnc.py
+++ b/tests/functional/test_vnc.py
@@ -14,22 +14,9 @@
from typing import List
from qemu_test import QemuSystemTest
-
+from qemu_test.ports import Ports
VNC_ADDR = '127.0.0.1'
-VNC_PORT_START = 32768
-VNC_PORT_END = VNC_PORT_START + 1024
-
-
-def check_bind(port: int) -> bool:
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
- try:
- sock.bind((VNC_ADDR, port))
- except OSError:
- return False
-
- return True
-
def check_connect(port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
@@ -40,18 +27,6 @@ def check_connect(port: int) -> bool:
return True
-
-def find_free_ports(count: int) -> List[int]:
- result = []
- for port in range(VNC_PORT_START, VNC_PORT_END):
- if check_bind(port):
- result.append(port)
- if len(result) >= count:
- break
- assert len(result) == count
- return result
-
-
class Vnc(QemuSystemTest):
def test_no_vnc(self):
@@ -90,8 +65,7 @@ def test_change_password(self):
self.vm.cmd('change-vnc-password',
password='new_password')
- def test_change_listen(self):
- a, b, c = find_free_ports(3)
+ def do_test_change_listen(self, a, b, c):
self.assertFalse(check_connect(a))
self.assertFalse(check_connect(b))
self.assertFalse(check_connect(c))
@@ -113,5 +87,11 @@ def test_change_listen(self):
self.assertTrue(check_connect(b))
self.assertTrue(check_connect(c))
+ def test_change_listen(self):
+ with Ports() as ports:
+ a, b, c = ports.find_free_ports(3)
+ self.do_test_change_listen(a, b, c)
+
+
if __name__ == '__main__':
QemuSystemTest.main()
--
2.47.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file
2024-12-04 7:19 ` [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file Thomas Huth
@ 2024-12-04 10:41 ` Daniel P. Berrangé
2024-12-06 15:53 ` Thomas Huth
2024-12-04 10:46 ` Daniel P. Berrangé
1 sibling, 1 reply; 12+ messages in thread
From: Daniel P. Berrangé @ 2024-12-04 10:41 UTC (permalink / raw)
To: Thomas Huth; +Cc: qemu-devel, Philippe Mathieu-Daudé
On Wed, Dec 04, 2024 at 08:19:08AM +0100, Thomas Huth wrote:
> We'll need this functionality in other functional tests, too, so
> let's extract it into the qemu_test module.
> Also add an __enter__ and __exit__ function that can be used for
> using this functionality in a locked context, so that tests that
> are running in parallel don't try to compete for the same ports
> later.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
> tests/functional/qemu_test/ports.py | 53 +++++++++++++++++++++++++++++
> tests/functional/test_vnc.py | 36 +++++---------------
> 2 files changed, 61 insertions(+), 28 deletions(-)
> create mode 100644 tests/functional/qemu_test/ports.py
>
> diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py
> new file mode 100644
> index 0000000000..d235d3432b
> --- /dev/null
> +++ b/tests/functional/qemu_test/ports.py
> @@ -0,0 +1,53 @@
> +#!/usr/bin/env python3
> +#
> +# Simple functional tests for VNC functionality
> +#
> +# Copyright 2018, 2024 Red Hat, Inc.
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or
> +# later. See the COPYING file in the top-level directory.
> +
> +import fcntl
> +import os
> +import socket
> +import sys
> +import tempfile
> +from typing import List
> +
> +class Ports():
> +
> + PORTS_ADDR = '127.0.0.1'
> + PORTS_START = 32768
> + PORTS_END = PORTS_START + 1024
> +
> + def __enter__(self):
> + lock_file = os.path.join(tempfile.gettempdir(), "qemu_port_lock")
> + self.lock_fh = os.open(lock_file, os.O_CREAT)
> + fcntl.flock(self.lock_fh, fcntl.LOCK_EX)
> + return self
> +
> + def __exit__(self, exc_type, exc_value, traceback):
> + fcntl.flock(self.lock_fh, fcntl.LOCK_UN)
> + os.close(self.lock_fh)
> +
> + def check_bind(self, port: int) -> bool:
> + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
> + try:
> + sock.bind((self.PORTS_ADDR, port))
> + except OSError:
> + return False
> +
> + return True
> +
> + def find_free_ports(self, count: int) -> List[int]:
> + result = []
> + for port in range(self.PORTS_START, self.PORTS_END):
> + if self.check_bind(port):
> + result.append(port)
> + if len(result) >= count:
> + break
> + assert len(result) == count
> + return result
> +
> + def find_free_port(self) -> int:
> + return self.find_free_ports(1)[0]
> diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py
> index b769d3b268..32a81259e4 100755
> --- a/tests/functional/test_vnc.py
> +++ b/tests/functional/test_vnc.py
> @@ -14,22 +14,9 @@
> from typing import List
>
> from qemu_test import QemuSystemTest
> -
> +from qemu_test.ports import Ports
>
> VNC_ADDR = '127.0.0.1'
> -VNC_PORT_START = 32768
> -VNC_PORT_END = VNC_PORT_START + 1024
> -
> -
> -def check_bind(port: int) -> bool:
> - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
> - try:
> - sock.bind((VNC_ADDR, port))
> - except OSError:
> - return False
> -
> - return True
> -
>
> def check_connect(port: int) -> bool:
> with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
> @@ -40,18 +27,6 @@ def check_connect(port: int) -> bool:
>
> return True
>
> -
> -def find_free_ports(count: int) -> List[int]:
> - result = []
> - for port in range(VNC_PORT_START, VNC_PORT_END):
> - if check_bind(port):
> - result.append(port)
> - if len(result) >= count:
> - break
> - assert len(result) == count
> - return result
> -
> -
> class Vnc(QemuSystemTest):
>
> def test_no_vnc(self):
> @@ -90,8 +65,7 @@ def test_change_password(self):
> self.vm.cmd('change-vnc-password',
> password='new_password')
>
> - def test_change_listen(self):
> - a, b, c = find_free_ports(3)
> + def do_test_change_listen(self, a, b, c):
> self.assertFalse(check_connect(a))
> self.assertFalse(check_connect(b))
> self.assertFalse(check_connect(c))
> @@ -113,5 +87,11 @@ def test_change_listen(self):
> self.assertTrue(check_connect(b))
> self.assertTrue(check_connect(c))
>
> + def test_change_listen(self):
> + with Ports() as ports:
> + a, b, c = ports.find_free_ports(3)
> + self.do_test_change_listen(a, b, c)
I think it would be possible to implement a decorator using "Ports"
such that we don't need to manually wrap the methods, and just receive
the list of port numbers as arguments.
eg to make this pattern with:
@findFreePorts(3)
def test_change_listen(self, ports):
....use 'ports' list ....
Fully untested, but I think something approximately like this would
work:
def findFreePorts(num)
def findFreePortsDeco(func):
def wrapper(*args, **kwargs):
with Ports() as ports:
freeports = ports.find_free_ports(num)
func(freeports, *args, **kwargs)
return wrapper
return findFreePortsDeco
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file
2024-12-04 10:41 ` Daniel P. Berrangé
@ 2024-12-06 15:53 ` Thomas Huth
0 siblings, 0 replies; 12+ messages in thread
From: Thomas Huth @ 2024-12-06 15:53 UTC (permalink / raw)
To: Daniel P. Berrangé; +Cc: qemu-devel, Philippe Mathieu-Daudé
On 04/12/2024 11.41, Daniel P. Berrangé wrote:
> On Wed, Dec 04, 2024 at 08:19:08AM +0100, Thomas Huth wrote:
>> We'll need this functionality in other functional tests, too, so
>> let's extract it into the qemu_test module.
>> Also add an __enter__ and __exit__ function that can be used for
>> using this functionality in a locked context, so that tests that
>> are running in parallel don't try to compete for the same ports
>> later.
>>
>> Signed-off-by: Thomas Huth <thuth@redhat.com>
>> ---
>> tests/functional/qemu_test/ports.py | 53 +++++++++++++++++++++++++++++
>> tests/functional/test_vnc.py | 36 +++++---------------
>> 2 files changed, 61 insertions(+), 28 deletions(-)
>> create mode 100644 tests/functional/qemu_test/ports.py
>>
>> diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py
>> new file mode 100644
>> index 0000000000..d235d3432b
>> --- /dev/null
>> +++ b/tests/functional/qemu_test/ports.py
>> @@ -0,0 +1,53 @@
>> +#!/usr/bin/env python3
>> +#
>> +# Simple functional tests for VNC functionality
>> +#
>> +# Copyright 2018, 2024 Red Hat, Inc.
>> +#
>> +# This work is licensed under the terms of the GNU GPL, version 2 or
>> +# later. See the COPYING file in the top-level directory.
>> +
>> +import fcntl
>> +import os
>> +import socket
>> +import sys
>> +import tempfile
>> +from typing import List
>> +
>> +class Ports():
>> +
>> + PORTS_ADDR = '127.0.0.1'
>> + PORTS_START = 32768
>> + PORTS_END = PORTS_START + 1024
>> +
>> + def __enter__(self):
>> + lock_file = os.path.join(tempfile.gettempdir(), "qemu_port_lock")
>> + self.lock_fh = os.open(lock_file, os.O_CREAT)
>> + fcntl.flock(self.lock_fh, fcntl.LOCK_EX)
>> + return self
>> +
>> + def __exit__(self, exc_type, exc_value, traceback):
>> + fcntl.flock(self.lock_fh, fcntl.LOCK_UN)
>> + os.close(self.lock_fh)
>> +
>> + def check_bind(self, port: int) -> bool:
>> + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
>> + try:
>> + sock.bind((self.PORTS_ADDR, port))
>> + except OSError:
>> + return False
>> +
>> + return True
>> +
>> + def find_free_ports(self, count: int) -> List[int]:
>> + result = []
>> + for port in range(self.PORTS_START, self.PORTS_END):
>> + if self.check_bind(port):
>> + result.append(port)
>> + if len(result) >= count:
>> + break
>> + assert len(result) == count
>> + return result
>> +
>> + def find_free_port(self) -> int:
>> + return self.find_free_ports(1)[0]
>> diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py
>> index b769d3b268..32a81259e4 100755
>> --- a/tests/functional/test_vnc.py
>> +++ b/tests/functional/test_vnc.py
>> @@ -14,22 +14,9 @@
>> from typing import List
>>
>> from qemu_test import QemuSystemTest
>> -
>> +from qemu_test.ports import Ports
>>
>> VNC_ADDR = '127.0.0.1'
>> -VNC_PORT_START = 32768
>> -VNC_PORT_END = VNC_PORT_START + 1024
>> -
>> -
>> -def check_bind(port: int) -> bool:
>> - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
>> - try:
>> - sock.bind((VNC_ADDR, port))
>> - except OSError:
>> - return False
>> -
>> - return True
>> -
>>
>> def check_connect(port: int) -> bool:
>> with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
>> @@ -40,18 +27,6 @@ def check_connect(port: int) -> bool:
>>
>> return True
>>
>> -
>> -def find_free_ports(count: int) -> List[int]:
>> - result = []
>> - for port in range(VNC_PORT_START, VNC_PORT_END):
>> - if check_bind(port):
>> - result.append(port)
>> - if len(result) >= count:
>> - break
>> - assert len(result) == count
>> - return result
>> -
>> -
>> class Vnc(QemuSystemTest):
>>
>> def test_no_vnc(self):
>> @@ -90,8 +65,7 @@ def test_change_password(self):
>> self.vm.cmd('change-vnc-password',
>> password='new_password')
>>
>> - def test_change_listen(self):
>> - a, b, c = find_free_ports(3)
>> + def do_test_change_listen(self, a, b, c):
>> self.assertFalse(check_connect(a))
>> self.assertFalse(check_connect(b))
>> self.assertFalse(check_connect(c))
>> @@ -113,5 +87,11 @@ def test_change_listen(self):
>> self.assertTrue(check_connect(b))
>> self.assertTrue(check_connect(c))
>>
>> + def test_change_listen(self):
>> + with Ports() as ports:
>> + a, b, c = ports.find_free_ports(3)
>> + self.do_test_change_listen(a, b, c)
>
> I think it would be possible to implement a decorator using "Ports"
> such that we don't need to manually wrap the methods, and just receive
> the list of port numbers as arguments.
>
> eg to make this pattern with:
>
> @findFreePorts(3)
> def test_change_listen(self, ports):
> ....use 'ports' list ....
>
> Fully untested, but I think something approximately like this would
> work:
>
> def findFreePorts(num)
> def findFreePortsDeco(func):
> def wrapper(*args, **kwargs):
> with Ports() as ports:
> freeports = ports.find_free_ports(num)
> func(freeports, *args, **kwargs)
> return wrapper
> return findFreePortsDeco
Being mainly a C coder, I think I'd rather avoid getting into too much
Python magic like such a decorator in the functional tests, to avoid scaring
the Python ignorants (like I have been in the past) too much ;-)
Thomas
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file
2024-12-04 7:19 ` [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file Thomas Huth
2024-12-04 10:41 ` Daniel P. Berrangé
@ 2024-12-04 10:46 ` Daniel P. Berrangé
2024-12-06 15:56 ` Thomas Huth
1 sibling, 1 reply; 12+ messages in thread
From: Daniel P. Berrangé @ 2024-12-04 10:46 UTC (permalink / raw)
To: Thomas Huth; +Cc: qemu-devel, Philippe Mathieu-Daudé
On Wed, Dec 04, 2024 at 08:19:08AM +0100, Thomas Huth wrote:
> We'll need this functionality in other functional tests, too, so
> let's extract it into the qemu_test module.
> Also add an __enter__ and __exit__ function that can be used for
> using this functionality in a locked context, so that tests that
> are running in parallel don't try to compete for the same ports
> later.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
> tests/functional/qemu_test/ports.py | 53 +++++++++++++++++++++++++++++
> tests/functional/test_vnc.py | 36 +++++---------------
> 2 files changed, 61 insertions(+), 28 deletions(-)
> create mode 100644 tests/functional/qemu_test/ports.py
>
> diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py
> new file mode 100644
> index 0000000000..d235d3432b
> --- /dev/null
> +++ b/tests/functional/qemu_test/ports.py
> @@ -0,0 +1,53 @@
> +#!/usr/bin/env python3
> +#
> +# Simple functional tests for VNC functionality
> +#
> +# Copyright 2018, 2024 Red Hat, Inc.
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or
> +# later. See the COPYING file in the top-level directory.
> +
> +import fcntl
> +import os
> +import socket
> +import sys
> +import tempfile
> +from typing import List
> +
> +class Ports():
> +
> + PORTS_ADDR = '127.0.0.1'
> + PORTS_START = 32768
> + PORTS_END = PORTS_START + 1024
> +
> + def __enter__(self):
> + lock_file = os.path.join(tempfile.gettempdir(), "qemu_port_lock")
> + self.lock_fh = os.open(lock_file, os.O_CREAT)
> + fcntl.flock(self.lock_fh, fcntl.LOCK_EX)
> + return self
> +
> + def __exit__(self, exc_type, exc_value, traceback):
> + fcntl.flock(self.lock_fh, fcntl.LOCK_UN)
> + os.close(self.lock_fh)
This code will leave '/tmp/qemu_port_lock' existing forever.... which is
correct, because if you try to unlink it after closing, you'll introduce
a race because the 2nd __enter__ will now be locking an unlinked file,
and a 3rd __enter__ that comes along will create & lock an entirely new
file.
There are ways to make this safe by using stat + fstat either side of
LOCK_EX, in a loop, to detect locking of an unlinked file. That is
overkill though. It is simpler to just put the lock file in the build
directory IMHO, and thus avoid needing to care about unlinking - that'll
be done when the user purges their build dir.
> +
> + def check_bind(self, port: int) -> bool:
> + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
> + try:
> + sock.bind((self.PORTS_ADDR, port))
> + except OSError:
> + return False
> +
> + return True
> +
> + def find_free_ports(self, count: int) -> List[int]:
> + result = []
> + for port in range(self.PORTS_START, self.PORTS_END):
> + if self.check_bind(port):
> + result.append(port)
> + if len(result) >= count:
> + break
> + assert len(result) == count
> + return result
> +
> + def find_free_port(self) -> int:
> + return self.find_free_ports(1)[0]
> diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py
> index b769d3b268..32a81259e4 100755
> --- a/tests/functional/test_vnc.py
> +++ b/tests/functional/test_vnc.py
> @@ -14,22 +14,9 @@
> from typing import List
>
> from qemu_test import QemuSystemTest
> -
> +from qemu_test.ports import Ports
>
> VNC_ADDR = '127.0.0.1'
> -VNC_PORT_START = 32768
> -VNC_PORT_END = VNC_PORT_START + 1024
> -
> -
> -def check_bind(port: int) -> bool:
> - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
> - try:
> - sock.bind((VNC_ADDR, port))
> - except OSError:
> - return False
> -
> - return True
> -
>
> def check_connect(port: int) -> bool:
> with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
> @@ -40,18 +27,6 @@ def check_connect(port: int) -> bool:
>
> return True
>
> -
> -def find_free_ports(count: int) -> List[int]:
> - result = []
> - for port in range(VNC_PORT_START, VNC_PORT_END):
> - if check_bind(port):
> - result.append(port)
> - if len(result) >= count:
> - break
> - assert len(result) == count
> - return result
> -
> -
> class Vnc(QemuSystemTest):
>
> def test_no_vnc(self):
> @@ -90,8 +65,7 @@ def test_change_password(self):
> self.vm.cmd('change-vnc-password',
> password='new_password')
>
> - def test_change_listen(self):
> - a, b, c = find_free_ports(3)
> + def do_test_change_listen(self, a, b, c):
> self.assertFalse(check_connect(a))
> self.assertFalse(check_connect(b))
> self.assertFalse(check_connect(c))
> @@ -113,5 +87,11 @@ def test_change_listen(self):
> self.assertTrue(check_connect(b))
> self.assertTrue(check_connect(c))
>
> + def test_change_listen(self):
> + with Ports() as ports:
> + a, b, c = ports.find_free_ports(3)
> + self.do_test_change_listen(a, b, c)
> +
> +
> if __name__ == '__main__':
> QemuSystemTest.main()
> --
> 2.47.0
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file
2024-12-04 10:46 ` Daniel P. Berrangé
@ 2024-12-06 15:56 ` Thomas Huth
0 siblings, 0 replies; 12+ messages in thread
From: Thomas Huth @ 2024-12-06 15:56 UTC (permalink / raw)
To: Daniel P. Berrangé; +Cc: qemu-devel, Philippe Mathieu-Daudé
On 04/12/2024 11.46, Daniel P. Berrangé wrote:
> On Wed, Dec 04, 2024 at 08:19:08AM +0100, Thomas Huth wrote:
>> We'll need this functionality in other functional tests, too, so
>> let's extract it into the qemu_test module.
>> Also add an __enter__ and __exit__ function that can be used for
>> using this functionality in a locked context, so that tests that
>> are running in parallel don't try to compete for the same ports
>> later.
>>
>> Signed-off-by: Thomas Huth <thuth@redhat.com>
>> ---
>> tests/functional/qemu_test/ports.py | 53 +++++++++++++++++++++++++++++
>> tests/functional/test_vnc.py | 36 +++++---------------
>> 2 files changed, 61 insertions(+), 28 deletions(-)
>> create mode 100644 tests/functional/qemu_test/ports.py
>>
>> diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py
>> new file mode 100644
>> index 0000000000..d235d3432b
>> --- /dev/null
>> +++ b/tests/functional/qemu_test/ports.py
>> @@ -0,0 +1,53 @@
>> +#!/usr/bin/env python3
>> +#
>> +# Simple functional tests for VNC functionality
>> +#
>> +# Copyright 2018, 2024 Red Hat, Inc.
>> +#
>> +# This work is licensed under the terms of the GNU GPL, version 2 or
>> +# later. See the COPYING file in the top-level directory.
>> +
>> +import fcntl
>> +import os
>> +import socket
>> +import sys
>> +import tempfile
>> +from typing import List
>> +
>> +class Ports():
>> +
>> + PORTS_ADDR = '127.0.0.1'
>> + PORTS_START = 32768
>> + PORTS_END = PORTS_START + 1024
>> +
>> + def __enter__(self):
>> + lock_file = os.path.join(tempfile.gettempdir(), "qemu_port_lock")
>> + self.lock_fh = os.open(lock_file, os.O_CREAT)
>> + fcntl.flock(self.lock_fh, fcntl.LOCK_EX)
>> + return self
>> +
>> + def __exit__(self, exc_type, exc_value, traceback):
>> + fcntl.flock(self.lock_fh, fcntl.LOCK_UN)
>> + os.close(self.lock_fh)
>
> This code will leave '/tmp/qemu_port_lock' existing forever.... which is
> correct, because if you try to unlink it after closing, you'll introduce
> a race because the 2nd __enter__ will now be locking an unlinked file,
> and a 3rd __enter__ that comes along will create & lock an entirely new
> file.
>
> There are ways to make this safe by using stat + fstat either side of
> LOCK_EX, in a loop, to detect locking of an unlinked file. That is
> overkill though. It is simpler to just put the lock file in the build
> directory IMHO, and thus avoid needing to care about unlinking - that'll
> be done when the user purges their build dir.
Putting the lock in the build directory is a nice idea - but it will fail if
the user is using multiple build directory and running the test in the
multiple directories in parallel. But maybe we could ease that situation by
randomizing PORTS_START based on a seed calculated somehow by the build
directory string?
Thomas
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/5] tests/functional/test_vnc: Do not use a hard-coded VNC port
2024-12-04 7:19 [PATCH for-10.0 0/5] tests/functional: Convert tests with find_free_ports() Thomas Huth
2024-12-04 7:19 ` [PATCH 1/5] tests/functional: Convert the vnc test Thomas Huth
2024-12-04 7:19 ` [PATCH 2/5] tests/functional: Extract the find_free_ports() function into a helper file Thomas Huth
@ 2024-12-04 7:19 ` Thomas Huth
2024-12-04 7:19 ` [PATCH 4/5] tests/functional/test_vnc: Remove the test_no_vnc test Thomas Huth
2024-12-04 7:19 ` [PATCH 5/5] tests/functional: Convert the migration avocado test Thomas Huth
4 siblings, 0 replies; 12+ messages in thread
From: Thomas Huth @ 2024-12-04 7:19 UTC (permalink / raw)
To: qemu-devel; +Cc: Daniel P. Berrangé, Philippe Mathieu-Daudé
Two tests here are using the hard-coded VNC port :0 ... if there
is already a QEMU or other program running that is using this
port, the tests will be failing. Let's better detect a free port
for these tests and use that one instead.
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
tests/functional/test_vnc.py | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py
index 32a81259e4..59c790b1ce 100755
--- a/tests/functional/test_vnc.py
+++ b/tests/functional/test_vnc.py
@@ -46,8 +46,8 @@ def test_no_vnc_change_password(self):
self.assertEqual(set_password_response['error']['desc'],
'Could not set password')
- def test_change_password_requires_a_password(self):
- self.vm.add_args('-nodefaults', '-S', '-vnc', ':0')
+ def do_test_change_password_requires_a_password(self, port):
+ self.vm.add_args('-nodefaults', '-S', '-vnc', f':{port - 5900}')
self.vm.launch()
self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
set_password_response = self.vm.qmp('change-vnc-password',
@@ -58,13 +58,24 @@ def test_change_password_requires_a_password(self):
self.assertEqual(set_password_response['error']['desc'],
'Could not set password')
- def test_change_password(self):
- self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password=on')
+ def test_change_password_requires_a_password(self):
+ with Ports() as ports:
+ port = ports.find_free_port()
+ self.do_test_change_password_requires_a_password(port)
+
+ def do_test_change_password(self, port):
+ self.vm.add_args('-nodefaults', '-S',
+ '-vnc', f':{port - 5900},password=on')
self.vm.launch()
self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled'])
self.vm.cmd('change-vnc-password',
password='new_password')
+ def test_change_password(self):
+ with Ports() as ports:
+ port = ports.find_free_port()
+ self.do_test_change_password(port)
+
def do_test_change_listen(self, a, b, c):
self.assertFalse(check_connect(a))
self.assertFalse(check_connect(b))
--
2.47.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 4/5] tests/functional/test_vnc: Remove the test_no_vnc test
2024-12-04 7:19 [PATCH for-10.0 0/5] tests/functional: Convert tests with find_free_ports() Thomas Huth
` (2 preceding siblings ...)
2024-12-04 7:19 ` [PATCH 3/5] tests/functional/test_vnc: Do not use a hard-coded VNC port Thomas Huth
@ 2024-12-04 7:19 ` Thomas Huth
2024-12-04 10:42 ` Daniel P. Berrangé
2024-12-04 7:19 ` [PATCH 5/5] tests/functional: Convert the migration avocado test Thomas Huth
4 siblings, 1 reply; 12+ messages in thread
From: Thomas Huth @ 2024-12-04 7:19 UTC (permalink / raw)
To: qemu-devel; +Cc: Daniel P. Berrangé, Philippe Mathieu-Daudé
This test matches exactly the first three lines of the following
test_no_vnc_change_password test, so there is exactly zero additional
test coverage in here.
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
tests/functional/test_vnc.py | 5 -----
1 file changed, 5 deletions(-)
diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py
index 59c790b1ce..96267707ff 100755
--- a/tests/functional/test_vnc.py
+++ b/tests/functional/test_vnc.py
@@ -29,11 +29,6 @@ def check_connect(port: int) -> bool:
class Vnc(QemuSystemTest):
- def test_no_vnc(self):
- self.vm.add_args('-nodefaults', '-S')
- self.vm.launch()
- self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled'])
-
def test_no_vnc_change_password(self):
self.vm.add_args('-nodefaults', '-S')
self.vm.launch()
--
2.47.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 5/5] tests/functional: Convert the migration avocado test
2024-12-04 7:19 [PATCH for-10.0 0/5] tests/functional: Convert tests with find_free_ports() Thomas Huth
` (3 preceding siblings ...)
2024-12-04 7:19 ` [PATCH 4/5] tests/functional/test_vnc: Remove the test_no_vnc test Thomas Huth
@ 2024-12-04 7:19 ` Thomas Huth
2024-12-04 8:27 ` Thomas Huth
4 siblings, 1 reply; 12+ messages in thread
From: Thomas Huth @ 2024-12-04 7:19 UTC (permalink / raw)
To: qemu-devel; +Cc: Daniel P. Berrangé, Philippe Mathieu-Daudé
Now that we've got a find_free_port() function in the functional
test framework, we can convert the migration test, too.
While the original avocado test was only meant to run on aarch64,
ppc64 and x86, we can turn this into a more generic test by now
and run it on all architectures that have a default machine that
ships with a working firmware.
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
tests/avocado/migration.py | 135 -----------------------------
tests/functional/meson.build | 19 ++++
tests/functional/test_migration.py | 83 ++++++++++++++++++
3 files changed, 102 insertions(+), 135 deletions(-)
delete mode 100644 tests/avocado/migration.py
create mode 100755 tests/functional/test_migration.py
diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py
deleted file mode 100644
index be6234b3c2..0000000000
--- a/tests/avocado/migration.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# Migration test
-#
-# Copyright (c) 2019 Red Hat, Inc.
-#
-# Authors:
-# Cleber Rosa <crosa@redhat.com>
-# Caio Carrara <ccarrara@redhat.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or
-# later. See the COPYING file in the top-level directory.
-
-
-import tempfile
-import os
-
-from avocado_qemu import QemuSystemTest
-from avocado import skipUnless
-
-from avocado.utils.network import ports
-from avocado.utils import wait
-from avocado.utils.path import find_command
-
-
-class MigrationTest(QemuSystemTest):
- """
- :avocado: tags=migration
- """
-
- timeout = 10
-
- @staticmethod
- def migration_finished(vm):
- return vm.cmd('query-migrate')['status'] in ('completed', 'failed')
-
- def assert_migration(self, src_vm, dst_vm):
- wait.wait_for(self.migration_finished,
- timeout=self.timeout,
- step=0.1,
- args=(src_vm,))
- wait.wait_for(self.migration_finished,
- timeout=self.timeout,
- step=0.1,
- args=(dst_vm,))
- self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed')
- self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed')
- self.assertEqual(dst_vm.cmd('query-status')['status'], 'running')
- self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate')
-
- def do_migrate(self, dest_uri, src_uri=None):
- dest_vm = self.get_vm('-incoming', dest_uri)
- dest_vm.add_args('-nodefaults')
- dest_vm.launch()
- if src_uri is None:
- src_uri = dest_uri
- source_vm = self.get_vm()
- source_vm.add_args('-nodefaults')
- source_vm.launch()
- source_vm.qmp('migrate', uri=src_uri)
- self.assert_migration(source_vm, dest_vm)
-
- def _get_free_port(self):
- port = ports.find_free_port()
- if port is None:
- self.cancel('Failed to find a free port')
- return port
-
- def migration_with_tcp_localhost(self):
- dest_uri = 'tcp:localhost:%u' % self._get_free_port()
- self.do_migrate(dest_uri)
-
- def migration_with_unix(self):
- with tempfile.TemporaryDirectory(prefix='socket_') as socket_path:
- dest_uri = 'unix:%s/qemu-test.sock' % socket_path
- self.do_migrate(dest_uri)
-
- @skipUnless(find_command('nc', default=False), "'nc' command not found")
- def migration_with_exec(self):
- """The test works for both netcat-traditional and netcat-openbsd packages."""
- free_port = self._get_free_port()
- dest_uri = 'exec:nc -l localhost %u' % free_port
- src_uri = 'exec:nc localhost %u' % free_port
- self.do_migrate(dest_uri, src_uri)
-
-
-@skipUnless('aarch64' in os.uname()[4], "host != target")
-class Aarch64(MigrationTest):
- """
- :avocado: tags=arch:aarch64
- :avocado: tags=machine:virt
- :avocado: tags=cpu:max
- """
-
- def test_migration_with_tcp_localhost(self):
- self.migration_with_tcp_localhost()
-
- def test_migration_with_unix(self):
- self.migration_with_unix()
-
- def test_migration_with_exec(self):
- self.migration_with_exec()
-
-
-@skipUnless('x86_64' in os.uname()[4], "host != target")
-class X86_64(MigrationTest):
- """
- :avocado: tags=arch:x86_64
- :avocado: tags=machine:pc
- :avocado: tags=cpu:qemu64
- """
-
- def test_migration_with_tcp_localhost(self):
- self.migration_with_tcp_localhost()
-
- def test_migration_with_unix(self):
- self.migration_with_unix()
-
- def test_migration_with_exec(self):
- self.migration_with_exec()
-
-
-@skipUnless('ppc64le' in os.uname()[4], "host != target")
-class PPC64(MigrationTest):
- """
- :avocado: tags=arch:ppc64
- :avocado: tags=machine:pseries
- """
-
- def test_migration_with_tcp_localhost(self):
- self.migration_with_tcp_localhost()
-
- def test_migration_with_unix(self):
- self.migration_with_unix()
-
- def test_migration_with_exec(self):
- self.migration_with_exec()
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 3d29b8245a..0558d0aa4e 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -61,6 +61,10 @@ tests_aarch64_system_thorough = [
'multiprocess',
]
+tests_alpha_system_quick = [
+ 'migration',
+]
+
tests_alpha_system_thorough = [
'alpha_clipper',
]
@@ -148,6 +152,10 @@ tests_ppc_system_thorough = [
'ppc_virtex_ml507',
]
+tests_ppc64_system_quick = [
+ 'migration',
+]
+
tests_ppc64_system_thorough = [
'ppc64_e500',
'ppc64_hv',
@@ -157,6 +165,7 @@ tests_ppc64_system_thorough = [
]
tests_riscv32_system_quick = [
+ 'migration',
'riscv_opensbi',
]
@@ -165,6 +174,7 @@ tests_riscv32_system_thorough = [
]
tests_riscv64_system_quick = [
+ 'migration',
'riscv_opensbi',
]
@@ -191,10 +201,18 @@ tests_sh4eb_system_thorough = [
'sh4eb_r2d',
]
+tests_sparc_system_quick = [
+ 'migration',
+]
+
tests_sparc_system_thorough = [
'sparc_sun4m',
]
+tests_sparc64_system_quick = [
+ 'migration',
+]
+
tests_sparc64_system_thorough = [
'sparc64_sun4u',
'sparc64_tuxrun',
@@ -203,6 +221,7 @@ tests_sparc64_system_thorough = [
tests_x86_64_system_quick = [
'cpu_queries',
'mem_addr_space',
+ 'migration',
'pc_cpu_hotplug_props',
'virtio_version',
'x86_cpu_model_versions',
diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py
new file mode 100755
index 0000000000..9c78444f80
--- /dev/null
+++ b/tests/functional/test_migration.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Migration test
+#
+# Copyright (c) 2019 Red Hat, Inc.
+#
+# Authors:
+# Cleber Rosa <crosa@redhat.com>
+# Caio Carrara <ccarrara@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+import os
+import tempfile
+import time
+
+from qemu_test import QemuSystemTest, has_cmd
+from qemu_test.ports import Ports
+from unittest import skipUnless
+
+class MigrationTest(QemuSystemTest):
+
+ timeout = 10
+
+ @staticmethod
+ def migration_finished(vm):
+ return vm.cmd('query-migrate')['status'] in ('completed', 'failed')
+
+ def assert_migration(self, src_vm, dst_vm):
+
+ end = time.monotonic() + self.timeout
+ while time.monotonic() < end and not self.migration_finished(src_vm):
+ time.sleep(0.1)
+
+ end = time.monotonic() + self.timeout
+ while time.monotonic() < end and not self.migration_finished(dst_vm):
+ time.sleep(0.1)
+
+ self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed')
+ self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed')
+ self.assertEqual(dst_vm.cmd('query-status')['status'], 'running')
+ self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate')
+
+ def do_migrate(self, dest_uri, src_uri=None):
+ dest_vm = self.get_vm('-incoming', dest_uri, name="dest-qemu")
+ dest_vm.add_args('-nodefaults')
+ dest_vm.launch()
+ if src_uri is None:
+ src_uri = dest_uri
+ source_vm = self.get_vm(name="source-qemu")
+ source_vm.add_args('-nodefaults')
+ source_vm.launch()
+ source_vm.qmp('migrate', uri=src_uri)
+ self.assert_migration(source_vm, dest_vm)
+
+ def _get_free_port(self, ports):
+ port = ports.find_free_port()
+ if port is None:
+ self.skipTest('Failed to find a free port')
+ return port
+
+ def test_migration_with_tcp_localhost(self):
+ with Ports() as ports:
+ dest_uri = 'tcp:localhost:%u' % self._get_free_port(ports)
+ self.do_migrate(dest_uri)
+
+ def test_migration_with_unix(self):
+ with tempfile.TemporaryDirectory(prefix='socket_') as socket_path:
+ dest_uri = 'unix:%s/qemu-test.sock' % socket_path
+ self.do_migrate(dest_uri)
+
+ @skipUnless(*has_cmd('nc'))
+ def test_migration_with_exec(self):
+ """The test works for both netcat-traditional and netcat-openbsd packages."""
+ with Ports() as ports:
+ free_port = self._get_free_port(ports)
+ dest_uri = 'exec:nc -l localhost %u' % free_port
+ src_uri = 'exec:nc localhost %u' % free_port
+ self.do_migrate(dest_uri, src_uri)
+
+if __name__ == '__main__':
+ QemuSystemTest.main()
--
2.47.0
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH 5/5] tests/functional: Convert the migration avocado test
2024-12-04 7:19 ` [PATCH 5/5] tests/functional: Convert the migration avocado test Thomas Huth
@ 2024-12-04 8:27 ` Thomas Huth
0 siblings, 0 replies; 12+ messages in thread
From: Thomas Huth @ 2024-12-04 8:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Daniel P. Berrangé, Philippe Mathieu-Daudé
On 04/12/2024 08.19, Thomas Huth wrote:
> Now that we've got a find_free_port() function in the functional
> test framework, we can convert the migration test, too.
> While the original avocado test was only meant to run on aarch64,
> ppc64 and x86, we can turn this into a more generic test by now
> and run it on all architectures that have a default machine that
> ships with a working firmware.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
> tests/avocado/migration.py | 135 -----------------------------
> tests/functional/meson.build | 19 ++++
> tests/functional/test_migration.py | 83 ++++++++++++++++++
> 3 files changed, 102 insertions(+), 135 deletions(-)
> delete mode 100644 tests/avocado/migration.py
> create mode 100755 tests/functional/test_migration.py
>
> diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py
> deleted file mode 100644
> index be6234b3c2..0000000000
> --- a/tests/avocado/migration.py
> +++ /dev/null
> @@ -1,135 +0,0 @@
> -# Migration test
> -#
> -# Copyright (c) 2019 Red Hat, Inc.
> -#
> -# Authors:
> -# Cleber Rosa <crosa@redhat.com>
> -# Caio Carrara <ccarrara@redhat.com>
> -#
> -# This work is licensed under the terms of the GNU GPL, version 2 or
> -# later. See the COPYING file in the top-level directory.
> -
> -
> -import tempfile
> -import os
> -
> -from avocado_qemu import QemuSystemTest
> -from avocado import skipUnless
> -
> -from avocado.utils.network import ports
> -from avocado.utils import wait
> -from avocado.utils.path import find_command
> -
> -
> -class MigrationTest(QemuSystemTest):
> - """
> - :avocado: tags=migration
> - """
> -
> - timeout = 10
> -
> - @staticmethod
> - def migration_finished(vm):
> - return vm.cmd('query-migrate')['status'] in ('completed', 'failed')
> -
> - def assert_migration(self, src_vm, dst_vm):
> - wait.wait_for(self.migration_finished,
> - timeout=self.timeout,
> - step=0.1,
> - args=(src_vm,))
> - wait.wait_for(self.migration_finished,
> - timeout=self.timeout,
> - step=0.1,
> - args=(dst_vm,))
> - self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed')
> - self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed')
> - self.assertEqual(dst_vm.cmd('query-status')['status'], 'running')
> - self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate')
> -
> - def do_migrate(self, dest_uri, src_uri=None):
> - dest_vm = self.get_vm('-incoming', dest_uri)
> - dest_vm.add_args('-nodefaults')
> - dest_vm.launch()
> - if src_uri is None:
> - src_uri = dest_uri
> - source_vm = self.get_vm()
> - source_vm.add_args('-nodefaults')
> - source_vm.launch()
> - source_vm.qmp('migrate', uri=src_uri)
> - self.assert_migration(source_vm, dest_vm)
> -
> - def _get_free_port(self):
> - port = ports.find_free_port()
> - if port is None:
> - self.cancel('Failed to find a free port')
> - return port
> -
> - def migration_with_tcp_localhost(self):
> - dest_uri = 'tcp:localhost:%u' % self._get_free_port()
> - self.do_migrate(dest_uri)
> -
> - def migration_with_unix(self):
> - with tempfile.TemporaryDirectory(prefix='socket_') as socket_path:
> - dest_uri = 'unix:%s/qemu-test.sock' % socket_path
> - self.do_migrate(dest_uri)
> -
> - @skipUnless(find_command('nc', default=False), "'nc' command not found")
> - def migration_with_exec(self):
> - """The test works for both netcat-traditional and netcat-openbsd packages."""
> - free_port = self._get_free_port()
> - dest_uri = 'exec:nc -l localhost %u' % free_port
> - src_uri = 'exec:nc localhost %u' % free_port
> - self.do_migrate(dest_uri, src_uri)
> -
> -
> -@skipUnless('aarch64' in os.uname()[4], "host != target")
> -class Aarch64(MigrationTest):
> - """
> - :avocado: tags=arch:aarch64
> - :avocado: tags=machine:virt
> - :avocado: tags=cpu:max
> - """
> -
> - def test_migration_with_tcp_localhost(self):
> - self.migration_with_tcp_localhost()
> -
> - def test_migration_with_unix(self):
> - self.migration_with_unix()
> -
> - def test_migration_with_exec(self):
> - self.migration_with_exec()
> -
> -
> -@skipUnless('x86_64' in os.uname()[4], "host != target")
> -class X86_64(MigrationTest):
> - """
> - :avocado: tags=arch:x86_64
> - :avocado: tags=machine:pc
> - :avocado: tags=cpu:qemu64
> - """
> -
> - def test_migration_with_tcp_localhost(self):
> - self.migration_with_tcp_localhost()
> -
> - def test_migration_with_unix(self):
> - self.migration_with_unix()
> -
> - def test_migration_with_exec(self):
> - self.migration_with_exec()
> -
> -
> -@skipUnless('ppc64le' in os.uname()[4], "host != target")
> -class PPC64(MigrationTest):
> - """
> - :avocado: tags=arch:ppc64
> - :avocado: tags=machine:pseries
> - """
> -
> - def test_migration_with_tcp_localhost(self):
> - self.migration_with_tcp_localhost()
> -
> - def test_migration_with_unix(self):
> - self.migration_with_unix()
> -
> - def test_migration_with_exec(self):
> - self.migration_with_exec()
> diff --git a/tests/functional/meson.build b/tests/functional/meson.build
> index 3d29b8245a..0558d0aa4e 100644
> --- a/tests/functional/meson.build
> +++ b/tests/functional/meson.build
> @@ -61,6 +61,10 @@ tests_aarch64_system_thorough = [
> 'multiprocess',
> ]
>
> +tests_alpha_system_quick = [
> + 'migration',
> +]
> +
> tests_alpha_system_thorough = [
> 'alpha_clipper',
> ]
> @@ -148,6 +152,10 @@ tests_ppc_system_thorough = [
> 'ppc_virtex_ml507',
> ]
>
> +tests_ppc64_system_quick = [
> + 'migration',
> +]
> +
> tests_ppc64_system_thorough = [
> 'ppc64_e500',
> 'ppc64_hv',
> @@ -157,6 +165,7 @@ tests_ppc64_system_thorough = [
> ]
>
> tests_riscv32_system_quick = [
> + 'migration',
> 'riscv_opensbi',
> ]
>
> @@ -165,6 +174,7 @@ tests_riscv32_system_thorough = [
> ]
>
> tests_riscv64_system_quick = [
> + 'migration',
> 'riscv_opensbi',
> ]
>
> @@ -191,10 +201,18 @@ tests_sh4eb_system_thorough = [
> 'sh4eb_r2d',
> ]
>
> +tests_sparc_system_quick = [
> + 'migration',
> +]
> +
> tests_sparc_system_thorough = [
> 'sparc_sun4m',
> ]
>
> +tests_sparc64_system_quick = [
> + 'migration',
> +]
> +
> tests_sparc64_system_thorough = [
> 'sparc64_sun4u',
> 'sparc64_tuxrun',
> @@ -203,6 +221,7 @@ tests_sparc64_system_thorough = [
> tests_x86_64_system_quick = [
> 'cpu_queries',
> 'mem_addr_space',
> + 'migration',
> 'pc_cpu_hotplug_props',
> 'virtio_version',
> 'x86_cpu_model_versions',
> diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py
> new file mode 100755
> index 0000000000..9c78444f80
> --- /dev/null
> +++ b/tests/functional/test_migration.py
> @@ -0,0 +1,83 @@
> +#!/usr/bin/env python3
> +#
> +# Migration test
> +#
> +# Copyright (c) 2019 Red Hat, Inc.
> +#
> +# Authors:
> +# Cleber Rosa <crosa@redhat.com>
> +# Caio Carrara <ccarrara@redhat.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or
> +# later. See the COPYING file in the top-level directory.
> +
> +import os
> +import tempfile
> +import time
> +
> +from qemu_test import QemuSystemTest, has_cmd
> +from qemu_test.ports import Ports
> +from unittest import skipUnless
> +
> +class MigrationTest(QemuSystemTest):
> +
> + timeout = 10
> +
> + @staticmethod
> + def migration_finished(vm):
> + return vm.cmd('query-migrate')['status'] in ('completed', 'failed')
> +
> + def assert_migration(self, src_vm, dst_vm):
> +
> + end = time.monotonic() + self.timeout
> + while time.monotonic() < end and not self.migration_finished(src_vm):
> + time.sleep(0.1)
> +
> + end = time.monotonic() + self.timeout
> + while time.monotonic() < end and not self.migration_finished(dst_vm):
> + time.sleep(0.1)
> +
> + self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed')
> + self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed')
> + self.assertEqual(dst_vm.cmd('query-status')['status'], 'running')
> + self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate')
> +
> + def do_migrate(self, dest_uri, src_uri=None):
> + dest_vm = self.get_vm('-incoming', dest_uri, name="dest-qemu")
> + dest_vm.add_args('-nodefaults')
> + dest_vm.launch()
> + if src_uri is None:
> + src_uri = dest_uri
> + source_vm = self.get_vm(name="source-qemu")
> + source_vm.add_args('-nodefaults')
> + source_vm.launch()
> + source_vm.qmp('migrate', uri=src_uri)
> + self.assert_migration(source_vm, dest_vm)
> +
> + def _get_free_port(self, ports):
> + port = ports.find_free_port()
> + if port is None:
> + self.skipTest('Failed to find a free port')
> + return port
> +
> + def test_migration_with_tcp_localhost(self):
> + with Ports() as ports:
> + dest_uri = 'tcp:localhost:%u' % self._get_free_port(ports)
> + self.do_migrate(dest_uri)
> +
> + def test_migration_with_unix(self):
> + with tempfile.TemporaryDirectory(prefix='socket_') as socket_path:
> + dest_uri = 'unix:%s/qemu-test.sock' % socket_path
> + self.do_migrate(dest_uri)
> +
> + @skipUnless(*has_cmd('nc'))
Note: The TemporaryDirectory socket stuff and the skipUnless should be
adapted to Daniel's new functions (see
https://lore.kernel.org/qemu-devel/20241129173120.761728-1-berrange@redhat.com/)
if they go in first.
Thomas
> + def test_migration_with_exec(self):
> + """The test works for both netcat-traditional and netcat-openbsd packages."""
> + with Ports() as ports:
> + free_port = self._get_free_port(ports)
> + dest_uri = 'exec:nc -l localhost %u' % free_port
> + src_uri = 'exec:nc localhost %u' % free_port
> + self.do_migrate(dest_uri, src_uri)
> +
> +if __name__ == '__main__':
> + QemuSystemTest.main()
^ permalink raw reply [flat|nested] 12+ messages in thread