qemu_img.py 26.6 KB
Newer Older
L
Lucas Meneghel Rodrigues 已提交
1
import os
2 3
import time
import re
L
Lucas Meneghel Rodrigues 已提交
4
import logging
5 6
import shutil
import tempfile
7
import signal
8

X
Xu Han 已提交
9
from avocado.utils import process
10

X
Xu Han 已提交
11
from virttest import error_context
12 13 14 15 16
from virttest import utils_misc
from virttest import env_process
from virttest import storage
from virttest import data_dir
from virttest import gluster
17 18


X
Xu Han 已提交
19
@error_context.context_aware
20
def run(test, params, env):
21 22 23 24 25
    """
    'qemu-img' functions test:
    1) Judge what subcommand is going to be tested
    2) Run subcommand test

L
Lucas Meneghel Rodrigues 已提交
26 27 28
    :param test: QEMU test object
    :param params: Dictionary with the test parameters
    :param env: Dictionary with test environment.
29
    """
30 31
    qemu_img_binary = utils_misc.get_qemu_img_binary(params)
    cmd = qemu_img_binary
32
    if not os.path.exists(cmd):
X
Xu Han 已提交
33
        test.error("Binary of 'qemu-img' not found")
34
    image_format = params["image_format"]
35
    image_size = params.get("image_size", "10G")
F
Feng Yang 已提交
36
    enable_gluster = params.get("enable_gluster", "no") == "yes"
37
    image_name = storage.get_image_filename(params, data_dir.get_data_dir())
38

F
Feng Yang 已提交
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
    def remove(path):
        try:
            os.remove(path)
        except OSError:
            pass

    def _get_image_filename(img_name, enable_gluster=False, img_fmt=None):
        """
        Generate an image path.

        :param image_name: Force name of image.
        :param enable_gluster: Enable gluster or not.
        :param image_format: Format for image.
        """
        if enable_gluster:
            gluster_uri = gluster.create_gluster_uri(params)
            image_filename = "%s%s" % (gluster_uri, img_name)
            if img_fmt:
                image_filename += ".%s" % img_fmt
        else:
            if img_fmt:
                img_name = "%s.%s" % (img_name, img_fmt)
            image_filename = utils_misc.get_path(data_dir.get_data_dir(),
                                                 img_name)
        return image_filename

65 66 67 68
    def _check(cmd, img):
        """
        Simple 'qemu-img check' function implementation.

L
Lucas Meneghel Rodrigues 已提交
69 70
        :param cmd: qemu-img base command.
        :param img: image to be checked
71 72
        """
        cmd += " check %s" % img
X
Xu Han 已提交
73 74
        error_context.context("Checking image '%s' by command '%s'"
                              % (img, cmd), logging.info)
75
        try:
76
            output = process.system_output(cmd, verbose=False).decode()
X
Xu Han 已提交
77
        except process.CmdError as err:
78 79
            result_stderr = err.result.stderr
            if "does not support checks" in result_stderr:
80 81
                return (True, "")
            else:
82
                return (False, result_stderr)
83 84 85 86 87 88 89 90 91
        return (True, output)

    def check_test(cmd):
        """
        Subcommand 'qemu-img check' test.

        This tests will 'dd' to create a specified size file, and check it.
        Then convert it to supported image_format in each loop and check again.

L
Lucas Meneghel Rodrigues 已提交
92
        :param cmd: qemu-img base command.
93
        """
F
Feng Yang 已提交
94 95
        test_image = _get_image_filename(params["image_name_dd"],
                                         enable_gluster)
96
        create_image_cmd = params["create_image_cmd"]
97
        create_image_cmd = create_image_cmd % test_image
98
        msg = " Create image %s by command %s" % (test_image, create_image_cmd)
X
Xu Han 已提交
99
        error_context.context(msg, logging.info)
X
Xu Han 已提交
100
        process.system(create_image_cmd, verbose=False, shell=True)
101 102
        status, output = _check(cmd, test_image)
        if not status:
X
Xu Han 已提交
103 104
            test.fail("Check image '%s' failed with error: %s" %
                      (test_image, output))
105
        for fmt in params["supported_image_formats"].split():
106 107
            output_image = test_image + ".%s" % fmt
            _convert(cmd, fmt, test_image, output_image)
108 109
            status, output = _check(cmd, output_image)
            if not status:
