mirror of
https://git.proxmox.com/git/qemu
synced 2025-06-19 22:51:13 +00:00
qemu-iotests: add testcases for mirroring on-source-error/on-target-error
The new options are tested with blkdebug on both the source and the target. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
9eb80eadd4
commit
9dfa9f5930
@ -292,6 +292,259 @@ class TestMirrorNoBacking(ImageMirroringTestCase):
|
|||||||
self.assertTrue(self.compare_images(test_img, target_img),
|
self.assertTrue(self.compare_images(test_img, target_img),
|
||||||
'target image does not match source after mirroring')
|
'target image does not match source after mirroring')
|
||||||
|
|
||||||
|
class TestReadErrors(ImageMirroringTestCase):
|
||||||
|
image_len = 2 * 1024 * 1024 # MB
|
||||||
|
|
||||||
|
# this should be a multiple of twice the default granularity
|
||||||
|
# so that we hit this offset first in state 1
|
||||||
|
MIRROR_GRANULARITY = 1024 * 1024
|
||||||
|
|
||||||
|
def create_blkdebug_file(self, name, event, errno):
|
||||||
|
file = open(name, 'w')
|
||||||
|
file.write('''
|
||||||
|
[inject-error]
|
||||||
|
state = "1"
|
||||||
|
event = "%s"
|
||||||
|
errno = "%d"
|
||||||
|
immediately = "off"
|
||||||
|
once = "on"
|
||||||
|
sector = "%d"
|
||||||
|
|
||||||
|
[set-state]
|
||||||
|
state = "1"
|
||||||
|
event = "%s"
|
||||||
|
new_state = "2"
|
||||||
|
|
||||||
|
[set-state]
|
||||||
|
state = "2"
|
||||||
|
event = "%s"
|
||||||
|
new_state = "1"
|
||||||
|
''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.blkdebug_file = backing_img + ".blkdebug"
|
||||||
|
self.create_image(backing_img, TestReadErrors.image_len)
|
||||||
|
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
|
||||||
|
qemu_img('create', '-f', iotests.imgfmt,
|
||||||
|
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
|
||||||
|
% (self.blkdebug_file, backing_img),
|
||||||
|
test_img)
|
||||||
|
self.vm = iotests.VM().add_drive(test_img)
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.vm.shutdown()
|
||||||
|
os.remove(test_img)
|
||||||
|
os.remove(backing_img)
|
||||||
|
os.remove(self.blkdebug_file)
|
||||||
|
|
||||||
|
def test_report_read(self):
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
|
||||||
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
target=target_img)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
completed = False
|
||||||
|
error = False
|
||||||
|
while not completed:
|
||||||
|
for event in self.vm.get_qmp_events(wait=True):
|
||||||
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/operation', 'read')
|
||||||
|
error = True
|
||||||
|
elif event['event'] == 'BLOCK_JOB_READY':
|
||||||
|
self.assertTrue(False, 'job completed unexpectedly')
|
||||||
|
elif event['event'] == 'BLOCK_JOB_COMPLETED':
|
||||||
|
self.assertTrue(error, 'job completed unexpectedly')
|
||||||
|
self.assert_qmp(event, 'data/type', 'mirror')
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/error', 'Input/output error')
|
||||||
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
|
completed = True
|
||||||
|
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
|
def test_ignore_read(self):
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
|
||||||
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
target=target_img, on_source_error='ignore')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
event = self.vm.get_qmp_event(wait=True)
|
||||||
|
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/operation', 'read')
|
||||||
|
result = self.vm.qmp('query-block-jobs')
|
||||||
|
self.assert_qmp(result, 'return[0]/paused', False)
|
||||||
|
self.complete_and_wait()
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
|
def test_stop_read(self):
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
|
||||||
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
target=target_img, on_source_error='stop')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
error = False
|
||||||
|
ready = False
|
||||||
|
while not ready:
|
||||||
|
for event in self.vm.get_qmp_events(wait=True):
|
||||||
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/operation', 'read')
|
||||||
|
|
||||||
|
result = self.vm.qmp('query-block-jobs')
|
||||||
|
self.assert_qmp(result, 'return[0]/paused', True)
|
||||||
|
self.assert_qmp(result, 'return[0]/io-status', 'failed')
|
||||||
|
|
||||||
|
result = self.vm.qmp('block-job-resume', device='drive0')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
error = True
|
||||||
|
elif event['event'] == 'BLOCK_JOB_READY':
|
||||||
|
self.assertTrue(error, 'job completed unexpectedly')
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
ready = True
|
||||||
|
|
||||||
|
result = self.vm.qmp('query-block-jobs')
|
||||||
|
self.assert_qmp(result, 'return[0]/paused', False)
|
||||||
|
self.assert_qmp(result, 'return[0]/io-status', 'ok')
|
||||||
|
|
||||||
|
self.complete_and_wait(wait_ready=False)
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
|
class TestWriteErrors(ImageMirroringTestCase):
|
||||||
|
image_len = 2 * 1024 * 1024 # MB
|
||||||
|
|
||||||
|
# this should be a multiple of twice the default granularity
|
||||||
|
# so that we hit this offset first in state 1
|
||||||
|
MIRROR_GRANULARITY = 1024 * 1024
|
||||||
|
|
||||||
|
def create_blkdebug_file(self, name, event, errno):
|
||||||
|
file = open(name, 'w')
|
||||||
|
file.write('''
|
||||||
|
[inject-error]
|
||||||
|
state = "1"
|
||||||
|
event = "%s"
|
||||||
|
errno = "%d"
|
||||||
|
immediately = "off"
|
||||||
|
once = "on"
|
||||||
|
sector = "%d"
|
||||||
|
|
||||||
|
[set-state]
|
||||||
|
state = "1"
|
||||||
|
event = "%s"
|
||||||
|
new_state = "2"
|
||||||
|
|
||||||
|
[set-state]
|
||||||
|
state = "2"
|
||||||
|
event = "%s"
|
||||||
|
new_state = "1"
|
||||||
|
''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.blkdebug_file = target_img + ".blkdebug"
|
||||||
|
self.create_image(backing_img, TestWriteErrors.image_len)
|
||||||
|
self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
|
||||||
|
qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
|
||||||
|
self.vm = iotests.VM().add_drive(test_img)
|
||||||
|
self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
|
||||||
|
qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.vm.shutdown()
|
||||||
|
os.remove(test_img)
|
||||||
|
os.remove(backing_img)
|
||||||
|
os.remove(self.blkdebug_file)
|
||||||
|
|
||||||
|
def test_report_write(self):
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
|
||||||
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
mode='existing', target=self.target_img)
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
completed = False
|
||||||
|
error = False
|
||||||
|
while not completed:
|
||||||
|
for event in self.vm.get_qmp_events(wait=True):
|
||||||
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/operation', 'write')
|
||||||
|
error = True
|
||||||
|
elif event['event'] == 'BLOCK_JOB_READY':
|
||||||
|
self.assertTrue(False, 'job completed unexpectedly')
|
||||||
|
elif event['event'] == 'BLOCK_JOB_COMPLETED':
|
||||||
|
self.assertTrue(error, 'job completed unexpectedly')
|
||||||
|
self.assert_qmp(event, 'data/type', 'mirror')
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/error', 'Input/output error')
|
||||||
|
self.assert_qmp(event, 'data/len', self.image_len)
|
||||||
|
completed = True
|
||||||
|
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
|
def test_ignore_write(self):
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
|
||||||
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
mode='existing', target=self.target_img,
|
||||||
|
on_target_error='ignore')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
event = self.vm.get_qmp_event(wait=True)
|
||||||
|
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/operation', 'write')
|
||||||
|
result = self.vm.qmp('query-block-jobs')
|
||||||
|
self.assert_qmp(result, 'return[0]/paused', False)
|
||||||
|
self.complete_and_wait()
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
|
def test_stop_write(self):
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
|
||||||
|
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
|
||||||
|
mode='existing', target=self.target_img,
|
||||||
|
on_target_error='stop')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
error = False
|
||||||
|
ready = False
|
||||||
|
while not ready:
|
||||||
|
for event in self.vm.get_qmp_events(wait=True):
|
||||||
|
if event['event'] == 'BLOCK_JOB_ERROR':
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
self.assert_qmp(event, 'data/operation', 'write')
|
||||||
|
|
||||||
|
result = self.vm.qmp('query-block-jobs')
|
||||||
|
self.assert_qmp(result, 'return[0]/paused', True)
|
||||||
|
self.assert_qmp(result, 'return[0]/io-status', 'failed')
|
||||||
|
|
||||||
|
result = self.vm.qmp('block-job-resume', device='drive0')
|
||||||
|
self.assert_qmp(result, 'return', {})
|
||||||
|
|
||||||
|
result = self.vm.qmp('query-block-jobs')
|
||||||
|
self.assert_qmp(result, 'return[0]/paused', False)
|
||||||
|
self.assert_qmp(result, 'return[0]/io-status', 'ok')
|
||||||
|
error = True
|
||||||
|
elif event['event'] == 'BLOCK_JOB_READY':
|
||||||
|
self.assertTrue(error, 'job completed unexpectedly')
|
||||||
|
self.assert_qmp(event, 'data/device', 'drive0')
|
||||||
|
ready = True
|
||||||
|
|
||||||
|
self.complete_and_wait(wait_ready=False)
|
||||||
|
self.assert_no_active_mirrors()
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
class TestSetSpeed(ImageMirroringTestCase):
|
class TestSetSpeed(ImageMirroringTestCase):
|
||||||
image_len = 80 * 1024 * 1024 # MB
|
image_len = 80 * 1024 * 1024 # MB
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
............
|
..................
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Ran 12 tests
|
Ran 18 tests
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
@ -106,6 +106,10 @@ class VM(object):
|
|||||||
|
|
||||||
return self._qmp.cmd(cmd, args=qmp_args)
|
return self._qmp.cmd(cmd, args=qmp_args)
|
||||||
|
|
||||||
|
def get_qmp_event(self, wait=False):
|
||||||
|
'''Poll for one queued QMP events and return it'''
|
||||||
|
return self._qmp.pull_event(wait=wait)
|
||||||
|
|
||||||
def get_qmp_events(self, wait=False):
|
def get_qmp_events(self, wait=False):
|
||||||
'''Poll for queued QMP events and return a list of dicts'''
|
'''Poll for queued QMP events and return a list of dicts'''
|
||||||
events = self._qmp.get_events(wait=wait)
|
events = self._qmp.get_events(wait=wait)
|
||||||
|
Loading…
Reference in New Issue
Block a user