qemu_img.py 27.0 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
from virttest.utils_numeric import normalize_data_size
18 19


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

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

F
Feng Yang 已提交
41 42 43 44 45 46
    def remove(path):
        try:
            os.remove(path)
        except OSError:
            pass

47 48
    def _get_image_filename(img_name, enable_gluster=False,
                            enable_nvme=False, img_fmt=None):
F
Feng Yang 已提交
49 50 51 52 53
        """
        Generate an image path.

        :param image_name: Force name of image.
        :param enable_gluster: Enable gluster or not.
54
        :param enable_nvme: Enable nvme or not.
F
Feng Yang 已提交
55 56 57 58 59 60 61
        :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
62 63
        elif enable_nvme:
            image_filename = image_name
F
Feng Yang 已提交
64 65 66 67 68 69 70
        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

71 72 73 74
    def _check(cmd, img):
        """
        Simple 'qemu-img check' function implementation.

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

    def _create(cmd, img_name, fmt, img_size=None, base_img=None,
122
                base_img_fmt=None, encrypted="no",
123
                preallocated="off", cluster_size=None):
124 125 126
        """
        Simple wrapper of 'qemu-img create'

L
Lucas Meneghel Rodrigues 已提交
127 128 129 130 131 132 133
        :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
134 135 136
        :param preallocated: if preallocation when create image,
                             allowed values: off, metadata. Default is "off"
        :param cluster_size: the cluster size for the image
137 138
        """
        cmd += " create"
139

140 141 142 143 144 145
        if encrypted == "yes":
            cmd += " -e"
        if base_img:
            cmd += " -b %s" % base_img
            if base_img_fmt:
                cmd += " -F %s" % base_img_fmt
146

147
        cmd += " -f %s" % fmt
148 149 150 151 152 153 154

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

157 158 159
        cmd += " %s" % img_name
        if img_size:
            cmd += " %s" % img_size
160

161
        msg = "Creating image %s by command %s" % (img_name, cmd)
X
Xu Han 已提交
162 163
        error_context.context(msg, logging.info)
        process.system(cmd, verbose=False)
164 165
        status, out = _check(qemu_img_binary, img_name)
        if not status:
X
Xu Han 已提交
166
            test.fail("Check image '%s' got error: %s" % (img_name, out))
167 168 169 170 171

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

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

187 188 189 190 191 192 193 194
    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:
195 196 197 198 199
            pid = process.system_output("pidof qemu-img",
                                        ignore_status=True,
                                        verbose=False).decode().strip()
            if bool(pid):
                break
200
            time.sleep(1)
201 202 203 204 205
        try:
            os.kill(int(pid), signal.SIGUSR1)
        except Exception as error:
            logging.info("Failed to send signal to qemu-img")
            test.error(str(error))
206 207 208 209 210 211 212 213 214

    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 已提交
215
            test.error("Unknown check output '%s'" % check_output)
216
        output = getattr(CmdResult, check_output)
217 218 219 220
        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 已提交
221
            test.fail(err_msg % (output, CmdResult.command))
222
        pattern = params.get("command_result_pattern")
223
        if not re.findall(pattern, output.decode()):
224 225
            err_msg = "Fail to get expected result!"
            err_msg += "Output: %s, expected pattern: %s" % (output, pattern)
X
Xu Han 已提交
226
            test.fail(err_msg)
227

228
    def _convert(cmd, output_fmt, img_name, output_filename,
L
Lucas Meneghel Rodrigues 已提交
229
                 fmt=None, compressed="no", encrypted="no"):
230 231 232
        """
        Simple wrapper of 'qemu-img convert' function.

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

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

L
Lucas Meneghel Rodrigues 已提交
273
        :param cmd: qemu-img base command.
274
        """
275
        dest_img_fmt = params["dest_image_format"]
276 277
        output_filename = "%s.converted_%s.%s" % (image_name,
                                                  dest_img_fmt, dest_img_fmt)
278 279

        _convert(cmd, dest_img_fmt, image_name, output_filename,
L
Lucas Meneghel Rodrigues 已提交
280
                 image_format, params["compressed"], params["encrypted"])
281 282 283 284
        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)
285 286

        if dest_img_fmt == "qcow2":
287 288
            status, output = _check(cmd, output_filename)
            if status:
F
Feng Yang 已提交
289
                remove(output_filename)
290
            else:
X
Xu Han 已提交
291 292
                test.fail("Check image '%s' failed with error: %s" %
                          (output_filename, output))
293
        else:
F
Feng Yang 已提交
294
            remove(output_filename)
295 296 297 298 299

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

L
Lucas Meneghel Rodrigues 已提交
300 301 302 303
        :param cmd: qemu-img base command.
        :param img: image file
        :param sub_info: sub info, say 'backing file'
        :param fmt: image format