X
Xu Han 已提交
110 111
                test.fail("Check image '%s' got error: %s" %
                          (output_image, output))
F
Feng Yang 已提交
112 113
            remove(output_image)
        remove(test_image)
114 115

    def _create(cmd, img_name, fmt, img_size=None, base_img=None,
116
                base_img_fmt=None, encrypted="no",
117
                preallocated="off", cluster_size=None):
118 119 120
        """
        Simple wrapper of 'qemu-img create'

L
Lucas Meneghel Rodrigues 已提交
121 122 123 124 125 126 127
        :param cmd: qemu-img base command.
        :param img_name: name of the image file
        :param fmt: image format
        :param img_size:  image size
        :param base_img: base image if create a snapshot image
        :param base_img_fmt: base image format if create a snapshot image
        :param encrypted: indicates whether the created image is encrypted
128 129 130
        :param preallocated: if preallocation when create image,
                             allowed values: off, metadata. Default is "off"
        :param cluster_size: the cluster size for the image
131 132
        """
        cmd += " create"
133

134 135 136 137 138 139
        if encrypted == "yes":
            cmd += " -e"
        if base_img:
            cmd += " -b %s" % base_img
            if base_img_fmt:
                cmd += " -F %s" % base_img_fmt
140

141
        cmd += " -f %s" % fmt
142 143 144 145 146 147 148

        options = []
        if preallocated != "off":
            options.append("preallocation=%s" % preallocated)
        if cluster_size is not None:
            options.append("cluster_size=%s" % cluster_size)
        if options:
149
            cmd += " -o %s" % ",".join(options)
150

151 152 153
        cmd += " %s" % img_name
        if img_size:
            cmd += " %s" % img_size
154

155
        msg = "Creating image %s by command %s" % (img_name, cmd)
X
Xu Han 已提交
156 157
        error_context.context(msg, logging.info)
        process.system(cmd, verbose=False)
158 159
        status, out = _check(qemu_img_binary, img_name)
        if not status:
X
Xu Han 已提交
160
            test.fail("Check image '%s' got error: %s" % (img_name, out))
161 162 163 164 165

    def create_test(cmd):
        """
        Subcommand 'qemu-img create' test.

L
Lucas Meneghel Rodrigues 已提交
166
        :param cmd: qemu-img base command.
167
        """
168
        image_large = params["image_name_large"]
169 170
        device = params.get("device")
        if not device:
F
Feng Yang 已提交
171 172
            img = _get_image_filename(image_large, enable_gluster,
                                      image_format)
173 174
        else:
            img = device
175
        _create(cmd, img_name=img, fmt=image_format,
176
                img_size=params["image_size_large"],
177 178
                preallocated=params.get("preallocated", "off"),
                cluster_size=params.get("image_cluster_size"))
F
Feng Yang 已提交
179
        remove(img)
180

181 182 183 184 185 186 187 188 189
    def send_signal(timeout=360):
        """
        send signal "SIGUSR1" to qemu-img without the option -p
        to report progress
        """
        logging.info("Send signal to qemu-img")
        end_time = time.time() + timeout
        while time.time() < end_time:
            time.sleep(1)
190 191 192 193 194 195 196 197 198 199
            pid = process.system_output("pidof qemu-img",
                                        ignore_status=True,
                                        verbose=False).decode().strip()
            if bool(pid):
                break
        try:
            os.kill(int(pid), signal.SIGUSR1)
        except Exception as error:
            logging.info("Failed to send signal to qemu-img")
            test.error(str(error))
200 201 202 203 204 205 206 207 208

    def check_command_output(CmdResult):
        """
        Check standard error or standard output of command
        : param CmdResult: a list of CmdResult objects
        """
        logging.info("Check result of command")
        check_output = params.get("check_output", "exit_status")
        if not hasattr(CmdResult, check_output):
X
Xu Han 已提交
209
            test.error("Unknown check output '%s'" % check_output)
210
        output = getattr(CmdResult, check_output)
211 212 213 214
        if check_output == "exit_status" and output == 0:
            return None
        if check_output == "exit_status" and output != 0:
            err_msg = "Get nonzero exit status(%d) '%s'"
X
Xu Han 已提交
215
            test.fail(err_msg % (output, CmdResult.command))
216
        pattern = params.get("command_result_pattern")
217
        if not re.findall(pattern, output.decode()):
218 219
            err_msg = "Fail to get expected result!"
            err_msg += "Output: %s, expected pattern: %s" % (output, pattern)
X
Xu Han 已提交
220
            test.fail(err_msg)
221

222
    def _convert(cmd, output_fmt, img_name, output_filename,
L
Lucas Meneghel Rodrigues 已提交
223
                 fmt=None, compressed="no", encrypted="no"):
224 225 226
        """
        Simple wrapper of 'qemu-img convert' function.

L
Lucas Meneghel Rodrigues 已提交
227 228 229 230 231 232 233
        :param cmd: qemu-img base command.
        :param output_fmt: the output format of converted image
        :param img_name: image name that to be converted
        :param output_filename: output image name that converted
        :param fmt: output image format
        :param compressed: whether output image is compressed
        :param encrypted: whether output image is encrypted
234 235 236 237 238 239
        """
        cmd += " convert"
        if compressed == "yes":
            cmd += " -c"
        if encrypted == "yes":
            cmd += " -e"
240 241 242
        show_progress = params.get("show_progress", "")
        if show_progress == "on":
            cmd += " -p"
243 244 245
        if fmt:
            cmd += " -f %s" % fmt
        cmd += " -O %s" % output_fmt
246 247 248 249 250 251 252 253
        options = params.get("qemu_img_options")
        if options:
            options = options.split()
            cmd += " -o "
            for option in options:
                value = params.get(option)
                cmd += "%s=%s," % (option, value)
            cmd = cmd.rstrip(",")
254
        cmd += " %s %s" % (img_name, output_filename)
255 256
        msg = "Converting '%s' from format '%s'" % (img_name, fmt)
        msg += " to '%s'" % output_fmt
X
Xu Han 已提交
257
        error_context.context(msg, logging.info)
258
        if show_progress == "off":
X
Xu Han 已提交
259
            bg = utils_misc.InterruptedThread(send_signal)
260
            bg.start()
X
Xu Han 已提交
261
        check_command_output(process.run(cmd, ignore_status=True))
262 263 264 265 266

    def convert_test(cmd):
        """
        Subcommand 'qemu-img convert' test.

L
Lucas Meneghel Rodrigues 已提交
267
        :param cmd: qemu-img base command.
268
        """
269
        dest_img_fmt = params["dest_image_format"]
270 271
        output_filename = "%s.converted_%s.%s" % (image_name,
                                                  dest_img_fmt, dest_img_fmt)
272 273

        _convert(cmd, dest_img_fmt, image_name, output_filename,
L
Lucas Meneghel Rodrigues 已提交
274
                 image_format, params["compressed"], params["encrypted"])
275 276 277 278
        orig_img_name = params.get("image_name")
        img_name = "%s.%s.converted_%s" % (orig_img_name,
                                           image_format, dest_img_fmt)
        _boot(img_name, dest_img_fmt)
279 280

        if dest_img_fmt == "qcow2":
281 282
            status, output = _check(cmd, output_filename)
            if status:
F
Feng Yang 已提交
283
                remove(output_filename)
284
            else:
X
Xu Han 已提交
285 286
                test.fail("Check image '%s' failed with error: %s" %
                          (output_filename, output))
287
        else:
F
Feng Yang 已提交
288
            remove(output_filename)
289 290 291 292 293

    def _info(cmd, img, sub_info=None, fmt=None):
        """
        Simple wrapper of 'qemu-img info'.

L
Lucas Meneghel Rodrigues 已提交
294 295 296 297
        :param cmd: qemu-img base command.
        :param img: image file
        :param sub_info: sub info, say 'backing file'
        :param fmt: image format
298 299 300 301 302 303 304
        """
        cmd += " info"
        if fmt:
            cmd += " -f %s" % fmt
        cmd += " %s" % img

        try:
305
            output = process.system_output(cmd).decode()
X
Xu Han 已提交
306
        except process.CmdError as err:
307
            logging.error("Get info of image '%s' failed: %s", img, str(err))
308 309 310 311 312 313 314
            return None

        if not sub_info:
            return output

        sub_info += ": (.*)"
        matches = re.findall(sub_info, output)
315 316 317
        if "virtual size" in sub_info:
            p = re.compile(r'\.0*(G|K)$')
            return p.sub(r'\1', matches[0].split()[0])
