From 83c93c776707d5da9c89cde81c03022178127261 Mon Sep 17 00:00:00 2001 From: xing-yang Date: Wed, 11 May 2016 15:23:31 -0400 Subject: [PATCH] Check 'thin_provisioning' in extra specs Currently in the capacity filter and weigher of the scheduler, we use the logic to evaluate whether there is enough capacity to thin provision a share on a backend if the driver reports thin_provisioning to be True. However, a driver may be able to support both thin and thick provisioning. The logic does not check whether the user wants the share to be provisioned as thin or not. In this patch, we check 'thin_provisioning' in extra specs of the share type and decide whether to use the logic for thin or thick. In the following two cases, we will use the thin logic: 1) 'thin_provisioning' is not set in extra specs. This is to provide backward compatibility. 2) 'thin_provisioning' is set in extra specs and it is ' True' or 'True'. To provision a thick share on a backend that supports both thin and thick, set one of the following in extra specs: {'thin_provisioning': 'False'} {'thin_provisioning': ' False'} {'capabilities:thin_provisioning': 'False'} {'capabilities:thin_provisioning': ' False'} DocImpact Change-Id: I238a7962425ea35c356c5ed2e31b8f68462b3769 Closes-Bug: #1578718 --- manila/scheduler/filters/capacity.py | 15 +- manila/scheduler/utils.py | 50 ++++++ manila/scheduler/weighers/capacity.py | 10 +- manila/tests/scheduler/fakes.py | 6 +- .../tests/scheduler/filters/test_capacity.py | 133 ++++++++++++--- manila/tests/scheduler/test_utils.py | 64 ++++++++ .../tests/scheduler/weighers/test_capacity.py | 151 +++++++++++++++--- ...ck-thin-provisioning-4bb702535f6b10b6.yaml | 7 + 8 files changed, 388 insertions(+), 48 deletions(-) create mode 100644 manila/tests/scheduler/test_utils.py create mode 100644 releasenotes/notes/check-thin-provisioning-4bb702535f6b10b6.yaml diff --git a/manila/scheduler/filters/capacity.py b/manila/scheduler/filters/capacity.py index 4f694557d5..119c563f61 100644 --- a/manila/scheduler/filters/capacity.py +++ b/manila/scheduler/filters/capacity.py @@ -24,6 +24,7 @@ from oslo_log import log from manila.i18n import _LE from manila.i18n import _LW from manila.scheduler.filters import base_host +from manila.scheduler import utils LOG = log.getLogger(__name__) @@ -77,14 +78,19 @@ class CapacityFilter(base_host.BaseHostFilter): "on host %(host)s (requested / avail): " "%(requested)s/%(available)s", msg_args) + share_type = filter_properties.get('share_type', {}) + use_thin_logic = utils.use_thin_logic(share_type) + thin_provisioning = utils.thin_provisioning( + host_state.thin_provisioning) + # NOTE(xyang): Only evaluate using max_over_subscription_ratio - # if thin_provisioning is True. Check if the ratio of - # provisioned capacity over total capacity would exceed + # if use_thin_logic and thin_provisioning are True. Check if the + # ratio of provisioned capacity over total capacity would exceed # subscription ratio. # If max_over_subscription_ratio = 1, the provisioned_ratio # should still be limited by the max_over_subscription_ratio; # otherwise, it could result in infinite provisioning. - if (host_state.thin_provisioning and + if (use_thin_logic and thin_provisioning and host_state.max_over_subscription_ratio >= 1): provisioned_ratio = ((host_state.provisioned_capacity_gb + share_size) / total) @@ -105,7 +111,8 @@ class CapacityFilter(base_host.BaseHostFilter): adjusted_free_virtual = ( free * host_state.max_over_subscription_ratio) return adjusted_free_virtual >= share_size - elif host_state.thin_provisioning: + elif (use_thin_logic and thin_provisioning and + host_state.max_over_subscription_ratio < 1): LOG.error(_LE("Invalid max_over_subscription_ratio: %(ratio)s. " "Valid value should be >= 1."), {"ratio": host_state.max_over_subscription_ratio}) diff --git a/manila/scheduler/utils.py b/manila/scheduler/utils.py index 2a24fcc1dd..87309f10bb 100644 --- a/manila/scheduler/utils.py +++ b/manila/scheduler/utils.py @@ -1,4 +1,6 @@ # Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2016 EMC Corporation +# # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -13,6 +15,10 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils import strutils + +from manila.scheduler.filters import extra_specs_ops + def generate_stats(host_state, properties): """Generates statistics from host and share data.""" @@ -61,3 +67,47 @@ def generate_stats(host_state, properties): } return stats + + +def use_thin_logic(share_type): + # NOTE(xyang): To preserve the existing behavior, we use thin logic + # to evaluate in two cases: + # 1) 'thin_provisioning' is not set in extra specs (This is for + # backward compatibility. If not set, the scheduler behaves + # the same as before this bug fix). + # 2) 'thin_provisioning' is set in extra specs and it is + # ' True' or 'True'. + # Otherwise we use the thick logic to evaluate. + use_thin_logic = True + thin_spec = None + try: + thin_spec = share_type.get('extra_specs', {}).get( + 'thin_provisioning') + if thin_spec is None: + thin_spec = share_type.get('extra_specs', {}).get( + 'capabilities:thin_provisioning') + # NOTE(xyang) 'use_thin_logic' and 'thin_provisioning' are NOT + # the same thing. The first purpose of "use_thin_logic" is to + # preserve the existing scheduler behavior if 'thin_provisioning' + # is NOT in extra_specs (if thin_spec is None, use_thin_logic + # should be True). The second purpose of 'use_thin_logic' + # is to honor 'thin_provisioning' if it is in extra specs (if + # thin_spec is set to True, use_thin_logic should be True; if + # thin_spec is set to False, use_thin_logic should be False). + use_thin_logic = strutils.bool_from_string( + thin_spec, strict=True) if thin_spec is not None else True + except ValueError: + # Check if the value of thin_spec is ' True'. + if thin_spec is not None and not extra_specs_ops.match( + True, thin_spec): + use_thin_logic = False + return use_thin_logic + + +def thin_provisioning(host_state_thin_provisioning): + # NOTE(xyang): host_state_thin_provisioning is reported by driver. + # It can be either bool (True or False) or + # list ([True, False], [True], [False]). + thin_capability = [host_state_thin_provisioning] if not isinstance( + host_state_thin_provisioning, list) else host_state_thin_provisioning + return True in thin_capability diff --git a/manila/scheduler/weighers/capacity.py b/manila/scheduler/weighers/capacity.py index 6943e10156..847a6f8608 100644 --- a/manila/scheduler/weighers/capacity.py +++ b/manila/scheduler/weighers/capacity.py @@ -31,7 +31,7 @@ import math from oslo_config import cfg - +from manila.scheduler import utils from manila.scheduler.weighers import base_host capacity_weight_opts = [ @@ -63,7 +63,13 @@ class CapacityWeigher(base_host.BaseHostWeigher): free = float('inf') else: total = float(total_space) - if host_state.thin_provisioning: + + share_type = weight_properties.get('share_type', {}) + use_thin_logic = utils.use_thin_logic(share_type) + thin_provisioning = utils.thin_provisioning( + host_state.thin_provisioning) + + if use_thin_logic and thin_provisioning: # NOTE(xyang): Calculate virtual free capacity for thin # provisioning. free = math.floor( diff --git a/manila/tests/scheduler/fakes.py b/manila/tests/scheduler/fakes.py index 12c339aa24..f4431c44f9 100644 --- a/manila/tests/scheduler/fakes.py +++ b/manila/tests/scheduler/fakes.py @@ -212,7 +212,7 @@ class FakeHostManager(host_manager.HostManager): 'allocated_capacity_gb': 256, 'provisioned_capacity_gb': 256, 'max_over_subscription_ratio': 2.0, - 'thin_provisioning': False, + 'thin_provisioning': [False], 'consistency_group_support': 'host', 'reserved_percentage': 0, 'snapshot_support': True, @@ -223,7 +223,7 @@ class FakeHostManager(host_manager.HostManager): 'allocated_capacity_gb': 1848, 'provisioned_capacity_gb': 1848, 'max_over_subscription_ratio': 1.0, - 'thin_provisioning': True, + 'thin_provisioning': [True], 'reserved_percentage': 5, 'timestamp': None, 'snapshot_support': True, @@ -235,7 +235,7 @@ class FakeHostManager(host_manager.HostManager): 'allocated_capacity_gb': 1548, 'provisioned_capacity_gb': 1548, 'max_over_subscription_ratio': 1.5, - 'thin_provisioning': True, + 'thin_provisioning': [True, False], 'reserved_percentage': 5, 'timestamp': None, 'snapshot_support': True, diff --git a/manila/tests/scheduler/filters/test_capacity.py b/manila/tests/scheduler/filters/test_capacity.py index a88df8c67c..4532ad2d96 100644 --- a/manila/tests/scheduler/filters/test_capacity.py +++ b/manila/tests/scheduler/filters/test_capacity.py @@ -121,31 +121,76 @@ class HostFiltersTestCase(test.TestCase): @ddt.data( {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 200, 'provisioned': 500, - 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'capabilities:thin_provisioning'}, {'size': 3000, 'cap_thin': ' True', 'total': 500, 'free': 200, 'provisioned': 7000, - 'max_ratio': 20, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 20, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'thin_provisioning'}, {'size': 100, 'cap_thin': ' False', 'total': 500, 'free': 200, 'provisioned': 300, - 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': False}, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': False, + 'cap_thin_key': 'capabilities:thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 200, 'provisioned': 400, - 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 125, 'provisioned': 400, - 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'capabilities:thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 80, 'provisioned': 600, - 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 100, 'provisioned': 400, - 'max_ratio': 2.0, 'reserved': 0, 'thin_prov': True}) + 'max_ratio': 2.0, 'reserved': 0, 'thin_prov': True, + 'cap_thin_key': 'capabilities:thin_provisioning'}, + {'size': 100, 'cap_thin': ' True', + 'total': 500, 'free': 200, 'provisioned': 500, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': [True, False], + 'cap_thin_key': 'thin_provisioning'}, + {'size': 3000, 'cap_thin': ' True', + 'total': 500, 'free': 200, 'provisioned': 7000, + 'max_ratio': 20, 'reserved': 5, 'thin_prov': [True], + 'cap_thin_key': 'capabilities:thin_provisioning'}, + {'size': 100, 'cap_thin': ' False', + 'total': 500, 'free': 200, 'provisioned': 300, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': [False], + 'cap_thin_key': 'thin_provisioning'}, + {'size': 100, 'cap_thin': 'True', + 'total': 500, 'free': 200, 'provisioned': 400, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': [False, True], + 'cap_thin_key': 'capabilities:thin_provisioning'}, + {'size': 100, 'cap_thin': 'False', + 'total': 500, 'free': 200, 'provisioned': 300, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': False, + 'cap_thin_key': 'thin_provisioning'}, + {'size': 100, 'cap_thin': 'true', + 'total': 500, 'free': 125, 'provisioned': 400, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': [True, ], + 'cap_thin_key': 'capabilities:thin_provisioning'}, + {'size': 100, 'cap_thin': 'false', + 'total': 500, 'free': 200, 'provisioned': 300, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': [False, ], + 'cap_thin_key': 'thin_provisioning'}, + {'size': 100, 'cap_thin': None, + 'total': 500, 'free': 80, 'provisioned': 600, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': None},) @ddt.unpack def test_filter_thin_passes(self, size, cap_thin, total, free, provisioned, - max_ratio, reserved, thin_prov): + max_ratio, reserved, thin_prov, cap_thin_key): self._stub_service_is_up(True) - filter_properties = {'size': size, - 'capabilities:thin_provisioning': cap_thin} + filter_properties = { + 'size': size, + 'share_type': { + 'extra_specs': { + cap_thin_key: cap_thin, + } + } + } service = {'disabled': False} host = fakes.FakeHostState('host1', {'total_capacity_gb': total, @@ -161,34 +206,80 @@ class HostFiltersTestCase(test.TestCase): @ddt.data( {'size': 200, 'cap_thin': ' True', 'total': 500, 'free': 100, 'provisioned': 400, - 'max_ratio': 0.8, 'reserved': 0, 'thin_prov': True}, + 'max_ratio': 0.8, 'reserved': 0, 'thin_prov': True, + 'cap_thin_key': 'capabilities:thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 200, 'provisioned': 700, - 'max_ratio': 1.5, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 1.5, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'thin_provisioning'}, {'size': 2000, 'cap_thin': ' True', 'total': 500, 'free': 30, 'provisioned': 9000, - 'max_ratio': 20.0, 'reserved': 0, 'thin_prov': True}, + 'max_ratio': 20.0, 'reserved': 0, 'thin_prov': True, + 'cap_thin_key': 'capabilities:thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 100, 'provisioned': 1000, - 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'thin_provisioning'}, {'size': 100, 'cap_thin': ' False', 'total': 500, 'free': 100, 'provisioned': 400, - 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': False}, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': False, + 'cap_thin_key': 'capabilities:thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 0, 'provisioned': 800, - 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'thin_provisioning'}, {'size': 100, 'cap_thin': ' True', 'total': 500, 'free': 99, 'provisioned': 1000, - 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True}, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'capabilities:thin_provisioning'}, {'size': 400, 'cap_thin': ' True', 'total': 500, 'free': 200, 'provisioned': 600, - 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True}) + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': True, + 'cap_thin_key': 'thin_provisioning'}, + {'size': 200, 'cap_thin': ' True', + 'total': 500, 'free': 100, 'provisioned': 400, + 'max_ratio': 0.8, 'reserved': 0, 'thin_prov': [False, True], + 'cap_thin_key': 'capabilities:thin_provisioning'}, + {'size': 2000, 'cap_thin': ' True', + 'total': 500, 'free': 30, 'provisioned': 9000, + 'max_ratio': 20.0, 'reserved': 0, 'thin_prov': [True], + 'cap_thin_key': 'thin_provisioning'}, + {'size': 100, 'cap_thin': ' False', + 'total': 500, 'free': 100, 'provisioned': 400, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': [False], + 'cap_thin_key': 'thin_provisioning'}, + {'size': 100, 'cap_thin': 'False', + 'total': 500, 'free': 100, 'provisioned': 400, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': False, + 'cap_thin_key': 'capabilities:thin_provisioning'}, + {'size': 100, 'cap_thin': 'True', + 'total': 500, 'free': 0, 'provisioned': 800, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': [False, True], + 'cap_thin_key': 'thin_provisioning'}, + {'size': 100, 'cap_thin': 'true', + 'total': 500, 'free': 99, 'provisioned': 1000, + 'max_ratio': 2.0, 'reserved': 5, 'thin_prov': [True, ], + 'cap_thin_key': 'capabilities:thin_provisioning'}, + {'size': 100, 'cap_thin': 'false', + 'total': 500, 'free': 100, 'provisioned': 400, + 'max_ratio': 1.0, 'reserved': 5, 'thin_prov': [False, ], + 'cap_thin_key': 'thin_provisioning'}, + {'size': 2000, 'cap_thin': None, + 'total': 500, 'free': 30, 'provisioned': 9000, + 'max_ratio': 20.0, 'reserved': 0, 'thin_prov': [True], + 'cap_thin_key': None},) @ddt.unpack def test_filter_thin_fails(self, size, cap_thin, total, free, provisioned, - max_ratio, reserved, thin_prov): + max_ratio, reserved, thin_prov, cap_thin_key): self._stub_service_is_up(True) - filter_properties = {'size': size, - 'capabilities:thin_provisioning': cap_thin} + filter_properties = { + 'size': size, + 'share_type': { + 'extra_specs': { + cap_thin_key: cap_thin, + } + } + } service = {'disabled': False} host = fakes.FakeHostState('host1', {'total_capacity_gb': total, diff --git a/manila/tests/scheduler/test_utils.py b/manila/tests/scheduler/test_utils.py new file mode 100644 index 0000000000..4dcf451059 --- /dev/null +++ b/manila/tests/scheduler/test_utils.py @@ -0,0 +1,64 @@ +# Copyright 2016 EMC Corporation OpenStack Foundation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Tests For utils. +""" + +import ddt + +from manila.scheduler import utils +from manila import test + + +@ddt.ddt +class UtilsTestCase(test.TestCase): + """Test case for utils.""" + + def setUp(self): + super(UtilsTestCase, self).setUp() + + @ddt.data( + ({'extra_specs': {'thin_provisioning': True}}, True), + ({'extra_specs': {'thin_provisioning': False}}, False), + ({'extra_specs': {'foo': 'bar'}}, True), + ({'foo': 'bar'}, True), + ({'extra_specs': {'thin_provisioning': ' True'}}, + True), + ({'extra_specs': {'thin_provisioning': ' False'}}, + False), + ({'extra_specs': {'thin_provisioning': ' True'}}, + False), + ({'extra_specs': {}}, True), + ({}, True), + ) + @ddt.unpack + def test_use_thin_logic(self, properties, use_thin): + use_thin_logic = utils.use_thin_logic(properties) + self.assertEqual(use_thin, use_thin_logic) + + @ddt.data( + (True, True), + (False, False), + (None, False), + ([True, False], True), + ([True], True), + ([False], False), + ('wrong', False), + ) + @ddt.unpack + def test_thin_provisioning(self, thin_capabilities, thin): + thin_provisioning = utils.thin_provisioning(thin_capabilities) + self.assertEqual(thin, thin_provisioning) diff --git a/manila/tests/scheduler/weighers/test_capacity.py b/manila/tests/scheduler/weighers/test_capacity.py index e01bade9b0..1d4f0813f0 100644 --- a/manila/tests/scheduler/weighers/test_capacity.py +++ b/manila/tests/scheduler/weighers/test_capacity.py @@ -16,6 +16,7 @@ Tests For Capacity Weigher. """ +import ddt import mock from oslo_config import cfg @@ -29,6 +30,7 @@ from manila.tests.scheduler import fakes CONF = cfg.CONF +@ddt.ddt class CapacityWeigherTestCase(test.TestCase): def setUp(self): super(CapacityWeigherTestCase, self).setUp() @@ -61,9 +63,37 @@ class CapacityWeigherTestCase(test.TestCase): # - total * reserved) # Otherwise, use the following formula: # free = math.floor(free_space - total * reserved) - def test_default_of_spreading_first(self): + + @ddt.data( + {'cap_thin': ' True', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': ' False', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host1'}, + {'cap_thin': 'True', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': 'False', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host1'}, + {'cap_thin': 'true', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': 'false', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host1'}, + {'cap_thin': None, + 'cap_thin_key': None, + 'winner': 'host2'}, + ) + @ddt.unpack + def test_default_of_spreading_first(self, cap_thin, cap_thin_key, + winner): hostinfo_list = self._get_all_hosts() + # Results for the 1st test + # {'capabilities:thin_provisioning': ' True'}: # host1: thin_provisioning = False # free_capacity_gb = 1024 # free = math.floor(1024 - 1024 * 0.1) = 921.0 @@ -73,16 +103,16 @@ class CapacityWeigherTestCase(test.TestCase): # free_capacity_gb = 300 # free = math.floor(2048 * 2.0 - 1748 - 2048 * 0.1)=2143.0 # weight = 1.0 - # host3: thin_provisioning = False + # host3: thin_provisioning = [False] # free_capacity_gb = 512 # free = math.floor(256 - 512 * 0)=256.0 # weight = 0.08 - # host4: thin_provisioning = True + # host4: thin_provisioning = [True] # max_over_subscription_ratio = 1.0 # free_capacity_gb = 200 # free = math.floor(2048 * 1.0 - 1848 - 2048 * 0.05) = 97.0 # weight = 0.0 - # host5: thin_provisioning = True + # host5: thin_provisioning = [True, False] # max_over_subscription_ratio = 1.5 # free_capacity_gb = 500 # free = math.floor(2048 * 1.5 - 1548 - 2048 * 0.05) = 1421.0 @@ -92,10 +122,20 @@ class CapacityWeigherTestCase(test.TestCase): # weight = 0.0 # so, host2 should win: - weighed_host = self._get_weighed_host(hostinfo_list) + weight_properties = { + 'size': 1, + 'share_type': { + 'extra_specs': { + cap_thin_key: cap_thin, + } + } + } + weighed_host = self._get_weighed_host( + hostinfo_list, + weight_properties=weight_properties) self.assertEqual(1.0, weighed_host.weight) self.assertEqual( - 'host2', utils.extract_host(weighed_host.obj.host)) + winner, utils.extract_host(weighed_host.obj.host)) def test_unknown_is_last(self): hostinfo_list = self._get_all_hosts() @@ -105,10 +145,38 @@ class CapacityWeigherTestCase(test.TestCase): 'host6', utils.extract_host(last_host.obj.host)) self.assertEqual(0.0, last_host.weight) - def test_capacity_weight_multiplier_negative_1(self): + @ddt.data( + {'cap_thin': ' True', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host4'}, + {'cap_thin': ' False', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': 'True', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host4'}, + {'cap_thin': 'False', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': 'true', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host4'}, + {'cap_thin': 'false', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': None, + 'cap_thin_key': None, + 'winner': 'host4'}, + ) + @ddt.unpack + def test_capacity_weight_multiplier_negative_1(self, cap_thin, + cap_thin_key, + winner): self.flags(capacity_weight_multiplier=-1.0) hostinfo_list = self._get_all_hosts() + # Results for the 1st test + # {'capabilities:thin_provisioning': ' True'}: # host1: thin_provisioning = False # free_capacity_gb = 1024 # free = math.floor(1024 - 1024 * 0.1) = 921.0 @@ -120,18 +188,18 @@ class CapacityWeigherTestCase(test.TestCase): # free = math.floor(2048 * 2.0-1748-2048 * 0.1) = 2143.0 # free * (-1) = -2143.0 # weight = -1.0 - # host3: thin_provisioning = False + # host3: thin_provisioning = [False] # free_capacity_gb = 512 # free = math.floor(256 - 512 * 0) = 256.0 # free * (-1) = -256.0 # weight = -0.08 - # host4: thin_provisioning = True + # host4: thin_provisioning = [True] # max_over_subscription_ratio = 1.0 # free_capacity_gb = 200 # free = math.floor(2048 * 1.0 - 1848 - 2048 * 0.05) = 97.0 # free * (-1) = -97.0 # weight = 0.0 - # host5: thin_provisioning = True + # host5: thin_provisioning = [True, False] # max_over_subscription_ratio = 1.5 # free_capacity_gb = 500 # free = math.floor(2048 * 1.5 - 1548 - 2048 * 0.05) = 1421.0 @@ -143,15 +211,52 @@ class CapacityWeigherTestCase(test.TestCase): # weight = 0.0 # so, host4 should win: - weighed_host = self._get_weighed_host(hostinfo_list) + weight_properties = { + 'size': 1, + 'share_type': { + 'extra_specs': { + cap_thin_key: cap_thin, + } + } + } + weighed_host = self._get_weighed_host( + hostinfo_list, + weight_properties=weight_properties) self.assertEqual(0.0, weighed_host.weight) self.assertEqual( - 'host4', utils.extract_host(weighed_host.obj.host)) + winner, utils.extract_host(weighed_host.obj.host)) - def test_capacity_weight_multiplier_2(self): + @ddt.data( + {'cap_thin': ' True', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': ' False', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host1'}, + {'cap_thin': 'True', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': 'False', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host1'}, + {'cap_thin': 'true', + 'cap_thin_key': 'capabilities:thin_provisioning', + 'winner': 'host2'}, + {'cap_thin': 'false', + 'cap_thin_key': 'thin_provisioning', + 'winner': 'host1'}, + {'cap_thin': None, + 'cap_thin_key': None, + 'winner': 'host2'}, + ) + @ddt.unpack + def test_capacity_weight_multiplier_2(self, cap_thin, cap_thin_key, + winner): self.flags(capacity_weight_multiplier=2.0) hostinfo_list = self._get_all_hosts() + # Results for the 1st test + # {'capabilities:thin_provisioning': ' True'}: # host1: thin_provisioning = False # free_capacity_gb = 1024 # free = math.floor(1024-1024*0.1) = 921.0 @@ -163,18 +268,18 @@ class CapacityWeigherTestCase(test.TestCase): # free = math.floor(2048 * 2.0 - 1748 - 2048 * 0.1) = 2143.0 # free * 2 = 4286.0 # weight = 2.0 - # host3: thin_provisioning = False + # host3: thin_provisioning = [False] # free_capacity_gb = 512 # free = math.floor(256 - 512 * 0) = 256.0 # free * 2 = 512.0 # weight = 0.16 - # host4: thin_provisioning = True + # host4: thin_provisioning = [True] # max_over_subscription_ratio = 1.0 # free_capacity_gb = 200 # free = math.floor(2048 * 1.0 - 1848 - 2048 * 0.05) = 97.0 # free * 2 = 194.0 # weight = 0.0 - # host5: thin_provisioning = True + # host5: thin_provisioning = [True, False] # max_over_subscription_ratio = 1.5 # free_capacity_gb = 500 # free = math.floor(2048 * 1.5 - 1548 - 2048 * 0.05) = 1421.0 @@ -185,7 +290,17 @@ class CapacityWeigherTestCase(test.TestCase): # weight = 0.0 # so, host2 should win: - weighed_host = self._get_weighed_host(hostinfo_list) + weight_properties = { + 'size': 1, + 'share_type': { + 'extra_specs': { + cap_thin_key: cap_thin, + } + } + } + weighed_host = self._get_weighed_host( + hostinfo_list, + weight_properties=weight_properties) self.assertEqual(2.0, weighed_host.weight) self.assertEqual( - 'host2', utils.extract_host(weighed_host.obj.host)) + winner, utils.extract_host(weighed_host.obj.host)) diff --git a/releasenotes/notes/check-thin-provisioning-4bb702535f6b10b6.yaml b/releasenotes/notes/check-thin-provisioning-4bb702535f6b10b6.yaml new file mode 100644 index 0000000000..8108fe51d5 --- /dev/null +++ b/releasenotes/notes/check-thin-provisioning-4bb702535f6b10b6.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - Capacity filter and weigher scheduler logic was modified to + account for back ends that can support thin and thick provisioning + for shares. Over subscription calculation is triggered with the + presence of the ``thin_provisioning`` extra-spec in the share type + of the share being created.