multi_disk.py 12.7 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
from autotest.client.shared import error, utils
11
from virttest import qemu_qtree, env_process, utils_misc
12 13 14 15 16 17 18 19 20 21 22 23 24 25

_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])
L
Lucas Meneghel Rodrigues 已提交
26
    :raise ValueError: In case incorrect values are given.
L
Lucas Meneghel Rodrigues 已提交
27
    :return: List of int values. In case it can't substitute 'n'
28 29 30 31 32 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
             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
73
def run(test, params, env):
74 75 76 77
    """
    Test multi disk suport of guest, this case will:
    1) Create disks image in configuration file.
    2) Start the guest with those disks.
78 79 80 81 82 83 84
    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.
85

L
Lucas Meneghel Rodrigues 已提交
86 87 88
    :param test: QEMU test object
    :param params: Dictionary with the test parameters
    :param env: Dictionary with test environment.
89 90 91 92 93 94 95 96 97
    """
    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 ''

98 99 100 101 102 103 104
    def _do_post_cmd(session):
        cmd = params.get("post_cmd")
        if cmd:
            session.cmd_status_output(cmd)
        session.close()

    error.context("Parsing test configuration", logging.info)
105 106 107 108 109
    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"))
110
    stg_params += _add_param("image_boot", params.get("stg_image_boot", "no"))
111
    stg_params += _add_param("drive_format", params.get("stg_drive_format"))
X
Xu Tian 已提交
112
    stg_params += _add_param("drive_cache", params.get("stg_drive_cache"))
113 114 115 116 117 118 119 120 121 122 123 124 125
    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 已提交
126
                                       stg_params.pop(i + 1))
127 128 129 130 131 132 133 134 135 136 137 138
        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 已提交
139
            if parm is False:
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
                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(_)

170
    stg_image_name = params.get('stg_image_name', 'images/%s')
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    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',
                     utils.matrix_to_string(param_table, param_table_header))
        return

189
    # Always recreate VMs and disks
190
    error.context("Start the guest with new disks", logging.info)
191 192
    for vm_name in params.objects("vms"):
        vm_params = params.object_params(vm_name)
193 194
        env_process.process_images(env_process.preprocess_image, test,
                                   vm_params)
195

196
    error.context("Start the guest with those disks", logging.info)
197
    vm = env.get_vm(params["main_vm"])
198
    vm.create(timeout=max(10, stg_image_num), params=params)
199 200 201
    session = vm.wait_for_login(timeout=int(params.get("login_timeout", 360)))

    n_repeat = int(params.get("n_repeat", "1"))
202
    file_system = [_.strip() for _ in params.get("file_system").split()]
203
    cmd_timeout = float(params.get("cmd_timeout", 360))
204 205
    re_str = params["re_str"]
    black_list = params["black_list"].split()
206

207
    have_qtree = True
208
    out = vm.monitor.human_monitor_cmd("info qtree", debug=False)
209 210 211 212
    if "unknown command" in str(out):
        have_qtree = False

    if have_qtree:
213
        error.context("Verifying qtree vs. test params")
214 215 216 217
        err = 0
        qtree = qemu_qtree.QtreeContainer()
        qtree.parse_info_qtree(vm.monitor.info('qtree'))
        disks = qemu_qtree.QtreeDisksContainer(qtree.get_nodes())
218
        (tmp1, tmp2) = disks.parse_info_block(vm.monitor.info_block())
219 220 221 222
        err += tmp1 + tmp2
        err += disks.generate_params()
        err += disks.check_disk_params(params)
        (tmp1, tmp2, _, _) = disks.check_guests_proc_scsi(
L
Lucas Meneghel Rodrigues 已提交
223
            session.cmd_output('cat /proc/scsi/scsi'))
224
        err += tmp1 + tmp2
225

226 227 228 229 230
        if err:
            raise error.TestFail("%s errors occurred while verifying"
                                 " qtree vs. params" % err)
        if params.get('multi_disk_only_qtree') == 'yes':
            return
231 232

    try:
233 234
        cmd = params.get("clean_cmd")
        if cmd:
235
            session.cmd_status_output(cmd)
236 237 238 239 240 241 242 243 244 245

        cmd = params.get("pre_cmd")
        if cmd:
            error.context("Create partition on those disks", logging.info)
            s, output = session.cmd_status_output(cmd, timeout=cmd_timeout)
            if s != 0:
                raise error.TestFail("Create partition on disks failed.\n"
                                     "Output is: %s\n" % output)

        error.context("Get disks dev filenames in guest", logging.info)
246
        cmd = params["list_volume_command"]
247 248 249 250 251
        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))

252 253 254 255
        output = session.cmd_output(cmd, timeout=cmd_timeout)
        disks = re.findall(re_str, output)
        disks = map(string.strip, disks)
        disks.sort()
256 257 258
        logging.debug("Volume list that meet regular expressions: %s",
                      " ".join(disks))

259 260 261
        images = params.get("images").split()
        if len(disks) < len(images):
            logging.debug("disks: %s , images: %s", len(disks), len(images))
262 263 264
            raise error.TestFail("Fail to list all the volumes!")

        if params.get("os_type") == "linux":
265 266
            output = session.cmd_output("mount")
            li = re.findall(r"^/dev/(%s)\d*" % re_str, output, re.M)
267 268
            if li:
                black_list.extend(li)
269 270 271 272 273 274
        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
275 276 277
    except Exception:
        _do_post_cmd(session)
        raise
278

279
    try:
280 281
        for i in range(n_repeat):
            logging.info("iterations: %s", (i + 1))
282
            error.context("Format those disks in guest", logging.info)
283 284
            for disk in disks:
                disk = disk.strip()
285
                error.context("Preparing disk: %s..." % disk)
286 287

                # Random select one file system from file_system
288
                index = random.randint(0, (len(file_system) - 1))
289
                fs = file_system[index].strip()
290
                cmd = params["format_command"] % (fs, disk)
291 292
                error.context("formatting test disk")
                session.cmd(cmd, timeout=cmd_timeout)
293 294 295 296
                cmd = params.get("mount_command")
                if cmd:
                    cmd = cmd % (disk, disk, disk)
                    session.cmd(cmd)
297

298
            error.context("Cope file into / out of those disks", logging.info)
299 300 301
            for disk in disks:
                disk = disk.strip()

302
                error.context("Performing I/O on disk: %s..." % disk)
303
                cmd_list = params["cmd_list"].split()
304
                for cmd_l in cmd_list:
305 306 307
                    cmd = params.get(cmd_l)
                    if cmd:
                        session.cmd(cmd % disk, timeout=cmd_timeout)
308

309
                cmd = params["compare_command"]
310
                key_word = params["check_result_key_word"]
311
                output = session.cmd_output(cmd)
312
                if key_word not in output:
313 314 315 316 317 318 319 320 321 322
                    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()
323 324
                    error.context("Unmounting disk: %s..." % disk)
                    cmd = params.get("umount_command") % (disk, disk)
325 326
                    session.cmd(cmd)
    finally:
327 328 329 330 331 332 333 334 335 336 337 338 339 340
        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:
                    error.context("Unmounting disk: %s..." % disk)
                    cmd = params["umount_command"] % (disk, disk)
                    session.cmd(cmd)
            except Exception, err:
                logging.warn("Get error when cleanup, '%s'", err)

        _do_post_cmd(session)
341
        session.close()