318 319 320 321 322 323 324 325
        if matches:
            return matches[0]
        return None

    def info_test(cmd):
        """
        Subcommand 'qemu-img info' test.

L
Lucas Meneghel Rodrigues 已提交
326
        :param cmd: qemu-img base command.
327 328 329
        """
        img_info = _info(cmd, image_name)
        logging.info("Info of image '%s':\n%s", image_name, img_info)
330
        if image_format not in img_info:
X
Xu Han 已提交
331 332
            test.fail("Got unexpected format of image '%s'"
                      " in info test" % image_name)
333
        if image_size not in img_info:
X
Xu Han 已提交
334 335
            test.fail("Got unexpected size of image '%s'"
                      " in info test" % image_name)
336 337 338 339 340

    def snapshot_test(cmd):
        """
        Subcommand 'qemu-img snapshot' test.

L
Lucas Meneghel Rodrigues 已提交
341
        :param cmd: qemu-img base command.
342 343 344 345 346 347
        """
        cmd += " snapshot"
        for i in range(2):
            crtcmd = cmd
            sn_name = "snapshot%d" % i
            crtcmd += " -c %s %s" % (sn_name, image_name)
348
            msg = "Created snapshot '%s' in '%s' by command %s" % (sn_name,
F
Feng Yang 已提交
349 350
                                                                   image_name,
                                                                   crtcmd)
X
Xu Han 已提交
351
            error_context.context(msg, logging.info)
X
Xu Han 已提交
352
            cmd_result = process.run(crtcmd, verbose=False, ignore_status=True)
353
            status, output = cmd_result.exit_status, cmd_result.stdout.decode()
354
            if status != 0:
X
Xu Han 已提交
355 356
                test.fail("Create snapshot failed via command: %s;"
                          "Output is: %s" % (crtcmd, output))
357 358
        listcmd = cmd
        listcmd += " -l %s" % image_name
X
Xu Han 已提交
359
        cmd_result = process.run(listcmd, verbose=False, ignore_status=True)
360
        status, out = cmd_result.exit_status, cmd_result.stdout.decode()
361
        if not ("snapshot0" in out and "snapshot1" in out and status == 0):
X
Xu Han 已提交
362 363
            test.fail("Snapshot created failed or missed;"
                      "snapshot list is: \n%s" % out)
364 365 366 367
        for i in range(2):
            sn_name = "snapshot%d" % i
            delcmd = cmd
            delcmd += " -d %s %s" % (sn_name, image_name)
368
            msg = "Delete snapshot '%s' by command %s" % (sn_name, delcmd)
X
Xu Han 已提交
369
            error_context.context(msg, logging.info)
X
Xu Han 已提交
370
            cmd_result = process.run(delcmd, verbose=False, ignore_status=True)
371
            status, output = cmd_result.exit_status, cmd_result.stdout.decode()
372
            if status != 0:
X
Xu Han 已提交
373 374
                test.fail("Delete snapshot '%s' failed: %s" %
                          (sn_name, output))
375 376 377 378

    def commit_test(cmd):
        """
        Subcommand 'qemu-img commit' test.
C
Cong Li 已提交
379 380 381
        1) Create a overlay file of the qemu harddisk specified by image_name.
        2) Start a VM using the overlay file as its harddisk.
        3) Touch a file "commit_testfile" in the overlay file, and shutdown the
382
           VM.
C
Cong Li 已提交
383
        4) Commit the change to the backing harddisk by executing
384
           "qemu-img commit" command.
C
Cong Li 已提交
385 386
        5) Start the VM using the backing harddisk.
        6) Check if the file "commit_testfile" exists.
387

L
Lucas Meneghel Rodrigues 已提交
388
        :param cmd: qemu-img base command.
389 390 391
        """

        logging.info("Commit testing started!")
F
Feng Yang 已提交
392 393 394
        image_name = storage.get_image_filename(params,
                                                data_dir.get_data_dir())
        pre_name = '.'.join(image_name.split('.')[:-1])
395
        image_format = params.get("image_format", "qcow2")
F
Feng Yang 已提交
396
        overlay_file_name = "%s_overlay.%s" % (pre_name, image_format)
397 398 399 400 401 402 403 404
        file_create_cmd = params.get("file_create_cmd",
                                     "touch /commit_testfile")
        file_info_cmd = params.get("file_info_cmd",
                                   "ls / | grep commit_testfile")
        file_exist_chk_cmd = params.get("file_exist_chk_cmd",
                                        "[ -e /commit_testfile ] && echo $?")
        file_del_cmd = params.get("file_del_cmd",
                                  "rm -f /commit_testfile")
