Skip to content

Commit b3ff3e8

Browse files
committed
Virtual_disk: add two nvme disk related blockcopy scenarios
Automate case: RHEL-203952 - Blockcopy disk to nvme destination with luks(with usage) RHEL-241215 - Blockcopy disk to nvme destination without luks Signed-off-by: Meina Li <meili@redhat.com>
1 parent d043d25 commit b3ff3e8

File tree

2 files changed

+218
-30
lines changed

2 files changed

+218
-30
lines changed

libvirt/tests/cfg/virtual_disks/virtual_disks_nvme.cfg

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
type = virtual_disks_nvme
33
take_regular_screendumps = "no"
44
start_vm = "no"
5-
target_format = "raw"
65
type_name = "nvme"
7-
driver_type = 'raw'
6+
disk_image_format = "raw"
87
device_type = "disk"
98
target_dev = "vdb"
109
target_bus = "virtio"
@@ -14,6 +13,30 @@
1413
variants:
1514
- attach_nvme:
1615
source_attrs = "{'type':'pci', 'managed':'yes', 'namespace':'1', 'index':'1'}"
16+
- blockcopy_nvme:
17+
only coldplug
18+
disk_type = "file"
19+
nvme_disk_type = "block"
20+
with_blockcopy = "yes"
21+
func_supported_since_libvirt_ver = (6, 3, 0)
22+
disk_dict = {"type_name":"${disk_type}", "target":{"dev": "${target_dev}", "bus": "virtio"}, "driver": {"name": "qemu", "type": "${disk_image_format}"}}
23+
source_attrs = "{'type':'pci', 'managed':'yes', 'namespace':'1', 'index':'1'}"
24+
blockcopy_common_options = "--transient-job --verbose --wait"
25+
variants:
26+
- finish:
27+
blockcopy_option = "--finish"
28+
- pivot:
29+
blockcopy_option = "--pivot"
30+
variants:
31+
- without_luks:
32+
nvme_disk_dict = {"type_name":"${nvme_disk_type}", 'device': 'disk', "target":{"dev": "${target_dev}", "bus": "virtio"}, "driver": {"name": "qemu", "type": "${disk_image_format}"}}
33+
- with_luks:
34+
with_luks = "yes"
35+
sec_volume = "luks"
36+
luks_secret_passwd = "redhat"
37+
disk_image_format = "qcow2"
38+
luks_extra_parameter = "--object secret,data=${luks_secret_passwd},id=sec0 -o encrypt.format=luks,encrypt.key-secret=sec0"
39+
nvme_disk_dict = {"source": {"encryption": {"attrs": {"format": "luks"}, "encryption": "luks", "secret": {"type": "passphrase", "usage": "${sec_volume}"}}}, "type_name":"${nvme_disk_type}", 'device': 'disk', "target":{"dev": "${target_dev}", "bus": "virtio"}, "driver": {"name": "qemu", "type": "${disk_image_format}"}}
1740
variants:
1841
- coldplug:
1942
virt_device_hotplug = "no"

libvirt/tests/src/virtual_disks/virtual_disks_nvme.py

Lines changed: 193 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ast
12
import logging
23
import platform
34
import shutil
@@ -9,9 +10,13 @@
910
from virttest import utils_misc
1011
from virttest import virsh
1112
from virttest import virt_vm
13+
from virttest import libvirt_version
1214

1315
from virttest.libvirt_xml import vm_xml, xcepts
1416
from virttest.utils_libvirt import libvirt_disk
17+
from virttest.utils_test import libvirt
18+
19+
from provider.virtual_disk import disk_base
1520

1621
LOG = logging.getLogger('avocado.' + __name__)
1722

