From 5377ed581083d51acfdf35faf185f0ff1ab0e86f Mon Sep 17 00:00:00 2001 From: Helen Walsh Date: Mon, 18 Apr 2016 22:56:23 +0100 Subject: [PATCH] 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 --- .../unit/volume/drivers/emc/test_emc_vmax.py | 81 ++++++++++++++++--- cinder/volume/drivers/emc/emc_vmax_common.py | 72 +++++++++++++---- cinder/volume/drivers/emc/emc_vmax_fast.py | 29 +++++-- cinder/volume/drivers/emc/emc_vmax_fc.py | 2 +- cinder/volume/drivers/emc/emc_vmax_iscsi.py | 1 + .../drivers/emc/emc_vmax_provision_v3.py | 16 +++- cinder/volume/drivers/emc/emc_vmax_utils.py | 55 ++++++++++++- ...max-oversubscription-d61d0e3b1df2487a.yaml | 3 + 8 files changed, 217 insertions(+), 42 deletions(-) create mode 100644 releasenotes/notes/vmax-oversubscription-d61d0e3b1df2487a.yaml diff --git a/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py b/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py index 1141a6a88ba..0c3f0d3f8d4 100644 --- a/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py +++ b/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py @@ -301,8 +301,11 @@ class EMCVMAXCommonData(object): poolname = 'gold' totalmanagedspace_bits = '1000000000000' subscribedcapacity_bits = '500000000000' + remainingmanagedspace_bits = '500000000000' + maxsubscriptionpercent = 150 totalmanagedspace_gbs = 931 - subscribedcapacity_gbs = 466 + subscribedcapacity_gbs = 465 + remainingmanagedspace_gbs = 465 fake_host = 'HostX@Backend#gold+1234567891011' fake_host_v3 = 'HostX@Backend#Bronze+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['TotalManagedSpace'] = self.data.totalmanagedspace_bits pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits + pool['RemainingManagedSpace'] = self.data.remainingmanagedspace_bits + pool['EMCMaxSubscriptionPercent'] = self.data.maxsubscriptionpercent return pool def _getinstance_replicationgroup(self, objectpath): @@ -2843,22 +2848,25 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): # Bug 1393555 - policy has been deleted by another process. def test_get_capacities_associated_to_policy(self): 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( conn, self.data.storage_system, self.data.policyrule)) # The capacities associated to the policy have been found. 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( 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( conn, self.data.storage_system, self.data.policyrule)) # The capacities have not been found as the policy has been # removed externally. self.assertEqual(0, total_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. def test_find_storage_masking_group(self): @@ -2994,7 +3002,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_utils.EMCVMAXUtils, 'get_pool_capacities', - return_value=(1234, 1200)) + return_value=(1234, 1200, 1200, 1)) @mock.patch.object( emc_vmax_fast.EMCVMAXFast, 'is_tiering_policy_enabled', @@ -3824,7 +3832,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, '_update_pool_stats', - return_value={1, 2, 3}) + return_value={1, 2, 3, 4, 5}) def test_ssl_support(self, pool_stats): self.driver.common.update_volume_stats() self.assertTrue(self.driver.common.ecomUseSSL) @@ -3955,7 +3963,11 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_fast.EMCVMAXFast, '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( emc_vmax_fast.EMCVMAXFast, 'get_tier_policy_by_name', @@ -3972,7 +3984,8 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase): mock_storage_system, mock_is_fast_enabled, mock_get_policy, - mock_capacity): + mock_pool_capacities, + mock_capacities_associated_to_policy): self.driver.get_volume_stats(True) @mock.patch.object( @@ -4591,7 +4604,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_utils.EMCVMAXUtils, 'get_pool_capacities', - return_value=(1234, 1200)) + return_value=(1234, 1200, 1200, 1)) @mock.patch.object( emc_vmax_fast.EMCVMAXFast, 'is_tiering_policy_enabled', @@ -5163,7 +5176,11 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_fast.EMCVMAXFast, '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( emc_vmax_fast.EMCVMAXFast, 'get_tier_policy_by_name', @@ -5180,7 +5197,8 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase): mock_storage_system, mock_is_fast_enabled, mock_get_policy, - mock_capacity): + mock_pool_capacities, + mock_capacities_associated_to_policy): self.driver.get_volume_stats(True) @mock.patch.object( @@ -5734,7 +5752,7 @@ class EMCV3DriverTestCase(test.TestCase): def set_configuration(self): configuration = mock.Mock() 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' 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( 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): def setUp(self): diff --git a/cinder/volume/drivers/emc/emc_vmax_common.py b/cinder/volume/drivers/emc/emc_vmax_common.py index a1feb800106..0e78b05aebd 100644 --- a/cinder/volume/drivers/emc/emc_vmax_common.py +++ b/cinder/volume/drivers/emc/emc_vmax_common.py @@ -102,7 +102,10 @@ class EMCVMAXCommon(object): pool_info = {'backend_name': None, 'config_file': None, - 'arrays_info': {}} + 'arrays_info': {}, + 'max_over_subscription_ratio': None, + 'reserved_percentage': None + } def __init__(self, prtcl, version, configuration=None): @@ -137,6 +140,10 @@ class EMCVMAXCommon(object): self.pool_info['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( "Updating volume stats on file %(emcConfigFileName)s on " "backend %(backendName)s.", @@ -626,20 +633,27 @@ class EMCVMAXCommon(object): """Retrieve stats info.""" pools = [] 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']: self._set_ecom_credentials(arrayInfo) # Check what type of array it is isV3 = self.utils.isArrayV3(self.conn, arrayInfo['SerialNumber']) if isV3: - location_info, total_capacity_gb, free_capacity_gb = ( - self._update_srp_stats(arrayInfo)) + (location_info, total_capacity_gb, free_capacity_gb, + provisioned_capacity_gb, + array_reserve_percent) = self._update_srp_stats(arrayInfo) poolName = ("%(slo)s+%(poolName)s+%(array)s" % {'slo': arrayInfo['SLO'], 'poolName': arrayInfo['PoolName'], 'array': arrayInfo['SerialNumber']}) else: # 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)) poolName = ("%(poolName)s+%(array)s" % {'poolName': arrayInfo['PoolName'], @@ -648,10 +662,25 @@ class EMCVMAXCommon(object): pool = {'pool_name': poolName, 'total_capacity_gb': total_capacity_gb, 'free_capacity_gb': free_capacity_gb, - 'reserved_percentage': 0, + 'provisioned_capacity_gb': provisioned_capacity_gb, 'QoS_support': False, '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) data = {'vendor_name': "EMC", @@ -662,6 +691,7 @@ class EMCVMAXCommon(object): # Use zero capacities here so we always use a pool. 'total_capacity_gb': 0, 'free_capacity_gb': 0, + 'provisioned_capacity_gb': 0, 'reserved_percentage': 0, 'pools': pools} @@ -674,20 +704,24 @@ class EMCVMAXCommon(object): :returns: location_info :returns: totalManagedSpaceGbs :returns: remainingManagedSpaceGbs + :returns: provisionedManagedSpaceGbs + :returns: array_reserve_percent """ - totalManagedSpaceGbs, remainingManagedSpaceGbs = ( - self.provisionv3.get_srp_pool_stats(self.conn, - arrayInfo)) + (totalManagedSpaceGbs, remainingManagedSpaceGbs, + provisionedManagedSpaceGbs, array_reserve_percent) = ( + self.provisionv3.get_srp_pool_stats(self.conn, arrayInfo)) LOG.info(_LI( "Capacity stats for SRP pool %(poolName)s on array " "%(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'], 'arrayName': arrayInfo['SerialNumber'], '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" % {'arrayName': arrayInfo['SerialNumber'], @@ -695,7 +729,9 @@ class EMCVMAXCommon(object): 'slo': arrayInfo['SLO'], '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): """Migrate volume to another host using retype. @@ -3193,7 +3229,8 @@ class EMCVMAXCommon(object): :param backendName: the backend name :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']: @@ -3216,7 +3253,8 @@ class EMCVMAXCommon(object): if (arrayInfo['FastPolicy'] is not None and 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.conn, arrayInfo['SerialNumber'], arrayInfo['FastPolicy'])) @@ -3229,7 +3267,8 @@ class EMCVMAXCommon(object): 'total_capacity_gb': total_capacity_gb, 'free_capacity_gb': free_capacity_gb}) 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, arrayInfo['PoolName'], arrayInfo['SerialNumber'])) @@ -3247,7 +3286,8 @@ class EMCVMAXCommon(object): 'poolName': arrayInfo['PoolName'], '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): """Set the VMAX V2 extra specs. diff --git a/cinder/volume/drivers/emc/emc_vmax_fast.py b/cinder/volume/drivers/emc/emc_vmax_fast.py index 5fc9b8ff098..3f88a5371c6 100644 --- a/cinder/volume/drivers/emc/emc_vmax_fast.py +++ b/cinder/volume/drivers/emc/emc_vmax_fast.py @@ -697,14 +697,19 @@ class EMCVMAXFast(object): :param policyName: the name of policy rule, a string value :returns: int -- total capacity in GB of all pools associated with the policy - :returns: int -- (total capacity-EMCSubscribedCapacity) in GB of all - pools associated with the policy + :returns: int -- real physical capacity in GB of all pools + 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( conn, arrayName, policyName) 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( conn, policyInstanceName) @@ -726,17 +731,25 @@ class EMCVMAXFast(object): break total_capacity_gb += self.utils.convert_bits_to_gbs( storagePoolInstance['TotalManagedSpace']) - allocated_capacity_gb += self.utils.convert_bits_to_gbs( + provisioned_capacity_gb += self.utils.convert_bits_to_gbs( 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( "PolicyName:%(policyName)s, pool: %(poolInstanceName)s, " - "allocated_capacity_gb = %(allocated_capacity_gb)lu.", + "provisioned_capacity_gb = %(provisioned_capacity_gb)lu.", {'policyName': policyName, '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( self, conn, controllerConfigService, fastPolicyName, diff --git a/cinder/volume/drivers/emc/emc_vmax_fc.py b/cinder/volume/drivers/emc/emc_vmax_fc.py index 29a3fce8d07..f999341240b 100644 --- a/cinder/volume/drivers/emc/emc_vmax_fc.py +++ b/cinder/volume/drivers/emc/emc_vmax_fc.py @@ -69,7 +69,7 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver): - Operations and timeout issues (bug #1538214) 2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634) - SnapVX licensing checks for VMAX3 (bug #1587017) - + - VMAX oversubscription Support (blueprint vmax-oversubscription) """ VERSION = "2.4.0" diff --git a/cinder/volume/drivers/emc/emc_vmax_iscsi.py b/cinder/volume/drivers/emc/emc_vmax_iscsi.py index 29538003a0a..2f53ad954f1 100644 --- a/cinder/volume/drivers/emc/emc_vmax_iscsi.py +++ b/cinder/volume/drivers/emc/emc_vmax_iscsi.py @@ -75,6 +75,7 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver): - Operations and timeout issues (bug #1538214) 2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634) - SnapVX licensing checks for VMAX3 (bug #1587017) + - VMAX oversubscription Support (blueprint vmax-oversubscription) """ diff --git a/cinder/volume/drivers/emc/emc_vmax_provision_v3.py b/cinder/volume/drivers/emc/emc_vmax_provision_v3.py index 0160a58289b..ece84b050ba 100644 --- a/cinder/volume/drivers/emc/emc_vmax_provision_v3.py +++ b/cinder/volume/drivers/emc/emc_vmax_provision_v3.py @@ -704,9 +704,13 @@ class EMCVMAXProvisionV3(object): :param arrayInfo: the array dict :returns: totalCapacityGb :returns: remainingCapacityGb + :returns: subscribedCapacityGb + :returns: array_reserve_percent """ totalCapacityGb = -1 remainingCapacityGb = -1 + subscribedCapacityGb = -1 + array_reserve_percent = -1 storageSystemInstanceName = self.utils.find_storageSystem( conn, arrayInfo['SerialNumber']) @@ -735,6 +739,15 @@ class EMCVMAXProvisionV3(object): remainingCapacityGb = ( self.utils.convert_bits_to_gbs( 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: pass remainingSLOCapacityGb = ( @@ -751,7 +764,8 @@ class EMCVMAXProvisionV3(object): "not be what you expect."), {'remainingCapacityGb': remainingCapacityGb}) - return totalCapacityGb, remainingCapacityGb + return (totalCapacityGb, remainingCapacityGb, subscribedCapacityGb, + array_reserve_percent) def _get_remaining_slo_capacity_wlp(self, conn, srpPoolInstanceName, arrayInfo, systemName): diff --git a/cinder/volume/drivers/emc/emc_vmax_utils.py b/cinder/volume/drivers/emc/emc_vmax_utils.py index 02ea6c9e440..488e8724c03 100644 --- a/cinder/volume/drivers/emc/emc_vmax_utils.py +++ b/cinder/volume/drivers/emc/emc_vmax_utils.py @@ -978,7 +978,8 @@ class EMCVMAXUtils(object): :param conn: connection to the ecom server :param poolName: string value of the storage pool 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( "Retrieving capacity for pool %(poolName)s on array %(array)s.", @@ -997,10 +998,17 @@ class EMCVMAXUtils(object): poolInstanceName, LocalOnly=False) total_capacity_gb = self.convert_bits_to_gbs( storagePoolInstance['TotalManagedSpace']) - allocated_capacity_gb = self.convert_bits_to_gbs( + provisioned_capacity_gb = self.convert_bits_to_gbs( storagePoolInstance['EMCSubscribedCapacity']) - free_capacity_gb = total_capacity_gb - allocated_capacity_gb - return (total_capacity_gb, free_capacity_gb) + free_capacity_gb = self.convert_bits_to_gbs( + 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): """Returns the instance name associated with a storage pool name. @@ -2598,3 +2606,42 @@ class EMCVMAXUtils(object): sgInstanceName = self.find_storage_masking_group( conn, controllerConfigService, storageGroupName) 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 diff --git a/releasenotes/notes/vmax-oversubscription-d61d0e3b1df2487a.yaml b/releasenotes/notes/vmax-oversubscription-d61d0e3b1df2487a.yaml new file mode 100644 index 00000000000..c31f05012a4 --- /dev/null +++ b/releasenotes/notes/vmax-oversubscription-d61d0e3b1df2487a.yaml @@ -0,0 +1,3 @@ +--- +features: + - Added oversubscription support in the VMAX driver