Add RemoteFSPoolMixin
This change adds a mixin class to the remotefs module so that any related volume driver can easily start reporting each share as a storage pool. Partially Implements: blueprint remotefs-pools-support Change-Id: I52da65b1987f4ebdcb5c513f2a316bba9de8e892
This commit is contained in:
parent
4287bfa9d4
commit
daeab949a7
@ -481,3 +481,96 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
|
||||
'of=/path', 'bs=1M',
|
||||
'count=1024',
|
||||
run_as_root=True)
|
||||
|
||||
|
||||
class RemoteFSPoolMixinTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(RemoteFSPoolMixinTestCase, self).setUp()
|
||||
# We'll instantiate this directly for now.
|
||||
self._driver = remotefs.RemoteFSPoolMixin()
|
||||
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
@mock.patch.object(remotefs.RemoteFSPoolMixin,
|
||||
'_get_pool_name_from_volume')
|
||||
@mock.patch.object(remotefs.RemoteFSPoolMixin,
|
||||
'_get_share_from_pool_name')
|
||||
def test_find_share(self, mock_get_share_from_pool,
|
||||
mock_get_pool_from_volume):
|
||||
share = self._driver._find_share(mock.sentinel.volume)
|
||||
|
||||
self.assertEqual(mock_get_share_from_pool.return_value, share)
|
||||
mock_get_pool_from_volume.assert_called_once_with(
|
||||
mock.sentinel.volume)
|
||||
mock_get_share_from_pool.assert_called_once_with(
|
||||
mock_get_pool_from_volume.return_value)
|
||||
|
||||
def test_get_pool_name_from_volume(self):
|
||||
fake_pool = 'fake_pool'
|
||||
fake_host = 'fake_host@fake_backend#%s' % fake_pool
|
||||
fake_vol = fake_volume.fake_volume_obj(
|
||||
self.context, provider_location='fake_share',
|
||||
host=fake_host)
|
||||
|
||||
pool_name = self._driver._get_pool_name_from_volume(fake_vol)
|
||||
self.assertEqual(fake_pool, pool_name)
|
||||
|
||||
def test_update_volume_stats(self):
|
||||
share_total_gb = 3
|
||||
share_free_gb = 2
|
||||
share_used_gb = 4 # provisioned space
|
||||
expected_allocated_gb = share_total_gb - share_free_gb
|
||||
|
||||
self._driver._mounted_shares = [mock.sentinel.share]
|
||||
|
||||
self._driver.configuration = mock.Mock()
|
||||
self._driver.configuration.safe_get.return_value = (
|
||||
mock.sentinel.backend_name)
|
||||
self._driver.vendor_name = mock.sentinel.vendor_name
|
||||
self._driver.driver_volume_type = mock.sentinel.driver_volume_type
|
||||
self._driver._thin_provisioning_support = (
|
||||
mock.sentinel.thin_prov_support)
|
||||
|
||||
self._driver.get_version = mock.Mock(
|
||||
return_value=mock.sentinel.driver_version)
|
||||
self._driver._ensure_shares_mounted = mock.Mock()
|
||||
self._driver._get_capacity_info = mock.Mock(
|
||||
return_value=(share_total_gb << 30,
|
||||
share_free_gb << 30,
|
||||
share_used_gb << 30))
|
||||
self._driver._get_pool_name_from_share = mock.Mock(
|
||||
return_value=mock.sentinel.pool_name)
|
||||
|
||||
expected_pool = {
|
||||
'pool_name': mock.sentinel.pool_name,
|
||||
'total_capacity_gb': float(share_total_gb),
|
||||
'free_capacity_gb': float(share_free_gb),
|
||||
'provisioned_capacity_gb': float(share_used_gb),
|
||||
'allocated_capacity_gb': float(expected_allocated_gb),
|
||||
'reserved_percentage': (
|
||||
self._driver.configuration.reserved_percentage),
|
||||
'max_over_subscription_ratio': (
|
||||
self._driver.configuration.max_over_subscription_ratio),
|
||||
'thin_provisioning_support': (
|
||||
mock.sentinel.thin_prov_support),
|
||||
'QoS_support': False,
|
||||
}
|
||||
|
||||
expected_stats = {
|
||||
'volume_backend_name': mock.sentinel.backend_name,
|
||||
'vendor_name': mock.sentinel.vendor_name,
|
||||
'driver_version': mock.sentinel.driver_version,
|
||||
'storage_protocol': mock.sentinel.driver_volume_type,
|
||||
'total_capacity_gb': 0,
|
||||
'free_capacity_gb': 0,
|
||||
'pools': [expected_pool],
|
||||
}
|
||||
|
||||
self._driver._update_volume_stats()
|
||||
|
||||
self.assertDictEqual(expected_stats, self._driver._stats)
|
||||
|
||||
self._driver._get_capacity_info.assert_called_once_with(
|
||||
mock.sentinel.share)
|
||||
self._driver.configuration.safe_get.assert_called_once_with(
|
||||
'volume_backend_name')
|
||||
|
@ -37,6 +37,7 @@ from cinder.image import image_utils
|
||||
from cinder.objects import fields
|
||||
from cinder import utils
|
||||
from cinder.volume import driver
|
||||
from cinder.volume import utils as volume_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -139,6 +140,7 @@ class RemoteFSDriver(driver.BaseVD):
|
||||
driver_volume_type = None
|
||||
driver_prefix = 'remotefs'
|
||||
volume_backend_name = None
|
||||
vendor_name = 'Open Source'
|
||||
SHARE_FORMAT_REGEX = r'.+:/.+'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -149,6 +151,10 @@ class RemoteFSDriver(driver.BaseVD):
|
||||
self._is_voldb_empty_at_startup = kwargs.pop('is_vol_db_empty', None)
|
||||
self._supports_encryption = False
|
||||
|
||||
# We let the drivers inheriting this specify
|
||||
# whether thin provisioning is supported or not.
|
||||
self._thin_provisioning_support = False
|
||||
|
||||
if self.configuration:
|
||||
self.configuration.append_config_values(nas_opts)
|
||||
self.configuration.append_config_values(volume_opts)
|
||||
@ -1579,3 +1585,64 @@ class RemoteFSSnapDriverDistributed(RemoteFSSnapDriverBase):
|
||||
|
||||
return self._copy_volume_to_image(context, volume, image_service,
|
||||
image_meta)
|
||||
|
||||
|
||||
class RemoteFSPoolMixin(object):
|
||||
"""Drivers inheriting this will report each share as a pool."""
|
||||
|
||||
def _find_share(self, volume):
|
||||
# We let the scheduler choose a pool for us.
|
||||
pool_name = self._get_pool_name_from_volume(volume)
|
||||
share = self._get_share_from_pool_name(pool_name)
|
||||
return share
|
||||
|
||||
def _get_pool_name_from_volume(self, volume):
|
||||
pool_name = volume_utils.extract_host(volume['host'],
|
||||
level='pool')
|
||||
return pool_name
|
||||
|
||||
def _get_pool_name_from_share(self, share):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_share_from_pool_name(self, pool_name):
|
||||
# To be implemented by drivers using pools.
|
||||
raise NotImplementedError()
|
||||
|
||||
def _update_volume_stats(self):
|
||||
data = {}
|
||||
pools = []
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
data['volume_backend_name'] = backend_name or self.volume_backend_name
|
||||
data['vendor_name'] = self.vendor_name
|
||||
data['driver_version'] = self.get_version()
|
||||
data['storage_protocol'] = self.driver_volume_type
|
||||
|
||||
self._ensure_shares_mounted()
|
||||
|
||||
for share in self._mounted_shares:
|
||||
(share_capacity,
|
||||
share_free,
|
||||
share_used) = self._get_capacity_info(share)
|
||||
|
||||
pool = {'pool_name': self._get_pool_name_from_share(share),
|
||||
'total_capacity_gb': share_capacity / float(units.Gi),
|
||||
'free_capacity_gb': share_free / float(units.Gi),
|
||||
'provisioned_capacity_gb': share_used / float(units.Gi),
|
||||
'allocated_capacity_gb': (
|
||||
share_capacity - share_free) / float(units.Gi),
|
||||
'reserved_percentage': (
|
||||
self.configuration.reserved_percentage),
|
||||
'max_over_subscription_ratio': (
|
||||
self.configuration.max_over_subscription_ratio),
|
||||
'thin_provisioning_support': (
|
||||
self._thin_provisioning_support),
|
||||
'QoS_support': False,
|
||||
}
|
||||
|
||||
pools.append(pool)
|
||||
|
||||
data['total_capacity_gb'] = 0
|
||||
data['free_capacity_gb'] = 0
|
||||
data['pools'] = pools
|
||||
|
||||
self._stats = data
|
||||
|
Loading…
x
Reference in New Issue
Block a user