1*bdebdc71SKevin Wolf#!/usr/bin/env python 2*bdebdc71SKevin Wolf# 3*bdebdc71SKevin Wolf# Copyright (C) 2018 Red Hat, Inc. 4*bdebdc71SKevin Wolf# 5*bdebdc71SKevin Wolf# This program is free software; you can redistribute it and/or modify 6*bdebdc71SKevin Wolf# it under the terms of the GNU General Public License as published by 7*bdebdc71SKevin Wolf# the Free Software Foundation; either version 2 of the License, or 8*bdebdc71SKevin Wolf# (at your option) any later version. 9*bdebdc71SKevin Wolf# 10*bdebdc71SKevin Wolf# This program is distributed in the hope that it will be useful, 11*bdebdc71SKevin Wolf# but WITHOUT ANY WARRANTY; without even the implied warranty of 12*bdebdc71SKevin Wolf# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13*bdebdc71SKevin Wolf# GNU General Public License for more details. 14*bdebdc71SKevin Wolf# 15*bdebdc71SKevin Wolf# You should have received a copy of the GNU General Public License 16*bdebdc71SKevin Wolf# along with this program. If not, see <http://www.gnu.org/licenses/>. 17*bdebdc71SKevin Wolf# 18*bdebdc71SKevin Wolf# Creator/Owner: Kevin Wolf <kwolf@redhat.com> 19*bdebdc71SKevin Wolf# 20*bdebdc71SKevin Wolf# Check using the job-* QMP commands with block jobs 21*bdebdc71SKevin Wolf 22*bdebdc71SKevin Wolfimport iotests 23*bdebdc71SKevin Wolf 24*bdebdc71SKevin Wolfiotests.verify_image_format(supported_fmts=['qcow2']) 25*bdebdc71SKevin Wolf 26*bdebdc71SKevin Wolfdef pause_wait(vm, job_id): 27*bdebdc71SKevin Wolf with iotests.Timeout(3, "Timeout waiting for job to pause"): 28*bdebdc71SKevin Wolf while True: 29*bdebdc71SKevin Wolf result = vm.qmp('query-jobs') 30*bdebdc71SKevin Wolf for job in result['return']: 31*bdebdc71SKevin Wolf if job['id'] == job_id and job['status'] in ['paused', 'standby']: 32*bdebdc71SKevin Wolf return job 33*bdebdc71SKevin Wolf 34*bdebdc71SKevin Wolf# Test that block-job-pause/resume and job-pause/resume can be mixed 35*bdebdc71SKevin Wolfdef test_pause_resume(vm): 36*bdebdc71SKevin Wolf for pause_cmd, pause_arg in [('block-job-pause', 'device'), 37*bdebdc71SKevin Wolf ('job-pause', 'id')]: 38*bdebdc71SKevin Wolf for resume_cmd, resume_arg in [('block-job-resume', 'device'), 39*bdebdc71SKevin Wolf ('job-resume', 'id')]: 40*bdebdc71SKevin Wolf iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd)) 41*bdebdc71SKevin Wolf 42*bdebdc71SKevin Wolf iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) 43*bdebdc71SKevin Wolf pause_wait(vm, 'job0') 44*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 45*bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 46*bdebdc71SKevin Wolf 47*bdebdc71SKevin Wolf iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) 48*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 49*bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 50*bdebdc71SKevin Wolf 51*bdebdc71SKevin Wolfdef test_job_lifecycle(vm, job, job_args, has_ready=False): 52*bdebdc71SKevin Wolf iotests.log('') 53*bdebdc71SKevin Wolf iotests.log('') 54*bdebdc71SKevin Wolf iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' % 55*bdebdc71SKevin Wolf (job, 56*bdebdc71SKevin Wolf job_args.get('auto-finalize', True), 57*bdebdc71SKevin Wolf job_args.get('auto-dismiss', True))) 58*bdebdc71SKevin Wolf iotests.log(vm.qmp(job, job_id='job0', **job_args)) 59*bdebdc71SKevin Wolf 60*bdebdc71SKevin Wolf # Depending on the storage, the first request may or may not have completed 61*bdebdc71SKevin Wolf # yet, so filter out the progress. Later query-job calls don't need the 62*bdebdc71SKevin Wolf # filtering because the progress is made deterministic by the block job 63*bdebdc71SKevin Wolf # speed 64*bdebdc71SKevin Wolf result = vm.qmp('query-jobs') 65*bdebdc71SKevin Wolf for j in result['return']: 66*bdebdc71SKevin Wolf del j['current-progress'] 67*bdebdc71SKevin Wolf iotests.log(result) 68*bdebdc71SKevin Wolf 69*bdebdc71SKevin Wolf # undefined -> created -> running 70*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 71*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 72*bdebdc71SKevin Wolf 73*bdebdc71SKevin Wolf # RUNNING state: 74*bdebdc71SKevin Wolf # pause/resume should work, complete/finalize/dismiss should error out 75*bdebdc71SKevin Wolf iotests.log('') 76*bdebdc71SKevin Wolf iotests.log('Pause/resume in RUNNING') 77*bdebdc71SKevin Wolf test_pause_resume(vm) 78*bdebdc71SKevin Wolf 79*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 80*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 81*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 82*bdebdc71SKevin Wolf 83*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-complete', device='job0')) 84*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-finalize', id='job0')) 85*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-dismiss', id='job0')) 86*bdebdc71SKevin Wolf 87*bdebdc71SKevin Wolf # Let the job complete (or transition to READY if it supports that) 88*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0)) 89*bdebdc71SKevin Wolf if has_ready: 90*bdebdc71SKevin Wolf iotests.log('') 91*bdebdc71SKevin Wolf iotests.log('Waiting for READY state...') 92*bdebdc71SKevin Wolf vm.event_wait('BLOCK_JOB_READY') 93*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 94*bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 95*bdebdc71SKevin Wolf 96*bdebdc71SKevin Wolf # READY state: 97*bdebdc71SKevin Wolf # pause/resume/complete should work, finalize/dismiss should error out 98*bdebdc71SKevin Wolf iotests.log('') 99*bdebdc71SKevin Wolf iotests.log('Pause/resume in READY') 100*bdebdc71SKevin Wolf test_pause_resume(vm) 101*bdebdc71SKevin Wolf 102*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 103*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 104*bdebdc71SKevin Wolf 105*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-finalize', id='job0')) 106*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-dismiss', id='job0')) 107*bdebdc71SKevin Wolf 108*bdebdc71SKevin Wolf # Transition to WAITING 109*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 110*bdebdc71SKevin Wolf 111*bdebdc71SKevin Wolf # Move to WAITING and PENDING state 112*bdebdc71SKevin Wolf iotests.log('') 113*bdebdc71SKevin Wolf iotests.log('Waiting for PENDING state...') 114*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 115*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 116*bdebdc71SKevin Wolf 117*bdebdc71SKevin Wolf if not job_args.get('auto-finalize', True): 118*bdebdc71SKevin Wolf # PENDING state: 119*bdebdc71SKevin Wolf # finalize should work, pause/complete/dismiss should error out 120*bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 121*bdebdc71SKevin Wolf 122*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-pause', id='job0')) 123*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 124*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 125*bdebdc71SKevin Wolf 126*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-pause', device='job0')) 127*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-complete', device='job0')) 128*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-dismiss', id='job0')) 129*bdebdc71SKevin Wolf 130*bdebdc71SKevin Wolf # Transition to CONCLUDED 131*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 132*bdebdc71SKevin Wolf 133*bdebdc71SKevin Wolf 134*bdebdc71SKevin Wolf # Move to CONCLUDED state 135*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 136*bdebdc71SKevin Wolf 137*bdebdc71SKevin Wolf if not job_args.get('auto-dismiss', True): 138*bdebdc71SKevin Wolf # CONCLUDED state: 139*bdebdc71SKevin Wolf # dismiss should work, pause/complete/finalize should error out 140*bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 141*bdebdc71SKevin Wolf 142*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-pause', id='job0')) 143*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 144*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 145*bdebdc71SKevin Wolf 146*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-pause', device='job0')) 147*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-complete', device='job0')) 148*bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-finalize', id='job0')) 149*bdebdc71SKevin Wolf 150*bdebdc71SKevin Wolf # Transition to NULL 151*bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 152*bdebdc71SKevin Wolf 153*bdebdc71SKevin Wolf # Move to NULL state 154*bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 155*bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 156*bdebdc71SKevin Wolf 157*bdebdc71SKevin Wolf 158*bdebdc71SKevin Wolfwith iotests.FilePath('disk.img') as disk_path, \ 159*bdebdc71SKevin Wolf iotests.FilePath('copy.img') as copy_path, \ 160*bdebdc71SKevin Wolf iotests.VM() as vm: 161*bdebdc71SKevin Wolf 162*bdebdc71SKevin Wolf img_size = '4M' 163*bdebdc71SKevin Wolf iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size) 164*bdebdc71SKevin Wolf iotests.qemu_io('-c', 'write 0 %s' % (img_size), 165*bdebdc71SKevin Wolf '-f', iotests.imgfmt, disk_path) 166*bdebdc71SKevin Wolf 167*bdebdc71SKevin Wolf iotests.log('Launching VM...') 168*bdebdc71SKevin Wolf vm.add_blockdev(vm.qmp_to_opts({ 169*bdebdc71SKevin Wolf 'driver': iotests.imgfmt, 170*bdebdc71SKevin Wolf 'node-name': 'drive0-node', 171*bdebdc71SKevin Wolf 'file': { 172*bdebdc71SKevin Wolf 'driver': 'file', 173*bdebdc71SKevin Wolf 'filename': disk_path, 174*bdebdc71SKevin Wolf }, 175*bdebdc71SKevin Wolf })) 176*bdebdc71SKevin Wolf vm.launch() 177*bdebdc71SKevin Wolf 178*bdebdc71SKevin Wolf # In order to keep things deterministic (especially progress in query-job, 179*bdebdc71SKevin Wolf # but related to this also automatic state transitions like job 180*bdebdc71SKevin Wolf # completion), but still get pause points often enough to avoid making this 181*bdebdc71SKevin Wolf # test very slow, it's important to have the right ratio between speed and 182*bdebdc71SKevin Wolf # buf_size. 183*bdebdc71SKevin Wolf # 184*bdebdc71SKevin Wolf # For backup, buf_size is hard-coded to the source image cluster size (64k), 185*bdebdc71SKevin Wolf # so we'll pick the same for mirror. The slice time, i.e. the granularity 186*bdebdc71SKevin Wolf # of the rate limiting is 100ms. With a speed of 256k per second, we can 187*bdebdc71SKevin Wolf # get four pause points per second. This gives us 250ms per iteration, 188*bdebdc71SKevin Wolf # which should be enough to stay deterministic. 189*bdebdc71SKevin Wolf 190*bdebdc71SKevin Wolf test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={ 191*bdebdc71SKevin Wolf 'device': 'drive0-node', 192*bdebdc71SKevin Wolf 'target': copy_path, 193*bdebdc71SKevin Wolf 'sync': 'full', 194*bdebdc71SKevin Wolf 'speed': 262144, 195*bdebdc71SKevin Wolf 'buf_size': 65536, 196*bdebdc71SKevin Wolf }) 197*bdebdc71SKevin Wolf 198*bdebdc71SKevin Wolf for auto_finalize in [True, False]: 199*bdebdc71SKevin Wolf for auto_dismiss in [True, False]: 200*bdebdc71SKevin Wolf test_job_lifecycle(vm, 'drive-backup', job_args={ 201*bdebdc71SKevin Wolf 'device': 'drive0-node', 202*bdebdc71SKevin Wolf 'target': copy_path, 203*bdebdc71SKevin Wolf 'sync': 'full', 204*bdebdc71SKevin Wolf 'speed': 262144, 205*bdebdc71SKevin Wolf 'auto-finalize': auto_finalize, 206*bdebdc71SKevin Wolf 'auto-dismiss': auto_dismiss, 207*bdebdc71SKevin Wolf }) 208*bdebdc71SKevin Wolf 209*bdebdc71SKevin Wolf vm.shutdown() 210