Coverage for manila/scheduler/filters/capacity.py: 100%
47 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright (c) 2012 Intel
2# Copyright (c) 2012 OpenStack, LLC.
3# Copyright (c) 2015 EMC Corporation
4#
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
20import math
22from oslo_log import log
24from manila.scheduler.filters import base_host
25from manila.scheduler import utils
27LOG = log.getLogger(__name__)
30class CapacityFilter(base_host.BaseHostFilter):
31 """CapacityFilter filters based on share host's capacity utilization."""
33 def host_passes(self, host_state, filter_properties):
34 """Return True if host has sufficient capacity."""
35 size_increase = filter_properties.get('size_increase')
36 share_size = size_increase if size_increase else filter_properties.get(
37 'size', 0)
39 if host_state.free_capacity_gb is None:
40 # Fail Safe
41 LOG.error("Free capacity not set: "
42 "share node info collection broken.")
43 return False
45 free_space = host_state.free_capacity_gb
46 total_space = host_state.total_capacity_gb
47 if filter_properties.get('snapshot_id'):
48 reserved = float(host_state.reserved_snapshot_percentage) / 100
49 elif filter_properties.get('is_share_extend'):
50 reserved = float(host_state.reserved_share_extend_percentage) / 100
51 else:
52 reserved = float(host_state.reserved_percentage) / 100
54 if free_space == 'unknown':
55 # NOTE(zhiteng) for those back-ends cannot report actual
56 # available capacity, we assume it is able to serve the
57 # request. Even if it was not, the retry mechanism is
58 # able to handle the failure by rescheduling
59 return True
60 elif total_space == 'unknown':
61 # NOTE(xyang): If total_space is 'unknown' and
62 # reserved is 0, we assume the back-ends can serve the request.
63 # If total_space is 'unknown' and reserved
64 # is not 0, we cannot calculate the reserved space.
65 # float(total_space) will throw an exception. total*reserved
66 # also won't work. So the back-ends cannot serve the request.
67 return reserved == 0 and share_size <= free_space
68 total = float(total_space)
69 if total <= 0:
70 LOG.warning("Insufficient free space for share creation. "
71 "Total capacity is %(total).2f on host %(host)s.",
72 {"total": total,
73 "host": host_state.host})
74 return False
75 # NOTE(xyang): Calculate how much free space is left after taking
76 # into account the reserved space.
77 free = math.floor(free_space - total * reserved)
79 msg_args = {"host": host_state.host,
80 "requested": share_size,
81 "available": free}
83 LOG.debug("Space information for share creation "
84 "on host %(host)s (requested / avail): "
85 "%(requested)s/%(available)s", msg_args)
87 share_type = filter_properties.get('share_type', {})
88 use_thin_logic = utils.use_thin_logic(share_type)
89 thin_provisioning = utils.thin_provisioning(
90 host_state.thin_provisioning)
92 # NOTE(xyang): Only evaluate using max_over_subscription_ratio
93 # if use_thin_logic and thin_provisioning are True. Check if the
94 # ratio of provisioned capacity over total capacity would exceed
95 # subscription ratio.
96 # If max_over_subscription_ratio = 1, the provisioned_ratio
97 # should still be limited by the max_over_subscription_ratio;
98 # otherwise, it could result in infinite provisioning.
99 if (use_thin_logic and thin_provisioning and
100 host_state.max_over_subscription_ratio >= 1):
101 provisioned_ratio = ((host_state.provisioned_capacity_gb +
102 share_size) / total)
103 if provisioned_ratio > host_state.max_over_subscription_ratio:
104 LOG.warning(
105 "Insufficient free space for thin provisioning. "
106 "The ratio of provisioned capacity over total capacity "
107 "%(provisioned_ratio).2f would exceed the maximum over "
108 "subscription ratio %(oversub_ratio).2f on host "
109 "%(host)s.",
110 {"provisioned_ratio": provisioned_ratio,
111 "oversub_ratio": host_state.max_over_subscription_ratio,
112 "host": host_state.host})
113 return False
114 else:
115 # NOTE(xyang): Adjust free_virtual calculation based on
116 # free and max_over_subscription_ratio.
117 adjusted_free_virtual = (
118 free * host_state.max_over_subscription_ratio)
119 return adjusted_free_virtual >= share_size
120 elif (use_thin_logic and thin_provisioning and
121 host_state.max_over_subscription_ratio < 1):
122 LOG.error("Invalid max_over_subscription_ratio: %(ratio)s. "
123 "Valid value should be >= 1.",
124 {"ratio": host_state.max_over_subscription_ratio})
125 return False
127 if free < share_size:
128 LOG.warning("Insufficient free space for share creation "
129 "on host %(host)s (requested / avail): "
130 "%(requested)s/%(available)s", msg_args)
131 return False
133 return True