From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:60789) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RlkJc-0003kY-97 for qemu-devel@nongnu.org; Fri, 13 Jan 2012 11:49:27 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RlkJS-0000lJ-VP for qemu-devel@nongnu.org; Fri, 13 Jan 2012 11:49:20 -0500 Received: from mail-we0-f173.google.com ([74.125.82.173]:43821) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RlkJS-0000lF-Ll for qemu-devel@nongnu.org; Fri, 13 Jan 2012 11:49:10 -0500 Received: by werf1 with SMTP id f1so489256wer.4 for ; Fri, 13 Jan 2012 08:49:09 -0800 (PST) MIME-Version: 1.0 In-Reply-To: <1326460457-19446-16-git-send-email-stefanha@linux.vnet.ibm.com> References: <1326460457-19446-1-git-send-email-stefanha@linux.vnet.ibm.com> <1326460457-19446-16-git-send-email-stefanha@linux.vnet.ibm.com> Date: Fri, 13 Jan 2012 16:49:09 +0000 Message-ID: From: Stefan Hajnoczi Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH v5 15/15] test: add image streaming test cases List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Lucas Meneghel Rodrigues Cc: Kevin Wolf , Marcelo Tosatti , qemu-devel@nongnu.org, Luiz Capitulino Hi Lucas, The Python script below verifies the image streaming feature. It's built on the standard library "unittest" module, as well as QEMU's qmp.py module. It spawns a qemu process and creates necessary disk image files. The tests themselves issue QMP commands and check their result or wait for QMP events to be raised. I think this sort of test could be done with kvm-autotest but I don't see much usage of cmd_qmp() in client/tests/kvm/tests/. I'm curious how you would approach this. The high-level steps are: 1. Create a backing file. 2. Create a test QED image file using the backing file. 3. Issue "block_stream device=3Ddrive0" to the running VM. This should return no value. 4. Wait for the BLOCK_JOB_COMPLETED QMP event and check its fields - they must contain expected values. 5. Ensure "query-block-job" does not show any active jobs anymore. 6. Use qemu-io's map command to verify that the image stays compact and isn't bloated with actual zero bytes (this is kind of unrelated to the rest of the test). The other test cases share much of the same building blocks as TestSingleDrive, so they are less interesting. Would it be possible to look at TestSingleDrive below and give a kvm-autotest equivalent? Thanks, Stefan On Fri, Jan 13, 2012 at 1:14 PM, Stefan Hajnoczi wrote: > python test-stream.py > > Signed-off-by: Stefan Hajnoczi > --- > =A0test-stream.py | =A0208 ++++++++++++++++++++++++++++++++++++++++++++++= ++++++++++ > =A01 files changed, 208 insertions(+), 0 deletions(-) > =A0create mode 100644 test-stream.py > > diff --git a/test-stream.py b/test-stream.py > new file mode 100644 > index 0000000..16cbe5d > --- /dev/null > +++ b/test-stream.py > @@ -0,0 +1,208 @@ > +#!/usr/bin/env python > +import unittest > +import subprocess > +import re > +import os > +import sys; sys.path.append('QMP/') > +import qmp > + > +def qemu_img(*args): > + =A0 =A0devnull =3D open('/dev/null', 'r+') > + =A0 =A0return subprocess.call(['./qemu-img'] + list(args), stdin=3Ddevn= ull, stdout=3Ddevnull) > + > +def qemu_io(*args): > + =A0 =A0args =3D ['./qemu-io'] + list(args) > + =A0 =A0return subprocess.Popen(args, stdout=3Dsubprocess.PIPE).communic= ate()[0] > + > +class VM(object): > + =A0 =A0def __init__(self): > + =A0 =A0 =A0 =A0self._monitor_path =3D '/tmp/qemu-mon.%d' % os.getpid() > + =A0 =A0 =A0 =A0self._qemu_log_path =3D '/tmp/qemu-log.%d' % os.getpid() > + =A0 =A0 =A0 =A0self._args =3D ['x86_64-softmmu/qemu-system-x86_64', > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0'-chardev', 'socket,id=3Dmon= ,path=3D' + self._monitor_path, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0'-mon', 'chardev=3Dmon,mode= =3Dcontrol', '-nographic'] > + =A0 =A0 =A0 =A0self._num_drives =3D 0 > + > + =A0 =A0def add_drive(self, path, opts=3D''): > + =A0 =A0 =A0 =A0options =3D ['if=3Dvirtio', > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 'cache=3Dnone', > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 'file=3D%s' % path, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 'id=3Ddrive%d' % self._num_drives] > + =A0 =A0 =A0 =A0if opts: > + =A0 =A0 =A0 =A0 =A0 =A0options.append(opts) > + > + =A0 =A0 =A0 =A0self._args.append('-drive') > + =A0 =A0 =A0 =A0self._args.append(','.join(options)) > + =A0 =A0 =A0 =A0self._num_drives +=3D 1 > + =A0 =A0 =A0 =A0return self > + > + =A0 =A0def launch(self): > + =A0 =A0 =A0 =A0devnull =3D open('/dev/null', 'rb') > + =A0 =A0 =A0 =A0qemulog =3D open(self._qemu_log_path, 'wb') > + =A0 =A0 =A0 =A0self._qmp =3D qmp.QEMUMonitorProtocol(self._monitor_path= , server=3DTrue) > + =A0 =A0 =A0 =A0self._popen =3D subprocess.Popen(self._args, stdin=3Ddev= null, stdout=3Dqemulog, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 stderr=3Dsubprocess.STDOUT) > + =A0 =A0 =A0 =A0self._qmp.accept() > + > + =A0 =A0def shutdown(self): > + =A0 =A0 =A0 =A0self._qmp.cmd('quit') > + =A0 =A0 =A0 =A0self._popen.wait() > + =A0 =A0 =A0 =A0os.remove(self._monitor_path) > + =A0 =A0 =A0 =A0os.remove(self._qemu_log_path) > + > + =A0 =A0def qmp(self, cmd, **args): > + =A0 =A0 =A0 =A0return self._qmp.cmd(cmd, args=3Dargs) > + > + =A0 =A0def get_qmp_events(self, wait=3DFalse): > + =A0 =A0 =A0 =A0events =3D self._qmp.get_events(wait=3Dwait) > + =A0 =A0 =A0 =A0self._qmp.clear_events() > + =A0 =A0 =A0 =A0return events > + > +index_re =3D re.compile(r'([^\[]+)\[([^\]]+)\]') > + > +class QMPTestCase(unittest.TestCase): > + =A0 =A0def dictpath(self, d, path): > + =A0 =A0 =A0 =A0"""Traverse a path in a nested dict""" > + =A0 =A0 =A0 =A0for component in path.split('/'): > + =A0 =A0 =A0 =A0 =A0 =A0m =3D index_re.match(component) > + =A0 =A0 =A0 =A0 =A0 =A0if m: > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0component, idx =3D m.groups() > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0idx =3D int(idx) > + > + =A0 =A0 =A0 =A0 =A0 =A0if not isinstance(d, dict) or component not in d= : > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.fail('failed path traversal for "%s= " in "%s"' % (path, str(d))) > + =A0 =A0 =A0 =A0 =A0 =A0d =3D d[component] > + > + =A0 =A0 =A0 =A0 =A0 =A0if m: > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if not isinstance(d, list): > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.fail('path component "%s" i= n "%s" is not a list in "%s"' % (component, path, str(d))) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0try: > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0d =3D d[idx] > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0except IndexError: > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.fail('invalid index "%s" in= path "%s" in "%s"' % (idx, path, str(d))) > + =A0 =A0 =A0 =A0return d > + > + =A0 =A0def assert_qmp(self, d, path, value): > + =A0 =A0 =A0 =A0result =3D self.dictpath(d, path) > + =A0 =A0 =A0 =A0self.assertEqual(result, value, 'values not equal "%s" a= nd "%s"' % (str(result), str(value))) > + > + =A0 =A0def assert_no_active_streams(self): > + =A0 =A0 =A0 =A0result =3D self.vm.qmp('query-block-jobs') > + =A0 =A0 =A0 =A0self.assert_qmp(result, 'return', []) > + > +class TestSingleDrive(QMPTestCase): > + =A0 =A0image_len =3D 1 * 1024 * 1024 # MB > + > + =A0 =A0def setUp(self): > + =A0 =A0 =A0 =A0qemu_img('create', 'backing.img', str(TestSingleDrive.im= age_len)) > + =A0 =A0 =A0 =A0qemu_img('create', '-f', 'qed', '-o', 'backing_file=3Dba= cking.img', 'test.qed') > + =A0 =A0 =A0 =A0self.vm =3D VM().add_drive('test.qed') > + =A0 =A0 =A0 =A0self.vm.launch() > + > + =A0 =A0def tearDown(self): > + =A0 =A0 =A0 =A0self.vm.shutdown() > + =A0 =A0 =A0 =A0os.remove('test.qed') > + =A0 =A0 =A0 =A0os.remove('backing.img') > + > + =A0 =A0def test_stream(self): > + =A0 =A0 =A0 =A0self.assert_no_active_streams() > + > + =A0 =A0 =A0 =A0result =3D self.vm.qmp('block_stream', device=3D'drive0'= ) > + =A0 =A0 =A0 =A0self.assert_qmp(result, 'return', {}) > + > + =A0 =A0 =A0 =A0completed =3D False > + =A0 =A0 =A0 =A0while not completed: > + =A0 =A0 =A0 =A0 =A0 =A0for event in self.vm.get_qmp_events(wait=3DTrue)= : > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if event['event'] =3D=3D 'BLOCK_JOB_COMP= LETED': > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/typ= e', 'stream') > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/dev= ice', 'drive0') > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/off= set', self.image_len) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/len= ', self.image_len) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0completed =3D True > + > + =A0 =A0 =A0 =A0self.assert_no_active_streams() > + > + =A0 =A0 =A0 =A0self.assertFalse('sectors not allocated' in qemu_io('-c'= , 'map', 'test.qed'), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 'image file not fully p= opulated after streaming') > + > + =A0 =A0def test_device_not_found(self): > + =A0 =A0 =A0 =A0result =3D self.vm.qmp('block_stream', device=3D'nonexis= tent') > + =A0 =A0 =A0 =A0self.assert_qmp(result, 'error/class', 'DeviceNotFound') > + > +class TestStreamStop(QMPTestCase): > + =A0 =A0image_len =3D 8 * 1024 * 1024 * 1024 # GB > + > + =A0 =A0def setUp(self): > + =A0 =A0 =A0 =A0qemu_img('create', 'backing.img', str(TestStreamStop.ima= ge_len)) > + =A0 =A0 =A0 =A0qemu_img('create', '-f', 'qed', '-o', 'backing_file=3Dba= cking.img', 'test.qed') > + =A0 =A0 =A0 =A0self.vm =3D VM().add_drive('test.qed') > + =A0 =A0 =A0 =A0self.vm.launch() > + > + =A0 =A0def tearDown(self): > + =A0 =A0 =A0 =A0self.vm.shutdown() > + =A0 =A0 =A0 =A0os.remove('test.qed') > + =A0 =A0 =A0 =A0os.remove('backing.img') > + > + =A0 =A0def test_stream_stop(self): > + =A0 =A0 =A0 =A0import time > + > + =A0 =A0 =A0 =A0self.assert_no_active_streams() > + > + =A0 =A0 =A0 =A0result =3D self.vm.qmp('block_stream', device=3D'drive0'= ) > + =A0 =A0 =A0 =A0self.assert_qmp(result, 'return', {}) > + > + =A0 =A0 =A0 =A0time.sleep(1) > + =A0 =A0 =A0 =A0events =3D self.vm.get_qmp_events(wait=3DFalse) > + =A0 =A0 =A0 =A0self.assertEqual(events, [], 'unexpected QMP event: %s' = % events) > + > + =A0 =A0 =A0 =A0self.vm.qmp('block_job_cancel', device=3D'drive0') > + =A0 =A0 =A0 =A0self.assert_qmp(result, 'return', {}) > + > + =A0 =A0 =A0 =A0cancelled =3D False > + =A0 =A0 =A0 =A0while not cancelled: > + =A0 =A0 =A0 =A0 =A0 =A0for event in self.vm.get_qmp_events(wait=3DTrue)= : > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if event['event'] =3D=3D 'BLOCK_JOB_CANC= ELLED': > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/typ= e', 'stream') > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/dev= ice', 'drive0') > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0cancelled =3D True > + > + =A0 =A0 =A0 =A0self.assert_no_active_streams() > + > +class TestSetSpeed(QMPTestCase): > + =A0 =A0image_len =3D 80 * 1024 * 1024 # MB > + > + =A0 =A0def setUp(self): > + =A0 =A0 =A0 =A0qemu_img('create', 'backing.img', str(TestSetSpeed.image= _len)) > + =A0 =A0 =A0 =A0qemu_img('create', '-f', 'qed', '-o', 'backing_file=3Dba= cking.img', 'test.qed') > + =A0 =A0 =A0 =A0self.vm =3D VM().add_drive('test.qed') > + =A0 =A0 =A0 =A0self.vm.launch() > + > + =A0 =A0def tearDown(self): > + =A0 =A0 =A0 =A0self.vm.shutdown() > + =A0 =A0 =A0 =A0os.remove('test.qed') > + =A0 =A0 =A0 =A0os.remove('backing.img') > + > + =A0 =A0# This doesn't print or verify anything, only use it via "test-s= tream.py TestSetSpeed" > + =A0 =A0def test_stream_set_speed(self): > + =A0 =A0 =A0 =A0self.assert_no_active_streams() > + > + =A0 =A0 =A0 =A0result =3D self.vm.qmp('block_stream', device=3D'drive0'= ) > + =A0 =A0 =A0 =A0self.assert_qmp(result, 'return', {}) > + > + =A0 =A0 =A0 =A0result =3D self.vm.qmp('block_job_set_speed', device=3D'= drive0', value=3D8 * 1024 * 1024) > + =A0 =A0 =A0 =A0self.assert_qmp(result, 'return', {}) > + > + =A0 =A0 =A0 =A0completed =3D False > + =A0 =A0 =A0 =A0while not completed: > + =A0 =A0 =A0 =A0 =A0 =A0for event in self.vm.get_qmp_events(wait=3DTrue)= : > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if event['event'] =3D=3D 'BLOCK_JOB_COMP= LETED': > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/typ= e', 'stream') > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/dev= ice', 'drive0') > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/off= set', self.image_len) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0self.assert_qmp(event, 'data/len= ', self.image_len) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0completed =3D True > + > + =A0 =A0 =A0 =A0self.assert_no_active_streams() > + > +if __name__ =3D=3D '__main__': > + =A0 =A0unittest.main() > -- > 1.7.7.3 > >