multi_disk.py 14.1 KB
Newer Older
1 2 3
"""
multi_disk test for Autotest framework.

L
Lucas Meneghel Rodrigues 已提交
4
:copyright: 2011-2012 Red Hat Inc.
5
"""
L
Lucas Meneghel Rodrigues 已提交
6 7 8 9
import logging
import re
import random
import string
10

11 12
from avocado.utils import astring

13
from virttest import env_process
14 15
from virttest import error_context
from virttest import qemu_qtree
16
from virttest import utils_misc
17 18 19 20 21 22

_RE_RANGE1 = re.compile(r'range\([ ]*([-]?\d+|n).*\)')
_RE_RANGE2 = re.compile(r',[ ]*([-]?\d+|n)')
_RE_BLANKS = re.compile(r'^([ ]*)')


23
@error_context.context_aware
24 25 26 27 28 29 30
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])
L
Lucas Meneghel Rodrigues 已提交
31
    :raise ValueError: In case incorrect values are given.
L
Lucas Meneghel Rodrigues 已提交
32
    :return: List of int values. In case it can't substitute 'n'
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
             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


77
@error_context.context_aware
78
def run(test, params, env):
79 80 81 82
    """
    Test multi disk suport of guest, this case will:
    1) Create disks image in configuration file.
    2) Start the guest with those disks.
83 84 85 86 87 88 89
    3) Checks qtree vs. test params. (Optional)
    4) Create partition on those disks.
    5) Get disk dev filenames in guest.
    6) Format those disks in guest.
    7) Copy file into / out of those disks.
    8) Compare the original file and the copied file using md5 or fc comand.
    9) Repeat steps 3-5 if needed.
90

L
Lucas Meneghel Rodrigues 已提交
91 92 93
    :param test: QEMU test object
    :param params: Dictionary with the test parameters
    :param env: Dictionary with test environment.
94 95 96 97 98 99 100 101 102
    """
    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 ''

103 104 105 106 107 108
    def _do_post_cmd(session):
        cmd = params.get("post_cmd")
        if cmd:
            session.cmd_status_output(cmd)
        session.close()

109 110 111 112 113 114 115 116 117 118 119 120 121 122
    def _get_disk_index(session, image_size, disk_indexs):
        list_disk_cmd = "echo list disk > disk && "
        list_disk_cmd += "echo exit >> disk && diskpart /s disk"
        disks = session.cmd_output(list_disk_cmd)
        size_type = image_size[-1] + "B"
        disk_size = ""

        if size_type == "MB":
            disk_size = image_size[:-1] + " MB"
        elif size_type == "GB" and int(image_size[:-1]) < 8:
            disk_size = str(int(image_size[:-1])*1024) + " MB"
        else:
            disk_size = image_size[:-1] + " GB"

X
Xu Han 已提交
123
        regex_str = r'Disk (\d+).*?%s.*?%s' % (disk_size, disk_size)
124 125 126 127 128 129
        for disk in disks.splitlines():
            if disk.startswith("  Disk"):
                o = re.findall(regex_str, disk, re.I | re.M)
                if o:
                    disk_indexs.append(o[0])

130
    error_context.context("Parsing test configuration", logging.info)
131 132 133 134 135
    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"))
136
    stg_params += _add_param("image_boot", params.get("stg_image_boot", "no"))
137
    stg_params += _add_param("drive_format", params.get("stg_drive_format"))
X
Xu Tian 已提交
138
    stg_params += _add_param("drive_cache", params.get("stg_drive_cache"))
139 140 141 142 143 144 145 146 147 148 149 150 151
    if params.get("stg_assign_index") != "no":
        # 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 not stg_params[i].strip():
            i += 1
            continue
        if stg_params[i][-1] == '\\':
            stg_params[i] = '%s %s' % (stg_params[i][:-1],
L
Lucas Meneghel Rodrigues 已提交
152
                                       stg_params.pop(i + 1))
153 154 155 156 157 158 159 160 161 162 163 164
        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)
