Merge "Differentiate thick and thin provisioning"
This commit is contained in:
commit
90e95c68f8
|
@ -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 "
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue