Add support for working with multiple glusterfs volumes

This patch adds support for working with multiple glusterfs volumes
for the GlusterFS native protocol driver. Since the semantics and
mapping for native driver is different compared to GlusterFS NFS
driver, this patch refactors the native driver as a standalone
driver and fixes some flows to fit the native driver with the
Manila share flows.

A more recent version of glusterfs having SSL support is needed.

Change-Id: I567c7602e97c9d55551baa29f121c05da2985007
Closes-Bug: #1368669
This commit is contained in:
Deepak C Shetty 2014-09-14 09:12:21 +00:00
parent cceb466f30
commit 10c3f8fb1f
4 changed files with 1238 additions and 268 deletions

View File

@ -17,3 +17,9 @@ ip: CommandFilter, /sbin/ip, root
# manila/network/linux/interface.py: 'ovs-vsctl', 'add-port', '%s', '%s' # manila/network/linux/interface.py: 'ovs-vsctl', 'add-port', '%s', '%s'
ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root
# manila/share/drivers/glusterfs_native.py: 'find', '%s', '-mindepth', '1', '-delete'
find_del: RegExpFilter, /bin/find, root, find, .*, -mindepth, 1, -delete
# manila/share/drivers/glusterfs_native.py: 'umount', '%s'
umount: CommandFilter, umount, root

View File

