summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorJustin Santa Barbara <justin@fathomdb.com>2012-01-19 21:56:58 -0800
committerJustin Santa Barbara <justin@fathomdb.com>2012-01-20 17:48:19 -0800
commitaf47d85f020c3455eb8f4efd824b8da14289dd9b (patch)
tree2128afd77adca8292b0a4e8de268c00d135305d5 /nova
parent1bf066c59bbfe40a30e498f2b24fdddd82fb2508 (diff)
Support filter based on CPU core (over)allocation
Change-Id: Ieb15c71e7a335fc642687fe59a3cc2f9929ade26
Diffstat (limited to 'nova')
-rw-r--r--nova/scheduler/filters/__init__.py1
-rw-r--r--nova/scheduler/filters/core_filter.py46
-rw-r--r--nova/scheduler/host_manager.py6
-rw-r--r--nova/tests/scheduler/fakes.py22
-rw-r--r--nova/tests/scheduler/test_distributed_scheduler.py5
-rw-r--r--nova/tests/scheduler/test_host_filters.py22
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))