Statistics
| Branch: | Revision:

root / tests / qemu-iotests / 030 @ b59b3d57

History | View | Annotate | Download (19.6 kB)

1
#!/usr/bin/env python
2
#
3
# Tests for image streaming.
4
#
5
# Copyright (C) 2012 IBM Corp.
6
#
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 2 of the License, or
10
# (at your option) any later version.
11
#
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
#
20

    
21
import time
22
import os
23
import iotests
24
from iotests import qemu_img, qemu_io
25

    
26
backing_img = os.path.join(iotests.test_dir, 'backing.img')
27
mid_img = os.path.join(iotests.test_dir, 'mid.img')
28
test_img = os.path.join(iotests.test_dir, 'test.img')
29

    
30
class TestSingleDrive(iotests.QMPTestCase):
31
    image_len = 1 * 1024 * 1024 # MB
32

    
33
    def setUp(self):
34
        iotests.create_image(backing_img, TestSingleDrive.image_len)
35
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
36
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
37
        qemu_io('-c', 'write -P 0x1 0 512', backing_img)
38
        self.vm = iotests.VM().add_drive(test_img)
39
        self.vm.launch()
40

    
41
    def tearDown(self):
42
        self.vm.shutdown()
43
        os.remove(test_img)
44
        os.remove(mid_img)
45
        os.remove(backing_img)
46

    
47
    def test_stream(self):
48
        self.assert_no_active_block_jobs()
49

    
50
        result = self.vm.qmp('block-stream', device='drive0')
51
        self.assert_qmp(result, 'return', {})
52

    
53
        completed = False
54
        while not completed:
55
            for event in self.vm.get_qmp_events(wait=True):
56
                if event['event'] == 'BLOCK_JOB_COMPLETED':
57
                    self.assert_qmp(event, 'data/type', 'stream')
58
                    self.assert_qmp(event, 'data/device', 'drive0')
59
                    self.assert_qmp(event, 'data/offset', self.image_len)
60
                    self.assert_qmp(event, 'data/len', self.image_len)
61
                    completed = True
62

    
63
        self.assert_no_active_block_jobs()
64
        self.vm.shutdown()
65

    
66
        self.assertEqual(qemu_io('-c', 'map', backing_img),
67
                         qemu_io('-c', 'map', test_img),
68
                         'image file map does not match backing file after streaming')
69

    
70
    def test_stream_pause(self):
71
        self.assert_no_active_block_jobs()
72

    
73
        self.vm.pause_drive('drive0')
74
        result = self.vm.qmp('block-stream', device='drive0')
75
        self.assert_qmp(result, 'return', {})
76

    
77
        result = self.vm.qmp('block-job-pause', device='drive0')
78
        self.assert_qmp(result, 'return', {})
79

    
80
        time.sleep(1)
81
        result = self.vm.qmp('query-block-jobs')
82
        offset = self.dictpath(result, 'return[0]/offset')
83

    
84
        time.sleep(1)
85
        result = self.vm.qmp('query-block-jobs')
86
        self.assert_qmp(result, 'return[0]/offset', offset)
87

    
88
        result = self.vm.qmp('block-job-resume', device='drive0')
89
        self.assert_qmp(result, 'return', {})
90

    
91
        self.vm.resume_drive('drive0')
92
        completed = False
93
        while not completed:
94
            for event in self.vm.get_qmp_events(wait=True):
95
                if event['event'] == 'BLOCK_JOB_COMPLETED':
96
                    self.assert_qmp(event, 'data/type', 'stream')
97
                    self.assert_qmp(event, 'data/device', 'drive0')
98
                    self.assert_qmp(event, 'data/offset', self.image_len)
99
                    self.assert_qmp(event, 'data/len', self.image_len)
100
                    completed = True
101

    
102
        self.assert_no_active_block_jobs()
103
        self.vm.shutdown()
104

    
105
        self.assertEqual(qemu_io('-c', 'map', backing_img),
106
                         qemu_io('-c', 'map', test_img),
107
                         'image file map does not match backing file after streaming')
108

    
109
    def test_stream_partial(self):
110
        self.assert_no_active_block_jobs()
