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:
@@ -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>`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user