qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2020-05-05' into staging

Block patches:
- Asynchronous copying for block-copy (i.e., the backup job)
- Allow resizing of qcow2 images when they have internal snapshots
- iotests: Logging improvements for Python tests
- iotest 153 fix, and block comment cleanups

# gpg: Signature made Tue 05 May 2020 13:56:58 BST
# gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40
# gpg: issuer "mreitz@redhat.com"
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40

* remotes/maxreitz/tags/pull-block-2020-05-05: (24 commits)
block/block-copy: use aio-task-pool API
block/block-copy: refactor task creation
block/block-copy: add state pointer to BlockCopyTask
block/block-copy: alloc task on each iteration
block/block-copy: rename in-flight requests to tasks
Fix iotest 153
block: Comment cleanups
qcow2: Tweak comment about bitmaps vs. resize
qcow2: Allow resize of images with internal snapshots
block: Add blk_new_with_bs() helper
iotests: use python logging for iotests.log()
iotests: Mark verify functions as private
iotest 258: use script_main
iotests: add script_initialize
iotests: add hmp helper with logging
iotests: limit line length to 79 chars
iotests: touch up log function signature
iotests: drop pre-Python 3.4 compatibility code
iotests: alphabetize standard imports
iotests: add pylintrc file
...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

+727 -385
+23
block/block-backend.c
··· 356 356 } 357 357 358 358 /* 359 + * Create a new BlockBackend connected to an existing BlockDriverState. 360 + * 361 + * @perm is a bitmasks of BLK_PERM_* constants which describes the 362 + * permissions to request for @bs that is attached to this 363 + * BlockBackend. @shared_perm is a bitmask which describes which 364 + * permissions may be granted to other users of the attached node. 365 + * Both sets of permissions can be changed later using blk_set_perm(). 366 + * 367 + * Return the new BlockBackend on success, null on failure. 368 + */ 369 + BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, 370 + uint64_t shared_perm, Error **errp) 371 + { 372 + BlockBackend *blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm); 373 + 374 + if (blk_insert_bs(blk, bs, errp) < 0) { 375 + blk_unref(blk); 376 + return NULL; 377 + } 378 + return blk; 379 + } 380 + 381 + /* 359 382 * Creates a new BlockBackend, opens a new BlockDriverState, and connects both. 360 383 * The new BlockBackend is in the main AioContext. 361 384 *
+194 -85
block/block-copy.c
··· 19 19 #include "block/block-copy.h" 20 20 #include "sysemu/block-backend.h" 21 21 #include "qemu/units.h" 22 + #include "qemu/coroutine.h" 23 + #include "block/aio_task.h" 22 24 23 25 #define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB) 24 26 #define BLOCK_COPY_MAX_BUFFER (1 * MiB) 25 27 #define BLOCK_COPY_MAX_MEM (128 * MiB) 28 + #define BLOCK_COPY_MAX_WORKERS 64 29 + 30 + static coroutine_fn int block_copy_task_entry(AioTask *task); 26 31 27 - typedef struct BlockCopyInFlightReq { 32 + typedef struct BlockCopyCallState { 33 + bool failed; 34 + bool error_is_read; 35 + } BlockCopyCallState; 36 + 37 + typedef struct BlockCopyTask { 38 + AioTask task; 39 + 40 + BlockCopyState *s; 41 + BlockCopyCallState *call_state; 28 42 int64_t offset; 29 43 int64_t bytes; 30 - QLIST_ENTRY(BlockCopyInFlightReq) list; 31 - CoQueue wait_queue; /* coroutines blocked on this request */ 32 - } BlockCopyInFlightReq; 44 + bool zeroes; 45 + QLIST_ENTRY(BlockCopyTask) list; 46 + CoQueue wait_queue; /* coroutines blocked on this task */ 47 + } BlockCopyTask; 48 + 49 + static int64_t task_end(BlockCopyTask *task) 50 + { 51 + return task->offset + task->bytes; 52 + } 33 53 34 54 typedef struct BlockCopyState { 35 55 /* ··· 45 65 bool use_copy_range; 46 66 int64_t copy_size; 47 67 uint64_t len; 48 - QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; 68 + QLIST_HEAD(, BlockCopyTask) tasks; 49 69 50 70 BdrvRequestFlags write_flags; 51 71 ··· 73 93 SharedResource *mem; 74 94 } BlockCopyState; 75 95 76 - static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, 77 - int64_t offset, 78 - int64_t bytes) 96 + static BlockCopyTask *find_conflicting_task(BlockCopyState *s, 97 + int64_t offset, int64_t bytes) 79 98 { 80 - BlockCopyInFlightReq *req; 99 + BlockCopyTask *t; 81 100 82 - QLIST_FOREACH(req, &s->inflight_reqs, list) { 83 - if (offset + bytes > req->offset && offset < req->offset + req->bytes) { 84 - return req; 101 + QLIST_FOREACH(t, &s->tasks, list) { 102 + if (offset + bytes > t->offset && offset < t->offset + t->bytes) { 103 + return t; 85 104 } 86 105 } 87 106 ··· 89 108 } 90 109 91 110 /* 92 - * If there are no intersecting requests return false. Otherwise, wait for the 93 - * first found intersecting request to finish and return true. 111 + * If there are no intersecting tasks return false. Otherwise, wait for the 112 + * first found intersecting tasks to finish and return true. 94 113 */ 95 114 static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset, 96 115 int64_t bytes) 97 116 { 98 - BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes); 117 + BlockCopyTask *task = find_conflicting_task(s, offset, bytes); 99 118 100 - if (!req) { 119 + if (!task) { 101 120 return false; 102 121 } 103 122 104 - qemu_co_queue_wait(&req->wait_queue, NULL); 123 + qemu_co_queue_wait(&task->wait_queue, NULL); 105 124 106 125 return true; 107 126 } 108 127 109 - /* Called only on full-dirty region */ 110 - static void block_copy_inflight_req_begin(BlockCopyState *s, 111 - BlockCopyInFlightReq *req, 112 - int64_t offset, int64_t bytes) 128 + /* 129 + * Search for the first dirty area in offset/bytes range and create task at 130 + * the beginning of it. 131 + */ 132 + static BlockCopyTask *block_copy_task_create(BlockCopyState *s, 133 + BlockCopyCallState *call_state, 134 + int64_t offset, int64_t bytes) 113 135 { 114 - assert(!find_conflicting_inflight_req(s, offset, bytes)); 136 + BlockCopyTask *task; 137 + 138 + if (!bdrv_dirty_bitmap_next_dirty_area(s->copy_bitmap, 139 + offset, offset + bytes, 140 + s->copy_size, &offset, &bytes)) 141 + { 142 + return NULL; 143 + } 144 + 145 + /* region is dirty, so no existent tasks possible in it */ 146 + assert(!find_conflicting_task(s, offset, bytes)); 115 147 116 148 bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); 117 149 s->in_flight_bytes += bytes; 118 150 119 - req->offset = offset; 120 - req->bytes = bytes; 121 - qemu_co_queue_init(&req->wait_queue); 122 - QLIST_INSERT_HEAD(&s->inflight_reqs, req, list); 151 + task = g_new(BlockCopyTask, 1); 152 + *task = (BlockCopyTask) { 153 + .task.func = block_copy_task_entry, 154 + .s = s, 155 + .call_state = call_state, 156 + .offset = offset, 157 + .bytes = bytes, 158 + }; 159 + qemu_co_queue_init(&task->wait_queue); 160 + QLIST_INSERT_HEAD(&s->tasks, task, list); 161 + 162 + return task; 123 163 } 124 164 125 165 /* 126 - * block_copy_inflight_req_shrink 166 + * block_copy_task_shrink 127 167 * 128 - * Drop the tail of the request to be handled later. Set dirty bits back and 129 - * wake up all requests waiting for us (may be some of them are not intersecting 130 - * with shrunk request) 168 + * Drop the tail of the task to be handled later. Set dirty bits back and 169 + * wake up all tasks waiting for us (may be some of them are not intersecting 170 + * with shrunk task) 131 171 */ 132 - static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s, 133 - BlockCopyInFlightReq *req, int64_t new_bytes) 172 + static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task, 173 + int64_t new_bytes) 134 174 { 135 - if (new_bytes == req->bytes) { 175 + if (new_bytes == task->bytes) { 136 176 return; 137 177 } 138 178 139 - assert(new_bytes > 0 && new_bytes < req->bytes); 179 + assert(new_bytes > 0 && new_bytes < task->bytes); 140 180 141 - s->in_flight_bytes -= req->bytes - new_bytes; 142 - bdrv_set_dirty_bitmap(s->copy_bitmap, 143 - req->offset + new_bytes, req->bytes - new_bytes); 181 + task->s->in_flight_bytes -= task->bytes - new_bytes; 182 + bdrv_set_dirty_bitmap(task->s->copy_bitmap, 183 + task->offset + new_bytes, task->bytes - new_bytes); 144 184 145 - req->bytes = new_bytes; 146 - qemu_co_queue_restart_all(&req->wait_queue); 185 + task->bytes = new_bytes; 186 + qemu_co_queue_restart_all(&task->wait_queue); 147 187 } 148 188 149 - static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s, 150 - BlockCopyInFlightReq *req, 151 - int ret) 189 + static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret) 152 190 { 153 - s->in_flight_bytes -= req->bytes; 191 + task->s->in_flight_bytes -= task->bytes; 154 192 if (ret < 0) { 155 - bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes); 193 + bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes); 156 194 } 157 - QLIST_REMOVE(req, list); 158 - qemu_co_queue_restart_all(&req->wait_queue); 195 + QLIST_REMOVE(task, list); 196 + qemu_co_queue_restart_all(&task->wait_queue); 159 197 } 160 198 161 199 void block_copy_state_free(BlockCopyState *s) ··· 223 261 s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); 224 262 } 225 263 226 - QLIST_INIT(&s->inflight_reqs); 264 + QLIST_INIT(&s->tasks); 227 265 228 266 return s; 229 267 } ··· 243 281 } 244 282 245 283 /* 284 + * Takes ownership of @task 285 + * 286 + * If pool is NULL directly run the task, otherwise schedule it into the pool. 287 + * 288 + * Returns: task.func return code if pool is NULL 289 + * otherwise -ECANCELED if pool status is bad 290 + * otherwise 0 (successfully scheduled) 291 + */ 292 + static coroutine_fn int block_copy_task_run(AioTaskPool *pool, 293 + BlockCopyTask *task) 294 + { 295 + if (!pool) { 296 + int ret = task->task.func(&task->task); 297 + 298 + g_free(task); 299 + return ret; 300 + } 301 + 302 + aio_task_pool_wait_slot(pool); 303 + if (aio_task_pool_status(pool) < 0) { 304 + co_put_to_shres(task->s->mem, task->bytes); 305 + block_copy_task_end(task, -ECANCELED); 306 + g_free(task); 307 + return -ECANCELED; 308 + } 309 + 310 + aio_task_pool_start_task(pool, &task->task); 311 + 312 + return 0; 313 + } 314 + 315 + /* 246 316 * block_copy_do_copy 247 317 * 248 318 * Do copy of cluster-aligned chunk. Requested region is allowed to exceed ··· 345 415 return ret; 346 416 } 347 417 418 + static coroutine_fn int block_copy_task_entry(AioTask *task) 419 + { 420 + BlockCopyTask *t = container_of(task, BlockCopyTask, task); 421 + bool error_is_read; 422 + int ret; 423 + 424 + ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes, 425 + &error_is_read); 426 + if (ret < 0 && !t->call_state->failed) { 427 + t->call_state->failed = true; 428 + t->call_state->error_is_read = error_is_read; 429 + } else { 430 + progress_work_done(t->s->progress, t->bytes); 431 + t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque); 432 + } 433 + co_put_to_shres(t->s->mem, t->bytes); 434 + block_copy_task_end(t, ret); 435 + 436 + return ret; 437 + } 438 + 348 439 static int block_copy_block_status(BlockCopyState *s, int64_t offset, 349 440 int64_t bytes, int64_t *pnum) 350 441 { ··· 462 553 { 463 554 int ret = 0; 464 555 bool found_dirty = false; 556 + int64_t end = offset + bytes; 557 + AioTaskPool *aio = NULL; 558 + BlockCopyCallState call_state = {false, false}; 465 559 466 560 /* 467 561 * block_copy() user is responsible for keeping source and target in same ··· 473 567 assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); 474 568 assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); 475 569 476 - while (bytes) { 477 - BlockCopyInFlightReq req; 478 - int64_t next_zero, cur_bytes, status_bytes; 570 + while (bytes && aio_task_pool_status(aio) == 0) { 571 + BlockCopyTask *task; 572 + int64_t status_bytes; 479 573 480 - if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) { 481 - trace_block_copy_skip(s, offset); 482 - offset += s->cluster_size; 483 - bytes -= s->cluster_size; 484 - continue; /* already copied */ 574 + task = block_copy_task_create(s, &call_state, offset, bytes); 575 + if (!task) { 576 + /* No more dirty bits in the bitmap */ 577 + trace_block_copy_skip_range(s, offset, bytes); 578 + break; 579 + } 580 + if (task->offset > offset) { 581 + trace_block_copy_skip_range(s, offset, task->offset - offset); 485 582 } 486 583 487 584 found_dirty = true; 488 585 489 - cur_bytes = MIN(bytes, s->copy_size); 490 - 491 - next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset, 492 - cur_bytes); 493 - if (next_zero >= 0) { 494 - assert(next_zero > offset); /* offset is dirty */ 495 - assert(next_zero < offset + cur_bytes); /* no need to do MIN() */ 496 - cur_bytes = next_zero - offset; 586 + ret = block_copy_block_status(s, task->offset, task->bytes, 587 + &status_bytes); 588 + assert(ret >= 0); /* never fail */ 589 + if (status_bytes < task->bytes) { 590 + block_copy_task_shrink(task, status_bytes); 497 591 } 498 - block_copy_inflight_req_begin(s, &req, offset, cur_bytes); 499 - 500 - ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes); 501 - assert(ret >= 0); /* never fail */ 502 - cur_bytes = MIN(cur_bytes, status_bytes); 503 - block_copy_inflight_req_shrink(s, &req, cur_bytes); 504 592 if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) { 505 - block_copy_inflight_req_end(s, &req, 0); 593 + block_copy_task_end(task, 0); 594 + g_free(task); 506 595 progress_set_remaining(s->progress, 507 596 bdrv_get_dirty_count(s->copy_bitmap) + 508 597 s->in_flight_bytes); 509 - trace_block_copy_skip_range(s, offset, status_bytes); 510 - offset += status_bytes; 511 - bytes -= status_bytes; 598 + trace_block_copy_skip_range(s, task->offset, task->bytes); 599 + offset = task_end(task); 600 + bytes = end - offset; 512 601 continue; 513 602 } 603 + task->zeroes = ret & BDRV_BLOCK_ZERO; 514 604 515 - trace_block_copy_process(s, offset); 605 + trace_block_copy_process(s, task->offset); 606 + 607 + co_get_from_shres(s->mem, task->bytes); 608 + 609 + offset = task_end(task); 610 + bytes = end - offset; 611 + 612 + if (!aio && bytes) { 613 + aio = aio_task_pool_new(BLOCK_COPY_MAX_WORKERS); 614 + } 516 615 517 - co_get_from_shres(s->mem, cur_bytes); 518 - ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO, 519 - error_is_read); 520 - co_put_to_shres(s->mem, cur_bytes); 521 - block_copy_inflight_req_end(s, &req, ret); 616 + ret = block_copy_task_run(aio, task); 522 617 if (ret < 0) { 523 - return ret; 618 + goto out; 524 619 } 620 + } 525 621 526 - progress_work_done(s->progress, cur_bytes); 527 - s->progress_bytes_callback(cur_bytes, s->progress_opaque); 528 - offset += cur_bytes; 529 - bytes -= cur_bytes; 622 + out: 623 + if (aio) { 624 + aio_task_pool_wait_all(aio); 625 + 626 + /* 627 + * We are not really interested in -ECANCELED returned from 628 + * block_copy_task_run. If it fails, it means some task already failed 629 + * for real reason, let's return first failure. 630 + * Still, assert that we don't rewrite failure by success. 631 + */ 632 + assert(ret == 0 || aio_task_pool_status(aio) < 0); 633 + ret = aio_task_pool_status(aio); 634 + 635 + aio_task_pool_free(aio); 636 + } 637 + if (error_is_read && ret < 0) { 638 + *error_is_read = call_state.error_is_read; 530 639 } 531 640 532 - return found_dirty; 641 + return ret < 0 ? ret : found_dirty; 533 642 } 534 643 535 644 /*
+4 -5
block/crypto.c
··· 261 261 QCryptoBlock *crypto = NULL; 262 262 struct BlockCryptoCreateData data; 263 263 264 - blk = blk_new(bdrv_get_aio_context(bs), 265 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 266 - 267 - ret = blk_insert_bs(blk, bs, errp); 268 - if (ret < 0) { 264 + blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, 265 + errp); 266 + if (!blk) { 267 + ret = -EPERM; 269 268 goto cleanup; 270 269 } 271 270
+2 -1
block/io.c
··· 960 960 * flags are passed through to bdrv_pwrite_zeroes (e.g. BDRV_REQ_MAY_UNMAP, 961 961 * BDRV_REQ_FUA). 962 962 * 963 - * Returns < 0 on error, 0 on success. For error codes see bdrv_write(). 963 + * Returns < 0 on error, 0 on success. For error codes see bdrv_pwrite(). 964 964 */ 965 965 int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) 966 966 { ··· 994 994 } 995 995 } 996 996 997 + /* return < 0 if error. See bdrv_pwrite() for the return codes */ 997 998 int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) 998 999 { 999 1000 int ret;
+4 -4
block/parallels.c
··· 559 559 return -EIO; 560 560 } 561 561 562 - blk = blk_new(bdrv_get_aio_context(bs), 563 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 564 - ret = blk_insert_bs(blk, bs, errp); 565 - if (ret < 0) { 562 + blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, 563 + errp); 564 + if (!blk) { 565 + ret = -EPERM; 566 566 goto out; 567 567 } 568 568 blk_set_allow_write_beyond_eof(blk, true);
+4 -4
block/qcow.c
··· 849 849 return -EIO; 850 850 } 851 851 852 - qcow_blk = blk_new(bdrv_get_aio_context(bs), 853 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 854 - ret = blk_insert_bs(qcow_blk, bs, errp); 855 - if (ret < 0) { 852 + qcow_blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, 853 + BLK_PERM_ALL, errp); 854 + if (!qcow_blk) { 855 + ret = -EPERM; 856 856 goto exit; 857 857 } 858 858 blk_set_allow_write_beyond_eof(qcow_blk, true);
+1 -1
block/qcow2-refcount.c
··· 2660 2660 * - 0 if writing to this offset will not affect the mentioned metadata 2661 2661 * - a positive QCow2MetadataOverlap value indicating one overlapping section 2662 2662 * - a negative value (-errno) indicating an error while performing a check, 2663 - * e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2 2663 + * e.g. when bdrv_pread failed on QCOW2_OL_INACTIVE_L2 2664 2664 */ 2665 2665 int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, 2666 2666 int64_t size)
+16 -4
block/qcow2-snapshot.c
··· 23 23 */ 24 24 25 25 #include "qemu/osdep.h" 26 + #include "sysemu/block-backend.h" 26 27 #include "qapi/error.h" 27 28 #include "qcow2.h" 28 29 #include "qemu/bswap.h" ··· 775 776 } 776 777 777 778 if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { 778 - error_report("qcow2: Loading snapshots with different disk " 779 - "size is not implemented"); 780 - ret = -ENOTSUP; 781 - goto fail; 779 + BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, 780 + &local_err); 781 + if (!blk) { 782 + error_report_err(local_err); 783 + ret = -ENOTSUP; 784 + goto fail; 785 + } 786 + 787 + ret = blk_truncate(blk, sn->disk_size, true, PREALLOC_MODE_OFF, 0, 788 + &local_err); 789 + blk_unref(blk); 790 + if (ret < 0) { 791 + error_report_err(local_err); 792 + goto fail; 793 + } 782 794 } 783 795 784 796 /*
+31 -14
block/qcow2.c
··· 3405 3405 } 3406 3406 3407 3407 /* Create BlockBackend to write to the image */ 3408 - blk = blk_new(bdrv_get_aio_context(bs), 3409 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 3410 - ret = blk_insert_bs(blk, bs, errp); 3411 - if (ret < 0) { 3408 + blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, 3409 + errp); 3410 + if (!blk) { 3411 + ret = -EPERM; 3412 3412 goto out; 3413 3413 } 3414 3414 blk_set_allow_write_beyond_eof(blk, true); ··· 3989 3989 3990 3990 qemu_co_mutex_lock(&s->lock); 3991 3991 3992 - /* cannot proceed if image has snapshots */ 3993 - if (s->nb_snapshots) { 3994 - error_setg(errp, "Can't resize an image which has snapshots"); 3992 + /* 3993 + * Even though we store snapshot size for all images, it was not 3994 + * required until v3, so it is not safe to proceed for v2. 3995 + */ 3996 + if (s->nb_snapshots && s->qcow_version < 3) { 3997 + error_setg(errp, "Can't resize a v2 image which has snapshots"); 3995 3998 ret = -ENOTSUP; 3996 3999 goto fail; 3997 4000 } 3998 4001 3999 - /* cannot proceed if image has bitmaps */ 4002 + /* See qcow2-bitmap.c for which bitmap scenarios prevent a resize. */ 4000 4003 if (qcow2_truncate_bitmaps_check(bs, errp)) { 4001 4004 ret = -ENOTSUP; 4002 4005 goto fail; ··· 5005 5008 BDRVQcow2State *s = bs->opaque; 5006 5009 int current_version = s->qcow_version; 5007 5010 int ret; 5011 + int i; 5008 5012 5009 5013 /* This is qcow2_downgrade(), not qcow2_upgrade() */ 5010 5014 assert(target_version < current_version); ··· 5022 5026 return -ENOTSUP; 5023 5027 } 5024 5028 5029 + /* 5030 + * If any internal snapshot has a different size than the current 5031 + * image size, or VM state size that exceeds 32 bits, downgrading 5032 + * is unsafe. Even though we would still use v3-compliant output 5033 + * to preserve that data, other v2 programs might not realize 5034 + * those optional fields are important. 5035 + */ 5036 + for (i = 0; i < s->nb_snapshots; i++) { 5037 + if (s->snapshots[i].vm_state_size > UINT32_MAX || 5038 + s->snapshots[i].disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { 5039 + error_setg(errp, "Internal snapshots prevent downgrade of image"); 5040 + return -ENOTSUP; 5041 + } 5042 + } 5043 + 5025 5044 /* clear incompatible features */ 5026 5045 if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { 5027 5046 ret = qcow2_mark_clean(bs); ··· 5412 5431 } 5413 5432 5414 5433 if (new_size) { 5415 - BlockBackend *blk = blk_new(bdrv_get_aio_context(bs), 5416 - BLK_PERM_RESIZE, BLK_PERM_ALL); 5417 - ret = blk_insert_bs(blk, bs, errp); 5418 - if (ret < 0) { 5419 - blk_unref(blk); 5420 - return ret; 5434 + BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, 5435 + errp); 5436 + if (!blk) { 5437 + return -EPERM; 5421 5438 } 5422 5439 5423 5440 /*
+4 -4
block/qed.c
··· 651 651 return -EIO; 652 652 } 653 653 654 - blk = blk_new(bdrv_get_aio_context(bs), 655 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 656 - ret = blk_insert_bs(blk, bs, errp); 657 - if (ret < 0) { 654 + blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, 655 + errp); 656 + if (!blk) { 657 + ret = -EPERM; 658 658 goto out; 659 659 } 660 660 blk_set_allow_write_beyond_eof(blk, true);
+5 -5
block/sheepdog.c
··· 1803 1803 void *buf = NULL; 1804 1804 int ret; 1805 1805 1806 - blk = blk_new(bdrv_get_aio_context(bs), 1807 - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, 1808 - BLK_PERM_ALL); 1806 + blk = blk_new_with_bs(bs, 1807 + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, 1808 + BLK_PERM_ALL, errp); 1809 1809 1810 - ret = blk_insert_bs(blk, bs, errp); 1811 - if (ret < 0) { 1810 + if (!blk) { 1811 + ret = -EPERM; 1812 1812 goto out_with_err_set; 1813 1813 } 1814 1814
+4 -4
block/vdi.c
··· 804 804 goto exit; 805 805 } 806 806 807 - blk = blk_new(bdrv_get_aio_context(bs_file), 808 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 809 - ret = blk_insert_bs(blk, bs_file, errp); 810 - if (ret < 0) { 807 + blk = blk_new_with_bs(bs_file, BLK_PERM_WRITE | BLK_PERM_RESIZE, 808 + BLK_PERM_ALL, errp); 809 + if (!blk) { 810 + ret = -EPERM; 811 811 goto exit; 812 812 } 813 813
+4 -4
block/vhdx.c
··· 1983 1983 return -EIO; 1984 1984 } 1985 1985 1986 - blk = blk_new(bdrv_get_aio_context(bs), 1987 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 1988 - ret = blk_insert_bs(blk, bs, errp); 1989 - if (ret < 0) { 1986 + blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, 1987 + errp); 1988 + if (!blk) { 1989 + ret = -EPERM; 1990 1990 goto delete_and_exit; 1991 1991 } 1992 1992 blk_set_allow_write_beyond_eof(blk, true);
+4 -5
block/vmdk.c
··· 2717 2717 if (!bs) { 2718 2718 return NULL; 2719 2719 } 2720 - blk = blk_new(bdrv_get_aio_context(bs), 2721 - BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, 2722 - BLK_PERM_ALL); 2723 - if (blk_insert_bs(blk, bs, errp)) { 2724 - bdrv_unref(bs); 2720 + blk = blk_new_with_bs(bs, 2721 + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, 2722 + BLK_PERM_ALL, errp); 2723 + if (!blk) { 2725 2724 return NULL; 2726 2725 } 2727 2726 blk_set_allow_write_beyond_eof(blk, true);
+4 -4
block/vpc.c
··· 1012 1012 return -EIO; 1013 1013 } 1014 1014 1015 - blk = blk_new(bdrv_get_aio_context(bs), 1016 - BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); 1017 - ret = blk_insert_bs(blk, bs, errp); 1018 - if (ret < 0) { 1015 + blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, 1016 + errp); 1017 + if (!blk) { 1018 + ret = -EPERM; 1019 1019 goto out; 1020 1020 } 1021 1021 blk_set_allow_write_beyond_eof(blk, true);
+5 -5
block/vvfat.c
··· 2148 2148 * - get modified FAT 2149 2149 * - compare the two FATs (TODO) 2150 2150 * - get buffer for marking used clusters 2151 - * - recurse direntries from root (using bs->bdrv_read to make 2151 + * - recurse direntries from root (using bs->bdrv_pread to make 2152 2152 * sure to get the new data) 2153 2153 * - check that the FAT agrees with the size 2154 2154 * - count the number of clusters occupied by this directory and ··· 2913 2913 /* 2914 2914 * synchronize mapping with new state: 2915 2915 * 2916 - * - copy FAT (with bdrv_read) 2916 + * - copy FAT (with bdrv_pread) 2917 2917 * - mark all filenames corresponding to mappings as deleted 2918 - * - recurse direntries from root (using bs->bdrv_read) 2918 + * - recurse direntries from root (using bs->bdrv_pread) 2919 2919 * - delete files corresponding to mappings marked as deleted 2920 2920 */ 2921 2921 static int do_commit(BDRVVVFATState* s) ··· 2935 2935 return ret; 2936 2936 } 2937 2937 2938 - /* copy FAT (with bdrv_read) */ 2938 + /* copy FAT (with bdrv_pread) */ 2939 2939 memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat); 2940 2940 2941 - /* recurse direntries from root (using bs->bdrv_read) */ 2941 + /* recurse direntries from root (using bs->bdrv_pread) */ 2942 2942 ret = commit_direntries(s, 0, -1); 2943 2943 if (ret) { 2944 2944 fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
+2 -4
blockdev.c
··· 2711 2711 BlockBackend *blk = NULL; 2712 2712 BlockDriverState *bs; 2713 2713 AioContext *aio_context; 2714 - int ret; 2715 2714 2716 2715 bs = bdrv_lookup_bs(has_device ? device : NULL, 2717 2716 has_node_name ? node_name : NULL, ··· 2734 2733 goto out; 2735 2734 } 2736 2735 2737 - blk = blk_new(bdrv_get_aio_context(bs), BLK_PERM_RESIZE, BLK_PERM_ALL); 2738 - ret = blk_insert_bs(blk, bs, errp); 2739 - if (ret < 0) { 2736 + blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp); 2737 + if (!blk) { 2740 2738 goto out; 2741 2739 } 2742 2740
+2 -5
blockjob.c
··· 397 397 { 398 398 BlockBackend *blk; 399 399 BlockJob *job; 400 - int ret; 401 400 402 401 if (job_id == NULL && !(flags & JOB_INTERNAL)) { 403 402 job_id = bdrv_get_device_name(bs); 404 403 } 405 404 406 - blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm); 407 - ret = blk_insert_bs(blk, bs, errp); 408 - if (ret < 0) { 409 - blk_unref(blk); 405 + blk = blk_new_with_bs(bs, perm, shared_perm, errp); 406 + if (!blk) { 410 407 return NULL; 411 408 } 412 409
+2
include/sysemu/block-backend.h
··· 77 77 } BlockBackendPublic; 78 78 79 79 BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm); 80 + BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, 81 + uint64_t shared_perm, Error **errp); 80 82 BlockBackend *blk_new_open(const char *filename, const char *reference, 81 83 QDict *options, int flags, Error **errp); 82 84 int blk_get_refcnt(BlockBackend *blk);
+1 -1
tests/qemu-iotests/001
··· 1 1 #!/usr/bin/env bash 2 2 # 3 - # Test simple read/write using plain bdrv_read/bdrv_write 3 + # Test simple read/write using plain bdrv_pread/bdrv_pwrite 4 4 # 5 5 # Copyright (C) 2009 Red Hat, Inc. 6 6 #
+2 -2
tests/qemu-iotests/030
··· 411 411 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) 412 412 self.assert_qmp(result, 'return', {}) 413 413 414 - self.vm.run_job(job='drive0', auto_dismiss=True, use_log=False) 415 - self.vm.run_job(job='node4', auto_dismiss=True, use_log=False) 414 + self.vm.run_job(job='drive0', auto_dismiss=True) 415 + self.vm.run_job(job='node4', auto_dismiss=True) 416 416 self.assert_no_active_block_jobs() 417 417 418 418 # Test a block-stream and a block-commit job in parallel
+1 -1
tests/qemu-iotests/052
··· 1 1 #!/usr/bin/env bash 2 2 # 3 - # Test bdrv_read/bdrv_write using BDRV_O_SNAPSHOT 3 + # Test bdrv_pread/bdrv_pwrite using BDRV_O_SNAPSHOT 4 4 # 5 5 # Copyright (C) 2013 Red Hat, Inc. 6 6 #
+2 -1
tests/qemu-iotests/055
··· 469 469 qemu_img('create', '-f', fmt, blockdev_target_img, 470 470 str(TestDriveCompression.image_len), *args) 471 471 if attach_target: 472 - self.vm.add_drive(blockdev_target_img, format=fmt, interface="none") 472 + self.vm.add_drive(blockdev_target_img, 473 + img_format=fmt, interface="none") 473 474 474 475 self.vm.launch() 475 476
+35
tests/qemu-iotests/061
··· 112 112 _check_test_img 113 113 114 114 echo 115 + echo "=== Testing resize with snapshots ===" 116 + echo 117 + _make_test_img -o "compat=0.10" 32M 118 + $QEMU_IO -c "write -P 0x2a 24M 64k" "$TEST_IMG" | _filter_qemu_io 119 + $QEMU_IMG snapshot -c foo "$TEST_IMG" 120 + $QEMU_IMG resize "$TEST_IMG" 64M && 121 + echo "unexpected pass" 122 + $PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' 123 + 124 + $QEMU_IMG amend -o "compat=1.1,size=128M" "$TEST_IMG" || 125 + echo "unexpected fail" 126 + $PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' 127 + 128 + $QEMU_IMG snapshot -c bar "$TEST_IMG" 129 + $QEMU_IMG resize --shrink "$TEST_IMG" 64M || 130 + echo "unexpected fail" 131 + $PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' 132 + 133 + $QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" && 134 + echo "unexpected pass" 135 + $PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' 136 + 137 + $QEMU_IMG snapshot -a bar "$TEST_IMG" || 138 + echo "unexpected fail" 139 + $PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' 140 + 141 + $QEMU_IMG snapshot -d bar "$TEST_IMG" 142 + $QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" || 143 + echo "unexpected fail" 144 + $PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' 145 + 146 + _check_test_img 147 + 148 + 149 + echo 115 150 echo "=== Testing dirty lazy_refcounts=off ===" 116 151 echo 117 152 _make_test_img -o "compat=1.1,lazy_refcounts=on" 64M
+28
tests/qemu-iotests/061.out
··· 271 271 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 272 272 No errors were found on the image. 273 273 274 + === Testing resize with snapshots === 275 + 276 + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 277 + wrote 65536/65536 bytes at offset 25165824 278 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 279 + qemu-img: Can't resize a v2 image which has snapshots 280 + version 2 281 + size 33554432 282 + nb_snapshots 1 283 + version 3 284 + size 134217728 285 + nb_snapshots 1 286 + Image resized. 287 + version 3 288 + size 67108864 289 + nb_snapshots 2 290 + qemu-img: Internal snapshots prevent downgrade of image 291 + version 3 292 + size 33554432 293 + nb_snapshots 2 294 + version 3 295 + size 134217728 296 + nb_snapshots 2 297 + version 2 298 + size 33554432 299 + nb_snapshots 1 300 + No errors were found on the image. 301 + 274 302 === Testing dirty lazy_refcounts=off === 275 303 276 304 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+1 -1
tests/qemu-iotests/134
··· 1 1 #!/usr/bin/env bash 2 2 # 3 - # Test encrypted read/write using plain bdrv_read/bdrv_write 3 + # Test encrypted read/write using plain bdrv_pread/bdrv_pwrite 4 4 # 5 5 # Copyright (C) 2015 Red Hat, Inc. 6 6 #
+1 -2
tests/qemu-iotests/149
··· 382 382 383 383 384 384 # Obviously we only work with the luks image format 385 - iotests.verify_image_format(supported_fmts=['luks']) 386 - iotests.verify_platform() 385 + iotests.script_initialize(supported_fmts=['luks']) 387 386 388 387 # We need sudo in order to run cryptsetup to create 389 388 # dm-crypt devices. This is safe to use on any
+1 -1
tests/qemu-iotests/153
··· 122 122 _run_cmd $QEMU_IMG check $L "${TEST_IMG}" 123 123 _run_cmd $QEMU_IMG compare $L "${TEST_IMG}" "${TEST_IMG}" 124 124 _run_cmd $QEMU_IMG map $L "${TEST_IMG}" 125 - _run_cmd $QEMU_IMG amend -o "" $L "${TEST_IMG}" 125 + _run_cmd $QEMU_IMG amend -o "size=$size" $L "${TEST_IMG}" 126 126 _run_cmd $QEMU_IMG commit $L "${TEST_IMG}" 127 127 _run_cmd $QEMU_IMG resize $L "${TEST_IMG}" $size 128 128 _run_cmd $QEMU_IMG rebase $L "${TEST_IMG}" -b "${TEST_IMG}.base"
+6 -6
tests/qemu-iotests/153.out
··· 56 56 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock 57 57 Is another process using the image [TEST_DIR/t.qcow2]? 58 58 59 - _qemu_img_wrapper amend -o TEST_DIR/t.qcow2 59 + _qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2 60 60 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock 61 61 Is another process using the image [TEST_DIR/t.qcow2]? 62 62 ··· 118 118 119 119 _qemu_img_wrapper map -U TEST_DIR/t.qcow2 120 120 121 - _qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2 121 + _qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2 122 122 qemu-img: unrecognized option '-U' 123 123 Try 'qemu-img --help' for more information 124 124 ··· 187 187 188 188 _qemu_img_wrapper map TEST_DIR/t.qcow2 189 189 190 - _qemu_img_wrapper amend -o TEST_DIR/t.qcow2 190 + _qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2 191 191 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock 192 192 Is another process using the image [TEST_DIR/t.qcow2]? 193 193 ··· 241 241 242 242 _qemu_img_wrapper map -U TEST_DIR/t.qcow2 243 243 244 - _qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2 244 + _qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2 245 245 qemu-img: unrecognized option '-U' 246 246 Try 'qemu-img --help' for more information 247 247 ··· 303 303 304 304 _qemu_img_wrapper map TEST_DIR/t.qcow2 305 305 306 - _qemu_img_wrapper amend -o TEST_DIR/t.qcow2 306 + _qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2 307 307 308 308 _qemu_img_wrapper commit TEST_DIR/t.qcow2 309 309 ··· 345 345 346 346 _qemu_img_wrapper map -U TEST_DIR/t.qcow2 347 347 348 - _qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2 348 + _qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2 349 349 qemu-img: unrecognized option '-U' 350 350 Try 'qemu-img --help' for more information 351 351
+1 -1
tests/qemu-iotests/155
··· 188 188 189 189 self.assert_qmp(result, 'return', {}) 190 190 191 - self.vm.run_job('mirror-job', use_log=False, auto_finalize=False, 191 + self.vm.run_job('mirror-job', auto_finalize=False, 192 192 pre_finalize=self.openBacking, auto_dismiss=True) 193 193 194 194 def testFull(self):
+1 -1
tests/qemu-iotests/188
··· 1 1 #!/usr/bin/env bash 2 2 # 3 - # Test encrypted read/write using plain bdrv_read/bdrv_write 3 + # Test encrypted read/write using plain bdrv_pread/bdrv_pwrite 4 4 # 5 5 # Copyright (C) 2017 Red Hat, Inc. 6 6 #
+2 -2
tests/qemu-iotests/194
··· 21 21 22 22 import iotests 23 23 24 - iotests.verify_image_format(supported_fmts=['qcow2', 'qed', 'raw']) 25 - iotests.verify_platform(['linux']) 24 + iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'], 25 + supported_platforms=['linux']) 26 26 27 27 with iotests.FilePath('source.img') as source_img_path, \ 28 28 iotests.FilePath('dest.img') as dest_img_path, \
+2 -2
tests/qemu-iotests/202
··· 24 24 25 25 import iotests 26 26 27 - iotests.verify_image_format(supported_fmts=['qcow2']) 28 - iotests.verify_platform(['linux']) 27 + iotests.script_initialize(supported_fmts=['qcow2'], 28 + supported_platforms=['linux']) 29 29 30 30 with iotests.FilePath('disk0.img') as disk0_img_path, \ 31 31 iotests.FilePath('disk1.img') as disk1_img_path, \
+2 -2
tests/qemu-iotests/203
··· 24 24 25 25 import iotests 26 26 27 - iotests.verify_image_format(supported_fmts=['qcow2']) 28 - iotests.verify_platform(['linux']) 27 + iotests.script_initialize(supported_fmts=['qcow2'], 28 + supported_platforms=['linux']) 29 29 30 30 with iotests.FilePath('disk0.img') as disk0_img_path, \ 31 31 iotests.FilePath('disk1.img') as disk1_img_path, \
+1 -1
tests/qemu-iotests/206
··· 23 23 import iotests 24 24 from iotests import imgfmt 25 25 26 - iotests.verify_image_format(supported_fmts=['qcow2']) 26 + iotests.script_initialize(supported_fmts=['qcow2']) 27 27 28 28 with iotests.FilePath('t.qcow2') as disk_path, \ 29 29 iotests.FilePath('t.qcow2.base') as backing_path, \
+4 -2
tests/qemu-iotests/207
··· 24 24 import subprocess 25 25 import re 26 26 27 - iotests.verify_image_format(supported_fmts=['raw']) 28 - iotests.verify_protocol(supported=['ssh']) 27 + iotests.script_initialize( 28 + supported_fmts=['raw'], 29 + supported_protocols=['ssh'], 30 + ) 29 31 30 32 def filter_hash(qmsg): 31 33 def _filter(key, value):
+1 -1
tests/qemu-iotests/208
··· 22 22 23 23 import iotests 24 24 25 - iotests.verify_image_format(supported_fmts=['generic']) 25 + iotests.script_initialize(supported_fmts=['generic']) 26 26 27 27 with iotests.FilePath('disk.img') as disk_img_path, \ 28 28 iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \
+1 -1
tests/qemu-iotests/209
··· 22 22 from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \ 23 23 file_path 24 24 25 - iotests.verify_image_format(supported_fmts=['qcow2']) 25 + iotests.script_initialize(supported_fmts=['qcow2']) 26 26 27 27 disk = file_path('disk') 28 28 nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir)
+4 -2
tests/qemu-iotests/210
··· 23 23 import iotests 24 24 from iotests import imgfmt 25 25 26 - iotests.verify_image_format(supported_fmts=['luks']) 27 - iotests.verify_protocol(supported=['file']) 26 + iotests.script_initialize( 27 + supported_fmts=['luks'], 28 + supported_protocols=['file'], 29 + ) 28 30 29 31 with iotests.FilePath('t.luks') as disk_path, \ 30 32 iotests.VM() as vm:
+4 -2
tests/qemu-iotests/211
··· 23 23 import iotests 24 24 from iotests import imgfmt 25 25 26 - iotests.verify_image_format(supported_fmts=['vdi']) 27 - iotests.verify_protocol(supported=['file']) 26 + iotests.script_initialize( 27 + supported_fmts=['vdi'], 28 + supported_protocols=['file'], 29 + ) 28 30 29 31 def blockdev_create(vm, options): 30 32 error = vm.blockdev_create(options)
+4 -2
tests/qemu-iotests/212
··· 23 23 import iotests 24 24 from iotests import imgfmt 25 25 26 - iotests.verify_image_format(supported_fmts=['parallels']) 27 - iotests.verify_protocol(supported=['file']) 26 + iotests.script_initialize( 27 + supported_fmts=['parallels'], 28 + supported_protocols=['file'], 29 + ) 28 30 29 31 with iotests.FilePath('t.parallels') as disk_path, \ 30 32 iotests.VM() as vm:
+4 -2
tests/qemu-iotests/213
··· 23 23 import iotests 24 24 from iotests import imgfmt 25 25 26 - iotests.verify_image_format(supported_fmts=['vhdx']) 27 - iotests.verify_protocol(supported=['file']) 26 + iotests.script_initialize( 27 + supported_fmts=['vhdx'], 28 + supported_protocols=['file'], 29 + ) 28 30 29 31 with iotests.FilePath('t.vhdx') as disk_path, \ 30 32 iotests.VM() as vm:
+2 -2
tests/qemu-iotests/216
··· 23 23 from iotests import log, qemu_img, qemu_io_silent 24 24 25 25 # Need backing file support 26 - iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk']) 27 - iotests.verify_platform(['linux']) 26 + iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'], 27 + supported_platforms=['linux']) 28 28 29 29 log('') 30 30 log('=== Copy-on-read across nodes ===')
+1 -1
tests/qemu-iotests/218
··· 29 29 import iotests 30 30 from iotests import log, qemu_img, qemu_io_silent 31 31 32 - iotests.verify_image_format(supported_fmts=['qcow2', 'raw']) 32 + iotests.script_initialize(supported_fmts=['qcow2', 'raw']) 33 33 34 34 35 35 # Launches the VM, adds two null-co nodes (source and target), and
+1 -1
tests/qemu-iotests/219
··· 21 21 22 22 import iotests 23 23 24 - iotests.verify_image_format(supported_fmts=['qcow2']) 24 + iotests.script_initialize(supported_fmts=['qcow2']) 25 25 26 26 img_size = 4 * 1024 * 1024 27 27
+4 -3
tests/qemu-iotests/222
··· 24 24 import iotests 25 25 from iotests import log, qemu_img, qemu_io, qemu_io_silent 26 26 27 - iotests.verify_platform(['linux']) 28 - iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 29 - 'vhdx', 'raw']) 27 + iotests.script_initialize( 28 + supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'], 29 + supported_platforms=['linux'], 30 + ) 30 31 31 32 patterns = [("0x5d", "0", "64k"), 32 33 ("0xd5", "1M", "64k"),
+2 -2
tests/qemu-iotests/224
··· 26 26 import json 27 27 28 28 # Need backing file support (for arbitrary backing formats) 29 - iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed']) 30 - iotests.verify_platform(['linux']) 29 + iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed'], 30 + supported_platforms=['linux']) 31 31 32 32 33 33 # There are two variations of this test:
+4 -2
tests/qemu-iotests/228
··· 25 25 filter_qmp_testfiles, filter_qmp_imgfmt 26 26 27 27 # Need backing file and change-backing-file support 28 - iotests.verify_image_format(supported_fmts=['qcow2', 'qed']) 29 - iotests.verify_platform(['linux']) 28 + iotests.script_initialize( 29 + supported_fmts=['qcow2', 'qed'], 30 + supported_platforms=['linux'], 31 + ) 30 32 31 33 32 34 def log_node_info(node):
+2 -2
tests/qemu-iotests/234
··· 23 23 import iotests 24 24 import os 25 25 26 - iotests.verify_image_format(supported_fmts=['qcow2']) 27 - iotests.verify_platform(['linux']) 26 + iotests.script_initialize(supported_fmts=['qcow2'], 27 + supported_platforms=['linux']) 28 28 29 29 with iotests.FilePath('img') as img_path, \ 30 30 iotests.FilePath('backing') as backing_path, \
+2 -2
tests/qemu-iotests/235
··· 27 27 28 28 from qemu.machine import QEMUMachine 29 29 30 + iotests.script_initialize(supported_fmts=['qcow2']) 31 + 30 32 # Note: 31 33 # This test was added to check that mirror dead-lock was fixed (see previous 32 34 # commit before this test addition). ··· 39 41 # 4. add iothread 40 42 41 43 size = 1 * 1024 * 1024 * 1024 42 - 43 - iotests.verify_image_format(supported_fmts=['qcow2']) 44 44 45 45 disk = file_path('disk') 46 46
+1 -1
tests/qemu-iotests/236
··· 22 22 import iotests 23 23 from iotests import log 24 24 25 - iotests.verify_image_format(supported_fmts=['generic']) 25 + iotests.script_initialize(supported_fmts=['generic']) 26 26 size = 64 * 1024 * 1024 27 27 granularity = 64 * 1024 28 28
+1 -1
tests/qemu-iotests/237
··· 24 24 import iotests 25 25 from iotests import imgfmt 26 26 27 - iotests.verify_image_format(supported_fmts=['vmdk']) 27 + iotests.script_initialize(supported_fmts=['vmdk']) 28 28 29 29 with iotests.FilePath('t.vmdk') as disk_path, \ 30 30 iotests.FilePath('t.vmdk.1') as extent1_path, \
+2
tests/qemu-iotests/238
··· 23 23 import iotests 24 24 from iotests import log 25 25 26 + iotests.script_initialize() 27 + 26 28 virtio_scsi_device = iotests.get_virtio_scsi_device() 27 29 28 30 vm = iotests.VM()
+1 -1
tests/qemu-iotests/242
··· 24 24 from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \ 25 25 file_path, img_info_log, log, filter_qemu_io 26 26 27 - iotests.verify_image_format(supported_fmts=['qcow2']) 27 + iotests.script_initialize(supported_fmts=['qcow2']) 28 28 29 29 disk = file_path('disk') 30 30 chunk = 256 * 1024
+1
tests/qemu-iotests/245
··· 1027 1027 self.run_test_iothreads(None, 'iothread0') 1028 1028 1029 1029 if __name__ == '__main__': 1030 + iotests.activate_logging() 1030 1031 iotests.main(supported_fmts=["qcow2"], 1031 1032 supported_protocols=["file"])
+5 -5
tests/qemu-iotests/245.out
··· 1 - ..................... 2 - ---------------------------------------------------------------------- 3 - Ran 21 tests 4 - 5 - OK 6 1 {"execute": "job-finalize", "arguments": {"id": "commit0"}} 7 2 {"return": {}} 8 3 {"data": {"id": "commit0", "type": "commit"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ··· 15 10 {"return": {}} 16 11 {"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} 17 12 {"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} 13 + ..................... 14 + ---------------------------------------------------------------------- 15 + Ran 21 tests 16 + 17 + OK
+1 -1
tests/qemu-iotests/246
··· 22 22 import iotests 23 23 from iotests import log 24 24 25 - iotests.verify_image_format(supported_fmts=['qcow2']) 25 + iotests.script_initialize(supported_fmts=['qcow2']) 26 26 size = 64 * 1024 * 1024 * 1024 27 27 gran_small = 32 * 1024 28 28 gran_large = 128 * 1024
+1 -1
tests/qemu-iotests/248
··· 21 21 import iotests 22 22 from iotests import qemu_img_create, qemu_io, file_path, filter_qmp_testfiles 23 23 24 - iotests.verify_image_format(supported_fmts=['qcow2']) 24 + iotests.script_initialize(supported_fmts=['qcow2']) 25 25 26 26 source, target = file_path('source', 'target') 27 27 size = 5 * 1024 * 1024
+1 -1
tests/qemu-iotests/254
··· 21 21 import iotests 22 22 from iotests import qemu_img_create, file_path, log 23 23 24 - iotests.verify_image_format(supported_fmts=['qcow2']) 24 + iotests.script_initialize(supported_fmts=['qcow2']) 25 25 26 26 disk, top = file_path('disk', 'top') 27 27 size = 1024 * 1024
+1 -1
tests/qemu-iotests/255
··· 23 23 import iotests 24 24 from iotests import imgfmt 25 25 26 - iotests.verify_image_format(supported_fmts=['qcow2']) 26 + iotests.script_initialize(supported_fmts=['qcow2']) 27 27 28 28 iotests.log('Finishing a commit job with background reads') 29 29 iotests.log('============================================')
+1 -1
tests/qemu-iotests/256
··· 23 23 import iotests 24 24 from iotests import log 25 25 26 - iotests.verify_image_format(supported_fmts=['qcow2']) 26 + iotests.script_initialize(supported_fmts=['qcow2']) 27 27 size = 64 * 1024 * 1024 28 28 29 29 with iotests.FilePath('img0') as img0_path, \
+4 -6
tests/qemu-iotests/258
··· 23 23 from iotests import log, qemu_img, qemu_io_silent, \ 24 24 filter_qmp_testfiles, filter_qmp_imgfmt 25 25 26 - # Need backing file and change-backing-file support 27 - iotests.verify_image_format(supported_fmts=['qcow2', 'qed']) 28 - iotests.verify_platform(['linux']) 29 - 30 - 31 26 # Returns a node for blockdev-add 32 27 def node(node_name, path, backing=None, fmt=None, throttle=None): 33 28 if fmt is None: ··· 160 155 test_concurrent_finish(False) 161 156 162 157 if __name__ == '__main__': 163 - main() 158 + # Need backing file and change-backing-file support 159 + iotests.script_main(main, 160 + supported_fmts=['qcow2', 'qed'], 161 + supported_platforms=['linux'])
+3 -1
tests/qemu-iotests/260
··· 21 21 import iotests 22 22 from iotests import qemu_img_create, file_path, log, filter_qmp_event 23 23 24 - iotests.verify_image_format(supported_fmts=['qcow2']) 24 + iotests.script_initialize( 25 + supported_fmts=['qcow2'] 26 + ) 25 27 26 28 base, top = file_path('base', 'top') 27 29 size = 64 * 1024 * 3
+2 -2
tests/qemu-iotests/262
··· 23 23 import iotests 24 24 import os 25 25 26 - iotests.verify_image_format(supported_fmts=['qcow2']) 27 - iotests.verify_platform(['linux']) 26 + iotests.script_initialize(supported_fmts=['qcow2'], 27 + supported_platforms=['linux']) 28 28 29 29 with iotests.FilePath('img') as img_path, \ 30 30 iotests.FilePath('mig_fifo') as fifo, \
+3 -1
tests/qemu-iotests/264
··· 24 24 from iotests import qemu_img_create, qemu_io_silent_check, file_path, \ 25 25 qemu_nbd_popen, log 26 26 27 - iotests.verify_image_format(supported_fmts=['qcow2']) 27 + iotests.script_initialize( 28 + supported_fmts=['qcow2'], 29 + ) 28 30 29 31 disk_a, disk_b, nbd_sock = file_path('disk_a', 'disk_b', 'nbd-sock') 30 32 nbd_uri = 'nbd+unix:///?socket=' + nbd_sock
+2 -2
tests/qemu-iotests/274
··· 21 21 22 22 import iotests 23 23 24 - iotests.verify_image_format(supported_fmts=['qcow2']) 25 - iotests.verify_platform(['linux']) 24 + iotests.script_initialize(supported_fmts=['qcow2'], 25 + supported_platforms=['linux']) 26 26 27 27 size_short = 1 * 1024 * 1024 28 28 size_long = 2 * 1024 * 1024
+2
tests/qemu-iotests/277
··· 23 23 import iotests 24 24 from iotests import file_path, log 25 25 26 + iotests.script_initialize() 27 + 26 28 27 29 nbd_sock, conf_file = file_path('nbd-sock', 'nbd-fault-injector.conf') 28 30
+5 -3
tests/qemu-iotests/280
··· 22 22 import iotests 23 23 import os 24 24 25 - iotests.verify_image_format(supported_fmts=['qcow2']) 26 - iotests.verify_protocol(supported=['file']) 27 - iotests.verify_platform(['linux']) 25 + iotests.script_initialize( 26 + supported_fmts=['qcow2'], 27 + supported_protocols=['file'], 28 + supported_platforms=['linux'], 29 + ) 28 30 29 31 with iotests.FilePath('base') as base_path , \ 30 32 iotests.FilePath('top') as top_path, \
+3 -1
tests/qemu-iotests/283
··· 21 21 import iotests 22 22 23 23 # The test is unrelated to formats, restrict it to qcow2 to avoid extra runs 24 - iotests.verify_image_format(supported_fmts=['qcow2']) 24 + iotests.script_initialize( 25 + supported_fmts=['qcow2'], 26 + ) 25 27 26 28 size = 1024 * 1024 27 29
+219 -147
tests/qemu-iotests/iotests.py
··· 16 16 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 17 # 18 18 19 - import errno 19 + import atexit 20 + from collections import OrderedDict 21 + import faulthandler 22 + import io 23 + import json 24 + import logging 20 25 import os 21 26 import re 27 + import signal 28 + import struct 22 29 import subprocess 23 - import string 30 + import sys 31 + from typing import (Any, Callable, Dict, Iterable, 32 + List, Optional, Sequence, TypeVar) 24 33 import unittest 25 - import sys 26 - import struct 27 - import json 28 - import signal 29 - import logging 30 - import atexit 31 - import io 32 - from collections import OrderedDict 33 - import faulthandler 34 34 35 + # pylint: disable=import-error, wrong-import-position 35 36 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) 36 37 from qemu import qtest 37 38 38 - assert sys.version_info >= (3,6) 39 + assert sys.version_info >= (3, 6) 40 + 41 + # Type Aliases 42 + QMPResponse = Dict[str, Any] 43 + 44 + 45 + # Use this logger for logging messages directly from the iotests module 46 + logger = logging.getLogger('qemu.iotests') 47 + logger.addHandler(logging.NullHandler()) 48 + 49 + # Use this logger for messages that ought to be used for diff output. 50 + test_logger = logging.getLogger('qemu.iotests.diff_io') 51 + 39 52 40 53 faulthandler.enable() 41 54 ··· 80 93 def qemu_img(*args): 81 94 '''Run qemu-img and return the exit code''' 82 95 devnull = open('/dev/null', 'r+') 83 - exitcode = subprocess.call(qemu_img_args + list(args), stdin=devnull, stdout=devnull) 96 + exitcode = subprocess.call(qemu_img_args + list(args), 97 + stdin=devnull, stdout=devnull) 84 98 if exitcode < 0: 85 - sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) 99 + sys.stderr.write('qemu-img received signal %i: %s\n' 100 + % (-exitcode, ' '.join(qemu_img_args + list(args)))) 86 101 return exitcode 87 102 88 103 def ordered_qmp(qmsg, conv_keys=True): ··· 121 136 '''Run qemu-img without suppressing its output and return the exit code''' 122 137 exitcode = subprocess.call(qemu_img_args + list(args)) 123 138 if exitcode < 0: 124 - sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) 139 + sys.stderr.write('qemu-img received signal %i: %s\n' 140 + % (-exitcode, ' '.join(qemu_img_args + list(args)))) 125 141 return exitcode 126 142 127 143 def qemu_img_pipe(*args): ··· 132 148 universal_newlines=True) 133 149 exitcode = subp.wait() 134 150 if exitcode < 0: 135 - sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) 151 + sys.stderr.write('qemu-img received signal %i: %s\n' 152 + % (-exitcode, ' '.join(qemu_img_args + list(args)))) 136 153 return subp.communicate()[0] 137 154 138 155 def qemu_img_log(*args): ··· 140 157 log(result, filters=[filter_testfiles]) 141 158 return result 142 159 143 - def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]): 144 - args = [ 'info' ] 160 + def img_info_log(filename, filter_path=None, imgopts=False, extra_args=()): 161 + args = ['info'] 145 162 if imgopts: 146 163 args.append('--image-opts') 147 164 else: 148 - args += [ '-f', imgfmt ] 165 + args += ['-f', imgfmt] 149 166 args += extra_args 150 167 args.append(filename) 151 168 ··· 162 179 universal_newlines=True) 163 180 exitcode = subp.wait() 164 181 if exitcode < 0: 165 - sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) 182 + sys.stderr.write('qemu-io received signal %i: %s\n' 183 + % (-exitcode, ' '.join(args))) 166 184 return subp.communicate()[0] 167 185 168 186 def qemu_io_log(*args): ··· 224 242 # quit command is in close(), '\n' is added automatically 225 243 assert '\n' not in cmd 226 244 cmd = cmd.strip() 227 - assert cmd != 'q' and cmd != 'quit' 245 + assert cmd not in ('q', 'quit') 228 246 self._p.stdin.write(cmd + '\n') 229 247 self._p.stdin.flush() 230 248 return self._read_output() ··· 246 264 sys.stderr.write('qemu-nbd received signal %i: %s\n' % 247 265 (-exitcode, 248 266 ' '.join(qemu_nbd_args + ['--fork'] + list(args)))) 249 - if exitcode == 0: 250 - return exitcode, '' 251 - else: 252 - return exitcode, subp.communicate()[0] 267 + 268 + return exitcode, subp.communicate()[0] if exitcode else '' 253 269 254 270 def qemu_nbd_popen(*args): 255 271 '''Run qemu-nbd in daemon mode and return the parent's exit code''' ··· 286 302 def filter_win32(msg): 287 303 return win32_re.sub("", msg) 288 304 289 - qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* \([0-9\/.inf]* [EPTGMKiBbytes]*\/sec and [0-9\/.inf]* ops\/sec\)") 305 + qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* " 306 + r"\([0-9\/.inf]* [EPTGMKiBbytes]*\/sec " 307 + r"and [0-9\/.inf]* ops\/sec\)") 290 308 def filter_qemu_io(msg): 291 309 msg = filter_win32(msg) 292 - return qemu_io_re.sub("X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)", msg) 310 + return qemu_io_re.sub("X ops; XX:XX:XX.X " 311 + "(XXX YYY/sec and XXX ops/sec)", msg) 293 312 294 313 chown_re = re.compile(r"chown [0-9]+:[0-9]+") 295 314 def filter_chown(msg): ··· 313 332 items = qmsg.items() 314 333 315 334 for k, v in items: 316 - if isinstance(v, list) or isinstance(v, dict): 335 + if isinstance(v, (dict, list)): 317 336 qmsg[k] = filter_qmp(v, filter_fn) 318 337 else: 319 338 qmsg[k] = filter_fn(k, v) ··· 324 343 return msg.replace(prefix, 'TEST_DIR/PID-') 325 344 326 345 def filter_qmp_testfiles(qmsg): 327 - def _filter(key, value): 346 + def _filter(_key, value): 328 347 if is_str(value): 329 348 return filter_testfiles(value) 330 349 return value ··· 342 361 line = filter_testfiles(line) 343 362 line = line.replace(imgfmt, 'IMGFMT') 344 363 line = re.sub('iters: [0-9]+', 'iters: XXX', line) 345 - line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line) 364 + line = re.sub('uuid: [-a-f0-9]+', 365 + 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', 366 + line) 346 367 line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line) 347 368 lines.append(line) 348 369 return '\n'.join(lines) ··· 351 372 return msg.replace(imgfmt, 'IMGFMT') 352 373 353 374 def filter_qmp_imgfmt(qmsg): 354 - def _filter(key, value): 375 + def _filter(_key, value): 355 376 if is_str(value): 356 377 return filter_imgfmt(value) 357 378 return value 358 379 return filter_qmp(qmsg, _filter) 359 380 360 - def log(msg, filters=[], indent=None): 361 - '''Logs either a string message or a JSON serializable message (like QMP). 362 - If indent is provided, JSON serializable messages are pretty-printed.''' 381 + 382 + Msg = TypeVar('Msg', Dict[str, Any], List[Any], str) 383 + 384 + def log(msg: Msg, 385 + filters: Iterable[Callable[[Msg], Msg]] = (), 386 + indent: Optional[int] = None) -> None: 387 + """ 388 + Logs either a string message or a JSON serializable message (like QMP). 389 + If indent is provided, JSON serializable messages are pretty-printed. 390 + """ 363 391 for flt in filters: 364 392 msg = flt(msg) 365 - if isinstance(msg, dict) or isinstance(msg, list): 366 - # Python < 3.4 needs to know not to add whitespace when pretty-printing: 367 - separators = (', ', ': ') if indent is None else (',', ': ') 393 + if isinstance(msg, (dict, list)): 368 394 # Don't sort if it's already sorted 369 395 do_sort = not isinstance(msg, OrderedDict) 370 - print(json.dumps(msg, sort_keys=do_sort, 371 - indent=indent, separators=separators)) 396 + test_logger.info(json.dumps(msg, sort_keys=do_sort, indent=indent)) 372 397 else: 373 - print(msg) 398 + test_logger.info(msg) 374 399 375 400 class Timeout: 376 - def __init__(self, seconds, errmsg = "Timeout"): 401 + def __init__(self, seconds, errmsg="Timeout"): 377 402 self.seconds = seconds 378 403 self.errmsg = errmsg 379 404 def __enter__(self): 380 405 signal.signal(signal.SIGALRM, self.timeout) 381 406 signal.setitimer(signal.ITIMER_REAL, self.seconds) 382 407 return self 383 - def __exit__(self, type, value, traceback): 408 + def __exit__(self, exc_type, value, traceback): 384 409 signal.setitimer(signal.ITIMER_REAL, 0) 385 410 return False 386 411 def timeout(self, signum, frame): ··· 389 414 def file_pattern(name): 390 415 return "{0}-{1}".format(os.getpid(), name) 391 416 392 - class FilePaths(object): 417 + class FilePaths: 393 418 """ 394 419 FilePaths is an auto-generated filename that cleans itself up. 395 420 ··· 490 515 self._args.append(opts) 491 516 return self 492 517 493 - def add_drive(self, path, opts='', interface='virtio', format=imgfmt): 518 + def add_drive(self, path, opts='', interface='virtio', img_format=imgfmt): 494 519 '''Add a virtio-blk drive to the VM''' 495 520 options = ['if=%s' % interface, 496 521 'id=drive%d' % self._num_drives] 497 522 498 523 if path is not None: 499 524 options.append('file=%s' % path) 500 - options.append('format=%s' % format) 525 + options.append('format=%s' % img_format) 501 526 options.append('cache=%s' % cachemode) 502 527 options.append('aio=%s' % aiomode) 503 528 504 529 if opts: 505 530 options.append(opts) 506 531 507 - if format == 'luks' and 'key-secret' not in opts: 532 + if img_format == 'luks' and 'key-secret' not in opts: 508 533 # default luks support 509 534 if luks_default_secret_object not in self._args: 510 535 self.add_object(luks_default_secret_object) ··· 529 554 self._args.append(addr) 530 555 return self 531 556 532 - def pause_drive(self, drive, event=None): 533 - '''Pause drive r/w operations''' 557 + def hmp(self, command_line: str, use_log: bool = False) -> QMPResponse: 558 + cmd = 'human-monitor-command' 559 + kwargs = {'command-line': command_line} 560 + if use_log: 561 + return self.qmp_log(cmd, **kwargs) 562 + else: 563 + return self.qmp(cmd, **kwargs) 564 + 565 + def pause_drive(self, drive: str, event: Optional[str] = None) -> None: 566 + """Pause drive r/w operations""" 534 567 if not event: 535 568 self.pause_drive(drive, "read_aio") 536 569 self.pause_drive(drive, "write_aio") 537 570 return 538 - self.qmp('human-monitor-command', 539 - command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive)) 571 + self.hmp(f'qemu-io {drive} "break {event} bp_{drive}"') 540 572 541 - def resume_drive(self, drive): 542 - self.qmp('human-monitor-command', 543 - command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive)) 573 + def resume_drive(self, drive: str) -> None: 574 + """Resume drive r/w operations""" 575 + self.hmp(f'qemu-io {drive} "remove_break bp_{drive}"') 544 576 545 - def hmp_qemu_io(self, drive, cmd): 546 - '''Write to a given drive using an HMP command''' 547 - return self.qmp('human-monitor-command', 548 - command_line='qemu-io %s "%s"' % (drive, cmd)) 577 + def hmp_qemu_io(self, drive: str, cmd: str, 578 + use_log: bool = False) -> QMPResponse: 579 + """Write to a given drive using an HMP command""" 580 + return self.hmp(f'qemu-io {drive} "{cmd}"', use_log=use_log) 549 581 550 582 def flatten_qmp_object(self, obj, output=None, basestr=''): 551 583 if output is None: 552 584 output = dict() 553 585 if isinstance(obj, list): 554 - for i in range(len(obj)): 555 - self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') 586 + for i, item in enumerate(obj): 587 + self.flatten_qmp_object(item, output, basestr + str(i) + '.') 556 588 elif isinstance(obj, dict): 557 589 for key in obj: 558 590 self.flatten_qmp_object(obj[key], output, basestr + key + '.') ··· 573 605 result.append(filter_qmp_event(ev)) 574 606 return result 575 607 576 - def qmp_log(self, cmd, filters=[], indent=None, **kwargs): 608 + def qmp_log(self, cmd, filters=(), indent=None, **kwargs): 577 609 full_cmd = OrderedDict(( 578 610 ("execute", cmd), 579 611 ("arguments", ordered_qmp(kwargs)) ··· 585 617 586 618 # Returns None on success, and an error string on failure 587 619 def run_job(self, job, auto_finalize=True, auto_dismiss=False, 588 - pre_finalize=None, cancel=False, use_log=True, wait=60.0): 620 + pre_finalize=None, cancel=False, wait=60.0): 589 621 """ 590 622 run_job moves a job from creation through to dismissal. 591 623 ··· 598 630 invoked prior to issuing job-finalize, if any. 599 631 :param cancel: Bool. When true, cancels the job after the pre_finalize 600 632 callback. 601 - :param use_log: Bool. When false, does not log QMP messages. 602 633 :param wait: Float. Timeout value specifying how long to wait for any 603 634 event, in seconds. Defaults to 60.0. 604 635 """ ··· 616 647 while True: 617 648 ev = filter_qmp_event(self.events_wait(events, timeout=wait)) 618 649 if ev['event'] != 'JOB_STATUS_CHANGE': 619 - if use_log: 620 - log(ev) 650 + log(ev) 621 651 continue 622 652 status = ev['data']['status'] 623 653 if status == 'aborting': ··· 625 655 for j in result['return']: 626 656 if j['id'] == job: 627 657 error = j['error'] 628 - if use_log: 629 - log('Job failed: %s' % (j['error'])) 658 + log('Job failed: %s' % (j['error'])) 630 659 elif status == 'ready': 631 - if use_log: 632 - self.qmp_log('job-complete', id=job) 633 - else: 634 - self.qmp('job-complete', id=job) 660 + self.qmp_log('job-complete', id=job) 635 661 elif status == 'pending' and not auto_finalize: 636 662 if pre_finalize: 637 663 pre_finalize() 638 - if cancel and use_log: 664 + if cancel: 639 665 self.qmp_log('job-cancel', id=job) 640 - elif cancel: 641 - self.qmp('job-cancel', id=job) 642 - elif use_log: 666 + else: 643 667 self.qmp_log('job-finalize', id=job) 644 - else: 645 - self.qmp('job-finalize', id=job) 646 668 elif status == 'concluded' and not auto_dismiss: 647 - if use_log: 648 - self.qmp_log('job-dismiss', id=job) 649 - else: 650 - self.qmp('job-dismiss', id=job) 669 + self.qmp_log('job-dismiss', id=job) 651 670 elif status == 'null': 652 671 return error 653 672 ··· 710 729 711 730 for bitmap in bitmaps[node_name]: 712 731 if bitmap.get('name', '') == bitmap_name: 713 - if recording is None: 714 - return bitmap 715 - elif bitmap.get('recording') == recording: 732 + if recording is None or bitmap.get('recording') == recording: 716 733 return bitmap 717 734 return None 718 735 ··· 763 780 assert node is not None, 'Cannot follow path %s%s' % (root, path) 764 781 765 782 try: 766 - node_id = next(edge['child'] for edge in graph['edges'] \ 767 - if edge['parent'] == node['id'] and 768 - edge['name'] == child_name) 783 + node_id = next(edge['child'] for edge in graph['edges'] 784 + if (edge['parent'] == node['id'] and 785 + edge['name'] == child_name)) 786 + 787 + node = next(node for node in graph['nodes'] 788 + if node['id'] == node_id) 769 789 770 - node = next(node for node in graph['nodes'] \ 771 - if node['id'] == node_id) 772 790 except StopIteration: 773 791 node = None 774 792 ··· 786 804 class QMPTestCase(unittest.TestCase): 787 805 '''Abstract base class for QMP test cases''' 788 806 807 + def __init__(self, *args, **kwargs): 808 + super().__init__(*args, **kwargs) 809 + # Many users of this class set a VM property we rely on heavily 810 + # in the methods below. 811 + self.vm = None 812 + 789 813 def dictpath(self, d, path): 790 814 '''Traverse a path in a nested dict''' 791 815 for component in path.split('/'): ··· 795 819 idx = int(idx) 796 820 797 821 if not isinstance(d, dict) or component not in d: 798 - self.fail('failed path traversal for "%s" in "%s"' % (path, str(d))) 822 + self.fail(f'failed path traversal for "{path}" in "{d}"') 799 823 d = d[component] 800 824 801 825 if m: 802 826 if not isinstance(d, list): 803 - self.fail('path component "%s" in "%s" is not a list in "%s"' % (component, path, str(d))) 827 + self.fail(f'path component "{component}" in "{path}" ' 828 + f'is not a list in "{d}"') 804 829 try: 805 830 d = d[idx] 806 831 except IndexError: 807 - self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) 832 + self.fail(f'invalid index "{idx}" in path "{path}" ' 833 + f'in "{d}"') 808 834 return d 809 835 810 836 def assert_qmp_absent(self, d, path): ··· 831 857 else: 832 858 self.assertEqual(result, value, 833 859 '"%s" is "%s", expected "%s"' 834 - % (path, str(result), str(value))) 860 + % (path, str(result), str(value))) 835 861 836 862 def assert_no_active_block_jobs(self): 837 863 result = self.vm.qmp('query-block-jobs') ··· 841 867 """Issue a query-named-block-nodes and assert node_name and/or 842 868 file_name is present in the result""" 843 869 def check_equal_or_none(a, b): 844 - return a == None or b == None or a == b 870 + return a is None or b is None or a == b 845 871 assert node_name or file_name 846 872 result = self.vm.qmp('query-named-block-nodes') 847 873 for x in result["return"]: 848 874 if check_equal_or_none(x.get("node-name"), node_name) and \ 849 875 check_equal_or_none(x.get("file"), file_name): 850 876 return 851 - self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \ 852 - (node_name, file_name, result)) 877 + self.fail("Cannot find %s %s in result:\n%s" % 878 + (node_name, file_name, result)) 853 879 854 880 def assert_json_filename_equal(self, json_filename, reference): 855 881 '''Asserts that the given filename is a json: filename and that its 856 882 content is equal to the given reference object''' 857 883 self.assertEqual(json_filename[:5], 'json:') 858 - self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])), 859 - self.vm.flatten_qmp_object(reference)) 884 + self.assertEqual( 885 + self.vm.flatten_qmp_object(json.loads(json_filename[5:])), 886 + self.vm.flatten_qmp_object(reference) 887 + ) 860 888 861 - def cancel_and_wait(self, drive='drive0', force=False, resume=False, wait=60.0): 889 + def cancel_and_wait(self, drive='drive0', force=False, 890 + resume=False, wait=60.0): 862 891 '''Cancel a block job and wait for it to finish, returning the event''' 863 892 result = self.vm.qmp('block-job-cancel', device=drive, force=force) 864 893 self.assert_qmp(result, 'return', {}) ··· 882 911 self.assert_no_active_block_jobs() 883 912 return result 884 913 885 - def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0, 886 - error=None): 914 + def wait_until_completed(self, drive='drive0', check_offset=True, 915 + wait=60.0, error=None): 887 916 '''Wait for a block job to finish, returning the event''' 888 917 while True: 889 918 for event in self.vm.get_qmp_events(wait=wait): ··· 898 927 self.assert_qmp(event, 'data/error', error) 899 928 self.assert_no_active_block_jobs() 900 929 return event 901 - elif event['event'] == 'JOB_STATUS_CHANGE': 930 + if event['event'] == 'JOB_STATUS_CHANGE': 902 931 self.assert_qmp(event, 'data/id', drive) 903 932 904 933 def wait_ready(self, drive='drive0'): 905 - '''Wait until a block job BLOCK_JOB_READY event''' 906 - f = {'data': {'type': 'mirror', 'device': drive } } 907 - event = self.vm.event_wait(name='BLOCK_JOB_READY', match=f) 934 + """Wait until a BLOCK_JOB_READY event, and return the event.""" 935 + f = {'data': {'type': 'mirror', 'device': drive}} 936 + return self.vm.event_wait(name='BLOCK_JOB_READY', match=f) 908 937 909 938 def wait_ready_and_cancel(self, drive='drive0'): 910 939 self.wait_ready(drive=drive) ··· 933 962 for job in result['return']: 934 963 if job['device'] == job_id: 935 964 found = True 936 - if job['paused'] == True and job['busy'] == False: 965 + if job['paused'] and not job['busy']: 937 966 return job 938 967 break 939 968 assert found ··· 957 986 seq = os.path.basename(sys.argv[0]) 958 987 959 988 open('%s/%s.notrun' % (output_dir, seq), 'w').write(reason + '\n') 960 - print('%s not run: %s' % (seq, reason)) 989 + logger.warning("%s not run: %s", seq, reason) 961 990 sys.exit(0) 962 991 963 992 def case_notrun(reason): ··· 972 1001 open('%s/%s.casenotrun' % (output_dir, seq), 'a').write( 973 1002 ' [case not run] ' + reason + '\n') 974 1003 975 - def verify_image_format(supported_fmts=[], unsupported_fmts=[]): 1004 + def _verify_image_format(supported_fmts: Sequence[str] = (), 1005 + unsupported_fmts: Sequence[str] = ()) -> None: 976 1006 assert not (supported_fmts and unsupported_fmts) 977 1007 978 1008 if 'generic' in supported_fmts and \ ··· 986 1016 if not_sup or (imgfmt in unsupported_fmts): 987 1017 notrun('not suitable for this image format: %s' % imgfmt) 988 1018 989 - def verify_protocol(supported=[], unsupported=[]): 1019 + def _verify_protocol(supported: Sequence[str] = (), 1020 + unsupported: Sequence[str] = ()) -> None: 990 1021 assert not (supported and unsupported) 991 1022 992 1023 if 'generic' in supported: ··· 996 1027 if not_sup or (imgproto in unsupported): 997 1028 notrun('not suitable for this protocol: %s' % imgproto) 998 1029 999 - def verify_platform(supported=None, unsupported=None): 1000 - if unsupported is not None: 1001 - if any((sys.platform.startswith(x) for x in unsupported)): 1002 - notrun('not suitable for this OS: %s' % sys.platform) 1030 + def _verify_platform(supported: Sequence[str] = (), 1031 + unsupported: Sequence[str] = ()) -> None: 1032 + if any((sys.platform.startswith(x) for x in unsupported)): 1033 + notrun('not suitable for this OS: %s' % sys.platform) 1003 1034 1004 - if supported is not None: 1035 + if supported: 1005 1036 if not any((sys.platform.startswith(x) for x in supported)): 1006 1037 notrun('not suitable for this OS: %s' % sys.platform) 1007 1038 1008 - def verify_cache_mode(supported_cache_modes=[]): 1039 + def _verify_cache_mode(supported_cache_modes: Sequence[str] = ()) -> None: 1009 1040 if supported_cache_modes and (cachemode not in supported_cache_modes): 1010 1041 notrun('not suitable for this cache mode: %s' % cachemode) 1011 1042 1012 - def verify_aio_mode(supported_aio_modes=[]): 1043 + def _verify_aio_mode(supported_aio_modes: Sequence[str] = ()): 1013 1044 if supported_aio_modes and (aiomode not in supported_aio_modes): 1014 1045 notrun('not suitable for this aio mode: %s' % aiomode) 1015 1046 ··· 1022 1053 notrun('quorum support missing') 1023 1054 1024 1055 def qemu_pipe(*args): 1025 - '''Run qemu with an option to print something and exit (e.g. a help option), 1026 - and return its output''' 1056 + """ 1057 + Run qemu with an option to print something and exit (e.g. a help option). 1058 + 1059 + :return: QEMU's stdout output. 1060 + """ 1027 1061 args = [qemu_prog] + qemu_opts + list(args) 1028 1062 subp = subprocess.Popen(args, stdout=subprocess.PIPE, 1029 1063 stderr=subprocess.STDOUT, 1030 1064 universal_newlines=True) 1031 1065 exitcode = subp.wait() 1032 1066 if exitcode < 0: 1033 - sys.stderr.write('qemu received signal %i: %s\n' % (-exitcode, 1034 - ' '.join(args))) 1067 + sys.stderr.write('qemu received signal %i: %s\n' % 1068 + (-exitcode, ' '.join(args))) 1035 1069 return subp.communicate()[0] 1036 1070 1037 1071 def supported_formats(read_only=False): ··· 1049 1083 1050 1084 return supported_formats.formats[read_only] 1051 1085 1052 - def skip_if_unsupported(required_formats=[], read_only=False): 1086 + def skip_if_unsupported(required_formats=(), read_only=False): 1053 1087 '''Skip Test Decorator 1054 1088 Runs the test if all the required formats are whitelisted''' 1055 1089 def skip_test_decorator(func): ··· 1061 1095 1062 1096 usf_list = list(set(fmts) - set(supported_formats(read_only))) 1063 1097 if usf_list: 1064 - test_case.case_skip('{}: formats {} are not whitelisted'.format( 1065 - test_case, usf_list)) 1098 + msg = f'{test_case}: formats {usf_list} are not whitelisted' 1099 + test_case.case_skip(msg) 1100 + return None 1066 1101 else: 1067 1102 return func(test_case, *args, **kwargs) 1068 1103 return func_wrapper ··· 1074 1109 def func_wrapper(*args, **kwargs): 1075 1110 if os.getuid() == 0: 1076 1111 case_notrun('{}: cannot be run as root'.format(args[0])) 1112 + return None 1077 1113 else: 1078 1114 return func(*args, **kwargs) 1079 1115 return func_wrapper 1080 1116 1081 - def execute_unittest(output, verbosity, debug): 1117 + def execute_unittest(debug=False): 1118 + """Executes unittests within the calling module.""" 1119 + 1120 + verbosity = 2 if debug else 1 1121 + 1122 + if debug: 1123 + output = sys.stdout 1124 + else: 1125 + # We need to filter out the time taken from the output so that 1126 + # qemu-iotest can reliably diff the results against master output. 1127 + output = io.StringIO() 1128 + 1082 1129 runner = unittest.TextTestRunner(stream=output, descriptions=True, 1083 1130 verbosity=verbosity) 1084 1131 try: ··· 1086 1133 # exception 1087 1134 unittest.main(testRunner=runner) 1088 1135 finally: 1136 + # We need to filter out the time taken from the output so that 1137 + # qemu-iotest can reliably diff the results against master output. 1089 1138 if not debug: 1090 1139 out = output.getvalue() 1091 1140 out = re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', out) ··· 1097 1146 1098 1147 sys.stderr.write(out) 1099 1148 1100 - def execute_test(test_function=None, 1101 - supported_fmts=[], 1102 - supported_platforms=None, 1103 - supported_cache_modes=[], supported_aio_modes={}, 1104 - unsupported_fmts=[], supported_protocols=[], 1105 - unsupported_protocols=[]): 1106 - """Run either unittest or script-style tests.""" 1149 + def execute_setup_common(supported_fmts: Sequence[str] = (), 1150 + supported_platforms: Sequence[str] = (), 1151 + supported_cache_modes: Sequence[str] = (), 1152 + supported_aio_modes: Sequence[str] = (), 1153 + unsupported_fmts: Sequence[str] = (), 1154 + supported_protocols: Sequence[str] = (), 1155 + unsupported_protocols: Sequence[str] = ()) -> bool: 1156 + """ 1157 + Perform necessary setup for either script-style or unittest-style tests. 1158 + 1159 + :return: Bool; Whether or not debug mode has been requested via the CLI. 1160 + """ 1161 + # Note: Python 3.6 and pylint do not like 'Collection' so use 'Sequence'. 1107 1162 1108 1163 # We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to 1109 1164 # indicate that we're not being run via "check". There may be ··· 1112 1167 if test_dir is None or qemu_default_machine is None: 1113 1168 sys.stderr.write('Please run this test via the "check" script\n') 1114 1169 sys.exit(os.EX_USAGE) 1170 + 1171 + _verify_image_format(supported_fmts, unsupported_fmts) 1172 + _verify_protocol(supported_protocols, unsupported_protocols) 1173 + _verify_platform(supported=supported_platforms) 1174 + _verify_cache_mode(supported_cache_modes) 1175 + _verify_aio_mode(supported_aio_modes) 1115 1176 1116 1177 debug = '-d' in sys.argv 1117 - verbosity = 1 1118 - verify_image_format(supported_fmts, unsupported_fmts) 1119 - verify_protocol(supported_protocols, unsupported_protocols) 1120 - verify_platform(supported=supported_platforms) 1121 - verify_cache_mode(supported_cache_modes) 1122 - verify_aio_mode(supported_aio_modes) 1123 - 1124 1178 if debug: 1125 - output = sys.stdout 1126 - verbosity = 2 1127 1179 sys.argv.remove('-d') 1128 - else: 1129 - # We need to filter out the time taken from the output so that 1130 - # qemu-iotest can reliably diff the results against master output. 1131 - output = io.StringIO() 1180 + logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN)) 1181 + logger.debug("iotests debugging messages active") 1182 + 1183 + return debug 1132 1184 1133 - logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN)) 1185 + def execute_test(*args, test_function=None, **kwargs): 1186 + """Run either unittest or script-style tests.""" 1134 1187 1188 + debug = execute_setup_common(*args, **kwargs) 1135 1189 if not test_function: 1136 - execute_unittest(output, verbosity, debug) 1190 + execute_unittest(debug) 1137 1191 else: 1138 1192 test_function() 1139 1193 1194 + def activate_logging(): 1195 + """Activate iotests.log() output to stdout for script-style tests.""" 1196 + handler = logging.StreamHandler(stream=sys.stdout) 1197 + formatter = logging.Formatter('%(message)s') 1198 + handler.setFormatter(formatter) 1199 + test_logger.addHandler(handler) 1200 + test_logger.setLevel(logging.INFO) 1201 + test_logger.propagate = False 1202 + 1203 + # This is called from script-style iotests without a single point of entry 1204 + def script_initialize(*args, **kwargs): 1205 + """Initialize script-style tests without running any tests.""" 1206 + activate_logging() 1207 + execute_setup_common(*args, **kwargs) 1208 + 1209 + # This is called from script-style iotests with a single point of entry 1140 1210 def script_main(test_function, *args, **kwargs): 1141 1211 """Run script-style tests outside of the unittest framework""" 1142 - execute_test(test_function, *args, **kwargs) 1212 + activate_logging() 1213 + execute_test(*args, test_function=test_function, **kwargs) 1143 1214 1215 + # This is called from unittest style iotests 1144 1216 def main(*args, **kwargs): 1145 1217 """Run tests using the unittest framework""" 1146 - execute_test(None, *args, **kwargs) 1218 + execute_test(*args, **kwargs)
+26
tests/qemu-iotests/pylintrc
··· 1 + [MESSAGES CONTROL] 2 + 3 + # Disable the message, report, category or checker with the given id(s). You 4 + # can either give multiple identifiers separated by comma (,) or put this 5 + # option multiple times (only on the command line, not in the configuration 6 + # file where it should appear only once). You can also use "--disable=all" to 7 + # disable everything first and then reenable specific checks. For example, if 8 + # you want to run only the similarities checker, you can use "--disable=all 9 + # --enable=similarities". If you want to run only the classes checker, but have 10 + # no Warning level messages displayed, use "--disable=all --enable=classes 11 + # --disable=W". 12 + disable=invalid-name, 13 + no-else-return, 14 + too-few-public-methods, 15 + too-many-arguments, 16 + too-many-branches, 17 + too-many-lines, 18 + too-many-locals, 19 + too-many-public-methods, 20 + # These are temporary, and should be removed: 21 + missing-docstring, 22 + 23 + [FORMAT] 24 + 25 + # Maximum number of characters on a single line. 26 + max-line-length=79