EMC VMAX - Oversubscription support
When max_over_subscription_ratio is 2.0 the pool are oversubscribed by a factor of 2, or 200% over subscribed. The reserved_percentage is the high water mark where by the physical remaining space cannot be exceeded. For example, if there is only 4% of physical space left and the reserve percentage is 5, user would not be permitted to provision a volume. This is a safety mechanism to prevent a scenario that a provisioning request failing due to insufficient raw space. Change-Id: I55614d05701d461d4b0b85f6f57c49d98b014641 Implements: blueprint vmax-oversubscription
This commit is contained in:
parent
7a8804aa20
commit
5377ed5810
@ -301,8 +301,11 @@ class EMCVMAXCommonData(object):
|
|||||||
poolname = 'gold'
|
poolname = 'gold'
|
||||||
totalmanagedspace_bits = '1000000000000'
|
totalmanagedspace_bits = '1000000000000'
|
||||||
subscribedcapacity_bits = '500000000000'
|
subscribedcapacity_bits = '500000000000'
|
||||||
|
remainingmanagedspace_bits = '500000000000'
|
||||||
|
maxsubscriptionpercent = 150
|
||||||
totalmanagedspace_gbs = 931
|
totalmanagedspace_gbs = 931
|
||||||
subscribedcapacity_gbs = 466
|
subscribedcapacity_gbs = 465
|
||||||
|
remainingmanagedspace_gbs = 465
|
||||||
fake_host = 'HostX@Backend#gold+1234567891011'
|
fake_host = 'HostX@Backend#gold+1234567891011'
|
||||||
fake_host_v3 = 'HostX@Backend#Bronze+SRP_1+1234567891011'
|
fake_host_v3 = 'HostX@Backend#Bronze+SRP_1+1234567891011'
|
||||||
fake_host_2_v3 = 'HostY@Backend#SRP_1+1234567891011'
|
fake_host_2_v3 = 'HostY@Backend#SRP_1+1234567891011'
|
||||||
@ -1114,6 +1117,8 @@ class FakeEcomConnection(object):
|
|||||||
pool['SystemName'] = self.data.storage_system
|
pool['SystemName'] = self.data.storage_system
|
||||||
pool['TotalManagedSpace'] = self.data.totalmanagedspace_bits
|
pool['TotalManagedSpace'] = self.data.totalmanagedspace_bits
|
||||||
pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits
|
pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits
|
||||||
|
pool['RemainingManagedSpace'] = self.data.remainingmanagedspace_bits
|
||||||
|
pool['EMCMaxSubscriptionPercent'] = self.data.maxsubscriptionpercent
|
||||||
return pool
|
return pool
|
||||||
|
|
||||||
def _getinstance_replicationgroup(self, objectpath):
|
def _getinstance_replicationgroup(self, objectpath):
|
||||||
@ -2843,22 +2848,25 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
|
|||||||
# Bug 1393555 - policy has been deleted by another process.
|
# Bug 1393555 - policy has been deleted by another process.
|
||||||
def test_get_capacities_associated_to_policy(self):
|
def test_get_capacities_associated_to_policy(self):
|
||||||
conn = self.fake_ecom_connection()
|
conn = self.fake_ecom_connection()
|
||||||
total_capacity_gb, free_capacity_gb = (
|
(total_capacity_gb, free_capacity_gb, provisioned_capacity_gb,
|
||||||
|
array_max_over_subscription) = (
|
||||||
self.driver.common.fast.get_capacities_associated_to_policy(
|
self.driver.common.fast.get_capacities_associated_to_policy(
|
||||||
conn, self.data.storage_system, self.data.policyrule))
|
conn, self.data.storage_system, self.data.policyrule))
|
||||||
# The capacities associated to the policy have been found.
|
# The capacities associated to the policy have been found.
|
||||||
self.assertEqual(self.data.totalmanagedspace_gbs, total_capacity_gb)
|
self.assertEqual(self.data.totalmanagedspace_gbs, total_capacity_gb)
|
||||||
self.assertEqual(self.data.subscribedcapacity_gbs, free_capacity_gb)
|
self.assertEqual(self.data.remainingmanagedspace_gbs, free_capacity_gb)
|
||||||
|
|
||||||
self.driver.common.fast.utils.get_existing_instance = mock.Mock(
|
self.driver.common.fast.utils.get_existing_instance = mock.Mock(
|
||||||
return_value=None)
|
return_value=None)
|
||||||
total_capacity_gb_2, free_capacity_gb_2 = (
|
(total_capacity_gb_2, free_capacity_gb_2, provisioned_capacity_gb_2,
|
||||||
|
array_max_over_subscription_2) = (
|
||||||
self.driver.common.fast.get_capacities_associated_to_policy(
|
self.driver.common.fast.get_capacities_associated_to_policy(
|
||||||
conn, self.data.storage_system, self.data.policyrule))
|
conn, self.data.storage_system, self.data.policyrule))
|
||||||
# The capacities have not been found as the policy has been
|
# The capacities have not been found as the policy has been
|
||||||
# removed externally.
|
# removed externally.
|
||||||
self.assertEqual(0, total_capacity_gb_2)
|
self.assertEqual(0, total_capacity_gb_2)
|
||||||
self.assertEqual(0, free_capacity_gb_2)
|
self.assertEqual(0, free_capacity_gb_2)
|
||||||
|
self.assertEqual(0, provisioned_capacity_gb_2)
|
||||||
|
|
||||||
# Bug 1393555 - storage group has been deleted by another process.
|
# Bug 1393555 - storage group has been deleted by another process.
|
||||||
def test_find_storage_masking_group(self):
|
def test_find_storage_masking_group(self):
|
||||||
@ -2994,7 +3002,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
|
|||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_utils.EMCVMAXUtils,
|
emc_vmax_utils.EMCVMAXUtils,
|
||||||
'get_pool_capacities',
|
'get_pool_capacities',
|
||||||
return_value=(1234, 1200))
|
return_value=(1234, 1200, 1200, 1))
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_fast.EMCVMAXFast,
|
emc_vmax_fast.EMCVMAXFast,
|
||||||
'is_tiering_policy_enabled',
|
'is_tiering_policy_enabled',
|
||||||
@ -3824,7 +3832,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
|
|||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_common.EMCVMAXCommon,
|
emc_vmax_common.EMCVMAXCommon,
|
||||||
'_update_pool_stats',
|
'_update_pool_stats',
|
||||||
return_value={1, 2, 3})
|
return_value={1, 2, 3, 4, 5})
|
||||||
def test_ssl_support(self, pool_stats):
|
def test_ssl_support(self, pool_stats):
|
||||||
self.driver.common.update_volume_stats()
|
self.driver.common.update_volume_stats()
|
||||||
self.assertTrue(self.driver.common.ecomUseSSL)
|
self.assertTrue(self.driver.common.ecomUseSSL)
|
||||||
@ -3955,7 +3963,11 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
|
|||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_fast.EMCVMAXFast,
|
emc_vmax_fast.EMCVMAXFast,
|
||||||
'get_capacities_associated_to_policy',
|
'get_capacities_associated_to_policy',
|
||||||
return_value=(1234, 1200))
|
return_value=(1234, 1200, 1200, 1))
|
||||||
|
@mock.patch.object(
|
||||||
|
emc_vmax_utils.EMCVMAXUtils,
|
||||||
|
'get_pool_capacities',
|
||||||
|
return_value=(1234, 1200, 1200, 1))
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_fast.EMCVMAXFast,
|
emc_vmax_fast.EMCVMAXFast,
|
||||||
'get_tier_policy_by_name',
|
'get_tier_policy_by_name',
|
||||||
@ -3972,7 +3984,8 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
|
|||||||
mock_storage_system,
|
mock_storage_system,
|
||||||
mock_is_fast_enabled,
|
mock_is_fast_enabled,
|
||||||
mock_get_policy,
|
mock_get_policy,
|
||||||
mock_capacity):
|
mock_pool_capacities,
|
||||||
|
mock_capacities_associated_to_policy):
|
||||||
self.driver.get_volume_stats(True)
|
self.driver.get_volume_stats(True)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
@ -4591,7 +4604,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
|
|||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_utils.EMCVMAXUtils,
|
emc_vmax_utils.EMCVMAXUtils,
|
||||||
'get_pool_capacities',
|
'get_pool_capacities',
|
||||||
return_value=(1234, 1200))
|
return_value=(1234, 1200, 1200, 1))
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_fast.EMCVMAXFast,
|
emc_vmax_fast.EMCVMAXFast,
|
||||||
'is_tiering_policy_enabled',
|
'is_tiering_policy_enabled',
|
||||||
@ -5163,7 +5176,11 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
|
|||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_fast.EMCVMAXFast,
|
emc_vmax_fast.EMCVMAXFast,
|
||||||
'get_capacities_associated_to_policy',
|
'get_capacities_associated_to_policy',
|
||||||
return_value=(1234, 1200))
|
return_value=(1234, 1200, 1200, 1))
|
||||||
|
@mock.patch.object(
|
||||||
|
emc_vmax_utils.EMCVMAXUtils,
|
||||||
|
'get_pool_capacities',
|
||||||
|
return_value=(1234, 1200, 1200, 1))
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
emc_vmax_fast.EMCVMAXFast,
|
emc_vmax_fast.EMCVMAXFast,
|
||||||
'get_tier_policy_by_name',
|
'get_tier_policy_by_name',
|
||||||
@ -5180,7 +5197,8 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
|
|||||||
mock_storage_system,
|
mock_storage_system,
|
||||||
mock_is_fast_enabled,
|
mock_is_fast_enabled,
|
||||||
mock_get_policy,
|
mock_get_policy,
|
||||||
mock_capacity):
|
mock_pool_capacities,
|
||||||
|
mock_capacities_associated_to_policy):
|
||||||
self.driver.get_volume_stats(True)
|
self.driver.get_volume_stats(True)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
@ -5734,7 +5752,7 @@ class EMCV3DriverTestCase(test.TestCase):
|
|||||||
def set_configuration(self):
|
def set_configuration(self):
|
||||||
configuration = mock.Mock()
|
configuration = mock.Mock()
|
||||||
configuration.cinder_emc_config_file = self.config_file_path
|
configuration.cinder_emc_config_file = self.config_file_path
|
||||||
configuration.safe_get.return_value = 'V3'
|
configuration.safe_get.return_value = 3
|
||||||
configuration.config_group = 'V3'
|
configuration.config_group = 'V3'
|
||||||
|
|
||||||
self.stubs.Set(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
|
self.stubs.Set(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
|
||||||
@ -7944,6 +7962,45 @@ class EMCVMAXUtilsTest(test.TestCase):
|
|||||||
self.assertFalse(self.driver.utils.is_clone_licensed(
|
self.assertFalse(self.driver.utils.is_clone_licensed(
|
||||||
conn, capabilityInstanceName, isV3))
|
conn, capabilityInstanceName, isV3))
|
||||||
|
|
||||||
|
def test_get_pool_capacities(self):
|
||||||
|
conn = FakeEcomConnection()
|
||||||
|
|
||||||
|
(total_capacity_gb, free_capacity_gb, provisioned_capacity_gb,
|
||||||
|
array_max_over_subscription) = (
|
||||||
|
self.driver.utils.get_pool_capacities(
|
||||||
|
conn, self.data.poolname, self.data.storage_system))
|
||||||
|
self.assertEqual(931, total_capacity_gb)
|
||||||
|
self.assertEqual(465, free_capacity_gb)
|
||||||
|
self.assertEqual(465, provisioned_capacity_gb)
|
||||||
|
self.assertEqual(1.5, array_max_over_subscription)
|
||||||
|
|
||||||
|
def test_get_pool_capacities_none_array_max_oversubscription(self):
|
||||||
|
conn = FakeEcomConnection()
|
||||||
|
null_emcmaxsubscriptionpercent = {
|
||||||
|
'TotalManagedSpace': '1000000000000',
|
||||||
|
'ElementName': 'gold',
|
||||||
|
'RemainingManagedSpace': '500000000000',
|
||||||
|
'SystemName': 'SYMMETRIX+000195900551',
|
||||||
|
'CreationClassName': 'Symm_VirtualProvisioningPool',
|
||||||
|
'EMCSubscribedCapacity': '500000000000'}
|
||||||
|
conn.GetInstance = mock.Mock(
|
||||||
|
return_value=null_emcmaxsubscriptionpercent)
|
||||||
|
(total_capacity_gb, free_capacity_gb, provisioned_capacity_gb,
|
||||||
|
array_max_over_subscription) = (
|
||||||
|
self.driver.utils.get_pool_capacities(
|
||||||
|
conn, self.data.poolname, self.data.storage_system))
|
||||||
|
self.assertEqual(65534, array_max_over_subscription)
|
||||||
|
|
||||||
|
def test_get_ratio_from_max_sub_per(self):
|
||||||
|
max_subscription_percent_float = (
|
||||||
|
self.driver.utils.get_ratio_from_max_sub_per(150))
|
||||||
|
self.assertEqual(1.5, max_subscription_percent_float)
|
||||||
|
|
||||||
|
def test_get_ratio_from_max_sub_per_none_value(self):
|
||||||
|
max_subscription_percent_float = (
|
||||||
|
self.driver.utils.get_ratio_from_max_sub_per(str(0)))
|
||||||
|
self.assertIsNone(max_subscription_percent_float)
|
||||||
|
|
||||||
|
|
||||||
class EMCVMAXCommonTest(test.TestCase):
|
class EMCVMAXCommonTest(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -102,7 +102,10 @@ class EMCVMAXCommon(object):
|
|||||||
|
|
||||||
pool_info = {'backend_name': None,
|
pool_info = {'backend_name': None,
|
||||||
'config_file': None,
|
'config_file': None,
|
||||||
'arrays_info': {}}
|
'arrays_info': {},
|
||||||
|
'max_over_subscription_ratio': None,
|
||||||
|
'reserved_percentage': None
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, prtcl, version, configuration=None):
|
def __init__(self, prtcl, version, configuration=None):
|
||||||
|
|
||||||
@ -137,6 +140,10 @@ class EMCVMAXCommon(object):
|
|||||||
|
|
||||||
self.pool_info['backend_name'] = (
|
self.pool_info['backend_name'] = (
|
||||||
self.configuration.safe_get('volume_backend_name'))
|
self.configuration.safe_get('volume_backend_name'))
|
||||||
|
self.pool_info['max_over_subscription_ratio'] = (
|
||||||
|
self.configuration.safe_get('max_over_subscription_ratio'))
|
||||||
|
self.pool_info['reserved_percentage'] = (
|
||||||
|
self.configuration.safe_get('reserved_percentage'))
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Updating volume stats on file %(emcConfigFileName)s on "
|
"Updating volume stats on file %(emcConfigFileName)s on "
|
||||||
"backend %(backendName)s.",
|
"backend %(backendName)s.",
|
||||||
@ -626,20 +633,27 @@ class EMCVMAXCommon(object):
|
|||||||
"""Retrieve stats info."""
|
"""Retrieve stats info."""
|
||||||
pools = []
|
pools = []
|
||||||
backendName = self.pool_info['backend_name']
|
backendName = self.pool_info['backend_name']
|
||||||
|
max_oversubscription_ratio = (
|
||||||
|
self.pool_info['max_over_subscription_ratio'])
|
||||||
|
reservedPercentage = self.pool_info['reserved_percentage']
|
||||||
|
array_max_over_subscription = None
|
||||||
|
array_reserve_percent = None
|
||||||
for arrayInfo in self.pool_info['arrays_info']:
|
for arrayInfo in self.pool_info['arrays_info']:
|
||||||
self._set_ecom_credentials(arrayInfo)
|
self._set_ecom_credentials(arrayInfo)
|
||||||
# Check what type of array it is
|
# Check what type of array it is
|
||||||
isV3 = self.utils.isArrayV3(self.conn, arrayInfo['SerialNumber'])
|
isV3 = self.utils.isArrayV3(self.conn, arrayInfo['SerialNumber'])
|
||||||
if isV3:
|
if isV3:
|
||||||
location_info, total_capacity_gb, free_capacity_gb = (
|
(location_info, total_capacity_gb, free_capacity_gb,
|
||||||
self._update_srp_stats(arrayInfo))
|
provisioned_capacity_gb,
|
||||||
|
array_reserve_percent) = self._update_srp_stats(arrayInfo)
|
||||||
poolName = ("%(slo)s+%(poolName)s+%(array)s"
|
poolName = ("%(slo)s+%(poolName)s+%(array)s"
|
||||||
% {'slo': arrayInfo['SLO'],
|
% {'slo': arrayInfo['SLO'],
|
||||||
'poolName': arrayInfo['PoolName'],
|
'poolName': arrayInfo['PoolName'],
|
||||||
'array': arrayInfo['SerialNumber']})
|
'array': arrayInfo['SerialNumber']})
|
||||||
else:
|
else:
|
||||||
# This is V2
|
# This is V2
|
||||||
location_info, total_capacity_gb, free_capacity_gb = (
|
(location_info, total_capacity_gb, free_capacity_gb,
|
||||||
|
provisioned_capacity_gb, array_max_over_subscription) = (
|
||||||
self._update_pool_stats(backendName, arrayInfo))
|
self._update_pool_stats(backendName, arrayInfo))
|
||||||
poolName = ("%(poolName)s+%(array)s"
|
poolName = ("%(poolName)s+%(array)s"
|
||||||
% {'poolName': arrayInfo['PoolName'],
|
% {'poolName': arrayInfo['PoolName'],
|
||||||
@ -648,10 +662,25 @@ class EMCVMAXCommon(object):
|
|||||||
pool = {'pool_name': poolName,
|
pool = {'pool_name': poolName,
|
||||||
'total_capacity_gb': total_capacity_gb,
|
'total_capacity_gb': total_capacity_gb,
|
||||||
'free_capacity_gb': free_capacity_gb,
|
'free_capacity_gb': free_capacity_gb,
|
||||||
'reserved_percentage': 0,
|
'provisioned_capacity_gb': provisioned_capacity_gb,
|
||||||
'QoS_support': False,
|
'QoS_support': False,
|
||||||
'location_info': location_info,
|
'location_info': location_info,
|
||||||
'consistencygroup_support': True}
|
'consistencygroup_support': True,
|
||||||
|
'thin_provisioning_support': True,
|
||||||
|
'thick_provisioning_support': False,
|
||||||
|
'max_over_subscription_ratio': max_oversubscription_ratio
|
||||||
|
}
|
||||||
|
if array_max_over_subscription:
|
||||||
|
pool['max_over_subscription_ratio'] = (
|
||||||
|
self.utils.override_ratio(
|
||||||
|
max_oversubscription_ratio,
|
||||||
|
array_max_over_subscription))
|
||||||
|
|
||||||
|
if array_reserve_percent and (
|
||||||
|
array_reserve_percent > reservedPercentage):
|
||||||
|
pool['reserved_percentage'] = array_reserve_percent
|
||||||
|
else:
|
||||||
|
pool['reserved_percentage'] = reservedPercentage
|
||||||
pools.append(pool)
|
pools.append(pool)
|
||||||
|
|
||||||
data = {'vendor_name': "EMC",
|
data = {'vendor_name': "EMC",
|
||||||
@ -662,6 +691,7 @@ class EMCVMAXCommon(object):
|
|||||||
# Use zero capacities here so we always use a pool.
|
# Use zero capacities here so we always use a pool.
|
||||||
'total_capacity_gb': 0,
|
'total_capacity_gb': 0,
|
||||||
'free_capacity_gb': 0,
|
'free_capacity_gb': 0,
|
||||||
|
'provisioned_capacity_gb': 0,
|
||||||
'reserved_percentage': 0,
|
'reserved_percentage': 0,
|
||||||
'pools': pools}
|
'pools': pools}
|
||||||
|
|
||||||
@ -674,20 +704,24 @@ class EMCVMAXCommon(object):
|
|||||||
:returns: location_info
|
:returns: location_info
|
||||||
:returns: totalManagedSpaceGbs
|
:returns: totalManagedSpaceGbs
|
||||||
:returns: remainingManagedSpaceGbs
|
:returns: remainingManagedSpaceGbs
|
||||||
|
:returns: provisionedManagedSpaceGbs
|
||||||
|
:returns: array_reserve_percent
|
||||||
"""
|
"""
|
||||||
|
|
||||||
totalManagedSpaceGbs, remainingManagedSpaceGbs = (
|
(totalManagedSpaceGbs, remainingManagedSpaceGbs,
|
||||||
self.provisionv3.get_srp_pool_stats(self.conn,
|
provisionedManagedSpaceGbs, array_reserve_percent) = (
|
||||||
arrayInfo))
|
self.provisionv3.get_srp_pool_stats(self.conn, arrayInfo))
|
||||||
|
|
||||||
LOG.info(_LI(
|
LOG.info(_LI(
|
||||||
"Capacity stats for SRP pool %(poolName)s on array "
|
"Capacity stats for SRP pool %(poolName)s on array "
|
||||||
"%(arrayName)s total_capacity_gb=%(total_capacity_gb)lu, "
|
"%(arrayName)s total_capacity_gb=%(total_capacity_gb)lu, "
|
||||||
"free_capacity_gb=%(free_capacity_gb)lu"),
|
"free_capacity_gb=%(free_capacity_gb)lu, "
|
||||||
|
"provisioned_capacity_gb=%(provisioned_capacity_gb)lu"),
|
||||||
{'poolName': arrayInfo['PoolName'],
|
{'poolName': arrayInfo['PoolName'],
|
||||||
'arrayName': arrayInfo['SerialNumber'],
|
'arrayName': arrayInfo['SerialNumber'],
|
||||||
'total_capacity_gb': totalManagedSpaceGbs,
|
'total_capacity_gb': totalManagedSpaceGbs,
|
||||||
'free_capacity_gb': remainingManagedSpaceGbs})
|
'free_capacity_gb': remainingManagedSpaceGbs,
|
||||||
|
'provisioned_capacity_gb': provisionedManagedSpaceGbs})
|
||||||
|
|
||||||
location_info = ("%(arrayName)s#%(poolName)s#%(slo)s#%(workload)s"
|
location_info = ("%(arrayName)s#%(poolName)s#%(slo)s#%(workload)s"
|
||||||
% {'arrayName': arrayInfo['SerialNumber'],
|
% {'arrayName': arrayInfo['SerialNumber'],
|
||||||
@ -695,7 +729,9 @@ class EMCVMAXCommon(object):
|
|||||||
'slo': arrayInfo['SLO'],
|
'slo': arrayInfo['SLO'],
|
||||||
'workload': arrayInfo['Workload']})
|
'workload': arrayInfo['Workload']})
|
||||||
|
|
||||||
return location_info, totalManagedSpaceGbs, remainingManagedSpaceGbs
|
return (location_info, totalManagedSpaceGbs,
|
||||||
|
remainingManagedSpaceGbs, provisionedManagedSpaceGbs,
|
||||||
|
array_reserve_percent)
|
||||||
|
|
||||||
def retype(self, ctxt, volume, new_type, diff, host):
|
def retype(self, ctxt, volume, new_type, diff, host):
|
||||||
"""Migrate volume to another host using retype.
|
"""Migrate volume to another host using retype.
|
||||||
@ -3193,7 +3229,8 @@ class EMCVMAXCommon(object):
|
|||||||
|
|
||||||
:param backendName: the backend name
|
:param backendName: the backend name
|
||||||
:param arrayInfo: the arrayInfo
|
:param arrayInfo: the arrayInfo
|
||||||
:returns: location_info, total_capacity_gb, free_capacity_gb
|
:returns: location_info, total_capacity_gb, free_capacity_gb,
|
||||||
|
provisioned_capacity_gb
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if arrayInfo['FastPolicy']:
|
if arrayInfo['FastPolicy']:
|
||||||
@ -3216,7 +3253,8 @@ class EMCVMAXCommon(object):
|
|||||||
|
|
||||||
if (arrayInfo['FastPolicy'] is not None and
|
if (arrayInfo['FastPolicy'] is not None and
|
||||||
isTieringPolicySupported is True): # FAST enabled
|
isTieringPolicySupported is True): # FAST enabled
|
||||||
total_capacity_gb, free_capacity_gb = (
|
(total_capacity_gb, free_capacity_gb, provisioned_capacity_gb,
|
||||||
|
array_max_over_subscription) = (
|
||||||
self.fast.get_capacities_associated_to_policy(
|
self.fast.get_capacities_associated_to_policy(
|
||||||
self.conn, arrayInfo['SerialNumber'],
|
self.conn, arrayInfo['SerialNumber'],
|
||||||
arrayInfo['FastPolicy']))
|
arrayInfo['FastPolicy']))
|
||||||
@ -3229,7 +3267,8 @@ class EMCVMAXCommon(object):
|
|||||||
'total_capacity_gb': total_capacity_gb,
|
'total_capacity_gb': total_capacity_gb,
|
||||||
'free_capacity_gb': free_capacity_gb})
|
'free_capacity_gb': free_capacity_gb})
|
||||||
else: # NON-FAST
|
else: # NON-FAST
|
||||||
total_capacity_gb, free_capacity_gb = (
|
(total_capacity_gb, free_capacity_gb, provisioned_capacity_gb,
|
||||||
|
array_max_over_subscription) = (
|
||||||
self.utils.get_pool_capacities(self.conn,
|
self.utils.get_pool_capacities(self.conn,
|
||||||
arrayInfo['PoolName'],
|
arrayInfo['PoolName'],
|
||||||
arrayInfo['SerialNumber']))
|
arrayInfo['SerialNumber']))
|
||||||
@ -3247,7 +3286,8 @@ class EMCVMAXCommon(object):
|
|||||||
'poolName': arrayInfo['PoolName'],
|
'poolName': arrayInfo['PoolName'],
|
||||||
'policyName': arrayInfo['FastPolicy']})
|
'policyName': arrayInfo['FastPolicy']})
|
||||||
|
|
||||||
return location_info, total_capacity_gb, free_capacity_gb
|
return (location_info, total_capacity_gb, free_capacity_gb,
|
||||||
|
provisioned_capacity_gb, array_max_over_subscription)
|
||||||
|
|
||||||
def _set_v2_extra_specs(self, extraSpecs, poolRecord):
|
def _set_v2_extra_specs(self, extraSpecs, poolRecord):
|
||||||
"""Set the VMAX V2 extra specs.
|
"""Set the VMAX V2 extra specs.
|
||||||
|
@ -697,14 +697,19 @@ class EMCVMAXFast(object):
|
|||||||
:param policyName: the name of policy rule, a string value
|
:param policyName: the name of policy rule, a string value
|
||||||
:returns: int -- total capacity in GB of all pools associated with
|
:returns: int -- total capacity in GB of all pools associated with
|
||||||
the policy
|
the policy
|
||||||
:returns: int -- (total capacity-EMCSubscribedCapacity) in GB of all
|
:returns: int -- real physical capacity in GB of all pools
|
||||||
pools associated with the policy
|
available to be used
|
||||||
|
:returns: int -- (Provisioned capacity-EMCSubscribedCapacity) in GB
|
||||||
|
is the the capacity that has been provisioned
|
||||||
|
:returns: int -- the maximum oversubscription ration
|
||||||
"""
|
"""
|
||||||
policyInstanceName = self.get_tier_policy_by_name(
|
policyInstanceName = self.get_tier_policy_by_name(
|
||||||
conn, arrayName, policyName)
|
conn, arrayName, policyName)
|
||||||
|
|
||||||
total_capacity_gb = 0
|
total_capacity_gb = 0
|
||||||
allocated_capacity_gb = 0
|
provisioned_capacity_gb = 0
|
||||||
|
free_capacity_gb = 0
|
||||||
|
array_max_over_subscription = None
|
||||||
|
|
||||||
tierInstanceNames = self.get_associated_tier_from_tier_policy(
|
tierInstanceNames = self.get_associated_tier_from_tier_policy(
|
||||||
conn, policyInstanceName)
|
conn, policyInstanceName)
|
||||||
@ -726,17 +731,25 @@ class EMCVMAXFast(object):
|
|||||||
break
|
break
|
||||||
total_capacity_gb += self.utils.convert_bits_to_gbs(
|
total_capacity_gb += self.utils.convert_bits_to_gbs(
|
||||||
storagePoolInstance['TotalManagedSpace'])
|
storagePoolInstance['TotalManagedSpace'])
|
||||||
allocated_capacity_gb += self.utils.convert_bits_to_gbs(
|
provisioned_capacity_gb += self.utils.convert_bits_to_gbs(
|
||||||
storagePoolInstance['EMCSubscribedCapacity'])
|
storagePoolInstance['EMCSubscribedCapacity'])
|
||||||
|
free_capacity_gb += self.utils.convert_bits_to_gbs(
|
||||||
|
storagePoolInstance['RemainingManagedSpace'])
|
||||||
|
try:
|
||||||
|
array_max_over_subscription = (
|
||||||
|
self.utils.get_ratio_from_max_sub_per(
|
||||||
|
storagePoolInstance['EMCMaxSubscriptionPercent']))
|
||||||
|
except KeyError:
|
||||||
|
array_max_over_subscription = 65534
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"PolicyName:%(policyName)s, pool: %(poolInstanceName)s, "
|
"PolicyName:%(policyName)s, pool: %(poolInstanceName)s, "
|
||||||
"allocated_capacity_gb = %(allocated_capacity_gb)lu.",
|
"provisioned_capacity_gb = %(provisioned_capacity_gb)lu.",
|
||||||
{'policyName': policyName,
|
{'policyName': policyName,
|
||||||
'poolInstanceName': poolInstanceName,
|
'poolInstanceName': poolInstanceName,
|
||||||
'allocated_capacity_gb': allocated_capacity_gb})
|
'provisioned_capacity_gb': provisioned_capacity_gb})
|
||||||
|
|
||||||
free_capacity_gb = total_capacity_gb - allocated_capacity_gb
|
return (total_capacity_gb, free_capacity_gb,
|
||||||
return (total_capacity_gb, free_capacity_gb)
|
provisioned_capacity_gb, array_max_over_subscription)
|
||||||
|
|
||||||
def get_or_create_default_storage_group(
|
def get_or_create_default_storage_group(
|
||||||
self, conn, controllerConfigService, fastPolicyName,
|
self, conn, controllerConfigService, fastPolicyName,
|
||||||
|
@ -69,7 +69,7 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
|
|||||||
- Operations and timeout issues (bug #1538214)
|
- Operations and timeout issues (bug #1538214)
|
||||||
2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634)
|
2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634)
|
||||||
- SnapVX licensing checks for VMAX3 (bug #1587017)
|
- SnapVX licensing checks for VMAX3 (bug #1587017)
|
||||||
|
- VMAX oversubscription Support (blueprint vmax-oversubscription)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "2.4.0"
|
VERSION = "2.4.0"
|
||||||
|
@ -75,6 +75,7 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
|
|||||||
- Operations and timeout issues (bug #1538214)
|
- Operations and timeout issues (bug #1538214)
|
||||||
2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634)
|
2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634)
|
||||||
- SnapVX licensing checks for VMAX3 (bug #1587017)
|
- SnapVX licensing checks for VMAX3 (bug #1587017)
|
||||||
|
- VMAX oversubscription Support (blueprint vmax-oversubscription)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -704,9 +704,13 @@ class EMCVMAXProvisionV3(object):
|
|||||||
:param arrayInfo: the array dict
|
:param arrayInfo: the array dict
|
||||||
:returns: totalCapacityGb
|
:returns: totalCapacityGb
|
||||||
:returns: remainingCapacityGb
|
:returns: remainingCapacityGb
|
||||||
|
:returns: subscribedCapacityGb
|
||||||
|
:returns: array_reserve_percent
|
||||||
"""
|
"""
|
||||||
totalCapacityGb = -1
|
totalCapacityGb = -1
|
||||||
remainingCapacityGb = -1
|
remainingCapacityGb = -1
|
||||||
|
subscribedCapacityGb = -1
|
||||||
|
array_reserve_percent = -1
|
||||||
storageSystemInstanceName = self.utils.find_storageSystem(
|
storageSystemInstanceName = self.utils.find_storageSystem(
|
||||||
conn, arrayInfo['SerialNumber'])
|
conn, arrayInfo['SerialNumber'])
|
||||||
|
|
||||||
@ -735,6 +739,15 @@ class EMCVMAXProvisionV3(object):
|
|||||||
remainingCapacityGb = (
|
remainingCapacityGb = (
|
||||||
self.utils.convert_bits_to_gbs(
|
self.utils.convert_bits_to_gbs(
|
||||||
remainingManagedSpace))
|
remainingManagedSpace))
|
||||||
|
elif properties[0] == 'EMCSubscribedCapacity':
|
||||||
|
cimProperties = properties[1]
|
||||||
|
subscribedManagedSpace = cimProperties.value
|
||||||
|
subscribedCapacityGb = (
|
||||||
|
self.utils.convert_bits_to_gbs(
|
||||||
|
subscribedManagedSpace))
|
||||||
|
elif properties[0] == 'EMCPercentReservedCapacity':
|
||||||
|
cimProperties = properties[1]
|
||||||
|
array_reserve_percent = int(cimProperties.value)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
remainingSLOCapacityGb = (
|
remainingSLOCapacityGb = (
|
||||||
@ -751,7 +764,8 @@ class EMCVMAXProvisionV3(object):
|
|||||||
"not be what you expect."),
|
"not be what you expect."),
|
||||||
{'remainingCapacityGb': remainingCapacityGb})
|
{'remainingCapacityGb': remainingCapacityGb})
|
||||||
|
|
||||||
return totalCapacityGb, remainingCapacityGb
|
return (totalCapacityGb, remainingCapacityGb, subscribedCapacityGb,
|
||||||
|
array_reserve_percent)
|
||||||
|
|
||||||
def _get_remaining_slo_capacity_wlp(self, conn, srpPoolInstanceName,
|
def _get_remaining_slo_capacity_wlp(self, conn, srpPoolInstanceName,
|
||||||
arrayInfo, systemName):
|
arrayInfo, systemName):
|
||||||
|
@ -978,7 +978,8 @@ class EMCVMAXUtils(object):
|
|||||||
:param conn: connection to the ecom server
|
:param conn: connection to the ecom server
|
||||||
:param poolName: string value of the storage pool name
|
:param poolName: string value of the storage pool name
|
||||||
:param storageSystemName: the storage system name
|
:param storageSystemName: the storage system name
|
||||||
:returns: tuple -- (total_capacity_gb, free_capacity_gb)
|
:returns: tuple -- (total_capacity_gb, free_capacity_gb,
|
||||||
|
provisioned_capacity_gb)
|
||||||
"""
|
"""
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Retrieving capacity for pool %(poolName)s on array %(array)s.",
|
"Retrieving capacity for pool %(poolName)s on array %(array)s.",
|
||||||
@ -997,10 +998,17 @@ class EMCVMAXUtils(object):
|
|||||||
poolInstanceName, LocalOnly=False)
|
poolInstanceName, LocalOnly=False)
|
||||||
total_capacity_gb = self.convert_bits_to_gbs(
|
total_capacity_gb = self.convert_bits_to_gbs(
|
||||||
storagePoolInstance['TotalManagedSpace'])
|
storagePoolInstance['TotalManagedSpace'])
|
||||||
allocated_capacity_gb = self.convert_bits_to_gbs(
|
provisioned_capacity_gb = self.convert_bits_to_gbs(
|
||||||
storagePoolInstance['EMCSubscribedCapacity'])
|
storagePoolInstance['EMCSubscribedCapacity'])
|
||||||
free_capacity_gb = total_capacity_gb - allocated_capacity_gb
|
free_capacity_gb = self.convert_bits_to_gbs(
|
||||||
return (total_capacity_gb, free_capacity_gb)
|
storagePoolInstance['RemainingManagedSpace'])
|
||||||
|
try:
|
||||||
|
array_max_over_subscription = self.get_ratio_from_max_sub_per(
|
||||||
|
storagePoolInstance['EMCMaxSubscriptionPercent'])
|
||||||
|
except KeyError:
|
||||||
|
array_max_over_subscription = 65534
|
||||||
|
return (total_capacity_gb, free_capacity_gb,
|
||||||
|
provisioned_capacity_gb, array_max_over_subscription)
|
||||||
|
|
||||||
def get_pool_by_name(self, conn, storagePoolName, storageSystemName):
|
def get_pool_by_name(self, conn, storagePoolName, storageSystemName):
|
||||||
"""Returns the instance name associated with a storage pool name.
|
"""Returns the instance name associated with a storage pool name.
|
||||||
@ -2598,3 +2606,42 @@ class EMCVMAXUtils(object):
|
|||||||
sgInstanceName = self.find_storage_masking_group(
|
sgInstanceName = self.find_storage_masking_group(
|
||||||
conn, controllerConfigService, storageGroupName)
|
conn, controllerConfigService, storageGroupName)
|
||||||
return storageGroupName, controllerConfigService, sgInstanceName
|
return storageGroupName, controllerConfigService, sgInstanceName
|
||||||
|
|
||||||
|
def get_ratio_from_max_sub_per(self, max_subscription_percent):
|
||||||
|
"""Get ratio from max subscription percent if it exists.
|
||||||
|
|
||||||
|
Check if the max subscription is set on the pool, if it is convert
|
||||||
|
it to a ratio.
|
||||||
|
|
||||||
|
:param max_subscription_percent: max subscription percent
|
||||||
|
:returns: max_over_subscription_ratio
|
||||||
|
"""
|
||||||
|
if max_subscription_percent == '0':
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
max_subscription_percent_int = int(max_subscription_percent)
|
||||||
|
except ValueError:
|
||||||
|
LOG.error(_LE("Cannot convert max subscription percent to int."))
|
||||||
|
return None
|
||||||
|
return float(max_subscription_percent_int) / 100
|
||||||
|
|
||||||
|
def override_ratio(self, max_over_sub_ratio, max_sub_ratio_from_per):
|
||||||
|
"""Override ratio if necessary
|
||||||
|
|
||||||
|
The over subscription ratio will be overriden if the max subscription
|
||||||
|
percent is less than the user supplied max oversubscription ratio.
|
||||||
|
|
||||||
|
:param max_over_sub_ratio: user supplied over subscription ratio
|
||||||
|
:param max_sub_ratio_from_per: property on the pool
|
||||||
|
:returns: max_over_sub_ratio
|
||||||
|
"""
|
||||||
|
if max_over_sub_ratio:
|
||||||
|
try:
|
||||||
|
max_over_sub_ratio = max(float(max_over_sub_ratio),
|
||||||
|
float(max_sub_ratio_from_per))
|
||||||
|
except ValueError:
|
||||||
|
max_over_sub_ratio = float(max_sub_ratio_from_per)
|
||||||
|
elif max_sub_ratio_from_per:
|
||||||
|
max_over_sub_ratio = float(max_sub_ratio_from_per)
|
||||||
|
|
||||||
|
return max_over_sub_ratio
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added oversubscription support in the VMAX driver
|
Loading…
Reference in New Issue
Block a user