diff --git a/manila/share/drivers/emc/driver.py b/manila/share/drivers/emc/driver.py index a6579d8dd3..0ea4d99343 100644 --- a/manila/share/drivers/emc/driver.py +++ b/manila/share/drivers/emc/driver.py @@ -29,6 +29,7 @@ from manila.share.drivers.emc import plugin_manager as manager LOG = log.getLogger(__name__) + EMC_NAS_OPTS = [ cfg.StrOpt('emc_nas_login', help='User name for the EMC server.'), @@ -47,8 +48,10 @@ EMC_NAS_OPTS = [ cfg.StrOpt('emc_nas_server_container', default='server_2', help='Container of share servers.'), - cfg.StrOpt('emc_nas_pool_name', - help='EMC pool name.'), + cfg.StrOpt('emc_nas_pool_names', + deprecated_name='emc_nas_pool_name', + default=None, + help='EMC pool names.'), cfg.StrOpt('emc_nas_root_dir', help='The root directory where shares will be located.'), ] @@ -118,7 +121,7 @@ class EMCShareDriver(driver.ShareDriver): def check_for_setup_error(self): """Check for setup error.""" - pass + self.plugin.check_for_setup_error() def do_setup(self, context): """Any initialization the share driver does while starting.""" diff --git a/manila/share/drivers/emc/plugins/isilon/isilon.py b/manila/share/drivers/emc/plugins/isilon/isilon.py index 58dbeaaf0d..95ac80efd8 100644 --- a/manila/share/drivers/emc/plugins/isilon/isilon.py +++ b/manila/share/drivers/emc/plugins/isilon/isilon.py @@ -283,6 +283,9 @@ class IsilonStorageConnection(base.StorageConnection): resp = self._isilon_api.request('PUT', url, data=share_params) resp.raise_for_status() + def check_for_setup_error(self): + """Check for setup error.""" + def connect(self, emc_share_driver, context): """Connect to an Isilon cluster.""" self._server = emc_share_driver.configuration.safe_get( diff --git a/manila/share/drivers/emc/plugins/vnx/connection.py b/manila/share/drivers/emc/plugins/vnx/connection.py index 7cf2c25940..fdc0d7eb81 100644 --- a/manila/share/drivers/emc/plugins/vnx/connection.py +++ b/manila/share/drivers/emc/plugins/vnx/connection.py @@ -15,6 +15,7 @@ """VNX backend for the EMC Manila driver.""" import copy +import fnmatch import random from oslo_log import log @@ -30,6 +31,7 @@ from manila.share.drivers.emc.plugins import base as driver from manila.share.drivers.emc.plugins.vnx import constants from manila.share.drivers.emc.plugins.vnx import object_manager as manager from manila.share.drivers.emc.plugins.vnx import utils as vnx_utils +from manila.share import utils as share_utils from manila import utils VERSION = "2.0.0" @@ -46,8 +48,10 @@ class VNXStorageConnection(driver.StorageConnection): def __init__(self, *args, **kwargs): super(VNXStorageConnection, self).__init__(*args, **kwargs) self.mover_name = None - self.pool_name = None + self.pools = None self.manager = None + self.pool_conf = None + self.reserved_percentage = None self.driver_handles_share_servers = True def create_share(self, context, share, share_server=None): @@ -57,11 +61,20 @@ class VNXStorageConnection(driver.StorageConnection): share_proto = share['share_proto'] + # Validate the share protocol if share_proto.upper() not in ('NFS', 'CIFS'): raise exception.InvalidShare( reason=(_('Invalid NAS protocol supplied: %s.') % share_proto)) + # Get the pool name from share host field + pool_name = share_utils.extract_host(share['host'], level='pool') + if not pool_name: + message = (_("Pool is not available in the share host %s.") % + share['host']) + raise exception.InvalidHost(reason=message) + + # Validate share server self._share_server_validation(share_server) if share_proto == 'CIFS': @@ -76,7 +89,7 @@ class VNXStorageConnection(driver.StorageConnection): LOG.error(message) raise exception.EMCVnxXMLAPIError(err=message) - self._allocate_container(share_name, size, share_server) + self._allocate_container(share_name, size, share_server, pool_name) if share_proto == 'NFS': location = self._create_nfs_share(share_name, share_server) @@ -100,14 +113,15 @@ class VNXStorageConnection(driver.StorageConnection): LOG.error(message) raise exception.EMCVnxXMLAPIError(err=message) - def _allocate_container(self, share_name, size, share_server): + def _allocate_container(self, share_name, size, share_server, pool_name): """Allocate file system for share.""" vdm_name = self._get_share_server_name(share_server) self._get_context('FileSystem').create( - share_name, size, self.pool_name, vdm_name) + share_name, size, pool_name, vdm_name) - def _allocate_container_from_snapshot(self, share, snapshot, share_server): + def _allocate_container_from_snapshot(self, share, snapshot, share_server, + pool_name): """Allocate file system from snapshot.""" vdm_name = self._get_share_server_name(share_server) @@ -116,10 +130,10 @@ class VNXStorageConnection(driver.StorageConnection): self._get_context('FileSystem').create_from_snapshot( share['id'], snapshot['id'], snapshot['share_id'], - self.pool_name, vdm_name, interconn_id) + pool_name, vdm_name, interconn_id) nwe_size = share['size'] * units.Ki - self._get_context('FileSystem').extend(share['id'], self.pool_name, + self._get_context('FileSystem').extend(share['id'], pool_name, nwe_size) @vnx_utils.log_enter_exit @@ -170,14 +184,23 @@ class VNXStorageConnection(driver.StorageConnection): share_proto = share['share_proto'] + # Validate the share protocol if share_proto.upper() not in ('NFS', 'CIFS'): raise exception.InvalidShare( reason=(_('Invalid NAS protocol supplied: %s.') % share_proto)) + # Get the pool name from share host field + pool_name = share_utils.extract_host(share['host'], level='pool') + if not pool_name: + message = (_("Pool is not available in the share host %s.") % + share['host']) + raise exception.InvalidHost(reason=message) + self._share_server_validation(share_server) - self._allocate_container_from_snapshot(share, snapshot, share_server) + self._allocate_container_from_snapshot( + share, snapshot, share_server, pool_name) if share_proto == 'NFS': self._create_nfs_share(share_name, share_server) @@ -191,9 +214,18 @@ class VNXStorageConnection(driver.StorageConnection): def create_snapshot(self, context, snapshot, share_server=None): """Create snapshot from share.""" + share_name = snapshot['share_id'] + status, filesystem = self._get_context('FileSystem').get(share_name) + if status != constants.STATUS_OK: + message = (_("File System %s not found.") % share_name) + LOG.error(message) + raise exception.EMCVnxXMLAPIError(err=message) + + pool_id = filesystem['pools_id'][0] + self._get_context('Snapshot').create(snapshot['id'], snapshot['share_id'], - self.pool_name) + pool_id) def delete_share(self, context, share, share_server=None): """Delete a share.""" @@ -261,9 +293,17 @@ class VNXStorageConnection(driver.StorageConnection): """Ensure that the share is exported.""" def extend_share(self, share, new_size, share_server=None): + # Get the pool name from share host field + pool_name = share_utils.extract_host(share['host'], level='pool') + if not pool_name: + message = (_("Pool is not available in the share host %s.") % + share['host']) + raise exception.InvalidHost(reason=message) + share_name = share['id'] + self._get_context('FileSystem').extend( - share_name, self.pool_name, new_size * units.Ki) + share_name, pool_name, new_size * units.Ki) def allow_access(self, context, share, access, share_server=None): """Allow access to a share.""" @@ -395,20 +435,7 @@ class VNXStorageConnection(driver.StorageConnection): def check_for_setup_error(self): """Check for setup error.""" - pass - - def connect(self, emc_share_driver, context): - """Connect to VNX NAS server.""" - self.mover_name = ( - emc_share_driver.configuration.emc_nas_server_container) - - self.pool_name = emc_share_driver.configuration.emc_nas_pool_name - - configuration = emc_share_driver.configuration - - self.manager = manager.StorageObjectManager(configuration) - - # To verify the input from manila configuration + # To verify the input from Manila configuration status, out = self._get_context('Mover').get_ref(self.mover_name, True) if constants.STATUS_ERROR == status: @@ -417,13 +444,68 @@ class VNXStorageConnection(driver.StorageConnection): LOG.error(message) raise exception.InvalidParameterValue(err=message) - status, out = self._get_context('StoragePool').get(self.pool_name, - True) - if constants.STATUS_ERROR == status: - message = (_("Could not find storage pool by name: %s.") % - self.pool_name) - LOG.error(message) - raise exception.InvalidParameterValue(err=message) + self.pools = self._get_managed_storage_pools(self.pool_conf) + + def _get_managed_storage_pools(self, pools): + matched_pools = set() + if pools: + # Get the real pools from the backend storage + status, backend_pools = self._get_context('StoragePool').get_all() + if status != constants.STATUS_OK: + message = (_("Failed to get storage pool information. " + "Reason: %s") % backend_pools) + LOG.error(message) + raise exception.EMCVnxXMLAPIError(err=message) + + real_pools = set([item for item in backend_pools]) + + conf_pools = set([item.strip() for item in pools.split(",")]) + + for pool in real_pools: + for matcher in conf_pools: + if fnmatch.fnmatchcase(pool, matcher): + matched_pools.add(pool) + + nonexistent_pools = real_pools.difference(matched_pools) + + if not matched_pools: + msg = (_("All the specified storage pools to be managed " + "do not exist. Please check your configuration " + "emc_nas_pool_names in manila.conf. " + "The available pools in the backend are %s") % + ",".join(real_pools)) + raise exception.InvalidParameterValue(err=msg) + if nonexistent_pools: + LOG.warning(_LW("The following specified storage pools " + "do not exist: %(unexist)s. " + "This host will only manage the storage " + "pools: %(exist)s"), + {'unexist': ",".join(nonexistent_pools), + 'exist': ",".join(matched_pools)}) + else: + LOG.debug("Storage pools: %s will be managed.", + ",".join(matched_pools)) + else: + LOG.debug("No storage pool is specified, so all pools " + "in storage system will be managed.") + return matched_pools + + def connect(self, emc_share_driver, context): + """Connect to VNX NAS server.""" + self.mover_name = ( + emc_share_driver.configuration.emc_nas_server_container) + + self.pool_conf = emc_share_driver.configuration.safe_get( + 'emc_nas_pool_names') + + self.reserved_percentage = emc_share_driver.configuration.safe_get( + 'reserved_share_percentage') + if self.reserved_percentage is None: + self.reserved_percentage = 0 + + configuration = emc_share_driver.configuration + + self.manager = manager.StorageObjectManager(configuration) def update_share_stats(self, stats_dict): """Communicate with EMCNASClient to get the stats.""" @@ -431,12 +513,58 @@ class VNXStorageConnection(driver.StorageConnection): self._get_context('Mover').get_ref(self.mover_name, True) - status, pool = self._get_context('StoragePool').get(self.pool_name, - True) + stats_dict['pools'] = [] - stats_dict['total_capacity_gb'] = int(pool['total_size']) - stats_dict['free_capacity_gb'] = ( - int(pool['total_size']) - int(pool['used_size'])) + status, pools = self._get_context('StoragePool').get_all() + for name, pool in pools.items(): + if not self.pools or pool['name'] in self.pools: + total_size = float(pool['total_size']) + used_size = float(pool['used_size']) + + pool_stat = dict( + pool_name=pool['name'], + total_capacity_gb=total_size, + free_capacity_gb=total_size - used_size, + QoS_support=False, + reserved_percentage=self.reserved_percentage, + ) + stats_dict['pools'].append(pool_stat) + + if not stats_dict['pools']: + message = _("Failed to update storage pool.") + LOG.error(message) + raise exception.EMCVnxXMLAPIError(err=message) + + def get_pool(self, share): + """Get the pool name of the share.""" + share_name = share['id'] + status, filesystem = self._get_context('FileSystem').get(share_name) + if status != constants.STATUS_OK: + message = (_("File System %(name)s not found. " + "Reason: %(err)s") % + {'name': share_name, 'err': filesystem}) + LOG.error(message) + raise exception.EMCVnxXMLAPIError(err=message) + + pool_id = filesystem['pools_id'][0] + + # Get the real pools from the backend storage + status, backend_pools = self._get_context('StoragePool').get_all() + if status != constants.STATUS_OK: + message = (_("Failed to get storage pool information. " + "Reason: %s") % backend_pools) + LOG.error(message) + raise exception.EMCVnxXMLAPIError(err=message) + + for name, pool_info in backend_pools.items(): + if pool_info['id'] == pool_id: + return name + + available_pools = [item for item in backend_pools] + message = (_("No matched pool name for share: %(share)s. " + "Available pools: %(pools)s") % + {'share': share_name, 'pools': available_pools}) + raise exception.EMCVnxXMLAPIError(err=message) def get_network_allocations_number(self): """Returns number of network allocations for creating VIFs.""" diff --git a/manila/share/drivers/emc/plugins/vnx/object_manager.py b/manila/share/drivers/emc/plugins/vnx/object_manager.py index b07795ecfd..aed2fb26c0 100644 --- a/manila/share/drivers/emc/plugins/vnx/object_manager.py +++ b/manila/share/drivers/emc/plugins/vnx/object_manager.py @@ -1026,11 +1026,9 @@ class Snapshot(StorageObject): super(Snapshot, self).__init__(conn, elt_maker, xml_parser, manager) self.snap_map = dict() - def create(self, name, fs_name, pool_name, ckpt_size=None): + def create(self, name, fs_name, pool_id, ckpt_size=None): fs_id = self.get_context('FileSystem').get_id(fs_name) - pool_id = self.get_context('StoragePool').get_id(pool_name) - if ckpt_size: elt_pool = self.elt_maker.StoragePool( pool=pool_id, diff --git a/manila/tests/share/drivers/emc/plugins/vnx/fakes.py b/manila/tests/share/drivers/emc/plugins/vnx/fakes.py index 34632edb6f..d4272ea70e 100644 --- a/manila/tests/share/drivers/emc/plugins/vnx/fakes.py +++ b/manila/tests/share/drivers/emc/plugins/vnx/fakes.py @@ -62,6 +62,7 @@ def response(func): class FakeData(object): # Share informaiton share_id = '7cf7c200_d3af_4e05_b87e_9167c95df4f9' + host = 'HostA@BackendB#fake_pool_name' share_name = share_id share_size = 10 new_size = 20 @@ -769,9 +770,11 @@ class PoolTestData(StorageObjectTestData): ) @response - def resp_get_succeed(self, name=None): + def resp_get_succeed(self, name=None, id=None): if not name: name = self.pool_name + if not id: + id = self.pool_id return ( '' '' '' % {'name': name, - 'id': self.pool_id, + 'id': id, 'pool_used_size': self.pool_used_size, 'pool_total_size': self.pool_total_size} ) @@ -1404,7 +1407,6 @@ class FakeEMCShareDriver(object): self.configuration.append_config_values = mock.Mock(return_value=0) self.configuration.emc_share_backend = FakeData.emc_share_backend self.configuration.emc_nas_server_container = FakeData.mover_name - self.configuration.emc_nas_pool_name = FakeData.pool_name self.configuration.emc_nas_server = FakeData.emc_nas_server self.configuration.emc_nas_login = FakeData.emc_nas_login self.configuration.emc_nas_password = FakeData.emc_nas_password @@ -1416,6 +1418,7 @@ CIFS_SHARE = fake_share.fake_share( size=FakeData.share_size, share_network_id=FakeData.share_network_id, share_server_id=FakeData.share_server_id, + host=FakeData.host, share_proto='CIFS') NFS_SHARE = fake_share.fake_share( @@ -1424,6 +1427,7 @@ NFS_SHARE = fake_share.fake_share( size=FakeData.share_size, share_network_id=FakeData.share_network_id, share_server_id=FakeData.share_server_id, + host=FakeData.host, share_proto='NFS') CIFS_RW_ACCESS = fake_share.fake_access( @@ -1500,6 +1504,4 @@ STATS = dict( share_backend_name='VNX', vendor_name='EMC', storage_protocol='NFS_CIFS', - driver_version='2.0.0,', - total_capacity_gb='unknown', - free_capacity_gb='unknow') + driver_version='2.0.0,') diff --git a/manila/tests/share/drivers/emc/plugins/vnx/test_connection.py b/manila/tests/share/drivers/emc/plugins/vnx/test_connection.py index b834fd5f9d..72c2ee148a 100644 --- a/manila/tests/share/drivers/emc/plugins/vnx/test_connection.py +++ b/manila/tests/share/drivers/emc/plugins/vnx/test_connection.py @@ -15,12 +15,14 @@ import copy +import ddt import mock from oslo_log import log from manila import exception from manila.share.drivers.emc.plugins.vnx import connection from manila.share.drivers.emc.plugins.vnx import connector +from manila.share.drivers.emc.plugins.vnx import object_manager from manila import test from manila.tests import fake_share from manila.tests.share.drivers.emc.plugins.vnx import fakes @@ -29,17 +31,13 @@ from manila.tests.share.drivers.emc.plugins.vnx import utils LOG = log.getLogger(__name__) +@ddt.ddt class StorageConnectionTestCase(test.TestCase): @mock.patch.object(connector.XMLAPIConnector, "_do_setup", mock.Mock()) def setUp(self): super(StorageConnectionTestCase, self).setUp() self.emc_share_driver = fakes.FakeEMCShareDriver() - self.cifs_server_name = fakes.FakeData.vdm_name - self.pool_name = fakes.FakeData.pool_name - self.vdm_name = fakes.FakeData.vdm_name - self.mover_name = fakes.FakeData.mover_name - self.connection = connection.VNXStorageConnection(LOG) self.pool = fakes.PoolTestData() @@ -53,51 +51,91 @@ class StorageConnectionTestCase(test.TestCase): self.cifs_server = fakes.CIFSServerTestData() self.dns = fakes.DNSDomainTestData() - hook = utils.RequestSideEffect() - hook.append(self.mover.resp_get_ref_succeed()) - hook.append(self.pool.resp_get_succeed()) - with mock.patch.object(connector.XMLAPIConnector, 'request', - mock.Mock(side_effect=hook)): + mock.Mock()): self.connection.connect(self.emc_share_driver, None) - expected_calls = [ - mock.call(self.mover.req_get_ref()), - mock.call(self.pool.req_get()), - ] - connector.XMLAPIConnector.request.assert_has_calls(expected_calls) - - @mock.patch.object(connector.XMLAPIConnector, "_do_setup", mock.Mock()) - def test_connect_with_invalid_mover_name(self): - hook = utils.RequestSideEffect() - hook.append(self.mover.resp_get_error()) - - with mock.patch.object(connector.XMLAPIConnector, 'request', - mock.Mock(side_effect=hook)): - self.assertRaises(exception.InvalidParameterValue, - self.connection.connect, - self.emc_share_driver, None) - - expected_calls = [mock.call(self.mover.req_get_ref())] - connector.XMLAPIConnector.request.assert_has_calls(expected_calls) - - @mock.patch.object(connector.XMLAPIConnector, "_do_setup", mock.Mock()) - def test_connect_with_invalid_pool_name(self): + def test_check_for_setup_error(self): hook = utils.RequestSideEffect() hook.append(self.mover.resp_get_ref_succeed()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + with mock.patch.object(connection.VNXStorageConnection, + '_get_managed_storage_pools', + mock.Mock()): + self.connection.check_for_setup_error() + + expected_calls = [mock.call(self.mover.req_get_ref())] + xml_req_mock.assert_has_calls(expected_calls) + + def test_check_for_setup_error_with_invalid_mover_name(self): + hook = utils.RequestSideEffect() + hook.append(self.mover.resp_get_error()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + self.assertRaises(exception.InvalidParameterValue, + self.connection.check_for_setup_error) + + expected_calls = [mock.call(self.mover.req_get_ref())] + xml_req_mock.assert_has_calls(expected_calls) + + @ddt.data({'pool_conf': None, + 'real_pools': ['fake_pool', 'nas_pool'], + 'matched_pool': set()}, + {'pool_conf': '*', + 'real_pools': ['fake_pool', 'nas_pool'], + 'matched_pool': {'fake_pool', 'nas_pool'}}, + {'pool_conf': 'fake_*', + 'real_pools': ['fake_pool', 'nas_pool', 'Perf_Pool'], + 'matched_pool': {'fake_pool'}}, + {'pool_conf': '*pool', + 'real_pools': ['fake_pool', 'NAS_Pool', 'Perf_POOL'], + 'matched_pool': {'fake_pool'}}, + {'pool_conf': 'nas_pool', + 'real_pools': ['fake_pool', 'nas_pool', 'perf_pool'], + 'matched_pool': {'nas_pool'}}) + @ddt.unpack + def test__get_managed_storage_pools(self, pool_conf, real_pools, + matched_pool): + with mock.patch.object(object_manager.StoragePool, + 'get_all', + mock.Mock(return_value=('ok', real_pools))): + pool = self.connection._get_managed_storage_pools(pool_conf) + self.assertEqual(matched_pool, pool) + + def test__get_managed_storage_pools_failed_to_get_pool_info(self): + hook = utils.RequestSideEffect() hook.append(self.pool.resp_get_error()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock - with mock.patch.object(connector.XMLAPIConnector, 'request', - mock.Mock(side_effect=hook)): + pool_conf = fakes.FakeData.pool_name + self.assertRaises(exception.EMCVnxXMLAPIError, + self.connection._get_managed_storage_pools, + pool_conf) + + expected_calls = [mock.call(self.pool.req_get())] + xml_req_mock.assert_has_calls(expected_calls) + + @ddt.data( + {'pool_conf': 'fake_*', + 'real_pools': ['nas_pool', 'Perf_Pool']}, + {'pool_conf': '*pool', + 'real_pools': ['NAS_Pool', 'Perf_POOL']}, + {'pool_conf': 'nas_pool', + 'real_pools': ['fake_pool', 'perf_pool']}, + ) + @ddt.unpack + def test__get_managed_storage_pools_without_matched_pool(self, pool_conf, + real_pools): + with mock.patch.object(object_manager.StoragePool, + 'get_all', + mock.Mock(return_value=('ok', real_pools))): self.assertRaises(exception.InvalidParameterValue, - self.connection.connect, - self.emc_share_driver, None) - - expected_calls = [ - mock.call(self.mover.req_get_ref()), - mock.call(self.pool.req_get()), - ] - connector.XMLAPIConnector.request.assert_has_calls(expected_calls) + self.connection._get_managed_storage_pools, + pool_conf) def test_create_cifs_share(self): share_server = fakes.SHARE_SERVER @@ -107,6 +145,7 @@ class StorageConnectionTestCase(test.TestCase): hook.append(self.vdm.resp_get_succeed()) hook.append(self.cifs_server.resp_get_succeed( mover_id=self.vdm.vdm_id, is_vdm=True, join_domain=True)) + hook.append(self.pool.resp_get_succeed()) hook.append(self.fs.resp_task_succeed()) hook.append(self.cifs_share.resp_task_succeed()) xml_req_mock = utils.EMCMock(side_effect=hook) @@ -122,6 +161,7 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), mock.call(self.cifs_server.req_get(self.vdm.vdm_id)), + mock.call(self.pool.req_get()), mock.call(self.fs.req_create_on_vdm()), mock.call(self.cifs_share.req_create(self.vdm.vdm_id)), ] @@ -138,6 +178,7 @@ class StorageConnectionTestCase(test.TestCase): share = fakes.NFS_SHARE hook = utils.RequestSideEffect() + hook.append(self.pool.resp_get_succeed()) hook.append(self.vdm.resp_get_succeed()) hook.append(self.fs.resp_task_succeed()) xml_req_mock = utils.EMCMock(side_effect=hook) @@ -151,6 +192,7 @@ class StorageConnectionTestCase(test.TestCase): location = self.connection.create_share(None, share, share_server) expected_calls = [ + mock.call(self.pool.req_get()), mock.call(self.vdm.req_get()), mock.call(self.fs.req_create_on_vdm()), ] @@ -206,6 +248,7 @@ class StorageConnectionTestCase(test.TestCase): hook.append(self.vdm.resp_get_succeed()) hook.append(self.cifs_server.resp_get_without_interface( mover_id=self.vdm.vdm_id, is_vdm=True, join_domain=True)) + hook.append(self.pool.resp_get_succeed()) hook.append(self.fs.resp_task_succeed()) xml_req_mock = utils.EMCMock(side_effect=hook) self.connection.manager.connectors['XML'].request = xml_req_mock @@ -217,11 +260,21 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), mock.call(self.cifs_server.req_get(self.vdm.vdm_id)), + mock.call(self.pool.req_get()), mock.call(self.fs.req_create_on_vdm()), ] xml_req_mock.assert_has_calls(expected_calls) + def test_create_cifs_share_without_pool_name(self): + share_server = fakes.SHARE_SERVER + share = fake_share.fake_share(host='HostA@BackendB', + share_proto='CIFS') + + self.assertRaises(exception.InvalidHost, + self.connection.create_share, + None, share, share_server) + def test_create_cifs_share_from_snapshot(self): share_server = fakes.SHARE_SERVER share = fakes.CIFS_SHARE @@ -348,6 +401,16 @@ class StorageConnectionTestCase(test.TestCase): self.connection.create_share_from_snapshot, None, share, snapshot, share_server) + def test_create_share_from_snapshot_without_pool_name(self): + share_server = fakes.SHARE_SERVER + share = fake_share.fake_share(host='HostA@BackendB', + share_proto='CIFS') + snapshot = fake_share.fake_snapshot() + + self.assertRaises(exception.InvalidHost, + self.connection.create_share_from_snapshot, + None, share, snapshot, share_server) + def test_delete_cifs_share(self): share_server = fakes.SHARE_SERVER share = fakes.CIFS_SHARE @@ -458,8 +521,8 @@ class StorageConnectionTestCase(test.TestCase): hook = utils.RequestSideEffect() hook.append(self.fs.resp_get_succeed()) + hook.append(self.pool.resp_get_succeed()) hook.append(self.fs.resp_task_succeed()) - xml_req_mock = utils.EMCMock(side_effect=hook) self.connection.manager.connectors['XML'].request = xml_req_mock @@ -467,10 +530,21 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.fs.req_get()), + mock.call(self.pool.req_get()), mock.call(self.fs.req_extend()), ] xml_req_mock.assert_has_calls(expected_calls) + def test_extend_share_without_pool_name(self): + share_server = fakes.SHARE_SERVER + share = fake_share.fake_share(host='HostA@BackendB', + share_proto='CIFS') + new_size = fakes.FakeData.new_size + + self.assertRaises(exception.InvalidHost, + self.connection.extend_share, + share, new_size, share_server) + def test_create_snapshot(self): share_server = fakes.SHARE_SERVER snapshot = fake_share.fake_snapshot( @@ -492,6 +566,25 @@ class StorageConnectionTestCase(test.TestCase): ] xml_req_mock.assert_has_calls(expected_calls) + def test_create_snapshot_with_incorrect_share_info(self): + share_server = fakes.SHARE_SERVER + snapshot = fake_share.fake_snapshot( + id=fakes.FakeData.snapshot_name, + share_id=fakes.FakeData.filesystem_name, + share_name=fakes.FakeData.share_name) + + hook = utils.RequestSideEffect() + hook.append(self.fs.resp_get_but_not_found()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + self.assertRaises(exception.EMCVnxXMLAPIError, + self.connection.create_snapshot, + None, snapshot, share_server) + + expected_calls = [mock.call(self.fs.req_get())] + xml_req_mock.assert_has_calls(expected_calls) + def test_delete_snapshot(self): share_server = fakes.SHARE_SERVER snapshot = fake_share.fake_snapshot( @@ -516,6 +609,7 @@ class StorageConnectionTestCase(test.TestCase): def test_setup_server(self): hook = utils.RequestSideEffect() hook.append(self.vdm.resp_get_but_not_found()) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.vdm.resp_task_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.mover.resp_task_succeed()) @@ -538,6 +632,7 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), + mock.call(self.mover.req_get_ref()), mock.call(self.vdm.req_create()), mock.call(self.mover.req_create_interface( if_name=if_name_1, @@ -560,6 +655,7 @@ class StorageConnectionTestCase(test.TestCase): def test_setup_server_with_existing_vdm(self): hook = utils.RequestSideEffect() hook.append(self.vdm.resp_get_succeed()) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.dns.resp_task_succeed()) @@ -580,6 +676,7 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), + mock.call(self.mover.req_get_ref()), mock.call(self.mover.req_create_interface( if_name=if_name_1, ip=fakes.FakeData.network_allocations_ip1)), @@ -608,6 +705,7 @@ class StorageConnectionTestCase(test.TestCase): def test_setup_server_without_valid_physical_device(self): hook = utils.RequestSideEffect() hook.append(self.vdm.resp_get_but_not_found()) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.vdm.resp_task_succeed()) hook.append(self.vdm.resp_get_succeed()) hook.append(self.cifs_server.resp_get_without_value()) @@ -627,6 +725,7 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), + mock.call(self.mover.req_get_ref()), mock.call(self.vdm.req_create()), mock.call(self.vdm.req_get()), mock.call(self.cifs_server.req_get(self.vdm.vdm_id)), @@ -643,6 +742,7 @@ class StorageConnectionTestCase(test.TestCase): def test_setup_server_with_exception(self): hook = utils.RequestSideEffect() hook.append(self.vdm.resp_get_but_not_found()) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.vdm.resp_task_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.mover.resp_task_error()) @@ -668,6 +768,7 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), + mock.call(self.mover.req_get_ref()), mock.call(self.vdm.req_create()), mock.call(self.mover.req_create_interface( if_name=if_name_1, @@ -697,6 +798,7 @@ class StorageConnectionTestCase(test.TestCase): hook.append(self.cifs_server.resp_task_succeed()) hook.append(self.cifs_server.resp_get_succeed( mover_id=self.vdm.vdm_id, is_vdm=True, join_domain=False)) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.vdm.resp_task_succeed()) @@ -718,6 +820,7 @@ class StorageConnectionTestCase(test.TestCase): mock.call(self.cifs_server.req_modify( mover_id=self.vdm.vdm_id, is_vdm=True, join_domain=False)), mock.call(self.cifs_server.req_delete(self.vdm.vdm_id)), + mock.call(self.mover.req_get_ref()), mock.call(self.mover.req_delete_interface( fakes.FakeData.network_allocations_ip1)), mock.call(self.mover.req_delete_interface( @@ -738,6 +841,7 @@ class StorageConnectionTestCase(test.TestCase): def test_teardown_server_without_security_services(self): hook = utils.RequestSideEffect() hook.append(self.vdm.resp_get_succeed()) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.vdm.resp_task_succeed()) @@ -754,6 +858,7 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), + mock.call(self.mover.req_get_ref()), mock.call(self.mover.req_delete_interface( fakes.FakeData.network_allocations_ip1)), mock.call(self.mover.req_delete_interface( @@ -791,6 +896,7 @@ class StorageConnectionTestCase(test.TestCase): hook = utils.RequestSideEffect() hook.append(self.vdm.resp_get_succeed()) hook.append(self.cifs_server.resp_get_error()) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.cifs_server.resp_task_succeed()) hook.append(self.cifs_server.resp_get_succeed( mover_id=self.vdm.vdm_id, is_vdm=True, join_domain=False)) @@ -812,6 +918,7 @@ class StorageConnectionTestCase(test.TestCase): expected_calls = [ mock.call(self.vdm.req_get()), mock.call(self.cifs_server.req_get(self.vdm.vdm_id)), + mock.call(self.mover.req_get_ref()), mock.call(self.mover.req_delete_interface( fakes.FakeData.network_allocations_ip1)), mock.call(self.mover.req_delete_interface( @@ -833,6 +940,7 @@ class StorageConnectionTestCase(test.TestCase): mover_id=self.vdm.vdm_id, is_vdm=True, join_domain=True)) hook.append(self.cifs_server.resp_task_error()) hook.append(self.cifs_server.resp_task_succeed()) + hook.append(self.mover.resp_get_ref_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.mover.resp_task_succeed()) hook.append(self.vdm.resp_task_succeed()) @@ -853,6 +961,7 @@ class StorageConnectionTestCase(test.TestCase): mock.call(self.cifs_server.req_get(self.vdm.vdm_id)), mock.call(self.cifs_server.req_modify(self.vdm.vdm_id)), mock.call(self.cifs_server.req_delete(self.vdm.vdm_id)), + mock.call(self.mover.req_get_ref()), mock.call(self.mover.req_delete_interface( fakes.FakeData.network_allocations_ip1)), mock.call(self.mover.req_delete_interface( @@ -1198,9 +1307,103 @@ class StorageConnectionTestCase(test.TestCase): ] xml_req_mock.assert_has_calls(expected_calls) - self.assertEqual(fakes.FakeData.pool_total_size, - fakes.STATS['total_capacity_gb']) + for pool in fakes.STATS['pools']: + if pool['pool_name'] == fakes.FakeData.pool_name: + self.assertEqual(fakes.FakeData.pool_total_size, + pool['total_capacity_gb']) - free_size = (fakes.FakeData.pool_total_size - - fakes.FakeData.pool_used_size) - self.assertEqual(free_size, fakes.STATS['free_capacity_gb']) + free_size = (fakes.FakeData.pool_total_size - + fakes.FakeData.pool_used_size) + self.assertEqual(free_size, pool['free_capacity_gb']) + + def test_update_share_stats_without_matched_config_pools(self): + self.connection.pools = set('fake_pool') + + hook = utils.RequestSideEffect() + hook.append(self.mover.resp_get_ref_succeed()) + hook.append(self.pool.resp_get_succeed()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + self.assertRaises(exception.EMCVnxXMLAPIError, + self.connection.update_share_stats, + fakes.STATS) + + expected_calls = [ + mock.call(self.mover.req_get_ref()), + mock.call(self.pool.req_get()), + ] + xml_req_mock.assert_has_calls(expected_calls) + + def test_get_pool(self): + share = fakes.CIFS_SHARE + + hook = utils.RequestSideEffect() + hook.append(self.fs.resp_get_succeed()) + hook.append(self.pool.resp_get_succeed()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + pool_name = self.connection.get_pool(share) + + expected_calls = [ + mock.call(self.fs.req_get()), + mock.call(self.pool.req_get()), + ] + xml_req_mock.assert_has_calls(expected_calls) + + self.assertEqual(fakes.FakeData.pool_name, pool_name) + + def test_get_pool_failed_to_get_filesystem_info(self): + share = fakes.CIFS_SHARE + + hook = utils.RequestSideEffect() + hook.append(self.fs.resp_get_error()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + self.assertRaises(exception.EMCVnxXMLAPIError, + self.connection.get_pool, + share) + + expected_calls = [mock.call(self.fs.req_get())] + xml_req_mock.assert_has_calls(expected_calls) + + def test_get_pool_failed_to_get_pool_info(self): + share = fakes.CIFS_SHARE + + hook = utils.RequestSideEffect() + hook.append(self.fs.resp_get_succeed()) + hook.append(self.pool.resp_get_error()) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + self.assertRaises(exception.EMCVnxXMLAPIError, + self.connection.get_pool, + share) + + expected_calls = [ + mock.call(self.fs.req_get()), + mock.call(self.pool.req_get()), + ] + xml_req_mock.assert_has_calls(expected_calls) + + def test_get_pool_failed_to_find_matched_pool_name(self): + share = fakes.CIFS_SHARE + + hook = utils.RequestSideEffect() + hook.append(self.fs.resp_get_succeed()) + hook.append(self.pool.resp_get_succeed(name='unmatch_pool_name', + id='unmatch_pool_id')) + xml_req_mock = utils.EMCMock(side_effect=hook) + self.connection.manager.connectors['XML'].request = xml_req_mock + + self.assertRaises(exception.EMCVnxXMLAPIError, + self.connection.get_pool, + share) + + expected_calls = [ + mock.call(self.fs.req_get()), + mock.call(self.pool.req_get()), + ] + xml_req_mock.assert_has_calls(expected_calls) diff --git a/manila/tests/share/drivers/emc/plugins/vnx/test_object_manager.py b/manila/tests/share/drivers/emc/plugins/vnx/test_object_manager.py index 56a5a7ffab..4084648242 100644 --- a/manila/tests/share/drivers/emc/plugins/vnx/test_object_manager.py +++ b/manila/tests/share/drivers/emc/plugins/vnx/test_object_manager.py @@ -1338,7 +1338,6 @@ class SnapshotTestCase(StorageObjectTestCase): def test_create_snapshot(self): self.hook.append(self.fs.resp_get_succeed()) - self.hook.append(self.pool.resp_get_succeed()) self.hook.append(self.snap.resp_task_succeed()) context = self.manager.getStorageContext('Snapshot') @@ -1346,18 +1345,16 @@ class SnapshotTestCase(StorageObjectTestCase): context.create(name=self.snap.snapshot_name, fs_name=self.fs.filesystem_name, - pool_name=self.pool.pool_name) + pool_id=self.pool.pool_id) expected_calls = [ mock.call(self.fs.req_get()), - mock.call(self.pool.req_get()), mock.call(self.snap.req_create()), ] context.conn['XML'].request.assert_has_calls(expected_calls) def test_create_snapshot_but_already_exist(self): self.hook.append(self.fs.resp_get_succeed()) - self.hook.append(self.pool.resp_get_succeed()) self.hook.append(self.snap.resp_create_but_already_exist()) context = self.manager.getStorageContext('Snapshot') @@ -1365,19 +1362,17 @@ class SnapshotTestCase(StorageObjectTestCase): context.create(name=self.snap.snapshot_name, fs_name=self.fs.filesystem_name, - pool_name=self.pool.pool_name, + pool_id=self.pool.pool_id, ckpt_size=self.snap.snapshot_size) expected_calls = [ mock.call(self.fs.req_get()), - mock.call(self.pool.req_get()), mock.call(self.snap.req_create_with_size()), ] context.conn['XML'].request.assert_has_calls(expected_calls) def test_create_snapshot_with_error(self): self.hook.append(self.fs.resp_get_succeed()) - self.hook.append(self.pool.resp_get_succeed()) self.hook.append(self.snap.resp_task_error()) context = self.manager.getStorageContext('Snapshot') @@ -1387,12 +1382,11 @@ class SnapshotTestCase(StorageObjectTestCase): context.create, name=self.snap.snapshot_name, fs_name=self.fs.filesystem_name, - pool_name=self.pool.pool_name, + pool_id=self.pool.pool_id, ckpt_size=self.snap.snapshot_size) expected_calls = [ mock.call(self.fs.req_get()), - mock.call(self.pool.req_get()), mock.call(self.snap.req_create_with_size()), ] context.conn['XML'].request.assert_has_calls(expected_calls)