diff --git a/doc/source/filter_scheduler.rst b/doc/source/filter_scheduler.rst index d3fbe0bd0f3b..e69e4a1d6ace 100644 --- a/doc/source/filter_scheduler.rst +++ b/doc/source/filter_scheduler.rst @@ -379,6 +379,9 @@ The Filter Scheduler weighs hosts based on the config option Sort with the largest weight winning. If the multiplier is negative, the host with least RAM available will win (useful for stacking hosts, instead of spreading). +* |DiskWeigher| Hosts are weighted and sorted by free disk space with the largest + weight winning. If the multiplier is negative, the host with less disk space available + will win (useful for stacking hosts, instead of spreading). * |MetricsWeigher| This weigher can compute the weight based on the compute node host's various metrics. The to-be weighed metrics and their weighing ratio are specified in the configuration file as the followings:: @@ -455,3 +458,4 @@ in :mod:`nova.tests.scheduler`. .. |IoOpsWeigher| replace:: :class:`IoOpsWeigher ` .. |ServerGroupSoftAffinityWeigher| replace:: :class:`ServerGroupSoftAffinityWeigher ` .. |ServerGroupSoftAntiAffinityWeigher| replace:: :class:`ServerGroupSoftAntiAffinityWeigher ` +.. |DiskWeigher| replace:: :class:`DiskWeigher ` diff --git a/nova/conf/scheduler.py b/nova/conf/scheduler.py index 404966b932d5..5134d8695d79 100644 --- a/nova/conf/scheduler.py +++ b/nova/conf/scheduler.py @@ -327,6 +327,11 @@ ram_weight_mult_opt = cfg.FloatOpt("ram_weight_multiplier", help="Multiplier used for weighing ram. Negative numbers mean to " "stack vs spread.") +disk_weight_mult_opt = cfg.FloatOpt("disk_weight_multiplier", + default=1.0, + help="Multiplier used for weighing free disk space. Negative " + "numbers mean to stack vs spread.") + io_ops_weight_mult_opt = cfg.FloatOpt("io_ops_weight_multiplier", default=-1.0, help="Multiplier used for weighing host io ops. Negative numbers mean " @@ -406,6 +411,7 @@ default_opts = [host_subset_size_opt, agg_img_prop_iso_separator_opt, max_instances_per_host_opt, ram_weight_mult_opt, + disk_weight_mult_opt, io_ops_weight_mult_opt, scheduler_max_att_opt, soft_affinity_weight_opt, diff --git a/nova/scheduler/weights/disk.py b/nova/scheduler/weights/disk.py new file mode 100644 index 000000000000..50714494faa8 --- /dev/null +++ b/nova/scheduler/weights/disk.py @@ -0,0 +1,38 @@ +# Copyright (c) 2015 OpenStack Foundation +# All Rights Reserved. +# +# 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. +""" +Disk Weigher. Weigh hosts by their disk usage. + +The default is to spread instances across all hosts evenly. If you prefer +stacking, you can set the 'disk_weight_multiplier' option to a negative +number and the weighing has the opposite effect of the default. +""" + +import nova.conf +from nova.scheduler import weights + +CONF = nova.conf.CONF + + +class DiskWeigher(weights.BaseHostWeigher): + minval = 0 + + def weight_multiplier(self): + """Override the weight multiplier.""" + return CONF.disk_weight_multiplier + + def _weigh_object(self, host_state, weight_properties): + """Higher weights win. We want spreading to be the default.""" + return host_state.free_disk_mb diff --git a/nova/tests/unit/scheduler/weights/test_weights_disk.py b/nova/tests/unit/scheduler/weights/test_weights_disk.py new file mode 100644 index 000000000000..d84922271ec8 --- /dev/null +++ b/nova/tests/unit/scheduler/weights/test_weights_disk.py @@ -0,0 +1,111 @@ +# Copyright 2011-2016 OpenStack Foundation +# All Rights Reserved. +# +# 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. +""" +Tests For Scheduler disk weights. +""" + +from nova.scheduler import weights +from nova.scheduler.weights import disk +from nova import test +from nova.tests.unit.scheduler import fakes + + +class DiskWeigherTestCase(test.NoDBTestCase): + def setUp(self): + super(DiskWeigherTestCase, self).setUp() + self.weight_handler = weights.HostWeightHandler() + self.weighers = [disk.DiskWeigher()] + + def _get_weighed_host(self, hosts, weight_properties=None): + if weight_properties is None: + weight_properties = {} + return self.weight_handler.get_weighed_objects(self.weighers, + hosts, weight_properties)[0] + + def _get_all_hosts(self): + host_values = [ + ('host1', 'node1', {'free_disk_mb': 5120}), + ('host2', 'node2', {'free_disk_mb': 10240}), + ('host3', 'node3', {'free_disk_mb': 30720}), + ('host4', 'node4', {'free_disk_mb': 81920}) + ] + return [fakes.FakeHostState(host, node, values) + for host, node, values in host_values] + + def test_default_of_spreading_first(self): + hostinfo_list = self._get_all_hosts() + + # host1: free_disk_mb=5120 + # host2: free_disk_mb=10240 + # host3: free_disk_mb=30720 + # host4: free_disk_mb=81920 + + # so, host4 should win: + weighed_host = self._get_weighed_host(hostinfo_list) + self.assertEqual(1.0, weighed_host.weight) + self.assertEqual('host4', weighed_host.obj.host) + + def test_disk_filter_multiplier1(self): + self.flags(disk_weight_multiplier=0.0) + hostinfo_list = self._get_all_hosts() + + # host1: free_disk_mb=5120 + # host2: free_disk_mb=10240 + # host3: free_disk_mb=30720 + # host4: free_disk_mb=81920 + + # We do not know the host, all have same weight. + weighed_host = self._get_weighed_host(hostinfo_list) + self.assertEqual(0.0, weighed_host.weight) + + def test_disk_filter_multiplier2(self): + self.flags(disk_weight_multiplier=2.0) + hostinfo_list = self._get_all_hosts() + + # host1: free_disk_mb=5120 + # host2: free_disk_mb=10240 + # host3: free_disk_mb=30720 + # host4: free_disk_mb=81920 + + # so, host4 should win: + weighed_host = self._get_weighed_host(hostinfo_list) + self.assertEqual(1.0 * 2, weighed_host.weight) + self.assertEqual('host4', weighed_host.obj.host) + + def test_disk_filter_negative(self): + self.flags(disk_weight_multiplier=1.0) + hostinfo_list = self._get_all_hosts() + host_attr = {'id': 100, 'disk_mb': 81920, 'free_disk_mb': -5120} + host_state = fakes.FakeHostState('negative', 'negative', host_attr) + hostinfo_list = list(hostinfo_list) + [host_state] + + # host1: free_disk_mb=5120 + # host2: free_disk_mb=10240 + # host3: free_disk_mb=30720 + # host4: free_disk_mb=81920 + # negativehost: free_disk_mb=-5120 + + # so, host4 should win + weights = self.weight_handler.get_weighed_objects(self.weighers, + hostinfo_list, {}) + + weighed_host = weights[0] + self.assertEqual(1, weighed_host.weight) + self.assertEqual('host4', weighed_host.obj.host) + + # and negativehost should lose + weighed_host = weights[-1] + self.assertEqual(0, weighed_host.weight) + self.assertEqual('negative', weighed_host.obj.host) diff --git a/releasenotes/notes/disk-weight-scheduler-98647f9c6317d21d.yaml b/releasenotes/notes/disk-weight-scheduler-98647f9c6317d21d.yaml new file mode 100644 index 000000000000..27ccf47a663d --- /dev/null +++ b/releasenotes/notes/disk-weight-scheduler-98647f9c6317d21d.yaml @@ -0,0 +1,10 @@ +--- +prelude: > + The disk_weight_multiplier option is now available. +features: + - A disk space scheduling filter is now available, + which prefers compute nodes with the most available + disk space. By default, free disk space is given equal + importance to available RAM. To increase the priority + of free disk space in scheduling, increase the + disk_weight_multiplier option.