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

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. 

18 

19 

20import math 

21 

22from oslo_log import log 

23 

24from manila.scheduler.filters import base_host 

25from manila.scheduler import utils 

26 

27LOG = log.getLogger(__name__) 

28 

29 

30class CapacityFilter(base_host.BaseHostFilter): 

31 """CapacityFilter filters based on share host's capacity utilization.""" 

32 

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) 

38 

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 

44 

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 

53 

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) 

78 

79 msg_args = {"host": host_state.host, 

80 "requested": share_size, 

81 "available": free} 

82 

83 LOG.debug("Space information for share creation " 

84 "on host %(host)s (requested / avail): " 

85 "%(requested)s/%(available)s", msg_args) 

86 

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) 

91 

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 

126 

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 

132 

133 return True