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:
Tom Barron
2015-08-17 14:08:28 -04:00
parent 77d6bf6550
commit 57b9729594
13 changed files with 360 additions and 173 deletions

View File

@@ -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