Merge "Differentiate thick and thin provisioning"

This commit is contained in:
Jenkins 2016-08-31 19:01:56 +00:00 committed by Gerrit Code Review
commit 90e95c68f8
6 changed files with 201 additions and 46 deletions

View File

@ -81,11 +81,22 @@ class CapacityFilter(filters.BaseHostFilter):
"requested": volume_size,
"available": free}
# NOTE(xyang): If 'provisioning:type' is 'thick' in extra_specs,
# we will not use max_over_subscription_ratio and
# provisioned_capacity_gb to determine whether a volume can be
# provisioned. Instead free capacity will be used to evaluate.
thin = True
vol_type = filter_properties.get('volume_type', {})
provision_type = vol_type.get('extra_specs', {}).get(
'provisioning:type')
if provision_type == 'thick':
thin = False
# Only evaluate using max_over_subscription_ratio if
# thin_provisioning_support is True. Check if the ratio of
# provisioned capacity over total capacity has exceeded over
# subscription ratio.
if (host_state.thin_provisioning_support and
if (thin and host_state.thin_provisioning_support and
host_state.max_over_subscription_ratio >= 1):
provisioned_ratio = ((host_state.provisioned_capacity_gb +
volume_size) / total)
@ -110,7 +121,7 @@ class CapacityFilter(filters.BaseHostFilter):
adjusted_free_virtual = (
free * host_state.max_over_subscription_ratio)
return adjusted_free_virtual >= volume_size
elif host_state.thin_provisioning_support:
elif thin and host_state.thin_provisioning_support:
LOG.warning(_LW("Filtering out host %(host)s with an invalid "
"maximum over subscription ratio of "
"%(oversub_ratio).2f. The ratio should be a "

View File

@ -114,13 +114,25 @@ class CapacityWeigher(weights.BaseHostWeigher):
# capacity anymore.
free = -1 if CONF.capacity_weight_multiplier > 0 else float('inf')
else:
# NOTE(xyang): If 'provisioning:type' is 'thick' in extra_specs,
# we will not use max_over_subscription_ratio and
# provisioned_capacity_gb to determine whether a volume can be
# provisioned. Instead free capacity will be used to evaluate.
thin = True
vol_type = weight_properties.get('volume_type', {})
provision_type = vol_type.get('extra_specs', {}).get(
'provisioning:type')
if provision_type == 'thick':
thin = False
free = utils.calculate_virtual_free_capacity(
total_space,
free_space,
host_state.provisioned_capacity_gb,
host_state.thin_provisioning_support,
host_state.max_over_subscription_ratio,
host_state.reserved_percentage)
host_state.reserved_percentage,
thin)
return free

View File

@ -16,6 +16,7 @@
Tests For Capacity Weigher.
"""
import ddt
import mock
from cinder.common import constants
@ -26,6 +27,7 @@ from cinder.tests.unit.scheduler import fakes
from cinder.volume import utils
@ddt.ddt
class CapacityWeigherTestCase(test.TestCase):
def setUp(self):
super(CapacityWeigherTestCase, self).setUp()
@ -53,15 +55,38 @@ class CapacityWeigherTestCase(test.TestCase):
topic=constants.VOLUME_TOPIC, disabled=disabled)
return host_states
# If thin_provisioning_support = False, use the following formula:
# free = free_space - math.floor(total * reserved)
# Otherwise, use the following formula:
# If thin and thin_provisioning_support are True,
# use the following formula:
# free = (total * host_state.max_over_subscription_ratio
# - host_state.provisioned_capacity_gb
# - math.floor(total * reserved))
def test_default_of_spreading_first(self):
# Otherwise, use the following formula:
# free = free_space - math.floor(total * reserved)
@ddt.data(
{'type_key': 'provisioning:type', 'type_val': 'thin',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host2'},
{'type_key': 'provisioning:type', 'type_val': 'thick',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host1'},
{'type_key': None, 'type_val': None,
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host2'},
{'type_key': None, 'type_val': None,
'vol_type': 'volume_type', 'extra_specs': None,
'winner': 'host2'},
{'type_key': None, 'type_val': None,
'vol_type': None, 'extra_specs': None,
'winner': 'host2'},
)
@ddt.unpack
def test_default_of_spreading_first(self, type_key, type_val,
vol_type, extra_specs, winner):
hostinfo_list = self._get_all_hosts()
# Results for the 1st test
# {'provisioning:type': 'thin'}:
# host1: thin_provisioning_support = False
# free_capacity_gb=1024,
# free=1024-math.floor(1024*0.1)=922
@ -81,14 +106,45 @@ class CapacityWeigherTestCase(test.TestCase):
# Norm=0.0819000819001
# so, host2 should win:
weighed_host = self._get_weighed_hosts(hostinfo_list)[0]
weight_properties = {
'size': 1,
vol_type: {
extra_specs: {
type_key: type_val,
}
}
}
weighed_host = self._get_weighed_hosts(
hostinfo_list,
weight_properties=weight_properties)[0]
self.assertEqual(1.0, weighed_host.weight)
self.assertEqual('host2', utils.extract_host(weighed_host.obj.host))
self.assertEqual(winner, utils.extract_host(weighed_host.obj.host))
def test_capacity_weight_multiplier1(self):
@ddt.data(
{'type_key': 'provisioning:type', 'type_val': 'thin',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host4'},
{'type_key': 'provisioning:type', 'type_val': 'thick',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host2'},
{'type_key': None, 'type_val': None,
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host4'},
{'type_key': None, 'type_val': None,
'vol_type': 'volume_type', 'extra_specs': None,
'winner': 'host4'},
{'type_key': None, 'type_val': None,
'vol_type': None, 'extra_specs': None,
'winner': 'host4'},
)
@ddt.unpack
def test_capacity_weight_multiplier1(self, type_key, type_val,
vol_type, extra_specs, winner):
self.flags(capacity_weight_multiplier=-1.0)
hostinfo_list = self._get_all_hosts()
# Results for the 1st test
# {'provisioning:type': 'thin'}:
# host1: thin_provisioning_support = False
# free_capacity_gb=1024,
# free=-(1024-math.floor(1024*0.1))=-922
@ -108,14 +164,45 @@ class CapacityWeigherTestCase(test.TestCase):
# Norm=-1.0
# so, host4 should win:
weighed_host = self._get_weighed_hosts(hostinfo_list)[0]
weight_properties = {
'size': 1,
vol_type: {
extra_specs: {
type_key: type_val,
}
}
}
weighed_host = self._get_weighed_hosts(
hostinfo_list,
weight_properties=weight_properties)[0]
self.assertEqual(0.0, weighed_host.weight)
self.assertEqual('host4', utils.extract_host(weighed_host.obj.host))
self.assertEqual(winner, utils.extract_host(weighed_host.obj.host))
def test_capacity_weight_multiplier2(self):
@ddt.data(
{'type_key': 'provisioning:type', 'type_val': 'thin',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host2'},
{'type_key': 'provisioning:type', 'type_val': 'thick',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host1'},
{'type_key': None, 'type_val': None,
'vol_type': 'volume_type', 'extra_specs': 'extra_specs',
'winner': 'host2'},
{'type_key': None, 'type_val': None,
'vol_type': 'volume_type', 'extra_specs': None,
'winner': 'host2'},
{'type_key': None, 'type_val': None,
'vol_type': None, 'extra_specs': None,
'winner': 'host2'},
)
@ddt.unpack
def test_capacity_weight_multiplier2(self, type_key, type_val,
vol_type, extra_specs, winner):
self.flags(capacity_weight_multiplier=2.0)
hostinfo_list = self._get_all_hosts()
# Results for the 1st test
# {'provisioning:type': 'thin'}:
# host1: thin_provisioning_support = False
# free_capacity_gb=1024,
# free=(1024-math.floor(1024*0.1))*2=1844
@ -135,9 +222,19 @@ class CapacityWeigherTestCase(test.TestCase):
# Norm=0.1638001638
# so, host2 should win:
weighed_host = self._get_weighed_hosts(hostinfo_list)[0]
weight_properties = {
'size': 1,
vol_type: {
extra_specs: {
type_key: type_val,
}
}
}
weighed_host = self._get_weighed_hosts(
hostinfo_list,
weight_properties=weight_properties)[0]
self.assertEqual(1.0 * 2, weighed_host.weight)
self.assertEqual('host2', utils.extract_host(weighed_host.obj.host))
self.assertEqual(winner, utils.extract_host(weighed_host.obj.host))
def test_capacity_weight_no_unknown_or_infinite(self):
self.flags(capacity_weight_multiplier=-1.0)

View File

@ -47,6 +47,7 @@ class HostFiltersTestCase(test.TestCase):
self.class_map[cls.__name__] = cls
@ddt.ddt
class CapacityFilterTestCase(HostFiltersTestCase):
def setUp(self):
super(CapacityFilterTestCase, self).setUp()
@ -510,6 +511,37 @@ class CapacityFilterTestCase(HostFiltersTestCase):
'service': service})
self.assertTrue(filt_cls.host_passes(host, filter_properties))
@ddt.data(
{'type_key': 'provisioning:type', 'type_val': 'thick',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs'},
{'type_key': 'provisioning:type', 'type_val': 'thin',
'vol_type': 'volume_type', 'extra_specs': 'extra_specs'},
{'type_key': None, 'type_val': None,
'vol_type': 'volume_type', 'extra_specs': None},
{'type_key': None, 'type_val': None,
'vol_type': None, 'extra_specs': None},
)
@ddt.unpack
@mock.patch('cinder.utils.service_is_up')
def test_filter_provisioning_type(self, _mock_serv_is_up, type_key,
type_val, vol_type, extra_specs):
_mock_serv_is_up.return_value = True
filt_cls = self.class_map['CapacityFilter']()
filter_properties = {'size': 100,
vol_type: {extra_specs: {type_key: type_val}}}
service = {'disabled': False}
host = fakes.FakeHostState('host1',
{'total_capacity_gb': 500,
'free_capacity_gb': 100,
'provisioned_capacity_gb': 400,
'max_over_subscription_ratio': 2.0,
'reserved_percentage': 0,
'thin_provisioning_support': True,
'thick_provisioning_support': True,
'updated_at': None,
'service': service})
self.assertTrue(filt_cls.host_passes(host, filter_properties))
class AffinityFilterTestCase(HostFiltersTestCase):
@mock.patch('cinder.utils.service_is_up')

View File

@ -18,6 +18,7 @@ import functools
import os
import time
import ddt
import mock
from oslo_concurrency import processutils as putils
from oslo_utils import timeutils
@ -1078,6 +1079,7 @@ class TestRetryDecorator(test.TestCase):
self.assertFalse(mock_sleep.called)
@ddt.ddt
class LogTracingTestCase(test.TestCase):
def test_utils_setup_tracing(self):
@ -1346,43 +1348,42 @@ class LogTracingTestCase(test.TestCase):
self.assertIn("'adminPass': '***'",
str(mock_log.debug.call_args_list[1]))
def test_utils_calculate_virtual_free_capacity_with_thick(self):
host_stat = {'total_capacity_gb': 30.01,
'free_capacity_gb': 28.01,
'provisioned_capacity_gb': 2.0,
'max_over_subscription_ratio': 1.0,
'thin_provisioning_support': False,
'thick_provisioning_support': True,
@ddt.data(
{'total': 30.01, 'free': 28.01, 'provisioned': 2.0, 'max_ratio': 1.0,
'thin_support': False, 'thick_support': True,
'is_thin_lun': False, 'expected': 27.01},
{'total': 20.01, 'free': 18.01, 'provisioned': 2.0, 'max_ratio': 2.0,
'thin_support': True, 'thick_support': False,
'is_thin_lun': True, 'expected': 37.02},
{'total': 20.01, 'free': 18.01, 'provisioned': 2.0, 'max_ratio': 2.0,
'thin_support': True, 'thick_support': True,
'is_thin_lun': True, 'expected': 37.02},
{'total': 30.01, 'free': 28.01, 'provisioned': 2.0, 'max_ratio': 2.0,
'thin_support': True, 'thick_support': True,
'is_thin_lun': False, 'expected': 27.01},
)
@ddt.unpack
def test_utils_calculate_virtual_free_capacity_provision_type(
self, total, free, provisioned, max_ratio, thin_support,
thick_support, is_thin_lun, expected):
host_stat = {'total_capacity_gb': total,
'free_capacity_gb': free,
'provisioned_capacity_gb': provisioned,
'max_over_subscription_ratio': max_ratio,
'thin_provisioning_support': thin_support,
'thick_provisioning_support': thick_support,
'reserved_percentage': 5}
free = utils.calculate_virtual_free_capacity(
free_capacity = utils.calculate_virtual_free_capacity(
host_stat['total_capacity_gb'],
host_stat['free_capacity_gb'],
host_stat['provisioned_capacity_gb'],
host_stat['thin_provisioning_support'],
host_stat['max_over_subscription_ratio'],
host_stat['reserved_percentage'])
host_stat['reserved_percentage'],
is_thin_lun)
self.assertEqual(27.01, free)
def test_utils_calculate_virtual_free_capacity_with_thin(self):
host_stat = {'total_capacity_gb': 20.01,
'free_capacity_gb': 18.01,
'provisioned_capacity_gb': 2.0,
'max_over_subscription_ratio': 2.0,
'thin_provisioning_support': True,
'thick_provisioning_support': False,
'reserved_percentage': 5}
free = utils.calculate_virtual_free_capacity(
host_stat['total_capacity_gb'],
host_stat['free_capacity_gb'],
host_stat['provisioned_capacity_gb'],
host_stat['thin_provisioning_support'],
host_stat['max_over_subscription_ratio'],
host_stat['reserved_percentage'])
self.assertEqual(37.02, free)
self.assertEqual(expected, free_capacity)
class Comparable(utils.ComparableMixin):

View File

@ -1009,7 +1009,8 @@ def calculate_virtual_free_capacity(total_capacity,
provisioned_capacity,
thin_provisioning_support,
max_over_subscription_ratio,
reserved_percentage):
reserved_percentage,
thin):
"""Calculate the virtual free capacity based on thin provisioning support.
:param total_capacity: total_capacity_gb of a host_state or pool.
@ -1022,13 +1023,14 @@ def calculate_virtual_free_capacity(total_capacity,
a host_state or a pool
:param reserved_percentage: reserved_percentage of a host_state or
a pool.
:param thin: whether volume to be provisioned is thin
:returns: the calculated virtual free capacity.
"""
total = float(total_capacity)
reserved = float(reserved_percentage) / 100
if thin_provisioning_support:
if thin and thin_provisioning_support:
free = (total * max_over_subscription_ratio
- provisioned_capacity
- math.floor(total * reserved))