Adam Litke has uploaded a new change for review.
Change subject: SDM: Add extendVolumeContainer API
......................................................................
SDM: Add extendVolumeContainer API
The extendVolumeContainer API is used to extend LVM logical volumes
which store thinly-provisioned vdsm volumes.
Change-Id: I6a128ba3eab4116ff4e794e94a171e51d9e432de
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M client/vdsClient.py
M vdsm/API.py
M vdsm/rpc/BindingXMLRPC.py
M vdsm/rpc/vdsmapi-schema.json
M vdsm/storage/hsm.py
M vdsm/storage/sdm/__init__.py
M vdsm/storage/sdm/blockstore.py
M vdsm/storage/sdm/filestore.py
8 files changed, 123 insertions(+), 9 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/96/39696/1
diff --git a/client/vdsClient.py b/client/vdsClient.py
index 23bfc1a..f6c0c83 100755
--- a/client/vdsClient.py
+++ b/client/vdsClient.py
@@ -1953,6 +1953,17 @@
else:
return status['status']['code'],
status['status']['message']
+ def extendVolumeContainer(self, args):
+ if len(args) != 4:
+ raise ValueError("Wrong number of arguments")
+
+ sdUUID, imgUUID, volUUID, size = args
+ status = self.s.extendVolumeContainer(sdUUID, imgUUID, volUUID, size)
+ if status['status']['code'] == 0:
+ return 0, ''
+ else:
+ return status['status']['code'],
status['status']['message']
+
if __name__ == '__main__':
if _glusterEnabled:
@@ -2844,6 +2855,11 @@
'<srcImage> <dstImage> <collapse>',
'Copy the date from one volume into another.'
)),
+ 'extendVolumeContainer': (
+ serv.extendVolumeContainer, (
+ '<sdUUID> <imgUUID> <volUUID>',
+ 'Extend a thinly-provisioned block volume.'
+ )),
}
if _glusterEnabled:
commands.update(ge.getGlusterCmdDict(serv))
diff --git a/vdsm/API.py b/vdsm/API.py
index b4b7308..5661b82 100644
--- a/vdsm/API.py
+++ b/vdsm/API.py
@@ -1675,6 +1675,10 @@
def copyData(self, srcImage, dstImage, collapse):
return self._cif.irs.copyData(srcImage, dstImage, collapse)
+ def extendVolumeContainer(self, sdUUID, imgUUID, volUUID, size):
+ return self._cif.irs.extendVolumeContainer(sdUUID, imgUUID, volUUID,
+ size)
+
# take a rough estimate on how much free mem is available for new vm
# memTotal = memFree + memCached + mem_used_by_non_qemu + resident .
# simply returning (memFree + memCached) is not good enough, as the
diff --git a/vdsm/rpc/BindingXMLRPC.py b/vdsm/rpc/BindingXMLRPC.py
index 3e70e28..ae43614 100644
--- a/vdsm/rpc/BindingXMLRPC.py
+++ b/vdsm/rpc/BindingXMLRPC.py
@@ -993,6 +993,10 @@
api = API.Global()
return api.copyData(srcImage, dstImage, collapse)
+ def extendVolumeContainer(self, sdUUID, imgUUID, volUUID, size):
+ api = API.Global()
+ return api.extendVolumeContainer(sdUUID, imgUUID, volUUID, size)
+
def getGlobalMethods(self):
return ((self.vmDestroy, 'destroy'),
(self.vmCreate, 'create'),
@@ -1143,7 +1147,8 @@
'storageServer_ConnectionRefs_statuses'),
(self.volumeCreateContainer, 'createVolumeContainer'),
(self.volumeRemove, 'removeVolume'),
- (self.copyData, 'copyData'))
+ (self.copyData, 'copyData'),
+ (self.extendVolumeContainer, 'extendVolumeContainer'))
def wrapApiMethod(f):
diff --git a/vdsm/rpc/vdsmapi-schema.json b/vdsm/rpc/vdsmapi-schema.json
index 8720703..f507324 100644
--- a/vdsm/rpc/vdsmapi-schema.json
+++ b/vdsm/rpc/vdsmapi-schema.json
@@ -4182,6 +4182,24 @@
'data': {'srcImage': 'VolumeSpec', 'dstImage':
'VolumeSpec',
'collapse': 'bool'}}
+##
+# @Host.extendVolumeContainer:
+#
+# Extend a thinly-provisioned volume.
+#
+# @sdUUID: The UUID of the storage domain containing the volume
+#
+# @imgUUID: The UUID of the image containing the volume
+#
+# @volUUID: The UUID of the volume
+#
+# @size: The new desired size (in bytes)
+#
+# Since: 4.18.0
+##
+{'command': {'class': 'Host', 'name':
'extendVolumeContainer'},
+ 'data': {'sdUUID': 'UUID', 'imgUUID': 'UUID',
'volUUID': 'UUID',
+ 'size': 'uint'}}
## Category: @ConnectionRefs ##################################################
##
diff --git a/vdsm/storage/hsm.py b/vdsm/storage/hsm.py
index 2236457..34730dd 100644
--- a/vdsm/storage/hsm.py
+++ b/vdsm/storage/hsm.py
@@ -854,13 +854,22 @@
"""
newSize = misc.validateN(newSize, "newSize") / 2 ** 20
- try:
- pool = self.getPool(spUUID)
- except se.StoragePoolUnknown:
- pass
+ enableSDM = True # Replace this with a check on the SD version
+ if enableSDM:
+ domain = sdCache.produce(volDict['domainID'])
+ self._sdmSchedule('extendVolumeContainer',
+ sdm.extendVolumeContainer, domain,
+ volDict['imageID'], volDict['volumeID'],
newSize,
+ callbackFunc, volDict)
else:
- if pool.hsmMailer:
- pool.hsmMailer.sendExtendMsg(volDict, newSize, callbackFunc)
+ try:
+ pool = self.getPool(spUUID)
+ except se.StoragePoolUnknown:
+ pass
+ else:
+ if pool.hsmMailer:
+ pool.hsmMailer.sendExtendMsg(volDict, newSize,
+ callbackFunc)
def _spmSchedule(self, spUUID, name, func, *args):
self.validateSPM(spUUID)
@@ -3744,3 +3753,18 @@
se.VolumeCopyError("Unsupported combination of image types: "
"src:%s, dst:%s" % (src.get('type'),
dst.get('type')))
+
+ @public
+ def extendVolumeContainer(self, sdUUID, imgUUID, volUUID, size):
+ vars.task.setDefaultException(
+ se.VolumeExtendingError("sdUUID=%s, volumeUUID=%s, size=%s" % (
+ sdUUID, volUUID, size)))
+ size = misc.validateN(size, "size")
+ # ExtendVolume expects size in MB
+ size = math.ceil(size / 2 ** 20)
+
+ dom = sdCache.produce(sdUUID=sdUUID)
+ misc.validateUUID(imgUUID, 'imgUUID')
+ misc.validateUUID(volUUID, 'volUUID')
+ vars.task.getSharedLock(STORAGE, sdUUID)
+ return sdm.extendVolumeContainer(dom, imgUUID, volUUID, size)
diff --git a/vdsm/storage/sdm/__init__.py b/vdsm/storage/sdm/__init__.py
index 9d74bd5..7f80ca2 100644
--- a/vdsm/storage/sdm/__init__.py
+++ b/vdsm/storage/sdm/__init__.py
@@ -195,3 +195,16 @@
finally:
dstDom.releaseVolumeLease(dstImage['imgUUID'],
dstImage['volUUID'])
srcDom.releaseVolumeLease(srcImage['imgUUID'],
srcImage['volUUID'])
+
+
+def extendVolumeContainer(domain, imgUUID, volUUID, size,
+ cbFn=None, cbData=None):
+ cls = __getStoreClass(domain)
+ hostId = getDomainHostId(domain.sdUUID)
+ domain.acquireClusterLock(hostId)
+ try:
+ cls.extendVolume(domain, imgUUID, volUUID, size)
+ finally:
+ domain.releaseClusterLock()
+ if cbFn:
+ cbFn(cbData)
diff --git a/vdsm/storage/sdm/blockstore.py b/vdsm/storage/sdm/blockstore.py
index b80f869..73a12a8 100644
--- a/vdsm/storage/sdm/blockstore.py
+++ b/vdsm/storage/sdm/blockstore.py
@@ -20,6 +20,7 @@
import os
import logging
+import math
import vdsm.utils as utils
from vdsm.config import config
@@ -27,9 +28,13 @@
import volumestore
from .. import blockVolume
from .. import lvm
+from .. import resourceManager as rm
+from .. import sd
from .. import storage_exception as se
from .. import volume
+from ..resourceFactories import IMAGE_NAMESPACE
+rmanager = rm.ResourceManager.getInstance()
log = logging.getLogger('Storage.sdm.blockstore')
SECTORS_TO_MB = (1 << 20) / volume.BLOCK_SIZE
@@ -37,6 +42,9 @@
class BlockStore(volumestore.VolumeStore):
volClass = blockVolume.BlockVolume
+
+ # Estimate of the additional space needed for qcow format internal data.
+ VOLWM_COW_OVERHEAD = 1.1
@classmethod
def volFormatToPreallocate(cls, volFormat):
@@ -109,7 +117,7 @@
parent = tag[len(blockVolume.TAG_PREFIX_PARENT):]
if parent and image:
break
- vols.append(volumestore.GCVol(lv.name, volUUID, image, parent))
+ vols.append(volumestore.GCVol(lv.name, volUUID, image, parent)
return vols
@classmethod
@@ -119,3 +127,24 @@
except se.VolumeMetadataReadError:
pass
lvm.removeLVs(dom.sdUUID, volName)
+
+ @classmethod
+ def extendVolume(cls, dom, imgUUID, volUUID, size):
+ imageResourcesNamespace = sd.getNamespace(dom.sdUUID, IMAGE_NAMESPACE)
+ with rmanager.acquireResource(imageResourcesNamespace, imgUUID,
+ rm.LockType.shared):
+ # Verify that the requested size is valid
+ vol = dom.produceVolume(imgUUID, volUUID)
+ volInfo = vol.getInfo()
+ maxSize = int(volInfo['capacity'])
+ if volInfo['format'] == volume.type2name(volume.COW_FORMAT):
+ maxSize = maxSize * cls.VOLWM_COW_OVERHEAD
+ maxSize = math.ceil(maxSize / 2 ** 20)
+ if size > maxSize:
+ raise se.VolumeExtendingError(
+ "Size %i exceeds the maximum extend size of %i for volume
"
+ "%s" % (size, maxSize, volUUID))
+
+ dom.extendVolume(volUUID, size)
+
+
diff --git a/vdsm/storage/sdm/filestore.py b/vdsm/storage/sdm/filestore.py
index dd6c58f..cc3e2bb 100644
--- a/vdsm/storage/sdm/filestore.py
+++ b/vdsm/storage/sdm/filestore.py
@@ -157,4 +157,9 @@
except se.ImageDeleteError:
dom.imageGarbageCollector()
else:
- dom.oop.os.unlink(volPath)
\ No newline at end of file
+ dom.oop.os.unlink(volPath)
+
+ @classmethod
+ def extendVolume(cls, dom, imgUUID, volUUID, size):
+ # There is nothing to do for file domains. The filesystem handles it,
+ pass
--
To view, visit
https://gerrit.ovirt.org/39696
To unsubscribe, visit
https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I6a128ba3eab4116ff4e794e94a171e51d9e432de
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>