L
Lucas Meneghel Rodrigues 已提交
165
            if parm is False:
X
Xu Han 已提交
166 167 168
                test.error("Incorrect cfg: stg_params %s looks "
                           "like range(..) but doesn't contain "
                           "numbers." % cmd)
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
            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* are 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(_)

196
    stg_image_name = params.get('stg_image_name', 'images/%s')
197 198 199 200 201 202 203 204
    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))
X
Xu Han 已提交
205
        for parm in param_matrix.items():
206 207 208 209 210 211
            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") == 'yes':
        # Only print the test param_matrix and finish
        logging.info('Newly added disks:\n%s',
212
                     astring.tabular_output(param_table, param_table_header))
213 214
        return

215
    # Always recreate VMs and disks
216
    error_context.context("Start the guest with new disks", logging.info)
217 218
    for vm_name in params.objects("vms"):
        vm_params = params.object_params(vm_name)
219 220
        env_process.process_images(env_process.preprocess_image, test,
                                   vm_params)
221

222
    error_context.context("Start the guest with those disks", logging.info)
223
    vm = env.get_vm(params["main_vm"])
224
    vm.create(timeout=max(10, stg_image_num), params=params)
225 226 227
    session = vm.wait_for_login(timeout=int(params.get("login_timeout", 360)))

    n_repeat = int(params.get("n_repeat", "1"))
228
    file_system = [_.strip() for _ in params.get("file_system").split()]
229
    cmd_timeout = float(params.get("cmd_timeout", 360))
230 231
    re_str = params["re_str"]
    black_list = params["black_list"].split()
232 233
    stg_image_size = params.get("stg_image_size")
    disk_indexs = []
234

235
    have_qtree = True
236
    out = vm.monitor.human_monitor_cmd("info qtree", debug=False)
237 238 239
    if "unknown command" in str(out):
        have_qtree = False

240
    if (params.get("check_guest_proc_scsi") == "yes") and have_qtree:
241
        error_context.context("Verifying qtree vs. test params")
242 243 244 245
        err = 0
        qtree = qemu_qtree.QtreeContainer()
        qtree.parse_info_qtree(vm.monitor.info('qtree'))
        disks = qemu_qtree.QtreeDisksContainer(qtree.get_nodes())
246
        (tmp1, tmp2) = disks.parse_info_block(vm.monitor.info_block())
247 248 249 250
        err += tmp1 + tmp2
        err += disks.generate_params()
        err += disks.check_disk_params(params)
        (tmp1, tmp2, _, _) = disks.check_guests_proc_scsi(
L
Lucas Meneghel Rodrigues 已提交
251
            session.cmd_output('cat /proc/scsi/scsi'))
252
        err += tmp1 + tmp2
253

254
        if err:
X
Xu Han 已提交
255 256
            test.fail("%s errors occurred while verifying"
                      " qtree vs. params" % err)
257 258
        if params.get('multi_disk_only_qtree') == 'yes':
            return
259 260

    try:
261 262
        cmd = params.get("clean_cmd")
        if cmd:
263
            session.cmd_status_output(cmd)
264

265
        if params.get("os_type") == "windows":
266
            error_context.context("Create partition on those disks", logging.info)
267 268
            # Get the disk index
            _get_disk_index(session, stg_image_size, disk_indexs)
269 270 271
            if len(disk_indexs) < stg_image_num:
                err_msg = "Set disks num: %d" % stg_image_num
                err_msg += ", Get disks num in guest: %d" % len(disk_indexs)
X
Xu Han 已提交
272
                test.fail("Fail to list all the volumes, %s" % err_msg)
273 274 275 276 277 278 279

            # Random select one file system from file_system
            index = random.randint(0, (len(file_system) - 1))
            fs_type = file_system[index].strip()
            for i in xrange(stg_image_num):
                utils_misc.format_windows_disk(session, disk_indexs[i], None,
                                               None, fs_type)
280

