# # 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. """ Stochastic weight handler This weight handler differs from the default weight handler by giving every pool a chance to be chosen where the probability is proportional to each pools' weight. """ import random from cinder.scheduler import base_weight from cinder.scheduler import weights as wts class StochasticHostWeightHandler(base_weight.BaseWeightHandler): def __init__(self, namespace): super(StochasticHostWeightHandler, self).__init__(wts.BaseHostWeigher, namespace) def get_weighed_objects(self, weigher_classes, obj_list, weighing_properties): # The normalization performed in the superclass is nonlinear, which # messes up the probabilities, so override it. The probabilistic # approach we use here is self-normalizing. # Also, the sorting done by the parent implementation is harmless but # useless for us. # Compute the object weights as the parent would but without sorting # or normalization. weighed_objs = [wts.WeighedHost(obj, 0.0) for obj in obj_list] for weigher_cls in weigher_classes: weigher = weigher_cls() weights = weigher.weigh_objects(weighed_objs, weighing_properties) for i, weight in enumerate(weights): obj = weighed_objs[i] obj.weight += weigher.weight_multiplier() * weight # Avoid processing empty lists if not weighed_objs: return [] # First compute the total weight of all the objects and the upper # bound for each object to "win" the lottery. total_weight = 0 table = [] for weighed_obj in weighed_objs: total_weight += weighed_obj.weight max_value = total_weight table.append((max_value, weighed_obj)) # Now draw a random value with the computed range winning_value = random.random() * total_weight # Scan the table to find the first object with a maximum higher than # the random number. Save the index of the winner. winning_index = 0 for (i, (max_value, weighed_obj)) in enumerate(table): if max_value > winning_value: # Return a single element array with the winner. winning_index = i break # It's theoretically possible for the above loop to terminate with no # winner. This happens when winning_value >= total_weight, which # could only occur with very large numbers and floating point # rounding. In those cases the actual winner should have been the # last element, so return it. return weighed_objs[winning_index:] + weighed_objs[0:winning_index]