Federico Simoncelli has uploaded a new change for review.

Change subject: sp: refactor out the metadata access from StoragePool
......................................................................

sp: refactor out the metadata access from StoragePool

Change-Id: I75493d1db60e51cccd5231b516f963c970d24c99
Signed-off-by: Federico Simoncelli <fsimo...@redhat.com>
---
M debian/vdsm.install
M vdsm.spec.in
M vdsm/storage/Makefile.am
M vdsm/storage/hsm.py
M vdsm/storage/sp.py
A vdsm/storage/spbackends.py
6 files changed, 375 insertions(+), 280 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/32/22132/1

diff --git a/debian/vdsm.install b/debian/vdsm.install
index ef46ed3..43ffd5a 100644
--- a/debian/vdsm.install
+++ b/debian/vdsm.install
@@ -113,6 +113,7 @@
 ./usr/share/vdsm/storage/sdc.py
 ./usr/share/vdsm/storage/securable.py
 ./usr/share/vdsm/storage/sp.py
+./usr/share/vdsm/storage/spbackends.py
 ./usr/share/vdsm/storage/storageConstants.py
 ./usr/share/vdsm/storage/storageServer.py
 ./usr/share/vdsm/storage/storage_exception.py
diff --git a/vdsm.spec.in b/vdsm.spec.in
index f248b74..2a70661 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -926,6 +926,7 @@
 %{_datadir}/%{vdsm_name}/storage/sd.py*
 %{_datadir}/%{vdsm_name}/storage/securable.py*
 %{_datadir}/%{vdsm_name}/storage/sp.py*
+%{_datadir}/%{vdsm_name}/storage/spbackends.py*
 %{_datadir}/%{vdsm_name}/storage/storageConstants.py*
 %{_datadir}/%{vdsm_name}/storage/storage_exception.py*
 %{_datadir}/%{vdsm_name}/storage/storage_mailbox.py*
diff --git a/vdsm/storage/Makefile.am b/vdsm/storage/Makefile.am
index 2d3e9a6..09b40ad 100644
--- a/vdsm/storage/Makefile.am
+++ b/vdsm/storage/Makefile.am
@@ -57,6 +57,7 @@
        sd.py \
        securable.py \
        sp.py \
+       spbackends.py \
        storageConstants.py \
        storage_exception.py \
        storage_mailbox.py \
diff --git a/vdsm/storage/hsm.py b/vdsm/storage/hsm.py
index 2ae508e..7ec4345 100644
--- a/vdsm/storage/hsm.py
+++ b/vdsm/storage/hsm.py
@@ -40,6 +40,7 @@
 
 from vdsm.config import config
 import sp
+import spbackends
 import domainMonitor
 import sd
 import blockSD
@@ -614,7 +615,7 @@
     def _getSpmStatusInfo(pool):
         return dict(
             zip(('spmStatus', 'spmLver', 'spmId'),
-                (pool.spmRole,) + pool.getSpmStatus()))
+                (pool.spmRole,) + pool.backend.getSpmStatus()))
 
     @public
     def getSpmStatus(self, spUUID, options=None):
@@ -1035,6 +1036,7 @@
                 return True
 
             pool = sp.StoragePool(spUUID, self.domainMonitor, self.taskMng)
+            pool.backend = spbackends.StoragePoolMetaBackend(pool)
 
             # Must register domain state change callbacks *before* connecting
             # the pool, which starts domain monitor threads. Otherwise we will
@@ -1820,8 +1822,8 @@
             except:
                 domDict[d] = sd.validateSDDeprecatedStatus(status)
 
-        return pool.reconstructMaster(hostId, poolName, masterDom, domDict,
-                                      masterVersion, leaseParams)
+        return pool.backend.reconstructMaster(
+            hostId, poolName, masterDom, domDict, masterVersion, leaseParams)
 
     def _logResp_getDeviceList(self, response):
         logableDevs = deepcopy(response)
diff --git a/vdsm/storage/sp.py b/vdsm/storage/sp.py
index d550652..ce60b55 100644
--- a/vdsm/storage/sp.py
+++ b/vdsm/storage/sp.py
@@ -41,7 +41,6 @@
 from vdsm.config import config
 from sdc import sdCache
 import storage_exception as se
-from persistentDict import DictValidator, unicodeEncoder, unicodeDecoder
 from remoteFileHandler import Timeout
 from securable import secured, unsecured
 import image
@@ -52,14 +51,6 @@
 import mount
 
 POOL_MASTER_DOMAIN = 'mastersd'