111

    
112
        result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
113
        self.assert_qmp(result, 'return', {})
114

    
115
        completed = False
116
        while not completed:
117
            for event in self.vm.get_qmp_events(wait=True):
118
                if event['event'] == 'BLOCK_JOB_COMPLETED':
119
                    self.assert_qmp(event, 'data/type', 'stream')
120
                    self.assert_qmp(event, 'data/device', 'drive0')
121
                    self.assert_qmp(event, 'data/offset', self.image_len)
122
                    self.assert_qmp(event, 'data/len', self.image_len)
123
                    completed = True
124

    
125
        self.assert_no_active_block_jobs()
126
        self.vm.shutdown()
127

    
128
        self.assertEqual(qemu_io('-c', 'map', mid_img),
129
                         qemu_io('-c', 'map', test_img),
130
                         'image file map does not match backing file after streaming')
131

    
132
    def test_device_not_found(self):
133
        result = self.vm.qmp('block-stream', device='nonexistent')
134
        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
135

    
136

    
137
class TestSmallerBackingFile(iotests.QMPTestCase):
138
    backing_len = 1 * 1024 * 1024 # MB
139
    image_len = 2 * backing_len
140

    
141
    def setUp(self):
142
        iotests.create_image(backing_img, self.backing_len)
143
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
144
        self.vm = iotests.VM().add_drive(test_img)
145
        self.vm.launch()
146

    
147
    # If this hangs, then you are missing a fix to complete streaming when the
148
    # end of the backing file is reached.
149
    def test_stream(self):
150
        self.assert_no_active_block_jobs()
151

    
152
        result = self.vm.qmp('block-stream', device='drive0')
153
        self.assert_qmp(result, 'return', {})
154

    
155
        completed = False
156
        while not completed:
157
            for event in self.vm.get_qmp_events(wait=True):
158
                if event['event'] == 'BLOCK_JOB_COMPLETED':
159
                    self.assert_qmp(event, 'data/type', 'stream')
160
                    self.assert_qmp(event, 'data/device', 'drive0')
161
                    self.assert_qmp(event, 'data/offset', self.image_len)
162
                    self.assert_qmp(event, 'data/len', self.image_len)
163
                    completed = True
164

    
165
        self.assert_no_active_block_jobs()
166
        self.vm.shutdown()
167

    
168
class TestErrors(iotests.QMPTestCase):
169
    image_len = 2 * 1024 * 1024 # MB
170

    
171
    # this should match STREAM_BUFFER_SIZE/512 in block/stream.c
172
    STREAM_BUFFER_SIZE = 512 * 1024
173

    
174
    def create_blkdebug_file(self, name, event, errno):
175
        file = open(name, 'w')
176
        file.write('''
177
[inject-error]
178
state = "1"
179
event = "%s"
180
errno = "%d"
181
immediately = "off"
182
once = "on"
183
sector = "%d"
184

    
185
[set-state]
186
state = "1"
187
event = "%s"
188
new_state = "2"
189

    
190
[set-state]
191
state = "2"
192
event = "%s"
193
new_state = "1"
194
''' % (event, errno, self.STREAM_BUFFER_SIZE / 512, event, event))
195
        file.close()
196

    
197
class TestEIO(TestErrors):
198
    def setUp(self):
199
        self.blkdebug_file = backing_img + ".blkdebug"
200
        iotests.create_image(backing_img, TestErrors.image_len)
201
        self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
202
        qemu_img('create', '-f', iotests.imgfmt,
203
                 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
204
                       % (self.blkdebug_file, backing_img),
205
                 test_img)
206
        self.vm = iotests.VM().add_drive(test_img)
207
        self.vm.launch()
208

    
209
    def tearDown(self):
210
        self.vm.shutdown()
211
        os.remove(test_img)
212
        os.remove(backing_img)
213
        os.remove(self.blkdebug_file)
214

    
215
    def test_report(self):
216
        self.assert_no_active_block_jobs()
217

    
218
        result = self.vm.qmp('block-stream', device='drive0')
219
        self.assert_qmp(result, 'return', {})
220

    
221
        completed = False