@ -19,81 +19,376 @@ Manila share is a GlusterFS volume. Unlike the generic driver, this
does not use service VM approach. Instances directly talk with the does not use service VM approach. Instances directly talk with the
GlusterFS backend storage pool. Instance use the 'glusterfs' protocol GlusterFS backend storage pool. Instance use the 'glusterfs' protocol
to mount the GlusterFS share. Access to the share is allowed via to mount the GlusterFS share. Access to the share is allowed via
SSL Certificates. Only the share which has the SSL trust established SSL Certificates. Only the instance which has the SSL trust established
with the GlusterFS backend can mount and hence use the share. with the GlusterFS backend can mount and hence use the share.
Supports working with multiple glusterfs volumes.
""" """
import errno
import pipes
import shutil
import tempfile
from oslo.config import cfg
import six
from manila import exception from manila import exception
from manila.openstack.common import log as logging from manila.openstack.common import log as logging
from manila.share import driver
from manila.share.drivers import glusterfs from manila.share.drivers import glusterfs
from manila import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CLIENT_SSL = 'client.ssl' glusterfs_native_manila_share_opts = [
SERVER_SSL = 'server.ssl' cfg.ListOpt('glusterfs_targets',
AUTH_SSL_ALLOW = 'auth.ssl-allow' default=[],
help='List of GlusterFS volumes that can be used to create '
'shares. Each GlusterFS volume should be of the form '
'[remoteuser@]<volserver>:/<volid>'),
]
CONF = cfg.CONF
CONF.register_opts(glusterfs_native_manila_share_opts)
ACCESS_TYPE_CERT = 'cert' ACCESS_TYPE_CERT = 'cert'
AUTH_SSL_ALLOW = 'auth.ssl-allow'
CLIENT_SSL = 'client.ssl'
NFS_EXPORT_VOL = 'nfs.export-volumes'
SERVER_SSL = 'server.ssl'
class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver): class GlusterfsNativeShareDriver(driver.ExecuteMixin, driver.ShareDriver):
"""GlusterFS native protocol (glusterfs) share driver.
def _setup_gluster_vol(self): Executes commands relating to Shares.
super(GlusterfsNativeShareDriver, self)._setup_gluster_vol() Supports working with multiple glusterfs volumes.
# Enable gluster volume for SSL access. API version history:
# This applies for both service mount and instance mount(s).
# TODO(deepakcs): Once gluster support dual-access, we can limit 1.0 - Initial version.
# service mount to non-ssl access. 1.1 - Support for working with multiple gluster volumes.
gargs, gkw = self.gluster_address.make_gluster_args( """
'volume', 'set', self.gluster_address.volume,
def __init__(self, db, *args, **kwargs):
super(GlusterfsNativeShareDriver, self).__init__(*args, **kwargs)
self.db = db
self._helpers = None
self.gluster_unused_vols_dict = {}
self.gluster_used_vols_dict = {}
self.configuration.append_config_values(
glusterfs_native_manila_share_opts)
self.backend_name = self.configuration.safe_get(
'share_backend_name') or 'GlusterFS-Native'
def do_setup(self, context):
"""Setup the GlusterFS volumes."""
super(GlusterfsNativeShareDriver, self).do_setup(context)
# We don't use a service mount as its not necessary for us.
# Do some sanity checks.
if len(self.configuration.glusterfs_targets) == 0:
# No volumes specified in the config file. Raise exception.
msg = (_("glusterfs_targets list seems to be empty! "
"Add one or more gluster volumes to work "
"with in the glusterfs_targets configuration "
"parameter."))
LOG.error(msg)
raise exception.GlusterfsException(msg)
LOG.info(_("Number of gluster volumes read from config: "
"%(numvols)s"),
{'numvols': len(self.configuration.glusterfs_targets)})
try:
self._execute('mount.glusterfs', check_exit_code=False)
except OSError as exc:
if exc.errno == errno.ENOENT:
msg = (_("mount.glusterfs is not installed."))
LOG.error(msg)
raise exception.GlusterfsException(msg)
else:
msg = (_("Error running mount.glusterfs."))
LOG.error(msg)
raise
# Update gluster_unused_vols_dict, gluster_used_vols_dict by walking
# through the DB.
self._update_gluster_vols_dict(context)
if len(self.gluster_unused_vols_dict) == 0:
# No volumes available for use as share. Warn user.
msg = (_("No unused gluster volumes available for use as share! "
"Create share won't be supported unless existing shares "
"are deleted or add one or more gluster volumes to work "
"with in the glusterfs_targets configuration parameter."))
LOG.warn(msg)
else:
LOG.info(_("Number of gluster volumes in use: %(inuse-numvols)s. "
"Number of gluster volumes available for use as share: "
"%(unused-numvols)s"),
{'inuse-numvols': len(self.gluster_used_vols_dict),
'unused-numvols': len(self.gluster_unused_vols_dict)})
self._setup_gluster_vols()
@utils.synchronized("glusterfs_native", external=False)
def _update_gluster_vols_dict(self, context):
"""Update dict of gluster vols that are used/unused."""
shares = self.db.share_get_all(context)
# Store the gluster volumes in dict thats helpful to track
# (push and pop) in future. {gluster_export: gluster_addr, ...}
# gluster_export is of form hostname:/volname which is unique
# enough to be used as a key.
self.gluster_unused_vols_dict = {}
self.gluster_used_vols_dict = {}
for gv in self.configuration.glusterfs_targets:
gaddr = glusterfs.GlusterAddress(gv)
exp_locn_gv = gaddr.export
# Assume its unused to begin with.
self.gluster_unused_vols_dict.update({exp_locn_gv: gaddr})
for s in shares:
exp_locn_share = s.get('export_location', None)
if exp_locn_share == exp_locn_gv:
# gluster volume is in use, move it to used list.
self.gluster_used_vols_dict.update({exp_locn_gv: gaddr})
self.gluster_unused_vols_dict.pop(exp_locn_gv)
break
@utils.synchronized("glusterfs_native", external=False)
def _setup_gluster_vols(self):
# Enable gluster volumes for SSL access only.
for gluster_addr in six.itervalues(self.gluster_unused_vols_dict):
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', gluster_addr.volume,
NFS_EXPORT_VOL, 'off')
try:
self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc:
msg = (_("Error in gluster volume set during volume setup. "
"Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"),
{'volname': gluster_addr.volume,
'option': NFS_EXPORT_VOL, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', gluster_addr.volume,
CLIENT_SSL, 'on')
try:
self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc:
msg = (_("Error in gluster volume set during volume setup. "
"Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"),
{'volname': gluster_addr.volume,
'option': CLIENT_SSL, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', gluster_addr.volume,
SERVER_SSL, 'on')
try:
self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc:
msg = (_("Error in gluster volume set during volume setup. "
"Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"),
{'volname': gluster_addr.volume,
'option': SERVER_SSL, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
# TODO(deepakcs) Remove this once ssl options can be
# set dynamically.
self._restart_gluster_vol(gluster_addr)
def _restart_gluster_vol(self, gluster_addr):
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'stop', gluster_addr.volume, '--mode=script')
try:
self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc:
msg = (_("Error stopping gluster volume. "
"Volume: %(volname)s, Error: %(error)s"),
{'volname': gluster_addr.volume, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'start', gluster_addr.volume)
try:
self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc:
msg = (_("Error starting gluster volume. "
"Volume: %(volname)s, Error: %(error)s"),
{'volname': gluster_addr.volume, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
@utils.synchronized("glusterfs_native", external=False)
def _pop_gluster_vol(self):
try:
exp_locn, gaddr = self.gluster_unused_vols_dict.popitem()
except KeyError:
msg = (_("Couldn't find a free gluster volume to use."))
LOG.error(msg)
raise exception.GlusterfsException(msg)
self.gluster_used_vols_dict.update({exp_locn: gaddr})
return exp_locn
@utils.synchronized("glusterfs_native", external=False)
def _push_gluster_vol(self, exp_locn):
try:
gaddr = self.gluster_used_vols_dict.pop(exp_locn)
except KeyError:
msg = (_("Couldn't find the share in used list."))
LOG.error(msg)
raise exception.GlusterfsException(msg)
self.gluster_unused_vols_dict.update({exp_locn: gaddr})
def _do_mount(self, gluster_export, mntdir):
cmd = ['mount', '-t', 'glusterfs', gluster_export, mntdir]
try:
self._execute(*cmd, run_as_root=True)
except exception.ProcessExecutionError as exc:
msg = (_("Unable to mount gluster volume. "
"gluster_export: %(export)s, Error: %(error)s"),
{'export': gluster_export, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
def _do_umount(self, mntdir):
cmd = ['umount', mntdir]
try:
self._execute(*cmd, run_as_root=True)
except exception.ProcessExecutionError as exc:
msg = (_("Unable to unmount gluster volume. "
"mount_dir: %(mntdir)s, Error: %(error)s"),
{'mntdir': mntdir, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
def _wipe_gluster_vol(self, gluster_addr):
# Reset the SSL options.
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', gluster_addr.volume,
CLIENT_SSL, 'off')
try:
self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc:
msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
"Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"),
{'volname': gluster_addr.volume,
'option': CLIENT_SSL, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', gluster_addr.volume,
SERVER_SSL, 'off')
try:
self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc:
msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
"Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"),
{'volname': gluster_addr.volume,
'option': SERVER_SSL, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
self._restart_gluster_vol(gluster_addr)
# Create a temporary mount.
gluster_export = gluster_addr.export
tmpdir = tempfile.mkdtemp()
try:
self._do_mount(gluster_export, tmpdir)
except exception.GlusterfsException:
shutil.rmtree(tmpdir, ignore_errors=True)
raise
# Delete only the contents, not the directory.
cmd = ['find', pipes.quote(tmpdir), '-mindepth', '1', '-delete']
try:
self._execute(*cmd, run_as_root=True)
except exception.ProcessExecutionError as exc:
msg = (_("Error trying to wipe gluster volume. "
"gluster_export: %(export)s, Error: %(error)s"),
{'export': gluster_export, 'error': exc.stderr})
LOG.error(msg)
raise exception.GlusterfsException(msg)
finally:
# Unmount.
self._do_umount(tmpdir)
shutil.rmtree(tmpdir, ignore_errors=True)
# Set the SSL options.
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', gluster_addr.volume,
CLIENT_SSL, 'on') CLIENT_SSL, 'on')
try: try:
self._execute(*gargs, **gkw) self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc: except exception.ProcessExecutionError as exc:
LOG.error(_("Error in gluster volume set during volume setup." msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
"Volume: %(volname)s, Option: %(option)s, " "Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"), "Error: %(error)s"),
{'volname': self.gluster_address.volume, {'volname': gluster_addr.volume,
'option': CLIENT_SSL, 'error': exc.stderr}) 'option': CLIENT_SSL, 'error': exc.stderr})
raise LOG.error(msg)
gargs, gkw = self.gluster_address.make_gluster_args( raise exception.GlusterfsException(msg)
'volume', 'set', self.gluster_address.volume,
gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', gluster_addr.volume,
SERVER_SSL, 'on') SERVER_SSL, 'on')
try: try:
self._execute(*gargs, **gkw) self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc: except exception.ProcessExecutionError as exc:
LOG.error(_("Error in gluster volume set during volume setup." msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
"Volume: %(volname)s, Option: %(option)s, " "Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"), "Error: %(error)s"),
{'volname': self.gluster_address.volume, {'volname': gluster_addr.volume,
'option': SERVER_SSL, 'error': exc.stderr}) 'option': SERVER_SSL, 'error': exc.stderr})
raise LOG.error(msg)
raise exception.GlusterfsException(msg)
def create_share(self, ctx, share, share_server=None): self._restart_gluster_vol(gluster_addr)
def create_share(self, context, share, share_server=None):
"""Create a share using GlusterFS volume. """Create a share using GlusterFS volume.
1 Manila share = 1 GlusterFS volume. Ensure that the 1 Manila share = 1 GlusterFS volume. Pick an unused
GlusterFS volume is properly setup to be consumed as GlusterFS volume for use as a share.
a share.
""" """
# Handle the case where create is called after delete share
try: try:
self._setup_gluster_vol() export_location = self._pop_gluster_vol()
except exception.ProcessExecutionError: except exception.GlusterfsException:
LOG.error(_("Unable to create share %s"), (share['name'],)) msg = (_("Error creating share %(share_id)s"),
{'share_id': share['id']})
LOG.error(msg)
raise raise
# TODO(deepakcs): Add validation for gluster mount being present # TODO(deepakcs): Enable quota and set it to the share size.
# (decorator maybe)
# For native protocol, the export_location should be of the form: # For native protocol, the export_location should be of the form:
# server:/volname # server:/volname
export_location = self.gluster_address.export
LOG.info(_("export_location sent back from create_share: %s"), LOG.info(_("export_location sent back from create_share: %s"),
(export_location,)) (export_location,))
return export_location return export_location
@ -101,22 +396,30 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver):
def delete_share(self, context, share, share_server=None): def delete_share(self, context, share, share_server=None):
"""Delete a share on the GlusterFS volume. """Delete a share on the GlusterFS volume.
1 Manila share = 1 GlusterFS volume. Ensure that the 1 Manila share = 1 GlusterFS volume. Put the gluster
GlusterFS volume is reset back to its original state. volume back in the available list.
""" """
# Get the gluster volume back to its original state exp_locn = share.get('export_location', None)
gargs, gkw = self.gluster_address.make_gluster_args(
'volume', 'reset', self.gluster_address.volume)
try: try:
self._execute(*gargs, **gkw) # Get the gluster address associated with the export.
except exception.ProcessExecutionError as exc: gaddr = self.gluster_used_vols_dict[exp_locn]
LOG.error(_("Error in gluster volume reset during delete share." except KeyError:
"Volume: %(volname)s, Error: %(error)s"), msg = (_("Invalid request. Ignoring delete_share request for "
{'volname': self.gluster_address.volume, "share %(share_id)s"), {'share_id': share['id']},)
'error': exc.stderr}) LOG.warn(msg)
return
try:
self._wipe_gluster_vol(gaddr)
self._push_gluster_vol(exp_locn)
except exception.GlusterfsException:
msg = (_("Error during delete_share request for "
"share %(share_id)s"), {'share_id': share['id']},)
LOG.error(msg)
raise raise
# TODO(deepakcs): Disable quota.
def allow_access(self, context, share, access, share_server=None): def allow_access(self, context, share, access, share_server=None):
"""Allow access to a share using certs. """Allow access to a share using certs.
@ -126,20 +429,27 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver):
if access['access_type'] != ACCESS_TYPE_CERT: if access['access_type'] != ACCESS_TYPE_CERT:
raise exception.InvalidShareAccess(_("Only 'cert' access type " raise exception.InvalidShareAccess(_("Only 'cert' access type "
"allowed")) "allowed"))
exp_locn = share.get('export_location', None)
gluster_addr = self.gluster_used_vols_dict.get(exp_locn)
gargs, gkw = self.gluster_address.make_gluster_args( gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'set', self.gluster_address.volume, 'volume', 'set', gluster_addr.volume,
AUTH_SSL_ALLOW, access['access_to']) AUTH_SSL_ALLOW, access['access_to'])
try: try:
self._execute(*gargs, **gkw) self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc: except exception.ProcessExecutionError as exc:
LOG.error(_("Error in gluster volume set during allow access." msg = (_("Error in gluster volume set during allow access. "
"Volume: %(volname)s, Option: %(option)s, " "Volume: %(volname)s, Option: %(option)s, "
"access_to: %(access_to)s, Error: %(error)s"), "access_to: %(access_to)s, Error: %(error)s"),
{'volname': self.gluster_address.volume, {'volname': gluster_addr.volume,
'option': AUTH_SSL_ALLOW, 'option': AUTH_SSL_ALLOW,
'access_to': access['access_to'], 'error': exc.stderr}) 'access_to': access['access_to'], 'error': exc.stderr})
raise LOG.error(msg)
raise exception.GlusterfsException(msg)
# TODO(deepakcs) Remove this once ssl options can be
# set dynamically.
self._restart_gluster_vol(gluster_addr)
def deny_access(self, context, share, access, share_server=None): def deny_access(self, context, share, access, share_server=None):
"""Deny access to a share that's using cert based auth. """Deny access to a share that's using cert based auth.
@ -151,16 +461,63 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver):
raise exception.InvalidShareAccess(_("Only 'cert' access type " raise exception.InvalidShareAccess(_("Only 'cert' access type "
"allowed for access " "allowed for access "
"removal.")) "removal."))
exp_locn = share.get('export_location', None)
gluster_addr = self.gluster_used_vols_dict.get(exp_locn)
gargs, gkw = self.gluster_address.make_gluster_args( gargs, gkw = gluster_addr.make_gluster_args(
'volume', 'reset', self.gluster_address.volume, 'volume', 'reset', gluster_addr.volume,
AUTH_SSL_ALLOW) AUTH_SSL_ALLOW)
try: try:
self._execute(*gargs, **gkw) self._execute(*gargs, **gkw)
except exception.ProcessExecutionError as exc: except exception.ProcessExecutionError as exc:
LOG.error(_("Error in gluster volume reset during deny access." msg = (_("Error in gluster volume reset during deny access. "
"Volume: %(volname)s, Option: %(option)s, " "Volume: %(volname)s, Option: %(option)s, "
"Error: %(error)s"), "Error: %(error)s"),
{'volname': self.gluster_address.volume, {'volname': gluster_addr.volume,
'option': AUTH_SSL_ALLOW, 'error': exc.stderr}) 'option': AUTH_SSL_ALLOW, 'error': exc.stderr})
raise LOG.error(msg)
raise exception.GlusterfsException(msg)
# TODO(deepakcs) Remove this once ssl options can be
# set dynamically.
self._restart_gluster_vol(gluster_addr)
def get_share_stats(self, refresh=False):
"""Get share stats.
If 'refresh' is True, update the stats first.
"""
if refresh:
self._update_share_stats()
return self._stats
def _update_share_stats(self):
"""Send stats info for the GlusterFS volume."""
LOG.debug("Updating share stats")
data = {}
data["share_backend_name"] = self.backend_name
data["vendor_name"] = 'Red Hat'
data["driver_version"] = '1.1'
data["storage_protocol"] = 'glusterfs'
data['reserved_percentage'] = (
self.configuration.reserved_share_percentage)
data['QoS_support'] = False
# We don't use a service mount to get stats data.
# Instead we use glusterfs quota feature and use that to limit
# the share to its expected share['size'].
# TODO(deepakcs): Change below once glusterfs supports volume
# specific stats via the gluster cli.
data['total_capacity_gb'] = 'infinite'
data['free_capacity_gb'] = 'infinite'
self._stats = data
def ensure_share(self, context, share, share_server=None):
"""Invoked to ensure that share is exported."""
pass

