Coverage for manila/scheduler/weighers/capacity.py: 94%
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 OpenStack, LLC.
2# Copyright (c) 2015 EMC Corporation
3#
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17"""
18Capacity Weigher. Weigh hosts by their virtual or actual free capacity.
20For thin provisioning, weigh hosts by their virtual free capacity calculated
21by the total capacity multiplied by the max over subscription ratio and
22subtracting the provisioned capacity; Otherwise, weigh hosts by their actual
23free capacity, taking into account the reserved space.
25The default is to spread shares across all hosts evenly. If you prefer
26stacking, you can set the 'capacity_weight_multiplier' option to a negative
27number and the weighing has the opposite effect of the default.
28"""
30import math
32from oslo_config import cfg
34from manila.scheduler import utils
35from manila.scheduler.weighers import base_host
37capacity_weight_opts = [
38 cfg.FloatOpt('capacity_weight_multiplier',
39 default=1.0,
40 help='Multiplier used for weighing share capacity. '
41 'Negative numbers mean to stack vs spread.'),
42]
44CONF = cfg.CONF
45CONF.register_opts(capacity_weight_opts)
48class CapacityWeigher(base_host.BaseHostWeigher):
49 def weight_multiplier(self):
50 """Override the weight multiplier."""
51 return CONF.capacity_weight_multiplier
53 def _weigh_object(self, host_state, weight_properties):
54 """Higher weighers win. We want spreading to be the default."""
55 if weight_properties.get('snapshot_id'): 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true
56 reserved = float(host_state.reserved_snapshot_percentage) / 100
57 elif weight_properties.get('is_share_extend'): 57 ↛ 58line 57 didn't jump to line 58 because the condition on line 57 was never true
58 reserved = float(host_state.reserved_share_extend_percentage) / 100
59 else:
60 reserved = float(host_state.reserved_percentage) / 100
62 free_space = host_state.free_capacity_gb
63 total_space = host_state.total_capacity_gb
64 if 'unknown' in (total_space, free_space):
65 # NOTE(u_glide): "unknown" capacity always sorts to the bottom
66 if CONF.capacity_weight_multiplier > 0:
67 free = float('-inf')
68 else:
69 free = float('inf')
70 else:
71 total = float(total_space)
73 share_type = weight_properties.get('share_type', {})
74 use_thin_logic = utils.use_thin_logic(share_type)
75 thin_provisioning = utils.thin_provisioning(
76 host_state.thin_provisioning)
78 if use_thin_logic and thin_provisioning:
79 # NOTE(xyang): Calculate virtual free capacity for thin
80 # provisioning.
81 free = math.floor(
82 total * host_state.max_over_subscription_ratio -
83 host_state.provisioned_capacity_gb -
84 total * reserved)
85 else:
86 # NOTE(xyang): Calculate how much free space is left after
87 # taking into account the reserved space.
88 free = math.floor(free_space - total * reserved)
89 return free
91 def weigh_objects(self, weighed_obj_list, weight_properties):
92 weights = super(CapacityWeigher, self).weigh_objects(weighed_obj_list,
93 weight_properties)
94 # NOTE(u_glide): Replace -inf with (minimum - 1) and
95 # inf with (maximum + 1) to avoid errors in
96 # manila.scheduler.weighers.base.normalize() method
97 if self.minval == float('-inf'):
98 self.minval = self.maxval
99 for val in weights:
100 if float('-inf') < val < self.minval:
101 self.minval = val
102 self.minval -= 1
103 return [self.minval if w == float('-inf') else w for w in weights]
104 elif self.maxval == float('inf'):
105 self.maxval = self.minval
106 for val in weights:
107 if self.maxval < val < float('inf'):
108 self.maxval = val
109 self.maxval += 1
110 return [self.maxval if w == float('inf') else w for w in weights]
111 else:
112 return weights