405
        try:
C
Cong Li 已提交
406
            # Remove the existing overlay file
F
Feng Yang 已提交
407 408
            if os.path.isfile(overlay_file_name):
                remove(overlay_file_name)
409

C
Cong Li 已提交
410
            # Create the new overlay file
F
Feng Yang 已提交
411 412 413
            create_cmd = "%s create -b %s -f %s %s" % (cmd, image_name,
                                                       image_format,
                                                       overlay_file_name)
C
Cong Li 已提交
414
            msg = "Create overlay file by command: %s" % create_cmd
X
Xu Han 已提交
415
            error_context.context(msg, logging.info)
416
            try:
X
Xu Han 已提交
417 418 419
                process.system(create_cmd, verbose=False)
            except process.CmdError:
                test.fail("Could not create a overlay file!")
420
            logging.info("overlay file (%s) created!" % overlay_file_name)
421

C
Cong Li 已提交
422
            # Set the qemu harddisk to the overlay file
L
Lucas Meneghel Rodrigues 已提交
423 424
            logging.info(
                "Original image_name is: %s", params.get('image_name'))
425
            params['image_name'] = '.'.join(overlay_file_name.split('.')[:-1])
426 427 428
            logging.info("Param image_name changed to: %s",
                         params.get('image_name'))

C
Cong Li 已提交
429
            msg = "Start a new VM, using overlay file as its harddisk"
X
Xu Han 已提交
430
            error_context.context(msg, logging.info)
431
            vm_name = params['main_vm']
432 433
            env_process.preprocess_vm(test, params, env, vm_name)
            vm = env.get_vm(vm_name)
434
            vm.verify_alive()
435 436 437
            timeout = int(params.get("login_timeout", 360))
            session = vm.wait_for_login(timeout=timeout)

C
Cong Li 已提交
438
            # Do some changes to the overlay_file harddisk
439
            try:
440 441 442 443
                output = session.cmd(file_create_cmd)
                logging.info("Output of %s: %s", file_create_cmd, output)
                output = session.cmd(file_info_cmd)
                logging.info("Output of %s: %s", file_info_cmd, output)
X
Xu Han 已提交
444
            except Exception as err:
X
Xu Han 已提交
445 446
                test.fail("Could not create commit_testfile in the "
                          "overlay file %s" % err)
447 448
            vm.destroy()

L
Lucas Meneghel Rodrigues 已提交
449
            # Execute the commit command
F
Feng Yang 已提交
450 451
            cmitcmd = "%s commit -f %s %s" % (cmd, image_format,
                                              overlay_file_name)
X
Xu Han 已提交
452 453
            error_context.context("Committing image by command %s" % cmitcmd,
                                  logging.info)
454
            try:
X
Xu Han 已提交
455 456 457
                process.system(cmitcmd, verbose=False)
            except process.CmdError:
                test.fail("Could not commit the overlay file")
458
            logging.info("overlay file (%s) committed!" % overlay_file_name)
459

460
            msg = "Start a new VM, using image_name as its harddisk"
X
Xu Han 已提交
461
            error_context.context(msg, logging.info)
462
            params['image_name'] = pre_name
463
            vm_name = params['main_vm']
464 465
            env_process.preprocess_vm(test, params, env, vm_name)
            vm = env.get_vm(vm_name)
466
            vm.verify_alive()
467 468 469
            timeout = int(params.get("login_timeout", 360))
            session = vm.wait_for_login(timeout=timeout)
            try:
470 471 472
                output = session.cmd(file_exist_chk_cmd)
                logging.info("Output of %s: %s", file_exist_chk_cmd, output)
                session.cmd(file_del_cmd)
473
            except Exception:
X
Xu Han 已提交
474
                test.fail("Could not find commit_testfile after a commit")
475 476 477
            vm.destroy()

        finally:
C
Cong Li 已提交
478
            # Remove the overlay file
F
Feng Yang 已提交
479 480
            if os.path.isfile(overlay_file_name):
                remove(overlay_file_name)
481 482 483 484 485

    def _rebase(cmd, img_name, base_img, backing_fmt, mode="unsafe"):
        """
        Simple wrapper of 'qemu-img rebase'.

L
Lucas Meneghel Rodrigues 已提交
486 487 488 489 490
        :param cmd: qemu-img base command.
        :param img_name: image name to be rebased
        :param base_img: indicates the base image
        :param backing_fmt: the format of base image
        :param mode: rebase mode: safe mode, unsafe mode
491 492
        """
        cmd += " rebase"
