Skip to content

Commit 8ca0104

Browse files
committed
Virtual disk: add migration case with vhostuser disk
Automate case: RHEL-200524: Migrate VM with a vhostuser disk Signed-off-by: Meina Li <meili@redhat.com>
1 parent 251d5bd commit 8ca0104

File tree

2 files changed

+244
-0
lines changed

2 files changed

+244
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
- migration.migration_with_disk.migration_with_vhostuser:
2+
type = migration_with_vhostuser
3+
start_vm = "no"
4+
migration_setup = "yes"
5+
storage_type = "nfs"
6+
setup_local_nfs = "yes"
7+
disk_type = "file"
8+
disk_source_protocol = "netfs"
9+
mnt_path_name = ${nfs_mount_dir}
10+
# Console output can only be monitored via virsh console output
11+
only_pty = True
12+
take_regular_screendumps = no
13+
# Extra options to pass after <domain> <desturi>
14+
virsh_migrate_extra = ""
15+
# SSH connection time out
16+
ssh_timeout = 60
17+
virsh_migrate_connect_uri = "qemu:///system"
18+
virsh_migrate_dest_state = "running"
19+
virsh_migrate_src_state = "shut off"
20+
server_ip = "${migrate_dest_host}"
21+
server_user = "root"
22+
server_pwd = "${migrate_dest_pwd}"
23+
client_ip = "${migrate_dest_host}"
24+
client_pwd = "${migrate_source_pwd}"
25+
migrate_desturi_port = "22"
26+
migrate_desturi_type = "ssh"
27+
virsh_migrate_desturi = "qemu+ssh://${migrate_dest_host}/system"
28+
func_supported_since_libvirt_ver = (7, 0, 0)
29+
vm_attrs = {"mb": {"source_type":"memfd", "access_mode": "shared"}}
30+
source_file = "/tmp/vhost.sock"
31+
queues = 1
32+
disk_dict = {"type_name": "vhostuser", "device": "disk", "driver": {"name": "qemu", "type": "raw", "queues": ${queues}}, "source": {"attrs": {"type": "unix", "path": "${source_file}"}}, "target": {"dev": "vdb", "bus": "virtio"}}
33+
no ppc64le
34+
variants:
35+
- with_precopy:
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import os
2+
import ast
3+
4+
from avocado.utils import process
5+
6+
from virttest import libvirt_version
7+
from virttest import remote
8+
from virttest import virsh
9+
from virttest import utils_disk
10+
from virttest import data_dir
11+
12+
from virttest.libvirt_xml import vm_xml
13+
from virttest.utils_libvirt import libvirt_vmxml
14+
from virttest.utils_test import libvirt
15+
16+
from provider.migration import base_steps
17+
18+
19+
def start_vhost_sock_service_in_source(start_sock_service_cmd, image_path, sock_path):
20+
"""
21+
Start one vhost sock service in source host.
22+
23+
:param start_sock_service_cmd: command to start vhost service
24+
:param image_path: image file path
25+
:param sock_path: sock file path
26+
"""
27+
# Create backend image in source host
28+
libvirt.create_local_disk("file", image_path, size="100M")
29+
chown_cmd = "chown qemu:qemu %s" % image_path
30+
process.run(chown_cmd, ignore_status=False, shell=True)
31+
# Start vhost sock service in source host
32+
cmd_output = process.run(start_sock_service_cmd, ignore_status=False, shell=True).stdout_text.strip()
33+
# Set SELinux context in source host
34+
ch_seccontext_cmd = "chcon -t svirt_image_t %s" % sock_path
35+
process.run(ch_seccontext_cmd, ignore_status=False, shell=True)
36+
set_bool_mmap_cmd = "setsebool domain_can_mmap_files 1 -P"
37+
process.run(set_bool_mmap_cmd, ignore_status=False, shell=True)
38+
return cmd_output
39+
40+
41+
def start_vhost_sock_service_in_remote(start_sock_service_cmd, image_path, sock_path, params):
42+
"""
43+
Prepare and start one vhost sock service in remote host.
44+
45+
:param start_sock_service_cmd: command to start vhost service
46+
:param image_path: image file path
47+
:param sock_path: sock file path
48+
:param params: test parameters
49+
"""
50+
remote.run_remote_cmd(f"mkdir -p {os.path.dirname(image_path)}", params, ignore_status=True)
51+
# Create backend image in remote host
52+
remote_create_cmd = f"dd if=/dev/zero of={image_path} bs=1M count=100 && chown qemu:qemu {image_path}"
53+
remote.run_remote_cmd(remote_create_cmd, params, ignore_status=False)
54+
# Start vhost sock service in remote host
55+
remote_start_result = remote.run_remote_cmd(start_sock_service_cmd, params, ignore_status=False)
56+
remote_vsock_service_id = remote_start_result.stdout_text.strip() if hasattr(remote_start_result, 'stdout_text') else None
57+
# Set SELinux context in remote host
58+
remote_selinux_cmd = f"chcon -t svirt_image_t {sock_path} && setsebool domain_can_mmap_files 1 -P"
59+
remote.run_remote_cmd(remote_selinux_cmd, params, ignore_status=False)
60+
return remote_vsock_service_id
61+
62+
63+
def run(test, params, env):
64+
"""
65+
Test vhostuser disk migration.
66+
67+
1.Prepare vhostuser disk and start the domain.
68+
2.Perform migration operation.
69+
3.Verify vhostuser disk after migration.
70+
"""
71+
72+
def setup_test():
73+
"""
74+
Setup steps before migration
75+
"""
76+
nonlocal vsock_service_id, remote_vsock_service_id, image_path, sock_path
77+
78+
test.log.info("Setup steps for vhostuser disk migration.")
79+
80+
sock_path = params.get("source_file", "/tmp/vhost.sock")
81+
image_path = data_dir.get_data_dir() + '/test.img'
82+
disk_dict = ast.literal_eval(params.get("disk_dict", "{}"))
83+
vm_attrs = ast.literal_eval(params.get("vm_attrs", "{}"))
84+
85+
# Define start_sock_service_cmd
86+
start_sock_service_cmd = (
87+
'systemd-run --uid qemu --gid qemu /usr/bin/qemu-storage-daemon'
88+
' --blockdev \'{"driver":"file","filename":"%s","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}\''
89+
' --blockdev \'{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}\''
90+
' --export vhost-user-blk,id=vhost-user-blk0,node-name=libvirt-1-format,addr.type=unix,addr.path=%s,writable=on'
91+
' --chardev stdio,mux=on,id=char0; sleep 3'
92+
% (image_path, sock_path))
93+
94+
# Start vhost service in source host
95+
vsock_service_id = start_vhost_sock_service_in_source(start_sock_service_cmd, image_path, sock_path)
96+
# Start vhost service in remote host
97+
remote_vsock_service_id = start_vhost_sock_service_in_remote(start_sock_service_cmd, image_path, sock_path, params)
98+
# Setup migration connection
99+
migration_obj.setup_connection()
100+
# Prepare the VM with memory backing and vhostuser disk.
101+
vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name)
102+
vmxml.setup_attrs(**vm_attrs)
103+
disk_obj = libvirt_vmxml.create_vm_device_by_type("disk", disk_dict)
104+
test.log.debug("vhostuser disk xml is:\n%s" % disk_obj)
105+
vmxml.add_device(disk_obj)
106+
vmxml.sync()
107+
base_steps.sync_cpu_for_mig(params)
108+
vm.start()
109+
vm.wait_for_login().close()
110+
111+
# Check if vhostuser disk is accessible in VM
112+
if "vhostuser" not in virsh.dumpxml(vm_name).stdout_text:
113+
test.fail("Check vhostuser disk in VM failed")
114+
115+
test.log.info("Setup completed successfully.")
116+
117+
def verify_test():
118+
"""
119+
Verify steps after migration
120+
121+
"""
122+
test.log.info("Verify steps after vhostuser disk migration.")
123+
124+
device_target = params.get("target_dev", "vdb")
125+
desturi = params.get("virsh_migrate_desturi")
126+
127+
# Switch to destination host
128+
backup_uri, vm.connect_uri = vm.connect_uri, desturi
129+
vm.cleanup_serial_console()
130+
vm.create_serial_console()
131+
vm_session = vm.wait_for_serial_login(timeout=120)
132+
133+
try:
134+
# Verify vhostuser disk is still accessible after migration
135+
output = vm_session.cmd_output("lsblk")
136+
test.log.debug("lsblk output after migration: %s", output)
137+
if device_target not in output:
138+
test.fail(f'Vhostuser disk device {device_target} not found in VM after migration')
139+
# Write data to the disk to ensure it's working
140+
utils_disk.dd_data_to_vm_disk(vm_session, "/dev/%s" % device_target)
141+
test.log.info(f"Vhostuser disk {device_target} is accessible after migration")
142+
143+
finally:
144+
vm_session.close()
145+
146+
# Restore original connection URI
147+
vm.connect_uri = backup_uri
148+
149+
# Run default migration verification
150+
migration_obj.verify_default()
151+
152+
test.log.info("Verification completed successfully.")
153+
154+
def cleanup_test():
155+
"""
156+
Cleanup steps for cases
157+
158+
"""
159+
test.log.info("Cleanup steps for vhostuser disk migration.")
160+
if vm.is_alive():
161+
vm.destroy(gracefully=False)
162+
vmxml_backup.sync()
163+
164+
migration_obj.cleanup_connection()
165+
166+
# Cleanup on remote host
167+
if remote_vsock_service_id:
168+
remote.run_remote_cmd(f"systemctl stop {remote_vsock_service_id}", params, ignore_status=True)
169+
remote.run_remote_cmd('pidof qemu-storage-daemon && killall qemu-storage-daemon', params, ignore_status=True)
170+
remote.run_remote_cmd(f"rm -rf {sock_path} {image_path}", params, ignore_status=True)
171+
172+
# Kill all qemu-storage-daemon process on local host
173+
process.run("pidof qemu-storage-daemon && killall qemu-storage-daemon",
174+
ignore_status=True, shell=True)
175+
176+
if vsock_service_id:
177+
stop_vsock_service_cmd = "systemctl stop %s" % vsock_service_id
178+
process.run(stop_vsock_service_cmd, ignore_status=True, shell=True)
179+
180+
# Clean up images
181+
for file_path in [image_path, sock_path]:
182+
if os.path.exists(file_path):
183+
os.remove(file_path)
184+
185+
test.log.info("Cleanup completed successfully.")
186+
187+
libvirt_version.is_libvirt_feature_supported(params)
188+
vm_name = params.get("migrate_main_vm")
189+
vm = env.get_vm(vm_name)
190+
191+
# Initialize variables
192+
vsock_service_id = None
193+
remote_vsock_service_id = None
194+
image_path = None
195+
sock_path = None
196+
197+
# Back up xml file.
198+
vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name)
199+
200+
# Migration object
201+
migration_obj = base_steps.MigrationBase(test, vm, params)
202+
203+
try:
204+
setup_test()
205+
migration_obj.run_migration()
206+
verify_test()
207+
208+
finally:
209+
cleanup_test()

0 commit comments

Comments
 (0)