Create Nova Scheduler IO Ops Weighter

Add a new nova scheduler weighter, sort the filter
hosts according to host io ops number, aims to
booting instances on light workload hosts.

DocImpact: Adds io_ops_weight_multiplier to [DEFAULT]
group of nova.conf and an new default schedule weigher.
Change-Id: Ib3c9184f10b2ebe6b1230365a51b5542dffd447c
Implements: blueprint io-ops-weight
kiwik-chenrui 9 years ago committed by Rui Chen
parent ad84585d75
commit fb559a34f9

@ -364,6 +364,11 @@ The Filter Scheduler weighs hosts based on the config option
metrics_weight_setting = name1=1.0, name2=-1.0
* |IoOpsWeigher| The weigher can compute the weight based on the compute node
host's workload. The default is to preferably choose light workload compute
hosts. If the multiplier is positive, the weigher prefer choosing heavy
workload compute hosts, the weighing has the opposite effect of the default.
Filter Scheduler finds local list of acceptable hosts by repeated filtering and
weighing. Each time it chooses a host, it virtually consumes resources on it,
so subsequent selections can adjust accordingly. It is useful if the customer
@ -415,3 +420,4 @@ in :mod:``nova.tests.scheduler``.
.. |AggregateImagePropertiesIsolation| replace:: :class:`AggregateImagePropertiesIsolation <nova.scheduler.filters.aggregate_image_properties_isolation.AggregateImagePropertiesIsolation>`
.. |MetricsFilter| replace:: :class:`MetricsFilter <nova.scheduler.filters.metrics_filter.MetricsFilter>`
.. |MetricsWeigher| replace:: :class:`MetricsWeigher <nova.scheduler.weights.metrics.MetricsWeigher>`
.. |IoOpsWeigher| replace:: :class:`IoOpsWeigher <nova.scheduler.weights.io_ops.IoOpsWeigher>`

@ -0,0 +1,51 @@
# Copyright (c) 2014 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
# 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.
Io Ops Weigher. Weigh hosts by their io ops number.
The default is to preferably choose light workload compute hosts. If you prefer
choosing heavy workload compute hosts, you can set 'io_ops_weight_multiplier'
option to a positive number and the weighing has the opposite effect of the
from oslo.config import cfg
from nova.scheduler import weights
io_ops_weight_opts = [
help='Multiplier used for weighing host io ops. Negative '
'numbers mean a preference to choose light workload '
'compute hosts.'),
class IoOpsWeigher(weights.BaseHostWeigher):
minval = 0
def weight_multiplier(self):
"""Override the weight multiplier."""
return CONF.io_ops_weight_multiplier
def _weigh_object(self, host_state, weight_properties):
"""Higher weights win. We want to choose light workload host
to be the default.
return host_state.num_io_ops

@ -16,6 +16,8 @@
Tests For Scheduler weights.
from oslo.serialization import jsonutils
from nova import context
from nova import exception
from nova.openstack.common.fixture import mockpatch
@ -36,9 +38,10 @@ class TestWeighedHost(test.NoDBTestCase):
def test_all_weighers(self):
classes = weights.all_weighers()
class_names = [cls.__name__ for cls in classes]
self.assertEqual(len(classes), 2)
self.assertEqual(len(classes), 3)
self.assertIn('RAMWeigher', class_names)
self.assertIn('MetricsWeigher', class_names)
self.assertIn('IoOpsWeigher', class_names)
class RamWeigherTestCase(test.NoDBTestCase):
@ -246,3 +249,91 @@ class MetricsWeigherTestCase(test.NoDBTestCase):
self.flags(required=False, group='metrics')
setting = ['foo=0.0001', 'zot=-1']
self._do_test(setting, 1.0, 'host5')
# host1: num_io_ops=1
dict(id=1, local_gb=1024, memory_mb=1024, vcpus=1,
disk_available_least=None, free_ram_mb=512, vcpus_used=1,
free_disk_gb=512, local_gb_used=0, updated_at=None,
service=dict(host='host1', disabled=False),
hypervisor_hostname='node1', host_ip='',
hypervisor_version=0, numa_topology=None,
stats=jsonutils.dumps({'io_workload': '1'})),
# host2: num_io_ops=2
dict(id=2, local_gb=2048, memory_mb=2048, vcpus=2,
disk_available_least=1024, free_ram_mb=1024, vcpus_used=2,
free_disk_gb=1024, local_gb_used=0, updated_at=None,
service=dict(host='host2', disabled=True),
hypervisor_hostname='node2', host_ip='',
hypervisor_version=0, numa_topology=None,
stats=jsonutils.dumps({'io_workload': '2'})),
# host3: num_io_ops=0, so host3 should win in the case of default
# io_ops_weight_multiplier configure.
dict(id=3, local_gb=4096, memory_mb=4096, vcpus=4,
disk_available_least=3333, free_ram_mb=3072, vcpus_used=1,
free_disk_gb=3072, local_gb_used=0, updated_at=None,
service=dict(host='host3', disabled=False),
hypervisor_hostname='node3', host_ip='',
hypervisor_version=0, numa_topology=None,
stats=jsonutils.dumps({'io_workload': '0'})),
# host4: num_io_ops=4, so host4 should win in the case of positive
# io_ops_weight_multiplier configure.
dict(id=4, local_gb=8192, memory_mb=8192, vcpus=8,
disk_available_least=8192, free_ram_mb=8192, vcpus_used=0,
free_disk_gb=8888, local_gb_used=0, updated_at=None,
service=dict(host='host4', disabled=False),
hypervisor_hostname='node4', host_ip='',
hypervisor_version=0, numa_topology=None,
stats=jsonutils.dumps({'io_workload': '4'})),
# Broken entry
dict(id=5, local_gb=1024, memory_mb=1024, vcpus=1, service=None),
class IoOpsWeigherTestCase(test.NoDBTestCase):
def setUp(self):
super(IoOpsWeigherTestCase, self).setUp()
self.host_manager = fakes.FakeHostManager()
self.weight_handler = weights.HostWeightHandler()
self.weight_classes = self.weight_handler.get_matching_classes(
def _get_weighed_host(self, hosts, io_ops_weight_multiplier):
if io_ops_weight_multiplier is not None:
return self.weight_handler.get_weighed_objects(self.weight_classes,
hosts, {})[0]
def _get_all_hosts(self):
ctxt = context.get_admin_context()
return self.host_manager.get_all_host_states(ctxt)
def _do_test(self, io_ops_weight_multiplier, expected_weight,
hostinfo_list = self._get_all_hosts()
weighed_host = self._get_weighed_host(hostinfo_list,
self.assertEqual(weighed_host.weight, expected_weight)
if expected_host:
self.assertEqual(, expected_host)
def test_io_ops_weight_multiplier_by_default(self):
def test_io_ops_weight_multiplier_zero_value(self):
# We do not know the host, all have same weight.
def test_io_ops_weight_multiplier_positive_value(self):