diff options
Diffstat (limited to 'nova')
-rw-r--r-- | nova/scheduler/filters/core_filter.py | 57 | ||||
-rw-r--r-- | nova/tests/scheduler/test_host_filters.py | 46 |
2 files changed, 99 insertions, 4 deletions
diff --git a/nova/scheduler/filters/core_filter.py b/nova/scheduler/filters/core_filter.py index 4eb6ebcf1..6e733a237 100644 --- a/nova/scheduler/filters/core_filter.py +++ b/nova/scheduler/filters/core_filter.py @@ -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 diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index 9306615ed..531ad8b0b 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -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) |