This patch makes multi_disk test more flexible. You can use
stg_params variable to set all kinds of setups. As example
I added few tests for virtio_scsi disks and all_drive_format test.
stg_params is a space separated set of parameters of newly added
disks. Every parameter have to have a name and after ':'
it can be ',' separated list of words or 'range(...)'.
range() is similar to python range(), but it supports 4th argument,
which multiplies occurrence of each value:
range(0,2,1,2) => [0,0,1,1]
Also range() supports 'n' which is substituted later to fit the
number of disks. See the code for details.
Signed-off-by: Lukas Doktor <ldoktor@xxxxxxxxxx>
---
client/tests/kvm/tests/multi_disk.py | 346 ++++++++++++++++++++++++++++++----
client/virt/guest-os.cfg.sample | 6 +-
client/virt/subtests.cfg.sample | 48 +++++-
3 files changed, 363 insertions(+), 37 deletions(-)
diff --git a/client/tests/kvm/tests/multi_disk.py b/client/tests/kvm/tests/multi_disk.py
index 5062fc3..4590b94 100644
--- a/client/tests/kvm/tests/multi_disk.py
+++ b/client/tests/kvm/tests/multi_disk.py
@@ -1,52 +1,324 @@
-import logging, re, random
+import logging
+import re
+import random
+import os
from autotest.client.shared import error
-from autotest.client.virt import virt_env_process
+from autotest.client.virt.virt_env_process import preprocess
+from autotest.client.shared.base_utils import matrix_to_string
+from autotest.client.virt.virt_vm import get_image_filename
+
+re_range1 = re.compile(r'range\([ ]*([-]?\d+|n).*\)')
+re_range2 = re.compile(r',[ ]*([-]?\d+|n)')
+re_blanks = re.compile(r'^([ ]*)')
@error.context_aware
+def _range(buf, n=None):
+ """
+ Converts 'range(..)' string to range. It supports 1-4 args. It supports
+ 'n' as correct input, which is substituted to return the correct range.
+ range1-3 ... ordinary python range()
+ range4 ... multiplies the occurrence of each value
+ (range(0,4,1,2) => [0,0,1,1,2,2,3,3])
+ @raise ValueError: In case incorrect values are given.
+ @return: List of int values. In case it can't substitute 'n'
+ it returns the original string.
+ """
+ out = re_range1.match(buf)
+ if not out: return False
+ out = [out.groups()[0]]
+ out.extend(re_range2.findall(buf))
+ if 'n' in out:
+ if n is None:
+ # Don't know what to substitute, return the original
+ return buf
+ else:
+ # Doesn't cover all cases and also it works it's way...
+ n = int(n)
+ if out[0] == 'n':
+ out[0] = int(n)
+ if len(out) > 1 and out[1] == 'n':
+ out[1] = int(out[0]) + n
+ if len(out) > 2 and out[2] == 'n':
+ out[2] = (int(out[1]) - int(out[0])) / n
+ if len(out) > 3 and out[3] == 'n':
+ _len = len(range(int(out[0]), int(out[1]), int(out[2])))
+ out[3] = n / _len
+ if n % _len:
+ out[3] += 1
+ for i in range(len(out)):
+ out[i] = int(out[i])
+ if len(out) == 1:
+ out = range(out[0])
+ elif len(out) == 2:
+ out = range(out[0], out[1])
+ elif len(out) == 3:
+ out = range(out[0], out[1], out[2])
+ elif len(out) == 4:
+ # arg4 * range
+ _out = []
+ for _ in range(out[0], out[1], out[2]):
+ _out.extend([_] * out[3])
+ out = _out
+ else:
+ raise ValueError("More than 4 parameters in _range()")
+ return out
+
+@error.context_aware
+def _qtree_check(vm, session, params, root_dir):
+ """
+ Tries to find differences between qemu qtree+info vs. /proc/scsi/scsi and
+ vm params.
+
+ @param vm: VM object
+ @param session: ssh session to VM
+ @param params: Dictionary with the test parameters
+ @param root_dir: vm's root_dir (for get_image_name function)
+ """
+ err = 0
+ # check [params_names, qtree search pattern]
+ check = [['name', 'channel', 'scsiid', 'lun'],
+ ['dev-prop: drive = ', 'bus-prop: channel = ',
+ 'bus-prop: scsi-id = ', 'bus-prop: lun = ']]
+ drive_formats = ['ide', 'scsi', 'virtio-blk-pci']
+
+ # Info about disks gathered from guest
+ disks = {}
+ no_virtio_disks = 0 # virtio-blk-pci disks are not in /proc/scsi/scsi
+
+ error.context("Gather info from 'info qtree'")
+ info = vm.monitor.info('qtree').split('\n')
+ current = None
+ line = info.pop(0)
+ offset = None
+ while len(info) > 0:
+ if current is not None: # Get all info about disk
+ _offset = len(re_blanks.match(line).group(0))
+ if offset == None:
+ offset = _offset
+ elif offset != _offset:
+ # This line is about next device, store current and prepare
+ # for next one.
+ name = current.get('name')
+ if not name:
+ logging.error("Skipping disk without a name: %s", current)
+ err += 1
+ elif name in disks:
+ logging.error("Disk %s present multiple times in qtree",
+ current)
+ else:
+ if current['drive_format'] == 'virtio-blk-pci':
+ no_virtio_disks += 1
+ disks[name] = current
+ current = None
+ continue # this line have to be proceeded in next round
+ line = line[offset:]
+ for i in xrange(len(check[1])):
+ if line.startswith(check[1][i]):
+ current[check[0][i]] = line[len(check[1][i]):].strip()
+ else: # Look for block with disk specification
+ line = line.strip()
+ for fmt in drive_formats:
+ if line.startswith('dev: %s' % fmt):
+ current = {'drive_format': fmt}
+ offset = None
+ line = info.pop(0) # Next line
+
+ error.context("Gather info from 'info block'")
+ info = vm.monitor.info("block").split('\n')
+ for line in info:
+ if not line: continue
+ line = line.split(':', 1)
+ name = line[0].strip()
+ if name not in disks:
+ logging.error("disk %s is in block but not in qtree", name)
+ err += 1
+ continue
+ item = {}
+ for _ in line[1].strip().split(' '):
+ _ = _.split('=')
+ item[_[0]] = _[1]
+ if item.get('backing_file'):
+ disks[name]['snapshot'] = 'yes'
+ disks[name]['image_name'] = os.path.realpath(
+ item.get('backing_file'))
+ elif item.get('file'):
+ disks[name]['image_name'] = os.path.realpath(item.get('file'))
+ else:
+ logging.error("Can'T get info about %s disk file.", name)
+ err += 1
+ if item.get('ro') and item.get('ro') != '0':
+ disks[name]['readonly'] = 'yes'
+
+ error.context("Verify info from guest's /proc/scsi/scsi")
+ # host, channel, id, lun, vendor
+ scsis = re.findall(r'Host:\s+(\w+)\s+Channel:\s+(\d+)\s+Id:\s+(\d+)\s+'
+ 'Lun:\s+(\d+)\n\s+Vendor:\s+([a-zA-Z0-9_-]+)\s+Model: ',
+ session.cmd_output('cat /proc/scsi/scsi'))
+ if len(scsis) + no_virtio_disks != len(disks):
+ logging.error("The number of disks in qtree and /proc/scsi/scsi is not"
+ " equal.")
+ err += 1
+ _disks = {}
+ # Check only scsi disks
+ for disk in disks.copy().iteritems():
+ if disk[1]['drive_format'].startswith('scsi'):
+ _disks[disk[0]] = disk[1]
+ _ = []
+ for scsi in scsis:
+ if scsi[4].startswith('QEMU'):
+ _.append("%d-%d-%d" % (int(scsi[1]), int(scsi[2]), int(scsi[3])))
+ scsis = _
+ # Check only channel, id and lun for now
+ for disk in _disks.itervalues():
+ name = '%d-%d-%d' % (int(disk['channel']), int(disk['scsiid']),
+ int(disk['lun']))
+ if name not in scsis:
+ logging.error('Disk %s is in qtree but not in /proc/scsi/scsi.',
+ disk)
+ err += 1
+ scsis.remove(name)
+
+ error.context("Verify the info from qtree+block vs. params.")
+ _disks = disks.copy()
+ for name in params.objects('images'):
+ current = None
+ image_params = params.object_params(name)
+ image_name = os.path.realpath(get_image_filename(image_params,
+ root_dir))
+ for disk in disks.itervalues():
+ if disk.get('image_name') == image_name:
+ current = disk
+ qname = current.get('name')
+ current.pop('name')
+ break
+ if not current:
+ logging.error("Disk %s is not in qtree but is in params.", name)
+ err += 1
+ continue
+ for prop in check[0]:
+ if (image_params.get(prop) and current.get(prop) and
+ image_params.get(prop) != current.get(prop)):
+ logging.error("Disk %s's property %s=%s doesn't math params %s",
+ qname, prop, current.get(prop),
+ image_params.get(prop))
+ err += 1
+ _disks.pop(qname)
+ if _disks:
+ logging.error('Some disks were in qtree but not in autotest params: %s',
+ _disks)
+ err += 1
+
+ return err
+
+@error.context_aware
def run_multi_disk(test, params, env):
"""
Test multi disk suport of guest, this case will:
1) Create disks image in configuration file.
2) Start the guest with those disks.
- 3) Format those disks.
- 4) Copy file into / out of those disks.
- 5) Compare the original file and the copied file using md5 or fc comand.
- 6) Repeat steps 3-5 if needed.
+ 3) Checks qtree vs. test params.
+ 4) Format those disks.
+ 5) Copy file into / out of those disks.
+ 6) Compare the original file and the copied file using md5 or fc comand.
+ 7) Repeat steps 3-5 if needed.
@param test: kvm test object
@param params: Dictionary with the test parameters
@param env: Dictionary with test environment.
"""
- stg_image_num = int(params.get("stg_image_num", 0))
- stg_image_size = params.get("stg_image_size")
- stg_image_format = params.get("stg_image_format")
- stg_image_boot = params.get("stg_image_boot")
- stg_drive_format = params.get("stg_drive_format")
- stg_assign_index = params.get("stg_assign_index") == "yes"
- for num in xrange(stg_image_num):
- name = "stg%d" % num
- params["images"] = params.get("images") + " %s" % name
- params["image_name_%s" % name] = name
- if stg_image_size:
- params["image_size_%s" % name] = stg_image_size
- if stg_image_format:
- params["image_format_%s" % name] = stg_image_format
- if stg_image_boot:
- params["image_boot_%s" % name] = stg_image_boot
- if stg_drive_format:
- params["drive_format_%s" % name] = stg_drive_format
- if stg_assign_index:
- params["drive_index_%s" % name] = num
- stg_params = params.object_params(name)
- virt_env_process.preprocess_image(test, stg_params)
+ def _add_param(name, value):
+ """ Converts name+value to stg_params string """
+ if value:
+ value = re.sub(' ', '\\ ', value)
+ return "%s:%s " % (name, value)
+ else:
+ return ''
+
+ stg_image_num = 0
+ stg_params = params.get("stg_params", "")
+ # Compatibility
+ stg_params += _add_param("image_size", params.get("stg_image_size"))
+ stg_params += _add_param("image_format", params.get("stg_image_format"))
+ stg_params += _add_param("image_boot", params.get("stg_image_boot"))
+ stg_params += _add_param("drive_format", params.get("stg_drive_format"))
+ if params.get("stg_assign_index") == "yes":
+ # Assume 0 and 1 are already occupied (hd0 and cdrom)
+ stg_params += _add_param("drive_index", 'range(2,n)')
+ param_matrix = {}
+
+ stg_params = stg_params.split(' ')
+ i = 0
+ while i < len(stg_params) - 1:
+ if stg_params[i][-1] == '\\':
+ stg_params[i] = '%s %s' % (stg_params[i][:-1],
+ stg_params.pop(i + 1))
+ i += 1
+ rerange = []
+ has_name = False
+ for i in xrange(len(stg_params)):
+ if not stg_params[i].strip(): continue
+ (cmd, parm) = stg_params[i].split(':', 1)
+ if cmd == "image_name": has_name = True
+ if re_range1.match(parm):
+ parm = _range(parm)
+ if parm == False:
+ raise error.TestError("Incorrect cfg: stg_params %s looks "
+ "like range(..) but doesn't contain "
+ "numbers." % cmd)
+ param_matrix[cmd] = parm
+ if type(parm) is str:
+ # When we know the stg_image_num, substitute it.
+ rerange.append(cmd)
+ continue
+ else:
+ # ',' separated list of values
+ parm = parm.split(',')
+ j = 0
+ while j < len(parm) - 1:
+ if parm[j][-1] == '\\':
+ parm[j] = '%s,%s' % (parm[j][:-1], parm.pop(j + 1))
+ j += 1
+ param_matrix[cmd] = parm
+ stg_image_num = max(stg_image_num, len(parm))
+
+ stg_image_num = int(params.get('stg_image_num', stg_image_num))
+ for cmd in rerange:
+ param_matrix[cmd] = _range(param_matrix[cmd], stg_image_num)
+ # param_table is for pretty print of param_matrix
+ param_table = []
+ param_table_header = ['name']
+ if not has_name: param_table_header.append('image_name')
+ for _ in param_matrix:
+ param_table_header.append(_)
+
+ stg_image_name = params.get('stg_image_name', '%s')
+ for i in xrange(stg_image_num):
+ name = "stg%d" % i
+ params['images'] += " %s" % name
+ param_table.append([])
+ param_table[-1].append(name)
+ if not has_name:
+ params["image_name_%s" % name] = stg_image_name % name
+ param_table[-1].append(params.get("image_name_%s" % name))
+ for parm in param_matrix.iteritems():
+ params['%s_%s' % (parm[0], name)] = str(parm[1][i % len(parm[1])])
+ param_table[-1].append(params.get('%s_%s' % (parm[0], name)))
+
+
+ if params.get("multi_disk_params_only"):
+ # Only print the test param_matrix and finish
+ logging.info('Newly added disks:\n%s',
+ matrix_to_string(param_table, param_table_header))
+ return
+
+ # Always recreate VM (disks are already marked for deletion
+ preprocess(test, params, env)
+ logging.debug(params)
vm = env.get_vm(params["main_vm"])
- # stg_image_num is greater than 0 means there is some disk(s) added in
- # this case. and guest must be created explicitly.
- if stg_image_num > 0:
- vm.create(params=params)
- vm.verify_alive()
+ vm.create(timeout=max(10, stg_image_num))
+ #time.sleep(stg_image_num) # Add some extra time
session = vm.wait_for_login(timeout=int(params.get("login_timeout", 360)))
images = params.get("images").split()
@@ -58,6 +330,14 @@ def run_multi_disk(test, params, env):
re_str = params.get("re_str")
block_list = params.get("block_list").split()
+ error.context("verifying qtree vs. test params")
+ _ = _qtree_check(vm, session, params, vm.root_dir)
+ if _:
+ raise error.TestFail("%s errors occurred while verifying qtree vs. "
+ "params" % _)
+ if params.get('multi_disk_only_qtree') == 'yes':
+ return
+
try:
if params.get("clean_cmd"):
cmd = params.get("clean_cmd")
diff --git a/client/virt/guest-os.cfg.sample b/client/virt/guest-os.cfg.sample
index fbbd01f..3aea5a5 100644
--- a/client/virt/guest-os.cfg.sample
+++ b/client/virt/guest-os.cfg.sample
@@ -47,8 +47,8 @@ variants:
file_system = "ext3 ext2"
mount_command = mkdir /mnt/%s && mount /dev/%s /mnt/%s
umount_command = umount /dev/%s && rmdir /mnt/%s
- list_volume_command = cd /dev && \ls [vhs]d?
- re_str = "[vhs]d[a-z]"
+ list_volume_command = cd /dev && \ls [vhs]d*
+ re_str = "[vhs]d[a-z]*"
format_command = echo y | mkfs -t %s /dev/%s
copy_to_command = \cp -rf /bin/ls /mnt/%s
copy_from_command = \cp -rf /mnt/%s/ls /tmp/ls
@@ -57,7 +57,7 @@ variants:
max_disk:
stg_image_num = 27
list_volume_command = cd /dev && \ls vd*
- re_str = "[vhs]d[a-z][^0-9]"
+ re_str = "[vhs]d[a-z]*[^0-9]"
usb_multi_disk:
show_mount_cmd = mount|gawk '/mnt/{print $1}'
clean_cmd = "\rm -rf /mnt/*"
diff --git a/client/virt/subtests.cfg.sample b/client/virt/subtests.cfg.sample
index bf189e0..3a08f70 100644
--- a/client/virt/subtests.cfg.sample
+++ b/client/virt/subtests.cfg.sample
@@ -1598,10 +1598,56 @@ variants:
stg_image_num = 23
stg_image_size = 1G
stg_image_boot = no
+ stg_assign_index = yes
# other variants.
# stg_image_format = qcow2
# stg_drive_format = virtio
- # stg_assign_index = yes
+ - all_drive_format_types:
+ stg_params = "drive_format:ide,scsi,virtio,scsi-hd,usb2"
+ usbs += " default-ehci"
+ usb_type_default-ehci = usb-ehci
+ - virtio_scsi_variants:
+ # Decrease length of the command
+ stg_image_name = '/tmp/%s'
+ stg_image_size = 1M
+ stg_assign_index = yes
+ stg_params = "drive_format:scsi-disk "
+ variants:
+ - @passthrough:
+ # We need to unload scsi_debug modules used by VM
+ kill_vm = yes
+ pre_command = "modprobe scsi_debug && echo 9 > /sys/bus/pseudo/drivers/scsi_debug/add_host"
+ post_command = "rmmod scsi_debug"
+ stg_params += "image_raw_device:yes "
+ stg_params += "image_format:raw "
+ stg_params += "indirect_image_select:range(-9,0) "
+ variants:
+ - block:
+ stg_params += "image_name:/dev/sd* "
+ - generic:
+ stg_params += "drive_format:scsi-generic "
+ stg_params += "image_name:/dev/sg* "
+ - multi_lun:
+ stg_params += "drive_lun:range(256) "
+ - multi_scsiid_lun:
+ stg_params += "drive_scsiid:range(15) "
+ stg_params += "drive_lun:range(0,256,15,15) "
+ - multi_bus_scsiid_lun:
+ stg_params += "drive_bus:range(0,15,1,9) "
+ stg_params += "drive_scsiid:range(0,11,5,3) "
+ stg_params += "drive_lun:range(0,256,127) "
+ - debug_params:
+ # Remove this to execute this test-params-devel test
+ no multi_disk
+ # Dont run the actual test, only show the disk setup
+ multi_disk_params_only = yes
+ stg_image_name = '/tmp/%s'
+ stg_image_size = 1M
+ stg_params += "list_params:item1,item2,item3 "
+ stg_params += "simplerange:range(55) "
+ stg_params += "fullrange:range(first,last,step,multiple_items) "
+ stg_params += "range_0-all_disk:range(n) "
+
- clock_getres: install setup image_copy unattended_install.cdrom
only Linux
--
1.7.7.6
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
[KVM ARM]
[KVM ia64]
[KVM ppc]
[Spice Development]
[Libvirt]
[Libvirt Users]
[Linux USB Devel]
[Video for Linux]
[Linux Audio Users]
[Photo]
[Yosemite News]
[Yosemite Photos]
[Linux Kernel]
[Linux SCSI]
[XFree86]