From patchwork Fri Jun 6 15:20:28 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Beno=C3=AEt_Canet?= X-Patchwork-Id: 356894 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 818461400E4 for ; Sat, 7 Jun 2014 01:22:50 +1000 (EST) Received: from localhost ([::1]:47746 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wsvyi-0005WX-Er for incoming@patchwork.ozlabs.org; Fri, 06 Jun 2014 11:22:48 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:32913) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wsvwj-00026M-2d for qemu-devel@nongnu.org; Fri, 06 Jun 2014 11:20:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WsvwX-0001Yb-45 for qemu-devel@nongnu.org; Fri, 06 Jun 2014 11:20:44 -0400 Received: from lputeaux-656-01-25-125.w80-12.abo.wanadoo.fr ([80.12.84.125]:46366 helo=paradis.irqsave.net) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WsvwW-0001YA-Nx for qemu-devel@nongnu.org; Fri, 06 Jun 2014 11:20:32 -0400 Received: from paradis.irqsave.net (unknown [192.168.77.254]) by paradis.irqsave.net (Postfix) with ESMTP id 08EA0A8151; Fri, 6 Jun 2014 17:20:31 +0200 (CEST) From: =?UTF-8?q?Beno=C3=AEt=20Canet?= To: qemu-devel@nongnu.org Date: Fri, 6 Jun 2014 17:20:28 +0200 Message-Id: <1402068028-18089-4-git-send-email-benoit.canet@irqsave.net> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1402068028-18089-1-git-send-email-benoit.canet@irqsave.net> References: <1402068028-18089-1-git-send-email-benoit.canet@irqsave.net> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] X-Received-From: 80.12.84.125 Cc: kwolf@redhat.com, =?UTF-8?q?Beno=C3=AEt=20Canet?= , Benoit Canet , mreitz@redhat.com, stefanha@redhat.com Subject: [Qemu-devel] [PATCH v6 3/3] qemu-iotests: Add 096 new test for drive-mirror-replace. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Tests for drive-mirror-replace whose purpose is to enable quorum file mirroring and replacement after failure. Signed-off-by: Benoit Canet --- tests/qemu-iotests/041 | 34 +------ tests/qemu-iotests/096 | 222 ++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/096.out | 5 + tests/qemu-iotests/group | 1 + tests/qemu-iotests/iotests.py | 33 +++++++ 5 files changed, 262 insertions(+), 33 deletions(-) create mode 100755 tests/qemu-iotests/096 create mode 100644 tests/qemu-iotests/096.out diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index ec470b2..10535a6 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -21,45 +21,13 @@ import time import os import iotests -from iotests import qemu_img, qemu_io +from iotests import qemu_img, qemu_io, ImageMirroringTestCase backing_img = os.path.join(iotests.test_dir, 'backing.img') 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') -class ImageMirroringTestCase(iotests.QMPTestCase): - '''Abstract base class for image mirroring test cases''' - - def wait_ready(self, drive='drive0'): - '''Wait until a block job BLOCK_JOB_READY event''' - ready = False - while not ready: - for event in self.vm.get_qmp_events(wait=True): - if event['event'] == 'BLOCK_JOB_READY': - self.assert_qmp(event, 'data/type', 'mirror') - self.assert_qmp(event, 'data/device', drive) - ready = True - - def wait_ready_and_cancel(self, drive='drive0'): - self.wait_ready(drive) - event = self.cancel_and_wait() - self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') - self.assert_qmp(event, 'data/type', 'mirror') - self.assert_qmp(event, 'data/offset', self.image_len) - self.assert_qmp(event, 'data/len', self.image_len) - - 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() - - result = self.vm.qmp('block-job-complete', device=drive) - self.assert_qmp(result, 'return', {}) - - event = self.wait_until_completed() - self.assert_qmp(event, 'data/type', 'mirror') - class TestSingleDrive(ImageMirroringTestCase): image_len = 1 * 1024 * 1024 # MB diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096 new file mode 100755 index 0000000..a5e92d8 --- /dev/null +++ b/tests/qemu-iotests/096 @@ -0,0 +1,222 @@ +#!/usr/bin/env python +# +# Tests for Quorum image replacement +# +# Copyright (C) 2014 Nodalink, EURL. +# +# based on 041 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import copy +import subprocess +import time +import os +import iotests +from iotests import qemu_img, qemu_img_args +from iotests import ImageMirroringTestCase + +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_backup_img = os.path.join(iotests.test_dir, 'quorum_backup.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 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(self.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', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready(drive='quorum0') + + result = self.vm.qmp('drive-mirror-replace', device='quorum0', + target_reference='img1', new_node_name='img11') + self.assert_qmp(result, 'return', {}) + + event = self.wait_until_completed(drive='quorum0') + self.assert_qmp(event, 'data/type', 'mirror') + + 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_device_not_active(self): + result = self.vm.qmp('drive-mirror-replace', device='quorum12', + target_reference='img1', new_node_name='img11') + self.assert_qmp(result, 'error/class', 'DeviceNotActive') + self.vm.shutdown() + + def test_sync_no_full(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='none', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready(drive='quorum0') + + result = self.vm.qmp('drive-mirror-replace', device='quorum0', + target_reference='img1', new_node_name='img11') + self.assert_qmp(result, 'error/class', 'GenericError') + + event = self.cancel_and_wait(drive='quorum0', resume=True) + self.assert_qmp(event, 'data/type', 'mirror') + + self.vm.shutdown() + + def test_sync_empty_reference(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready(drive='quorum0') + + result = self.vm.qmp('drive-mirror-replace', device='quorum0', + target_reference='', new_node_name='img11') + self.assert_qmp(result, 'error/class', 'GenericError') + + event = self.cancel_and_wait(drive='quorum0', resume=True) + self.assert_qmp(event, 'data/type', 'mirror') + + self.vm.shutdown() + + def test_sync_no_reference(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready(drive='quorum0') + + result = self.vm.qmp('drive-mirror-replace', device='quorum0', + new_node_name='img11') + self.assert_qmp(result, 'error/class', 'GenericError') + + event = self.cancel_and_wait(drive='quorum0', resume=True) + self.assert_qmp(event, 'data/type', 'mirror') + + self.vm.shutdown() + + def test_sync_no_node_name(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready(drive='quorum0') + + result = self.vm.qmp('drive-mirror-replace', device='quorum0', + target_reference='img1') + self.assert_qmp(result, 'error/class', 'GenericError') + + event = self.cancel_and_wait(drive='quorum0', resume=True) + self.assert_qmp(event, 'data/type', 'mirror') + + self.vm.shutdown() + + def test_sync_empty_node_name(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready(drive='quorum0') + + result = self.vm.qmp('drive-mirror-replace', device='quorum0', + target_reference='img1', new_node_name='') + self.assert_qmp(result, 'error/class', 'GenericError') + + event = self.cancel_and_wait(drive='quorum0', resume=True) + self.assert_qmp(event, 'data/type', 'mirror') + + self.vm.shutdown() + + def test_source_and_dest_equal(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('drive-mirror', device='quorum0', sync='full', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + self.wait_ready(drive='quorum0') + + result = self.vm.qmp('drive-mirror-replace', device='quorum0', + target_reference='quorum0', new_node_name='img11') + self.assert_qmp(result, 'error/class', 'GenericError') + + event = self.cancel_and_wait(drive='quorum0', resume=True) + self.assert_qmp(event, 'data/type', 'mirror') + + self.vm.shutdown() + +if __name__ == '__main__': + args = copy.deepcopy(qemu_img_args) + args.append("--help") + p1 = subprocess.Popen(args, stdout=subprocess.PIPE) + p2 = subprocess.Popen(["grep", "quorum"], stdin=p1.stdout, stdout=subprocess.PIPE) + p1.stdout.close() # Allow p1 to receive a SIGsubprocess.PIPE if p2 exits. + has_quorum = len(p2.communicate()[0]) != 0 + if has_quorum: + iotests.main(supported_fmts=['qcow2', 'qed']) + else: + iotests.notrun("no builtin quorum support") diff --git a/tests/qemu-iotests/096.out b/tests/qemu-iotests/096.out new file mode 100644 index 0000000..594c16f --- /dev/null +++ b/tests/qemu-iotests/096.out @@ -0,0 +1,5 @@ +........ +---------------------------------------------------------------------- +Ran 8 tests + +OK diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0f07440..86419fb 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -99,3 +99,4 @@ 090 rw auto quick 091 rw auto 092 rw auto quick +096 rw auto diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index f6c437c..e2c45b3 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -273,6 +273,39 @@ class QMPTestCase(unittest.TestCase): self.assert_no_active_block_jobs() return event +# made common here for 041 and 096 +class ImageMirroringTestCase(QMPTestCase): + '''Abstract base class for image mirroring test cases''' + + def wait_ready(self, drive='drive0'): + '''Wait until a block job BLOCK_JOB_READY event''' + ready = False + while not ready: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_READY': + self.assert_qmp(event, 'data/type', 'mirror') + self.assert_qmp(event, 'data/device', drive) + ready = True + + def wait_ready_and_cancel(self, drive='drive0'): + 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) + self.assert_qmp(event, 'data/len', self.image_len) + + 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(drive=drive) + + result = self.vm.qmp('block-job-complete', device=drive) + self.assert_qmp(result, 'return', {}) + + event = self.wait_until_completed(drive=drive) + self.assert_qmp(event, 'data/type', 'mirror') + def notrun(reason): '''Skip this test suite''' # Each test in qemu-iotests has a number ("seq")