Change in vdsm[master]: sp: refactor out the metadata access from StoragePool
fsimonce at redhat.com
fsimonce at redhat.com
Fri Dec 6 17:12:32 UTC 2013
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 <fsimonce at 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)
+}
+
+
+ at 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 <fsimonce at redhat.com>
More information about the vdsm-patches
mailing list