-
-MAX_POOL_DESCRIPTION_SIZE = 50
-
-PMDK_DOMAINS = "POOL_DOMAINS"
-PMDK_POOL_DESCRIPTION = "POOL_DESCRIPTION"
-PMDK_LVER = "POOL_SPM_LVER"
-PMDK_SPM_ID = "POOL_SPM_ID"
-PMDK_MASTER_VER = "MASTER_VERSION"
 
 rmanager = rm.ResourceManager.getInstance()
 
@@ -83,21 +74,6 @@
         k, v = domDecl.split(':')
         domList[k.strip("'")] = v.strip("'").capitalize()
     return domList
-
-SP_MD_FIELDS = {
-    # Key          dec,  enc
-    PMDK_DOMAINS: (domainListDecoder, domainListEncoder),
-    PMDK_POOL_DESCRIPTION: (unicodeDecoder, unicodeEncoder),
-    PMDK_LVER: (int, str),
-    PMDK_SPM_ID: (int, str),
-    PMDK_MASTER_VER: (int, str)
-}
-
-# Calculate how many domains can be in the pool before overflowing the Metadata
-MAX_DOMAINS = blockSD.SD_METADATA_SIZE - blockSD.METADATA_BASE_SIZE
-MAX_DOMAINS -= MAX_POOL_DESCRIPTION_SIZE + sd.MAX_DOMAIN_DESCRIPTION_SIZE
-MAX_DOMAINS -= blockSD.PVS_METADATA_SIZE
-MAX_DOMAINS /= 48
 
 
 @secured
@@ -130,65 +106,14 @@
         self.domainMonitor = domainMonitor
         self._upgradeCallback = partial(StoragePool._upgradePoolDomain,
                                         proxy(self))
+        self.backend = None
 
     def isSafe(self):
         return self._safety.isSet()
 
-    @unsecured
-    def getSpmStatus(self):
-        try:
-            # XXX: in case the host id is not acquired yet we won't be
-            # able to get the spm id (I should verify this) and the code
-            # below would return SPM_ID_FREE. If that's the case and this
-            # new behavior introduces a problem then we should prepend:
-            #   self.masterDomain.acquireHostId(self.id)
-            # that could take a long time.
-            lVer, spmId = self.masterDomain.inquireClusterLock()
-            lVer, spmId = lVer or LVER_INVALID, spmId or SPM_ID_FREE
-        except NotImplementedError:
-            poolMeta = self._getPoolMD(self.masterDomain)
-
-            # if we claim that we were the SPM (but we're currently not) we
-            # have to make sure that we're not returning stale data
-            if (poolMeta[PMDK_SPM_ID] == self.id
-                    and not self.spmRole == SPM_ACQUIRED):
-                self.invalidateMetadata()
-                poolMeta = self._getPoolMD(self.masterDomain)
-
-            lVer, spmId = poolMeta[PMDK_LVER], poolMeta[PMDK_SPM_ID]
-
-        return lVer, spmId
-
-    def setSpmStatus(self, lVer=None, spmId=None):
-        self.invalidateMetadata()
-        metaParams = dict(filter(lambda (k, v): v is not None,
-                          ((PMDK_LVER, lVer), (PMDK_SPM_ID, spmId))))
-        # this method must be secured (as it changes the pool metadata),
-        # but since it is also used during the SPM status transition by
-        # default we override the security for setMetaParams.
-        # NOTE: this introduces a race when the method is used in the
-        # secured mode, but generally you shouldn't need to call this at
-        # any time.
-        self.setMetaParams(metaParams, __securityOverride=True)
-
-    @unsecured
-    def getDomainsMap(self):
-        self.invalidateMetadata()
-        return self.getMetaParam(PMDK_DOMAINS)
-
-    def setDomainsMap(self, domains):
-        self.setMetaParam(PMDK_DOMAINS, domains)
-
     def __del__(self):
         if len(self.domainMonitor.poolMonitoredDomains) > 0:
             threading.Thread(target=self.stopMonitoringDomains).start()
-
-    @unsecured
-    def forceFreeSpm(self):
-        # DO NOT USE, STUPID, HERE ONLY FOR BC
-        # TODO: SCSI Fence the 'lastOwner'
-        self.setSpmStatus(LVER_INVALID, SPM_ID_FREE)
-        self.spmRole = SPM_FREE
 
     def _upgradePoolDomain(self, sdUUID, isValid):
         # This method is called everytime the onDomainStateChange
@@ -260,7 +185,7 @@
                 continue
 
             try:
-                self.setDomainMasterRole(domain, sd.REGULAR_DOMAIN, 0)
+                self.backend.setDomainMasterRole(domain, sd.REGULAR_DOMAIN, 0)
             except:
                 self.log.exception('Unable to set role for domain %s', sdUUID)
 
@@ -292,7 +217,7 @@
                 raise se.OperationInProgress("spm start %s" % self.spUUID)
 
             self.updateMonitoringThreads()
