From nobody Fri Oct 24 09:58:56 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1519430065713432.1747875419875; Fri, 23 Feb 2018 15:54:25 -0800 (PST) Received: from localhost ([::1]:47858 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1epNAW-0007UJ-Ru for importer@patchew.org; Fri, 23 Feb 2018 18:54:24 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55382) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1epN8O-0005uw-Ov for qemu-devel@nongnu.org; Fri, 23 Feb 2018 18:52:15 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1epN8K-0005vP-2T for qemu-devel@nongnu.org; Fri, 23 Feb 2018 18:52:12 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:33878 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1epN8B-0005aj-Jj; Fri, 23 Feb 2018 18:51:59 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1B94E4078073; Fri, 23 Feb 2018 23:51:56 +0000 (UTC) Received: from probe.bos.redhat.com (dhcp-17-231.bos.redhat.com [10.18.17.231]) by smtp.corp.redhat.com (Postfix) with ESMTP id DD0DD1049475; Fri, 23 Feb 2018 23:51:55 +0000 (UTC) From: John Snow To: qemu-block@nongnu.org Date: Fri, 23 Feb 2018 18:51:41 -0500 Message-Id: <20180223235142.21501-21-jsnow@redhat.com> In-Reply-To: <20180223235142.21501-1-jsnow@redhat.com> References: <20180223235142.21501-1-jsnow@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 23 Feb 2018 23:51:56 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 23 Feb 2018 23:51:56 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'jsnow@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v4 20/21] iotests: test manual job dismissal X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, John Snow , pkrempa@redhat.com, jtc@redhat.com, qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Signed-off-by: John Snow Reviewed-by: Eric Blake --- tests/qemu-iotests/056 | 195 +++++++++++++++++++++++++++++++++++++++++= ++++ tests/qemu-iotests/056.out | 4 +- 2 files changed, 197 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056 index 04f2c3c841..bc21ba9af8 100755 --- a/tests/qemu-iotests/056 +++ b/tests/qemu-iotests/056 @@ -29,6 +29,26 @@ backing_img =3D os.path.join(iotests.test_dir, 'backing.= img') test_img =3D os.path.join(iotests.test_dir, 'test.img') target_img =3D os.path.join(iotests.test_dir, 'target.img') =20 +def img_create(img, fmt=3Diotests.imgfmt, size=3D'64M', **kwargs): + fullname =3D os.path.join(iotests.test_dir, '%s.%s' % (img, fmt)) + optargs =3D [] + for k,v in kwargs.iteritems(): + optargs =3D optargs + ['-o', '%s=3D%s' % (k,v)] + args =3D ['create', '-f', fmt] + optargs + [fullname, size] + iotests.qemu_img(*args) + return fullname + +def try_remove(img): + try: + os.remove(img) + except OSError: + pass + +def io_write_patterns(img, patterns): + for pattern in patterns: + iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img) + + class TestSyncModesNoneAndTop(iotests.QMPTestCase): image_len =3D 64 * 1024 * 1024 # MB =20 @@ -108,5 +128,180 @@ class TestBeforeWriteNotifier(iotests.QMPTestCase): event =3D self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') =20 +class BackupTest(iotests.QMPTestCase): + def setUp(self): + self.vm =3D iotests.VM() + self.test_img =3D img_create('test') + self.dest_img =3D img_create('dest') + self.vm.add_drive(self.test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + try_remove(self.test_img) + try_remove(self.dest_img) + + def hmp_io_writes(self, drive, patterns): + for pattern in patterns: + self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern) + self.vm.hmp_qemu_io(drive, 'flush') + + def qmp_job_pending_wait(self, device): + event =3D self.vm.event_wait(name=3D"BLOCK_JOB_PENDING", + match=3D{'data': {'id': device}}) + self.assertNotEqual(event, None) + res =3D self.vm.qmp("block-job-finalize", id=3Ddevice) + self.assert_qmp(res, 'return', {}) + + def qmp_backup_and_wait(self, cmd=3D'drive-backup', serror=3DNone, + aerror=3DNone, **kwargs): + if not self.qmp_backup(cmd, serror, **kwargs): + return False + if 'manual' in kwargs and kwargs['manual']: + self.qmp_job_pending_wait(kwargs['device']) + return self.qmp_backup_wait(kwargs['device'], aerror) + + def qmp_backup(self, cmd=3D'drive-backup', + error=3DNone, **kwargs): + self.assertTrue('device' in kwargs) + res =3D self.vm.qmp(cmd, **kwargs) + if error: + self.assert_qmp(res, 'error/desc', error) + return False + self.assert_qmp(res, 'return', {}) + return True + + def qmp_backup_wait(self, device, error=3DNone): + event =3D self.vm.event_wait(name=3D"BLOCK_JOB_COMPLETED", + match=3D{'data': {'device': device}}) + self.assertNotEqual(event, None) + try: + failure =3D self.dictpath(event, 'data/error') + except AssertionError: + # Backup succeeded. + self.assert_qmp(event, 'data/offset', event['data']['len']) + return True + else: + # Failure. + self.assert_qmp(event, 'data/error', qerror) + return False + + def test_dismiss_false(self): + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + self.qmp_backup_and_wait(device=3D'drive0', format=3Diotests.imgfm= t, + sync=3D'full', target=3Dself.dest_img, ma= nual=3DFalse) + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + + def test_dismiss_true(self): + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + self.qmp_backup_and_wait(device=3D'drive0', format=3Diotests.imgfm= t, + sync=3D'full', target=3Dself.dest_img, ma= nual=3DTrue) + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'concluded') + res =3D self.vm.qmp('block-job-dismiss', id=3D'drive0') + self.assert_qmp(res, 'return', {}) + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + + def test_dismiss_bad_id(self): + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + res =3D self.vm.qmp('block-job-dismiss', id=3D'foobar') + self.assert_qmp(res, 'error/class', 'DeviceNotActive') + + def test_dismiss_collision(self): + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + self.qmp_backup_and_wait(device=3D'drive0', format=3Diotests.imgfm= t, + sync=3D'full', target=3Dself.dest_img, ma= nual=3DTrue) + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'concluded') + # Leave zombie job un-dismissed, observe a failure: + res =3D self.qmp_backup_and_wait(serror=3D'Need a root block node', + device=3D'drive0', format=3Diotests= .imgfmt, + sync=3D'full', target=3Dself.dest_i= mg, + manual=3DTrue) + self.assertEqual(res, False) + # OK, dismiss the zombie. + res =3D self.vm.qmp('block-job-dismiss', id=3D'drive0') + self.assert_qmp(res, 'return', {}) + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + # Ensure it's really gone. + self.qmp_backup_and_wait(device=3D'drive0', format=3Diotests.imgfm= t, + sync=3D'full', target=3Dself.dest_img, ma= nual=3DTrue) + + def dismissal_failure(self, manual_opt): + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + # Give blkdebug something to chew on + self.hmp_io_writes('drive0', + (('0x9a', 0, 512), + ('0x55', '8M', '352k'), + ('0x78', '15872k', '1M'))) + # Add destination node via blkdebug + res =3D self.vm.qmp('blockdev-add', + node_name=3D'target0', + driver=3Diotests.imgfmt, + file=3D{ + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': self.dest_img + }, + 'inject-error': [{ + 'event': 'write_aio', + 'errno': 5, + 'immediately': False, + 'once': True + }], + }) + self.assert_qmp(res, 'return', {}) + + res =3D self.qmp_backup(cmd=3D'blockdev-backup', + device=3D'drive0', target=3D'target0', + on_target_error=3D'stop', + sync=3D'full', + manual=3Dmanual_opt) + self.assertTrue(res) + event =3D self.vm.event_wait(name=3D"BLOCK_JOB_ERROR", + match=3D{'data': {'device': 'drive0'}}) + self.assertNotEqual(event, None) + # OK, job should be wedged + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'paused') + res =3D self.vm.qmp('block-job-dismiss', id=3D'drive0') + self.assert_qmp(res, 'error/desc', + "Job 'drive0' in state 'paused' cannot accept" + " command verb 'dismiss'") + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'paused') + # OK, unstick job and move forward. + res =3D self.vm.qmp('block-job-resume', device=3D'drive0') + self.assert_qmp(res, 'return', {}) + if manual_opt: + # Job is now PENDING. Wait and finalize it. + self.qmp_job_pending_wait('drive0') + # And now we need to wait for it to conclude; + res =3D self.qmp_backup_wait(device=3D'drive0') + self.assertTrue(res) + if manual_opt: + # Job should now be languishing: + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'concluded') + res =3D self.vm.qmp('block-job-dismiss', id=3D'drive0') + self.assert_qmp(res, 'return', {}) + res =3D self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + + def test_dismiss_premature(self): + self.dismissal_failure(True) + + def test_dismiss_erroneous(self): + self.dismissal_failure(False) + if __name__ =3D=3D '__main__': iotests.main(supported_fmts=3D['qcow2', 'qed']) diff --git a/tests/qemu-iotests/056.out b/tests/qemu-iotests/056.out index 8d7e996700..dae404e278 100644 --- a/tests/qemu-iotests/056.out +++ b/tests/qemu-iotests/056.out @@ -1,5 +1,5 @@ -... +......... ---------------------------------------------------------------------- -Ran 3 tests +Ran 9 tests =20 OK --=20 2.14.3