Coverage for manila/scheduler/weighers/base.py: 96%

56 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2011-2012 OpenStack Foundation. 

2# All Rights Reserved. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); you may 

5# not use this file except in compliance with the License. You may obtain 

6# a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

13# License for the specific language governing permissions and limitations 

14# under the License. 

15 

16""" 

17Pluggable Weighing support 

18""" 

19 

20import abc 

21 

22from manila.scheduler import base_handler 

23 

24 

25def normalize(weight_list, minval=None, maxval=None): 

26 """Normalize the values in a list between 0 and 1.0. 

27 

28 The normalization is made regarding the lower and upper values present in 

29 weight_list. If the minval and/or maxval parameters are set, these values 

30 will be used instead of the minimum and maximum from the list. 

31 

32 If all the values are equal, they are normalized to 0. 

33 """ 

34 

35 if not weight_list: 

36 return () 

37 

38 if maxval is None: 

39 maxval = max(weight_list) 

40 

41 if minval is None: 

42 minval = min(weight_list) 

43 

44 maxval = float(maxval) 

45 minval = float(minval) 

46 

47 if minval == maxval: 

48 return [0] * len(weight_list) 

49 

50 range_ = maxval - minval 

51 return ((i - minval) / range_ for i in weight_list) 

52 

53 

54class WeighedObject(object): 

55 """Object with weight information.""" 

56 def __init__(self, obj, weight): 

57 self.obj = obj 

58 self.weight = weight 

59 

60 def __repr__(self): 

61 return "<WeighedObject '%s': %s>" % (self.obj, self.weight) 

62 

63 

64class BaseWeigher(metaclass=abc.ABCMeta): 

65 """Base class for pluggable weighers. 

66 

67 The attributes maxval and minval can be specified to set up the maximum 

68 and minimum values for the weighed objects. These values will then be 

69 taken into account in the normalization step, instead of taking the values 

70 from the calculated weighers. 

71 """ 

72 

73 minval = None 

74 maxval = None 

75 

76 def weight_multiplier(self): 

77 """How weighted this weigher should be. 

78 

79 Override this method in a subclass, so that the returned value is 

80 read from a configuration option to permit operators specify a 

81 multiplier for the weigher. 

82 """ 

83 return 1.0 

84 

85 @abc.abstractmethod 

86 def _weigh_object(self, obj, weight_properties): 

87 """Override in a subclass to specify a weight for a specific object.""" 

88 

89 def weigh_objects(self, weighed_obj_list, weight_properties): 

90 """Weigh multiple objects. 

91 

92 Override in a subclass if you need access to all objects in order 

93 to calculate weighers. Do not modify the weight of an object here, 

94 just return a list of weighers. 

95 """ 

96 # Calculate the weighers 

97 weights = [] 

98 for obj in weighed_obj_list: 

99 weight = self._weigh_object(obj.obj, weight_properties) 

100 

101 # Record the min and max values if they are None. If they anything 

102 # but none we assume that the weigher has set them 

103 if self.minval is None: 

104 self.minval = weight 

105 if self.maxval is None: 

106 self.maxval = weight 

107 

108 if weight < self.minval: 

109 self.minval = weight 

110 elif weight > self.maxval: 

111 self.maxval = weight 

112 

113 weights.append(weight) 

114 

115 return weights 

116 

117 

118class BaseWeightHandler(base_handler.BaseHandler): 

119 object_class = WeighedObject 

120 

121 def get_weighed_objects(self, weigher_classes, obj_list, 

122 weighing_properties): 

123 """Return a sorted (descending), normalized list of WeighedObjects.""" 

124 

125 if not obj_list: 125 ↛ 126line 125 didn't jump to line 126 because the condition on line 125 was never true

126 return [] 

127 

128 weighed_objs = [self.object_class(obj, 0.0) for obj in obj_list] 

129 for weigher_cls in weigher_classes: 

130 weigher = weigher_cls() 

131 weights = weigher.weigh_objects(weighed_objs, weighing_properties) 

132 

133 # Normalize the weighers 

134 weights = normalize(weights, 

135 minval=weigher.minval, 

136 maxval=weigher.maxval) 

137 

138 for i, weight in enumerate(weights): 

139 obj = weighed_objs[i] 

140 obj.weight += weigher.weight_multiplier() * weight 

141 

142 return sorted(weighed_objs, key=lambda x: x.weight, reverse=True)