[17/47] qemu-iotests: add tests for streaming error handling

Submitted by Paolo Bonzini on July 24, 2012, 11:03 a.m.

Details

Message ID 1343127865-16608-18-git-send-email-pbonzini@redhat.com
State New
Headers show

Commit Message

Paolo Bonzini July 24, 2012, 11:03 a.m.
Add a test for each of report/ignore/stop.  The tests use blkdebug
to generate an error in the middle of a script.  The error is
recoverable (once = "on") so that we can test resuming a job after
stopping for an error.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 tests/qemu-iotests/030        |  138 +++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/group      |    2 +-
 tests/qemu-iotests/iotests.py |    7 +++
 3 files changed, 146 insertions(+), 1 deletion(-)

Comments

Kevin Wolf Aug. 1, 2012, 10:43 a.m.
Am 24.07.2012 13:03, schrieb Paolo Bonzini:
> Add a test for each of report/ignore/stop.  The tests use blkdebug
> to generate an error in the middle of a script.  The error is
> recoverable (once = "on") so that we can test resuming a job after
> stopping for an error.
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  tests/qemu-iotests/030        |  138 +++++++++++++++++++++++++++++++++++++++++
>  tests/qemu-iotests/group      |    2 +-
>  tests/qemu-iotests/iotests.py |    7 +++
>  3 files changed, 146 insertions(+), 1 deletion(-)
> 
> diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
> index 0163945..c65bf5e 100755
> --- a/tests/qemu-iotests/030
> +++ b/tests/qemu-iotests/030
> @@ -163,6 +163,144 @@ class TestSingleDrive(ImageStreamingTestCase):
>          result = self.vm.qmp('block-stream', device='nonexistent')
>          self.assert_qmp(result, 'error/class', 'DeviceNotFound')
>  
> +class TestErrors(ImageStreamingTestCase):
> +    image_len = 2 * 1024 * 1024 # MB
> +
> +    # this should match STREAM_BUFFER_SIZE/512 in block/stream.c
> +    STREAM_BUFFER_SIZE = 512 * 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.STREAM_BUFFER_SIZE / 512, event, event))
> +        file.close()
> +
> +    def setUp(self):
> +        self.blkdebug_file = backing_img + ".blkdebug"
> +        self.create_image(backing_img, TestErrors.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(self):
> +        self.assert_no_active_streams()
> +
> +        result = self.vm.qmp('block-stream', device='drive0')
> +        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')

data/action should be asserted as well (same in the other tests).

What about adding an enospc test as well, once with EIO and once with
ENOSPC?

Kevin
Paolo Bonzini Aug. 1, 2012, 11:09 a.m.
Il 01/08/2012 12:43, Kevin Wolf ha scritto:
>> > +
>> > +    def test_report(self):
>> > +        self.assert_no_active_streams()
>> > +
>> > +        result = self.vm.qmp('block-stream', device='drive0')
>> > +        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')
> data/action should be asserted as well (same in the other tests).
> 
> What about adding an enospc test as well, once with EIO and once with
> ENOSPC?

Ok.

Paolo

Patch hide | download patch | download mbox

diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index 0163945..c65bf5e 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -163,6 +163,144 @@  class TestSingleDrive(ImageStreamingTestCase):
         result = self.vm.qmp('block-stream', device='nonexistent')
         self.assert_qmp(result, 'error/class', 'DeviceNotFound')
 
+class TestErrors(ImageStreamingTestCase):
+    image_len = 2 * 1024 * 1024 # MB
+
+    # this should match STREAM_BUFFER_SIZE/512 in block/stream.c
+    STREAM_BUFFER_SIZE = 512 * 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.STREAM_BUFFER_SIZE / 512, event, event))
+        file.close()
+
+    def setUp(self):
+        self.blkdebug_file = backing_img + ".blkdebug"
+        self.create_image(backing_img, TestErrors.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(self):
+        self.assert_no_active_streams()
+
+        result = self.vm.qmp('block-stream', device='drive0')
+        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_COMPLETED':
+                    self.assertTrue(error, 'job completed unexpectedly')
+                    self.assert_qmp(event, 'data/type', 'stream')
+                    self.assert_qmp(event, 'data/device', 'drive0')
+                    self.assert_qmp(event, 'data/error', 'Input/output error')
+                    self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
+                    self.assert_qmp(event, 'data/len', self.image_len)
+                    completed = True
+
+        self.assert_no_active_streams()
+        self.vm.shutdown()
+
+    def test_ignore(self):
+        self.assert_no_active_streams()
+
+        result = self.vm.qmp('block-stream', device='drive0', on_error='ignore')
+        self.assert_qmp(result, 'return', {})
+
+        error = False
+        completed = 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')
+                    result = self.vm.qmp('query-block-jobs')
+                    self.assert_qmp(result, 'return[0]/paused', False)
+                    error = True
+                elif event['event'] == 'BLOCK_JOB_COMPLETED':
+                    self.assertTrue(error, 'job completed unexpectedly')
+                    self.assert_qmp(event, 'data/type', 'stream')
+                    self.assert_qmp(event, 'data/device', 'drive0')
+                    self.assert_qmp(event, 'data/error', 'Input/output error')
+                    self.assert_qmp(event, 'data/offset', self.image_len)
+                    self.assert_qmp(event, 'data/len', self.image_len)
+                    completed = True
+
+        self.assert_no_active_streams()
+        self.vm.shutdown()
+
+    def test_stop(self):
+        self.assert_no_active_streams()
+
+        result = self.vm.qmp('block-stream', device='drive0', on_error='stop')
+        self.assert_qmp(result, 'return', {})
+
+        error = False
+        completed = 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')
+
+                    result = self.vm.qmp('query-block-jobs')
+                    self.assert_qmp(result, 'return[0]/paused', True)
+                    self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE)
+                    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_COMPLETED':
+                    self.assertTrue(error, 'job completed unexpectedly')
+                    self.assert_qmp(event, 'data/type', 'stream')
+                    self.assert_qmp(event, 'data/device', 'drive0')
+                    self.assert_qmp_absent(event, 'data/error')
+                    self.assert_qmp(event, 'data/offset', self.image_len)
+                    self.assert_qmp(event, 'data/len', self.image_len)
+                    completed = True
+
+        self.assert_no_active_streams()
+        self.vm.shutdown()
+
 class TestStreamStop(ImageStreamingTestCase):
     image_len = 8 * 1024 * 1024 * 1024 # GB
 
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 7a2c92b..a569bc9 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -36,7 +36,7 @@ 
 027 rw auto quick
 028 rw backing auto
 029 rw auto quick
-030 rw auto
+030 rw auto backing
 031 rw auto quick
 032 rw auto
 033 rw auto
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index a94ea75..3c60b2d 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -138,6 +138,13 @@  class QMPTestCase(unittest.TestCase):
                     self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
         return d
 
+    def assert_qmp_absent(self, d, path):
+        try:
+            result = self.dictpath(d, path)
+        except AssertionError:
+            return
+        self.fail('path "%s" has value "%s"' % (path, str(result)))
+
     def assert_qmp(self, d, path, value):
         '''Assert that the value for a specific path in a QMP dict matches'''
         result = self.dictpath(d, path)