304 305 306 307 308 309 310
        """
        cmd += " info"
        if fmt:
            cmd += " -f %s" % fmt
        cmd += " %s" % img

        try:
311
            output = process.system_output(cmd).decode()
X
Xu Han 已提交
312
        except process.CmdError as err:
P
Ping Li 已提交
313 314
            logging.error("Get info of image '%s' failed: %s",
                          img, err.result.stderr.decode())
315 316 317 318 319 320 321
            return None

        if not sub_info:
            return output

        sub_info += ": (.*)"
        matches = re.findall(sub_info, output)
322 323 324
        if "virtual size" in sub_info:
            p = re.compile(r'\.0*(G|K)$')
            return p.sub(r'\1', matches[0].split()[0])
325 326 327 328 329 330 331 332
        if matches:
            return matches[0]
        return None

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

L
Lucas Meneghel Rodrigues 已提交
333
        :param cmd: qemu-img base command.
334 335 336
        """
        img_info = _info(cmd, image_name)
        logging.info("Info of image '%s':\n%s", image_name, img_info)
337
        if image_format not in img_info:
X
Xu Han 已提交
338 339
            test.fail("Got unexpected format of image '%s'"
                      " in info test" % image_name)
340 341
        if not re.search(r'%s\s+bytes' % normalize_data_size(
                  image_size, "B"), img_info):
X
Xu Han 已提交
342 343
            test.fail("Got unexpected size of image '%s'"
                      " in info test" % image_name)
344 345 346 347 348

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

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

    def commit_test(cmd):
        """
        Subcommand 'qemu-img commit' test.
C
Cong Li 已提交
387 388 389
        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
390
           VM.
C
Cong Li 已提交
391
        4) Commit the change to the backing harddisk by executing
392
           "qemu-img commit" command.
C
Cong Li 已提交
393 394
        5) Start the VM using the backing harddisk.
        6) Check if the file "commit_testfile" exists.
395

L
Lucas Meneghel Rodrigues 已提交
396
        :param cmd: qemu-img base command.
397 398 399
        """

        logging.info("Commit testing started!")
F
Feng Yang 已提交
400 401 402
        image_name = storage.get_image_filename(params,
                                                data_dir.get_data_dir())
        pre_name = '.'.join(image_name.split('.')[:-1])
403
        image_format = params.get("image_format", "qcow2")
F
Feng Yang 已提交
404
        overlay_file_name = "%s_overlay.%s" % (pre_name, image_format)
405 406 407 408 409 410 411 412
        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")
413
        try:
C
Cong Li 已提交
414
            # Remove the existing overlay file
F
Feng Yang 已提交
415 416
            if os.path.isfile(overlay_file_name):
                remove(overlay_file_name)
417

C
Cong Li 已提交
418
            # Create the new overlay file
F
Feng Yang 已提交
419 420 421
            create_cmd = "%s create -b %s -f %s %s" % (cmd, image_name,
                                                       image_format,
                                                       overlay_file_name)
C
Cong Li 已提交
422
            msg = "Create overlay file by command: %s" % create_cmd
X
Xu Han 已提交
423
            error_context.context(msg, logging.info)
424
            try:
X
Xu Han 已提交
425 426 427
                process.system(create_cmd, verbose=False)
            except process.CmdError:
                test.fail("Could not create a overlay file!")
428
            logging.info("overlay file (%s) created!" % overlay_file_name)
429

C
Cong Li 已提交
430
            # Set the qemu harddisk to the overlay file
L
Lucas Meneghel Rodrigues 已提交
431 432
            logging.info(
                "Original image_name is: %s", params.get('image_name'))
433
            params['image_name'] = '.'.join(overlay_file_name.split('.')[:-1])
434 435 436
            logging.info("Param image_name changed to: %s",
                         params.get('image_name'))

C
Cong Li 已提交
437
            msg = "Start a new VM, using overlay file as its harddisk"
X
Xu Han 已提交
438
            error_context.context(msg, logging.info)
439
            vm_name = params['main_vm']
440 441
            env_process.preprocess_vm(test, params, env, vm_name)
            vm = env.get_vm(vm_name)
442
            vm.verify_alive()
443 444 445
            timeout = int(params.get("login_timeout", 360))
            session = vm.wait_for_login(timeout=timeout)

C
Cong Li 已提交
446
            # Do some changes to the overlay_file harddisk
447
            try:
448 449 450 451
                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 已提交
452
            except Exception as err:
X
Xu Han 已提交
453 454
                test.fail("Could not create commit_testfile in the "
                          "overlay file %s" % err)
455 456
            vm.destroy()

L
Lucas Meneghel Rodrigues 已提交
457
            # Execute the commit command
F
Feng Yang 已提交
458 459
            cmitcmd = "%s commit -f %s %s" % (cmd, image_format,
                                              overlay_file_name)
X
Xu Han 已提交
460 461
            error_context.context("Committing image by command %s" % cmitcmd,
                                  logging.info)
462
            try:
X
Xu Han 已提交
463 464 465
                process.system(cmitcmd, verbose=False)
            except process.CmdError:
                test.fail("Could not commit the overlay file")
466
            logging.info("overlay file (%s) committed!" % overlay_file_name)
467

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

        finally:
C
Cong Li 已提交
486
            # Remove the overlay file
F
Feng Yang 已提交
487 488
            if os.path.isfile(overlay_file_name):
                remove(overlay_file_name)
489 490 491 492 493

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

