diff --git a/etc/manila/rootwrap.d/share.filters b/etc/manila/rootwrap.d/share.filters index 57f2a63237..621583c45a 100644 --- a/etc/manila/rootwrap.d/share.filters +++ b/etc/manila/rootwrap.d/share.filters @@ -17,3 +17,9 @@ ip: CommandFilter, /sbin/ip, root # manila/network/linux/interface.py: 'ovs-vsctl', 'add-port', '%s', '%s' 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 diff --git a/manila/share/drivers/glusterfs_native.py b/manila/share/drivers/glusterfs_native.py index c4f55748db..dba7c152ad 100644 --- a/manila/share/drivers/glusterfs_native.py +++ b/manila/share/drivers/glusterfs_native.py @@ -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 GlusterFS backend storage pool. Instance use the 'glusterfs' protocol 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. + +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.openstack.common import log as logging +from manila.share import driver from manila.share.drivers import glusterfs +from manila import utils LOG = logging.getLogger(__name__) -CLIENT_SSL = 'client.ssl' -SERVER_SSL = 'server.ssl' -AUTH_SSL_ALLOW = 'auth.ssl-allow' +glusterfs_native_manila_share_opts = [ + cfg.ListOpt('glusterfs_targets', + default=[], + help='List of GlusterFS volumes that can be used to create ' + 'shares. Each GlusterFS volume should be of the form ' + '[remoteuser@]:/'), +] + +CONF = cfg.CONF +CONF.register_opts(glusterfs_native_manila_share_opts) + 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): - super(GlusterfsNativeShareDriver, self)._setup_gluster_vol() + Executes commands relating to Shares. + Supports working with multiple glusterfs volumes. - # Enable gluster volume for SSL access. - # This applies for both service mount and instance mount(s). + API version history: - # TODO(deepakcs): Once gluster support dual-access, we can limit - # service mount to non-ssl access. - gargs, gkw = self.gluster_address.make_gluster_args( - 'volume', 'set', self.gluster_address.volume, + 1.0 - Initial version. + 1.1 - Support for working with multiple gluster volumes. + """ + + 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') try: self._execute(*gargs, **gkw) except exception.ProcessExecutionError as exc: - LOG.error(_("Error in gluster volume set during volume setup." - "Volume: %(volname)s, Option: %(option)s, " - "Error: %(error)s"), - {'volname': self.gluster_address.volume, - 'option': CLIENT_SSL, 'error': exc.stderr}) - raise - gargs, gkw = self.gluster_address.make_gluster_args( - 'volume', 'set', self.gluster_address.volume, + 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, 'on') try: self._execute(*gargs, **gkw) except exception.ProcessExecutionError as exc: - LOG.error(_("Error in gluster volume set during volume setup." - "Volume: %(volname)s, Option: %(option)s, " - "Error: %(error)s"), - {'volname': self.gluster_address.volume, - 'option': SERVER_SSL, 'error': exc.stderr}) - raise + 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) - 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. - 1 Manila share = 1 GlusterFS volume. Ensure that the - GlusterFS volume is properly setup to be consumed as - a share. + 1 Manila share = 1 GlusterFS volume. Pick an unused + GlusterFS volume for use as a share. """ - - # Handle the case where create is called after delete share try: - self._setup_gluster_vol() - except exception.ProcessExecutionError: - LOG.error(_("Unable to create share %s"), (share['name'],)) + export_location = self._pop_gluster_vol() + except exception.GlusterfsException: + msg = (_("Error creating share %(share_id)s"), + {'share_id': share['id']}) + LOG.error(msg) raise - # TODO(deepakcs): Add validation for gluster mount being present - # (decorator maybe) + # TODO(deepakcs): Enable quota and set it to the share size. # For native protocol, the export_location should be of the form: # server:/volname - export_location = self.gluster_address.export - LOG.info(_("export_location sent back from create_share: %s"), (export_location,)) return export_location @@ -101,22 +396,30 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver): def delete_share(self, context, share, share_server=None): """Delete a share on the GlusterFS volume. - 1 Manila share = 1 GlusterFS volume. Ensure that the - GlusterFS volume is reset back to its original state. + 1 Manila share = 1 GlusterFS volume. Put the gluster + volume back in the available list. """ - # Get the gluster volume back to its original state - - gargs, gkw = self.gluster_address.make_gluster_args( - 'volume', 'reset', self.gluster_address.volume) + exp_locn = share.get('export_location', None) try: - self._execute(*gargs, **gkw) - except exception.ProcessExecutionError as exc: - LOG.error(_("Error in gluster volume reset during delete share." - "Volume: %(volname)s, Error: %(error)s"), - {'volname': self.gluster_address.volume, - 'error': exc.stderr}) + # Get the gluster address associated with the export. + gaddr = self.gluster_used_vols_dict[exp_locn] + except KeyError: + msg = (_("Invalid request. Ignoring delete_share request for " + "share %(share_id)s"), {'share_id': share['id']},) + 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 + # TODO(deepakcs): Disable quota. + def allow_access(self, context, share, access, share_server=None): """Allow access to a share using certs. @@ -126,20 +429,27 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver): if access['access_type'] != ACCESS_TYPE_CERT: raise exception.InvalidShareAccess(_("Only 'cert' access type " "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( - 'volume', 'set', self.gluster_address.volume, + gargs, gkw = gluster_addr.make_gluster_args( + 'volume', 'set', gluster_addr.volume, AUTH_SSL_ALLOW, access['access_to']) try: self._execute(*gargs, **gkw) except exception.ProcessExecutionError as exc: - LOG.error(_("Error in gluster volume set during allow access." - "Volume: %(volname)s, Option: %(option)s, " - "access_to: %(access_to)s, Error: %(error)s"), - {'volname': self.gluster_address.volume, - 'option': AUTH_SSL_ALLOW, - 'access_to': access['access_to'], 'error': exc.stderr}) - raise + msg = (_("Error in gluster volume set during allow access. " + "Volume: %(volname)s, Option: %(option)s, " + "access_to: %(access_to)s, Error: %(error)s"), + {'volname': gluster_addr.volume, + 'option': AUTH_SSL_ALLOW, + 'access_to': access['access_to'], '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 deny_access(self, context, share, access, share_server=None): """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 " "allowed for access " "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( - 'volume', 'reset', self.gluster_address.volume, + gargs, gkw = gluster_addr.make_gluster_args( + 'volume', 'reset', gluster_addr.volume, AUTH_SSL_ALLOW) try: self._execute(*gargs, **gkw) except exception.ProcessExecutionError as exc: - LOG.error(_("Error in gluster volume reset during deny access." - "Volume: %(volname)s, Option: %(option)s, " - "Error: %(error)s"), - {'volname': self.gluster_address.volume, - 'option': AUTH_SSL_ALLOW, 'error': exc.stderr}) - raise + msg = (_("Error in gluster volume reset during deny access. " + "Volume: %(volname)s, Option: %(option)s, " + "Error: %(error)s"), + {'volname': gluster_addr.volume, + 'option': AUTH_SSL_ALLOW, '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 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 diff --git a/manila/tests/share/drivers/test_glusterfs_native.py b/manila/tests/share/drivers/test_glusterfs_native.py new file mode 100644 index 0000000000..899031ec13 --- /dev/null +++ b/manila/tests/share/drivers/test_glusterfs_native.py @@ -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) diff --git a/manila/tests/test_share_glusterfs_native.py b/manila/tests/test_share_glusterfs_native.py deleted file mode 100644 index ec9e83edf4..0000000000 --- a/manila/tests/test_share_glusterfs_native.py +++ /dev/null @@ -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)