222
        error = False
223
        while not completed:
224
            for event in self.vm.get_qmp_events(wait=True):
225
                if event['event'] == 'BLOCK_JOB_ERROR':
226
                    self.assert_qmp(event, 'data/device', 'drive0')
227
                    self.assert_qmp(event, 'data/operation', 'read')
228
                    error = True
229
                elif event['event'] == 'BLOCK_JOB_COMPLETED':
230
                    self.assertTrue(error, 'job completed unexpectedly')
231
                    self.assert_qmp(event, 'data/type', 'stream')
232
                    self.assert_qmp(event, 'data/device', 'drive0')
233
                    self.assert_qmp(event, 'data/error', 'Input/output error')
234
                    self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
235
                    self.assert_qmp(event, 'data/len', self.image_len)
236
                    completed = True
237

    
238
        self.assert_no_active_block_jobs()
239
        self.vm.shutdown()
240

    
241
    def test_ignore(self):
242
        self.assert_no_active_block_jobs()
243

    
244
        result = self.vm.qmp('block-stream', device='drive0', on_error='ignore')
245
        self.assert_qmp(result, 'return', {})
246

    
247
        error = False
248
        completed = False
249
        while not completed:
250
            for event in self.vm.get_qmp_events(wait=True):
251
                if event['event'] == 'BLOCK_JOB_ERROR':
252
                    self.assert_qmp(event, 'data/device', 'drive0')
253
                    self.assert_qmp(event, 'data/operation', 'read')
254
                    result = self.vm.qmp('query-block-jobs')
255
                    self.assert_qmp(result, 'return[0]/paused', False)
256
                    error = True
257
                elif event['event'] == 'BLOCK_JOB_COMPLETED':
258
                    self.assertTrue(error, 'job completed unexpectedly')
259
                    self.assert_qmp(event, 'data/type', 'stream')
260
                    self.assert_qmp(event, 'data/device', 'drive0')
261
                    self.assert_qmp(event, 'data/error', 'Input/output error')
262
                    self.assert_qmp(event, 'data/offset', self.image_len)
263
                    self.assert_qmp(event, 'data/len', self.image_len)
264
                    completed = True
265

    
266
        self.assert_no_active_block_jobs()
267
        self.vm.shutdown()
268

    
269
    def test_stop(self):
270
        self.assert_no_active_block_jobs()
271

    
272
        result = self.vm.qmp('block-stream', device='drive0', on_error='stop')
273
        self.assert_qmp(result, 'return', {})
274

    
275
        error = False
276
        completed = False
277
        while not completed:
278
            for event in self.vm.get_qmp_events(wait=True):
279
                if event['event'] == 'BLOCK_JOB_ERROR':
280
                    self.assert_qmp(event, 'data/device', 'drive0')
281
                    self.assert_qmp(event, 'data/operation', 'read')
282

    
283
                    result = self.vm.qmp('query-block-jobs')
284
                    self.assert_qmp(result, 'return[0]/paused', True)
285
                    self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE)
286
                    self.assert_qmp(result, 'return[0]/io-status', 'failed')
287

    
288
                    result = self.vm.qmp('block-job-resume', device='drive0')
289
                    self.assert_qmp(result, 'return', {})
290

    
291
                    result = self.vm.qmp('query-block-jobs')
292
                    self.assert_qmp(result, 'return[0]/paused', False)
293
                    self.assert_qmp(result, 'return[0]/io-status', 'ok')
294
                    error = True
295
                elif event['event'] == 'BLOCK_JOB_COMPLETED':
296
                    self.assertTrue(error, 'job completed unexpectedly')
297
                    self.assert_qmp(event, 'data/type', 'stream')
298
                    self.assert_qmp(event, 'data/device', 'drive0')
299
                    self.assert_qmp_absent(event, 'data/error')
300
                    self.assert_qmp(event, 'data/offset', self.image_len)
301
                    self.assert_qmp(event, 'data/len', self.image_len)
302
                    completed = True
303

    
304
        self.assert_no_active_block_jobs()
305
        self.vm.shutdown()
306

    
307
    def test_enospc(self):
308
        self.assert_no_active_block_jobs()
