Change in vdsm[master]: v2v: introduction to OutputParser

shavivi at redhat.com shavivi at redhat.com
Mon Mar 30 13:22:17 UTC 2015


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 <shaharh at redhat.com>
---
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 <shavivi at redhat.com>


More information about the vdsm-patches mailing list