Add AggregateCoreFilter

Implements blueprint per-aggregate-resource-ratio

* AggregateCoreFilter to support per-aggregate cpu_allocation_ratio
* Falls back to global setting if per-aggregate value not found

DocImpact

Change-Id: I9230f46e2490226f3c50d616aa173d4722095087
This commit is contained in:
Qiu Yu
2013-06-21 17:55:31 +08:00
parent f47c9c036e
commit 855be80b8f
3 changed files with 105 additions and 4 deletions

View File

@@ -73,6 +73,11 @@ There are some standard filter classes to use (:mod:`nova.scheduler.filters`):
* |ComputeFilter| - passes all hosts that are operational and enabled.
* |CoreFilter| - filters based on CPU core utilization. It passes hosts with
sufficient number of CPU cores.
* |AggregateCoreFilter| - filters hosts by CPU core number with per-aggregate
cpu_allocation_ratio setting. If no per-aggregate value is found, it will
fall back to the global default cpu_allocation_ratio. If more than one value
is found for a host (meaning the host is in two differenet aggregate with
different ratio settings), the minimum value will be used.
* |IsolatedHostsFilter| - filter based on "image_isolated" and "host_isolated"
flags.
* |JsonFilter| - allows simple JSON-based grammar for selecting hosts.
@@ -282,6 +287,7 @@ in :mod:`nova.tests.scheduler`.
.. |ComputeCapabilitiesFilter| replace:: :class:`ComputeCapabilitiesFilter <nova.scheduler.filters.compute_capabilities_filter.ComputeCapabilitiesFilter>`
.. |ComputeFilter| replace:: :class:`ComputeFilter <nova.scheduler.filters.compute_filter.ComputeFilter>`
.. |CoreFilter| replace:: :class:`CoreFilter <nova.scheduler.filters.core_filter.CoreFilter>`
.. |AggregateCoreFilter| replace:: :class:`AggregateCoreFilter <nova.scheduler.filters.core_filter.AggregateCoreFilter>`
.. |IsolatedHostsFilter| replace:: :class:`IsolatedHostsFilter <nova.scheduler.filters.isolated_hosts_filter>`
.. |JsonFilter| replace:: :class:`JsonFilter <nova.scheduler.filters.json_filter.JsonFilter>`
.. |RamFilter| replace:: :class:`RamFilter <nova.scheduler.filters.ram_filter.RamFilter>`

View File

@@ -17,6 +17,7 @@
from oslo.config import cfg
from nova import db
from nova.openstack.common import log as logging
from nova.scheduler import filters
@@ -24,14 +25,19 @@ LOG = logging.getLogger(__name__)
cpu_allocation_ratio_opt = cfg.FloatOpt('cpu_allocation_ratio',
default=16.0,
help='Virtual CPU to Physical CPU allocation ratio')
help='Virtual CPU to physical CPU allocation ratio which affects '
'all CPU filters. This configuration specifies a global ratio '
'for CoreFilter. For AggregateCoreFilter, it will fall back to '
'this configuration value if no per-aggregate setting found.')
CONF = cfg.CONF
CONF.register_opt(cpu_allocation_ratio_opt)
class CoreFilter(filters.BaseHostFilter):
"""CoreFilter filters based on CPU core utilization."""
class BaseCoreFilter(filters.BaseHostFilter):
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
raise NotImplementedError
def host_passes(self, host_state, filter_properties):
"""Return True if host has sufficient CPU cores."""
@@ -45,7 +51,9 @@ class CoreFilter(filters.BaseHostFilter):
return True
instance_vcpus = instance_type['vcpus']
vcpus_total = host_state.vcpus_total * CONF.cpu_allocation_ratio
cpu_allocation_ratio = self._get_cpu_allocation_ratio(host_state,
filter_properties)
vcpus_total = host_state.vcpus_total * cpu_allocation_ratio
# Only provide a VCPU limit to compute if the virt driver is reporting
# an accurate count of installed VCPUs. (XenServer driver does not)
@@ -53,3 +61,44 @@ class CoreFilter(filters.BaseHostFilter):
host_state.limits['vcpu'] = vcpus_total
return (vcpus_total - host_state.vcpus_used) >= instance_vcpus
class CoreFilter(BaseCoreFilter):
"""CoreFilter filters based on CPU core utilization."""
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
return CONF.cpu_allocation_ratio
class AggregateCoreFilter(BaseCoreFilter):
"""AggregateRamFilter with per-aggregate CPU subscription flag.
Fall back to global cpu_allocation_ratio if no per-aggregate setting found.
"""
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
context = filter_properties['context'].elevated()
# TODO(uni): DB query in filter is a performance hit, especially for
# system with lots of hosts. Will need a general solution here to fix
# all filters with aggregate DB call things.
metadata = db.aggregate_metadata_get_by_host(
context, host_state.host, key='cpu_allocation_ratio')
aggregate_vals = metadata.get('cpu_allocation_ratio', set())
num_values = len(aggregate_vals)
if num_values == 0:
return CONF.cpu_allocation_ratio
if num_values > 1:
LOG.warning(_("%(num_values)d ratio values found, "
"of which the minimum value will be used."),
{'num_values': num_values})
try:
ratio = float(min(aggregate_vals))
except ValueError as e:
LOG.warning(_("Could not decode cpu_allocation_ratio: "
"'%(e)s'") % locals())
ratio = CONF.cpu_allocation_ratio
return ratio