309

    
310
        result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
311
        self.assert_qmp(result, 'return', {})
312

    
313
        completed = False
314
        error = False
315
        while not completed:
316
            for event in self.vm.get_qmp_events(wait=True):
317
                if event['event'] == 'BLOCK_JOB_ERROR':
318
                    self.assert_qmp(event, 'data/device', 'drive0')
319
                    self.assert_qmp(event, 'data/operation', 'read')
320
                    error = True
321
                elif event['event'] == 'BLOCK_JOB_COMPLETED':
322
                    self.assertTrue(error, 'job completed unexpectedly')
323
                    self.assert_qmp(event, 'data/type', 'stream')
324
                    self.assert_qmp(event, 'data/device', 'drive0')
325
                    self.assert_qmp(event, 'data/error', 'Input/output error')
326
                    self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
327
                    self.assert_qmp(event, 'data/len', self.image_len)
328
                    completed = True
329

    
330
        self.assert_no_active_block_jobs()
331
        self.vm.shutdown()
332

    
333
class TestENOSPC(TestErrors):
334
    def setUp(self):
335
        self.blkdebug_file = backing_img + ".blkdebug"
336
        iotests.create_image(backing_img, TestErrors.image_len)
337
        self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28)
338
        qemu_img('create', '-f', iotests.imgfmt,
339
                 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
340
                       % (self.blkdebug_file, backing_img),
341
                 test_img)
342
        self.vm = iotests.VM().add_drive(test_img)
343
        self.vm.launch()
344

    
345
    def tearDown(self):
346
        self.vm.shutdown()
347
        os.remove(test_img)
348
        os.remove(backing_img)
349
        os.remove(self.blkdebug_file)
350

    
351
    def test_enospc(self):
352
        self.assert_no_active_block_jobs()
353

    
354
        result = self.vm.qmp('block-stream', device='drive0', on_error='enospc')
355
        self.assert_qmp(result, 'return', {})
356

    
357
        error = False
358
        completed = False
359
        while not completed:
360
            for event in self.vm.get_qmp_events(wait=True):
361
                if event['event'] == 'BLOCK_JOB_ERROR':
362
                    self.assert_qmp(event, 'data/device', 'drive0')
363
                    self.assert_qmp(event, 'data/operation', 'read')
364

    
365
                    result = self.vm.qmp('query-block-jobs')
366
                    self.assert_qmp(result, 'return[0]/paused', True)
367
                    self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE)
368
                    self.assert_qmp(result, 'return[0]/io-status', 'nospace')
369

    
370
                    result = self.vm.qmp('block-job-resume', device='drive0')
371
                    self.assert_qmp(result, 'return', {})
372

    
373
                    result = self.vm.qmp('query-block-jobs')
374
                    self.assert_qmp(result, 'return[0]/paused', False)
375
                    self.assert_qmp(result, 'return[0]/io-status', 'ok')
376
                    error = True
377
                elif event['event'] == 'BLOCK_JOB_COMPLETED':
378
                    self.assertTrue(error, 'job completed unexpectedly')
379
                    self.assert_qmp(event, 'data/type', 'stream')
380
                    self.assert_qmp(event, 'data/device', 'drive0')
381
                    self.assert_qmp_absent(event, 'data/error')
382
                    self.assert_qmp(event, 'data/offset', self.image_len)
383
                    self.assert_qmp(event, 'data/len', self.image_len)
384
                    completed = True
385

    
386
        self.assert_no_active_block_jobs()
387
        self.vm.shutdown()
388

    
389
class TestStreamStop(iotests.QMPTestCase):
390
    image_len = 8 * 1024 * 1024 * 1024 # GB
391

    
392
    def setUp(self):
393
        qemu_img('create', backing_img, str(TestStreamStop.image_len))
394
        qemu_io('-c', 'write -P 0x1 0 32M', backing_img)
395
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
396
        qemu_io('-c', 'write -P 0x1 32M 32M', test_img)
397
        self.vm = iotests.VM().add_drive("blkdebug::" + test_img)
398
        self.vm.launch()
399

    
400
    def tearDown(self):