493
        show_progress = params.get("show_progress", "")
494 495
        if mode == "unsafe":
            cmd += " -u"
496 497
        if show_progress == "on":
            cmd += " -p"
498
        cmd += " -b %s -F %s %s" % (base_img, backing_fmt, img_name)
499
        msg = "Trying to rebase '%s' to '%s' by command %s" % (img_name,
L
Lucas Meneghel Rodrigues 已提交
500
                                                               base_img, cmd)
X
Xu Han 已提交
501
        error_context.context(msg, logging.info)
502
        if show_progress == "off":
X
Xu Han 已提交
503
            bg = utils_misc.InterruptedThread(send_signal)
504
            bg.start()
X
Xu Han 已提交
505
        check_command_output(process.run(cmd))
506 507 508 509 510 511 512 513 514 515

    def rebase_test(cmd):
        """
        Subcommand 'qemu-img rebase' test

        Change the backing file of a snapshot image in "unsafe mode":
        Assume the previous backing file had missed and we just have to change
        reference of snapshot to new one. After change the backing file of a
        snapshot image in unsafe mode, the snapshot should work still.

L
Lucas Meneghel Rodrigues 已提交
516
        :param cmd: qemu-img base command.
517
        """
X
Xu Han 已提交
518 519 520 521
        if 'rebase' not in process.system_output(cmd + ' --help',
                                                 ignore_status=True):
            test.cancel("Current kvm user space version does not"
                        " support 'rebase' subcommand")
522
        sn_fmt = params.get("snapshot_format", "qcow2")
523
        sn1 = params["image_name_snapshot1"]
F
Feng Yang 已提交
524
        sn1 = _get_image_filename(sn1, enable_gluster, sn_fmt)
525
        base_img = storage.get_image_filename(params, data_dir.get_data_dir())
526 527 528
        _create(cmd, sn1, sn_fmt, base_img=base_img, base_img_fmt=image_format)

        # Create snapshot2 based on snapshot1
529
        sn2 = params["image_name_snapshot2"]
F
Feng Yang 已提交
530
        sn2 = _get_image_filename(sn2, enable_gluster, sn_fmt)
531 532
        _create(cmd, sn2, sn_fmt, base_img=sn1, base_img_fmt=sn_fmt)

533
        rebase_mode = params.get("rebase_mode", "safe")
534
        if rebase_mode == "unsafe":
F
Feng Yang 已提交
535
            remove(sn1)
536 537

        _rebase(cmd, sn2, base_img, image_format, mode=rebase_mode)
538
        # Boot snapshot image after rebase
F
Feng Yang 已提交
539 540
        img_format = sn2.split('.')[-1]
        img_name = ".".join(sn2.split('.')[:-1])
541
        _boot(img_name, img_format)
542 543 544

        # Check sn2's format and backing_file
        actual_base_img = _info(cmd, sn2, "backing file")
545
        base_img_name = os.path.basename(base_img)
546
        if base_img_name not in actual_base_img:
X
Xu Han 已提交
547 548 549
            test.fail("After rebase the backing_file of 'sn2' is "
                      "'%s' which is not expected as '%s'"
                      % (actual_base_img, base_img_name))
550 551
        status, output = _check(cmd, sn2)
        if not status:
X
Xu Han 已提交
552 553
            test.fail("Check image '%s' failed after rebase;"
                      "got error: %s" % (sn2, output))
F
Feng Yang 已提交
554 555
        remove(sn2)
        remove(sn1)
556

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
    def _amend(cmd, img_name, img_fmt, options):
        """
        Simple wrapper of 'qemu-img amend'.

        :param cmd: qemu-img base command
        :param img_name: image name that should be amended
        :param img_fmt: image format
        :param options: a comma separated list of format specific options
        """

        msg = "Amend '%s' with options '%s'" % (img_name, options)
        cmd += " amend"
        if img_fmt:
            cmd += " -f %s" % img_fmt
        cache = params.get("cache_mode", '')
        if cache:
            cmd += " -t %s" % cache
        if options:
            cmd += " -o "
            for option in options:
                cmd += "%s=%s," % (option, params.get(option))
            cmd = cmd.rstrip(',')
        cmd += " %s" % img_name
