# 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 volume mapped share layout testcases. """ import re import shutil import tempfile from unittest import mock import ddt from oslo_config import cfg from manila.common import constants from manila import context from manila import exception from manila.share import configuration as config from manila.share.drivers.glusterfs import common from manila.share.drivers.glusterfs import layout_volume from manila import test from manila.tests import fake_utils CONF = cfg.CONF def new_share(**kwargs): share = { 'id': 'fakeid', 'name': 'fakename', 'size': 1, 'share_proto': 'glusterfs', } share.update(kwargs) return share def glusterXMLOut(**kwargs): template = """ %(ret)d %(errno)d fake error """ return template % kwargs, '' FAKE_UUID1 = '11111111-1111-1111-1111-111111111111' FAKE_UUID2 = '22222222-2222-2222-2222-222222222222' @ddt.ddt class GlusterfsVolumeMappedLayoutTestCase(test.TestCase): """Tests GlusterfsVolumeMappedLayout.""" def setUp(self): super(GlusterfsVolumeMappedLayoutTestCase, self).setUp() fake_utils.stub_out_utils_execute(self) self._execute = fake_utils.fake_execute self._context = context.get_admin_context() self.glusterfs_target1 = 'root@host1:/gv1' self.glusterfs_target2 = 'root@host2:/gv2' self.glusterfs_server1 = 'root@host1' self.glusterfs_server2 = 'root@host2' self.glusterfs_server1_volumes = 'manila-share-1-1G\nshare1' self.glusterfs_server2_volumes = 'manila-share-2-2G\nshare2' self.share1 = new_share( export_location=self.glusterfs_target1, status=constants.STATUS_AVAILABLE) self.share2 = new_share( export_location=self.glusterfs_target2, status=constants.STATUS_AVAILABLE) gmgr = common.GlusterManager self.gmgr1 = gmgr(self.glusterfs_server1, self._execute, None, None, requires={'volume': False}) self.gmgr2 = gmgr(self.glusterfs_server2, self._execute, None, None, requires={'volume': False}) self.glusterfs_volumes_dict = ( {'root@host1:/manila-share-1-1G': {'size': 1}, 'root@host2:/manila-share-2-2G': {'size': 2}}) self.glusterfs_used_vols = set([ 'root@host1:/manila-share-1-1G', 'root@host2:/manila-share-2-2G']) CONF.set_default('glusterfs_servers', [self.glusterfs_server1, self.glusterfs_server2]) CONF.set_default('glusterfs_server_password', 'fake_password') CONF.set_default('glusterfs_path_to_private_key', '/fakepath/to/privatekey') CONF.set_default('glusterfs_volume_pattern', r'manila-share-\d+-#{size}G$') CONF.set_default('driver_handles_share_servers', False) self.fake_driver = mock.Mock() self.mock_object(self.fake_driver, '_execute', self._execute) self.fake_driver.GLUSTERFS_VERSION_MIN = (3, 6) self.fake_conf = config.Configuration(None) self.mock_object(tempfile, 'mkdtemp', mock.Mock(return_value='/tmp/tmpKGHKJ')) self.mock_object(common.GlusterManager, 'make_gluster_call') self.fake_private_storage = mock.Mock() with mock.patch.object(layout_volume.GlusterfsVolumeMappedLayout, '_glustermanager', side_effect=[self.gmgr1, self.gmgr2]): self._layout = layout_volume.GlusterfsVolumeMappedLayout( self.fake_driver, configuration=self.fake_conf, private_storage=self.fake_private_storage) self._layout.glusterfs_versions = {self.glusterfs_server1: ('3', '6'), self.glusterfs_server2: ('3', '7')} self.addCleanup(fake_utils.fake_execute_set_repliers, []) self.addCleanup(fake_utils.fake_execute_clear_log) @ddt.data({"test_kwargs": {}, "requires": {"volume": True}}, {"test_kwargs": {'req_volume': False}, "requires": {"volume": False}}) @ddt.unpack def test_glustermanager(self, test_kwargs, requires): fake_obj = mock.Mock() self.mock_object(common, 'GlusterManager', mock.Mock(return_value=fake_obj)) ret = self._layout._glustermanager(self.glusterfs_target1, **test_kwargs) common.GlusterManager.assert_called_once_with( self.glusterfs_target1, self._execute, self._layout.configuration.glusterfs_path_to_private_key, self._layout.configuration.glusterfs_server_password, requires=requires) self.assertEqual(fake_obj, ret) def test_compile_volume_pattern(self): volume_pattern = r'manila-share-\d+-(?P\d+)G$' ret = self._layout._compile_volume_pattern() self.assertEqual(re.compile(volume_pattern), ret) @ddt.data({'root@host1:/manila-share-1-1G': 'NONE', 'root@host2:/manila-share-2-2G': None}, {'root@host1:/manila-share-1-1G': FAKE_UUID1, 'root@host2:/manila-share-2-2G': None}, {'root@host1:/manila-share-1-1G': 'foobarbaz', 'root@host2:/manila-share-2-2G': FAKE_UUID2}, {'root@host1:/manila-share-1-1G': FAKE_UUID1, 'root@host2:/manila-share-2-2G': FAKE_UUID2}) def test_fetch_gluster_volumes(self, sharemark): vol1_qualified = 'root@host1:/manila-share-1-1G' gmgr_vol1 = common.GlusterManager(vol1_qualified) gmgr_vol1.get_vol_option = mock.Mock( return_value=sharemark[vol1_qualified]) vol2_qualified = 'root@host2:/manila-share-2-2G' gmgr_vol2 = common.GlusterManager(vol2_qualified) gmgr_vol2.get_vol_option = mock.Mock( return_value=sharemark[vol2_qualified]) self.mock_object( self.gmgr1, 'gluster_call', mock.Mock(return_value=(self.glusterfs_server1_volumes, ''))) self.mock_object( self.gmgr2, 'gluster_call', mock.Mock(return_value=(self.glusterfs_server2_volumes, ''))) _glustermanager_calls = (self.gmgr1, gmgr_vol1, self.gmgr2, gmgr_vol2) self.mock_object(self._layout, '_glustermanager', mock.Mock(side_effect=_glustermanager_calls)) expected_output = {} for q, d in self.glusterfs_volumes_dict.items(): if sharemark[q] not in (FAKE_UUID1, FAKE_UUID2): expected_output[q] = d ret = self._layout._fetch_gluster_volumes() test_args = ('volume', 'list') self.gmgr1.gluster_call.assert_called_once_with(*test_args, log=mock.ANY) self.gmgr2.gluster_call.assert_called_once_with(*test_args, log=mock.ANY) gmgr_vol1.get_vol_option.assert_called_once_with( 'user.manila-share') gmgr_vol2.get_vol_option.assert_called_once_with( 'user.manila-share') self.assertEqual(expected_output, ret) def test_fetch_gluster_volumes_no_filter_used(self): vol1_qualified = 'root@host1:/manila-share-1-1G' gmgr_vol1 = common.GlusterManager(vol1_qualified) gmgr_vol1.get_vol_option = mock.Mock() vol2_qualified = 'root@host2:/manila-share-2-2G' gmgr_vol2 = common.GlusterManager(vol2_qualified) gmgr_vol2.get_vol_option = mock.Mock() self.mock_object( self.gmgr1, 'gluster_call', mock.Mock(return_value=(self.glusterfs_server1_volumes, ''))) self.mock_object( self.gmgr2, 'gluster_call', mock.Mock(return_value=(self.glusterfs_server2_volumes, ''))) _glustermanager_calls = (self.gmgr1, gmgr_vol1, self.gmgr2, gmgr_vol2) self.mock_object(self._layout, '_glustermanager', mock.Mock(side_effect=_glustermanager_calls)) expected_output = self.glusterfs_volumes_dict ret = self._layout._fetch_gluster_volumes(filter_used=False) test_args = ('volume', 'list') self.gmgr1.gluster_call.assert_called_once_with(*test_args, log=mock.ANY) self.gmgr2.gluster_call.assert_called_once_with(*test_args, log=mock.ANY) self.assertFalse(gmgr_vol1.get_vol_option.called) self.assertFalse(gmgr_vol2.get_vol_option.called) self.assertEqual(expected_output, ret) def test_fetch_gluster_volumes_no_keymatch(self): vol1_qualified = 'root@host1:/manila-share-1' gmgr_vol1 = common.GlusterManager(vol1_qualified) gmgr_vol1.get_vol_option = mock.Mock(return_value=None) self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] self.mock_object( self.gmgr1, 'gluster_call', mock.Mock(return_value=('manila-share-1', ''))) _glustermanager_calls = (self.gmgr1, gmgr_vol1) self.mock_object(self._layout, '_glustermanager', mock.Mock(side_effect=_glustermanager_calls)) self.mock_object(self._layout, 'volume_pattern', re.compile(r'manila-share-\d+(-(?P\d+)G)?$')) expected_output = {'root@host1:/manila-share-1': {'size': None}} ret = self._layout._fetch_gluster_volumes() test_args = ('volume', 'list') self.gmgr1.gluster_call.assert_called_once_with(*test_args, log=mock.ANY) self.assertEqual(expected_output, ret) def test_fetch_gluster_volumes_error(self): test_args = ('volume', 'list') def raise_exception(*args, **kwargs): if(args == test_args): raise exception.GlusterfsException() self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] self.mock_object(self.gmgr1, 'gluster_call', mock.Mock(side_effect=raise_exception)) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=self.gmgr1)) self.mock_object(layout_volume.LOG, 'error') self.assertRaises(exception.GlusterfsException, self._layout._fetch_gluster_volumes) self.gmgr1.gluster_call.assert_called_once_with(*test_args, log=mock.ANY) def test_do_setup(self): self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] self.mock_object(self.gmgr1, 'get_gluster_version', mock.Mock(return_value=('3', '6'))) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=self.gmgr1)) self.mock_object(self._layout, '_fetch_gluster_volumes', mock.Mock(return_value=self.glusterfs_volumes_dict)) self.mock_object(self._layout, '_check_mount_glusterfs') self._layout.gluster_used_vols = self.glusterfs_used_vols self.mock_object(layout_volume.LOG, 'warning') self._layout.do_setup(self._context) self._layout._fetch_gluster_volumes.assert_called_once_with( filter_used=False) self._layout._check_mount_glusterfs.assert_called_once_with() self.gmgr1.get_gluster_version.assert_called_once_with() def test_do_setup_unsupported_glusterfs_version(self): self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] self.mock_object(self.gmgr1, 'get_gluster_version', mock.Mock(return_value=('3', '5'))) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=self.gmgr1)) self.assertRaises(exception.GlusterfsException, self._layout.do_setup, self._context) self.gmgr1.get_gluster_version.assert_called_once_with() @ddt.data(exception.GlusterfsException, RuntimeError) def test_do_setup_get_gluster_version_fails(self, exc): def raise_exception(*args, **kwargs): raise exc self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] self.mock_object(self.gmgr1, 'get_gluster_version', mock.Mock(side_effect=raise_exception)) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=self.gmgr1)) self.assertRaises(exc, self._layout.do_setup, self._context) self.gmgr1.get_gluster_version.assert_called_once_with() def test_do_setup_glusterfs_no_volumes_provided_by_backend(self): self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] self.mock_object(self.gmgr1, 'get_gluster_version', mock.Mock(return_value=('3', '6'))) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=self.gmgr1)) self.mock_object(self._layout, '_fetch_gluster_volumes', mock.Mock(return_value={})) self.assertRaises(exception.GlusterfsException, self._layout.do_setup, self._context) self._layout._fetch_gluster_volumes.assert_called_once_with( filter_used=False) def test_share_manager(self): self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=self.gmgr1)) self.mock_object(self._layout.private_storage, 'get', mock.Mock(return_value='host1:/gv1')) ret = self._layout._share_manager(self.share1) self._layout.private_storage.get.assert_called_once_with( self.share1['id'], 'volume') self._layout._glustermanager.assert_called_once_with('host1:/gv1') self.assertEqual(self.gmgr1, ret) def test_share_manager_no_privdata(self): self.mock_object(self._layout.private_storage, 'get', mock.Mock(return_value=None)) ret = self._layout._share_manager(self.share1) self._layout.private_storage.get.assert_called_once_with( self.share1['id'], 'volume') self.assertIsNone(ret) def test_ensure_share(self): share = self.share1 gmgr1 = common.GlusterManager(self.glusterfs_target1, self._execute, None, None) gmgr1.set_vol_option = mock.Mock() self.mock_object(self._layout, '_share_manager', mock.Mock(return_value=gmgr1)) self._layout.ensure_share(self._context, share) self._layout._share_manager.assert_called_once_with(share) self.assertIn(self.glusterfs_target1, self._layout.gluster_used_vols) gmgr1.set_vol_option.assert_called_once_with( 'user.manila-share', share['id']) @ddt.data({"voldict": {"host:/share2G": {"size": 2}}, "used_vols": set(), "size": 1, "expected": "host:/share2G"}, {"voldict": {"host:/share2G": {"size": 2}}, "used_vols": set(), "size": 2, "expected": "host:/share2G"}, {"voldict": {"host:/share2G": {"size": 2}}, "used_vols": set(), "size": None, "expected": "host:/share2G"}, {"voldict": {"host:/share2G": {"size": 2}, "host:/share": {"size": None}}, "used_vols": set(["host:/share2G"]), "size": 1, "expected": "host:/share"}, {"voldict": {"host:/share2G": {"size": 2}, "host:/share": {"size": None}}, "used_vols": set(["host:/share2G"]), "size": 2, "expected": "host:/share"}, {"voldict": {"host:/share2G": {"size": 2}, "host:/share": {"size": None}}, "used_vols": set(["host:/share2G"]), "size": 3, "expected": "host:/share"}, {"voldict": {"host:/share2G": {"size": 2}, "host:/share": {"size": None}}, "used_vols": set(["host:/share2G"]), "size": None, "expected": "host:/share"}, {"voldict": {"host:/share": {}}, "used_vols": set(), "size": 1, "expected": "host:/share"}, {"voldict": {"host:/share": {}}, "used_vols": set(), "size": None, "expected": "host:/share"}) @ddt.unpack def test_pop_gluster_vol(self, voldict, used_vols, size, expected): gmgr = common.GlusterManager gmgr1 = gmgr(expected, self._execute, None, None) self._layout._fetch_gluster_volumes = mock.Mock(return_value=voldict) self._layout.gluster_used_vols = used_vols self._layout._glustermanager = mock.Mock(return_value=gmgr1) self._layout.volume_pattern_keys = list(voldict.values())[0].keys() result = self._layout._pop_gluster_vol(size=size) self.assertEqual(expected, result) self.assertIn(result, used_vols) self._layout._fetch_gluster_volumes.assert_called_once_with() self._layout._glustermanager.assert_called_once_with(result) @ddt.data({"voldict": {"share2G": {"size": 2}}, "used_vols": set(), "size": 3}, {"voldict": {"share2G": {"size": 2}}, "used_vols": set(["share2G"]), "size": None}) @ddt.unpack def test_pop_gluster_vol_excp(self, voldict, used_vols, size): self._layout._fetch_gluster_volumes = mock.Mock(return_value=voldict) self._layout.gluster_used_vols = used_vols self._layout.volume_pattern_keys = list(voldict.values())[0].keys() self.assertRaises(exception.GlusterfsException, self._layout._pop_gluster_vol, size=size) self._layout._fetch_gluster_volumes.assert_called_once_with() self.assertFalse( self.fake_driver._setup_via_manager.called) def test_push_gluster_vol(self): self._layout.gluster_used_vols = set([ self.glusterfs_target1, self.glusterfs_target2]) self._layout._push_gluster_vol(self.glusterfs_target2) self.assertEqual(1, len(self._layout.gluster_used_vols)) self.assertFalse( self.glusterfs_target2 in self._layout.gluster_used_vols) def test_push_gluster_vol_excp(self): self._layout.gluster_used_vols = set([self.glusterfs_target1]) self._layout.gluster_unused_vols_dict = {} self.assertRaises(exception.GlusterfsException, self._layout._push_gluster_vol, self.glusterfs_target2) @ddt.data({'vers_minor': '6', 'cmd': ['find', '/tmp/tmpKGHKJ', '-mindepth', '1', '-delete']}, {'vers_minor': '7', 'cmd': ['find', '/tmp/tmpKGHKJ', '-mindepth', '1', '!', '-path', '/tmp/tmpKGHKJ/.trashcan', '!', '-path', '/tmp/tmpKGHKJ/.trashcan/internal_op', '-delete']}) @ddt.unpack def test_wipe_gluster_vol(self, vers_minor, cmd): tmpdir = '/tmp/tmpKGHKJ' gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) self._layout.glusterfs_versions = { self.glusterfs_server1: ('3', vers_minor)} self.mock_object(tempfile, 'mkdtemp', mock.Mock(return_value=tmpdir)) self.mock_object(self.fake_driver, '_execute', mock.Mock()) self.mock_object(common, '_mount_gluster_vol', mock.Mock()) self.mock_object(common, '_umount_gluster_vol', mock.Mock()) self.mock_object(shutil, 'rmtree', mock.Mock()) self._layout._wipe_gluster_vol(gmgr1) tempfile.mkdtemp.assert_called_once_with() common._mount_gluster_vol.assert_called_once_with( self.fake_driver._execute, gmgr1.export, tmpdir) kwargs = {'run_as_root': True} self.fake_driver._execute.assert_called_once_with( *cmd, **kwargs) common._umount_gluster_vol.assert_called_once_with( self.fake_driver._execute, tmpdir) kwargs = {'ignore_errors': True} shutil.rmtree.assert_called_once_with(tmpdir, **kwargs) def test_wipe_gluster_vol_mount_fail(self): tmpdir = '/tmp/tmpKGHKJ' gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) self._layout.glusterfs_versions = { self.glusterfs_server1: ('3', '6')} self.mock_object(tempfile, 'mkdtemp', mock.Mock(return_value=tmpdir)) self.mock_object(self.fake_driver, '_execute', mock.Mock()) self.mock_object(common, '_mount_gluster_vol', mock.Mock(side_effect=exception.GlusterfsException)) self.mock_object(common, '_umount_gluster_vol', mock.Mock()) self.mock_object(shutil, 'rmtree', mock.Mock()) self.assertRaises(exception.GlusterfsException, self._layout._wipe_gluster_vol, gmgr1) tempfile.mkdtemp.assert_called_once_with() common._mount_gluster_vol.assert_called_once_with( self.fake_driver._execute, gmgr1.export, tmpdir) self.assertFalse(self.fake_driver._execute.called) self.assertFalse(common._umount_gluster_vol.called) kwargs = {'ignore_errors': True} shutil.rmtree.assert_called_once_with(tmpdir, **kwargs) def test_wipe_gluster_vol_error_wiping_gluster_vol(self): tmpdir = '/tmp/tmpKGHKJ' gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) self._layout.glusterfs_versions = { self.glusterfs_server1: ('3', '6')} cmd = ['find', '/tmp/tmpKGHKJ', '-mindepth', '1', '-delete'] self.mock_object(tempfile, 'mkdtemp', mock.Mock(return_value=tmpdir)) self.mock_object( self.fake_driver, '_execute', mock.Mock(side_effect=exception.ProcessExecutionError)) self.mock_object(common, '_mount_gluster_vol', mock.Mock()) self.mock_object(common, '_umount_gluster_vol', mock.Mock()) self.mock_object(shutil, 'rmtree', mock.Mock()) self.assertRaises(exception.GlusterfsException, self._layout._wipe_gluster_vol, gmgr1) tempfile.mkdtemp.assert_called_once_with() common._mount_gluster_vol.assert_called_once_with( self.fake_driver._execute, gmgr1.export, tmpdir) kwargs = {'run_as_root': True} self.fake_driver._execute.assert_called_once_with( *cmd, **kwargs) common._umount_gluster_vol.assert_called_once_with( self.fake_driver._execute, tmpdir) kwargs = {'ignore_errors': True} shutil.rmtree.assert_called_once_with(tmpdir, **kwargs) def test_create_share(self): self._layout._pop_gluster_vol = mock.Mock( return_value=self.glusterfs_target1) gmgr1 = common.GlusterManager(self.glusterfs_target1) gmgr1.set_vol_option = mock.Mock() self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) self.mock_object(self.fake_driver, '_setup_via_manager', mock.Mock(return_value='host1:/gv1')) share = new_share() exp_locn = self._layout.create_share(self._context, share) self._layout._pop_gluster_vol.assert_called_once_with(share['size']) self.fake_driver._setup_via_manager.assert_called_once_with( {'manager': gmgr1, 'share': share}) self._layout.private_storage.update.assert_called_once_with( share['id'], {'volume': self.glusterfs_target1}) gmgr1.set_vol_option.assert_called_once_with( 'user.manila-share', share['id']) self.assertEqual('host1:/gv1', exp_locn) def test_create_share_error(self): self._layout._pop_gluster_vol = mock.Mock( side_effect=exception.GlusterfsException) share = new_share() self.assertRaises(exception.GlusterfsException, self._layout.create_share, self._context, share) self._layout._pop_gluster_vol.assert_called_once_with( share['size']) @ddt.data(None, '', 'Eeyore') def test_delete_share(self, clone_of): self._layout._push_gluster_vol = mock.Mock() self._layout._wipe_gluster_vol = mock.Mock() gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) gmgr1.set_vol_option = mock.Mock() gmgr1.get_vol_option = mock.Mock(return_value=clone_of) new_vol_addr = self.glusterfs_target1 self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self._layout.delete_share(self._context, self.share1) gmgr1.get_vol_option.assert_called_once_with( 'user.manila-cloned-from') self._layout._wipe_gluster_vol.assert_called_once_with(gmgr1) self.assertIn(new_vol_addr, self._layout.gluster_used_vols) self._layout._push_gluster_vol.assert_called_once_with( self.glusterfs_target1) self._layout.private_storage.delete.assert_called_once_with( self.share1['id']) gmgr1.set_vol_option.assert_has_calls([ mock.call('user.manila-share', 'NONE'), mock.call('nfs.disable', 'on') ]) def test_delete_share_clone(self): self._layout._push_gluster_vol = mock.Mock() self._layout._wipe_gluster_vol = mock.Mock() gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) gmgr1.gluster_call = mock.Mock() gmgr1.get_vol_option = mock.Mock(return_value=FAKE_UUID1) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self._layout.delete_share(self._context, self.share1) gmgr1.get_vol_option.assert_called_once_with( 'user.manila-cloned-from') self.assertFalse(self._layout._wipe_gluster_vol.called) self._layout._push_gluster_vol.assert_called_once_with( self.glusterfs_target1) self._layout.private_storage.delete.assert_called_once_with( self.share1['id']) gmgr1.gluster_call.assert_called_once_with( 'volume', 'delete', 'gv1') def test_delete_share_error(self): self._layout._wipe_gluster_vol = mock.Mock() self._layout._wipe_gluster_vol.side_effect = ( exception.GlusterfsException) self._layout._push_gluster_vol = mock.Mock() gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) gmgr1.get_vol_option = mock.Mock(return_value=None) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self.assertRaises(exception.GlusterfsException, self._layout.delete_share, self._context, self.share1) self._layout._wipe_gluster_vol.assert_called_once_with(gmgr1) self.assertFalse(self._layout._push_gluster_vol.called) def test_delete_share_missing_record(self): self.mock_object(self._layout, '_share_manager', mock.Mock(return_value=None)) self._layout.delete_share(self._context, self.share1) self._layout._share_manager.assert_called_once_with(self.share1) def test_create_snapshot(self): self._layout.gluster_nosnap_vols_dict = {} self._layout.glusterfs_versions = {self.glusterfs_server1: ('3', '6')} gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self.mock_object(gmgr1, 'gluster_call', mock.Mock( side_effect=(glusterXMLOut(ret=0, errno=0),))) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } ret = self._layout.create_snapshot(self._context, snapshot) self.assertIsNone(ret) args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id', gmgr1.volume) gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) @ddt.data({'side_effect': (glusterXMLOut(ret=-1, errno=2),), '_exception': exception.GlusterfsException}, {'side_effect': (('', ''),), '_exception': exception.GlusterfsException}) @ddt.unpack def test_create_snapshot_error(self, side_effect, _exception): self._layout.gluster_nosnap_vols_dict = {} self._layout.glusterfs_versions = {self.glusterfs_server1: ('3', '6')} gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self.mock_object(gmgr1, 'gluster_call', mock.Mock(side_effect=side_effect)) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } self.assertRaises(_exception, self._layout.create_snapshot, self._context, snapshot) args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id', gmgr1.volume) gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) @ddt.data({"vers_minor": '6', "exctype": exception.GlusterfsException}, {"vers_minor": '7', "exctype": exception.ShareSnapshotNotSupported}) @ddt.unpack def test_create_snapshot_no_snap(self, vers_minor, exctype): self._layout.gluster_nosnap_vols_dict = {} self._layout.glusterfs_versions = { self.glusterfs_server1: ('3', vers_minor)} gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self.mock_object(gmgr1, 'gluster_call', mock.Mock( side_effect=(glusterXMLOut(ret=-1, errno=0),))) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } self.assertRaises(exctype, self._layout.create_snapshot, self._context, snapshot) args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id', gmgr1.volume) gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) @ddt.data({"vers_minor": '6', "exctype": exception.GlusterfsException}, {"vers_minor": '7', "exctype": exception.ShareSnapshotNotSupported}) @ddt.unpack def test_create_snapshot_no_snap_cached(self, vers_minor, exctype): self._layout.gluster_nosnap_vols_dict = { self.glusterfs_target1: 'fake error'} self._layout.glusterfs_versions = { self.glusterfs_server1: ('3', vers_minor)} self._layout.gluster_used_vols = set([self.glusterfs_target1]) gmgr = common.GlusterManager gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) self.mock_object(self._layout, '_share_manager', mock.Mock(return_value=gmgr1)) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } self.assertRaises(exctype, self._layout.create_snapshot, self._context, snapshot) def test_find_actual_backend_snapshot_name(self): gmgr = common.GlusterManager gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) self.mock_object(gmgr1, 'gluster_call', mock.Mock(return_value=('fake_snap_id_xyz', ''))) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } ret = self._layout._find_actual_backend_snapshot_name(gmgr1, snapshot) args = ('snapshot', 'list', gmgr1.volume, '--mode=script') gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) self.assertEqual('fake_snap_id_xyz', ret) @ddt.data('this is too bad', 'fake_snap_id_xyx\nfake_snap_id_pqr') def test_find_actual_backend_snapshot_name_bad_snap_list(self, snaplist): gmgr = common.GlusterManager gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) self.mock_object(gmgr1, 'gluster_call', mock.Mock(return_value=(snaplist, ''))) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } self.assertRaises(exception.GlusterfsException, self._layout._find_actual_backend_snapshot_name, gmgr1, snapshot) args = ('snapshot', 'list', gmgr1.volume, '--mode=script') gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) @ddt.data({'glusterfs_target': 'root@host1:/gv1', 'glusterfs_server': 'root@host1'}, {'glusterfs_target': 'host1:/gv1', 'glusterfs_server': 'host1'}) @ddt.unpack def test_create_share_from_snapshot(self, glusterfs_target, glusterfs_server): share = new_share() snapshot = { 'id': 'fake_snap_id', 'share_instance': new_share(export_location=glusterfs_target), 'share_id': 'fake_share_id', } volume = ''.join(['manila-', share['id']]) new_vol_addr = ':/'.join([glusterfs_server, volume]) gmgr = common.GlusterManager old_gmgr = gmgr(glusterfs_target, self._execute, None, None) new_gmgr = gmgr(new_vol_addr, self._execute, None, None) self._layout.gluster_used_vols = set([glusterfs_target]) self._layout.glusterfs_versions = {glusterfs_server: ('3', '7')} self.mock_object(old_gmgr, 'gluster_call', mock.Mock(side_effect=[ ('', ''), ('', ''), ('', ''), ('', '')])) self.mock_object(new_gmgr, 'gluster_call', mock.Mock(side_effect=[('', ''), ('', '')])) self.mock_object(new_gmgr, 'get_vol_option', mock.Mock()) new_gmgr.get_vol_option.return_value = ( 'glusterfs-server-1,client') self.mock_object(self._layout, '_find_actual_backend_snapshot_name', mock.Mock(return_value='fake_snap_id_xyz')) self.mock_object(self._layout, '_share_manager', mock.Mock(return_value=old_gmgr)) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=new_gmgr)) self.mock_object(self.fake_driver, '_setup_via_manager', mock.Mock(return_value='host1:/gv1')) ret = self._layout.create_share_from_snapshot( self._context, share, snapshot, None) (self._layout._find_actual_backend_snapshot_name. assert_called_once_with(old_gmgr, snapshot)) args = (('snapshot', 'activate', 'fake_snap_id_xyz', 'force', '--mode=script'), ('snapshot', 'clone', volume, 'fake_snap_id_xyz'), ('volume', 'start', volume)) old_gmgr.gluster_call.assert_has_calls( [mock.call(*a, log=mock.ANY) for a in args]) args = (('volume', 'set', volume, 'user.manila-share', share['id']), ('volume', 'set', volume, 'user.manila-cloned-from', snapshot['share_id'])) new_gmgr.gluster_call.assert_has_calls( [mock.call(*a, log=mock.ANY) for a in args], any_order=True) self._layout._share_manager.assert_called_once_with( snapshot['share_instance']) self._layout._glustermanager.assert_called_once_with( gmgr.parse(new_vol_addr)) self._layout.driver._setup_via_manager.assert_called_once_with( {'manager': new_gmgr, 'share': share}, {'manager': old_gmgr, 'share': snapshot['share_instance']}) self._layout.private_storage.update.assert_called_once_with( share['id'], {'volume': new_vol_addr}) self.assertIn( new_vol_addr, self._layout.gluster_used_vols) self.assertEqual(['host1:/gv1'], ret) def test_create_share_from_snapshot_error_unsupported_gluster_version( self): glusterfs_target = 'root@host1:/gv1' glusterfs_server = 'root@host1' share = new_share() volume = ''.join(['manila-', share['id']]) new_vol_addr = ':/'.join([glusterfs_server, volume]) gmgr = common.GlusterManager old_gmgr = gmgr(glusterfs_target, self._execute, None, None) new_gmgr = gmgr(new_vol_addr, self._execute, None, None) self._layout.gluster_used_vols_dict = {glusterfs_target: old_gmgr} self._layout.glusterfs_versions = {glusterfs_server: ('3', '6')} self.mock_object( old_gmgr, 'gluster_call', mock.Mock(side_effect=[('', ''), ('', '')])) self.mock_object(new_gmgr, 'get_vol_option', mock.Mock()) new_gmgr.get_vol_option.return_value = ( 'glusterfs-server-1,client') self.mock_object(self._layout, '_find_actual_backend_snapshot_name', mock.Mock(return_value='fake_snap_id_xyz')) self.mock_object(self._layout, '_share_manager', mock.Mock(return_value=old_gmgr)) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=new_gmgr)) snapshot = { 'id': 'fake_snap_id', 'share_instance': new_share(export_location=glusterfs_target) } self.assertRaises(exception.GlusterfsException, self._layout.create_share_from_snapshot, self._context, share, snapshot) self.assertFalse( self._layout._find_actual_backend_snapshot_name.called) self.assertFalse(old_gmgr.gluster_call.called) self._layout._share_manager.assert_called_once_with( snapshot['share_instance']) self.assertFalse(self._layout._glustermanager.called) self.assertFalse(new_gmgr.get_vol_option.called) self.assertFalse(new_gmgr.gluster_call.called) self.assertNotIn(new_vol_addr, self._layout.glusterfs_versions.keys()) def test_delete_snapshot(self): self._layout.gluster_nosnap_vols_dict = {} gmgr = common.GlusterManager gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self.mock_object(self._layout, '_find_actual_backend_snapshot_name', mock.Mock(return_value='fake_snap_id_xyz')) self.mock_object( gmgr1, 'gluster_call', mock.Mock(return_value=glusterXMLOut(ret=0, errno=0))) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } ret = self._layout.delete_snapshot(self._context, snapshot) self.assertIsNone(ret) args = ('--xml', 'snapshot', 'delete', 'fake_snap_id_xyz', '--mode=script') gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) (self._layout._find_actual_backend_snapshot_name. assert_called_once_with(gmgr1, snapshot)) @ddt.data({'side_effect': (glusterXMLOut(ret=-1, errno=0),), '_exception': exception.GlusterfsException}, {'side_effect': (('', ''),), '_exception': exception.GlusterfsException}) @ddt.unpack def test_delete_snapshot_error(self, side_effect, _exception): self._layout.gluster_nosnap_vols_dict = {} gmgr = common.GlusterManager gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) self._layout.gluster_used_vols = set([self.glusterfs_target1]) self.mock_object(self._layout, '_find_actual_backend_snapshot_name', mock.Mock(return_value='fake_snap_id_xyz')) args = ('--xml', 'snapshot', 'delete', 'fake_snap_id_xyz', '--mode=script') self.mock_object( gmgr1, 'gluster_call', mock.Mock(side_effect=side_effect)) self.mock_object(self._layout, '_glustermanager', mock.Mock(return_value=gmgr1)) snapshot = { 'id': 'fake_snap_id', 'share_id': self.share1['id'], 'share': self.share1 } self.assertRaises(_exception, self._layout.delete_snapshot, self._context, snapshot) gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) (self._layout._find_actual_backend_snapshot_name. assert_called_once_with(gmgr1, snapshot)) @ddt.data( ('manage_existing', ('share', 'driver_options'), {}), ('unmanage', ('share',), {}), ('extend_share', ('share', 'new_size'), {'share_server': None}), ('shrink_share', ('share', 'new_size'), {'share_server': None})) def test_nonimplemented_methods(self, method_invocation): method, args, kwargs = method_invocation self.assertRaises(NotImplementedError, getattr(self._layout, method), *args, **kwargs)