17c477526SPhilippe Mathieu-Daudé#!/usr/bin/env python3 29dd003a9SVladimir Sementsov-Ogievskiy# group: rw 3bdebdc71SKevin Wolf# 4bdebdc71SKevin Wolf# Copyright (C) 2018 Red Hat, Inc. 5bdebdc71SKevin Wolf# 6bdebdc71SKevin Wolf# This program is free software; you can redistribute it and/or modify 7bdebdc71SKevin Wolf# it under the terms of the GNU General Public License as published by 8bdebdc71SKevin Wolf# the Free Software Foundation; either version 2 of the License, or 9bdebdc71SKevin Wolf# (at your option) any later version. 10bdebdc71SKevin Wolf# 11bdebdc71SKevin Wolf# This program is distributed in the hope that it will be useful, 12bdebdc71SKevin Wolf# but WITHOUT ANY WARRANTY; without even the implied warranty of 13bdebdc71SKevin Wolf# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14bdebdc71SKevin Wolf# GNU General Public License for more details. 15bdebdc71SKevin Wolf# 16bdebdc71SKevin Wolf# You should have received a copy of the GNU General Public License 17bdebdc71SKevin Wolf# along with this program. If not, see <http://www.gnu.org/licenses/>. 18bdebdc71SKevin Wolf# 19bdebdc71SKevin Wolf# Creator/Owner: Kevin Wolf <kwolf@redhat.com> 20bdebdc71SKevin Wolf# 21bdebdc71SKevin Wolf# Check using the job-* QMP commands with block jobs 22bdebdc71SKevin Wolf 23bdebdc71SKevin Wolfimport iotests 24bdebdc71SKevin Wolf 257d814059SJohn Snowiotests.script_initialize(supported_fmts=['qcow2']) 26bdebdc71SKevin Wolf 27d9efe938SMax Reitzimg_size = 4 * 1024 * 1024 28d9efe938SMax Reitz 29bdebdc71SKevin Wolfdef pause_wait(vm, job_id): 30bdebdc71SKevin Wolf with iotests.Timeout(3, "Timeout waiting for job to pause"): 31bdebdc71SKevin Wolf while True: 32bdebdc71SKevin Wolf result = vm.qmp('query-jobs') 33bdebdc71SKevin Wolf for job in result['return']: 34bdebdc71SKevin Wolf if job['id'] == job_id and job['status'] in ['paused', 'standby']: 35bdebdc71SKevin Wolf return job 36bdebdc71SKevin Wolf 37bdebdc71SKevin Wolf# Test that block-job-pause/resume and job-pause/resume can be mixed 38bdebdc71SKevin Wolfdef test_pause_resume(vm): 39bdebdc71SKevin Wolf for pause_cmd, pause_arg in [('block-job-pause', 'device'), 40bdebdc71SKevin Wolf ('job-pause', 'id')]: 41bdebdc71SKevin Wolf for resume_cmd, resume_arg in [('block-job-resume', 'device'), 42bdebdc71SKevin Wolf ('job-resume', 'id')]: 43bdebdc71SKevin Wolf iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd)) 44bdebdc71SKevin Wolf 45bdebdc71SKevin Wolf iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) 46bdebdc71SKevin Wolf pause_wait(vm, 'job0') 47bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 4883f90b53SMax Reitz result = vm.qmp('query-jobs') 4983f90b53SMax Reitz iotests.log(result) 5083f90b53SMax Reitz 5183f90b53SMax Reitz old_progress = result['return'][0]['current-progress'] 5283f90b53SMax Reitz total_progress = result['return'][0]['total-progress'] 53bdebdc71SKevin Wolf 54bdebdc71SKevin Wolf iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) 55bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 5683f90b53SMax Reitz if old_progress < total_progress: 5783f90b53SMax Reitz # Wait for the job to advance 5883f90b53SMax Reitz while result['return'][0]['current-progress'] == old_progress: 5983f90b53SMax Reitz result = vm.qmp('query-jobs') 6083f90b53SMax Reitz iotests.log(result) 6183f90b53SMax Reitz else: 6283f90b53SMax Reitz # Already reached the end, so the job cannot advance 6383f90b53SMax Reitz # any further; therefore, the query-jobs result can be 6483f90b53SMax Reitz # logged immediately 65bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 66bdebdc71SKevin Wolf 672288ccfaSSergio Lopezdef test_job_lifecycle(vm, job, job_args, has_ready=False, is_mirror=False): 68d9efe938SMax Reitz global img_size 69d9efe938SMax Reitz 70bdebdc71SKevin Wolf iotests.log('') 71bdebdc71SKevin Wolf iotests.log('') 72bdebdc71SKevin Wolf iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' % 73bdebdc71SKevin Wolf (job, 74bdebdc71SKevin Wolf job_args.get('auto-finalize', True), 75bdebdc71SKevin Wolf job_args.get('auto-dismiss', True))) 76bdebdc71SKevin Wolf iotests.log(vm.qmp(job, job_id='job0', **job_args)) 77bdebdc71SKevin Wolf 78bdebdc71SKevin Wolf # Depending on the storage, the first request may or may not have completed 7983f90b53SMax Reitz # yet (and the total progress may not have been fully determined yet), so 8083f90b53SMax Reitz # filter out the progress. Later query-job calls don't need the filtering 8183f90b53SMax Reitz # because the progress is made deterministic by the block job speed 82bdebdc71SKevin Wolf result = vm.qmp('query-jobs') 83bdebdc71SKevin Wolf for j in result['return']: 8483f90b53SMax Reitz j['current-progress'] = 'FILTERED' 8583f90b53SMax Reitz j['total-progress'] = 'FILTERED' 86bdebdc71SKevin Wolf iotests.log(result) 87bdebdc71SKevin Wolf 88bdebdc71SKevin Wolf # undefined -> created -> running 89bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 90bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 91bdebdc71SKevin Wolf 92d9efe938SMax Reitz # Wait for total-progress to stabilize 93d9efe938SMax Reitz while vm.qmp('query-jobs')['return'][0]['total-progress'] < img_size: 94d9efe938SMax Reitz pass 95d9efe938SMax Reitz 96bdebdc71SKevin Wolf # RUNNING state: 97bdebdc71SKevin Wolf # pause/resume should work, complete/finalize/dismiss should error out 98bdebdc71SKevin Wolf iotests.log('') 99bdebdc71SKevin Wolf iotests.log('Pause/resume in RUNNING') 100bdebdc71SKevin Wolf test_pause_resume(vm) 101bdebdc71SKevin Wolf 102bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 103bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 104bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 105bdebdc71SKevin Wolf 106bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-complete', device='job0')) 107bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-finalize', id='job0')) 108bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-dismiss', id='job0')) 109bdebdc71SKevin Wolf 110bdebdc71SKevin Wolf # Let the job complete (or transition to READY if it supports that) 111bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0)) 112bdebdc71SKevin Wolf if has_ready: 113bdebdc71SKevin Wolf iotests.log('') 114bdebdc71SKevin Wolf iotests.log('Waiting for READY state...') 115bdebdc71SKevin Wolf vm.event_wait('BLOCK_JOB_READY') 116bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 117bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 118bdebdc71SKevin Wolf 119bdebdc71SKevin Wolf # READY state: 120bdebdc71SKevin Wolf # pause/resume/complete should work, finalize/dismiss should error out 121bdebdc71SKevin Wolf iotests.log('') 122bdebdc71SKevin Wolf iotests.log('Pause/resume in READY') 123bdebdc71SKevin Wolf test_pause_resume(vm) 124bdebdc71SKevin Wolf 125bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 126bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 127bdebdc71SKevin Wolf 128bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-finalize', id='job0')) 129bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-dismiss', id='job0')) 130bdebdc71SKevin Wolf 131bdebdc71SKevin Wolf # Transition to WAITING 132bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 133bdebdc71SKevin Wolf 134bdebdc71SKevin Wolf # Move to WAITING and PENDING state 135bdebdc71SKevin Wolf iotests.log('') 136bdebdc71SKevin Wolf iotests.log('Waiting for PENDING state...') 137bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 138bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 1392288ccfaSSergio Lopez if is_mirror: 1402288ccfaSSergio Lopez iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 1412288ccfaSSergio Lopez iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 142bdebdc71SKevin Wolf 143bdebdc71SKevin Wolf if not job_args.get('auto-finalize', True): 144bdebdc71SKevin Wolf # PENDING state: 145bdebdc71SKevin Wolf # finalize should work, pause/complete/dismiss should error out 146bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 147bdebdc71SKevin Wolf 148bdebdc71SKevin Wolf iotests.log(vm.qmp('job-pause', id='job0')) 149bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 150bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 151bdebdc71SKevin Wolf 152bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-pause', device='job0')) 153bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-complete', device='job0')) 154bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-dismiss', id='job0')) 155bdebdc71SKevin Wolf 156bdebdc71SKevin Wolf # Transition to CONCLUDED 157bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 158bdebdc71SKevin Wolf 159bdebdc71SKevin Wolf 160bdebdc71SKevin Wolf # Move to CONCLUDED state 161bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 162bdebdc71SKevin Wolf 163bdebdc71SKevin Wolf if not job_args.get('auto-dismiss', True): 164bdebdc71SKevin Wolf # CONCLUDED state: 165bdebdc71SKevin Wolf # dismiss should work, pause/complete/finalize should error out 166bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 167bdebdc71SKevin Wolf 168bdebdc71SKevin Wolf iotests.log(vm.qmp('job-pause', id='job0')) 169bdebdc71SKevin Wolf iotests.log(vm.qmp('job-complete', id='job0')) 170bdebdc71SKevin Wolf iotests.log(vm.qmp('job-finalize', id='job0')) 171bdebdc71SKevin Wolf 172bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-pause', device='job0')) 173bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-complete', device='job0')) 174bdebdc71SKevin Wolf iotests.log(vm.qmp('block-job-finalize', id='job0')) 175bdebdc71SKevin Wolf 176bdebdc71SKevin Wolf # Transition to NULL 177bdebdc71SKevin Wolf iotests.log(vm.qmp('job-dismiss', id='job0')) 178bdebdc71SKevin Wolf 179bdebdc71SKevin Wolf # Move to NULL state 180bdebdc71SKevin Wolf iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) 181bdebdc71SKevin Wolf iotests.log(vm.qmp('query-jobs')) 182bdebdc71SKevin Wolf 183bdebdc71SKevin Wolf 184bdebdc71SKevin Wolfwith iotests.FilePath('disk.img') as disk_path, \ 185bdebdc71SKevin Wolf iotests.FilePath('copy.img') as copy_path, \ 186bdebdc71SKevin Wolf iotests.VM() as vm: 187bdebdc71SKevin Wolf 188d9efe938SMax Reitz iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, str(img_size)) 189d9efe938SMax Reitz iotests.qemu_io('-c', 'write 0 %i' % (img_size), 190bdebdc71SKevin Wolf '-f', iotests.imgfmt, disk_path) 191bdebdc71SKevin Wolf 192bdebdc71SKevin Wolf iotests.log('Launching VM...') 193bdebdc71SKevin Wolf vm.add_blockdev(vm.qmp_to_opts({ 194bdebdc71SKevin Wolf 'driver': iotests.imgfmt, 195bdebdc71SKevin Wolf 'node-name': 'drive0-node', 196bdebdc71SKevin Wolf 'file': { 197bdebdc71SKevin Wolf 'driver': 'file', 198bdebdc71SKevin Wolf 'filename': disk_path, 199bdebdc71SKevin Wolf }, 200bdebdc71SKevin Wolf })) 201bdebdc71SKevin Wolf vm.launch() 202bdebdc71SKevin Wolf 203bdebdc71SKevin Wolf # In order to keep things deterministic (especially progress in query-job, 204bdebdc71SKevin Wolf # but related to this also automatic state transitions like job 205bdebdc71SKevin Wolf # completion), but still get pause points often enough to avoid making this 206bdebdc71SKevin Wolf # test very slow, it's important to have the right ratio between speed and 207*34a5de52SVladimir Sementsov-Ogievskiy # copy-chunk-size. 208bdebdc71SKevin Wolf # 209*34a5de52SVladimir Sementsov-Ogievskiy # Chose 64k copy-chunk-size both for mirror (by buf_size) and backup (by 210*34a5de52SVladimir Sementsov-Ogievskiy # x-max-chunk). The slice time, i.e. the granularity of the rate limiting 211*34a5de52SVladimir Sementsov-Ogievskiy # is 100ms. With a speed of 256k per second, we can get four pause points 212*34a5de52SVladimir Sementsov-Ogievskiy # per second. This gives us 250ms per iteration, which should be enough to 213*34a5de52SVladimir Sementsov-Ogievskiy # stay deterministic. 214bdebdc71SKevin Wolf 215bdebdc71SKevin Wolf test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={ 216bdebdc71SKevin Wolf 'device': 'drive0-node', 217bdebdc71SKevin Wolf 'target': copy_path, 218bdebdc71SKevin Wolf 'sync': 'full', 219bdebdc71SKevin Wolf 'speed': 262144, 220bdebdc71SKevin Wolf 'buf_size': 65536, 221bdebdc71SKevin Wolf }) 222bdebdc71SKevin Wolf 223bdebdc71SKevin Wolf for auto_finalize in [True, False]: 224bdebdc71SKevin Wolf for auto_dismiss in [True, False]: 2252288ccfaSSergio Lopez test_job_lifecycle(vm, 'drive-backup', is_mirror=True, job_args={ 226bdebdc71SKevin Wolf 'device': 'drive0-node', 227bdebdc71SKevin Wolf 'target': copy_path, 228bdebdc71SKevin Wolf 'sync': 'full', 229bdebdc71SKevin Wolf 'speed': 262144, 230*34a5de52SVladimir Sementsov-Ogievskiy 'x-perf': {'max-chunk': 65536}, 231bdebdc71SKevin Wolf 'auto-finalize': auto_finalize, 232bdebdc71SKevin Wolf 'auto-dismiss': auto_dismiss, 233bdebdc71SKevin Wolf }) 234bdebdc71SKevin Wolf 235bdebdc71SKevin Wolf vm.shutdown() 236