X
Xu Han 已提交
580 581
        error_context.context(msg, logging.info)
        check_command_output(process.run(cmd, ignore_status=True))
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604

    def amend_test(cmd):
        """
        Subcommand 'qemu-img amend' test
        Amend the image format specific options for the image file

        :param cmd: qemu-img base command.
        """
        img_name = params.get("image_name_stg")
        img_fmt = params.get("image_format_stg", "qcow2")
        options = params.get("qemu_img_options", "").split()
        check_output = params.get("check_output", "exit_status")
        img = _get_image_filename(img_name, img_fmt=img_fmt)
        _amend(cmd, img, img_fmt, options)
        if check_output == "exit_status":
            for option in options:
                expect = params.get(option)
                if option == "size":
                    option = "virtual size"
                actual = _info(cmd, img, option)
                if actual is not None and actual != expect:
                    msg = "Get wrong %s from image %s!" % (option, img_name)
                    msg += "Expect: %s, actual: %s" % (expect, actual)
X
Xu Han 已提交
605
                    test.fail(msg)
606 607
        status, output = _check(cmd, img)
        if not status:
X
Xu Han 已提交
608 609
            test.fail("Check image '%s' failed after rebase;"
                      "got error: %s" % (img, output))
610

611 612 613 614 615 616 617
    def _boot(img_name, img_fmt):
        """
        Boot test:
        1) Login guest
        2) Run dd in rhel guest
        3) Shutdown guest

L
Lucas Meneghel Rodrigues 已提交
618 619
        :param img_name: image name
        :param img_fmt: image format
620 621 622 623 624
        """
        params['image_name'] = img_name
        params['image_format'] = img_fmt
        image_name = "%s.%s" % (img_name, img_fmt)
        msg = "Try to boot vm with image %s" % image_name
X
Xu Han 已提交
625
        error_context.context(msg, logging.info)
626 627 628 629 630 631 632 633 634 635 636 637
        vm_name = params.get("main_vm")
        dd_timeout = int(params.get("dd_timeout", 60))
        params['vms'] = vm_name
        env_process.preprocess_vm(test, params, env, vm_name)
        vm = env.get_vm(params.get("main_vm"))
        vm.verify_alive()
        login_timeout = int(params.get("login_timeout", 360))
        session = vm.wait_for_login(timeout=login_timeout)

        # Run dd in linux guest
        if params.get("os_type") == 'linux':
            cmd = "dd if=/dev/zero of=/mnt/test bs=1000 count=1000"
X
Xu Han 已提交
638
            status = session.cmd_status(cmd, timeout=dd_timeout)
639
            if status != 0:
X
Xu Han 已提交
640
                test.error("dd failed")
641

X
Xu Han 已提交
642
        error_context.context("Shutdown guest", logging.info)
643
        try:
X
Xu Tian 已提交
644 645 646 647 648 649 650
            vm.graceful_shutdown(timeout=login_timeout)
        except Exception:
            image_filename = _get_image_filename(img_name,
                                                 enable_gluster,
                                                 img_fmt)
            backup_img_chain(image_filename)
            raise
651
        finally:
X
Xu Tian 已提交
652
            vm.destroy(gracefully=True)
X
Xu Han 已提交
653
            process.system("sync")
654

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
    def backup_img_chain(image_file):
        """
        Backup whole image in a image chain;
        """
        mount_point = tempfile.mkdtemp(dir=test.resultsdir)
        qemu_img = utils_misc.get_qemu_img_binary(params)
        if enable_gluster:
            g_uri = gluster.create_gluster_uri(params)
            gluster.glusterfs_mount(g_uri, mount_point)
            image_name = os.path.basename(image_file)
            image_file = os.path.join(mount_point, image_name)
        logging.warn("backup %s to %s" % (image_file, test.resultsdir))
        shutil.copy(image_file, test.resultsdir)
        backing_file = _info(qemu_img, image_file, "backing file", None)
        if backing_file:
            backup_img_chain(backing_file)
        elif enable_gluster:
            utils_misc.umount(g_uri, mount_point,
                              "glusterfs", False,
                              "fuse.glusterfs")
            shutil.rmtree(mount_point)
        return None

678
    # Here starts test
679
    subcommand = params["subcommand"]
X
Xu Han 已提交
680
    error_context.context("Running %s_test(cmd)" % subcommand, logging.info)
681
    eval("%s_test(cmd)" % subcommand)