-            oldlver, oldid = self.getSpmStatus()
+            oldlver, oldid = self.backend.getSpmStatus()
             masterDomVersion = self.getVersion()
             # If no specific domain version was specified use current master
             # domain version
@@ -324,7 +249,8 @@
             try:
                 self.lver = int(oldlver) + 1
 
-                self.setSpmStatus(self.lver, self.id, __securityOverride=True)
+                self.backend.setSpmStatus(self.lver, self.id,
+                                          __securityOverride=True)
                 self._maxHostID = maxHostID
 
                 # Upgrade the master domain now if needed
@@ -460,8 +386,8 @@
 
             if not stopFailed:
                 try:
-                    self.setSpmStatus(spmId=SPM_ID_FREE,
-                                      __securityOverride=True)
+                    self.backend.setSpmStatus(spmId=SPM_ID_FREE,
+                                              __securityOverride=True)
                 except:
                     pass  # The system can handle this inconsistency
 
@@ -532,22 +458,6 @@
         # Cleanup links to domains under /rhev/datacenter/poolName
         self.refresh(msdUUID, masterVersion)
 
-    @unsecured
-    def getMasterVersion(self, useMasterDomain=None):
-        domain = (self.masterDomain
-                  if useMasterDomain is None else useMasterDomain)
-        return self._getPoolMD(domain)[PMDK_MASTER_VER]
-
-    def setDomainMasterRole(self, domain, role, masterVersion):
-        poolMeta = self._getPoolMD(domain)
-        # NOTE: the transaction here does not ensure the consistency between
-        # the domain and pool metadata. For example if the role on the domain
-        # has been changed and the pool metadata transaction fails then the
-        # domain role is not reverted to the previous value.
-        with poolMeta.transaction():
-            poolMeta[PMDK_MASTER_VER] = masterVersion
-            domain.changeRole(role)
-
     # TODO: Remove or rename this function.
     def validatePoolSD(self, sdUUID):
         if sdUUID not in self.getDomains():
@@ -564,17 +474,6 @@
             if self.spUUID not in dom.getPools():
                 raise se.StorageDomainNotInPool(self.spUUID, dom.sdUUID)
         return True
-
-    @unsecured
-    def getMaximumSupportedDomains(self):
-        msdInfo = self.masterDomain.getInfo()
-        msdType = sd.name2type(msdInfo["type"])
-        msdVersion = int(msdInfo["version"])
-        if msdType in sd.BLOCK_DOMAIN_TYPES and \
-                msdVersion in blockSD.VERS_METADATA_LV:
-            return MAX_DOMAINS
-        else:
-            return config.getint("irs", "maximum_domains_in_pool")
 
     @unsecured
     def _acquireTemporaryClusterLock(self, msdUUID, leaseParams):
@@ -805,80 +704,13 @@
             if not misc.isAscii(poolName) and not domain.supportsUnicode():
                 raise se.UnicodeArgumentException()
 
-            futurePoolMD.update({
-                PMDK_SPM_ID: SPM_ID_FREE,
-                PMDK_LVER: LVER_INVALID,
-                PMDK_MASTER_VER: masterVersion,
-                PMDK_POOL_DESCRIPTION: poolName,
-                PMDK_DOMAINS: {domain.sdUUID: sd.DOM_ACTIVE_STATUS}})
-
-    @unsecured
-    def reconstructMaster(self, hostId, poolName, msdUUID, domDict,
-                          masterVersion, leaseParams):
-        self.log.info("spUUID=%s hostId=%s poolName=%s msdUUID=%s domDict=%s "
-                      "masterVersion=%s leaseparams=(%s)", self.spUUID, hostId,
-                      poolName, msdUUID, domDict, masterVersion, leaseParams)
-
-        if msdUUID not in domDict:
-            raise se.InvalidParameterException("masterDomain", msdUUID)
-
-        futureMaster = sdCache.produce(msdUUID)
-
-        # @deprecated, domain version < 3
-        # For backward compatibility we must support a reconstructMaster
-        # that doesn't specify an hostId.
-        if not hostId:
-            self._acquireTemporaryClusterLock(msdUUID, leaseParams)
-            temporaryLock = True
-        else:
-            # Forcing to acquire the host id (if it's not acquired already).
-            futureMaster.acquireHostId(hostId)
-            futureMaster.acquireClusterLock(hostId)
-
-            # The host id must be set for createMaster(...).
-            self.id = hostId
-            temporaryLock = False
-
-        try:
-            self.createMaster(poolName, futureMaster, masterVersion,
-                              leaseParams)
-
-            for sdUUID in domDict:
-                domDict[sdUUID] = domDict[sdUUID].capitalize()
-
-            # Add domain to domain list in pool metadata.
-            self.log.info("Set storage pool domains: %s", domDict)
-            self._getPoolMD(futureMaster).update({PMDK_DOMAINS: domDict})
-
-            self.refresh(msdUUID=msdUUID, masterVersion=masterVersion)
-        finally:
-            if temporaryLock:
-                self._releaseTemporaryClusterLock(msdUUID)
-                self.stopMonitoringDomains()
-            else:
-                futureMaster.releaseClusterLock()
-
-    @unsecured
-    def copyPoolMD(self, prevMd, newMD):
-        prevPoolMD = self._getPoolMD(prevMd)
-        domains = prevPoolMD[PMDK_DOMAINS]
-        pool_descr = prevPoolMD[PMDK_POOL_DESCRIPTION]
-        lver = prevPoolMD[PMDK_LVER]
-        spmId = prevPoolMD[PMDK_SPM_ID]
-        # This is actually domain metadata, But I can't change this because of
-        # backward compatibility
-        leaseParams = prevMd.getLeaseParams()
-
-        # Now insert pool metadata into new mastersd metadata
-
-        newPoolMD = self._getPoolMD(newMD)
-        with newPoolMD.transaction():
-            newPoolMD.update({
-                PMDK_DOMAINS: domains,
-                PMDK_POOL_DESCRIPTION: pool_descr,
-                PMDK_LVER: lver,
-                PMDK_SPM_ID: spmId})
-            newMD.changeLeaseParams(leaseParams)
+# FIXME !!!!!!!!!!
+#           futurePoolMD.update({
+#               PMDK_SPM_ID: SPM_ID_FREE,
+#               PMDK_LVER: LVER_INVALID,
+#               PMDK_MASTER_VER: masterVersion,
+#               PMDK_POOL_DESCRIPTION: poolName,
+#               PMDK_DOMAINS: {domain.sdUUID: sd.DOM_ACTIVE_STATUS}})
 
     @unsecured
     def _copyLeaseParameters(self, srcDomain, dstDomain):
@@ -893,7 +725,7 @@
                       msdUUID)
 
         # TODO: is this check still relevant?
-        if not masterVersion > self.getMasterVersion():
+        if not masterVersion > self.backend.getMasterVersion():
             raise se.StoragePoolWrongMaster(self.spUUID,
                                             self.masterDomain.sdUUID)
 
@@ -938,7 +770,7 @@
                 self.log.error("Unexpected error", exc_info=True)
             raise se.StorageDomainMasterCopyError(msdUUID)
 
-        self.copyPoolMD(curmsd, newmsd)
+        self.backend.prepareNewMasterDomain(curmsd, newmsd)
 
         path = newmsd.getMDPath()
         if not path:
@@ -968,7 +800,8 @@
         self.log.debug("masterMigrate - lease acquired successfully")
 
         try:
-            self.setDomainMasterRole(newmsd, sd.MASTER_DOMAIN, masterVersion)
+            self.backend.setDomainMasterRole(
+                newmsd, sd.MASTER_DOMAIN, masterVersion)
             self.savePoolParams(self.id, newmsd.sdUUID, masterVersion)
         except Exception:
             self.log.error("Unexpected error", exc_info=True)
@@ -1014,7 +847,7 @@
         if sdUUID in domains:
             return True
 
-        if len(domains) >= self.getMaximumSupportedDomains():
+        if len(domains) >= self.backend.getMaximumSupportedDomains():
             raise se.TooManyDomainsInStoragePoolError()
 
         try:
@@ -1040,7 +873,7 @@
 
                 dom.attach(self.spUUID)
                 domains[sdUUID] = sd.DOM_ATTACHED_STATUS
-                self.setDomainsMap(domains)
+                self.backend.setDomainsMap(domains)
                 self._refreshDomainLinks(dom)
 
             finally:
@@ -1060,7 +893,7 @@
 
         del domains[sdUUID]
 
-        self.setDomainsMap(domains)
+        self.backend.setDomainsMap(domains)
         self._cleanupDomainLinks(sdUUID)
 
         # If the domain that we are detaching is the master domain
@@ -1110,6 +943,23 @@
         # Remove domain from pool metadata
         self.forcedDetachSD(sdUUID)
 
+    def detachAllDomains(self):
+        """
+        Detach all domains from pool before destroying pool
+
+        Assumed cluster lock and that SPM is already stopped.
+        """
+        # Find regular (i.e. not master) domains from the pool metadata
+        regularDoms = tuple(sdUUID for sdUUID in self.getDomains()
+                            if sdUUID != self.masterDomain.sdUUID)
+        # The Master domain should be detached last
+        for sdUUID in regularDoms:
+            self.detachSD(sdUUID)
+
+        # Forced detach master domain
+        self.forcedDetachSD(self.masterDomain.sdUUID)
+        self.masterDomain.detach(self.spUUID)
+
     @unsecured
     def _convertDomain(self, domain, targetFormat=None):
         # Remember to get the sdUUID before upgrading because the object is
@@ -1149,7 +999,7 @@
 
         # Domain conversion requires the links to be present
         self._refreshDomainLinks(dom)
-        self.setDomainMasterRole(dom, sd.REGULAR_DOMAIN, 0)
+        self.backend.setDomainMasterRole(dom, sd.REGULAR_DOMAIN, 0)
 
         if dom.getDomainClass() == sd.DATA_DOMAIN:
             self._convertDomain(dom)
@@ -1157,7 +1007,7 @@
         dom.activate()
         # set domains also do rebuild
         domainStatuses[sdUUID] = sd.DOM_ACTIVE_STATUS
-        self.setDomainsMap(domainStatuses)
+        self.backend.setDomainsMap(domainStatuses)
         self.updateMonitoringThreads()
         return True
 
@@ -1220,7 +1070,7 @@
                                        "%s", masterDir, dom)
 
         domList[sdUUID] = sd.DOM_ATTACHED_STATUS
-        self.setDomainsMap(domList)
+        self.backend.setDomainsMap(domList)
         self.updateMonitoringThreads()
 
     @unsecured
@@ -1393,21 +1243,6 @@
         if os.path.exists(os.path.join(vms, vmUUID)):
             fileUtils.cleanupdir(os.path.join(vms, vmUUID))
 
-    def setDescription(self, descr):
-        """
-        Set storage pool description.
-         'descr' - pool description
-        """
-        if len(descr) > MAX_POOL_DESCRIPTION_SIZE:
-            raise se.StoragePoolDescriptionTooLongError()
-
-        self.log.info("spUUID=%s descr=%s", self.spUUID, descr)
-
-        if not misc.isAscii(descr) and not self.masterDomain.supportsUnicode():
-            raise se.UnicodeArgumentException()
-
-        self.setMetaParam(PMDK_POOL_DESCRIPTION, descr)
-
     def extendVolume(self, sdUUID, volumeUUID, size, isShuttingDown=None):
         # This method is not exposed through the remote API but it's called
         # directly from the mailbox to implement the thin provisioning on
@@ -1423,27 +1258,6 @@
                                       rm.LockType.exclusive):
             return sdCache.produce(sdUUID) \
                 .produceVolume(imgUUID, volUUID).extendSize(int(newSize))
-
-    @classmethod
-    def _getPoolMD(cls, domain):
-        # This might look disgusting but this makes it so that
-        # This is the only intrusion needed to satisfy the
-        # unholy union between pool and SD metadata
-        return DictValidator(domain._metadata._dict, SP_MD_FIELDS)
-
-    @property
-    def _metadata(self):
-        return self._getPoolMD(self.masterDomain)
-
-    @unsecured
-    def getDescription(self):
-        try:
-            return self.getMetaParam(PMDK_POOL_DESCRIPTION)
-            # There was a bug that cause pool description to
-            # disappear. Returning "" might be ugly but it keeps
-            # everyone happy.
-        except KeyError:
-            return ""
 
     @unsecured
     def getVersion(self):
@@ -1461,25 +1275,22 @@
             raise se.StoragePoolMasterNotFound(self.spUUID,
                                                self.masterDomain.sdUUID)
 
-        try:
-            pmd = self._getPoolMD(self.masterDomain)
-        except Exception:
-            self.log.error("Pool metadata error", exc_info=True)
-            raise se.StoragePoolActionError(self.spUUID)
+        lVer, spmId = self.backend.getSpmStatus()
 
         poolInfo = {
             'type': msdInfo['type'],
-            'name': pmd[PMDK_POOL_DESCRIPTION],
-            'domains': domainListEncoder(pmd[PMDK_DOMAINS]),
+            'name': '',
+            'domains': domainListEncoder(self.backend.getDomainsMap()),
             'master_uuid': self.masterDomain.sdUUID,
-            'master_ver': pmd[PMDK_MASTER_VER],
-            'lver': pmd[PMDK_LVER],
-            'spm_id': pmd[PMDK_SPM_ID],
+            'master_ver': self.backend.getMasterVersion(),
+            'lver': lVer,
+            'spm_id': spmId,
             'pool_status': 'uninitialized',
             'version': str(msdInfo['version']),
             'isoprefix': '',
             'pool_status': 'connected',
         }
+
         return poolInfo
 
     @unsecured
@@ -1498,22 +1309,6 @@
             if dom.isISO():
                 return dom
         return None
