Change in vdsm[master]: VolumeMetadata: move prepare and teardown
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: VolumeMetadata: move prepare and teardown
......................................................................
VolumeMetadata: move prepare and teardown
Change-Id: Iba0954ace3b1da5ea9afc41aeb6ea69a729fe29c
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/blockVolume.py
M vdsm/storage/fileVolume.py
M vdsm/storage/volume.py
3 files changed, 117 insertions(+), 109 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/50/44050/1
diff --git a/vdsm/storage/blockVolume.py b/vdsm/storage/blockVolume.py
index 0168d64..597e7ee 100644
--- a/vdsm/storage/blockVolume.py
+++ b/vdsm/storage/blockVolume.py
@@ -74,6 +74,8 @@
volume.VolumeMetadata.__init__(self, repoPath, sdUUID, imgUUID,
volUUID)
self.metaoff = None
+ self.lvmActivationNamespace = sd.getNamespace(self.sdUUID,
+ LVM_ACTIVATION_NAMESPACE)
def getMetadataId(self):
"""
@@ -332,6 +334,49 @@
lvs = lvm.lvsByTag(sdUUID, "%s%s" % (TAG_PREFIX_IMAGE, imgUUID))
return [lv.name for lv in lvs]
+ @logskip("ResourceManager")
+ def llPrepare(self, rw=False, setrw=False):
+ """
+ Perform low level volume use preparation
+
+ For the Block Volumes the actual LV activation is wrapped
+ into lvmActivation resource. It is being initialized by the
+ storage domain sitting on top of the encapsulating VG.
+ We just use it here.
+ """
+ if setrw:
+ self.setrw(rw=rw)
+ access = rm.LockType.exclusive if rw else rm.LockType.shared
+ activation = rmanager.acquireResource(self.lvmActivationNamespace,
+ self.volUUID, access)
+ activation.autoRelease = False
+
+ @classmethod
+ def teardown(cls, sdUUID, volUUID, justme=False):
+ """
+ Deactivate volume and release resources.
+ Volume deactivation occurs as part of resource releasing.
+ If justme is false, the entire COW chain should be torn down.
+ """
+ cls.log.info("Tearing down volume %s/%s justme %s"
+ % (sdUUID, volUUID, justme))
+ lvmActivationNamespace = sd.getNamespace(sdUUID,
+ LVM_ACTIVATION_NAMESPACE)
+ rmanager.releaseResource(lvmActivationNamespace, volUUID)
+ if not justme:
+ try:
+ pvolUUID = _getVolumeTag(sdUUID, volUUID, TAG_PREFIX_PARENT)
+ except Exception as e:
+ # If storage not accessible or lvm error occurred
+ # we will failure to get the parent volume.
+ # We can live with it and still succeed in volume's teardown.
+ pvolUUID = volume.BLANK_UUID
+ cls.log.warn("Failure to get parent of volume %s/%s (%s)"
+ % (sdUUID, volUUID, e))
+
+ if pvolUUID != volume.BLANK_UUID:
+ cls.teardown(sdUUID=sdUUID, volUUID=pvolUUID, justme=False)
+
class BlockVolume(volume.Volume):
""" Actually represents a single volume (i.e. part of virtual disk).
@@ -341,8 +386,6 @@
def __init__(self, repoPath, sdUUID, imgUUID, volUUID):
md = self.MetadataClass(repoPath, sdUUID, imgUUID, volUUID)
volume.Volume.__init__(self, md)
- self.lvmActivationNamespace = sd.getNamespace(self.sdUUID,
- LVM_ACTIVATION_NAMESPACE)
@property
def metaoff(self):
@@ -609,49 +652,6 @@
def shareVolumeRollback(cls, taskObj, volPath):
cls.log.info("Volume rollback for volPath=%s", volPath)
utils.rmFile(volPath)
-
- @logskip("ResourceManager")
- def llPrepare(self, rw=False, setrw=False):
- """
- Perform low level volume use preparation
-
- For the Block Volumes the actual LV activation is wrapped
- into lvmActivation resource. It is being initialized by the
- storage domain sitting on top of the encapsulating VG.
- We just use it here.
- """
- if setrw:
- self.setrw(rw=rw)
- access = rm.LockType.exclusive if rw else rm.LockType.shared
- activation = rmanager.acquireResource(self.lvmActivationNamespace,
- self.volUUID, access)
- activation.autoRelease = False
-
- @classmethod
- def teardown(cls, sdUUID, volUUID, justme=False):
- """
- Deactivate volume and release resources.
- Volume deactivation occurs as part of resource releasing.
- If justme is false, the entire COW chain should be torn down.
- """
- cls.log.info("Tearing down volume %s/%s justme %s"
- % (sdUUID, volUUID, justme))
- lvmActivationNamespace = sd.getNamespace(sdUUID,
- LVM_ACTIVATION_NAMESPACE)
- rmanager.releaseResource(lvmActivationNamespace, volUUID)
- if not justme:
- try:
- pvolUUID = _getVolumeTag(sdUUID, volUUID, TAG_PREFIX_PARENT)
- except Exception as e:
- # If storage not accessible or lvm error occurred
- # we will failure to get the parent volume.
- # We can live with it and still succeed in volume's teardown.
- pvolUUID = volume.BLANK_UUID
- cls.log.warn("Failure to get parent of volume %s/%s (%s)"
- % (sdUUID, volUUID, e))
-
- if pvolUUID != volume.BLANK_UUID:
- cls.teardown(sdUUID=sdUUID, volUUID=pvolUUID, justme=False)
def getVolumeTag(self, tagPrefix):
return self.md.getVolumeTag(tagPrefix)
diff --git a/vdsm/storage/fileVolume.py b/vdsm/storage/fileVolume.py
index 2dad91f..08e3f50 100644
--- a/vdsm/storage/fileVolume.py
+++ b/vdsm/storage/fileVolume.py
@@ -337,6 +337,26 @@
volList.append(volid)
return volList
+ def llPrepare(self, rw=False, setrw=False):
+ """
+ Make volume accessible as readonly (internal) or readwrite (leaf)
+ """
+ volPath = self.getVolumePath()
+
+ # Volumes leaves created in 2.2 did not have group writeable bit
+ # set. We have to set it here if we want qemu-kvm to write to old
+ # NFS volumes.
+ self.oop.fileUtils.copyUserModeToGroup(volPath)
+
+ if setrw:
+ self.setrw(rw=rw)
+ if rw:
+ if not self.oop.os.access(volPath, os.R_OK | os.W_OK):
+ raise se.VolumeAccessError(volPath)
+ else:
+ if not self.oop.os.access(volPath, os.R_OK):
+ raise se.VolumeAccessError(volPath)
+
class FileVolume(volume.Volume):
""" Actually represents a single volume (i.e. part of virtual disk).
@@ -488,26 +508,6 @@
procPool.utils.rmFile(volPath)
procPool.utils.rmFile(cls.__metaVolumePath(volPath))
procPool.utils.rmFile(cls.MetadataClass._leaseVolumePath(volPath))
-
- def llPrepare(self, rw=False, setrw=False):
- """
- Make volume accessible as readonly (internal) or readwrite (leaf)
- """
- volPath = self.getVolumePath()
-
- # Volumes leaves created in 2.2 did not have group writeable bit
- # set. We have to set it here if we want qemu-kvm to write to old
- # NFS volumes.
- self.oop.fileUtils.copyUserModeToGroup(volPath)
-
- if setrw:
- self.setrw(rw=rw)
- if rw:
- if not self.oop.os.access(volPath, os.R_OK | os.W_OK):
- raise se.VolumeAccessError(volPath)
- else:
- if not self.oop.os.access(volPath, os.R_OK):
- raise se.VolumeAccessError(volPath)
@classmethod
def __putMetadata(cls, metaId, meta):
diff --git a/vdsm/storage/volume.py b/vdsm/storage/volume.py
index a098eb7..22f7ead 100644
--- a/vdsm/storage/volume.py
+++ b/vdsm/storage/volume.py
@@ -525,6 +525,56 @@
return apparentSize
return req_size
+ def prepare(self, rw=True, justme=False,
+ chainrw=False, setrw=False, force=False):
+ """
+ Prepare volume for use by consumer.
+ If justme is false, the entire COW chain is prepared.
+ Note: setrw arg may be used only by SPM flows.
+ """
+ self.log.info("Volume: preparing volume %s/%s",
+ self.sdUUID, self.volUUID)
+
+ if not force:
+ # Cannot prepare ILLEGAL volume
+ if not self.isLegal():
+ raise se.prepareIllegalVolumeError(self.volUUID)
+
+ if rw and self.isShared():
+ if chainrw:
+ rw = False # Shared cannot be set RW
+ else:
+ raise se.SharedVolumeNonWritable(self)
+
+ if (not chainrw and rw and self.isInternal() and setrw and
+ not self.recheckIfLeaf()):
+ raise se.InternalVolumeNonWritable(self)
+
+ self.llPrepare(rw=rw, setrw=setrw)
+ self.updateInvalidatedSize()
+
+ try:
+ if justme:
+ return True
+ pvol = self.produceParent()
+ if pvol:
+ pvol.prepare(rw=chainrw, justme=False,
+ chainrw=chainrw, setrw=setrw)
+ except Exception:
+ self.log.error("Unexpected error", exc_info=True)
+ self.teardown(self.sdUUID, self.volUUID)
+ raise
+
+ return True
+
+ @classmethod
+ def teardown(cls, sdUUID, volUUID, justme=False):
+ """
+ Teardown volume.
+ If justme is false, the entire COW chain is teared down.
+ """
+ pass
+
class Volume(object):
log = logging.getLogger('Storage.Volume')
@@ -1091,53 +1141,11 @@
def prepare(self, rw=True, justme=False,
chainrw=False, setrw=False, force=False):
- """
- Prepare volume for use by consumer.
- If justme is false, the entire COW chain is prepared.
- Note: setrw arg may be used only by SPM flows.
- """
- self.log.info("Volume: preparing volume %s/%s",
- self.sdUUID, self.volUUID)
-
- if not force:
- # Cannot prepare ILLEGAL volume
- if not self.isLegal():
- raise se.prepareIllegalVolumeError(self.volUUID)
-
- if rw and self.isShared():
- if chainrw:
- rw = False # Shared cannot be set RW
- else:
- raise se.SharedVolumeNonWritable(self)
-
- if (not chainrw and rw and self.isInternal() and setrw and
- not self.recheckIfLeaf()):
- raise se.InternalVolumeNonWritable(self)
-
- self.llPrepare(rw=rw, setrw=setrw)
- self.updateInvalidatedSize()
-
- try:
- if justme:
- return True
- pvol = self.produceParent()
- if pvol:
- pvol.prepare(rw=chainrw, justme=False,
- chainrw=chainrw, setrw=setrw)
- except Exception:
- self.log.error("Unexpected error", exc_info=True)
- self.teardown(self.sdUUID, self.volUUID)
- raise
-
- return True
+ return self.md.prepare(rw, justme, chainrw, setrw, force)
@classmethod
def teardown(cls, sdUUID, volUUID, justme=False):
- """
- Teardown volume.
- If justme is false, the entire COW chain is teared down.
- """
- pass
+ cls.MetadataClass.teardown(sdUUID, volUUID, justme)
@classmethod
def newMetadata(cls, metaId, sdUUID, imgUUID, puuid, size, format, type,
--
To view, visit https://gerrit.ovirt.org/44050
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iba0954ace3b1da5ea9afc41aeb6ea69a729fe29c
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: sdc: Allow StorageDomainCache to cache StorageDomainManifest...
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: sdc: Allow StorageDomainCache to cache StorageDomainManifest objects
......................................................................
sdc: Allow StorageDomainCache to cache StorageDomainManifest objects
When HSM is operating without SPM the system should always be using
StorageDomainManifest objects instead of StorageDomains. With this
patch we can instruct the cache to serve the correct type of object
without changing all consumers of sdCache.
Change-Id: I9a3dc7d9bf24f7d8b60ddff6f5364b65a9354e45
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/sdc.py
1 file changed, 12 insertions(+), 3 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/41/44041/1
diff --git a/vdsm/storage/sdc.py b/vdsm/storage/sdc.py
index ecb9708..cbdaed9 100644
--- a/vdsm/storage/sdc.py
+++ b/vdsm/storage/sdc.py
@@ -64,12 +64,13 @@
STORAGE_STALE = 1
STORAGE_REFRESHING = 2
- def __init__(self, storage_repo):
+ def __init__(self, storage_repo, use_manifests=False):
self._syncroot = threading.Condition()
self.__domainCache = {}
self.__inProgress = set()
self.__staleStatus = self.STORAGE_STALE
self.storage_repo = storage_repo
+ self.use_manifests = use_manifests
self.knownSDs = {} # {sdUUID: mod.findDomain}
def invalidateStorage(self):
@@ -162,12 +163,18 @@
# this changes, please update the order.
for mod in (blockSD, glusterSD, localFsSD, nfsSD):
try:
- return mod.findDomain(sdUUID)
+ if self.use_manifests:
+ ret = mod.findDomainManifest(sdUUID)
+ else:
+ ret = mod.findDomain(sdUUID)
except se.StorageDomainDoesNotExist:
pass
except Exception:
self.log.error("Error while looking for domain `%s`", sdUUID,
exc_info=True)
+ else:
+ self.log.debug("Found domain %s", ret)
+ return ret
raise se.StorageDomainDoesNotExist(sdUUID)
@@ -181,10 +188,12 @@
return uuids
- def refresh(self):
+ def refresh(self, use_manifests=None):
with self._syncroot:
lvm.invalidateCache()
self.__domainCache.clear()
+ if use_manifests is not None:
+ self.use_manifests = use_manifests
def manuallyAddDomain(self, domain):
with self._syncroot:
--
To view, visit https://gerrit.ovirt.org/44041
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9a3dc7d9bf24f7d8b60ddff6f5364b65a9354e45
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: VolumeMetadata: Move getParentVolume
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: VolumeMetadata: Move getParentVolume
......................................................................
VolumeMetadata: Move getParentVolume
Move getParentVolume into VolumeMetadata. If HSM configured sdCache to
return StorageDomainManifest objects then this will return
VolumeMetadata objects. Otherwise it will continue to return Volume
objects as it always has.
Change-Id: I74def8861e0b7b97ca4d218a969440ccba7f07dd
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/volume.py
1 file changed, 11 insertions(+), 5 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/42/44042/1
diff --git a/vdsm/storage/volume.py b/vdsm/storage/volume.py
index c40ba77..f7e5bc0 100644
--- a/vdsm/storage/volume.py
+++ b/vdsm/storage/volume.py
@@ -485,6 +485,16 @@
"""
pass # Do not remove this method or the V3 upgrade will fail.
+ def getParentVolume(self):
+ """
+ Return parent volume object
+ """
+ puuid = self.getParent()
+ if puuid and puuid != BLANK_UUID:
+ return sdCache.produce(self.sdUUID).produceVolume(self.imgUUID,
+ puuid)
+ return None
+
class Volume(object):
log = logging.getLogger('Storage.Volume')
@@ -1128,11 +1138,7 @@
"""
Return parent volume object
"""
- puuid = self.getParent()
- if puuid and puuid != BLANK_UUID:
- return sdCache.produce(self.sdUUID).produceVolume(self.imgUUID,
- puuid)
- return None
+ return self.md.getParentVolume()
def setParent(self, puuid):
"""
--
To view, visit https://gerrit.ovirt.org/44042
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I74def8861e0b7b97ca4d218a969440ccba7f07dd
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: storage: Make Image.__chainSizeCalc public
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: storage: Make Image.__chainSizeCalc public
......................................................................
storage: Make Image.__chainSizeCalc public
The new SDM copyVolumeData wants to make use of the same logic being
used by the classic copy flows to extend the size of the target volume
to the appropriate size. Make Image.__chainSizeCalc public so it can be
accessed from the SDM code.
Change-Id: Id079eb5067c16f934370e42b5f4e09bbcef1512b
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/image.py
1 file changed, 2 insertions(+), 2 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/95/38995/1
diff --git a/vdsm/storage/image.py b/vdsm/storage/image.py
index 791b48c..8d5e8c2 100644
--- a/vdsm/storage/image.py
+++ b/vdsm/storage/image.py
@@ -140,7 +140,7 @@
randomStr = misc.randomStr(RENAME_RANDOM_STRING_LEN)
return "%s%s_%s" % (sd.REMOVED_IMAGE_PREFIX, randomStr, uuid)
- def __chainSizeCalc(self, sdUUID, imgUUID, volUUID, size):
+ def chainSizeCalc(self, sdUUID, imgUUID, volUUID, size):
"""
Compute an estimate of the whole chain size
using the sum of the actual size of the chain's volumes
@@ -763,7 +763,7 @@
if volParams['volFormat'] != volume.COW_FORMAT or \
volParams['prealloc'] != volume.SPARSE_VOL:
raise se.IncorrectFormat(self)
- volParams['apparentsize'] = self.__chainSizeCalc(
+ volParams['apparentsize'] = self.chainSizeCalc(
sdUUID, srcImgUUID, srcVolUUID, volParams['size'])
# Find out dest volume parameters
--
To view, visit https://gerrit.ovirt.org/38995
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id079eb5067c16f934370e42b5f4e09bbcef1512b
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: SDM: Add extendVolumeContainer API
by alitke@redhat.com
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>
7 years, 10 months
Change in vdsm[master]: SDM: isolateVolumes API
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: SDM: isolateVolumes API
......................................................................
SDM: isolateVolumes API
Change-Id: I9b67e2df82afba9956e8246c1a4f9093aed729f2
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
M vdsm/storage/sdm/volumestore.py
M vdsm/storage/storage_exception.py
10 files changed, 134 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/79/40379/1
diff --git a/client/vdsClient.py b/client/vdsClient.py
index 633d1b7..d3c482e 100755
--- a/client/vdsClient.py
+++ b/client/vdsClient.py
@@ -1963,6 +1963,17 @@
else:
return status['status']['code'], status['status']['message']
+ def isolateVolumes(self, args):
+ if len(args) != 4:
+ raise ValueError('Wrong number of arguments')
+ sdUUID, srcImgUUID, dstImgUUID, volStr = args
+ volList = volStr.split(',')
+ status = self.s.isolateVolumes(sdUUID, srcImgUUID, dstImgUUID, volList)
+ if status['status']['code'] == 0:
+ return 0, ''
+ else:
+ return status['status']['code'], status['status']['message']
+
if __name__ == '__main__':
if _glusterEnabled:
@@ -2855,6 +2866,12 @@
'<sdUUID> <imgUUID> <volUUID>',
'Extend a thinly-provisioned block volume.'
)),
+ 'isolateVolumes': (
+ serv.isolateVolumes, (
+ '<sdUUID> <srcImgUUID> <dstImgUUID> <volUUID>[...,<volUUID>]',
+ 'Isolate volumes from one image into a new image for '
+ 'post-processing.'
+ )),
}
if _glusterEnabled:
commands.update(ge.getGlusterCmdDict(serv))
diff --git a/vdsm/API.py b/vdsm/API.py
index a50025f..44dddb4 100644
--- a/vdsm/API.py
+++ b/vdsm/API.py
@@ -1049,6 +1049,10 @@
def validate(self):
return self._irs.validateStorageDomain(self._UUID)
+ def isolateVolumes(self, srcImageID, dstImageID, volumeList):
+ return self._irs.isolateVolumes(self._UUID, srcImageID, dstImageID,
+ volumeList)
+
class StoragePool(APIBase):
ctorArgs = ['storagepoolID']
diff --git a/vdsm/rpc/BindingXMLRPC.py b/vdsm/rpc/BindingXMLRPC.py
index 7834e83..0e1e5e4 100644
--- a/vdsm/rpc/BindingXMLRPC.py
+++ b/vdsm/rpc/BindingXMLRPC.py
@@ -1000,6 +1000,10 @@
api = API.Global()
return api.extendVolumeContainer(sdUUID, imgUUID, volUUID, size)
+ def isolateVolumes(self, sdUUID, srcImgUUID, dstImgUUID, volumeList):
+ api = API.StorageDomain(sdUUID)
+ return api.isolateVolumes(srcImgUUID, dstImgUUID, volumeList)
+
def getGlobalMethods(self):
return ((self.vmDestroy, 'destroy'),
(self.vmCreate, 'create'),
@@ -1151,7 +1155,8 @@
'storageServer_ConnectionRefs_statuses'),
(self.volumeCreateContainer, 'createVolumeContainer'),
(self.copyData, 'copyData'),
- (self.extendVolumeContainer, 'extendVolumeContainer'))
+ (self.extendVolumeContainer, 'extendVolumeContainer'),
+ (self.isolateVolumes, 'isolateVolumes'))
def wrapApiMethod(f):
diff --git a/vdsm/rpc/vdsmapi-schema.json b/vdsm/rpc/vdsmapi-schema.json
index c0d8caf..a873d22 100644
--- a/vdsm/rpc/vdsmapi-schema.json
+++ b/vdsm/rpc/vdsmapi-schema.json
@@ -5459,6 +5459,25 @@
{'command': {'class': 'StorageDomain', 'name': 'validate'},
'data': {'storagedomainID': 'UUID'}}
+##
+# @StorageDomain.isolateVolumes:
+#
+# Isolate volumes from one image into a new image.
+#
+# @storagedomainID: The UUID of the Storage Domain
+#
+# @srcImageID: The UUID of the Image containing the volumes
+#
+# @dstImageID: The UUID of the destination Image
+#
+# @volumeList: Identifies a set of volumes to move
+#
+# Since: 4.18.0
+##
+{'command': {'class': 'StorageDomain', 'name': 'isolateVolumes'},
+ 'data': {'storagedomainID': 'UUID', 'srcImageID': 'UUID',
+ 'dstImageID': 'UUID', 'volumeList': ['UUID']}}
+
## Category: @StoragePool #####################################################
##
# @StoragePool:
diff --git a/vdsm/storage/hsm.py b/vdsm/storage/hsm.py
index 63c9b3b..746423d 100644
--- a/vdsm/storage/hsm.py
+++ b/vdsm/storage/hsm.py
@@ -3760,3 +3760,13 @@
misc.validateUUID(volUUID, 'volUUID')
vars.task.getSharedLock(STORAGE, sdUUID)
return sdm.extendVolumeContainer(dom, imgUUID, volUUID, size)
+
+ @public
+ def isolateVolumes(self, sdUUID, srcImgUUID, dstImgUUID, volumeList):
+ vars.task.setDefaultException(
+ se.IsolateVolumesError(sdUUID, srcImgUUID, dstImgUUID, volumeList))
+ dom = sdCache.produce(sdUUID=sdUUID)
+ misc.validateUUID(srcImgUUID, 'srcImgUUID')
+ misc.validateUUID(dstImgUUID, 'dstImgUUID')
+ vars.task.getSharedLock(STORAGE, sdUUID)
+ return sdm.isolateVolumes(dom, srcImgUUID, dstImgUUID, volumeList)
diff --git a/vdsm/storage/sdm/__init__.py b/vdsm/storage/sdm/__init__.py
index da1e858..1636e63 100644
--- a/vdsm/storage/sdm/__init__.py
+++ b/vdsm/storage/sdm/__init__.py
@@ -208,3 +208,21 @@
domain.releaseClusterLock()
if cbFn:
cbFn(cbData)
+
+
+def isolateVolumes(domain, srcImgUUID, dstImgUUID, volumeList):
+ cls = __getStoreClass(domain)
+ imageResourcesNamespace = sd.getNamespace(domain.sdUUID, IMAGE_NAMESPACE)
+
+ hostId = getDomainHostId(domain.sdUUID)
+ domain.acquireClusterLock(hostId)
+ try:
+ with nested(rmanager.acquireResource(imageResourcesNamespace,
+ srcImgUUID,
+ rm.LockType.exclusive),
+ rmanager.acquireResource(imageResourcesNamespace,
+ dstImgUUID,
+ rm.LockType.exclusive)):
+ cls.isolateVolumes(domain, srcImgUUID, dstImgUUID, volumeList)
+ finally:
+ domain.releaseClusterLock()
diff --git a/vdsm/storage/sdm/blockstore.py b/vdsm/storage/sdm/blockstore.py
index 06a77e4..bd9d3f1 100644
--- a/vdsm/storage/sdm/blockstore.py
+++ b/vdsm/storage/sdm/blockstore.py
@@ -101,6 +101,18 @@
return newName
@classmethod
+ def _isolateVolume(cls, dom, srcImgUUID, dstImgUUID, vol):
+ pVolUUID = vol.getParent()
+ toAdd = [blockVolume.TAG_PREFIX_PARENT + volume.BLANK_UUID,
+ blockVolume.TAG_PREFIX_IMAGE + dstImgUUID]
+ toDel = [blockVolume.TAG_PREFIX_PARENT + pVolUUID,
+ blockVolume.TAG_PREFIX_IMAGE + srcImgUUID]
+ lvm.changeLVTags(dom.sdUUID, vol.volUUID, addTags=toAdd, delTags=toDel)
+ if pVolUUID and pVolUUID != volume.BLANK_UUID:
+ pVol = dom.produceVolume(srcImgUUID, pVolUUID)
+ cls.recheckIfLeaf(pVol)
+
+ @classmethod
def _getGCVolumes(cls, dom, onlyImg, onlyVol):
lvs = lvm.getLV(dom.sdUUID)
vols = []
diff --git a/vdsm/storage/sdm/filestore.py b/vdsm/storage/sdm/filestore.py
index 3e99ee9..9385d87 100644
--- a/vdsm/storage/sdm/filestore.py
+++ b/vdsm/storage/sdm/filestore.py
@@ -98,6 +98,21 @@
return newName
@classmethod
+ def _isolateVolume(cls, dom, srcImgUUID, dstImgUUID, vol):
+ srcImgPath = os.path.join(dom.getRepoPath(), dom.sdUUID,
+ sd.DOMAIN_IMAGES, srcImgUUID)
+ dstImgPath = os.path.join(dom.getRepoPath(), dom.sdUUID,
+ sd.DOMAIN_IMAGES, dstImgUUID)
+ pUUID = vol.getParent()
+ vol._share(dstImgPath)
+ dstVol = dom.produceVolume(dstImgUUID, vol.volUUID)
+ dstVol.setParent(volume.BLANK_UUID)
+ dstVol.setImage(dstImgUUID)
+ newName = cls._beginRemoveVolume(dom, srcImgPath, vol.volUUID)
+ volInfo = volumestore.GCVol(newName, vol.volUUID, srcImgUUID, pUUID)
+ cls._garbageCollectVolume(dom, volInfo)
+
+ @classmethod
def _getGCVolumes(cls, dom, onlyImg, onlyVol):
vols = []
volPaths = []
diff --git a/vdsm/storage/sdm/volumestore.py b/vdsm/storage/sdm/volumestore.py
index d518f20..6568d75 100644
--- a/vdsm/storage/sdm/volumestore.py
+++ b/vdsm/storage/sdm/volumestore.py
@@ -355,3 +355,27 @@
newName = cls._beginRemoveVolume(dom, imageDir, volUUID)
volInfo = GCVol(newName, volUUID, imgUUID, pUUID)
cls._garbageCollectVolume(dom, volInfo)
+
+ @classmethod
+ def isolateVolumes(cls, dom, srcImgUUID, dstImgUUID, volumeList):
+ repoPath = dom.getRepoPath()
+ # Create dest image
+ cls.createImage(repoPath, dom.sdUUID, dstImgUUID)
+ # Verify dest image contains only volumes in volumeList
+ uuidList = cls.volClass.getImageVolumes(repoPath, dom.sdUUID,
+ dstImgUUID)
+ extraVols = set(uuidList) - set(volumeList)
+ if extraVols:
+ log.error("Destination image contains unexpected volumes: %s",
+ extraVols)
+ raise se.IsolateVolumesError(dom.sdUUID, srcImgUUID,
+ dstImgUUID, volumeList)
+ # Iterate over volumes in volumeList
+ for volUUID in volumeList:
+ try:
+ vol = cls.volClass(repoPath, dom.sdUUID, srcImgUUID, volUUID)
+ except se.VolumeDoesNotExist:
+ log.debug("Skipping non-existent source volume %s", volUUID)
+ continue
+ vol.validateDelete()
+ cls._isolateVolume(dom, srcImgUUID, dstImgUUID, vol)
diff --git a/vdsm/storage/storage_exception.py b/vdsm/storage/storage_exception.py
index 1cfc8e4..a695cf4 100644
--- a/vdsm/storage/storage_exception.py
+++ b/vdsm/storage/storage_exception.py
@@ -453,6 +453,15 @@
message = "Image does not exist in domain"
+class IsolateVolumesError(StorageException):
+ def __init__(self, sdUUID, srcImgUUID, dstImgUUID, volumeList):
+ self.value = ("domain=%s srcImg=%s dstImg=%s "
+ "volumes=%s" % (sdUUID, srcImgUUID, dstImgUUID,
+ volumeList))
+ code = 269
+ message = "Unable to isolate volumes"
+
+
#################################################
# Pool Exceptions
#################################################
--
To view, visit https://gerrit.ovirt.org/40379
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9b67e2df82afba9956e8246c1a4f9093aed729f2
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: HACK: run GC in domain monitor
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: HACK: run GC in domain monitor
......................................................................
HACK: run GC in domain monitor
Change-Id: I3c560e6fbccdf50b135cc9c90b23824ae04b0376
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/monitor.py
M vdsm/storage/sdm/__init__.py
2 files changed, 22 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/80/40380/1
diff --git a/vdsm/storage/monitor.py b/vdsm/storage/monitor.py
index ef032a5..a2a1bd7 100644
--- a/vdsm/storage/monitor.py
+++ b/vdsm/storage/monitor.py
@@ -28,6 +28,7 @@
from . import clusterlock
from . import misc
+from . import sdm
from .sdc import sdCache
@@ -249,6 +250,7 @@
self._performDomainSelftest()
self._checkReadDelay()
self._collectStatistics()
+ self._garbageCollect()
except Exception as e:
self.log.exception("Error monitoring domain %s", self.sdUUID)
self.nextStatus.error = e
@@ -340,6 +342,14 @@
self.nextStatus.isoPrefix = self.isoPrefix
self.nextStatus.version = self.domain.getVersion()
+ def _garbageCollect(self):
+ if True: # XXX: limit this to domain ver 4 or later
+ try:
+ sdm.garbageCollectStorageDomain(self.domain)
+ except:
+ self.log.exception("Garbage collection failed for domain %s",
+ self.domain.sdUUID)
+
# Managing host id
def _shouldAcquireHostId(self):
diff --git a/vdsm/storage/sdm/__init__.py b/vdsm/storage/sdm/__init__.py
index 1636e63..4a31332 100644
--- a/vdsm/storage/sdm/__init__.py
+++ b/vdsm/storage/sdm/__init__.py
@@ -226,3 +226,15 @@
cls.isolateVolumes(domain, srcImgUUID, dstImgUUID, volumeList)
finally:
domain.releaseClusterLock()
+
+
+def garbageCollectStorageDomain(domain):
+ if domain.isISO():
+ return
+ cls = __getStoreClass(domain)
+ hostId = getDomainHostId(domain.sdUUID)
+ domain.acquireClusterLock(hostId)
+ try:
+ cls.garbageCollectStorageDomain(domain)
+ finally:
+ domain.releaseClusterLock()
--
To view, visit https://gerrit.ovirt.org/40380
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I3c560e6fbccdf50b135cc9c90b23824ae04b0376
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: Add wipeVolume API
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: Add wipeVolume API
......................................................................
Add wipeVolume API
Change-Id: I572099e1fee4ae847de95e27973405ba90b56a9b
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
6 files changed, 70 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/28/40628/1
diff --git a/client/vdsClient.py b/client/vdsClient.py
index bac146e..c267b75 100755
--- a/client/vdsClient.py
+++ b/client/vdsClient.py
@@ -1973,6 +1973,16 @@
else:
return status['status']['code'], status['status']['message']
+ def wipeVolume(self, args):
+ if len(args) != 3:
+ raise ValueError('Wrong number of arguments')
+ sdUUID, imgUUID, volUUID = args
+ status = self.s.wipeVolume(sdUUID, imgUUID, volUUID)
+ if status['status']['code'] == 0:
+ return 0, ''
+ else:
+ return status['status']['code'], status['status']['message']
+
if __name__ == '__main__':
if _glusterEnabled:
diff --git a/vdsm/API.py b/vdsm/API.py
index 6adefa8..a49448b 100644
--- a/vdsm/API.py
+++ b/vdsm/API.py
@@ -840,6 +840,9 @@
return self._irs.setVolumeLegality(self._sdUUID, self._spUUID,
self._imgUUID, self._UUID, legality)
+ def wipe(self):
+ return self._irs.wipeVolume(self._sdUUID, self._imgUUID, self._UUID)
+
class Image(APIBase):
ctorArgs = ['imageID', 'storagepoolID', 'storagedomainID']
diff --git a/vdsm/rpc/BindingXMLRPC.py b/vdsm/rpc/BindingXMLRPC.py
index f110468..270f7a1 100644
--- a/vdsm/rpc/BindingXMLRPC.py
+++ b/vdsm/rpc/BindingXMLRPC.py
@@ -1004,6 +1004,10 @@
api = API.StorageDomain(sdUUID)
return api.isolateVolume(srcImgUUID, dstImgUUID, volumeID)
+ def wipeVolume(self, sdUUID, imgUUID, volUUID):
+ volume = API.Volume(volUUID, None, sdUUID, imgUUID)
+ return volume.wipe()
+
def getGlobalMethods(self):
return ((self.vmDestroy, 'destroy'),
(self.vmCreate, 'create'),
@@ -1156,7 +1160,8 @@
(self.volumeCreateContainer, 'createVolumeContainer'),
(self.copyData, 'copyData'),
(self.extendVolumeContainer, 'extendVolumeContainer'),
- (self.isolateVolume, 'isolateVolume'))
+ (self.isolateVolume, 'isolateVolume'),
+ (self.wipeVolume, 'wipeVolume'))
def wrapApiMethod(f):
diff --git a/vdsm/rpc/vdsmapi-schema.json b/vdsm/rpc/vdsmapi-schema.json
index a76db8d..80b1968 100644
--- a/vdsm/rpc/vdsmapi-schema.json
+++ b/vdsm/rpc/vdsmapi-schema.json
@@ -8312,6 +8312,22 @@
'legality': 'VolumeLegality'}}
##
+# @Volume.wipe:
+#
+# Wipe a volume to ensure previous data is erased.
+#
+# @volumeID: The UUID of the Volume
+#
+# @storagedomainID: The Storage Domain associated with the Volume
+#
+# @imageID: The Image associated with the Volume
+#
+# Since: 4.18.0
+##
+{'command': {'class': 'Volume', 'name': 'wipe'},
+ 'data': {'volumeID': 'UUID', 'storagedomainID': 'UUID', 'imageID': 'UUID'}}
+
+##
# @VM.setNumberOfCpus:
#
# Set the number CPUs for a VM
diff --git a/vdsm/storage/hsm.py b/vdsm/storage/hsm.py
index 432c8f9..cb51cd1 100644
--- a/vdsm/storage/hsm.py
+++ b/vdsm/storage/hsm.py
@@ -3776,3 +3776,16 @@
misc.validateUUID(dstImgUUID, 'dstImgUUID')
vars.task.getSharedLock(STORAGE, sdUUID)
return sdm.isolateVolume(dom, srcImgUUID, dstImgUUID, volumeID)
+
+ @public
+ def wipeVolume(self, sdUUID, imgUUID, volUUID):
+ argsStr = ("sdUUID=%s, imgUUID=%s, volUUID=%s" %
+ (sdUUID, imgUUID, volUUID))
+ vars.task.setDefaultException(se.VolumesZeroingError(argsStr))
+ misc.validateUUID(imgUUID, 'imgUUID')
+ misc.validateUUID(volUUID, 'volUUID')
+ vars.task.getSharedLock(STORAGE, sdUUID)
+ imageSpec = {'sdUUID': sdUUID, 'imgUUID': imgUUID, 'volUUID': volUUID}
+ with sdm.prepareCopy(imageSpec, False, 0, True):
+ self._sdmSchedule('wipeVolume', sdm.wipeVolume, sdUUID, imgUUID,
+ volUUID)
diff --git a/vdsm/storage/sdm/__init__.py b/vdsm/storage/sdm/__init__.py
index 4738813..2113d26 100644
--- a/vdsm/storage/sdm/__init__.py
+++ b/vdsm/storage/sdm/__init__.py
@@ -22,6 +22,7 @@
from contextlib import contextmanager, nested
from .. import image
+from .. import misc
from .. import resourceManager as rm
from .. import sd
from .. import storage_exception as se
@@ -234,6 +235,27 @@
domain.releaseClusterLock()
+def wipeVolume(sdUUID, imgUUID, volUUID):
+ dom = sdCache.produce(sdUUID)
+ imageResourcesNamespace = sd.getNamespace(sdUUID, IMAGE_NAMESPACE)
+ try:
+ with rmanager.acquireResource(imageResourcesNamespace,
+ imgUUID, rm.LockType.exclusive):
+ vol = dom.produceVolume(imgUUID, volUUID)
+ size = vol.getVolumeSize(bs=1)
+ vol_path = vol.getVolumePath()
+ vol.prepare(rw=True, justme=True)
+ try:
+ vol.setLegality(volume.ILLEGAL_VOL)
+ misc.ddWatchCopy(
+ "/dev/zero", vol_path, vars.task.aborting, int(size))
+ finally:
+ vol.tearDown(imgUUID, volUUID)
+ vol.setLegality(volume.LEGAL_VOL)
+ finally:
+ dom.releaseVolumeLease(imgUUID, volUUID)
+
+
def garbageCollectStorageDomain(domain):
if domain.isISO():
return
--
To view, visit https://gerrit.ovirt.org/40628
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I572099e1fee4ae847de95e27973405ba90b56a9b
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: api: support multiple 'ipv6addrs' per network and deprecate ...
by osvoboda@redhat.com
Ondřej Svoboda has uploaded a new change for review.
Change subject: api: support multiple 'ipv6addrs' per network and deprecate 'ipv6addr'
......................................................................
api: support multiple 'ipv6addrs' per network and deprecate 'ipv6addr'
Next, the 'ipv6' hook will be extended to understand a whitespace-separated
list of IPv6 addresses and also testStaticNetworkConfig will learn to pass
multiple IPv6 addresses to VDSM.
IPv4 support is planned but needs discussion (should we accept netmasks or
just CIDR notation?).
Change-Id: I8d16083781919fe5e5d9c75e9dd4ab744afe45be
Signed-off-by: Ondřej Svoboda <osvoboda(a)redhat.com>
---
M vdsm/network/api.py
M vdsm/network/configurators/ifcfg.py
M vdsm/network/configurators/iproute2.py
M vdsm/network/configurators/pyroute_two.py
M vdsm/network/models.py
5 files changed, 45 insertions(+), 24 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/07/39307/1
diff --git a/vdsm/network/api.py b/vdsm/network/api.py
index de92fbf..096f89f 100755
--- a/vdsm/network/api.py
+++ b/vdsm/network/api.py
@@ -82,7 +82,7 @@
def _objectivizeNetwork(bridge=None, vlan=None, bonding=None,
bondingOptions=None, nics=None, mtu=None, ipaddr=None,
netmask=None, gateway=None, bootproto=None,
- ipv6addr=None, ipv6gateway=None, ipv6autoconf=None,
+ ipv6addrs=None, ipv6gateway=None, ipv6autoconf=None,
dhcpv6=None, defaultRoute=None, _netinfo=None,
configurator=None, blockingdhcp=None,
implicitBonding=None, opts=None):
@@ -100,7 +100,7 @@
:param netmask: IPv4 mask in dotted decimal format.
:param gateway: IPv4 address in dotted decimal format.
:param bootproto: protocol for getting IP config for the net, e.g., 'dhcp'
- :param ipv6addr: IPv6 address in format address[/prefixlen].
+ :param ipv6addrs: list of IPv6 addresses in the format address[/prefixlen]
:param ipv6gateway: IPv6 address in format address[/prefixlen].
:param ipv6autoconf: whether to use IPv6's stateless autoconfiguration.
:param dhcpv6: whether to use DHCPv6.
@@ -162,7 +162,7 @@
raise ConfigNetworkError(ne.ERR_BAD_PARAMS, 'Network defined without '
'devices.')
ipv4 = IPv4(ipaddr, netmask, gateway, defaultRoute)
- ipv6 = IPv6(ipv6addr, ipv6gateway, defaultRoute)
+ ipv6 = IPv6(ipv6addrs, ipv6gateway, defaultRoute)
blockingdhcp = configurator._inRollback or utils.tobool(blockingdhcp)
topNetDev.ipconfig = IpConfig(
ipv4=ipv4, ipv6=ipv6, bootproto=bootproto, blockingdhcp=blockingdhcp,
@@ -249,6 +249,7 @@
@_alterRunningConfig
def _addNetwork(network, vlan=None, bonding=None, nics=None, ipaddr=None,
netmask=None, prefix=None, mtu=None, gateway=None, dhcpv6=None,
+ ipv6addrs=None,
ipv6addr=None, ipv6gateway=None, ipv6autoconf=None,
force=False, configurator=None, bondingOptions=None,
bridged=True, _netinfo=None, hostQos=None,
@@ -279,6 +280,12 @@
except ValueError as ve:
raise ConfigNetworkError(ne.ERR_BAD_ADDR, "Bad prefix: %s" %
ve)
+ if ipv6addr:
+ if ipv6addrs:
+ raise ConfigNetworkError(ne.ERR_BAD_PARAMS,
+ 'Both ipv6addrs and ipv6addr supplied')
+ else:
+ ipv6addrs = [ipv6addr]
if not utils.tobool(force):
logging.debug('validating network...')
@@ -312,7 +319,8 @@
bridge=network if bridged else None, vlan=vlan, bonding=bonding,
bondingOptions=bondingOptions, nics=nics, mtu=mtu, ipaddr=ipaddr,
netmask=netmask, gateway=gateway, bootproto=bootproto, dhcpv6=dhcpv6,
- blockingdhcp=blockingdhcp, ipv6addr=ipv6addr, ipv6gateway=ipv6gateway,
+ blockingdhcp=blockingdhcp, ipv6addrs=ipv6addrs,
+ ipv6gateway=ipv6gateway,
ipv6autoconf=ipv6autoconf, defaultRoute=defaultRoute,
_netinfo=_netinfo, configurator=configurator, opts=options)
@@ -783,7 +791,8 @@
netmask="<ipv4>"
gateway="<ipv4>"
bootproto="..."
- ipv6addr="<ipv6>[/<prefixlen>]"
+ ipv6addrs=["<ipv6>[/<prefixlen>]", ...]
+ ipv6addr="<ipv6>[/<prefixlen>]" (deprecated)
ipv6gateway="<ipv6>"
ipv6autoconf="0|1"
dhcpv6="0|1"
diff --git a/vdsm/network/configurators/ifcfg.py b/vdsm/network/configurators/ifcfg.py
index 866ade8..f228306 100644
--- a/vdsm/network/configurators/ifcfg.py
+++ b/vdsm/network/configurators/ifcfg.py
@@ -584,10 +584,13 @@
if ipv4.defaultRoute is not None:
cfg += 'DEFROUTE=%s\n' % _to_ifcfg_bool(ipv4.defaultRoute)
cfg += 'NM_CONTROLLED=no\n'
- if ipv6.address or ipconfig.ipv6autoconf or ipconfig.dhcpv6:
+ if ipv6.addresses or ipconfig.ipv6autoconf or ipconfig.dhcpv6:
cfg += 'IPV6INIT=yes\n'
- if ipv6.address is not None:
- cfg += 'IPV6ADDR=%s\n' % pipes.quote(ipv6.address)
+ if ipv6.addresses:
+ cfg += 'IPV6ADDR=%s\n' % pipes.quote(ipv6.addresses[0])
+ if len(ipv6.addresses) > 1:
+ cfg += 'IPV6ADDR_SECONDARIES=%s\n' % pipes.quote(
+ ' '.join(ipv6.addresses[1:]))
if ipv6.gateway is not None:
cfg += 'IPV6_DEFAULTGW=%s\n' % pipes.quote(ipv6.gateway)
elif ipconfig.dhcpv6:
@@ -701,7 +704,10 @@
if ipv4.defaultRoute is None and confParams.get('DEFROUTE'):
ipv4.defaultRoute = _from_ifcfg_bool(confParams['DEFROUTE'])
if confParams.get('IPV6INIT') == 'yes':
- ipv6.address = confParams.get('IPV6ADDR')
+ ipv6addr = confParams.get('IPV6ADDR')
+ ipv6addrs = confParams.get('IPV6ADDR_SECONDARIES')
+ ipv6.addresses = ([ipv6addr] if ipv6addr else []) + (
+ ipv6addrs.split() if ipv6addrs else [])
ipv6.gateway = confParams.get('IPV6_DEFAULTGW')
ipconfig.ipv6autoconf = (
confParams.get('IPV6_AUTOCONF') == 'yes')
diff --git a/vdsm/network/configurators/iproute2.py b/vdsm/network/configurators/iproute2.py
index 1cde1cc..045ed66 100644
--- a/vdsm/network/configurators/iproute2.py
+++ b/vdsm/network/configurators/iproute2.py
@@ -225,16 +225,17 @@
ipconfig = iface.ipconfig
ipv4 = ipconfig.ipv4
ipv6 = ipconfig.ipv6
- if ipv4.address or ipv6.address:
+ if ipv4.address or ipv6.addresses:
self.removeIpConfig(iface)
if ipv4.address:
ipwrapper.addrAdd(iface.name, ipv4.address,
ipv4.netmask)
if ipv4.gateway and ipv4.defaultRoute:
ipwrapper.routeAdd(['default', 'via', ipv4.gateway])
- if ipv6.address:
- ipv6addr, ipv6netmask = ipv6.address.split('/')
- ipwrapper.addrAdd(iface.name, ipv6addr, ipv6netmask, family=6)
+ if ipv6.addresses:
+ for address in ipv6.addresses:
+ ipv6addr, ipv6netmask = address.split('/')
+ ipwrapper.addrAdd(iface.name, ipv6addr, ipv6netmask, family=6)
if ipv6.gateway:
ipwrapper.routeAdd(['default', 'via', ipv6.gateway],
dev=iface.name, family=6)
diff --git a/vdsm/network/configurators/pyroute_two.py b/vdsm/network/configurators/pyroute_two.py
index 221e142..a6833d5 100644
--- a/vdsm/network/configurators/pyroute_two.py
+++ b/vdsm/network/configurators/pyroute_two.py
@@ -59,7 +59,7 @@
ipconfig = iface.ipconfig
ipv4 = ipconfig.ipv4
ipv6 = ipconfig.ipv6
- if ipv4.address or ipv6.address:
+ if ipv4.address or ipv6.addresses:
self.removeIpConfig(iface)
if ipv4.address:
with self.ip.interfaces[iface.name] as i:
@@ -67,9 +67,10 @@
if ipv4.gateway and ipv4.defaultRoute:
self.ip.routes.add({'dst': 'default',
'gateway': ipv4.gateway}).commit()
- if ipv6.address:
+ if ipv6.addresses:
with self.ip.interfaces[iface.name] as i:
- i.add_ip(ipv6.address)
+ for address in ipv6.addresses:
+ i.add_ip(address)
if ipv6.gateway:
self.ip.routes.add({'dst': 'default',
'gateway': ipv6.gateway}).commit()
diff --git a/vdsm/network/models.py b/vdsm/network/models.py
index 1d34c88..2c577de 100644
--- a/vdsm/network/models.py
+++ b/vdsm/network/models.py
@@ -410,20 +410,24 @@
class IPv6(object):
- def __init__(self, address=None, gateway=None, defaultRoute=None):
- if address:
- self.validateAddress(address)
+ def __init__(self, addresses=None, gateway=None, defaultRoute=None):
+ if addresses:
+ # TODO: Do we want this?
+ # if isinstance(addresses, str):
+ # addresses = addresses.split()
+ for address in addresses:
+ self.validateAddress(address)
if gateway:
self.validateGateway(gateway)
elif gateway:
raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Specified gateway but '
'not ip address.')
- self.address = address
+ self.addresses = addresses
self.gateway = gateway
self.defaultRoute = defaultRoute
def __repr__(self):
- return 'IPv6(%s, %s, %s)' % (self.address, self.gateway,
+ return 'IPv6(%s, %s, %s)' % (self.addresses, self.gateway,
self.defaultRoute)
@classmethod
@@ -465,7 +469,7 @@
if ipv6 is None:
ipv6 = IPv6()
if (ipv4.address and bootproto == 'dhcp') or \
- (ipv6.address and (ipv6autoconf or dhcpv6)):
+ (ipv6.addresses and (ipv6autoconf or dhcpv6)):
raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Static and dynamic IP '
'configurations are mutually exclusive.')
self.ipv4 = ipv4
@@ -477,8 +481,8 @@
def __nonzero__(self):
# iproute2 and pyroute_two check that IP configuration is not empty
- return bool(self.ipv4.address or self.ipv6.address or self.bootproto or
- self.ipv6autoconf or self.dhcpv6)
+ return bool(self.ipv4.address or self.ipv6.addresses or self.bootproto
+ or self.ipv6autoconf or self.dhcpv6)
def __repr__(self):
return 'IpConfig(%r, %r, %s, %s, %s)' % (self.ipv4, self.ipv6,
--
To view, visit https://gerrit.ovirt.org/39307
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8d16083781919fe5e5d9c75e9dd4ab744afe45be
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Ondřej Svoboda <osvoboda(a)redhat.com>
7 years, 10 months
Change in vdsm[master]: contrib: Add tool for getting hsm stats
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: contrib: Add tool for getting hsm stats
......................................................................
contrib: Add tool for getting hsm stats
This is a very simple tool showing historgam of hsm calls in the given
log files.
Example:
$ hsmstat vdsm.log.1 vdsm.log.2
8515 getVolumeSize
2258 repoStats
51 getVolumeInfo
30 getVolumesList
19 teardownImage
...
We can get same output or better using logdb by writing some smart sql.
Change-Id: Ia6013da71ca96535b98edace8577a4d8bb529465
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
A contrib/hsmstat
1 file changed, 39 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/48/38748/1
diff --git a/contrib/hsmstat b/contrib/hsmstat
new file mode 100755
index 0000000..d3400d2
--- /dev/null
+++ b/contrib/hsmstat
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+from collections import defaultdict
+import fileinput
+import re
+
+verb_re = re.compile(r" Run and protect: (?P<name>.+)\(")
+
+verbs = defaultdict(int)
+
+for line in fileinput.input():
+ match = verb_re.search(line)
+ if match:
+ name = match.group('name')
+ verbs[name] += 1
+
+histogram = sorted(((count, verb) for verb, count in verbs.iteritems()),
+ reverse=True)
+for count, name in histogram:
+ print " %6d %s" % (count, name)
--
To view, visit https://gerrit.ovirt.org/38748
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia6013da71ca96535b98edace8577a4d8bb529465
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
7 years, 10 months