Add CPUWeigher

nova provides many different types of filter, providing both resource
stacking/spreading and affinity/anti-affinity patterns. Curiously
enough, however, there is no way to configure a stack or spread policy
of CPUs. Add one.

Change-Id: I90ee8a2081c2a0465441a8d81d161f4887b4e1fb
Implements: bp vcpu-weighter
This commit is contained in:
Stephen Finucane 2016-09-29 11:13:37 +01:00
parent fb0b785169
commit 67f52ab36d
5 changed files with 208 additions and 2 deletions

View File

@ -397,9 +397,15 @@ The Filter Scheduler weighs hosts based on the config option
`nova.scheduler.weights.all_weighers`, which selects the following weighers:
* |RAMWeigher| Compute weight based on available RAM on the compute node.
Sort with the largest weight winning. If the multiplier is negative, the
Sort with the largest weight winning. If the multiplier,
:oslo.config:opt:`filter_scheduler.ram_weight_multiplier`, is negative, the
host with least RAM available will win (useful for stacking hosts, instead
of spreading).
* |CPUWeigher| Compute weight based on available vCPUs on the compute node.
Sort with the largest weight winning. If the multiplier,
:oslo.config:opt:`filter_scheduler.cpu_weight_multiplier`, is negative, the
host with least CPUs 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).
@ -489,6 +495,7 @@ in :mod:`nova.tests.scheduler`.
.. |AggregateMultiTenancyIsolation| replace:: :class:`AggregateMultiTenancyIsolation <nova.scheduler.filters.aggregate_multitenancy_isolation.AggregateMultiTenancyIsolation>`
.. |NUMATopologyFilter| replace:: :class:`NUMATopologyFilter <nova.scheduler.filters.numa_topology_filter.NUMATopologyFilter>`
.. |RAMWeigher| replace:: :class:`RAMWeigher <nova.scheduler.weights.ram.RAMWeigher>`
.. |CPUWeigher| replace:: :class:`CPUWeigher <nova.scheduler.weights.cpu.CPUWeigher>`
.. |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>`

View File

@ -431,7 +431,7 @@ Possible values:
default=1.0,
deprecated_group="DEFAULT",
help="""
Ram weight multipler ratio.
RAM weight multipler ratio.
This option determines how hosts with more or less available RAM are weighed. A
positive value will result in the scheduler preferring hosts with more
@ -450,6 +450,29 @@ Possible values:
* An integer or float value, where the value corresponds to the multipler
ratio for this weigher.
"""),
cfg.FloatOpt("cpu_weight_multiplier",
default=1.0,
help="""
CPU weight multiplier ratio.
Multiplier used for weighting free vCPUs. Negative numbers indicate stacking
rather than spreading.
This option is only used by the FilterScheduler and its subclasses; if you use
a different scheduler, this option has no effect. Also note that this setting
only affects scheduling if the 'cpu' weigher is enabled.
Possible values:
* An integer or float value, where the value corresponds to the multipler
ratio for this weigher.
Related options:
* ``filter_scheduler.weight_classes``: This weigher must be added to list of
enabled weight classes if the ``weight_classes`` setting is set to a
non-default value.
"""),
cfg.FloatOpt("disk_weight_multiplier",
default=1.0,

View File

@ -0,0 +1,41 @@
# Copyright (c) 2016, Red Hat Inc.
# 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.
"""
CPU Weigher. Weigh hosts by their CPU usage.
The default is to spread instances across all hosts evenly. If you prefer
stacking, you can set the 'cpu_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 CPUWeigher(weights.BaseHostWeigher):
minval = 0
def weight_multiplier(self):
"""Override the weight multiplier."""
return CONF.filter_scheduler.cpu_weight_multiplier
def _weigh_object(self, host_state, weight_properties):
"""Higher weights win. We want spreading to be the default."""
vcpus_free = (host_state.vcpus_total * host_state.cpu_allocation_ratio
- host_state.vcpus_used)
return vcpus_free

View File

@ -0,0 +1,129 @@
# Copyright 2016, Red Hat Inc.
# 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 CPU weights.
"""
from nova.scheduler import weights
from nova.scheduler.weights import cpu
from nova import test
from nova.tests.unit.scheduler import fakes
class CPUWeigherTestCase(test.NoDBTestCase):
def setUp(self):
super(CPUWeigherTestCase, self).setUp()
self.weight_handler = weights.HostWeightHandler()
self.weighers = [cpu.CPUWeigher()]
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', {'vcpus_total': 8, 'vcpus_used': 8,
'cpu_allocation_ratio': 1.0}), # 0 free
('host2', 'node2', {'vcpus_total': 4, 'vcpus_used': 2,
'cpu_allocation_ratio': 1.0}), # 2 free
('host3', 'node3', {'vcpus_total': 6, 'vcpus_used': 0,
'cpu_allocation_ratio': 1.0}), # 6 free
('host4', 'node4', {'vcpus_total': 8, 'vcpus_used': 0,
'cpu_allocation_ratio': 2.0}), # 16 free
]
return [fakes.FakeHostState(host, node, values)
for host, node, values in host_values]
def test_multiplier_default(self):
hostinfo_list = self._get_all_hosts()
# host1: vcpus_free=0
# host2: vcpus_free=2
# host3: vcpus_free=6
# host4: vcpus_free=16
# 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_multiplier_none(self):
self.flags(cpu_weight_multiplier=0.0, group='filter_scheduler')
hostinfo_list = self._get_all_hosts()
# host1: vcpus_free=0
# host2: vcpus_free=2
# host3: vcpus_free=6
# host4: vcpus_free=16
# 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_multiplier_positive(self):
self.flags(cpu_weight_multiplier=2.0, group='filter_scheduler')
hostinfo_list = self._get_all_hosts()
# host1: vcpus_free=0
# host2: vcpus_free=2
# host3: vcpus_free=6
# host4: vcpus_free=16
# 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_multiplier_negative(self):
self.flags(cpu_weight_multiplier=-1.0, group='filter_scheduler')
hostinfo_list = self._get_all_hosts()
# host1: vcpus_free=0
# host2: vcpus_free=2
# host3: vcpus_free=6
# host4: vcpus_free=16
# so, host1 should win:
weighed_host = self._get_weighed_host(hostinfo_list)
self.assertEqual('host1', weighed_host.obj.host)
def test_negative_host(self):
self.flags(cpu_weight_multiplier=1.0, group='filter_scheduler')
hostinfo_list = self._get_all_hosts()
host_attr = {'vcpus_total': 4, 'vcpus_used': 6,
'cpu_allocation_ratio': 1.0}
host_state = fakes.FakeHostState('negative', 'negative', host_attr)
hostinfo_list = list(hostinfo_list) + [host_state]
# host1: vcpus_free=0
# host2: vcpus_free=2
# host3: vcpus_free=6
# host4: vcpus_free=16
# negative: vcpus_free=-2
# 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)

View File

@ -0,0 +1,6 @@
---
features:
- |
Add ``CPUWeigher`` weigher. This can be used to spread (default) or pack
workloads on hosts based on their vCPU usage. This can be configured using
the ``[filter_scheduler] cpu_weight_multiplier`` configuration option.