Yoav Kleinberger has uploaded a new change for review.
Change subject: tests: added NFS support to functional tests
......................................................................
tests: added NFS support to functional tests
In this patch, I introduce a new NFS storage context for the storage
functional tests. Since there is much shared logic between NFS and
LocalFS storage context, I introduce a common superclass.
Change-Id: I1781fc400c0604855d3143dde22ccb29e6cc8013
Signed-off-by: Yoav Kleinberger <ykleinbe(a)redhat.com>
---
M tests/functional/basicStorageTest.py
M tests/functional/testlib/storagecontexts/base.py
A tests/functional/testlib/storagecontexts/filebased.py
M tests/functional/testlib/storagecontexts/iscsi.py
M tests/functional/testlib/storagecontexts/localfs.py
A tests/functional/testlib/storagecontexts/nfs.py
6 files changed, 249 insertions(+), 114 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/90/32990/1
diff --git a/tests/functional/basicStorageTest.py b/tests/functional/basicStorageTest.py
index c01d14b..438f8c8 100644
--- a/tests/functional/basicStorageTest.py
+++ b/tests/functional/basicStorageTest.py
@@ -4,6 +4,7 @@
from functional.testlib import controlvdsm
from functional.testlib.storagecontexts import localfs
from functional.testlib.storagecontexts import iscsi
+from functional.testlib.storagecontexts import nfs
class TestBasicStorageDomain(object):
@@ -23,6 +24,9 @@
def testCreateVolumeISCSI(self):
self._testCreateVolume(iscsi.ISCSI)
+ def testCreateVolumeNFS(self):
+ self._testCreateVolume(nfs.NFS)
+
def _testCreateVolume(self, storageBackend):
with storageBackend() as (vdsm, verify):
storageServerID = vdsm.connectStorageServer()
diff --git a/tests/functional/testlib/storagecontexts/base.py
b/tests/functional/testlib/storagecontexts/base.py
index 7179694..2e308ad 100644
--- a/tests/functional/testlib/storagecontexts/base.py
+++ b/tests/functional/testlib/storagecontexts/base.py
@@ -73,16 +73,47 @@
class StorageBackend(object):
def __init__(self):
self._vdsmCaller = vdsmcaller.VDSMCaller()
+ self._domainID = self._newUUID()
+ self._poolID = self._newUUID()
+ self._imageID = self._newUUID()
+ self._volumeID = self._newUUID()
+ self._connectionID = self._newUUID()
+
+ def connectionID(self):
+ return self._connectionID
+
+ def volumeID(self):
+ return self._volumeID
+
+ def imageID(self):
+ return self._imageID
+
+ def poolID(self):
+ return self._poolID
+
+ def domainID(self):
+ return self._domainID
def vdsm(self):
return self._vdsmCaller
- def newUUID(self):
+ def _newUUID(self):
return str(uuid.uuid4())
def randomName(self, base):
return "%s_%04d" % (base, random.randint(1, 10000))
+ def createStoragePool(self):
+ POOL_TYPE_DEPRECATED = 0
+ self.vdsm().createStoragePool(
+ POOL_TYPE_DEPRECATED,
+ self.poolID(),
+ self.randomName('pool'),
+ self.domainID(),
+ [self.domainID()],
+ 1)
+ return self.poolID()
+
def connectStoragePool(self, poolID, masterDomainID):
SCSI_KEY_DEPRECATED = 0
self.vdsm().connectStoragePool(
diff --git a/tests/functional/testlib/storagecontexts/filebased.py
b/tests/functional/testlib/storagecontexts/filebased.py
new file mode 100644
index 0000000..bdb15d7
--- /dev/null
+++ b/tests/functional/testlib/storagecontexts/filebased.py
@@ -0,0 +1,69 @@
+import os
+import storage.volume
+import storage.image
+from . import base
+import logging
+
+
+class Verify(base.Verify):
+
+ def rhevMountPoint(self):
+ raise Exception('you must override this function')
+
+ def storageServerConnected(self):
+ self.sleepWhileVDSMCompletesTask(duration=2)
+ assert os.path.exists(self.rhevMountPoint())
+
+ def storageDomainCreated(self, domainID):
+ self.sleepWhileVDSMCompletesTask(duration=1)
+ self._directIOTestFileExists()
+ domainRoot = os.path.join(self.rhevMountPoint(), domainID)
+ expectedFiles = [
+ 'images',
+ 'dom_md',
+ 'dom_md/leases',
+ 'dom_md/inbox',
+ 'dom_md/outbox',
+ 'dom_md/metadata',
+ 'dom_md/ids']
+ expectedFullPaths =\
+ [os.path.join(domainRoot, path) for path in expectedFiles]
+ for path in expectedFullPaths:
+ logging.info('verifying path: %s' % path)
+ assert os.path.exists(path)
+
+ def _directIOTestFileExists(self):
+ directIOTestFile = os.path.join(
+ self.rhevMountPoint(),
+ '__DIRECT_IO_TEST__')
+ assert os.path.exists(directIOTestFile)
+
+ def volumeCreated(self, volumeInfo):
+ taskID, domainID, imageID, volumeID = volumeInfo
+ self.waitUntilVDSMTaskFinished(taskID, 20)
+ domainRoot = os.path.join(self.rhevMountPoint(), domainID)
+ imageDirectory = os.path.join(domainRoot, 'images', imageID)
+ assert os.path.exists(imageDirectory)
+ volumeFile = os.path.join(imageDirectory, volumeID)
+ volumeLease = '%s.lease' % volumeFile
+ volumeMeta = '%s.meta' % volumeFile
+ for path in volumeFile, volumeLease, volumeMeta:
+ logging.info('verifying path: %s' % path)
+ assert os.path.exists(path)
+
+
+class FileBased(base.StorageBackend):
+ def createVolume(self, size):
+ PREALLOCATE = 1
+ result = self.vdsm().createVolume(
+ self.domainID(),
+ self.poolID(),
+ self.imageID(),
+ self.largeIntegerXMLRPCWorkaround(size),
+ storage.volume.RAW_FORMAT,
+ PREALLOCATE,
+ storage.image.DATA_DISK_TYPE,
+ self.volumeID(),
+ self.randomName('volume_description'))
+ taskID = result['uuid']
+ return taskID, self.domainID(), self.imageID(), self.volumeID()
diff --git a/tests/functional/testlib/storagecontexts/iscsi.py
b/tests/functional/testlib/storagecontexts/iscsi.py
index 0e891ee..eb19715 100644
--- a/tests/functional/testlib/storagecontexts/iscsi.py
+++ b/tests/functional/testlib/storagecontexts/iscsi.py
@@ -60,11 +60,7 @@
base.StorageBackend.__init__(self)
self._iqn = 'iqn.1970-01.functional.test:%04d' %\
random.randint(1, 10000)
- self._volumeGroup = {'uuid': self.newUUID(), 'vgs_uuid': None}
- self._poolID = self.newUUID()
- self._connectionID = self.newUUID()
- self._imageID = self.newUUID()
- self._volumeID = self.newUUID()
+ self._volumeGroup = {'uuid': self.domainID(), 'vgs_uuid': None}
def _targetcli(self, command):
commandAsList = command.split()
@@ -77,6 +73,16 @@
self._fileioBackstore = self.randomName('backfile')
logging.info('using %s, %s' %
(self._fileioBackstore, self._storageFile))
+ self._setupISCSITarget()
+ return (self,
+ Verify(
+ self._iqn,
+ self._volumeGroup,
+ self.vdsm,
+ self.volumeID())
+ )
+
+ def _setupISCSITarget(self):
self._targetcli('/backstores/fileio create %s %s 10G' %
(self._fileioBackstore, self._storageFile))
self._targetcli('/iscsi create %s' % self._iqn)
@@ -88,13 +94,6 @@
self._targetcli('/iscsi/%s/tpg1 set attribute '
'generate_node_acls=1 '
'cache_dynamic_acls=1' % self._iqn)
- return (self,
- Verify(
- self._iqn,
- self._volumeGroup,
- self.vdsm,
- self._volumeID)
- )
def __exit__(self, *args):
doubleDashed = self._volumeGroup['uuid'].replace('-',
'--')
@@ -145,27 +144,13 @@
'user': '',
'tpgt': '1',
'password': '',
- 'id': self._connectionID,
+ 'id': self.connectionID(),
'port': '3260'
}
self.vdsm().disconnectStorageServer(
storage.sd.ISCSI_DOMAIN,
- self._poolID,
+ self.poolID(),
[connection])
-
- def createStoragePool(self):
- POOL_TYPE_DEPRECATED = 0
- self.vdsm().createStoragePool(
- POOL_TYPE_DEPRECATED,
- self._poolID,
- self.randomName('pool'),
- self._domainID(),
- [self._domainID()],
- 1)
- return self._poolID
-
- def _domainID(self):
- return self._volumeGroup['uuid']
def _createVG(self):
lun = self._findLUN()
@@ -194,14 +179,14 @@
def createVolume(self, size):
PREALLOCATE = 1
result = self.vdsm().createVolume(
- self._domainID(),
- self._poolID,
- self._imageID,
+ self.domainID(),
+ self.poolID(),
+ self.imageID(),
self.largeIntegerXMLRPCWorkaround(size),
storage.volume.RAW_FORMAT,
PREALLOCATE,
storage.image.DATA_DISK_TYPE,
- self._volumeID,
+ self.volumeID(),
self.randomName('iscsi_description'))
logging.info('createVolume result: %s' % result)
return result['uuid']
diff --git a/tests/functional/testlib/storagecontexts/localfs.py
b/tests/functional/testlib/storagecontexts/localfs.py
index ef341a0..6ba2914 100644
--- a/tests/functional/testlib/storagecontexts/localfs.py
+++ b/tests/functional/testlib/storagecontexts/localfs.py
@@ -5,67 +5,24 @@
import storage.sd
import storage.volume
import storage.image
-from . import base
+from . import filebased
-class Verify(base.Verify):
+class Verify(filebased.Verify):
def __init__(self, directory, vdsm):
- base.Verify.__init__(self, vdsm)
+ filebased.Verify.__init__(self, vdsm)
self._directory = directory
- def storageServerConnected(self):
- self.sleepWhileVDSMCompletesTask(duration=2)
+ def rhevMountPoint(self):
transformedDirectory = self._directory.replace('/', '_')
- expectedSymlink = os.path.join(
+ result = os.path.join(
'/rhev/data-center/mnt/',
transformedDirectory)
- assert os.path.lexists(expectedSymlink)
-
- def storageDomainCreated(self, domainID):
- self.sleepWhileVDSMCompletesTask(duration=1)
- self._directIOTestFileExists()
- domainRoot = os.path.join(self._directory, domainID)
- expectedFiles = [
- 'images',
- 'dom_md',
- 'dom_md/leases',
- 'dom_md/inbox',
- 'dom_md/outbox',
- 'dom_md/metadata',
- 'dom_md/ids']
- expectedFullPaths =\
- [os.path.join(domainRoot, path) for path in expectedFiles]
- for path in expectedFullPaths:
- assert os.path.exists(path)
-
- def _directIOTestFileExists(self):
- self.sleepWhileVDSMCompletesTask(duration=1)
- directIOTestFile = os.path.join(self._directory, '__DIRECT_IO_TEST__')
- assert os.path.exists(directIOTestFile)
-
- def volumeCreated(self, volumeInfo):
- taskID, domainID, imageID, volumeID = volumeInfo
- self.waitUntilVDSMTaskFinished(taskID, 20)
- domain_directory = os.path.join(self._directory, domainID)
- image_directory = os.path.join(domain_directory, 'images', imageID)
- assert os.path.exists(image_directory)
- volume_file = os.path.join(image_directory, volumeID)
- volume_lease = '%s.lease' % volume_file
- volume_meta = '%s.meta' % volume_file
- for path in volume_file, volume_lease, volume_meta:
- assert os.path.exists(path)
+ return result
-class LocalFS(base.StorageBackend):
+class LocalFS(filebased.FileBased):
_NON_EXISTANT_POOL = '00000000-0000-0000-0000-000000000000'
-
- def __init__(self):
- base.StorageBackend.__init__(self)
- self._domainID = self.newUUID()
- self._poolID = self.newUUID()
- self._imageID = self.newUUID()
- self._volumeID = self.newUUID()
- self._connectionID = self.newUUID()
def __enter__(self):
self._createDirectoryForLocalFSStorage()
@@ -87,13 +44,13 @@
'user': '',
'tpgt': '1',
'password': '******',
- 'id': self._connectionID,
+ 'id': self.connectionID(),
'port': ''}
result = self.vdsm().connectStorageServer(
storage.sd.LOCALFS_DOMAIN,
self._NON_EXISTANT_POOL,
[localFilesystemConnection])
- assert result['statuslist'][0]['id'] == self._connectionID
+ assert result['statuslist'][0]['id'] == self.connectionID()
def disconnectStorageServer(self):
localFilesystemConnection = {
@@ -102,46 +59,20 @@
'user': '',
'tpgt': '1',
'password': '******',
- 'id': self._connectionID,
+ 'id': self.connectionID(),
'port': ''}
self.vdsm().disconnectStorageServer(
storage.sd.LOCALFS_DOMAIN,
self._NON_EXISTANT_POOL,
[localFilesystemConnection])
- def createStoragePool(self):
- POOL_TYPE_DEPRECATED = 0
- self.vdsm().createStoragePool(
- POOL_TYPE_DEPRECATED,
- self._poolID,
- self.randomName('pool'),
- self._domainID,
- [self._domainID],
- 1)
- return self._poolID
-
def createStorageDomain(self):
DOMAIN_VERSION = 3
self.vdsm().createStorageDomain(
storage.sd.LOCALFS_DOMAIN,
- self._domainID,
+ self.domainID(),
'some_name',
self._directory,
storage.sd.DATA_DOMAIN,
DOMAIN_VERSION)
- return self._domainID
-
- def createVolume(self, size):
- PREALLOCATE = 1
- result = self.vdsm().createVolume(
- self._domainID,
- self._poolID,
- self._imageID,
- self.largeIntegerXMLRPCWorkaround(size),
- storage.volume.RAW_FORMAT,
- PREALLOCATE,
- storage.image.DATA_DISK_TYPE,
- self._volumeID,
- self.randomName('localfs_description'))
- taskID = result['uuid']
- return taskID, self._domainID, self._imageID, self._volumeID
+ return self.domainID()
diff --git a/tests/functional/testlib/storagecontexts/nfs.py
b/tests/functional/testlib/storagecontexts/nfs.py
new file mode 100644
index 0000000..cdde3a1
--- /dev/null
+++ b/tests/functional/testlib/storagecontexts/nfs.py
@@ -0,0 +1,115 @@
+import subprocess
+import tempfile
+import pwd
+import shutil
+import os
+import storage.sd
+from . import filebased
+
+
+class Verify(filebased.Verify):
+ def __init__(self, directory, vdsm):
+ filebased.Verify.__init__(self, vdsm)
+ self._directory = directory
+
+ def rhevMountPoint(self):
+ transformedDirectory = self._directory.replace('/', '_')
+ result = os.path.join(
+ '/rhev/data-center/mnt/',
+ '127.0.0.1:%s' % transformedDirectory)
+ return result
+
+
+class NFS(filebased.FileBased):
+ _NON_EXISTANT_POOL = '00000000-0000-0000-0000-000000000000'
+
+ def __enter__(self):
+ self._setupNFSExport()
+ return self, Verify(self._directory, self.vdsm)
+
+ def _setupNFSExport(self):
+ self._export()
+ self._restartNFSServer()
+ mountPoint = tempfile.mkdtemp()
+ COMMAND = 'sudo --non-interactive mount -t nfs localhost:%s %s' %\
+ (self._directory, mountPoint)
+ subprocess.check_call(COMMAND, shell=True)
+ self._setPermissions(mountPoint)
+ subprocess.check_call(
+ 'sudo --non-interactive umount %s' % mountPoint, shell=True)
+ os.rmdir(mountPoint)
+
+ def _export(self):
+ self._directory = tempfile.mkdtemp('', 'nfstest',
'/var/tmp')
+ self._previousExports = open('/etc/exports').read()
+ with open('/etc/exports', 'a') as f:
+ line = '%s
127.0.0.1(rw,sync,no_root_squash,no_all_squash,fsid=0)\n'\
+ % self._directory
+ f.write(line)
+
+ def _unexport(self):
+ self._umount()
+ with open('/etc/exports', 'w') as f:
+ f.write(self._previousExports)
+ self._restartNFSServer()
+
+ def _umount(self):
+ COMMAND = 'sudo --non-interactive umount 127.0.0.1:%s'\
+ % self._directory
+ code = subprocess.call(COMMAND, shell=True)
+
+ def _setPermissions(self, directory):
+ vdsmUser = pwd.getpwnam('vdsm')
+ os.chown(directory, vdsmUser.pw_uid, vdsmUser.pw_gid)
+ os.chmod(directory, 0755)
+
+ def _restartNFSServer(self):
+ subprocess.check_call(
+ 'sudo --non-interactive service nfs-server restart', shell=True)
+
+ def __exit__(self, *args):
+ self._unexport()
+ shutil.rmtree(self._directory)
+
+ def connectStorageServer(self):
+ connection = {
+ 'connection': self._nfsPath(),
+ 'iqn': '',
+ 'user': '',
+ 'tpgt': '1',
+ 'password': '******',
+ 'id': self.connectionID(),
+ 'port': ''}
+ result = self.vdsm().connectStorageServer(
+ storage.sd.NFS_DOMAIN,
+ self._NON_EXISTANT_POOL,
+ [connection])
+ assert result['statuslist'][0]['id'] == self.connectionID()
+
+ def disconnectStorageServer(self):
+ connection = {
+ 'connection': self._nfsPath(),
+ 'iqn': '',
+ 'user': '',
+ 'tpgt': '1',
+ 'password': '******',
+ 'id': self.connectionID(),
+ 'port': ''}
+ self.vdsm().disconnectStorageServer(
+ storage.sd.LOCALFS_DOMAIN,
+ self._NON_EXISTANT_POOL,
+ [connection])
+
+ def createStorageDomain(self):
+ DOMAIN_VERSION = 3
+ self.vdsm().createStorageDomain(
+ storage.sd.NFS_DOMAIN,
+ self.domainID(),
+ 'some_name',
+ self._nfsPath(),
+ storage.sd.DATA_DOMAIN,
+ DOMAIN_VERSION)
+ return self.domainID()
+
+ def _nfsPath(self):
+ return '127.0.0.1:%s' % self._directory
--
To view, visit
http://gerrit.ovirt.org/32990
To unsubscribe, visit
http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I1781fc400c0604855d3143dde22ccb29e6cc8013
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Yoav Kleinberger <ykleinbe(a)redhat.com>