Message ID | 1402493053-13787-5-git-send-email-benoit.canet@irqsave.net |
---|---|
State | New |
Headers | show |
On 11.06.2014 15:24, Benoît Canet wrote: > The to-replace-node-name is designed to allow repairing of broken Quorum file. "a broken Quorum file" or "broken Quorum files". > This patch introduce a new class TestRepairQuorum testing that the feature *introduces > works. > Some further work will be done on QEMU to improve the robutness of the tests. *robustness > Signed-off-by: Benoit Canet <benoit@irqsave.net> > --- > tests/qemu-iotests/041 | 196 ++++++++++++++++++++++++++++++++++++++++++++- > tests/qemu-iotests/041.out | 4 +- > 2 files changed, 194 insertions(+), 6 deletions(-) > > diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 > index ec470b2..c0480e1 100755 > --- a/tests/qemu-iotests/041 > +++ b/tests/qemu-iotests/041 > @@ -28,6 +28,12 @@ target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') > test_img = os.path.join(iotests.test_dir, 'test.img') > target_img = os.path.join(iotests.test_dir, 'target.img') > > +quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img') > +quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img') > +quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') > +quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') > +quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') > + > class ImageMirroringTestCase(iotests.QMPTestCase): > '''Abstract base class for image mirroring test cases''' > > @@ -42,8 +48,8 @@ class ImageMirroringTestCase(iotests.QMPTestCase): > ready = True > > def wait_ready_and_cancel(self, drive='drive0'): > - self.wait_ready(drive) > - event = self.cancel_and_wait() > + self.wait_ready(drive=drive) > + event = self.cancel_and_wait(drive=drive) My Python is very bad (if existent at all), but is this "drive=drive" really necessary? Wouldn't simply "drive" suffice? > self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') > self.assert_qmp(event, 'data/type', 'mirror') > self.assert_qmp(event, 'data/offset', self.image_len) > @@ -52,12 +58,12 @@ class ImageMirroringTestCase(iotests.QMPTestCase): > def complete_and_wait(self, drive='drive0', wait_ready=True): > '''Complete a block job and wait for it to finish''' > if wait_ready: > - self.wait_ready() > + self.wait_ready(drive=drive) > > result = self.vm.qmp('block-job-complete', device=drive) > self.assert_qmp(result, 'return', {}) > > - event = self.wait_until_completed() > + event = self.wait_until_completed(drive=drive) > self.assert_qmp(event, 'data/type', 'mirror') > > class TestSingleDrive(ImageMirroringTestCase): > @@ -718,5 +724,187 @@ class TestUnbackedSource(ImageMirroringTestCase): > self.complete_and_wait() > self.assert_no_active_block_jobs() > > +class TestRepairQuorum(ImageMirroringTestCase): > + """ This class test quorum file repair using drive-mirror. > + It's mostly a fork of TestSingleDrive """ > + image_len = 1 * 1024 * 1024 # MB > + IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] > + > + def setUp(self): > + self.vm = iotests.VM() > + > + # Add each individual quorum images > + for i in self.IMAGES: > + qemu_img('create', '-f', iotests.imgfmt, i, > + str(TestSingleDrive.image_len)) > + # Assign a node name to each quorum image in order to manipulate > + # them > + opts = "node-name=img%i" % self.IMAGES.index(i) > + self.vm = self.vm.add_drive(i, opts) Just an idea: As you are constantly swapping out img1, why don't you write some data to img2 and img3 and different data to img1 to test it all how it's supposed to work? > + > + self.vm.launch() > + > + #assemble the quorum block device from the individual files > + args = { "options" : { "driver": "quorum", "id": "quorum0", > + "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } } > + result = self.vm.qmp("blockdev-add", **args) > + self.assert_qmp(result, 'return', {}) > + > + > + def tearDown(self): > + self.vm.shutdown() > + for i in self.IMAGES + [ quorum_repair_img ]: > + # Do a try/except because the test may have deleted some images > + try: > + os.remove(i) > + except OSError: > + pass > + > + def test_complete(self): > + self.assert_no_active_block_jobs() > + > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name="repair0", > + replaces="img1", > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'return', {}) > + > + self.complete_and_wait(drive="quorum0") > + result = self.vm.qmp('query-named-block-nodes') > + self.assert_qmp(result, 'return[0]/file', quorum_repair_img) Hm, why do you know this is at index 0? > + # TODO: a better test requiring some QEMU infrastructure will be added > + # to check that this file is really driven by quorum > + self.vm.shutdown() > + self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), > + 'target image does not match source after mirroring') > + > + def test_cancel(self): > + self.assert_no_active_block_jobs() > + > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name="repair0", > + replaces="img1", > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'return', {}) > + > + self.cancel_and_wait(drive="quorum0", force=True) > + # here we check that the last registered quorum file has not been > + # swapped out and unref > + result = self.vm.qmp('query-named-block-nodes') > + self.assert_qmp(result, 'return[0]/file', quorum_img3) Why would img3 be affected at all? Aren't you trying to replace img1? (Also, again, why index 0?) > + self.vm.shutdown() > + > + def test_cancel_after_ready(self): > + self.assert_no_active_block_jobs() > + > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name="repair0", > + replaces="img1", > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'return', {}) > + > + self.wait_ready_and_cancel(drive="quorum0") > + result = self.vm.qmp('query-named-block-nodes') > + # here we check that the last registered quorum file has not been > + # swapped out and unref > + self.assert_qmp(result, 'return[0]/file', quorum_img3) Same here. > + self.vm.shutdown() > + self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), > + 'target image does not match source after mirroring') > + > + def test_pause(self): > + self.assert_no_active_block_jobs() > + > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name="repair0", > + replaces="img1", > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'return', {}) > + > + result = self.vm.qmp('block-job-pause', device='quorum0') > + self.assert_qmp(result, 'return', {}) > + > + time.sleep(1) > + result = self.vm.qmp('query-block-jobs') > + offset = self.dictpath(result, 'return[0]/offset') > + > + time.sleep(1) > + result = self.vm.qmp('query-block-jobs') > + self.assert_qmp(result, 'return[0]/offset', offset) > + > + result = self.vm.qmp('block-job-resume', device='quorum0') > + self.assert_qmp(result, 'return', {}) > + > + self.complete_and_wait(drive="quorum0") > + self.vm.shutdown() > + self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), > + 'target image does not match source after mirroring') > + > + def test_medium_not_found(self): > + result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full', I don't know whether this device is supposed to exist forever, but if it disappears from default configuration one day, TestSingleDrive will break as well, so it's probably fine. Max > + node_name='repair0', > + replaces='img1', > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'error/class', 'GenericError') > + > + def test_image_not_found(self): > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name='repair0', > + replaces='img1', > + mode='existing', > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'error/class', 'GenericError') > + > + def test_device_not_found(self): > + result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full', > + node_name='repair0', > + replaces='img1', > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'error/class', 'DeviceNotFound') > + > + def test_wrong_sync_mode(self): > + result = self.vm.qmp('drive-mirror', device='quorum0', > + node_name='repair0', > + replaces='img1', > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'error/class', 'GenericError') > + > + def test_no_node_name(self): > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + replaces='img1', > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'error/class', 'GenericError') > + > + def test_unexistant_replaces(self): > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name='repair0', > + replaces='img77', > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'error/class', 'GenericError') > + > + def test_after_a_quorum_snapshot(self): > + result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', > + snapshot_file=quorum_snapshot_file, > + snapshot_node_name="snap1"); > + > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name='repair0', > + replaces="img1", > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'error/class', 'GenericError') > + > + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > + node_name='repair0', > + replaces="snap1", > + target=quorum_repair_img, format=iotests.imgfmt) > + self.assert_qmp(result, 'return', {}) > + > + self.complete_and_wait(drive="quorum0") > + result = self.vm.qmp('query-named-block-nodes') > + self.assert_qmp(result, 'return[0]/file', quorum_repair_img) > + # TODO: a better test requiring some QEMU infrastructure will be added > + # to check that this file is really driven by quorum > + self.vm.shutdown() > + > if __name__ == '__main__': > iotests.main(supported_fmts=['qcow2', 'qed']) > diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out > index 6d9bee1..73e375a 100644 > --- a/tests/qemu-iotests/041.out > +++ b/tests/qemu-iotests/041.out > @@ -1,5 +1,5 @@ > -........................... > +...................................... > ---------------------------------------------------------------------- > -Ran 27 tests > +Ran 38 tests > > OK
The Saturday 14 Jun 2014 à 02:44:29 (+0200), Max Reitz wrote : > On 11.06.2014 15:24, Benoît Canet wrote: > >The to-replace-node-name is designed to allow repairing of broken Quorum file. > > "a broken Quorum file" or "broken Quorum files". > > >This patch introduce a new class TestRepairQuorum testing that the feature > > *introduces > > >works. > >Some further work will be done on QEMU to improve the robutness of the tests. > > *robustness > > >Signed-off-by: Benoit Canet <benoit@irqsave.net> > >--- > > tests/qemu-iotests/041 | 196 ++++++++++++++++++++++++++++++++++++++++++++- > > tests/qemu-iotests/041.out | 4 +- > > 2 files changed, 194 insertions(+), 6 deletions(-) > > > >diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 > >index ec470b2..c0480e1 100755 > >--- a/tests/qemu-iotests/041 > >+++ b/tests/qemu-iotests/041 > >@@ -28,6 +28,12 @@ target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') > > test_img = os.path.join(iotests.test_dir, 'test.img') > > target_img = os.path.join(iotests.test_dir, 'target.img') > >+quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img') > >+quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img') > >+quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') > >+quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') > >+quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') > >+ > > class ImageMirroringTestCase(iotests.QMPTestCase): > > '''Abstract base class for image mirroring test cases''' > >@@ -42,8 +48,8 @@ class ImageMirroringTestCase(iotests.QMPTestCase): > > ready = True > > def wait_ready_and_cancel(self, drive='drive0'): > >- self.wait_ready(drive) > >- event = self.cancel_and_wait() > >+ self.wait_ready(drive=drive) > >+ event = self.cancel_and_wait(drive=drive) > > My Python is very bad (if existent at all), but is this > "drive=drive" really necessary? Wouldn't simply "drive" suffice? drive=drive means pass the drive variable in the parameter of the callee named drive. cancel_and_wait have multiple named parameters I need to pass the variable into the right one. > > > self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') > > self.assert_qmp(event, 'data/type', 'mirror') > > self.assert_qmp(event, 'data/offset', self.image_len) > >@@ -52,12 +58,12 @@ class ImageMirroringTestCase(iotests.QMPTestCase): > > def complete_and_wait(self, drive='drive0', wait_ready=True): > > '''Complete a block job and wait for it to finish''' > > if wait_ready: > >- self.wait_ready() > >+ self.wait_ready(drive=drive) > > result = self.vm.qmp('block-job-complete', device=drive) > > self.assert_qmp(result, 'return', {}) > >- event = self.wait_until_completed() > >+ event = self.wait_until_completed(drive=drive) > > self.assert_qmp(event, 'data/type', 'mirror') > > class TestSingleDrive(ImageMirroringTestCase): > >@@ -718,5 +724,187 @@ class TestUnbackedSource(ImageMirroringTestCase): > > self.complete_and_wait() > > self.assert_no_active_block_jobs() > >+class TestRepairQuorum(ImageMirroringTestCase): > >+ """ This class test quorum file repair using drive-mirror. > >+ It's mostly a fork of TestSingleDrive """ > >+ image_len = 1 * 1024 * 1024 # MB > >+ IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] > >+ > >+ def setUp(self): > >+ self.vm = iotests.VM() > >+ > >+ # Add each individual quorum images > >+ for i in self.IMAGES: > >+ qemu_img('create', '-f', iotests.imgfmt, i, > >+ str(TestSingleDrive.image_len)) > >+ # Assign a node name to each quorum image in order to manipulate > >+ # them > >+ opts = "node-name=img%i" % self.IMAGES.index(i) > >+ self.vm = self.vm.add_drive(i, opts) > > Just an idea: As you are constantly swapping out img1, why don't you > write some data to img2 and img3 and different data to img1 to test > it all how it's supposed to work? Hmm we have another test for quorum file corruption repairing. This one is for major failure like the lost of a filer containing a particular quorum file. > > >+ > >+ self.vm.launch() > >+ > >+ #assemble the quorum block device from the individual files > >+ args = { "options" : { "driver": "quorum", "id": "quorum0", > >+ "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } } > >+ result = self.vm.qmp("blockdev-add", **args) > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ > >+ def tearDown(self): > >+ self.vm.shutdown() > >+ for i in self.IMAGES + [ quorum_repair_img ]: > >+ # Do a try/except because the test may have deleted some images > >+ try: > >+ os.remove(i) > >+ except OSError: > >+ pass > >+ > >+ def test_complete(self): > >+ self.assert_no_active_block_jobs() > >+ > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name="repair0", > >+ replaces="img1", > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ self.complete_and_wait(drive="quorum0") > >+ result = self.vm.qmp('query-named-block-nodes') > >+ self.assert_qmp(result, 'return[0]/file', quorum_repair_img) > > Hm, why do you know this is at index 0? Because I ran the comand manually and it's predictible. It's all linked list insertion and traversal. > > >+ # TODO: a better test requiring some QEMU infrastructure will be added > >+ # to check that this file is really driven by quorum > >+ self.vm.shutdown() > >+ self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), > >+ 'target image does not match source after mirroring') > >+ > >+ def test_cancel(self): > >+ self.assert_no_active_block_jobs() > >+ > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name="repair0", > >+ replaces="img1", > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ self.cancel_and_wait(drive="quorum0", force=True) > >+ # here we check that the last registered quorum file has not been > >+ # swapped out and unref > >+ result = self.vm.qmp('query-named-block-nodes') > >+ self.assert_qmp(result, 'return[0]/file', quorum_img3) > > Why would img3 be affected at all? Aren't you trying to replace > img1? (Also, again, why index 0?) It means quorum_img3 should be at position 0 in the linked list: the last inserted quorum file since we canceled the drive-mirror. > > >+ self.vm.shutdown() > >+ > >+ def test_cancel_after_ready(self): > >+ self.assert_no_active_block_jobs() > >+ > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name="repair0", > >+ replaces="img1", > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ self.wait_ready_and_cancel(drive="quorum0") > >+ result = self.vm.qmp('query-named-block-nodes') > >+ # here we check that the last registered quorum file has not been > >+ # swapped out and unref > >+ self.assert_qmp(result, 'return[0]/file', quorum_img3) > > Same here. same > > >+ self.vm.shutdown() > >+ self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), > >+ 'target image does not match source after mirroring') > >+ > >+ def test_pause(self): > >+ self.assert_no_active_block_jobs() > >+ > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name="repair0", > >+ replaces="img1", > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ result = self.vm.qmp('block-job-pause', device='quorum0') > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ time.sleep(1) > >+ result = self.vm.qmp('query-block-jobs') > >+ offset = self.dictpath(result, 'return[0]/offset') > >+ > >+ time.sleep(1) > >+ result = self.vm.qmp('query-block-jobs') > >+ self.assert_qmp(result, 'return[0]/offset', offset) > >+ > >+ result = self.vm.qmp('block-job-resume', device='quorum0') > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ self.complete_and_wait(drive="quorum0") > >+ self.vm.shutdown() > >+ self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), > >+ 'target image does not match source after mirroring') > >+ > >+ def test_medium_not_found(self): > >+ result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full', > > I don't know whether this device is supposed to exist forever, but > if it disappears from default configuration one day, TestSingleDrive > will break as well, so it's probably fine. > > Max > > >+ node_name='repair0', > >+ replaces='img1', > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'error/class', 'GenericError') > >+ > >+ def test_image_not_found(self): > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name='repair0', > >+ replaces='img1', > >+ mode='existing', > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'error/class', 'GenericError') > >+ > >+ def test_device_not_found(self): > >+ result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full', > >+ node_name='repair0', > >+ replaces='img1', > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'error/class', 'DeviceNotFound') > >+ > >+ def test_wrong_sync_mode(self): > >+ result = self.vm.qmp('drive-mirror', device='quorum0', > >+ node_name='repair0', > >+ replaces='img1', > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'error/class', 'GenericError') > >+ > >+ def test_no_node_name(self): > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ replaces='img1', > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'error/class', 'GenericError') > >+ > >+ def test_unexistant_replaces(self): > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name='repair0', > >+ replaces='img77', > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'error/class', 'GenericError') > >+ > >+ def test_after_a_quorum_snapshot(self): > >+ result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', > >+ snapshot_file=quorum_snapshot_file, > >+ snapshot_node_name="snap1"); > >+ > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name='repair0', > >+ replaces="img1", > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'error/class', 'GenericError') > >+ > >+ result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', > >+ node_name='repair0', > >+ replaces="snap1", > >+ target=quorum_repair_img, format=iotests.imgfmt) > >+ self.assert_qmp(result, 'return', {}) > >+ > >+ self.complete_and_wait(drive="quorum0") > >+ result = self.vm.qmp('query-named-block-nodes') > >+ self.assert_qmp(result, 'return[0]/file', quorum_repair_img) > >+ # TODO: a better test requiring some QEMU infrastructure will be added > >+ # to check that this file is really driven by quorum > >+ self.vm.shutdown() > >+ > > if __name__ == '__main__': > > iotests.main(supported_fmts=['qcow2', 'qed']) > >diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out > >index 6d9bee1..73e375a 100644 > >--- a/tests/qemu-iotests/041.out > >+++ b/tests/qemu-iotests/041.out > >@@ -1,5 +1,5 @@ > >-........................... > >+...................................... > > ---------------------------------------------------------------------- > >-Ran 27 tests > >+Ran 38 tests > > OK >
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index ec470b2..c0480e1 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -28,6 +28,12 @@ target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') test_img = os.path.join(iotests.test_dir, 'test.img') target_img = os.path.join(iotests.test_dir, 'target.img') +quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img') +quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img') +quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') +quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') +quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') + class ImageMirroringTestCase(iotests.QMPTestCase): '''Abstract base class for image mirroring test cases''' @@ -42,8 +48,8 @@ class ImageMirroringTestCase(iotests.QMPTestCase): ready = True def wait_ready_and_cancel(self, drive='drive0'): - self.wait_ready(drive) - event = self.cancel_and_wait() + self.wait_ready(drive=drive) + event = self.cancel_and_wait(drive=drive) self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') self.assert_qmp(event, 'data/type', 'mirror') self.assert_qmp(event, 'data/offset', self.image_len) @@ -52,12 +58,12 @@ class ImageMirroringTestCase(iotests.QMPTestCase): def complete_and_wait(self, drive='drive0', wait_ready=True): '''Complete a block job and wait for it to finish''' if wait_ready: - self.wait_ready() + self.wait_ready(drive=drive) result = self.vm.qmp('block-job-complete', device=drive) self.assert_qmp(result, 'return', {}) - event = self.wait_until_completed() + event = self.wait_until_completed(drive=drive) self.assert_qmp(event, 'data/type', 'mirror') class TestSingleDrive(ImageMirroringTestCase): @@ -718,5 +724,187 @@ class TestUnbackedSource(ImageMirroringTestCase): self.complete_and_wait() self.assert_no_active_block_jobs() +class TestRepairQuorum(ImageMirroringTestCase): + """ This class test quorum file repair using drive-mirror. + It's mostly a fork of TestSingleDrive """ + image_len = 1 * 1024 * 1024 # MB + IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] + + def setUp(self): + self.vm = iotests.VM() + + # Add each individual quorum images + for i in self.IMAGES: + qemu_img('create', '-f', iotests.imgfmt, i, + str(TestSingleDrive.image_len)) + # Assign a node name to each quorum image in order to manipulate + # them + opts = "node-name=img%i" % self.IMAGES.index(i) + self.vm = self.vm.add_drive(i, opts) + + self.vm.launch() + + #assemble the quorum block device from the individual files + args = { "options" : { "driver": "quorum", "id": "quorum0", + "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } } + result = self.vm.qmp("blockdev-add", **args) + self.assert_qmp(result, 'return', {}) + + + def tearDown(self): + self.vm.shutdown() + for i in self.IMAGES + [ quorum_repair_img ]: + # Do a try/except because the test may have deleted some images + try: + os.remove(i) + except OSError: + pass + + def test_complete(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name="repair0", + replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait(drive="quorum0") + result = self.vm.qmp('query-named-block-nodes') + self.assert_qmp(result, 'return[0]/file', quorum_repair_img) + # TODO: a better test requiring some QEMU infrastructure will be added + # to check that this file is really driven by quorum + self.vm.shutdown() + self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), + 'target image does not match source after mirroring') + + def test_cancel(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name="repair0", + replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.cancel_and_wait(drive="quorum0", force=True) + # here we check that the last registered quorum file has not been + # swapped out and unref + result = self.vm.qmp('query-named-block-nodes') + self.assert_qmp(result, 'return[0]/file', quorum_img3) + self.vm.shutdown() + + def test_cancel_after_ready(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name="repair0", + replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready_and_cancel(drive="quorum0") + result = self.vm.qmp('query-named-block-nodes') + # here we check that the last registered quorum file has not been + # swapped out and unref + self.assert_qmp(result, 'return[0]/file', quorum_img3) + self.vm.shutdown() + self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), + 'target image does not match source after mirroring') + + def test_pause(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name="repair0", + replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('block-job-pause', device='quorum0') + self.assert_qmp(result, 'return', {}) + + time.sleep(1) + result = self.vm.qmp('query-block-jobs') + offset = self.dictpath(result, 'return[0]/offset') + + time.sleep(1) + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/offset', offset) + + result = self.vm.qmp('block-job-resume', device='quorum0') + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait(drive="quorum0") + self.vm.shutdown() + self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), + 'target image does not match source after mirroring') + + def test_medium_not_found(self): + result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full', + node_name='repair0', + replaces='img1', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_image_not_found(self): + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name='repair0', + replaces='img1', + mode='existing', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_device_not_found(self): + result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full', + node_name='repair0', + replaces='img1', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + + def test_wrong_sync_mode(self): + result = self.vm.qmp('drive-mirror', device='quorum0', + node_name='repair0', + replaces='img1', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_no_node_name(self): + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + replaces='img1', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_unexistant_replaces(self): + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name='repair0', + replaces='img77', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/class', 'GenericError') + + def test_after_a_quorum_snapshot(self): + result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', + snapshot_file=quorum_snapshot_file, + snapshot_node_name="snap1"); + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name='repair0', + replaces="img1", + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/class', 'GenericError') + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + node_name='repair0', + replaces="snap1", + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait(drive="quorum0") + result = self.vm.qmp('query-named-block-nodes') + self.assert_qmp(result, 'return[0]/file', quorum_repair_img) + # TODO: a better test requiring some QEMU infrastructure will be added + # to check that this file is really driven by quorum + self.vm.shutdown() + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed']) diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index 6d9bee1..73e375a 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -........................... +...................................... ---------------------------------------------------------------------- -Ran 27 tests +Ran 38 tests OK
The to-replace-node-name is designed to allow repairing of broken Quorum file. This patch introduce a new class TestRepairQuorum testing that the feature works. Some further work will be done on QEMU to improve the robutness of the tests. Signed-off-by: Benoit Canet <benoit@irqsave.net> --- tests/qemu-iotests/041 | 196 ++++++++++++++++++++++++++++++++++++++++++++- tests/qemu-iotests/041.out | 4 +- 2 files changed, 194 insertions(+), 6 deletions(-)