401
        self.vm.shutdown()
402
        os.remove(test_img)
403
        os.remove(backing_img)
404

    
405
    def test_stream_stop(self):
406
        self.assert_no_active_block_jobs()
407

    
408
        self.vm.pause_drive('drive0')
409
        result = self.vm.qmp('block-stream', device='drive0')
410
        self.assert_qmp(result, 'return', {})
411

    
412
        time.sleep(0.1)
413
        events = self.vm.get_qmp_events(wait=False)
414
        self.assertEqual(events, [], 'unexpected QMP event: %s' % events)
415

    
416
        self.cancel_and_wait(resume=True)
417

    
418
class TestSetSpeed(iotests.QMPTestCase):
419
    image_len = 80 * 1024 * 1024 # MB
420

    
421
    def setUp(self):
422
        qemu_img('create', backing_img, str(TestSetSpeed.image_len))
423
        qemu_io('-c', 'write -P 0x1 0 32M', backing_img)
424
        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
425
        qemu_io('-c', 'write -P 0x1 32M 32M', test_img)
426
        self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
427
        self.vm.launch()
428

    
429
    def tearDown(self):
430
        self.vm.shutdown()
431
        os.remove(test_img)
432
        os.remove(backing_img)
433

    
434
    # This is a short performance test which is not run by default.
435
    # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput"
436
    def perf_test_throughput(self):
437
        self.assert_no_active_block_jobs()
438

    
439
        result = self.vm.qmp('block-stream', device='drive0')
440
        self.assert_qmp(result, 'return', {})
441

    
442
        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
443
        self.assert_qmp(result, 'return', {})
444

    
445
        completed = False
446
        while not completed:
447
            for event in self.vm.get_qmp_events(wait=True):
448
                if event['event'] == 'BLOCK_JOB_COMPLETED':
449
                    self.assert_qmp(event, 'data/type', 'stream')
450
                    self.assert_qmp(event, 'data/device', 'drive0')
451
                    self.assert_qmp(event, 'data/offset', self.image_len)
452
                    self.assert_qmp(event, 'data/len', self.image_len)
453
                    completed = True
454

    
455
        self.assert_no_active_block_jobs()
456

    
457
    def test_set_speed(self):
458
        self.assert_no_active_block_jobs()
459

    
460
        self.vm.pause_drive('drive0')
461
        result = self.vm.qmp('block-stream', device='drive0')
462
        self.assert_qmp(result, 'return', {})
463

    
464
        # Default speed is 0
465
        result = self.vm.qmp('query-block-jobs')
466
        self.assert_qmp(result, 'return[0]/device', 'drive0')
467
        self.assert_qmp(result, 'return[0]/speed', 0)
468

    
469
        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
470
        self.assert_qmp(result, 'return', {})
471

    
472
        # Ensure the speed we set was accepted
473
        result = self.vm.qmp('query-block-jobs')
474
        self.assert_qmp(result, 'return[0]/device', 'drive0')
475
        self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
476

    
477
        self.cancel_and_wait(resume=True)
478
        self.vm.pause_drive('drive0')
479

    
480
        # Check setting speed in block-stream works
481
        result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024)
482
        self.assert_qmp(result, 'return', {})
483

    
484
        result = self.vm.qmp('query-block-jobs')
485
        self.assert_qmp(result, 'return[0]/device', 'drive0')
486
        self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
487

    
488
        self.cancel_and_wait(resume=True)
489

    
490
    def test_set_speed_invalid(self):
491
        self.assert_no_active_block_jobs()
492

    
493
        result = self.vm.qmp('block-stream', device='drive0', speed=-1)
494
        self.assert_qmp(result, 'error/class', 'GenericError')
495

    
496
        self.assert_no_active_block_jobs()
497

    
498
        result = self.vm.qmp('block-stream', device='drive0')
499
        self.assert_qmp(result, 'return', {})
500

    
501
        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
502
        self.assert_qmp(result, 'error/class', 'GenericError')
503

    
504
        self.cancel_and_wait()
505

    
506
if __name__ == '__main__':
507
    iotests.main(supported_fmts=['qcow2', 'qed'])