Shahar Havivi has uploaded a new change for review. Change subject: v2v: introduction to OutputParser ......................................................................
v2v: introduction to OutputParser OutputParser is a parser for virt-v2v process output. It analyze the phases of copying disk and each copy disk progress. class also raise events respectfully. Change-Id: I54f3d610d704d0b922e38073edcc813cbc27fe83 Signed-off-by: Shahar Havivi <[email protected]> --- M tests/v2vTests.py M vdsm/v2v.py 2 files changed, 77 insertions(+), 85 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/60/39360/1 diff --git a/tests/v2vTests.py b/tests/v2vTests.py index f295771..dff77ac 100644 --- a/tests/v2vTests.py +++ b/tests/v2vTests.py @@ -17,95 +17,36 @@ # Refer to the README and COPYING files for full details of the license # +from StringIO import StringIO + import v2v -from vdsm import libvirtconnection - -from nose.plugins.skip import SkipTest from testlib import VdsmTestCase as TestCaseBase -from monkeypatch import MonkeyPatch - - -class VmMock(object): - def name(self): - return 'windows' - - def state(self, flags=0): - return [5, 0] - - def XMLDesc(self, flags=0): - return """ -<domain type='vmware' id='15'> - <name>RHEL</name> - <uuid>564d7cb4-8e3d-06ec-ce82-7b2b13c6a611</uuid> - <memory unit='KiB'>2097152</memory> - <currentMemory unit='KiB'>2097152</currentMemory> - <vcpu placement='static'>1</vcpu> - <os> - <type arch='x86_64'>hvm</type> - </os> - <clock offset='utc'/> - <on_poweroff>destroy</on_poweroff> - <on_reboot>restart</on_reboot> - <on_crash>destroy</on_crash> - <devices> - <disk type='file' device='disk'> - <source file='[datastore1] RHEL/RHEL.vmdk'/> - <target dev='sda' bus='scsi'/> - <address type='drive' controller='0' bus='0' target='0' unit='0'/> - </disk> - <controller type='scsi' index='0' model='vmpvscsi'/> - <interface type='bridge'> - <mac address='00:0c:29:c6:a6:11'/> - <source bridge='VM Network'/> - <model type='vmxnet3'/> - </interface> - <video> - <model type='vmvga' vram='8192'/> - </video> - </devices> -</domain>""" - - -# FIXME: extend vmfakelib allowing to set predefined domain in Connection class -class LibvirtMock(object): - def close(self): - pass - - def listAllDomains(self): - return [VmMock()] - - def storageVolLookupByPath(self, name): - return LibvirtMock.Volume() - - class Volume(object): - def info(self): - return [0, 0, 0] - - -def hypervisorConnect(uri, username, passwd): - return LibvirtMock() class v2vTests(TestCaseBase): - @MonkeyPatch(libvirtconnection, 'open_connection', hypervisorConnect) - def testGetExternalVMs(self): - if not v2v.supported(): - raise SkipTest('v2v is not supported current os version') + def testOutputParser(self): + output = ''.join(['[ 0.0] Opening the source -i libvirt ://roo...\n', + '[ 1.0] Creating an overlay to protect the f...\n', + '[ 88.0] Copying disk 1/2 to /tmp/v2v/0000000...\n', + ' (0/100%)\r', + ' (50/100%)\r', + ' (100/100%)\r', + '[ 180.0] Copying disk 2/2 to /tmp/v2v/100000-...\n', + ' (0/100%)\r', + ' (50/100%)\r', + ' (100/100%)\r', + '[ 256.0] Creating output metadata', + '[ 256.0] Finishing off']) - vms = v2v.get_external_vms('esx://mydomain', 'user', - 'password')['vmList'] - self.assertEquals(len(vms), 1) - vm = vms[0] - self.assertEquals(vm['vmId'], '564d7cb4-8e3d-06ec-ce82-7b2b13c6a611') - self.assertEquals(vm['memSize'], 2048) - self.assertEquals(vm['smp'], 1) - self.assertEquals(len(vm['disks']), 1) - self.assertEquals(len(vm['networks']), 1) - disk = vm['disks'][0] - self.assertEquals(disk['dev'], 'sda') - self.assertEquals(disk['alias'], '[datastore1] RHEL/RHEL.vmdk') - network = vm['networks'][0] - self.assertEquals(network['type'], 'bridge') - self.assertEquals(network['macAddr'], '00:0c:29:c6:a6:11') - self.assertEquals(network['bridge'], 'VM Network') + parser = v2v.OutputParser() + events = list(parser.parse(StringIO(output))) + self.assertEqual(events, [ + (v2v.ImportProgress(1, 2, 'Copying disk 1/2')), + (v2v.DiskProgress(0)), + (v2v.DiskProgress(50)), + (v2v.DiskProgress(100)), + (v2v.ImportProgress(2, 2, 'Copying disk 2/2')), + (v2v.DiskProgress(0)), + (v2v.DiskProgress(50)), + (v2v.DiskProgress(100))]) diff --git a/vdsm/v2v.py b/vdsm/v2v.py index 4fbecf0..3990851 100644 --- a/vdsm/v2v.py +++ b/vdsm/v2v.py @@ -16,8 +16,10 @@ # # Refer to the README and COPYING files for full details of the license # +from collections import namedtuple from contextlib import closing import logging +import re import xml.etree.ElementTree as ET import libvirt @@ -28,8 +30,17 @@ import caps +ImportProgress = namedtuple('ImportProgress', + ['current_disk', 'disk_count', 'description']) +DiskProgress = namedtuple('DiskProgress', ['progress']) + + class InvalidVMConfiguration(ValueError): ''' Unexpected error while parsing libvirt domain xml ''' + + +class OutputParserError(ValueError): + ''' Error while parsing virt-v2v output ''' def supported(): @@ -70,6 +81,46 @@ return {'status': doneCode, 'vmList': vms} +class OutputParser(object): + COPY_DISK_RE = re.compile(r'.*(Copying disk (\d+)/(\d+)).*') + DISK_PROGRESS_RE = re.compile(r'\s+\((\d+).*') + + def parse(self, stream): + for line in stream: + if 'Copying disk' in line: + description, current_disk, disk_count = self._parse_line(line) + yield ImportProgress(int(current_disk), int(disk_count), + description) + for progress in self._parse_progress(stream): + yield DiskProgress(progress) + + def _parse_line(self, line): + m = self.COPY_DISK_RE.match(line) + if m is None: + raise OutputParserError('unexpected format in "Copying disk"' + ', line: %r' % line) + return m.group(1), m.group(2), m.group(3) + + def _parse_progress(self, stream): + buf = '' + progress = 0 + while progress < 100: + c = stream.read(1) + buf += c + if c == '\r': + m = self.DISK_PROGRESS_RE.match(buf) + if m is None: + raise OutputParserError('error parsing progress, buf: %r' + % buf) + buf = '' + try: + progress = int(m.group(1)) + except ValueError: + raise OutputParserError('error parsing progress regex: %r' + % m.groups) + yield progress + + def _mem_to_mib(size, unit): lunit = unit.lower() if lunit in ('bytes', 'b'): -- To view, visit https://gerrit.ovirt.org/39360 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I54f3d610d704d0b922e38073edcc813cbc27fe83 Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: master Gerrit-Owner: Shahar Havivi <[email protected]> _______________________________________________ vdsm-patches mailing list [email protected] https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches
