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>
6 years, 11 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>
6 years, 11 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>
6 years, 11 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>
6 years, 11 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>
6 years, 11 months
Change in vdsm[master]: contrib: Add tool for getting repoStats statistics
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: contrib: Add tool for getting repoStats statistics
......................................................................
contrib: Add tool for getting repoStats statistics
This tool is useful when debugging storage domain monitoring issues.
Examples:
$ repostat vdsm.log.*
domain: ebcd25b1-1ab0-47e3-aa8b-83ee5f91b5bd
delay avg: 0.093941 min: 0.000430 max: 1.499200
last check avg: 5.117508 min: 0.000000 max: 12.300000
domain: 6d7f7a48-4511-43b8-8abf-d323f7118b19
delay avg: 0.001090 min: 0.000300 max: 0.030056
last check avg: 4.975313 min: 0.000000 max: 10.200000
Change-Id: Id5f7828216058bb401f8a472fa5601e79542def1
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M Makefile.am
A contrib/repostat
2 files changed, 55 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/49/38749/1
diff --git a/Makefile.am b/Makefile.am
index 34885a1..7407b22 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,6 +49,7 @@
autogen.sh \
build-aux/pkg-version \
contrib/profile-stats \
+ contrib/repostat \
pylintrc \
vdsm.spec \
vdsm.spec.in \
@@ -80,6 +81,7 @@
WHITELIST = \
contrib/profile-stats \
+ contrib/repostat \
init/daemonAdapter \
vdsm/get-conf-item \
vdsm/set-conf-item \
diff --git a/contrib/repostat b/contrib/repostat
new file mode 100755
index 0000000..c140b4c
--- /dev/null
+++ b/contrib/repostat
@@ -0,0 +1,53 @@
+#!/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
+#
+"""
+Parse repoStats log lines and calculate statistics.
+
+Usage: repostat vdsm.log [...]
+"""
+
+import fileinput
+import re
+
+repostat_re = re.compile(r' Run and protect: repoStats, Return response: ')
+
+stats = {}
+
+
+def liststat(a):
+ b = sorted(a)
+ return sum(b) / len(b), b[0], b[-1]
+
+
+for line in fileinput.input():
+ match = repostat_re.search(line)
+ if not match:
+ continue
+ response = eval(line[match.end():])
+ for uuid, info in response.items():
+ stats.setdefault(uuid, {'delays': [], 'checks': []})
+ stats[uuid]['delays'].append(float(info['delay']))
+ stats[uuid]['checks'].append(float(info['lastCheck']))
+
+for uuid, info in stats.iteritems():
+ print 'domain:', uuid
+ print ' delay avg: %f min: %f max: %f' % liststat(info['delays'])
+ print ' last check avg: %f min: %f max: %f' % liststat(info['checks'])
--
To view, visit https://gerrit.ovirt.org/38749
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id5f7828216058bb401f8a472fa5601e79542def1
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 11 months
Change in vdsm[master]: gluster: rename glusterCreateBrick to glusterBrickCreate
by barumuga@redhat.com
Hello Piotr Kliczewski, Timothy Asir, Dan Kenigsberg,
I'd like you to do a code review. Please visit
https://gerrit.ovirt.org/42308
to review the following change.
Change subject: gluster: rename glusterCreateBrick to glusterBrickCreate
......................................................................
gluster: rename glusterCreateBrick to glusterBrickCreate
To make consistency in function names in supervdsm and verb names in
vdsm, this patch renames verb glusterCreateBrick to glusterBrickCreate
Change-Id: I367a864bc9bf4aa9b6a0d36b64f541fa89c61c0d
Signed-off-by: Bala.FA <barumuga(a)redhat.com>
---
M client/vdsClientGluster.py
M vdsm/gluster/api.py
M vdsm/gluster/apiwrapper.py
M vdsm/gluster/storagedev.py
M vdsm/rpc/vdsmapi-gluster-schema.json
5 files changed, 11 insertions(+), 11 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/08/42308/1
diff --git a/client/vdsClientGluster.py b/client/vdsClientGluster.py
index a4bce63..76b91e1 100644
--- a/client/vdsClientGluster.py
+++ b/client/vdsClientGluster.py
@@ -633,7 +633,7 @@
pp.pprint(status)
return status['status']['code'], status['status']['message']
- def do_glusterCreateBrick(self, args):
+ def do_glusterBrickCreate(self, args):
params = self._eqSplit(args)
devList = params.get('devices', '').split(',')
brickName = params.get('brickName', '')
@@ -646,7 +646,7 @@
raidParams['stripeSize'] = int(params.get('stripeSize', 0))
raidParams['pdCount'] = int(params.get('pdCount', 0))
- status = self.s.glusterCreateBrick(brickName, mountPoint,
+ status = self.s.glusterBrickCreate(brickName, mountPoint,
devList, fsType, raidParams)
pp.pprint(status)
return status['status']['code'], status['status']['message']
@@ -1172,8 +1172,8 @@
('[volumeName=<volume_name>]',
'snapshot list for given volume'
)),
- 'glusterCreateBrick': (
- serv.do_glusterCreateBrick,
+ 'glusterBrickCreate': (
+ serv.do_glusterBrickCreate,
('brickName=<brick_name> mountPoint=<mountPoint> '
'devices=<device[,device, ...]> '
'[raidType=<raid_type>] [stripeSize=<stripe_size>] '
diff --git a/vdsm/gluster/api.py b/vdsm/gluster/api.py
index 24b8230..918c3cc 100644
--- a/vdsm/gluster/api.py
+++ b/vdsm/gluster/api.py
@@ -672,9 +672,9 @@
return {'snapshotList': status}
@exportAsVerb
- def createBrick(self, name, mountPoint, devList, fsType=None,
+ def brickCreate(self, name, mountPoint, devList, fsType=None,
raidParams={}, options=None):
- status = self.svdsmProxy.glusterCreateBrick(name,
+ status = self.svdsmProxy.glusterBrickCreate(name,
mountPoint,
devList,
fsType,
diff --git a/vdsm/gluster/apiwrapper.py b/vdsm/gluster/apiwrapper.py
index 4768a86..f607dd4 100644
--- a/vdsm/gluster/apiwrapper.py
+++ b/vdsm/gluster/apiwrapper.py
@@ -81,9 +81,9 @@
def storageDevicesList(self, options=None):
return self._gluster.storageDevicesList()
- def createBrick(self, name, mountPoint, devList, fsType=None,
+ def brickCreate(self, name, mountPoint, devList, fsType=None,
raidParams={}):
- return self._gluster.createBrick(name, mountPoint,
+ return self._gluster.brickCreate(name, mountPoint,
devList, fsType, raidParams)
diff --git a/vdsm/gluster/storagedev.py b/vdsm/gluster/storagedev.py
index 2aeabad..f4178e9 100644
--- a/vdsm/gluster/storagedev.py
+++ b/vdsm/gluster/storagedev.py
@@ -123,7 +123,7 @@
@makePublic
-def createBrick(brickName, mountPoint, devNameList, fsType=DEFAULT_FS_TYPE,
+def brickCreate(brickName, mountPoint, devNameList, fsType=DEFAULT_FS_TYPE,
raidParams={}):
def _getDeviceList(devNameList):
return [blivetEnv.devicetree.getDeviceByName(devName.split("/")[-1])
diff --git a/vdsm/rpc/vdsmapi-gluster-schema.json b/vdsm/rpc/vdsmapi-gluster-schema.json
index c86f43c..209bfbd 100644
--- a/vdsm/rpc/vdsmapi-gluster-schema.json
+++ b/vdsm/rpc/vdsmapi-gluster-schema.json
@@ -1274,7 +1274,7 @@
'data': {'type': 'str', 'stripeSize': 'int', 'diskCnt': 'int'}}
##
-# @GlusterHost.createBrick:
+# @GlusterHost.brickCreate:
#
# Create a brick for the gluster volume
#
@@ -1293,7 +1293,7 @@
#
# Since: 4.17.0
##
-{'command': {'class': 'GlusterHost', 'name': 'createBrick'},
+{'command': {'class': 'GlusterHost', 'name': 'brickCreate'},
'data': {'name': 'str', 'mountPoint': 'str', 'devList': ['str'],
'*fsType': 'str', '*raidParams': 'RaidDevice'},
'returns': 'bool'}
--
To view, visit https://gerrit.ovirt.org/42308
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I367a864bc9bf4aa9b6a0d36b64f541fa89c61c0d
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Bala.FA <barumuga(a)redhat.com>
Gerrit-Reviewer: Dan Kenigsberg <danken(a)redhat.com>
Gerrit-Reviewer: Piotr Kliczewski <piotr.kliczewski(a)gmail.com>
Gerrit-Reviewer: Timothy Asir <tjeyasin(a)redhat.com>
6 years, 11 months
Change in vdsm[master]: tests: Add time per test if nose-timer is available
by asegurap@redhat.com
Antoni Segura Puimedon has uploaded a new change for review.
Change subject: tests: Add time per test if nose-timer is available
......................................................................
tests: Add time per test if nose-timer is available
This patch adds an output like:
resourceManagerTests.ResourceManagerTests.testStressTest: 12.8313s
resourceManagerTests.ResourceManagerTests.testResourceAcquireTimeout: 1.0069s
resourceManagerTests.ResourceManagerTests.testResourceLockSwitch: 0.0108s
resourceManagerTests.ResourceManagerTests.testResourceAutorelease: 0.0101s
resourceManagerTests.ResourceManagerTests.testResourceInvalidation: 0.0093s
resourceManagerTests.ResourceManagerTests.testCrashOnSwitch: 0.0067s
resourceManagerTests.ResourceManagerTests.testResourceSwitchLockTypeFail:
0.0062s
resourceManagerTests.ResourceManagerTests.testCancelExclusiveBetweenShared:
0.0049s
resourceManagerTests.ResourceManagerTests.testAcquireResourceExclusive: 0.0042s
resourceManagerTests.ResourceManagerTests.testCancelRequest: 0.0031s
resourceManagerTests.ResourceManagerTests.testRequestRefCmp: 0.0030s
resourceManagerTests.ResourceManagerTests.testRequestRecancel: 0.0030s
resourceManagerTests.ResourceManagerTests.testResourceStatuses: 0.0029s
resourceManagerTests.ResourceManagerTests.testAccessAttributeNotExposedByRequestRef:
0.0029s
resourceManagerTests.ResourceManagerTests.testFailCreateAfterSwitch: 0.0025s
resourceManagerTests.ResourceManagerTests.testRequestWithBadCallbackOnCancel:
0.0025s
resourceManagerTests.ResourceManagerTests.testAcquireResourceShared: 0.0023s
resourceManagerTests.ResourceManagerTests.testRereleaseResource: 0.0022s
resourceManagerTests.ResourceManagerTests.testResourceWrapper: 0.0020s
resourceManagerTests.ResourceManagerTests.testRequestWithBadCallbackOnGrant:
0.0020s
resourceManagerTests.ResourceManagerTests.testRequestRefStr: 0.0020s
resourceManagerTests.ResourceManagerTests.testAccessAttributeNotExposedByWrapper:
0.0019s
resourceManagerTests.ResourceManagerTests.testErrorInFactory: 0.0017s
resourceManagerTests.ResourceManagerTests.testAcquireNonExistingResource:
0.0016s
resourceManagerTests.ResourceManagerTests.testRequestInvalidResource: 0.0015s
resourceManagerTests.ResourceManagerTests.testRequestRegrant: 0.0015s
resourceManagerTests.ResourceManagerTests.testResourceAcquireInvalidTimeout:
0.0014s
resourceManagerTests.ResourceManagerTests.testReleaseInvalidResource: 0.0014s
resourceManagerTests.ResourceManagerTests.testSingleton: 0.0014s
resourceManagerTests.ResourceManagerTests.testForceRegisterNamespace: 0.0013s
resourceManagerTests.ResourceManagerTests.testListNamespaces: 0.0013s
resourceManagerTests.ResourceManagerTests.testReregisterNamespace: 0.0012s
resourceManagerTests.ResourceManagerTests.testRegisterInvalidNamespace: 0.0012s
At the end of the usual tests if nose-timer is detected in the
system. It shows the tests over 500ms in yellow and those over 5s
in red.
If you want to get this output, do:
pip install nose-timer
Change-Id: I4960b30532a84abd259fb268c7e40a1751847a96
Signed-off-by: Antoni S. Puimedon <asegurap(a)redhat.com>
---
M tests/run_tests_local.sh.in
M tests/testrunner.py
2 files changed, 18 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/71/30171/1
diff --git a/tests/run_tests_local.sh.in b/tests/run_tests_local.sh.in
index d19f22c..f51473d 100644
--- a/tests/run_tests_local.sh.in
+++ b/tests/run_tests_local.sh.in
@@ -7,4 +7,9 @@
@top_srcdir(a)/tests/makecert.sh
fi
-PYTHONDONTWRITEBYTECODE=1 LC_ALL=C PYTHONPATH="@top_srcdir@/lib:@top_srcdir@/vdsm:@top_srcdir@/client:@top_srcdir@/vdsm_api:$PYTHONPATH" "$PYTHON_EXE" @top_srcdir(a)/tests/testrunner.py --local-modules $@
+python -c 'import nosetimer' > /dev/null 2>&1
+if [ $? -eq 0 ]; then
+ EXTRA_ARGS='--with-timer'
+fi
+
+PYTHONDONTWRITEBYTECODE=1 LC_ALL=C PYTHONPATH="@top_srcdir@/lib:@top_srcdir@/vdsm:@top_srcdir@/client:@top_srcdir@/vdsm_api:$PYTHONPATH" "$PYTHON_EXE" @top_srcdir(a)/tests/testrunner.py --local-modules $@ "$EXTRA_ARGS"
diff --git a/tests/testrunner.py b/tests/testrunner.py
index eed743f..ec24c2b 100644
--- a/tests/testrunner.py
+++ b/tests/testrunner.py
@@ -40,6 +40,16 @@
from nose import config
from nose import core
from nose import result
+try:
+ from nosetimer.plugin import TimerPlugin
+except ImportError:
+ timer_plugin = None
+else:
+ timer_plugin = TimerPlugin()
+ timer_plugin.enabled = True
+ timer_plugin.timer_ok = 500 # Okay <= 500ms
+ timer_plugin.timer_warning = 5000 # Warn > 5s
+ timer_plugin.timer_no_color = False # Enable color output
from testValidation import SlowTestsPlugin, StressTestsPlugin
@@ -378,6 +388,8 @@
plugins=core.DefaultPluginManager())
conf.plugins.addPlugin(SlowTestsPlugin())
conf.plugins.addPlugin(StressTestsPlugin())
+ if timer_plugin is not None:
+ conf.plugins.addPlugin(timer_plugin)
runner = VdsmTestRunner(stream=conf.stream,
verbosity=conf.verbosity,
--
To view, visit http://gerrit.ovirt.org/30171
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4960b30532a84abd259fb268c7e40a1751847a96
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Antoni Segura Puimedon <asegurap(a)redhat.com>
6 years, 11 months
Change in vdsm[master]: ImageManifest: create_image_dir
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: ImageManifest: create_image_dir
......................................................................
ImageManifest: create_image_dir
Change-Id: I94a8cfdd9fb1f3c3fd4e4e2ab409bfd287829351
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/image_manifest_tests.py
M vdsm/storage/image.py
2 files changed, 20 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/57/43557/1
diff --git a/tests/image_manifest_tests.py b/tests/image_manifest_tests.py
index a2ac1c0..a27eabd 100644
--- a/tests/image_manifest_tests.py
+++ b/tests/image_manifest_tests.py
@@ -23,6 +23,7 @@
from testlib import VdsmTestCase, namedTemporaryDir
from storage import sd, image
+from storage.sdm import sdm_verb
class ImageManifestTests(VdsmTestCase):
@@ -33,3 +34,14 @@
manifest = image.ImageManifest(tmpdir)
expected = os.path.join(tmpdir, sduuid, sd.DOMAIN_IMAGES, imguuid)
self.assertEquals(expected, manifest.getImageDir(sduuid, imguuid))
+
+ @sdm_verb
+ def test_create_image_dir(self):
+ with namedTemporaryDir() as tmpdir:
+ sduuid = str(uuid.uuid4())
+ imguuid = str(uuid.uuid4())
+ manifest = image.ImageManifest(tmpdir)
+ os.makedirs(os.path.join(tmpdir, sduuid, sd.DOMAIN_IMAGES))
+ manifest.create_image_dir(sduuid, imguuid)
+ self.assertTrue(os.path.exists(manifest.getImageDir(sduuid,
+ imguuid)))
\ No newline at end of file
diff --git a/vdsm/storage/image.py b/vdsm/storage/image.py
index 81b2043..5f87921 100644
--- a/vdsm/storage/image.py
+++ b/vdsm/storage/image.py
@@ -38,6 +38,7 @@
from threadLocal import vars
import resourceFactories
import resourceManager as rm
+from sdm import require_sdm
log = logging.getLogger('Storage.Image')
rmanager = rm.ResourceManager.getInstance()
@@ -97,6 +98,13 @@
"""
return os.path.join(self.repoPath, sdUUID, sd.DOMAIN_IMAGES, imgUUID)
+ @require_sdm
+ def create_image_dir(self, sd_id, img_id):
+ img_dir = self.getImageDir(sd_id, img_id)
+ if not os.path.isdir(img_dir):
+ log.info("Create placeholder %s for image's volumes", img_dir)
+ os.mkdir(img_dir)
+ return img_dir
class Image:
""" Actually represents a whole virtual disk.
--
To view, visit https://gerrit.ovirt.org/43557
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I94a8cfdd9fb1f3c3fd4e4e2ab409bfd287829351
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
6 years, 11 months
Change in vdsm[master]: tests: Add a live merge functional test
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: tests: Add a live merge functional test
......................................................................
tests: Add a live merge functional test
Test whether we can successfully merge the active layer. Uses lots of
the functional test infrastructure! Only runs if vdsm says it can
support live merge.
Change-Id: Idd5a2f7eedaef9e90981256de66fc3ed21658e89
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/functional/utils.py
M tests/functional/virtTests.py
2 files changed, 185 insertions(+), 5 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/24/29824/1
diff --git a/tests/functional/utils.py b/tests/functional/utils.py
index 494be98..e3cdba6 100644
--- a/tests/functional/utils.py
+++ b/tests/functional/utils.py
@@ -228,3 +228,41 @@
def updateVmPolicy(self, vmId, vcpuLimit):
result = self.vdscli.updateVmPolicy([vmId, vcpuLimit])
return result['status']['code'], result['status']['message']
+
+ def getTaskStatus(self, taskId):
+ result = self.vdscli.getTaskStatus(taskId)
+ return result['status']['code'], result['status']['message'],\
+ result['taskStatus']
+
+ def getVolumeInfo(self, sdId, spId, imgId, volId):
+ result = self.vdscli.getVolumeInfo(sdId, spId, imgId, volId)
+ return result['status']['code'], result['status']['message'],\
+ result['info']
+
+ def createVolume(self, sdId, spId, imgId, size, volFormat, preallocate,
+ diskType, volId, desc, baseImgId, baseVolId):
+ result = self.vdscli.createVolume(sdId, spId, imgId, size, volFormat,
+ preallocate, diskType, volId, desc,
+ baseImgId, baseVolId)
+ return result['status']['code'], result['status']['message'],\
+ result['uuid']
+
+ def deleteVolume(self, sdId, spId, imgId, volIds, postZero=False,
+ force=False):
+ result = self.vdscli.deleteVolume(sdId, spId, imgId, volIds, postZero,
+ force)
+ return result['status']['code'], result['status']['message'],\
+ result['uuid']
+
+ def snapshot(self, vmId, snapDrives, snapMemVolHandle=''):
+ result = self.vdscli.snapshot(vmId, snapDrives, snapMemVolHandle)
+ return result['status']['code'], result['status']['message']
+
+ def merge(self, vmId, drive, base, top, bandwidth, jobId):
+ result = self.vdscli.merge(vmId, drive, base, top, bandwidth, jobId)
+ return result['status']['code'], result['status']['message']
+
+ def list(self, fullStatus=False, vmList=()):
+ result = self.vdscli.list(fullStatus, vmList)
+ return result['status']['code'], result['status']['message'], \
+ result['vmList']
diff --git a/tests/functional/virtTests.py b/tests/functional/virtTests.py
index 94ce240..b811b92 100644
--- a/tests/functional/virtTests.py
+++ b/tests/functional/virtTests.py
@@ -22,6 +22,7 @@
import math
import tempfile
import logging
+import uuid
from stat import S_IROTH
from functools import partial, wraps
@@ -32,7 +33,8 @@
from testrunner import temporaryPath
from vdsm.utils import CommandPath, RollbackContext
-import storageTests as storage
+import storageTests
+import storage
from storage.misc import execCmd
from utils import VdsProxy, SUCCESS
@@ -109,6 +111,18 @@
return method(self, *args, **kwargs)
else:
raise SkipTest('KVM is not enabled')
+ return wrapped
+
+
+def requireLiveMerge(method):
+ @wraps(method)
+ def wrapped(self, *args, **kwargs):
+ status, msg, result = self.vdsm.getVdsCapabilities()
+ self.assertEqual(status, SUCCESS, msg)
+ if result.get('liveMerge') == 'true':
+ return method(self, *args, **kwargs)
+ else:
+ raise SkipTest('Live Merge is not available')
return wrapped
@@ -227,9 +241,9 @@
@requireKVM
@permutations([['localfs'], ['iscsi'], ['nfs']])
def testVmWithStorage(self, backendType):
- disk = storage.StorageTest()
+ disk = storageTests.StorageTest()
disk.setUp()
- conf = storage.storageLayouts[backendType]
+ conf = storageTests.storageLayouts[backendType]
drives = disk.generateDriveConf(conf)
customization = {'vmId': '88888888-eeee-ffff-aaaa-111111111111',
'vmName': 'testVmWithStorage' + backendType,
@@ -247,8 +261,8 @@
def testVmWithDevice(self, *devices):
customization = {'vmId': '77777777-ffff-3333-bbbb-222222222222',
'vmName': 'testVm', 'devices': [], 'display': 'vnc'}
- storageLayout = storage.storageLayouts['localfs']
- diskSpecs = storage.StorageTest.generateDriveConf(storageLayout)
+ storageLayout = storageTests.storageLayouts['localfs']
+ diskSpecs = storageTests.StorageTest.generateDriveConf(storageLayout)
pciSpecs = {'bus': '0x00', 'domain': '0x0000',
'function': '0x0', 'type': 'pci'}
ccidSpecs = {'slot': '0', 'controller': '0', 'type': 'ccid'}
@@ -412,3 +426,131 @@
self.vdsm.updateVmPolicy(customization['vmId'],
'50')
self.assertEqual(status, SUCCESS, msg)
+
+
+@expandPermutations
+class LiveMergeTest(VirtTestBase):
+ def _waitTask(self, taskId):
+ def assertTaskOK():
+ status, msg, result = self.vdsm.getTaskStatus(taskId)
+ self.assertEqual(status, SUCCESS, msg)
+ self.assertEquals(result['taskState'], 'finished')
+
+ self.retryAssert(assertTaskOK, timeout=60)
+
+ def _waitBlockJobs(self, vmId, jobIds):
+ def assertJobsGone():
+ status, msg, result = self.vdsm.getVmStats(vmId)
+ self.assertEqual(status, SUCCESS, msg)
+ self.assertTrue('vmJobs' in result)
+ self.assertTrue(all([x not in result['vmJobs'].keys()
+ for x in jobIds]))
+
+ self.retryAssert(assertJobsGone, timeout=60)
+
+ def _snapshotVM(self, vmId, drives, rollback):
+ snapDrives = []
+ for drive in drives:
+ sd = drive['domainID']
+ sp = drive['poolID']
+ img = drive['imageID']
+ vol = drive['volumeID']
+ newVol = str(uuid.uuid4())
+ volFormat = storage.volume.COW_FORMAT
+ preallocate = storage.volume.SPARSE_VOL
+ desc = 'snapshot for %s' % vol
+
+ # Create volume and wait
+ status, msg, result = self.vdsm.getVolumeInfo(sd, sp, img, vol)
+ self.assertEqual(status, SUCCESS, msg)
+ size = result['capacity']
+ diskType = result['disktype']
+
+ status, msg, taskId = self.vdsm.createVolume(sd, sp, img, size,
+ volFormat,
+ preallocate, diskType,
+ newVol, desc, img,
+ vol)
+ self.assertEqual(status, SUCCESS, msg)
+ self._waitTask(taskId)
+ undo = lambda sd=sd, sp=sp, img=img, vol=newVol: \
+ self._waitTask(self.vdsm.deleteVolume(sd, sp, img, vol)[2])
+ rollback.prependDefer(undo)
+
+ snapDrives.append({'domainID': sd,
+ 'imageID': img,
+ 'volumeID': newVol,
+ 'baseVolumeID': vol})
+
+ # Create snapshot
+ status, msg = self.vdsm.snapshot(vmId, snapDrives)
+ self.assertEqual(status, SUCCESS, msg)
+ return snapDrives
+
+ def _orderChain(self, vmId, dev, chain):
+ parentMap = {}
+ for vol in chain:
+ status, msg, info = self.vdsm.getVolumeInfo(dev['domainID'],
+ dev['poolID'],
+ dev['imageID'], vol)
+ self.assertEqual(status, SUCCESS, msg)
+ parent = info['parent']
+ parentMap[vol] = parent
+
+ vol = dev['volumeID']
+ chain = list()
+ while True:
+ chain.insert(0, vol)
+ vol = parentMap.get(vol, '00000000-0000-0000-0000-000000000000')
+ if vol == '00000000-0000-0000-0000-000000000000':
+ break
+ return chain
+
+ def _getVolumeChains(self, vmId):
+ chains = {}
+ status, msg, result = self.vdsm.list(True, (vmId,))
+ self.assertEqual(status, SUCCESS, msg)
+ vmDef = result[0]
+ for dev in vmDef['devices']:
+ if dev['device'] != 'disk':
+ continue
+ chains[dev['imageID']] = self._orderChain(vmId, dev,
+ [x['volumeID'] for x in
+ dev['volumeChain']])
+ return chains
+
+ @requireKVM
+ @requireLiveMerge
+ def testCapable(self):
+ pass
+
+ @permutations([['localfs']])
+ def testMergeActiveLayer(self, backendType):
+ disk = storageTests.StorageTest()
+ disk.setUp()
+ conf = storageTests.storageLayouts[backendType]
+ drives = disk.generateDriveConf(conf)
+ vmId = '12121212-abab-baba-abab-222222222222'
+ customization = {'vmId': vmId,
+ 'vmName': 'testMergeActive' + backendType,
+ 'drives': drives,
+ 'display': 'vnc'}
+
+ with RollbackContext() as rollback:
+ disk.createVdsmStorageLayout(conf, 3, rollback)
+ with RunningVm(self.vdsm, customization) as vm:
+ self._waitForStartup(vm, VM_MINIMAL_UPTIME)
+ snapDrives = self._snapshotVM(vmId, drives, rollback)
+ chains = {}
+ jobIds = []
+ for drive in snapDrives:
+ base = drive['baseVolumeID']
+ top = drive['volumeID']
+ jobId = str(uuid.uuid4())
+ chains[drive['imageID']] = [base, top]
+ status, msg = self.vdsm.merge(vmId, drive, base, top, 0,
+ jobId)
+ jobIds.append(jobId)
+ self._waitBlockJobs(vmId, jobIds)
+ actual = self._getVolumeChains(vmId)
+ self.assertEquals(chains, actual)
--
To view, visit http://gerrit.ovirt.org/29824
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Idd5a2f7eedaef9e90981256de66fc3ed21658e89
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
6 years, 11 months