@@ -20,15 +25,20 @@ def run(test, params, env):
2025
"""
2126
Test nvme virtual.
2227
28+
For attach test case:
2329
1.Prepare a vm with nvme type disk
2430
2.Attach the virtual disk to the vm
2531
3.Start vm
2632
4.Check the disk in vm
2733
5.Detach nvme device
28-
"""
29-
vm_name = params.get("main_vm")
30-
vm = env.get_vm(vm_name)
3134
35+
For blockcopy test case:
36+
1.Start a VM with a regular disk
37+
2.Prepare nvme destination disk XML
38+
3.Execute blockcopy with --xml option (--finish or --pivot)
39+
4.Check VM XML after operation
40+
5.Verify disk source in VM
41+
"""
3242
def get_new_disks(old_partitions):
3343
"""
3444
Get new virtual disks in VM after disk plug.
@@ -81,40 +91,92 @@ def create_customized_disk(params, pci_address):
8191
disk_device = params.get("device_type")
8292
device_target = params.get("target_dev")
8393
device_bus = params.get("target_bus")
84-
device_format = params.get("target_format")
94+
device_format = params.get("disk_image_format")
8595

8696
customized_disk = libvirt_disk.create_primitive_disk_xml(
8797
type_name, disk_device,
8898
device_target, device_bus,
8999
device_format, None, None)
90100

91-
source_dict = eval(params.get("source_attrs"))
101+
source_dict = ast.literal_eval(params.get("source_attrs", "{}"))
92102
disk_src_dict = {"attrs": source_dict}
93103
disk_source = customized_disk.new_disk_source(**disk_src_dict)
94104
disk_source.address = pci_address
95105
customized_disk.source = disk_source
96106
LOG.debug("create customized xml: %s", customized_disk)
97107
return customized_disk
98108

99-
hotplug = "yes" == params.get("virt_device_hotplug")
100-
pkgs_host = params.get("pkgs_host", "")
109+
def create_block_nvme_disk(params, disk_type, disk_obj, disk_dict):
110+
"""
111+
Create a block NVME disk by partitioning an NVME device and creating disk XML
101112
102-
# Get disk partitions info before hot/cold plug virtual disk
103-
if vm.is_dead():
104-
vm.start()
105-
session = vm.wait_for_login()
106-
old_partitions = utils_disk.get_parts_list(session)
107-
session.close()
108-
vm.destroy(gracefully=False)
113+
:param params: Dictionary containing test parameters
114+
:param disk_type: the disk type
115+
:param disk_obj: DiskBase object used for preparing disk XML configuration
116+
:param disk_dict: Dictionary containing disk configuration parameters
117+
:return: Tuple containing:
118+
disk_xml: Disk XML object for the created NVME partition
119+
nvme_device: String path of the base NVME device (e.g., "/dev/nvme0n1")
120+
nvme_partition_path: String path of the created partition (e.g., "/dev/nvme0n1p1")
121+
luks_sec_uuid: UUID of the LUKS secret (None if LUKS not used)
122+
"""
123+
luks_secret_passwd = params.get("luks_secret_passwd", "redhat")
124+
with_luks = "yes" == params.get("with_luks", "no")
125+
luks_sec_uuid = None
126+
try:
127+
LOG.info("SETUP_TEST: Getting available NVME device in host")
128+
lsblk_result = process.run("lsblk -d -n -o NAME | grep nvme",
129+
timeout=10, ignore_status=True, shell=True)
130+
if lsblk_result.exit_status != 0:
131+
test.cancel("No NVME device found in host")
109132

110-
# Backup vm xml
111-
vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name)
112-
vmxml = vmxml_backup.copy()
133+
nvme_devices = lsblk_result.stdout_text.strip().split('\n')
134+
for device in nvme_devices:
135+
device_path = "/dev/" + device.strip()
136+
mount_check = process.run(f"lsblk {device_path} | grep -q SWAP\\|/",
137+
ignore_status=True, shell=True)
138+
if mount_check.exit_status != 0:
139+
nvme_device = device_path
140+
break
141+
else:
142+
test.cancel("No available unmounted NVME device found")
113143