281
        error_context.context("Get disks dev filenames in guest", logging.info)
282
        cmd = params["list_volume_command"]
283 284
        s, output = session.cmd_status_output(cmd, timeout=cmd_timeout)
        if s != 0:
X
Xu Han 已提交
285 286
            test.fail("List volume command failed with cmd '%s'.\n"
                      "Output is: %s\n" % (cmd, output))
287

288 289 290 291
        output = session.cmd_output(cmd, timeout=cmd_timeout)
        disks = re.findall(re_str, output)
        disks = map(string.strip, disks)
        disks.sort()
292 293 294
        logging.debug("Volume list that meet regular expressions: %s",
                      " ".join(disks))

295 296 297
        images = params.get("images").split()
        if len(disks) < len(images):
            logging.debug("disks: %s , images: %s", len(disks), len(images))
X
Xu Han 已提交
298
            test.fail("Fail to list all the volumes!")
299 300

        if params.get("os_type") == "linux":
301 302
            output = session.cmd_output("mount")
            li = re.findall(r"^/dev/(%s)\d*" % re_str, output, re.M)
303 304
            if li:
                black_list.extend(li)
305 306 307 308 309 310
        else:
            black_list.extend(utils_misc.get_winutils_vol(session))
        disks = set(disks)
        black_list = set(black_list)
        logging.info("No need to check volume '%s'", (disks & black_list))
        disks = disks - black_list
311 312 313
    except Exception:
        _do_post_cmd(session)
        raise
314

315
    try:
316 317
        for i in range(n_repeat):
            logging.info("iterations: %s", (i + 1))
318
            error_context.context("Format those disks in guest", logging.info)
319 320
            for disk in disks:
                disk = disk.strip()
321
                error_context.context("Preparing disk: %s..." % disk)
322 323

                # Random select one file system from file_system
324
                index = random.randint(0, (len(file_system) - 1))
325
                fs = file_system[index].strip()
326
                cmd = params["format_command"] % (fs, disk)
327
                error_context.context("formatting test disk")
328
                session.cmd(cmd, timeout=cmd_timeout)
329 330 331 332
                cmd = params.get("mount_command")
                if cmd:
                    cmd = cmd % (disk, disk, disk)
                    session.cmd(cmd)
333

334
            error_context.context("Cope file into / out of those disks", logging.info)
335 336 337
            for disk in disks:
                disk = disk.strip()

338
                error_context.context("Performing I/O on disk: %s..." % disk)
339
                cmd_list = params["cmd_list"].split()
340
                for cmd_l in cmd_list:
341 342 343
                    cmd = params.get(cmd_l)
                    if cmd:
                        session.cmd(cmd % disk, timeout=cmd_timeout)
344

345
                cmd = params["compare_command"]
346
                key_word = params["check_result_key_word"]
347
                output = session.cmd_output(cmd)
348
                if key_word not in output:
X
Xu Han 已提交
349
                    test.fail("Files on guest os root fs and disk differ")
350 351 352 353 354 355 356 357

            if params.get("umount_command"):
                cmd = params.get("show_mount_cmd")
                output = session.cmd_output(cmd)
                disks = re.findall(re_str, output)
                disks.sort()
                for disk in disks:
                    disk = disk.strip()
358
                    error_context.context("Unmounting disk: %s..." % disk)
359
                    cmd = params.get("umount_command") % (disk, disk)
360 361
                    session.cmd(cmd)
    finally:
362 363 364 365 366 367 368
        cmd = params.get("show_mount_cmd")
        if cmd:
            try:
                output = session.cmd_output(cmd)
                disks = re.findall(re_str, output)
                disks.sort()
                for disk in disks:
369
                    error_context.context("Unmounting disk: %s..." % disk)
370 371 372 373 374 375
                    cmd = params["umount_command"] % (disk, disk)
                    session.cmd(cmd)
            except Exception, err:
                logging.warn("Get error when cleanup, '%s'", err)

        _do_post_cmd(session)