-
-    def setMetaParams(self, params):
-        self._metadata.update(params)
-
-    def setMetaParam(self, key, value):
-        """
-        Set key:value in pool metadata file
-        """
-        self._metadata[key] = value
-
-    @unsecured
-    def getMetaParam(self, key):
-        """
-        Get parameter from pool metadata file
-        """
-        return self._metadata[key]
 
     @unsecured
     def setMasterDomain(self, msdUUID, masterVersion):
@@ -1540,7 +1335,7 @@
                            " %s", msdUUID, self.spUUID)
             raise se.StoragePoolWrongMaster(self.spUUID, msdUUID)
 
-        version = self.getMasterVersion(useMasterDomain=domain)
+        version = self.backend.getMasterVersion(useMasterDomain=domain)
         if version != int(masterVersion):
             self.log.error("Requested master domain %s does not have expected "
                            "version %s it is version %s",
@@ -1551,11 +1346,6 @@
                        masterVersion)
         self.masterDomain = domain
         self.updateMonitoringThreads()
-
-    @unsecured
-    def invalidateMetadata(self):
-        if not self.spmRole == SPM_ACQUIRED:
-            self._metadata.invalidate()
 
     @unsecured
     @misc.samplingmethod
@@ -1589,7 +1379,7 @@
     @unsecured
     def getDomains(self, activeOnly=False):
         return dict((sdUUID, status) for sdUUID, status
-                    in self.getDomainsMap().iteritems()
+                    in self.backend.getDomainsMap().iteritems()
                     if not activeOnly or status == sd.DOM_ACTIVE_STATUS)
 
     def checkBackupDomain(self):
@@ -1975,23 +1765,6 @@
         self._maxHostID
         self.spmMailer.setMaxHostID(maxID)
         raise se.NotImplementedException
-
-    def detachAllDomains(self):
-        """
-        Detach all domains from pool before destroying pool
-
-        Assumed cluster lock and that SPM is already stopped.
-        """
-        # Find regular (i.e. not master) domains from the pool metadata
-        regularDoms = tuple(sdUUID for sdUUID in self.getDomains()
-                            if sdUUID != self.masterDomain.sdUUID)
-        # The Master domain should be detached last
-        for sdUUID in regularDoms:
-            self.detachSD(sdUUID)
-
-        # Forced detach master domain
-        self.forcedDetachSD(self.masterDomain.sdUUID)
-        self.masterDomain.detach(self.spUUID)
 
     def setVolumeDescription(self, sdUUID, imgUUID, volUUID, description):
         self.validatePoolSD(sdUUID)
diff --git a/vdsm/storage/spbackends.py b/vdsm/storage/spbackends.py
new file mode 100644
index 0000000..e0f8013
--- /dev/null
+++ b/vdsm/storage/spbackends.py
@@ -0,0 +1,317 @@
+#
+# Copyright 2013 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
+#
+
+import logging
+import weakref
+
+import blockSD
+import misc
+import sd
+import storage_exception as se
+
+from persistentDict import DictValidator
+from persistentDict import unicodeDecoder
+from persistentDict import unicodeEncoder
+from sdc import sdCache
+from securable import secured
+from securable import unsecured
+from sp import LVER_INVALID
+from sp import SPM_ACQUIRED
+from sp import SPM_FREE
+from sp import SPM_ID_FREE
+from sp import domainListDecoder
+from sp import domainListEncoder
+from vdsm.config import config
+
+
+MAX_POOL_DESCRIPTION_SIZE = 50
+
+PMDK_DOMAINS = "POOL_DOMAINS"
+PMDK_POOL_DESCRIPTION = "POOL_DESCRIPTION"
+PMDK_LVER = "POOL_SPM_LVER"
+PMDK_SPM_ID = "POOL_SPM_ID"
+PMDK_MASTER_VER = "MASTER_VERSION"
+
+
+# Calculate how many domains can be in the pool before overflowing the Metadata
+MAX_DOMAINS = blockSD.SD_METADATA_SIZE - blockSD.METADATA_BASE_SIZE
+MAX_DOMAINS -= MAX_POOL_DESCRIPTION_SIZE + sd.MAX_DOMAIN_DESCRIPTION_SIZE
+MAX_DOMAINS -= blockSD.PVS_METADATA_SIZE
+MAX_DOMAINS /= 48
+
+
+SP_MD_FIELDS = {
+    # Key          dec,  enc
+    PMDK_DOMAINS: (domainListDecoder, domainListEncoder),
+    PMDK_POOL_DESCRIPTION: (unicodeDecoder, unicodeEncoder),
+    PMDK_LVER: (int, str),
+    PMDK_SPM_ID: (int, str),
+    PMDK_MASTER_VER: (int, str)
+}
+
+
+@secured
+class StoragePoolMetaBackend(object):
+
+    __slots__ = ('pool',)
+
+    log = logging.getLogger('Storage.StoragePoolMetaBackend')
+
+    def __init__(self, pool):
+        self.pool = weakref.proxy(pool)
+
+    ### Read-Only StoragePool Object Accessors ###
+
+    def isSafe(self):
+        return self.pool.isSafe()
+
+    @property
+    def id(self):
+        return self.pool.id
+
+    @property
+    def spmRole(self):
+        return self.pool.spmRole
+
+    @property
+    def spUUID(self):
+        return self.pool.spUUID
+
+    @property
+    def masterDomain(self):
+        return self.pool.masterDomain
+
+    ### StoragePool Abstract Methods Implementation ###
+
+    @unsecured
+    def getSpmStatus(self):
+        try:
+            # XXX: in case the host id is not acquired yet we won't be
+            # able to get the spm id (I should verify this) and the code
+            # below would return SPM_ID_FREE. If that's the case and this
+            # new behavior introduces a problem then we should prepend:
+            #   self.masterDomain.acquireHostId(self.id)
+            # that could take a long time.
+            lVer, spmId = self.masterDomain.inquireClusterLock()
+            lVer, spmId = lVer or LVER_INVALID, spmId or SPM_ID_FREE
+        except NotImplementedError:
+            poolMeta = self._getPoolMD(self.masterDomain)
+
+            # if we claim that we were the SPM (but we're currently not) we
+            # have to make sure that we're not returning stale data
+            if (poolMeta[PMDK_SPM_ID] == self.id
+                    and not self.spmRole == SPM_ACQUIRED):
+                self.invalidateMetadata()
+                poolMeta = self._getPoolMD(self.masterDomain)
+
+            lVer, spmId = poolMeta[PMDK_LVER], poolMeta[PMDK_SPM_ID]
+
+        return lVer, spmId
+
+    def setSpmStatus(self, lVer=None, spmId=None):
+        self.invalidateMetadata()
+        metaParams = dict(filter(lambda (k, v): v is not None,
+                          ((PMDK_LVER, lVer), (PMDK_SPM_ID, spmId))))
+        # this method must be secured (as it changes the pool metadata),
+        # but since it is also used during the SPM status transition by
+        # default we override the security for setMetaParams.
+        # NOTE: this introduces a race when the method is used in the
+        # secured mode, but generally you shouldn't need to call this at
+        # any time.
+        self.setMetaParams(metaParams, __securityOverride=True)
+
+    @unsecured
+    def getDomainsMap(self):
+        self.invalidateMetadata()
+        return self.getMetaParam(PMDK_DOMAINS)
+
+    def setDomainsMap(self, domains):
+        self.setMetaParam(PMDK_DOMAINS, domains)
+
+    @unsecured
+    def getMaximumSupportedDomains(self):
+        msdInfo = self.masterDomain.getInfo()
+        msdType = sd.name2type(msdInfo["type"])
+        msdVersion = int(msdInfo["version"])
+        if msdType in sd.BLOCK_DOMAIN_TYPES and \
+                msdVersion in blockSD.VERS_METADATA_LV:
+            return MAX_DOMAINS
+        else:
+            return config.getint("irs", "maximum_domains_in_pool")
+
+    @unsecured
+    def getMasterVersion(self, useMasterDomain=None):
+        domain = (self.masterDomain
+                  if useMasterDomain is None else useMasterDomain)
+        return self._getPoolMD(domain)[PMDK_MASTER_VER]
+
+    def setDomainMasterRole(self, domain, role, masterVersion):
+        poolMeta = self._getPoolMD(domain)
+        # NOTE: the transaction here does not ensure the consistency between
+        # the domain and pool metadata. For example if the role on the domain
+        # has been changed and the pool metadata transaction fails then the
+        # domain role is not reverted to the previous value.
+        with poolMeta.transaction():
+            poolMeta[PMDK_MASTER_VER] = masterVersion
+            domain.changeRole(role)
+
+    @unsecured
+    def prepareNewMasterDomain(self, prevMd, newMD):
+        prevPoolMD = self._getPoolMD(prevMd)
+        domains = prevPoolMD[PMDK_DOMAINS]
+        pool_descr = prevPoolMD[PMDK_POOL_DESCRIPTION]
+        lver = prevPoolMD[PMDK_LVER]
+        spmId = prevPoolMD[PMDK_SPM_ID]
+        # This is actually domain metadata, But I can't change this because of
+        # backward compatibility
+        leaseParams = prevMd.getLeaseParams()
+
+        # Now insert pool metadata into new mastersd metadata
+
+        newPoolMD = self._getPoolMD(newMD)
+        with newPoolMD.transaction():
+            newPoolMD.update({
+                PMDK_DOMAINS: domains,
+                PMDK_POOL_DESCRIPTION: pool_descr,
+                PMDK_LVER: lver,
+                PMDK_SPM_ID: spmId})
+            newMD.changeLeaseParams(leaseParams)
+
+    ### StoragePool Overridden Methods ###
+
+    @unsecured
+    def getInfo(self):
+        poolInfo = super(StoragePoolMetaBackend, self).getInfo()
+
+        # XXX: in the previous implementation the
+        poolInfo.update({'name': self.getDescription()})
+        return poolInfo
+
+    ### Backend Specific Methods ###
+
+    @unsecured
+    def forceFreeSpm(self):
+        # DO NOT USE, STUPID, HERE ONLY FOR BC
+        # TODO: SCSI Fence the 'lastOwner'
+        self.setSpmStatus(LVER_INVALID, SPM_ID_FREE)
+        self.spmRole = SPM_FREE
+
+    @classmethod
+    def _getPoolMD(cls, domain):
+        # This might look disgusting but this makes it so that
+        # This is the only intrusion needed to satisfy the
+        # unholy union between pool and SD metadata
+        return DictValidator(domain._metadata._dict, SP_MD_FIELDS)
+
+    @property
+    def _metadata(self):
+        return self._getPoolMD(self.masterDomain)
+
+    @unsecured
+    def getMetaParam(self, key):
+        """
+        Get parameter from pool metadata file
+        """
+        return self._metadata[key]
+
+    def setMetaParams(self, params):
+        self._metadata.update(params)
+
+    def setMetaParam(self, key, value):
+        """
+        Set key:value in pool metadata file
+        """
+        self._metadata[key] = value
+
+    @unsecured
+    def getDescription(self):
+        try:
+            return self.getMetaParam(PMDK_POOL_DESCRIPTION)
+            # There was a bug that cause pool description to
+            # disappear. Returning "" might be ugly but it keeps
+            # everyone happy.
+        except KeyError:
+            return ""
+
+    def setDescription(self, descr):
+        """
+        Set storage pool description.
+         'descr' - pool description
+        """
+        if len(descr) > MAX_POOL_DESCRIPTION_SIZE:
+            raise se.StoragePoolDescriptionTooLongError()
+
+        self.log.info("spUUID=%s descr=%s", self.spUUID, descr)
+
+        if not misc.isAscii(descr) and not self.masterDomain.supportsUnicode():
+            raise se.UnicodeArgumentException()
+
+        self.setMetaParam(PMDK_POOL_DESCRIPTION, descr)
+
+    @unsecured
+    def invalidateMetadata(self):
+        if not self.spmRole == SPM_ACQUIRED:
+            self._metadata.invalidate()
+
+    @unsecured
+    def reconstructMaster(self, hostId, poolName, msdUUID, domDict,
+                          masterVersion, leaseParams):
+        self.log.info("spUUID=%s hostId=%s poolName=%s msdUUID=%s domDict=%s "
+                      "masterVersion=%s leaseparams=(%s)", self.spUUID, hostId,
+                      poolName, msdUUID, domDict, masterVersion, leaseParams)
+
+        if msdUUID not in domDict:
+            raise se.InvalidParameterException("masterDomain", msdUUID)
+
+        futureMaster = sdCache.produce(msdUUID)
+
+        # @deprecated, domain version < 3
+        # For backward compatibility we must support a reconstructMaster
+        # that doesn't specify an hostId.
+        if not hostId:
+            self.pool._acquireTemporaryClusterLock(msdUUID, leaseParams)
+            temporaryLock = True
+        else:
+            # Forcing to acquire the host id (if it's not acquired already).
+            futureMaster.acquireHostId(hostId)
+            futureMaster.acquireClusterLock(hostId)
+
+            # The host id must be set for createMaster(...).
+            self.id = hostId
+            temporaryLock = False
+
+        try:
+            self.pool.createMaster(poolName, futureMaster, masterVersion,
+                                   leaseParams)
+
+            for sdUUID in domDict:
+                domDict[sdUUID] = domDict[sdUUID].capitalize()
+
+            # Add domain to domain list in pool metadata.
+            self.log.info("Set storage pool domains: %s", domDict)
+            self._getPoolMD(futureMaster).update({PMDK_DOMAINS: domDict})
+
+            self.pool.refresh(msdUUID=msdUUID, masterVersion=masterVersion)
+        finally:
+            if temporaryLock:
+                self.pool._releaseTemporaryClusterLock(msdUUID)
+                self.pool.stopMonitoringDomains()
+            else:
+                futureMaster.releaseClusterLock()


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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I75493d1db60e51cccd5231b516f963c970d24c99
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Federico Simoncelli <fsimo...@redhat.com>
_______________________________________________
vdsm-patches mailing list
vdsm-patches@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches

Reply via email to