114-
try:
115-
# install essential package in host
116-
if not shutil.which('lspci'):
117-
test.error("package {} not installed, please install them before testing".format(pkgs_host))
144+
LOG.info("SETUP_TEST: Creating GPT partition table on NVME device")
145+
result = process.run("parted -s %s mklabel gpt" % nvme_device,
146+
timeout=30, ignore_status=False, verbose=True, shell=True)
147+
LOG.debug("GPT partition table creation result: %s", result)
148+
149+
LOG.info("SETUP_TEST: Creating 1G primary partition on NVME device")
150+
result = process.run("parted -s %s mkpart primary 0%% 1G" % nvme_device,
151+
timeout=30, ignore_status=False, verbose=True, shell=True)
152+
LOG.debug("Partition creation result: %s", result)
153+
time.sleep(2)
154+
155+
# Use the created partition as disk image
156+
nvme_partition_path = nvme_device + "p1"
157+
if with_luks:
158+
luks_sec_uuid = libvirt.create_secret(params)
159+
ret = virsh.secret_set_value(luks_sec_uuid, luks_secret_passwd,
160+
encode=True, debug=True)
161+
libvirt.check_exit_status(ret)
162+
163+
disk_xml, _ = disk_obj.prepare_disk_obj(disk_type, disk_dict, nvme_partition_path)
164+
LOG.debug("Created disk XML: %s", disk_xml)
165+
return disk_xml, nvme_device, nvme_partition_path, luks_sec_uuid
166+
167+
except Exception as err:
168+
test.fail("Failed to create block NVME disk: %s" % str(err))
169+
170+
def run_attach_detach_test():
171+
"""
172+
Run original attach/detach test with nvme device
173+
"""
174+
if vm.is_dead():
175+
vm.start()
176+
session = vm.wait_for_login()
177+
old_partitions = utils_disk.get_parts_list(session)
178+
session.close()
179+
vm.destroy(gracefully=False)
118180

119181
pci_addr_in_dict = get_usable_nvme_pci_address()
120182

