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 <[email protected]>
---
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 <[email protected]>
_______________________________________________
vdsm-patches mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches

Reply via email to