Merge tag 'qemu-macppc-20230206' of https://github.com/mcayland/qemu into staging
[qemu.git] / tests / qemu-iotests / 219
blobd1757e9e6fcfda42e56e4c0673769db779d7e96d
1 #!/usr/bin/env python3
2 # group: rw
4 # Copyright (C) 2018 Red Hat, Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 # Creator/Owner: Kevin Wolf <kwolf@redhat.com>
21 # Check using the job-* QMP commands with block jobs
23 import iotests
25 iotests.script_initialize(supported_fmts=['qcow2'])
27 img_size = 4 * 1024 * 1024
29 def pause_wait(vm, job_id):
30     with iotests.Timeout(3, "Timeout waiting for job to pause"):
31         while True:
32             result = vm.qmp('query-jobs')
33             for job in result['return']:
34                 if job['id'] == job_id and job['status'] in ['paused', 'standby']:
35                     return job
37 # Test that block-job-pause/resume and job-pause/resume can be mixed
38 def test_pause_resume(vm):
39     for pause_cmd, pause_arg in [('block-job-pause', 'device'),
40                                  ('job-pause', 'id')]:
41         for resume_cmd, resume_arg in [('block-job-resume', 'device'),
42                                        ('job-resume', 'id')]:
43             iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd))
45             iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
46             pause_wait(vm, 'job0')
47             iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
48             result = vm.qmp('query-jobs')
49             iotests.log(result)
51             old_progress = result['return'][0]['current-progress']
52             total_progress = result['return'][0]['total-progress']
54             iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
55             iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
56             if old_progress < total_progress:
57                 # Wait for the job to advance
58                 while result['return'][0]['current-progress'] == old_progress:
59                     result = vm.qmp('query-jobs')
60                 iotests.log(result)
61             else:
62                 # Already reached the end, so the job cannot advance
63                 # any further; therefore, the query-jobs result can be
64                 # logged immediately
65                 iotests.log(vm.qmp('query-jobs'))
67 def test_job_lifecycle(vm, job, job_args, has_ready=False, is_mirror=False):
68     global img_size
70     iotests.log('')
71     iotests.log('')
72     iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
73                 (job,
74                  job_args.get('auto-finalize', True),
75                  job_args.get('auto-dismiss', True)))
76     iotests.log(vm.qmp(job, job_id='job0', **job_args))
78     # Depending on the storage, the first request may or may not have completed
79     # yet (and the total progress may not have been fully determined yet), so
80     # filter out the progress. Later query-job calls don't need the filtering
81     # because the progress is made deterministic by the block job speed
82     result = vm.qmp('query-jobs')
83     for j in result['return']:
84         j['current-progress'] = 'FILTERED'
85         j['total-progress'] = 'FILTERED'
86     iotests.log(result)
88     # undefined -> created -> running
89     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
90     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
92     # Wait for total-progress to stabilize
93     while vm.qmp('query-jobs')['return'][0]['total-progress'] < img_size:
94         pass
96     # RUNNING state:
97     # pause/resume should work, complete/finalize/dismiss should error out
98     iotests.log('')
99     iotests.log('Pause/resume in RUNNING')
100     test_pause_resume(vm)
102     iotests.log(vm.qmp('job-complete', id='job0'))
103     iotests.log(vm.qmp('job-finalize', id='job0'))
104     iotests.log(vm.qmp('job-dismiss', id='job0'))
106     iotests.log(vm.qmp('block-job-complete', device='job0'))
107     iotests.log(vm.qmp('block-job-finalize', id='job0'))
108     iotests.log(vm.qmp('block-job-dismiss', id='job0'))
110     # Let the job complete (or transition to READY if it supports that)
111     iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0))
112     if has_ready:
113         iotests.log('')
114         iotests.log('Waiting for READY state...')
115         vm.event_wait('BLOCK_JOB_READY')
116         iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
117         iotests.log(vm.qmp('query-jobs'))
119         # READY state:
120         # pause/resume/complete should work, finalize/dismiss should error out
121         iotests.log('')
122         iotests.log('Pause/resume in READY')
123         test_pause_resume(vm)
125         iotests.log(vm.qmp('job-finalize', id='job0'))
126         iotests.log(vm.qmp('job-dismiss', id='job0'))
128         iotests.log(vm.qmp('block-job-finalize', id='job0'))
129         iotests.log(vm.qmp('block-job-dismiss', id='job0'))
131         # Transition to WAITING
132         iotests.log(vm.qmp('job-complete', id='job0'))
134     # Move to WAITING and PENDING state
135     iotests.log('')
136     iotests.log('Waiting for PENDING state...')
137     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
138     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
139     if is_mirror:
140         iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
141         iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
143     if not job_args.get('auto-finalize', True):
144         # PENDING state:
145         # finalize should work, pause/complete/dismiss should error out
146         iotests.log(vm.qmp('query-jobs'))
148         iotests.log(vm.qmp('job-pause', id='job0'))
149         iotests.log(vm.qmp('job-complete', id='job0'))
150         iotests.log(vm.qmp('job-dismiss', id='job0'))
152         iotests.log(vm.qmp('block-job-pause', device='job0'))
153         iotests.log(vm.qmp('block-job-complete', device='job0'))
154         iotests.log(vm.qmp('block-job-dismiss', id='job0'))
156         # Transition to CONCLUDED
157         iotests.log(vm.qmp('job-finalize', id='job0'))
160     # Move to CONCLUDED state
161     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
163     if not job_args.get('auto-dismiss', True):
164         # CONCLUDED state:
165         # dismiss should work, pause/complete/finalize should error out
166         iotests.log(vm.qmp('query-jobs'))
168         iotests.log(vm.qmp('job-pause', id='job0'))
169         iotests.log(vm.qmp('job-complete', id='job0'))
170         iotests.log(vm.qmp('job-finalize', id='job0'))
172         iotests.log(vm.qmp('block-job-pause', device='job0'))
173         iotests.log(vm.qmp('block-job-complete', device='job0'))
174         iotests.log(vm.qmp('block-job-finalize', id='job0'))
176         # Transition to NULL
177         iotests.log(vm.qmp('job-dismiss', id='job0'))
179     # Move to NULL state
180     iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
181     iotests.log(vm.qmp('query-jobs'))
184 with iotests.FilePath('disk.img') as disk_path, \
185      iotests.FilePath('copy.img') as copy_path, \
186      iotests.VM() as vm:
188     iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, str(img_size))
189     iotests.qemu_io('-c', 'write 0 %i' % (img_size),
190                     '-f', iotests.imgfmt, disk_path)
192     iotests.log('Launching VM...')
193     vm.add_blockdev(vm.qmp_to_opts({
194         'driver': iotests.imgfmt,
195         'node-name': 'drive0-node',
196         'file': {
197             'driver': 'file',
198             'filename': disk_path,
199         },
200     }))
201     vm.launch()
203     # In order to keep things deterministic (especially progress in query-job,
204     # but related to this also automatic state transitions like job
205     # completion), but still get pause points often enough to avoid making this
206     # test very slow, it's important to have the right ratio between speed and
207     # copy-chunk-size.
208     #
209     # Chose 64k copy-chunk-size both for mirror (by buf_size) and backup (by
210     # x-max-chunk). The slice time, i.e. the granularity of the rate limiting
211     # is 100ms. With a speed of 256k per second, we can get four pause points
212     # per second. This gives us 250ms per iteration, which should be enough to
213     # stay deterministic.
215     test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
216         'device': 'drive0-node',
217         'target': copy_path,
218         'sync': 'full',
219         'speed': 262144,
220         'buf_size': 65536,
221     })
223     for auto_finalize in [True, False]:
224         for auto_dismiss in [True, False]:
225             test_job_lifecycle(vm, 'drive-backup', is_mirror=True, job_args={
226                 'device': 'drive0-node',
227                 'target': copy_path,
228                 'sync': 'full',
229                 'speed': 262144,
230                 'x-perf': {'max-chunk': 65536},
231                 'auto-finalize': auto_finalize,
232                 'auto-dismiss': auto_dismiss,
233             })
235     vm.shutdown()