@@ -131,13 +193,7 @@ def create_customized_disk(params, pci_address):
131193
if hotplug:
132194
virsh.attach_device(vm_name, device_obj.xml, flagstr="--live",
133195
ignore_status=False, debug=True)
134-
except virt_vm.VMStartError as details:
135-
test.fail("VM failed to start."
136-
"Error: %s" % str(details))
137-
except xcepts.LibvirtXMLError as xml_error:
138-
test.fail("VM failed to define"
139-
"Error: %s" % str(xml_error))
140-
else:
196+
141197
utils_misc.wait_for(lambda: get_new_disks(old_partitions), 20)
142198
new_disks = get_new_disks(old_partitions)
143199
if len(new_disks) != 1:
@@ -147,8 +203,117 @@ def create_customized_disk(params, pci_address):
147203
time.sleep(10)
148204
if not libvirt_disk.check_virtual_disk_io(vm, new_disk):
149205
test.fail("Cannot operate the newly added disk in vm.")
206+
150207
virsh.detach_device(vm_name, device_obj.xml, flagstr="--live",
151208
debug=True, ignore_status=False)
209+
210+
def run_blockcopy_test():
211+
"""
212+
Run blockcopy test with nvme destination
213+
"""
214+
libvirt_version.is_libvirt_feature_supported(params)
215+
target_dev = params.get("target_dev", "vdb")
216+
disk_type = params.get("disk_type", "file")
217+
nvme_disk_type = params.get("nvme_disk_type", "block")
218+
disk_dict = ast.literal_eval(params.get("disk_dict", "{}"))
219+
nvme_disk_dict = ast.literal_eval(params.get("nvme_disk_dict", "{}"))
220+
common_options = params.get("blockcopy_common_options")
221+
specific_option = params.get("blockcopy_option", "")
222+
with_luks = "yes" == params.get("with_luks", "no")
223+
luks_extra_parameter = params.get("luks_extra_parameter", "")
224+
disk_obj = disk_base.DiskBase(test, vm, params)
225+
226+
LOG.info("TEST_STEP1: Start VM.")
227+
nvme_disk, nvme_device, nvme_partition_path, luks_sec_uuid = create_block_nvme_disk(params,
228+
nvme_disk_type,
229+
disk_obj,
230+
nvme_disk_dict)
231+
disk_info_dict = utils_misc.get_image_info(nvme_partition_path)
232+
disk_size = disk_info_dict.get("vsize")
233+
disk_size_bytes = str(disk_size) + "b"
234+
original_disk_source = disk_obj.add_vm_disk(disk_type, disk_dict, size=disk_size_bytes)
235+
LOG.debug("Original disk source: %s", original_disk_source)
236+
if vm.is_dead():
237+
vm.start()
238+
LOG.debug("The current guest xml is: %s" % (virsh.dumpxml(vm_name).stdout_text))
239+
vm.wait_for_login().close()
240+
241+
LOG.info("TEST_STEP2: Execute blockcopy command with option '%s'.", specific_option)
242+
if with_luks:
243+
cmd = ("qemu-img create -f qcow2 %s %s %s" % (luks_extra_parameter,
244+
nvme_partition_path,
245+
disk_size_bytes))
246+
process.run(cmd, shell=True, ignore_status=False)
247+
options = "--xml %s %s %s" % (nvme_disk.xml, specific_option, common_options)
248+
virsh.blockcopy(vm_name, target_dev, options, debug=True, ignore_status=False)
249+
LOG.debug("Blockcopy completed successfully.")
250+
251+
LOG.info("TEST_STEP3: Verify blockcopy result in VM XML.")
252+
verify_blockcopy_result(params, original_disk_source, nvme_partition_path)
253+
254+
if vm.is_alive():
255+
vm.destroy(gracefully=False)
256+
process.run("sgdisk --zap-all %s" % nvme_device, shell=True, ignore_status=False)
257+
disk_obj.cleanup_disk_preparation(disk_type)
258+
if with_luks:
259+
virsh.secret_undefine(luks_sec_uuid, debug=True, ignore_status=False)
260+
261+
def verify_blockcopy_result(params, original_source, nvme_dest_disk):
262+
"""
263+
Verify blockcopy result by checking VM XML
264+
265+
:param params: Dictionary containing test parameters
266+
:param original_source: Original disk source path before blockcopy operation
267+
:param nvme_dest_disk: NVME destination disk XML object used in blockcopy
268+
"""
269+
specific_option = params.get("blockcopy_option", "")
270+
target_dev = params.get("target_dev", "vdb")
271+
disk_type = params.get("disk_type", "file")
272+
nvme_disk_type = params.get("nvme_disk_type", "block")
273+
current_vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name)
274+
LOG.debug("The current guest xml after blockcopy is: %s" % current_vmxml)
275+
disk_sources = current_vmxml.get_disk_source(vm_name)
276+
disk_obj = disk_base.DiskBase(test, vm, params)
277+
if len(disk_sources) < 2:
278+
test.fail("VM doesn't have enough disks after blockcopy operation.")
279+
280+
if "--finish" in specific_option:
281+
current_disk_source = disk_obj.get_source_list(current_vmxml, disk_type, target_dev)[0]
282+
if current_disk_source != original_source:
283+
test.fail("Expected VM to keep old disk source '%s', but found '%s'" %
284+
(original_source, current_disk_source))
285+
LOG.info("PASS: VM disk kept the old disk source as expected with --finish")
286+
287+
elif "--pivot" in specific_option:
288+
current_disk_source = disk_obj.get_source_list(current_vmxml, nvme_disk_type, target_dev)[0]
289+
if current_disk_source != nvme_dest_disk:
290+
test.fail("Expected VM to pivot to nvme destination, but disk source remains unchanged: '%s'" %
291+
current_disk_source)
292+
LOG.info("PASS: VM disk pivoted to the new nvme destination as expected with --pivot")
293+
294+
vm_name = params.get("main_vm")
295+
vm = env.get_vm(vm_name)
296+
with_blockcopy = "yes" == params.get("with_blockcopy", "no")
297+
hotplug = "yes" == params.get("virt_device_hotplug")
298+
pkgs_host = params.get("pkgs_host", "")
299+
300+
vmxml_backup = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name)
301+
vmxml = vmxml_backup.copy()
302+
303+
try:
304+
# install essential package in host
305+
if not shutil.which('lspci'):
306+
test.error("package {} not installed, please install them before testing".format(pkgs_host))
307+
308+
if with_blockcopy:
309+
run_blockcopy_test()
310+
else:
311+
run_attach_detach_test()
312+
313+
except virt_vm.VMStartError as details:
314+
test.fail("VM failed to start. Error: %s" % str(details))
315+
except xcepts.LibvirtXMLError as xml_error:
316+
test.fail("VM failed to define. Error: %s" % str(xml_error))
152317
finally:
153318
if vm.is_alive():
154319
vm.destroy(gracefully=False)

0 commit comments

Comments
 (0)