qemu_img.py 26.3 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

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

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


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

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

F
Feng Yang 已提交
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
    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

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

L
Lucas Meneghel Rodrigues 已提交
68 69
        :param cmd: qemu-img base command.
        :param img: image to be checked
70 71
        """
        cmd += " check %s" % img
X
Xu Han 已提交
72 73
        error_context.context("Checking image '%s' by command '%s'"
                              % (img, cmd), logging.info)
74
        try:
X
Xu Han 已提交
75
            output = process.system_output(cmd, verbose=False)
X
Xu Han 已提交
76
        except process.CmdError as err:
77 78
            result_stderr = err.result.stderr
            if "does not support checks" in result_stderr:
79 80
                return (True, "")
            else:
81
                return (False, result_stderr)
82 83 84 85 86 87 88 89 90
        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 已提交
91
        :param cmd: qemu-img base command.
92
        """
F
Feng Yang 已提交
93 94
        test_image = _get_image_filename(params["image_name_dd"],
                                         enable_gluster)
95
        create_image_cmd = params["create_image_cmd"]
96
        create_image_cmd = create_image_cmd % test_image
97
        msg = " Create image %s by command %s" % (test_image, create_image_cmd)
X
Xu Han 已提交
98
        error_context.context(msg, logging.info)
X
Xu Han 已提交
99
        process.system(create_image_cmd, verbose=False, shell=True)
100 101
        status, output = _check(cmd, test_image)
        if not status:
X
Xu Han 已提交
102 103
            test.fail("Check image '%s' failed with error: %s" %
                      (test_image, output))
104
        for fmt in params["supported_image_formats"].split():
105 106
            output_image = test_image + ".%s" % fmt
            _convert(cmd, fmt, test_image, output_image)
107 108
            status, output = _check(cmd, output_image)
            if not status:
X
Xu Han 已提交
109 110
                test.fail("Check image '%s' got error: %s" %
                          (output_image, output))
F
Feng Yang 已提交
111 112
            remove(output_image)
        remove(test_image)
113 114

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

L
Lucas Meneghel Rodrigues 已提交
120 121 122 123 124 125 126
        :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
127 128 129
        :param preallocated: if preallocation when create image,
                             allowed values: off, metadata. Default is "off"
        :param cluster_size: the cluster size for the image
130 131
        """
        cmd += " create"
132

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

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

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

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

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

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

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

180 181 182 183 184 185 186 187 188
    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)
X
Xu Han 已提交
189
            status = process.system("kill -SIGUSR1 `pidof qemu-img`",
X
Xu Han 已提交
190
                                    ignore_status=True, shell=True)
191 192 193 194 195 196 197 198 199 200 201 202
            if status == 0:
                return None
        logging.info("Fail to get pid of qemu-img")

    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 已提交
203
            test.error("Unknown check output '%s'" % check_output)
204 205 206 207 208
        output = getattr(CmdResult, check_output)
        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 已提交
209
            test.fail(err_msg % (output, CmdResult.command))
210 211 212 213
        pattern = params.get("command_result_pattern")
        if not re.findall(pattern, output):
            err_msg = "Fail to get expected result!"
            err_msg += "Output: %s, expected pattern: %s" % (output, pattern)
X
Xu Han 已提交
214
            test.fail(err_msg)
215

216
    def _convert(cmd, output_fmt, img_name, output_filename,
L
Lucas Meneghel Rodrigues 已提交
217
                 fmt=None, compressed="no", encrypted="no"):
218 219 220
        """
        Simple wrapper of 'qemu-img convert' function.

L
Lucas Meneghel Rodrigues 已提交
221 222 223 224 225 226 227
        :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
228 229 230 231 232 233
        """
        cmd += " convert"
        if compressed == "yes":
            cmd += " -c"
        if encrypted == "yes":
            cmd += " -e"
234 235 236
        show_progress = params.get("show_progress", "")
        if show_progress == "on":
            cmd += " -p"
237 238 239
        if fmt:
            cmd += " -f %s" % fmt
        cmd += " -O %s" % output_fmt
240 241 242 243 244 245 246 247
        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(",")
248
        cmd += " %s %s" % (img_name, output_filename)
249 250
        msg = "Converting '%s' from format '%s'" % (img_name, fmt)
        msg += " to '%s'" % output_fmt
X
Xu Han 已提交
251
        error_context.context(msg, logging.info)
252
        if show_progress == "off":
X
Xu Han 已提交
253
            bg = utils_misc.InterruptedThread(send_signal)
254
            bg.start()
X
Xu Han 已提交
255
        check_command_output(process.run(cmd, ignore_status=True))
256 257 258 259 260

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

L
Lucas Meneghel Rodrigues 已提交
261
        :param cmd: qemu-img base command.
262
        """
263
        dest_img_fmt = params["dest_image_format"]
264 265
        output_filename = "%s.converted_%s.%s" % (image_name,
                                                  dest_img_fmt, dest_img_fmt)
266 267

        _convert(cmd, dest_img_fmt, image_name, output_filename,
L
Lucas Meneghel Rodrigues 已提交
268
                 image_format, params["compressed"], params["encrypted"])
269 270 271 272
        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)
273 274

        if dest_img_fmt == "qcow2":
275 276
            status, output = _check(cmd, output_filename)
            if status:
F
Feng Yang 已提交
277
                remove(output_filename)
278
            else:
X
Xu Han 已提交
279 280
                test.fail("Check image '%s' failed with error: %s" %
                          (output_filename, output))
281
        else:
F
Feng Yang 已提交
282
            remove(output_filename)
283 284 285 286 287

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

L
Lucas Meneghel Rodrigues 已提交
288 289 290 291
        :param cmd: qemu-img base command.
        :param img: image file
        :param sub_info: sub info, say 'backing file'
        :param fmt: image format
292 293 294 295 296 297 298
        """
        cmd += " info"
        if fmt:
            cmd += " -f %s" % fmt
        cmd += " %s" % img

        try:
X
Xu Han 已提交
299
            output = process.system_output(cmd)
X
Xu Han 已提交
300
        except process.CmdError as err:
301
            logging.error("Get info of image '%s' failed: %s", img, str(err))
302 303 304 305 306 307 308
            return None

        if not sub_info:
            return output

        sub_info += ": (.*)"
        matches = re.findall(sub_info, output)
309 310 311
        if "virtual size" in sub_info:
            p = re.compile(r'\.0*(G|K)$')
            return p.sub(r'\1', matches[0].split()[0])
312 313 314 315 316 317 318 319
        if matches:
            return matches[0]
        return None

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

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

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

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

    def commit_test(cmd):
        """
        Subcommand 'qemu-img commit' test.
C
Cong Li 已提交
373 374 375
        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
376
           VM.
C
Cong Li 已提交
377
        4) Commit the change to the backing harddisk by executing
378
           "qemu-img commit" command.
C
Cong Li 已提交
379 380
        5) Start the VM using the backing harddisk.
        6) Check if the file "commit_testfile" exists.
381

L
Lucas Meneghel Rodrigues 已提交
382
        :param cmd: qemu-img base command.
383 384 385
        """

        logging.info("Commit testing started!")
F
Feng Yang 已提交
386 387 388
        image_name = storage.get_image_filename(params,
                                                data_dir.get_data_dir())
        pre_name = '.'.join(image_name.split('.')[:-1])
389
        image_format = params.get("image_format", "qcow2")
F
Feng Yang 已提交
390
        overlay_file_name = "%s_overlay.%s" % (pre_name, image_format)
391 392 393 394 395 396 397 398
        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")
399
        try:
C
Cong Li 已提交
400
            # Remove the existing overlay file
F
Feng Yang 已提交
401 402
            if os.path.isfile(overlay_file_name):
                remove(overlay_file_name)
403

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

C
Cong Li 已提交
416
            # Set the qemu harddisk to the overlay file
L
Lucas Meneghel Rodrigues 已提交
417 418
            logging.info(
                "Original image_name is: %s", params.get('image_name'))
419
            params['image_name'] = '.'.join(overlay_file_name.split('.')[:-1])
420 421 422
            logging.info("Param image_name changed to: %s",
                         params.get('image_name'))

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

C
Cong Li 已提交
432
            # Do some changes to the overlay_file harddisk
433
            try:
434 435 436 437
                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 已提交
438
            except Exception as err:
X
Xu Han 已提交
439 440
                test.fail("Could not create commit_testfile in the "
                          "overlay file %s" % err)
441 442
            vm.destroy()

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

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

        finally:
C
Cong Li 已提交
472
            # Remove the overlay file
F
Feng Yang 已提交
473 474
            if os.path.isfile(overlay_file_name):
                remove(overlay_file_name)
475 476 477 478 479

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

L
Lucas Meneghel Rodrigues 已提交
480 481 482 483 484
        :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
485 486
        """
        cmd += " rebase"
