Scheduler-based over-subscription for NFS drivers
Add support to the NFS drivers for over-subscription/overprovisioning. This allows the Cinder scheduler to more accurately represent the remaining space on a pool when thin-provisioned Cinder Volumes are in use on an NFS backend. DocImpact Co-Authored-By: Clinton Knight <cknight@netapp.com> Co-Authored-By: Mike Rooney <rooneym@netapp.com> Implements: blueprint nfs-over-subscription-thin-provisioning Change-Id: Ib94d5131554466709e168c18d1b8690dddd55ff0
This commit is contained in:
@@ -21,6 +21,7 @@ from os_brick.remotefs import remotefs as remotefs_brick
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_log import versionutils
|
||||
from oslo_utils import units
|
||||
import six
|
||||
|
||||
@@ -35,6 +36,9 @@ VERSION = '1.3.0'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
NFS_USED_RATIO_DEFAULT = 0.95
|
||||
NFS_OVERSUB_RATIO_DEFAULT = 1.0
|
||||
|
||||
nfs_opts = [
|
||||
cfg.StrOpt('nfs_shares_config',
|
||||
default='/etc/cinder/nfs_shares',
|
||||
@@ -44,16 +48,23 @@ nfs_opts = [
|
||||
help=('Create volumes as sparsed files which take no space.'
|
||||
'If set to False volume is created as regular file.'
|
||||
'In such case volume creation takes a lot of time.')),
|
||||
# TODO(tbarron): remove nfs_used_ratio in the Mitaka release.
|
||||
cfg.FloatOpt('nfs_used_ratio',
|
||||
default=0.95,
|
||||
default=NFS_USED_RATIO_DEFAULT,
|
||||
help=('Percent of ACTUAL usage of the underlying volume '
|
||||
'before no new volumes can be allocated to the volume '
|
||||
'destination.')),
|
||||
'destination. Note that this option is deprecated '
|
||||
'in favor of "reserved_percentage" and will be removed '
|
||||
'in the Mitaka release.')),
|
||||
# TODO(tbarron): remove nfs_oversub_ratio in the Mitaka release.
|
||||
cfg.FloatOpt('nfs_oversub_ratio',
|
||||
default=1.0,
|
||||
default=NFS_OVERSUB_RATIO_DEFAULT,
|
||||
help=('This will compare the allocated to available space on '
|
||||
'the volume destination. If the ratio exceeds this '
|
||||
'number, the destination will no longer be valid.')),
|
||||
'number, the destination will no longer be valid. '
|
||||
'Note that this option is deprecated in favor of '
|
||||
'"max_oversubscription_ratio" and will be removed '
|
||||
'in the Mitaka release.')),
|
||||
cfg.StrOpt('nfs_mount_point_base',
|
||||
default='$state_path/mnt',
|
||||
help=('Base dir containing mount points for nfs shares.')),
|
||||
@@ -110,6 +121,10 @@ class NfsDriver(driver.ExtendVD, remotefs.RemoteFSDriver):
|
||||
nfs_mount_point_base=self.base,
|
||||
nfs_mount_options=opts)
|
||||
|
||||
self._sparse_copy_volume_data = True
|
||||
self.reserved_percentage = self._get_reserved_percentage()
|
||||
self.over_subscription_ratio = self._get_over_subscription_ratio()
|
||||
|
||||
def set_execute(self, execute):
|
||||
super(NfsDriver, self).set_execute(execute)
|
||||
if self._remotefsclient:
|
||||
@@ -130,18 +145,6 @@ class NfsDriver(driver.ExtendVD, remotefs.RemoteFSDriver):
|
||||
{'config': config})
|
||||
LOG.warning(msg)
|
||||
raise exception.NfsException(msg)
|
||||
if not self.configuration.nfs_oversub_ratio > 0:
|
||||
msg = _("NFS config 'nfs_oversub_ratio' invalid. Must be > 0: "
|
||||
"%s") % self.configuration.nfs_oversub_ratio
|
||||
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidConfigurationValue(msg)
|
||||
if not ((self.configuration.nfs_used_ratio > 0) and
|
||||
(self.configuration.nfs_used_ratio <= 1)):
|
||||
msg = _("NFS config 'nfs_used_ratio' invalid. Must be > 0 "
|
||||
"and <= 1.0: %s") % self.configuration.nfs_used_ratio
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidConfigurationValue(msg)
|
||||
|
||||
self.shares = {} # address : options
|
||||
|
||||
@@ -232,17 +235,30 @@ class NfsDriver(driver.ExtendVD, remotefs.RemoteFSDriver):
|
||||
:param nfs_share: nfs share
|
||||
:param volume_size_in_gib: int size in GB
|
||||
"""
|
||||
# Because the generic NFS driver aggregates over all shares
|
||||
# when reporting capacity and usage stats to the scheduler,
|
||||
# we still have to perform some scheduler-like capacity
|
||||
# checks here, and these have to take into account
|
||||
# configuration for reserved space and oversubscription.
|
||||
# It would be better to do all this in the scheduler, but
|
||||
# this requires either pool support for the generic NFS
|
||||
# driver or limiting each NFS backend driver to a single share.
|
||||
|
||||
used_ratio = self.configuration.nfs_used_ratio
|
||||
oversub_ratio = self.configuration.nfs_oversub_ratio
|
||||
# 'nfs_used_ratio' is deprecated, so derive used_ratio from
|
||||
# reserved_percentage.
|
||||
used_percentage = 100 - self.reserved_percentage
|
||||
used_ratio = used_percentage / 100.0
|
||||
|
||||
oversub_ratio = self.over_subscription_ratio
|
||||
requested_volume_size = volume_size_in_gib * units.Gi
|
||||
|
||||
total_size, total_available, total_allocated = \
|
||||
self._get_capacity_info(nfs_share)
|
||||
apparent_size = max(0, total_size * oversub_ratio)
|
||||
apparent_available = max(0, apparent_size - total_allocated)
|
||||
used = (total_size - total_available) / total_size
|
||||
if used > used_ratio:
|
||||
|
||||
actual_used_ratio = (total_size - total_available) / float(total_size)
|
||||
if actual_used_ratio > used_ratio:
|
||||
# NOTE(morganfainberg): We check the used_ratio first since
|
||||
# with oversubscription it is possible to not have the actual
|
||||
# available space but be within our oversubscription limit
|
||||
@@ -372,3 +388,64 @@ class NfsDriver(driver.ExtendVD, remotefs.RemoteFSDriver):
|
||||
|
||||
super(NfsDriver, self)._update_volume_stats()
|
||||
self._stats['sparse_copy_volume'] = True
|
||||
data = self._stats
|
||||
|
||||
global_capacity = data['total_capacity_gb']
|
||||
global_free = data['free_capacity_gb']
|
||||
|
||||
thin_enabled = self.configuration.nfs_sparsed_volumes
|
||||
if thin_enabled:
|
||||
provisioned_capacity = self._get_provisioned_capacity()
|
||||
else:
|
||||
provisioned_capacity = round(global_capacity - global_free, 2)
|
||||
|
||||
data['provisioned_capacity_gb'] = provisioned_capacity
|
||||
data['max_over_subscription_ratio'] = self.over_subscription_ratio
|
||||
data['reserved_percentage'] = self.reserved_percentage
|
||||
data['thin_provisioning_support'] = thin_enabled
|
||||
data['thick_provisioning_support'] = not thin_enabled
|
||||
|
||||
self._stats = data
|
||||
|
||||
def _get_over_subscription_ratio(self):
|
||||
legacy_oversub_ratio = self.configuration.nfs_oversub_ratio
|
||||
if legacy_oversub_ratio == NFS_OVERSUB_RATIO_DEFAULT:
|
||||
return self.configuration.max_over_subscription_ratio
|
||||
|
||||
# Honor legacy option if its value is not the default.
|
||||
msg = _LW("The option 'nfs_oversub_ratio' is deprecated and will "
|
||||
"be removed in the Mitaka release. Please set "
|
||||
"'max_over_subscription_ratio = %s' instead.") % (
|
||||
self.configuration.nfs_oversub_ratio)
|
||||
versionutils.report_deprecated_feature(LOG, msg)
|
||||
|
||||
if not self.configuration.nfs_oversub_ratio > 0:
|
||||
msg = _("NFS config 'nfs_oversub_ratio' invalid. Must be > 0: "
|
||||
"%s.") % self.configuration.nfs_oversub_ratio
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidConfigurationValue(msg)
|
||||
|
||||
return legacy_oversub_ratio
|
||||
|
||||
def _get_reserved_percentage(self):
|
||||
legacy_used_ratio = self.configuration.nfs_used_ratio
|
||||
legacy_reserved_ratio = 1 - legacy_used_ratio
|
||||
legacy_percentage = legacy_reserved_ratio * 100
|
||||
if legacy_used_ratio == NFS_USED_RATIO_DEFAULT:
|
||||
return self.configuration.reserved_percentage
|
||||
|
||||
# Honor legacy option if its value is not the default.
|
||||
msg = _LW("The option 'nfs_used_ratio' is deprecated and will "
|
||||
"be removed in the Mitaka release. Please set "
|
||||
"'reserved_percentage = %d' instead.") % (
|
||||
legacy_percentage)
|
||||
versionutils.report_deprecated_feature(LOG, msg)
|
||||
|
||||
if not ((self.configuration.nfs_used_ratio > 0) and
|
||||
(self.configuration.nfs_used_ratio <= 1)):
|
||||
msg = _("NFS config 'nfs_used_ratio' invalid. Must be > 0 "
|
||||
"and <= 1.0: %s.") % self.configuration.nfs_used_ratio
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidConfigurationValue(msg)
|
||||
|
||||
return legacy_percentage
|
||||
|
||||
Reference in New Issue
Block a user