multi_disk.py 14.3 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 14 15
from autotest.client.shared import error

from virttest import env_process
16 17
from virttest import error_context
from virttest import qemu_qtree
18
from virttest import utils_misc
19 20 21 22 23 24

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


25
@error_context.context_aware
26 27 28 29 30 31 32
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 已提交
33
    :raise ValueError: In case incorrect values are given.
L
Lucas Meneghel Rodrigues 已提交
34
    :return: List of int values. In case it can't substitute 'n'
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 77 78
             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


79
@error_context.context_aware
80
def run(test, params, env):
81 82 83 84
    """
    Test multi disk suport of guest, this case will:
    1) Create disks image in configuration file.
    2) Start the guest with those disks.
85 86 87 88 89 90 91
    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.
92

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

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

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    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"

        regex_str = 'Disk (\d+).*?%s.*?%s' % (disk_size, disk_size)
        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])

132
    error_context.context("Parsing test configuration", logging.info)
133 134 135 136 137
    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"))
138
    stg_params += _add_param("image_boot", params.get("stg_image_boot", "no"))
139
    stg_params += _add_param("drive_format", params.get("stg_drive_format"))
X
Xu Tian 已提交
140
    stg_params += _add_param("drive_cache", params.get("stg_drive_cache"))
141 142 143 144 145 146 147 148 149 150 151 152 153
    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 已提交
154
                                       stg_params.pop(i + 1))
155 156 157 158 159 160 161 162 163 164 165 166
        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 已提交
167
            if parm is False:
168 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 196 197
                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* 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(_)

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

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

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

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

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

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

256 257 258 259 260
        if err:
            raise error.TestFail("%s errors occurred while verifying"
                                 " qtree vs. params" % err)
        if params.get('multi_disk_only_qtree') == 'yes':
            return
261 262

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

267
        if params.get("os_type") == "windows":
268
            error_context.context("Create partition on those disks", logging.info)
269 270
            # Get the disk index
            _get_disk_index(session, stg_image_size, disk_indexs)
271 272 273 274
            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)
                raise error.TestFail("Fail to list all the volumes, %s" % err_msg)
275 276 277 278 279 280 281

            # 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)
282

283
        error_context.context("Get disks dev filenames in guest", logging.info)
284
        cmd = params["list_volume_command"]
285 286 287 288 289
        s, output = session.cmd_status_output(cmd, timeout=cmd_timeout)
        if s != 0:
            raise error.TestFail("List volume command failed with cmd '%s'.\n"
                                 "Output is: %s\n" % (cmd, output))

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

297 298 299
        images = params.get("images").split()
        if len(disks) < len(images):
            logging.debug("disks: %s , images: %s", len(disks), len(images))
300 301 302
            raise error.TestFail("Fail to list all the volumes!")

        if params.get("os_type") == "linux":
303 304
            output = session.cmd_output("mount")
            li = re.findall(r"^/dev/(%s)\d*" % re_str, output, re.M)
305 306
            if li:
                black_list.extend(li)
307 308 309 310 311 312
        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
313 314 315
    except Exception:
        _do_post_cmd(session)
        raise
316

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

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

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

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

347
                cmd = params["compare_command"]
348
                key_word = params["check_result_key_word"]
349
                output = session.cmd_output(cmd)
350
                if key_word not in output:
351 352 353 354 355 356 357 358 359 360
                    raise error.TestFail("Files on guest os root fs and disk "
                                         "differ")

            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()
361
                    error_context.context("Unmounting disk: %s..." % disk)
362
                    cmd = params.get("umount_command") % (disk, disk)
363 364
                    session.cmd(cmd)
    finally:
365 366 367 368 369 370 371
        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:
372
                    error_context.context("Unmounting disk: %s..." % disk)
373 374 375 376 377 378
                    cmd = params["umount_command"] % (disk, disk)
                    session.cmd(cmd)
            except Exception, err:
                logging.warn("Get error when cleanup, '%s'", err)

        _do_post_cmd(session)