• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import re, os, logging, commands
2from autotest_lib.client.common_lib import utils, error
3from autotest_lib.client.virt import virt_vm, virt_utils, virt_env_process
4
5
6def run_qemu_img(test, params, env):
7    """
8    'qemu-img' functions test:
9    1) Judge what subcommand is going to be tested
10    2) Run subcommand test
11
12    @param test: kvm test object
13    @param params: Dictionary with the test parameters
14    @param env: Dictionary with test environment.
15    """
16    cmd = virt_utils.get_path(test.bindir, params.get("qemu_img_binary"))
17    if not os.path.exists(cmd):
18        raise error.TestError("Binary of 'qemu-img' not found")
19    image_format = params.get("image_format")
20    image_size = params.get("image_size", "10G")
21    image_name = virt_vm.get_image_filename(params, test.bindir)
22
23
24    def _check(cmd, img):
25        """
26        Simple 'qemu-img check' function implementation.
27
28        @param cmd: qemu-img base command.
29        @param img: image to be checked
30        """
31        cmd += " check %s" % img
32        logging.info("Checking image '%s'...", img)
33        try:
34            output = utils.system_output(cmd)
35        except error.CmdError, e:
36            if "does not support checks" in str(e):
37                return (True, "")
38            else:
39                return (False, str(e))
40        return (True, output)
41
42
43    def check_test(cmd):
44        """
45        Subcommand 'qemu-img check' test.
46
47        This tests will 'dd' to create a specified size file, and check it.
48        Then convert it to supported image_format in each loop and check again.
49
50        @param cmd: qemu-img base command.
51        """
52        test_image = virt_utils.get_path(test.bindir,
53                                        params.get("image_name_dd"))
54        print "test_image = %s" % test_image
55        create_image_cmd = params.get("create_image_cmd")
56        create_image_cmd = create_image_cmd % test_image
57        print "create_image_cmd = %s" % create_image_cmd
58        utils.system(create_image_cmd)
59        s, o = _check(cmd, test_image)
60        if not s:
61            raise error.TestFail("Check image '%s' failed with error: %s" %
62                                                           (test_image, o))
63        for fmt in params.get("supported_image_formats").split():
64            output_image = test_image + ".%s" % fmt
65            _convert(cmd, fmt, test_image, output_image)
66            s, o = _check(cmd, output_image)
67            if not s:
68                raise error.TestFail("Check image '%s' got error: %s" %
69                                                     (output_image, o))
70            os.remove(output_image)
71        os.remove(test_image)
72
73
74    def _create(cmd, img_name, fmt, img_size=None, base_img=None,
75               base_img_fmt=None, encrypted="no"):
76        """
77        Simple wrapper of 'qemu-img create'
78
79        @param cmd: qemu-img base command.
80        @param img_name: name of the image file
81        @param fmt: image format
82        @param img_size:  image size
83        @param base_img: base image if create a snapshot image
84        @param base_img_fmt: base image format if create a snapshot image
85        @param encrypted: indicates whether the created image is encrypted
86        """
87        cmd += " create"
88        if encrypted == "yes":
89            cmd += " -e"
90        if base_img:
91            cmd += " -b %s" % base_img
92            if base_img_fmt:
93                cmd += " -F %s" % base_img_fmt
94        cmd += " -f %s" % fmt
95        cmd += " %s" % img_name
96        if img_size:
97            cmd += " %s" % img_size
98        utils.system(cmd)
99
100
101    def create_test(cmd):
102        """
103        Subcommand 'qemu-img create' test.
104
105        @param cmd: qemu-img base command.
106        """
107        image_large = params.get("image_name_large")
108        img = virt_utils.get_path(test.bindir, image_large)
109        img += '.' + image_format
110        _create(cmd, img_name=img, fmt=image_format,
111               img_size=params.get("image_size_large"))
112        os.remove(img)
113
114
115    def _convert(cmd, output_fmt, img_name, output_filename,
116                fmt=None, compressed="no", encrypted="no"):
117        """
118        Simple wrapper of 'qemu-img convert' function.
119
120        @param cmd: qemu-img base command.
121        @param output_fmt: the output format of converted image
122        @param img_name: image name that to be converted
123        @param output_filename: output image name that converted
124        @param fmt: output image format
125        @param compressed: whether output image is compressed
126        @param encrypted: whether output image is encrypted
127        """
128        cmd += " convert"
129        if compressed == "yes":
130            cmd += " -c"
131        if encrypted == "yes":
132            cmd += " -e"
133        if fmt:
134            cmd += " -f %s" % fmt
135        cmd += " -O %s" % output_fmt
136        cmd += " %s %s" % (img_name, output_filename)
137        logging.info("Converting '%s' from format '%s' to '%s'", img_name, fmt,
138                     output_fmt)
139        utils.system(cmd)
140
141
142    def convert_test(cmd):
143        """
144        Subcommand 'qemu-img convert' test.
145
146        @param cmd: qemu-img base command.
147        """
148        dest_img_fmt = params.get("dest_image_format")
149        output_filename = "%s.converted_%s" % (image_name, dest_img_fmt)
150
151        _convert(cmd, dest_img_fmt, image_name, output_filename,
152                image_format, params.get("compressed"), params.get("encrypted"))
153
154        if dest_img_fmt == "qcow2":
155            s, o = _check(cmd, output_filename)
156            if s:
157                os.remove(output_filename)
158            else:
159                raise error.TestFail("Check image '%s' failed with error: %s" %
160                                                        (output_filename, o))
161        else:
162            os.remove(output_filename)
163
164
165    def _info(cmd, img, sub_info=None, fmt=None):
166        """
167        Simple wrapper of 'qemu-img info'.
168
169        @param cmd: qemu-img base command.
170        @param img: image file
171        @param sub_info: sub info, say 'backing file'
172        @param fmt: image format
173        """
174        cmd += " info"
175        if fmt:
176            cmd += " -f %s" % fmt
177        cmd += " %s" % img
178
179        try:
180            output = utils.system_output(cmd)
181        except error.CmdError, e:
182            logging.error("Get info of image '%s' failed: %s", img, str(e))
183            return None
184
185        if not sub_info:
186            return output
187
188        sub_info += ": (.*)"
189        matches = re.findall(sub_info, output)
190        if matches:
191            return matches[0]
192        return None
193
194
195    def info_test(cmd):
196        """
197        Subcommand 'qemu-img info' test.
198
199        @param cmd: qemu-img base command.
200        """
201        img_info = _info(cmd, image_name)
202        logging.info("Info of image '%s':\n%s", image_name, img_info)
203        if not image_format in img_info:
204            raise error.TestFail("Got unexpected format of image '%s'"
205                                 " in info test" % image_name)
206        if not image_size in img_info:
207            raise error.TestFail("Got unexpected size of image '%s'"
208                                 " in info test" % image_name)
209
210
211    def snapshot_test(cmd):
212        """
213        Subcommand 'qemu-img snapshot' test.
214
215        @param cmd: qemu-img base command.
216        """
217        cmd += " snapshot"
218        for i in range(2):
219            crtcmd = cmd
220            sn_name = "snapshot%d" % i
221            crtcmd += " -c %s %s" % (sn_name, image_name)
222            s, o = commands.getstatusoutput(crtcmd)
223            if s != 0:
224                raise error.TestFail("Create snapshot failed via command: %s;"
225                                     "Output is: %s" % (crtcmd, o))
226            logging.info("Created snapshot '%s' in '%s'", sn_name, image_name)
227        listcmd = cmd
228        listcmd += " -l %s" % image_name
229        s, o = commands.getstatusoutput(listcmd)
230        if not ("snapshot0" in o and "snapshot1" in o and s == 0):
231            raise error.TestFail("Snapshot created failed or missed;"
232                                 "snapshot list is: \n%s" % o)
233        for i in range(2):
234            sn_name = "snapshot%d" % i
235            delcmd = cmd
236            delcmd += " -d %s %s" % (sn_name, image_name)
237            s, o = commands.getstatusoutput(delcmd)
238            if s != 0:
239                raise error.TestFail("Delete snapshot '%s' failed: %s" %
240                                                     (sn_name, o))
241
242
243    def commit_test(cmd):
244        """
245        Subcommand 'qemu-img commit' test.
246        1) Create a backing file of the qemu harddisk specified by image_name.
247        2) Start a VM using the backing file as its harddisk.
248        3) Touch a file "commit_testfile" in the backing_file, and shutdown the
249           VM.
250        4) Make sure touching the file does not affect the original harddisk.
251        5) Commit the change to the original harddisk by executing
252           "qemu-img commit" command.
253        6) Start the VM using the original harddisk.
254        7) Check if the file "commit_testfile" exists.
255
256        @param cmd: qemu-img base command.
257        """
258        cmd += " commit"
259
260        logging.info("Commit testing started!")
261        image_name = params.get("image_name", "image")
262        image_format = params.get("image_format", "qcow2")
263        backing_file_name = "%s_bak" % (image_name)
264
265        try:
266            # Remove the existing backing file
267            backing_file = "%s.%s" % (backing_file_name, image_format)
268            if os.path.isfile(backing_file):
269                os.remove(backing_file)
270
271            # Create the new backing file
272            create_cmd = "qemu-img create -b %s.%s -f %s %s.%s" % (image_name,
273                                                                  image_format,
274                                                                  image_format,
275                                                             backing_file_name,
276                                                                  image_format)
277            try:
278                utils.system(create_cmd)
279            except error.CmdError, e:
280                raise error.TestFail("Could not create a backing file!")
281            logging.info("backing_file created!")
282
283            # Set the qemu harddisk to the backing file
284            logging.info("Original image_name is: %s", params.get('image_name'))
285            params['image_name'] = backing_file_name
286            logging.info("Param image_name changed to: %s",
287                         params.get('image_name'))
288
289            # Start a new VM, using backing file as its harddisk
290            vm_name = params.get('main_vm')
291            virt_env_process.preprocess_vm(test, params, env, vm_name)
292            vm = env.get_vm(vm_name)
293            vm.create()
294            timeout = int(params.get("login_timeout", 360))
295            session = vm.wait_for_login(timeout=timeout)
296
297            # Do some changes to the backing_file harddisk
298            try:
299                output = session.cmd("touch /commit_testfile")
300                logging.info("Output of touch /commit_testfile: %s", output)
301                output = session.cmd("ls / | grep commit_testfile")
302                logging.info("Output of ls / | grep commit_testfile: %s",
303                             output)
304            except Exception, e:
305                raise error.TestFail("Could not create commit_testfile in the "
306                                     "backing file %s", e)
307            vm.destroy()
308
309            # Make sure there is no effect on the original harddisk
310            # First, set the harddisk back to the original one
311            logging.info("Current image_name is: %s", params.get('image_name'))
312            params['image_name'] = image_name
313            logging.info("Param image_name reverted to: %s",
314                         params.get('image_name'))
315
316            # Second, Start a new VM, using image_name as its harddisk
317            # Here, the commit_testfile should not exist
318            vm_name = params.get('main_vm')
319            virt_env_process.preprocess_vm(test, params, env, vm_name)
320            vm = env.get_vm(vm_name)
321            vm.create()
322            timeout = int(params.get("login_timeout", 360))
323            session = vm.wait_for_login(timeout=timeout)
324            try:
325                output = session.cmd("[ ! -e /commit_testfile ] && echo $?")
326                logging.info("Output of [ ! -e /commit_testfile ] && echo $?: "
327                             "%s", output)
328            except:
329                output = session.cmd("rm -f /commit_testfile")
330                raise error.TestFail("The commit_testfile exists on the "
331                                     "original file")
332            vm.destroy()
333
334            # Excecute the commit command
335            logging.info("Commiting image")
336            cmitcmd = "%s -f %s %s.%s" % (cmd, image_format, backing_file_name,
337                                          image_format)
338            try:
339                utils.system(cmitcmd)
340            except error.CmdError, e:
341                raise error.TestFail("Could not commit the backing file")
342
343            # Start a new VM, using image_name as its harddisk
344            vm_name = params.get('main_vm')
345            virt_env_process.preprocess_vm(test, params, env, vm_name)
346            vm = env.get_vm(vm_name)
347            vm.create()
348            timeout = int(params.get("login_timeout", 360))
349            session = vm.wait_for_login(timeout=timeout)
350            try:
351                output = session.cmd("[ -e /commit_testfile ] && echo $?")
352                logging.info("Output of [ -e /commit_testfile ] && echo $?: %s",
353                             output)
354                session.cmd("rm -f /commit_testfile")
355            except:
356                raise error.TestFail("Could not find commit_testfile after a "
357                                     "commit")
358            vm.destroy()
359
360        finally:
361            # Remove the backing file
362            if os.path.isfile(backing_file):
363                os.remove(backing_file)
364
365
366    def _rebase(cmd, img_name, base_img, backing_fmt, mode="unsafe"):
367        """
368        Simple wrapper of 'qemu-img rebase'.
369
370        @param cmd: qemu-img base command.
371        @param img_name: image name to be rebased
372        @param base_img: indicates the base image
373        @param backing_fmt: the format of base image
374        @param mode: rebase mode: safe mode, unsafe mode
375        """
376        cmd += " rebase"
377        if mode == "unsafe":
378            cmd += " -u"
379        cmd += " -b %s -F %s %s" % (base_img, backing_fmt, img_name)
380        logging.info("Trying to rebase '%s' to '%s'...", img_name, base_img)
381        s, o = commands.getstatusoutput(cmd)
382        if s != 0:
383            raise error.TestError("Failed to rebase '%s' to '%s': %s" %
384                                               (img_name, base_img, o))
385
386
387    def rebase_test(cmd):
388        """
389        Subcommand 'qemu-img rebase' test
390
391        Change the backing file of a snapshot image in "unsafe mode":
392        Assume the previous backing file had missed and we just have to change
393        reference of snapshot to new one. After change the backing file of a
394        snapshot image in unsafe mode, the snapshot should work still.
395
396        @param cmd: qemu-img base command.
397        """
398        if not 'rebase' in utils.system_output(cmd + ' --help',
399                                               ignore_status=True):
400            raise error.TestNAError("Current kvm user space version does not"
401                                    " support 'rebase' subcommand")
402        sn_fmt = params.get("snapshot_format", "qcow2")
403        sn1 = params.get("image_name_snapshot1")
404        sn1 = virt_utils.get_path(test.bindir, sn1) + ".%s" % sn_fmt
405        base_img = virt_vm.get_image_filename(params, test.bindir)
406        _create(cmd, sn1, sn_fmt, base_img=base_img, base_img_fmt=image_format)
407
408        # Create snapshot2 based on snapshot1
409        sn2 = params.get("image_name_snapshot2")
410        sn2 = virt_utils.get_path(test.bindir, sn2) + ".%s" % sn_fmt
411        _create(cmd, sn2, sn_fmt, base_img=sn1, base_img_fmt=sn_fmt)
412
413        rebase_mode = params.get("rebase_mode")
414        if rebase_mode == "unsafe":
415            os.remove(sn1)
416
417        _rebase(cmd, sn2, base_img, image_format, mode=rebase_mode)
418
419        # Check sn2's format and backing_file
420        actual_base_img = _info(cmd, sn2, "backing file")
421        base_img_name = os.path.basename(params.get("image_name"))
422        if not base_img_name in actual_base_img:
423            raise error.TestFail("After rebase the backing_file of 'sn2' is "
424                                 "'%s' which is not expected as '%s'"
425                                 % (actual_base_img, base_img_name))
426        s, o = _check(cmd, sn2)
427        if not s:
428            raise error.TestFail("Check image '%s' failed after rebase;"
429                                 "got error: %s" % (sn2, o))
430        try:
431            os.remove(sn2)
432            os.remove(sn1)
433        except:
434            pass
435
436
437    # Here starts test
438    subcommand = params.get("subcommand")
439    eval("%s_test(cmd)" % subcommand)
440