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