Adam Litke has uploaded a new change for review.

Change subject: Live Merge: Restore watermark tracking
......................................................................

Live Merge: Restore watermark tracking

Change-Id: I632f31e7795ec5d8c6f52a480116b14470c3163f
Signed-off-by: Adam Litke <ali...@redhat.com>
---
M vdsm/virt/vm.py
1 file changed, 108 insertions(+), 10 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/24/36924/1

diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index f22610d..09080b9 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -1512,12 +1512,94 @@
         with self._confLock:
             self.conf['timeOffset'] = newTimeOffset
 
+    def _getWriteWatermarks(self):
+        def pathToVolID(drive, path):
+            for vol in drive.volumeChain:
+                if os.path.realpath(vol['path']) == os.path.realpath(path):
+                    return vol['volumeID']
+            raise LookupError("Unable to find VolumeID for path '%s'", path)
+
+        volAllocMap = {}
+        statsFlags = self._libvirtBackingChainStatsFlag()
+        conn = libvirtconnection.get()
+        blkStats = conn.domainListGetStats([self._dom._dom],
+                                           libvirt.VIR_DOMAIN_STATS_BLOCK,
+                                           statsFlags)[0][1]
+        for i in xrange(0, blkStats['block.count']):
+            name = blkStats['block.%i.name' % i]
+            try:
+                drive = self._findDriveByName(name)
+            except LookupError:
+                continue
+            if not drive.blockDev or drive.format != 'cow':
+                continue
+
+            try:
+                path = blkStats['block.%i.path' % i]
+                alloc = blkStats['block.%i.allocation' % i]
+            except KeyError as e:
+                self.log.debug("Block stats are missing expected key '%s', "
+                               "skipping volume", e.args[0])
+                continue
+            volID = pathToVolID(drive, path)
+            volAllocMap[volID] = alloc
+        return volAllocMap
+
+    def _getLiveMergeExtendCandidates(self):
+        # The common case is that there are no active jobs.
+        if not self.conf['_blockJobs'].values():
+            return {}
+
+        candidates = {}
+        watermarks = self._getWriteWatermarks()
+        for job in self.conf['_blockJobs'].values():
+            try:
+                drive = self._findDriveByUUIDs(job['disk'])
+            except LookupError:
+                # After an active layer merge completes the vdsm metadata will
+                # be out of sync for a brief period.  If we cannot find the old
+                # disk then it's safe to skip it.
+                continue
+
+            if not drive.blockDev:
+                continue
+
+            if job['strategy'] == 'commit':
+                volumeID = job['baseVolume']
+            else:
+                self.log.debug("Unrecognized merge strategy '%s'",
+                               job['strategy'])
+                continue
+            res = self.cif.irs.getVolumeInfo(drive.domainID, drive.poolID,
+                                             drive.imageID, volumeID)
+            if res['status']['code'] != 0:
+                self.log.error("Unable to get the info of volume %s (domain: "
+                               "%s image: %s)", volumeID, drive.domainID,
+                               drive.imageID)
+                continue
+            volInfo = res['info']
+
+            if volInfo['format'].lower() != 'cow':
+                continue
+
+            if volumeID in watermarks:
+                self.log.debug("Adding live merge extension candidate: "
+                               "volume=%s allocation=%i", volumeID,
+                               watermarks[volumeID])
+                candidates[drive.imageID] = {
+                    'alloc': watermarks[volumeID],
+                    'physical': int(volInfo['truesize']),
+                    'capacity': int(volInfo['apparentsize']),
+                    'volumeID': volumeID}
+            else:
+                self.log.warning("No watermark info available for %s",
+                                 volumeID)
+        return candidates
+
     def _getExtendCandidates(self):
         ret = []
 
-        # FIXME: mergeCandidates should be a dictionary of candidate volumes
-        # once libvirt starts reporting watermark information for all volumes.
-        mergeCandidates = {}
+        mergeCandidates = self._getLiveMergeExtendCandidates()
         for drive in self._devices[hwclass.DISK]:
             if not drive.blockDev or drive.format != 'cow':
                 continue
@@ -4771,6 +4853,14 @@
                 jobsRet[jobID] = entry
         return jobsRet
 
+    def _libvirtBackingChainStatsFlag(self):
+        # Since libvirt 1.2.13, the virConnectGetAllDomainStats API will return
+        # block statistics for all volumes in the chain when using a new flag.
+        try:
+            return libvirt.VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING
+        except AttributeError:
+            return 0
+
     def merge(self, driveSpec, baseVolUUID, topVolUUID, bandwidth, jobUUID):
         if not caps.getLiveMergeSupport():
             self.log.error("Live merge is not supported on this host")
@@ -4815,6 +4905,8 @@
         if res['info']['voltype'] == 'SHARED':
             self.log.error("merge: Refusing to merge into a shared volume")
             return errCode['mergeErr']
+        baseSize = int(res['info']['apparentsize'])
+        baseCow = bool(res['info']['format'].lower() == 'cow')
 
         # Indicate that we expect libvirt to maintain the relative paths of
         # backing files.  This is necessary to ensure that a volume chain is
@@ -4865,13 +4957,19 @@
 
         # blockCommit will cause data to be written into the base volume.
         # Perform an initial extension to ensure there is enough space to
-        # copy all the required data.  Normally we'd use monitoring to extend
-        # the volume on-demand but internal watermark information is not being
-        # reported by libvirt so we must do the full extension up front.  In
-        # the worst case, we'll need to extend 'base' to the same size as 'top'
-        # plus a bit more to accomodate additional writes to 'top' during the
-        # live merge operation.
-        self.extendDriveVolume(drive, baseVolUUID, topSize)
+        # copy all the required data.  If libvirt supports monitoring of
+        # backing chain volumes, just extend by one chunk now and monitor
+        # during the rest of the operation.  Otherwise, extend now to
+        # accomodate the worst case scenario: no intersection between the
+        # allocated blocks in the base volume and the top volume.
+        if drive.blockDev and baseCow:
+            if self._libvirtBackingChainStatsFlag():
+                self.extendDrivesIfNeeded()
+            else:
+                extendSize = baseSize + topSize
+                self.log.debug("Preemptively extending volume %s with size %i"
+                               "(job: %s)", baseVolUUID, extendSize, jobUUID)
+                self.extendDriveVolume(drive, baseVolUUID, extendCurSize)
 
         # Trigger the collection of stats before returning so that callers
         # of getVmStats after this returns will see the new job


-- 
To view, visit http://gerrit.ovirt.org/36924
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I632f31e7795ec5d8c6f52a480116b14470c3163f
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <ali...@redhat.com>
_______________________________________________
vdsm-patches mailing list
vdsm-patches@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches

Reply via email to