L
Lucas Meneghel Rodrigues 已提交
494 495 496 497 498
        :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
499 500
        """
        cmd += " rebase"
501
        show_progress = params.get("show_progress", "")
502 503
        if mode == "unsafe":
            cmd += " -u"
504 505
        if show_progress == "on":
            cmd += " -p"
506
        cmd += " -b %s -F %s %s" % (base_img, backing_fmt, img_name)
507
        msg = "Trying to rebase '%s' to '%s' by command %s" % (img_name,
L
Lucas Meneghel Rodrigues 已提交
508
                                                               base_img, cmd)
X
Xu Han 已提交
509
        error_context.context(msg, logging.info)
510
        if show_progress == "off":
X
Xu Han 已提交
511
            bg = utils_misc.InterruptedThread(send_signal)
512
            bg.start()
X
Xu Han 已提交
513
        check_command_output(process.run(cmd))
514 515 516 517 518 519 520 521 522 523

    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 已提交
524
        :param cmd: qemu-img base command.
525
        """
X
Xu Han 已提交
526
        if 'rebase' not in process.system_output(cmd + ' --help',
P
Ping Li 已提交
527
                                                 ignore_status=True).decode():
X
Xu Han 已提交
528 529
            test.cancel("Current kvm user space version does not"
                        " support 'rebase' subcommand")
530
        sn_fmt = params.get("snapshot_format", "qcow2")
531
        sn1 = params["image_name_snapshot1"]
532
        sn1 = _get_image_filename(sn1, enable_gluster, img_fmt=sn_fmt)
533
        base_img = storage.get_image_filename(params, data_dir.get_data_dir())
534 535 536
        _create(cmd, sn1, sn_fmt, base_img=base_img, base_img_fmt=image_format)

        # Create snapshot2 based on snapshot1
537
        sn2 = params["image_name_snapshot2"]
538
        sn2 = _get_image_filename(sn2, enable_gluster, img_fmt=sn_fmt)
539 540
        _create(cmd, sn2, sn_fmt, base_img=sn1, base_img_fmt=sn_fmt)

541
        rebase_mode = params.get("rebase_mode", "safe")
542
        if rebase_mode == "unsafe":
F
Feng Yang 已提交
543
            remove(sn1)
544 545

        _rebase(cmd, sn2, base_img, image_format, mode=rebase_mode)
546
        # Boot snapshot image after rebase
F
Feng Yang 已提交
547 548
        img_format = sn2.split('.')[-1]
        img_name = ".".join(sn2.split('.')[:-1])
549
        _boot(img_name, img_format)
550 551 552

        # Check sn2's format and backing_file
        actual_base_img = _info(cmd, sn2, "backing file")
553
        base_img_name = os.path.basename(base_img)
554
        if base_img_name not in actual_base_img:
X
Xu Han 已提交
555 556 557
            test.fail("After rebase the backing_file of 'sn2' is "
                      "'%s' which is not expected as '%s'"
                      % (actual_base_img, base_img_name))
558 559
        status, output = _check(cmd, sn2)
        if not status:
X
Xu Han 已提交
560 561
            test.fail("Check image '%s' failed after rebase;"
                      "got error: %s" % (sn2, output))
F
Feng Yang 已提交
562 563
        remove(sn2)
        remove(sn1)
564

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
    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 已提交
588 589
        error_context.context(msg, logging.info)
        check_command_output(process.run(cmd, ignore_status=True))
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612

    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 已提交
613
                    test.fail(msg)
614 615
        status, output = _check(cmd, img)
        if not status:
X
Xu Han 已提交
616 617
            test.fail("Check image '%s' failed after rebase;"
                      "got error: %s" % (img, output))
618

619 620 621 622 623 624 625
    def _boot(img_name, img_fmt):
        """
        Boot test:
        1) Login guest
        2) Run dd in rhel guest
        3) Shutdown guest

L
Lucas Meneghel Rodrigues 已提交
626 627
        :param img_name: image name
        :param img_fmt: image format
628 629 630 631 632
        """
        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 已提交
633
        error_context.context(msg, logging.info)
634 635 636 637 638 639 640 641 642 643 644 645
        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 已提交
646
            status = session.cmd_status(cmd, timeout=dd_timeout)
647
            if status != 0:
X
Xu Han 已提交
648
                test.error("dd failed")
649

X
Xu Han 已提交
650
        error_context.context("Shutdown guest", logging.info)
651
        try:
X
Xu Tian 已提交
652 653 654 655
            vm.graceful_shutdown(timeout=login_timeout)
        except Exception:
            image_filename = _get_image_filename(img_name,
                                                 enable_gluster,
656
                                                 img_fmt=img_fmt)
X
Xu Tian 已提交
657 658
            backup_img_chain(image_filename)
            raise
659
        finally:
X
Xu Tian 已提交
660
            vm.destroy(gracefully=True)
X
Xu Han 已提交
661
            process.system("sync")
662

663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
    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

686
    # Here starts test
687
    subcommand = params["subcommand"]
X
Xu Han 已提交
688
    error_context.context("Running %s_test(cmd)" % subcommand, logging.info)
689
    eval("%s_test(cmd)" % subcommand)