aboutsummaryrefslogtreecommitdiff
path: root/tests/qemu-iotests/219
diff options
context:
space:
mode:
Diffstat (limited to 'tests/qemu-iotests/219')
-rwxr-xr-xtests/qemu-iotests/219209
1 files changed, 209 insertions, 0 deletions
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
new file mode 100755
index 0000000000..898a26eef0
--- /dev/null
+++ b/tests/qemu-iotests/219
@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
+#
+# Check using the job-* QMP commands with block jobs
+
+import iotests
+
+iotests.verify_image_format(supported_fmts=['qcow2'])
+
+def pause_wait(vm, job_id):
+ with iotests.Timeout(3, "Timeout waiting for job to pause"):
+ while True:
+ result = vm.qmp('query-jobs')
+ for job in result['return']:
+ if job['id'] == job_id and job['status'] in ['paused', 'standby']:
+ return job
+
+# Test that block-job-pause/resume and job-pause/resume can be mixed
+def test_pause_resume(vm):
+ for pause_cmd, pause_arg in [('block-job-pause', 'device'),
+ ('job-pause', 'id')]:
+ for resume_cmd, resume_arg in [('block-job-resume', 'device'),
+ ('job-resume', 'id')]:
+ iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd))
+
+ iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
+ pause_wait(vm, 'job0')
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+ iotests.log(vm.qmp('query-jobs'))
+
+ iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+ iotests.log(vm.qmp('query-jobs'))
+
+def test_job_lifecycle(vm, job, job_args, has_ready=False):
+ iotests.log('')
+ iotests.log('')
+ iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
+ (job,
+ job_args.get('auto-finalize', True),
+ job_args.get('auto-dismiss', True)))
+ iotests.log(vm.qmp(job, job_id='job0', **job_args))
+
+ # Depending on the storage, the first request may or may not have completed
+ # yet, so filter out the progress. Later query-job calls don't need the
+ # filtering because the progress is made deterministic by the block job
+ # speed
+ result = vm.qmp('query-jobs')
+ for j in result['return']:
+ del j['current-progress']
+ iotests.log(result)
+
+ # undefined -> created -> running
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+
+ # RUNNING state:
+ # pause/resume should work, complete/finalize/dismiss should error out
+ iotests.log('')
+ iotests.log('Pause/resume in RUNNING')
+ test_pause_resume(vm)
+
+ iotests.log(vm.qmp('job-complete', id='job0'))
+ iotests.log(vm.qmp('job-finalize', id='job0'))
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
+
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
+
+ # Let the job complete (or transition to READY if it supports that)
+ iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0))
+ if has_ready:
+ iotests.log('')
+ iotests.log('Waiting for READY state...')
+ vm.event_wait('BLOCK_JOB_READY')
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+ iotests.log(vm.qmp('query-jobs'))
+
+ # READY state:
+ # pause/resume/complete should work, finalize/dismiss should error out
+ iotests.log('')
+ iotests.log('Pause/resume in READY')
+ test_pause_resume(vm)
+
+ iotests.log(vm.qmp('job-finalize', id='job0'))
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
+
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
+
+ # Transition to WAITING
+ iotests.log(vm.qmp('job-complete', id='job0'))
+
+ # Move to WAITING and PENDING state
+ iotests.log('')
+ iotests.log('Waiting for PENDING state...')
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+
+ if not job_args.get('auto-finalize', True):
+ # PENDING state:
+ # finalize should work, pause/complete/dismiss should error out
+ iotests.log(vm.qmp('query-jobs'))
+
+ iotests.log(vm.qmp('job-pause', id='job0'))
+ iotests.log(vm.qmp('job-complete', id='job0'))
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
+
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
+
+ # Transition to CONCLUDED
+ iotests.log(vm.qmp('job-finalize', id='job0'))
+
+
+ # Move to CONCLUDED state
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+
+ if not job_args.get('auto-dismiss', True):
+ # CONCLUDED state:
+ # dismiss should work, pause/complete/finalize should error out
+ iotests.log(vm.qmp('query-jobs'))
+
+ iotests.log(vm.qmp('job-pause', id='job0'))
+ iotests.log(vm.qmp('job-complete', id='job0'))
+ iotests.log(vm.qmp('job-finalize', id='job0'))
+
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
+
+ # Transition to NULL
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
+
+ # Move to NULL state
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
+ iotests.log(vm.qmp('query-jobs'))
+
+
+with iotests.FilePath('disk.img') as disk_path, \
+ iotests.FilePath('copy.img') as copy_path, \
+ iotests.VM() as vm:
+
+ img_size = '4M'
+ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size)
+ iotests.qemu_io('-c', 'write 0 %s' % (img_size),
+ '-f', iotests.imgfmt, disk_path)
+
+ iotests.log('Launching VM...')
+ vm.add_blockdev(vm.qmp_to_opts({
+ 'driver': iotests.imgfmt,
+ 'node-name': 'drive0-node',
+ 'file': {
+ 'driver': 'file',
+ 'filename': disk_path,
+ },
+ }))
+ vm.launch()
+
+ # In order to keep things deterministic (especially progress in query-job,
+ # but related to this also automatic state transitions like job
+ # completion), but still get pause points often enough to avoid making this
+ # test very slow, it's important to have the right ratio between speed and
+ # buf_size.
+ #
+ # For backup, buf_size is hard-coded to the source image cluster size (64k),
+ # so we'll pick the same for mirror. The slice time, i.e. the granularity
+ # of the rate limiting is 100ms. With a speed of 256k per second, we can
+ # get four pause points per second. This gives us 250ms per iteration,
+ # which should be enough to stay deterministic.
+
+ test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
+ 'device': 'drive0-node',
+ 'target': copy_path,
+ 'sync': 'full',
+ 'speed': 262144,
+ 'buf_size': 65536,
+ })
+
+ for auto_finalize in [True, False]:
+ for auto_dismiss in [True, False]:
+ test_job_lifecycle(vm, 'drive-backup', job_args={
+ 'device': 'drive0-node',
+ 'target': copy_path,
+ 'sync': 'full',
+ 'speed': 262144,
+ 'auto-finalize': auto_finalize,
+ 'auto-dismiss': auto_dismiss,
+ })
+
+ vm.shutdown()