View File

@@ -1317,6 +1317,52 @@ class HostFiltersTestCase(test.NoDBTestCase):
{'vcpus_total': 4, 'vcpus_used': 8})
self.assertFalse(filt_cls.host_passes(host, filter_properties))
def test_aggregate_core_filter_value_error(self):
filt_cls = self.class_map['AggregateCoreFilter']()
filter_properties = {'context': self.context,
'instance_type': {'vcpus': 1}}
self.flags(cpu_allocation_ratio=2)
host = fakes.FakeHostState('host1', 'node1',
{'vcpus_total': 4, 'vcpus_used': 7})
self._create_aggregate_with_host(name='fake_aggregate',
hosts=['host1'],
metadata={'cpu_allocation_ratio': 'XXX'})
self.assertTrue(filt_cls.host_passes(host, filter_properties))
self.assertEqual(4 * 2, host.limits['vcpu'])
def test_aggregate_core_filter_default_value(self):
filt_cls = self.class_map['AggregateCoreFilter']()
filter_properties = {'context': self.context,
'instance_type': {'vcpus': 1}}
self.flags(cpu_allocation_ratio=2)
host = fakes.FakeHostState('host1', 'node1',
{'vcpus_total': 4, 'vcpus_used': 8})
# False: fallback to default flag w/o aggregates
self.assertFalse(filt_cls.host_passes(host, filter_properties))
self._create_aggregate_with_host(name='fake_aggregate',
hosts=['host1'],
metadata={'cpu_allocation_ratio': '3'})
# True: use ratio from aggregates
self.assertTrue(filt_cls.host_passes(host, filter_properties))
self.assertEqual(4 * 3, host.limits['vcpu'])
def test_aggregate_core_filter_conflict_values(self):
filt_cls = self.class_map['AggregateCoreFilter']()
filter_properties = {'context': self.context,
'instance_type': {'vcpus': 1}}
self.flags(cpu_allocation_ratio=1)
host = fakes.FakeHostState('host1', 'node1',
{'vcpus_total': 4, 'vcpus_used': 8})
self._create_aggregate_with_host(name='fake_aggregate1',
hosts=['host1'],
metadata={'cpu_allocation_ratio': '2'})
self._create_aggregate_with_host(name='fake_aggregate2',
hosts=['host1'],
metadata={'cpu_allocation_ratio': '3'})
# use the minimum ratio from aggregates
self.assertFalse(filt_cls.host_passes(host, filter_properties))
self.assertEqual(4 * 2, host.limits['vcpu'])
@staticmethod
def _make_zone_request(zone, is_admin=False):
ctxt = context.RequestContext('fake', 'fake', is_admin=is_admin)