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
« 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.
16"""
17Pluggable Weighing support
18"""
20import abc
22from manila.scheduler import base_handler
25def normalize(weight_list, minval=None, maxval=None):
26 """Normalize the values in a list between 0 and 1.0.
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.
32 If all the values are equal, they are normalized to 0.
33 """
35 if not weight_list:
36 return ()
38 if maxval is None:
39 maxval = max(weight_list)
41 if minval is None:
42 minval = min(weight_list)
44 maxval = float(maxval)
45 minval = float(minval)
47 if minval == maxval:
48 return [0] * len(weight_list)
50 range_ = maxval - minval
51 return ((i - minval) / range_ for i in weight_list)
54class WeighedObject(object):
55 """Object with weight information."""
56 def __init__(self, obj, weight):
57 self.obj = obj
58 self.weight = weight
60 def __repr__(self):
61 return "<WeighedObject '%s': %s>" % (self.obj, self.weight)
64class BaseWeigher(metaclass=abc.ABCMeta):
65 """Base class for pluggable weighers.
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 """
73 minval = None
74 maxval = None
76 def weight_multiplier(self):
77 """How weighted this weigher should be.
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
85 @abc.abstractmethod
86 def _weigh_object(self, obj, weight_properties):
87 """Override in a subclass to specify a weight for a specific object."""
89 def weigh_objects(self, weighed_obj_list, weight_properties):
90 """Weigh multiple objects.
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)
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
108 if weight < self.minval:
109 self.minval = weight
110 elif weight > self.maxval:
111 self.maxval = weight
113 weights.append(weight)
115 return weights
118class BaseWeightHandler(base_handler.BaseHandler):
119 object_class = WeighedObject
121 def get_weighed_objects(self, weigher_classes, obj_list,
122 weighing_properties):
123 """Return a sorted (descending), normalized list of WeighedObjects."""
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 []
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)
133 # Normalize the weighers
134 weights = normalize(weights,
135 minval=weigher.minval,
136 maxval=weigher.maxval)
138 for i, weight in enumerate(weights):
139 obj = weighed_objs[i]
140 obj.weight += weigher.weight_multiplier() * weight
142 return sorted(weighed_objs, key=lambda x: x.weight, reverse=True)