View File

@ -0,0 +1,806 @@
# Copyright (c) 2014 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
""" GlusterFS native protocol (glusterfs) driver for shares.
Test cases for GlusterFS native protocol driver.
"""
import shutil
import tempfile
import mock
from oslo.config import cfg
from manila import context
from manila import exception
from manila.share import configuration as config
from manila.share.drivers import glusterfs
from manila.share.drivers import glusterfs_native
from manila import test
from manila.tests import fake_utils
CONF = cfg.CONF
def fake_db_share1(**kwargs):
share = {
'id': 'fakeid',
'name': 'fakename',
'size': 1,
'share_proto': 'glusterfs',
'export_location': 'host1:/gv1',
}
share.update(kwargs)
return [share]
def fake_db_share2(**kwargs):
share = {
'id': 'fakeid',
'name': 'fakename',
'size': 1,
'share_proto': 'glusterfs',
'export_location': 'host2:/gv2',
}
share.update(kwargs)
return [share]
def new_share(**kwargs):
share = {
'id': 'fakeid',
'name': 'fakename',
'size': 1,
'share_proto': 'glusterfs',
}
share.update(kwargs)
return share
class GlusterfsNativeShareDriverTestCase(test.TestCase):
"""Tests GlusterfsNativeShareDriver."""
def setUp(self):
super(GlusterfsNativeShareDriverTestCase, self).setUp()
fake_utils.stub_out_utils_execute(self.stubs)
self._execute = fake_utils.fake_execute
self._context = context.get_admin_context()
self.gluster_target1 = 'root@host1:/gv1'
self.gluster_target2 = 'root@host2:/gv2'
CONF.set_default('glusterfs_targets',
[self.gluster_target1, self.gluster_target2])
self.fake_conf = config.Configuration(None)
self._db = mock.Mock()
self._driver = glusterfs_native.GlusterfsNativeShareDriver(
self._db, execute=self._execute,
configuration=self.fake_conf)
self.stubs.Set(tempfile, 'mkdtemp',
mock.Mock(return_value='/tmp/tmpKGHKJ'))
self.addCleanup(fake_utils.fake_execute_set_repliers, [])
self.addCleanup(fake_utils.fake_execute_clear_log)
def test_do_setup(self):
self._driver._setup_gluster_vols = mock.Mock()
self._db.share_get_all = mock.Mock(return_value=[])
expected_exec = ['mount.glusterfs']
gaddr = glusterfs.GlusterAddress
self._driver.do_setup(self._context)
self.assertEqual(2, len(self._driver.gluster_unused_vols_dict))
self.assertTrue(gaddr(self.gluster_target1).export in
self._driver.gluster_unused_vols_dict)
self.assertTrue(gaddr(self.gluster_target2).export in
self._driver.gluster_unused_vols_dict)
self.assertTrue(self._driver._setup_gluster_vols.called)
self.assertTrue(self._db.share_get_all.called)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
def test_do_setup_glusterfs_targets_empty(self):
self._driver.configuration.glusterfs_targets = []
self.assertRaises(exception.GlusterfsException, self._driver.do_setup,
self._context)
def test_update_gluster_vols_dict(self):
self._db.share_get_all = mock.Mock(return_value=fake_db_share1())
self._driver._update_gluster_vols_dict(self._context)
self.assertEqual(1, len(self._driver.gluster_used_vols_dict))
self.assertEqual(1, len(self._driver.gluster_unused_vols_dict))
self.assertTrue(self._db.share_get_all.called)
share_in_use = fake_db_share1()[0]
share_not_in_use = fake_db_share2()[0]
self.assertTrue(
share_in_use['export_location'] in
self._driver.gluster_used_vols_dict)
self.assertFalse(
share_not_in_use['export_location'] in
self._driver.gluster_used_vols_dict)
self.assertTrue(
share_not_in_use['export_location'] in
self._driver.gluster_unused_vols_dict)
self.assertFalse(
share_in_use['export_location'] in
self._driver.gluster_unused_vols_dict)
def test_setup_gluster_vols(self):
self._driver._restart_gluster_vol = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
expected_exec = [
'ssh root@host2 gluster volume set gv2 nfs.export-volumes off',
'ssh root@host2 gluster volume set gv2 client.ssl on',
'ssh root@host2 gluster volume set gv2 server.ssl on']
self._driver._setup_gluster_vols()
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
self.assertTrue(self._driver._restart_gluster_vol.called)
def test_setup_gluster_vols_excp1(self):
self._driver._restart_gluster_vol = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host2 gluster volume set gv2 nfs.export-volumes off']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._setup_gluster_vols)
self.assertFalse(self._driver._restart_gluster_vol.called)
def test_setup_gluster_vols_excp2(self):
self._driver._restart_gluster_vol = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host2 gluster volume set gv2 client.ssl on']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._setup_gluster_vols)
self.assertFalse(self._driver._restart_gluster_vol.called)
def test_setup_gluster_vols_excp3(self):
self._driver._restart_gluster_vol = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host2 gluster volume set gv2 server.ssl on']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._setup_gluster_vols)
self.assertFalse(self._driver._restart_gluster_vol.called)
def test_restart_gluster_vol(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
expected_exec = [
'ssh root@host1 gluster volume stop gv1 --mode=script',
'ssh root@host1 gluster volume start gv1']
self._driver._restart_gluster_vol(gaddr1)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
def test_restart_gluster_vol_excp1(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host1 gluster volume stop gv1 --mode=script']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._restart_gluster_vol, gaddr1)
def test_restart_gluster_vol_excp2(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host1 gluster volume start gv1']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._restart_gluster_vol, gaddr1)
def test_pop_gluster_vol(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
exp_locn = self._driver._pop_gluster_vol()
self.assertEqual(0, len(self._driver.gluster_unused_vols_dict))
self.assertFalse(
gaddr2.export in self._driver.gluster_unused_vols_dict)
self.assertEqual(2, len(self._driver.gluster_used_vols_dict))
self.assertTrue(
gaddr2.export in self._driver.gluster_used_vols_dict)
self.assertEqual(exp_locn, gaddr2.export)
def test_pop_gluster_vol_excp(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {
gaddr2.export: gaddr2, gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {}
self.assertRaises(exception.GlusterfsException,
self._driver._pop_gluster_vol)
def test_push_gluster_vol(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {
gaddr1.export: gaddr1, gaddr2.export: gaddr2}
self._driver.gluster_unused_vols_dict = {}
self._driver._push_gluster_vol(gaddr2.export)
self.assertEqual(1, len(self._driver.gluster_unused_vols_dict))
self.assertTrue(
gaddr2.export in self._driver.gluster_unused_vols_dict)
self.assertEqual(1, len(self._driver.gluster_used_vols_dict))
self.assertFalse(
gaddr2.export in self._driver.gluster_used_vols_dict)
def test_push_gluster_vol_excp(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {}
self.assertRaises(exception.GlusterfsException,
self._driver._push_gluster_vol, gaddr2.export)
def test_do_mount(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
tmpdir = '/tmp/tmpKGHKJ'
expected_exec = ['mount -t glusterfs host1:/gv1 /tmp/tmpKGHKJ']
self._driver._do_mount(gaddr1.export, tmpdir)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
def test_do_mount_excp(self):
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
tmpdir = '/tmp/tmpKGHKJ'
expected_exec = ['mount -t glusterfs host1:/gv1 /tmp/tmpKGHKJ']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._do_mount, gaddr1.export, tmpdir)
def test_do_umount(self):
tmpdir = '/tmp/tmpKGHKJ'
expected_exec = ['umount /tmp/tmpKGHKJ']
self._driver._do_umount(tmpdir)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
def test_do_umount_excp(self):
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
tmpdir = '/tmp/tmpKGHKJ'
expected_exec = ['umount /tmp/tmpKGHKJ']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._do_umount, tmpdir)
def test_wipe_gluster_vol(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_umount = mock.Mock()
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
expected_exec = [
'ssh root@host1 gluster volume set gv1 client.ssl off',
'ssh root@host1 gluster volume set gv1 server.ssl off',
'find /tmp/tmpKGHKJ -mindepth 1 -delete',
'ssh root@host1 gluster volume set gv1 client.ssl on',
'ssh root@host1 gluster volume set gv1 server.ssl on']
self._driver._wipe_gluster_vol(gaddr1)
self.assertEqual(2, self._driver._restart_gluster_vol.call_count)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
self.assertTrue(tempfile.mkdtemp.called)
self.assertTrue(self._driver._do_mount.called)
self.assertTrue(self._driver._do_umount.called)
self.assertTrue(shutil.rmtree.called)
def test_wipe_gluster_vol_excp1(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_umount = mock.Mock()
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host1 gluster volume set gv1 client.ssl off']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._wipe_gluster_vol, gaddr1)
self.assertFalse(self._driver._restart_gluster_vol.called)
self.assertFalse(tempfile.mkdtemp.called)
self.assertFalse(self._driver._do_mount.called)
self.assertFalse(self._driver._do_umount.called)
self.assertFalse(shutil.rmtree.called)
def test_wipe_gluster_vol_excp2(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_umount = mock.Mock()
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host1 gluster volume set gv1 server.ssl off']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._wipe_gluster_vol, gaddr1)
self.assertFalse(self._driver._restart_gluster_vol.called)
self.assertFalse(tempfile.mkdtemp.called)
self.assertFalse(self._driver._do_mount.called)
self.assertFalse(self._driver._do_umount.called)
self.assertFalse(shutil.rmtree.called)
def test_wipe_gluster_vol_excp3(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_umount = mock.Mock()
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host1 gluster volume set gv1 client.ssl on']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._wipe_gluster_vol, gaddr1)
self.assertTrue(self._driver._restart_gluster_vol.called)
self.assertTrue(tempfile.mkdtemp.called)
self.assertTrue(self._driver._do_mount.called)
self.assertTrue(self._driver._do_umount.called)
self.assertTrue(shutil.rmtree.called)
def test_wipe_gluster_vol_excp4(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_umount = mock.Mock()
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host1 gluster volume set gv1 server.ssl on']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._wipe_gluster_vol, gaddr1)
self.assertTrue(self._driver._restart_gluster_vol.called)
self.assertTrue(tempfile.mkdtemp.called)
self.assertTrue(self._driver._do_mount.called)
self.assertTrue(self._driver._do_umount.called)
self.assertTrue(shutil.rmtree.called)
def test_wipe_gluster_vol_excp5(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_umount = mock.Mock()
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'find /tmp/tmpKGHKJ -mindepth 1 -delete']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver._wipe_gluster_vol, gaddr1)
self.assertTrue(self._driver._restart_gluster_vol.called)
self.assertTrue(tempfile.mkdtemp.called)
self.assertTrue(self._driver._do_mount.called)
self.assertTrue(self._driver._do_umount.called)
self.assertTrue(shutil.rmtree.called)
def test_wipe_gluster_vol_mount_fail(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_mount.side_effect = exception.GlusterfsException
self._driver._do_umount = mock.Mock()
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
expected_exec = [
'ssh root@host1 gluster volume set gv1 client.ssl off',
'ssh root@host1 gluster volume set gv1 server.ssl off']
self.assertRaises(exception.GlusterfsException,
self._driver._wipe_gluster_vol, gaddr1)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
self.assertTrue(self._driver._restart_gluster_vol.called)
self.assertTrue(tempfile.mkdtemp.called)
self.assertTrue(self._driver._do_mount.called)
self.assertFalse(self._driver._do_umount.called)
self.assertTrue(shutil.rmtree.called)
def test_wipe_gluster_vol_umount_fail(self):
self._driver._restart_gluster_vol = mock.Mock()
self._driver._do_mount = mock.Mock()
self._driver._do_umount = mock.Mock()
self._driver._do_umount.side_effect = exception.GlusterfsException
shutil.rmtree = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
expected_exec = [
'ssh root@host1 gluster volume set gv1 client.ssl off',
'ssh root@host1 gluster volume set gv1 server.ssl off',
'find /tmp/tmpKGHKJ -mindepth 1 -delete']
self.assertRaises(exception.GlusterfsException,
self._driver._wipe_gluster_vol, gaddr1)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
self.assertTrue(self._driver._restart_gluster_vol.called)
self.assertTrue(tempfile.mkdtemp.called)
self.assertTrue(self._driver._do_mount.called)
self.assertTrue(self._driver._do_umount.called)
self.assertFalse(shutil.rmtree.called)
def test_create_share(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
share = new_share()
exp_locn = self._driver.create_share(self._context, share)
self.assertEqual(exp_locn, gaddr2.export)
def test_create_share_excp(self):
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {
gaddr2.export: gaddr2, gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {}
share = new_share()
self.assertRaises(exception.GlusterfsException,
self._driver.create_share, self._context, share)
def test_delete_share(self):
self._driver._wipe_gluster_vol = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {
gaddr2.export: gaddr2, gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {}
share = fake_db_share2()[0]
self._driver.delete_share(self._context, share)
self.assertEqual(1, len(self._driver.gluster_used_vols_dict))
self.assertEqual(1, len(self._driver.gluster_unused_vols_dict))
self.assertTrue(self._driver._wipe_gluster_vol.called)
def test_delete_share_warn(self):
glusterfs_native.LOG.warn = mock.Mock()
self._driver._wipe_gluster_vol = mock.Mock()
self._driver._push_gluster_vol = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {}
self._driver.gluster_unused_vols_dict = {
gaddr2.export: gaddr2, gaddr1.export: gaddr1}
share = fake_db_share2()[0]
self._driver.delete_share(self._context, share)
self.assertTrue(glusterfs_native.LOG.warn.called)
self.assertFalse(self._driver._wipe_gluster_vol.called)
self.assertFalse(self._driver._push_gluster_vol.called)
def test_delete_share_excp1(self):
self._driver._wipe_gluster_vol = mock.Mock()
self._driver._wipe_gluster_vol.side_effect = (
exception.GlusterfsException)
self._driver._push_gluster_vol = mock.Mock()
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {
gaddr2.export: gaddr2, gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {}
share = fake_db_share2()[0]
self.assertRaises(exception.GlusterfsException,
self._driver.delete_share, self._context, share)
self.assertTrue(self._driver._wipe_gluster_vol.called)
self.assertFalse(self._driver._push_gluster_vol.called)
def test_allow_access(self):
self._driver._restart_gluster_vol = mock.Mock()
access = {'access_type': 'cert', 'access_to': 'client.example.com'}
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
share = fake_db_share1()[0]
expected_exec = [
'ssh root@host1 gluster volume set gv1 '
'auth.ssl-allow client.example.com']
self._driver.allow_access(self._context, share, access)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
self.assertTrue(self._driver._restart_gluster_vol.called)
def test_allow_access_invalid_access_type(self):
self._driver._restart_gluster_vol = mock.Mock()
access = {'access_type': 'invalid', 'access_to': 'client.example.com'}
share = fake_db_share1()[0]
expected_exec = []
self.assertRaises(exception.InvalidShareAccess,
self._driver.allow_access,
self._context, share, access)
self.assertFalse(self._driver._restart_gluster_vol.called)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
def test_allow_access_excp(self):
self._driver._restart_gluster_vol = mock.Mock()
access = {'access_type': 'cert', 'access_to': 'client.example.com'}
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
share = fake_db_share1()[0]
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
expected_exec = [
'ssh root@host1 gluster volume set gv1 '
'auth.ssl-allow client.example.com']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver.allow_access,
self._context, share, access)
self.assertFalse(self._driver._restart_gluster_vol.called)
def test_deny_access(self):
self._driver._restart_gluster_vol = mock.Mock()
access = {'access_type': 'cert', 'access_to': 'NotApplicable'}
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
share = fake_db_share1()[0]
expected_exec = [
'ssh root@host1 gluster volume reset gv1 auth.ssl-allow']
self._driver.deny_access(self._context, share, access)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
self.assertTrue(self._driver._restart_gluster_vol.called)
def test_deny_access_invalid_access_type(self):
self._driver._restart_gluster_vol = mock.Mock()
access = {'access_type': 'invalid', 'access_to': 'NotApplicable'}
share = fake_db_share1()[0]
expected_exec = []
self.assertRaises(exception.InvalidShareAccess,
self._driver.deny_access,
self._context, share, access)
self.assertFalse(self._driver._restart_gluster_vol.called)
self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
def test_deny_access_excp(self):
self._driver._restart_gluster_vol = mock.Mock()
access = {'access_type': 'cert', 'access_to': 'NotApplicable'}
gaddr = glusterfs.GlusterAddress
gaddr1 = gaddr(self.gluster_target1)
gaddr2 = gaddr(self.gluster_target2)
self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
share = fake_db_share1()[0]
expected_exec = [
'ssh root@host1 gluster volume reset gv1 auth.ssl-allow']
def exec_runner(*ignore_args, **ignore_kwargs):
raise exception.ProcessExecutionError
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.GlusterfsException,
self._driver.deny_access,
self._context, share, access)
self.assertFalse(self._driver._restart_gluster_vol.called)
def test_get_share_stats_refresh_false(self):
self._driver._stats = mock.Mock()
ret = self._driver.get_share_stats()
self.assertEqual(ret, self._driver._stats)
def test_get_share_stats_refresh_true(self):
def foo():
self._driver._stats = {'key': 'value'}
self._driver._update_share_stats = mock.Mock(side_effect=foo)
ret = self._driver.get_share_stats(refresh=True)
self.assertEqual(ret, {'key': 'value'})
def test_update_share_stats(self):
test_data = {
'share_backend_name': 'GlusterFS-Native',
'vendor_name': 'Red Hat',
'driver_version': '1.1',
'storage_protocol': 'glusterfs',
'reserved_percentage': 0,
'QoS_support': False,
'total_capacity_gb': 'infinite',
'free_capacity_gb': 'infinite',
}
self._driver._update_share_stats()
self.assertEqual(self._driver._stats, test_data)

View File

@ -1,199 +0,0 @@
# Copyright (c) 2014 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
""" GlusterFS native protocol (glusterfs) driver for shares.
Test cases for GlusterFS native protocol driver.
"""
import mock
from oslo.config import cfg
from manila import context
from manila import exception
from manila.share import configuration as config
from manila.share.drivers import glusterfs_native
from manila import test
from manila.tests.db import fakes as db_fakes
from manila.tests import fake_utils
CONF = cfg.CONF
gluster_address_attrs = {
'export': '127.0.0.1:/testvol',
'host': '127.0.0.1',
'qualified': 'testuser@127.0.0.1:/testvol',
'remote_user': 'testuser',
'volume': 'testvol',
}
def fake_share(**kwargs):
share = {
'id': 'fakeid',
'name': 'fakename',
'size': 1,
'share_proto': 'glusterfs',
'export_location': '127.0.0.1:/mnt/glusterfs/testvol',
}
share.update(kwargs)
return db_fakes.FakeModel(share)
class GlusterfsNativeShareDriverTestCase(test.TestCase):
"""Tests GlusterfsNativeShareDriver."""
def setUp(self):
super(GlusterfsNativeShareDriverTestCase, self).setUp()
fake_utils.stub_out_utils_execute(self.stubs)
self._execute = fake_utils.fake_execute
self._context = context.get_admin_context()
CONF.set_default('glusterfs_mount_point_base', '/mnt/glusterfs')
CONF.set_default('reserved_share_percentage', 50)
self.fake_conf = config.Configuration(None)
self._db = mock.Mock()
self._driver = glusterfs_native.GlusterfsNativeShareDriver(
self._db, execute=self._execute,
configuration=self.fake_conf)
self._driver.gluster_address = mock.Mock(**gluster_address_attrs)
self.share = fake_share()
self.addCleanup(fake_utils.fake_execute_set_repliers, [])
self.addCleanup(fake_utils.fake_execute_clear_log)
def test_create_share(self):
self._driver._setup_gluster_vol = mock.Mock()
expected = gluster_address_attrs['export']
actual = self._driver.create_share(self._context, self.share)
self.assertTrue(self._driver._setup_gluster_vol.called)
self.assertEqual(actual, expected)
def test_create_share_error(self):
self._driver._setup_gluster_vol = mock.Mock()
self._driver._setup_gluster_vol.side_effect = (
exception.ProcessExecutionError)
self.assertRaises(exception.ProcessExecutionError,
self._driver.create_share, self._context, self.share)
def test_delete_share(self):
self._driver.gluster_address = mock.Mock(
make_gluster_args=mock.Mock(return_value=(('true',), {})))
self._driver.delete_share(self._context, self.share)
self.assertTrue(self._driver.gluster_address.make_gluster_args.called)
self.assertEqual(
self._driver.gluster_address.make_gluster_args.call_args[0][1],
'reset')
def test_delete_share_error(self):
self._driver.gluster_address = mock.Mock(
make_gluster_args=mock.Mock(return_value=(('true',), {})))
def exec_runner(*ignore_args, **ignore_kw):
raise exception.ProcessExecutionError
expected_exec = ['true']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.ProcessExecutionError,
self._driver.delete_share, self._context, self.share)
def test_allow_access(self):
self._driver.gluster_address = mock.Mock(
make_gluster_args=mock.Mock(return_value=(('true',), {})))
access = {'access_type': 'cert', 'access_to': 'client.example.com'}
self._driver.allow_access(self._context, self.share, access)
self.assertTrue(self._driver.gluster_address.make_gluster_args.called)
self.assertEqual(
self._driver.gluster_address.make_gluster_args.call_args[0][1],
'set')
self.assertEqual(
self._driver.gluster_address.make_gluster_args.call_args[0][-2],
'auth.ssl-allow')
self.assertEqual(
self._driver.gluster_address.make_gluster_args.call_args[0][-1],
access['access_to'])
def test_allow_access_error(self):
# Invalid access type
access = {'access_type': 'invalid', 'access_to': 'client.example.com'}
self.assertRaises(exception.InvalidShareAccess,
self._driver.allow_access, self._context, self.share,
access)
# ProcessExecutionError
self._driver.gluster_address = mock.Mock(
make_gluster_args=mock.Mock(return_value=(('true',), {})))
access = {'access_type': 'cert', 'access_to': 'client.example.com'}
def exec_runner(*ignore_args, **ignore_kw):
raise exception.ProcessExecutionError
expected_exec = ['true']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.ProcessExecutionError,
self._driver.allow_access, self._context, self.share,
access)
def test_deny_access(self):
self._driver.gluster_address = mock.Mock(
make_gluster_args=mock.Mock(return_value=(('true',), {})))
access = {'access_type': 'cert', 'access_to': 'client.example.com'}
self._driver.deny_access(self._context, self.share, access)
self.assertTrue(self._driver.gluster_address.make_gluster_args.called)
self.assertEqual(
self._driver.gluster_address.make_gluster_args.call_args[0][1],
'reset')
self.assertEqual(
self._driver.gluster_address.make_gluster_args.call_args[0][-1],
'auth.ssl-allow')
def test_deny_access_error(self):
# Invalid access type
access = {'access_type': 'invalid', 'access_to': 'client.example.com'}
self.assertRaises(exception.InvalidShareAccess,
self._driver.deny_access, self._context, self.share,
access)
# ProcessExecutionError
self._driver.gluster_address = mock.Mock(
make_gluster_args=mock.Mock(return_value=(('true',), {})))
access = {'access_type': 'cert', 'access_to': 'client.example.com'}
def exec_runner(*ignore_args, **ignore_kw):
raise exception.ProcessExecutionError
expected_exec = ['true']
fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
self.assertRaises(exception.ProcessExecutionError,
self._driver.deny_access, self._context, self.share,
access)