487
        show_progress = params.get("show_progress", "")
488 489
        if mode == "unsafe":
            cmd += " -u"
490 491
        if show_progress == "on":
            cmd += " -p"
492
        cmd += " -b %s -F %s %s" % (base_img, backing_fmt, img_name)
493
        msg = "Trying to rebase '%s' to '%s' by command %s" % (img_name,
L
Lucas Meneghel Rodrigues 已提交
494
                                                               base_img, cmd)
X
Xu Han 已提交
495
        error_context.context(msg, logging.info)
496
        if show_progress == "off":
X
Xu Han 已提交
497
            bg = utils_misc.InterruptedThread(send_signal)
498
            bg.start()
X
Xu Han 已提交
499
        check_command_output(process.run(cmd))
500 501 502 503 504 505 506 507 508 509

    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 已提交
510
        :param cmd: qemu-img base command.
511
        """
X
Xu Han 已提交
512 513 514 515
        if 'rebase' not in process.system_output(cmd + ' --help',
                                                 ignore_status=True):
            test.cancel("Current kvm user space version does not"
                        " support 'rebase' subcommand")
516
        sn_fmt = params.get("snapshot_format", "qcow2")
517
        sn1 = params["image_name_snapshot1"]
F
Feng Yang 已提交
518
        sn1 = _get_image_filename(sn1, enable_gluster, sn_fmt)
519
        base_img = storage.get_image_filename(params, data_dir.get_data_dir())
520 521 522
        _create(cmd, sn1, sn_fmt, base_img=base_img, base_img_fmt=image_format)

        # Create snapshot2 based on snapshot1
523
        sn2 = params["image_name_snapshot2"]
F
Feng Yang 已提交
524
        sn2 = _get_image_filename(sn2, enable_gluster, sn_fmt)
525 526
        _create(cmd, sn2, sn_fmt, base_img=sn1, base_img_fmt=sn_fmt)

527
        rebase_mode = params.get("rebase_mode", "safe")
528
        if rebase_mode == "unsafe":
F
Feng Yang 已提交
529
            remove(sn1)
530 531

        _rebase(cmd, sn2, base_img, image_format, mode=rebase_mode)
532
        # Boot snapshot image after rebase
F
Feng Yang 已提交
533 534
        img_format = sn2.split('.')[-1]
        img_name = ".".join(sn2.split('.')[:-1])
535
        _boot(img_name, img_format)
536 537 538

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

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    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 已提交
574 575
        error_context.context(msg, logging.info)
        check_command_output(process.run(cmd, ignore_status=True))
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598

    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 已提交
599
                    test.fail(msg)
600 601
        status, output = _check(cmd, img)
        if not status:
X
Xu Han 已提交
602 603
            test.fail("Check image '%s' failed after rebase;"
                      "got error: %s" % (img, output))
604

605 606 607 608 609 610 611
    def _boot(img_name, img_fmt):
        """
        Boot test:
        1) Login guest
        2) Run dd in rhel guest
        3) Shutdown guest

L
Lucas Meneghel Rodrigues 已提交
612 613
        :param img_name: image name
        :param img_fmt: image format
614 615 616 617 618
        """
        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 已提交
619
        error_context.context(msg, logging.info)
620 621 622 623 624 625 626 627 628 629 630 631
        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 已提交
632
            status = session.cmd_status(cmd, timeout=dd_timeout)
633
            if status != 0:
X
Xu Han 已提交
634
                test.error("dd failed")
635

X
Xu Han 已提交
636
        error_context.context("Shutdown guest", logging.info)
637
        try:
X
Xu Tian 已提交
638 639 640 641 642 643 644
            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
645
        finally:
X
Xu Tian 已提交
646
            vm.destroy(gracefully=True)
X
Xu Han 已提交
647
            process.system("sync")
648

649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
    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

672
    # Here starts test
673
    subcommand = params["subcommand"]
X
Xu Han 已提交
674
    error_context.context("Running %s_test(cmd)" % subcommand, logging.info)
675
    eval("%s_test(cmd)" % subcommand)