diff options
| author | Justin Santa Barbara <justin@fathomdb.com> | 2012-01-19 21:56:58 -0800 |
|---|---|---|
| committer | Justin Santa Barbara <justin@fathomdb.com> | 2012-01-20 17:48:19 -0800 |
| commit | af47d85f020c3455eb8f4efd824b8da14289dd9b (patch) | |
| tree | 2128afd77adca8292b0a4e8de268c00d135305d5 /nova | |
| parent | 1bf066c59bbfe40a30e498f2b24fdddd82fb2508 (diff) | |
Support filter based on CPU core (over)allocation
Change-Id: Ieb15c71e7a335fc642687fe59a3cc2f9929ade26
Diffstat (limited to 'nova')
| -rw-r--r-- | nova/scheduler/filters/__init__.py | 1 | ||||
| -rw-r--r-- | nova/scheduler/filters/core_filter.py | 46 | ||||
| -rw-r--r-- | nova/scheduler/host_manager.py | 6 | ||||
| -rw-r--r-- | nova/tests/scheduler/fakes.py | 22 | ||||
| -rw-r--r-- | nova/tests/scheduler/test_distributed_scheduler.py | 5 | ||||
| -rw-r--r-- | nova/tests/scheduler/test_host_filters.py | 22 |
6 files changed, 89 insertions, 13 deletions
diff --git a/nova/scheduler/filters/__init__.py b/nova/scheduler/filters/__init__.py index f9bf6641b..aa710f982 100644 --- a/nova/scheduler/filters/__init__.py +++ b/nova/scheduler/filters/__init__.py @@ -33,4 +33,5 @@ InstanceType filter. from abstract_filter import AbstractHostFilter from all_hosts_filter import AllHostsFilter from compute_filter import ComputeFilter +from core_filter import CoreFilter from json_filter import JsonFilter diff --git a/nova/scheduler/filters/core_filter.py b/nova/scheduler/filters/core_filter.py new file mode 100644 index 000000000..e9b0aa46d --- /dev/null +++ b/nova/scheduler/filters/core_filter.py @@ -0,0 +1,46 @@ +# Copyright (c) 2011 Openstack, LLC. +# Copyright (c) 2012 Justin Santa Barbara +# +# 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. + +from nova import flags +from nova import log as logging +from nova.scheduler.filters import abstract_filter + + +LOG = logging.getLogger('nova.scheduler.filter.core_filter') + +FLAGS = flags.FLAGS +flags.DEFINE_float('cpu_allocation_ratio', 16.0, + 'Virtual CPU to Physical CPU allocation ratio') + + +class CoreFilter(abstract_filter.AbstractHostFilter): + """CoreFilter filters based on CPU core utilization.""" + + def host_passes(self, host_state, filter_properties): + """Return True if host has sufficient CPU cores.""" + instance_type = filter_properties.get('instance_type') + if host_state.topic != 'compute' or not instance_type: + return True + + if not host_state.vcpus_total: + # Fail safe + LOG.warning(_("VCPUs not set; assuming CPU collection broken")) + return True + + instance_vcpus = instance_type['vcpus'] + vcpus_total = host_state.vcpus_total * FLAGS.cpu_allocation_ratio + return (vcpus_total - host_state.vcpus_used) >= instance_vcpus diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py index 6996155ff..d7c577c32 100644 --- a/nova/scheduler/host_manager.py +++ b/nova/scheduler/host_manager.py @@ -93,24 +93,30 @@ class HostState(object): # These will change as resources are virtually "consumed". self.free_ram_mb = 0 self.free_disk_mb = 0 + self.vcpus_total = 0 + self.vcpus_used = 0 def update_from_compute_node(self, compute): """Update information about a host from its compute_node info.""" all_disk_mb = compute['local_gb'] * 1024 all_ram_mb = compute['memory_mb'] + vcpus_total = compute['vcpus'] if FLAGS.reserved_host_disk_mb > 0: all_disk_mb -= FLAGS.reserved_host_disk_mb if FLAGS.reserved_host_memory_mb > 0: all_ram_mb -= FLAGS.reserved_host_memory_mb self.free_ram_mb = all_ram_mb self.free_disk_mb = all_disk_mb + self.vcpus_total = vcpus_total def consume_from_instance(self, instance): """Update information about a host from instance info.""" disk_mb = instance['local_gb'] * 1024 ram_mb = instance['memory_mb'] + vcpus = instance['vcpus'] self.free_ram_mb -= ram_mb self.free_disk_mb -= disk_mb + self.vcpus_used += vcpus def passes_filters(self, filter_fns, filter_properties): """Return whether or not this host passes filters.""" diff --git a/nova/tests/scheduler/fakes.py b/nova/tests/scheduler/fakes.py index 0b8391a4d..991ad84f1 100644 --- a/nova/tests/scheduler/fakes.py +++ b/nova/tests/scheduler/fakes.py @@ -23,27 +23,27 @@ from nova.scheduler import zone_manager COMPUTE_NODES = [ - dict(id=1, local_gb=1024, memory_mb=1024, + dict(id=1, local_gb=1024, memory_mb=1024, vcpus=1, service=dict(host='host1', disabled=False)), - dict(id=2, local_gb=2048, memory_mb=2048, + dict(id=2, local_gb=2048, memory_mb=2048, vcpus=2, service=dict(host='host2', disabled=True)), - dict(id=3, local_gb=4096, memory_mb=4096, + dict(id=3, local_gb=4096, memory_mb=4096, vcpus=4, service=dict(host='host3', disabled=False)), - dict(id=4, local_gb=8192, memory_mb=8192, + dict(id=4, local_gb=8192, memory_mb=8192, vcpus=8, service=dict(host='host4', disabled=False)), # Broken entry - dict(id=5, local_gb=1024, memory_mb=1024, service=None), + dict(id=5, local_gb=1024, memory_mb=1024, vcpus=1, service=None), ] INSTANCES = [ - dict(local_gb=512, memory_mb=512, host='host1'), - dict(local_gb=512, memory_mb=512, host='host2'), - dict(local_gb=512, memory_mb=512, host='host2'), - dict(local_gb=1024, memory_mb=1024, host='host3'), + dict(local_gb=512, memory_mb=512, vcpus=1, host='host1'), + dict(local_gb=512, memory_mb=512, vcpus=1, host='host2'), + dict(local_gb=512, memory_mb=512, vcpus=1, host='host2'), + dict(local_gb=1024, memory_mb=1024, vcpus=1, host='host3'), # Broken host - dict(local_gb=1024, memory_mb=1024, host=None), + dict(local_gb=1024, memory_mb=1024, vcpus=1, host=None), # No matching host - dict(local_gb=1024, memory_mb=1024, host='host5'), + dict(local_gb=1024, memory_mb=1024, vcpus=1, host='host5'), ] diff --git a/nova/tests/scheduler/test_distributed_scheduler.py b/nova/tests/scheduler/test_distributed_scheduler.py index 3e909d005..ca498f7aa 100644 --- a/nova/tests/scheduler/test_distributed_scheduler.py +++ b/nova/tests/scheduler/test_distributed_scheduler.py @@ -260,7 +260,8 @@ class DistributedSchedulerTestCase(test.TestCase): 'instance_type': {'memory_mb': 512, 'local_gb': 512}, 'instance_properties': {'project_id': 1, 'memory_mb': 512, - 'local_gb': 512}} + 'local_gb': 512, + 'vcpus': 1}} filter_properties = {'local_zone_only': True} self.mox.ReplayAll() weighted_hosts = sched._schedule(fake_context, 'compute', @@ -299,5 +300,5 @@ class DistributedSchedulerTestCase(test.TestCase): self.assertEquals(weight, 1.0) hostinfo = host_manager.HostState('host', 'compute') hostinfo.update_from_compute_node(dict(memory_mb=1000, - local_gb=0)) + local_gb=0, vcpus=1)) self.assertEquals(1000 - 128, fn(hostinfo, {})) diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index 8462422ad..6b5f6d736 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -415,3 +415,25 @@ class HostFiltersTestCase(test.TestCase): raw = ['=', '$foo', 2, 2] filter_properties = {'query': json.dumps(raw)} self.assertTrue(filt_cls.host_passes(host, filter_properties)) + + def test_core_filter_passes(self): + filt_cls = filters.CoreFilter() + filter_properties = {'instance_type': {'vcpus': 1}} + self.flags(cpu_allocation_ratio=2) + host = fakes.FakeHostState('host1', 'compute', + {'vcpus_total': 4, 'vcpus_used': 7}) + self.assertTrue(filt_cls.host_passes(host, filter_properties)) + + def test_core_filter_fails_safe(self): + filt_cls = filters.CoreFilter() + filter_properties = {'instance_type': {'vcpus': 1}} + host = fakes.FakeHostState('host1', 'compute', {}) + self.assertTrue(filt_cls.host_passes(host, filter_properties)) + + def test_core_filter_fails(self): + filt_cls = filters.CoreFilter() + filter_properties = {'instance_type': {'vcpus': 1}} + self.flags(cpu_allocation_ratio=2) + host = fakes.FakeHostState('host1', 'compute', + {'vcpus_total': 4, 'vcpus_used': 8}) + self.assertFalse(filt_cls.host_passes(host, filter_properties)) |
