summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Fehlig <jfehlig@suse.com>2012-08-15 09:42:13 -0600
committerJim Fehlig <jfehlig@suse.com>2012-08-15 14:37:00 -0600
commitd39137fa27cb175ba22f3af4ef06a93684b9d86b (patch)
treead472f33aa9b21887e342da0c8a4f6ad020eb232
parent4dd167378d33e3efee4657251655fc2992da2764 (diff)
Make ComputeFilter verify compute-related instance properties
This change adds checks in the ComputeFilter to verify compute hosts can support the (architecture, hypervisor_type, vm_mode) triple specified in the instance properties, improving support for heterogeneous clouds. Images can be marked with the architecture, hypervisor, and virtual machine mode they require, e.g. glance image-update --property architecture=x86_64 \ --property hypervisor_type=kvm <image-uuid> glance image-update --property architecture=i686 \ --property hypervisor_type=xen --property vm_mode=xen <image-uuid> glance image-update --property architecture=x86_64 \ --property hypervisor_type=xen --property vm_mode=hvm <image-uuid> These properties are included in the request_spec part of the run_instance RPC message and made available to the ComputeFilter. The ComputeFilter will only pass compute nodes that satisfy the specified properties. If no properties are specified, the ComputeFilter behavior is unchanged. Adding these checks to the compute filter seems consistent with its definition [1]: "ComputeFilter - checks that the capabilities provided by the compute service satisfy the extra specifications, associated with the instance type." [1] https://github.com/openstack/nova/blob/master/doc/source/devref/filter_scheduler.rst DocImpact Change-Id: I993f2e222ec036f1045f3cc5cc1851e730962729
-rw-r--r--nova/scheduler/filters/compute_filter.py57
-rw-r--r--nova/tests/scheduler/test_host_filters.py100
2 files changed, 155 insertions, 2 deletions
diff --git a/nova/scheduler/filters/compute_filter.py b/nova/scheduler/filters/compute_filter.py
index 2d7c898d6..c0ee98762 100644
--- a/nova/scheduler/filters/compute_filter.py
+++ b/nova/scheduler/filters/compute_filter.py
@@ -22,10 +22,59 @@ LOG = logging.getLogger(__name__)
class ComputeFilter(filters.BaseHostFilter):
- """Filter on active Compute nodes"""
+ """Filter on active Compute nodes that satisfy the instance properties"""
+
+ def _instance_supported(self, capabilities, instance_meta):
+ """Check if the instance is supported by the hypervisor.
+
+ The instance may specify an architecture, hypervisor, and
+ vm_mode, e.g. (x86_64, kvm, hvm).
+ """
+ inst_arch = instance_meta.get('image_architecture', None)
+ inst_h_type = instance_meta.get('image_hypervisor_type', None)
+ inst_vm_mode = instance_meta.get('image_vm_mode', None)
+ inst_props_req = (inst_arch, inst_h_type, inst_vm_mode)
+
+ # Supported if no compute-related instance properties are specified
+ if not any(inst_props_req):
+ return True
+
+ supp_instances = capabilities.get('supported_instances', None)
+ # Not supported if an instance property is requested but nothing
+ # advertised by the host.
+ if not supp_instances:
+ LOG.debug(_("Instance contains properties %(instance_meta)s, "
+ "but no corresponding capabilities are advertised "
+ "by the compute node"), locals())
+ return False
+
+ def _compare_props(props, other_props):
+ for i in props:
+ if i and i not in other_props:
+ return False
+ return True
+
+ for supp_inst in supp_instances:
+ if _compare_props(inst_props_req, supp_inst):
+ LOG.debug(_("Instance properties %(instance_meta)s "
+ "are satisfied by compute host capabilities "
+ "%(capabilities)s"), locals())
+ return True
+
+ LOG.debug(_("Instance contains properties %(instance_meta)s "
+ "that are not provided by the compute node "
+ "capabilities %(capabilities)s"), locals())
+ return False
def host_passes(self, host_state, filter_properties):
- """Returns True for only active compute nodes"""
+ """Check if host passes instance compute properties.
+
+ Returns True for active compute nodes that satisfy
+ the compute properties specified in the instance.
+ """
+ spec = filter_properties.get('request_spec', {})
+ instance_props = spec.get('instance_properties', {})
+ instance_meta = instance_props.get('system_metadata', {})
instance_type = filter_properties.get('instance_type')
if host_state.topic != 'compute' or not instance_type:
return True
@@ -40,4 +89,8 @@ class ComputeFilter(filters.BaseHostFilter):
LOG.debug(_("%(host_state)s is disabled via capabilities"),
locals())
return False
+ if not self._instance_supported(capabilities, instance_meta):
+ LOG.debug(_("%(host_state)s does not support requested "
+ "instance_properties"), locals())
+ return False
return True
diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py
index ca5ca5366..227fbfa82 100644
--- a/nova/tests/scheduler/test_host_filters.py
+++ b/nova/tests/scheduler/test_host_filters.py
@@ -394,6 +394,106 @@ class HostFiltersTestCase(test.TestCase):
'service': service})
self.assertTrue(filt_cls.host_passes(host, filter_properties))
+ def test_compute_filter_passes_same_inst_props(self):
+ self._stub_service_is_up(True)
+ filt_cls = self.class_map['ComputeFilter']()
+ inst_meta = {'system_metadata': {'image_architecture': 'x86_64',
+ 'image_hypervisor_type': 'kvm',
+ 'image_vm_mode': 'hvm'}}
+ req_spec = {'instance_properties': inst_meta}
+ filter_properties = {'instance_type': {'memory_mb': 1024},
+ 'request_spec': req_spec}
+ capabilities = {'enabled': True,
+ 'supported_instances': [
+ ('x86_64', 'kvm', 'hvm')]}
+ service = {'disabled': False}
+ host = fakes.FakeHostState('host1', 'compute',
+ {'free_ram_mb': 1024, 'capabilities': capabilities,
+ 'service': service})
+ self.assertTrue(filt_cls.host_passes(host, filter_properties))
+
+ def test_compute_filter_fails_different_inst_props(self):
+ self._stub_service_is_up(True)
+ filt_cls = self.class_map['ComputeFilter']()
+ inst_meta = {'system_metadata': {'image_architecture': 'arm',
+ 'image_hypervisor_type': 'qemu',
+ 'image_vm_mode': 'hvm'}}
+ req_spec = {'instance_properties': inst_meta}
+ filter_properties = {'instance_type': {'memory_mb': 1024},
+ 'request_spec': req_spec}
+ capabilities = {'enabled': True,
+ 'supported_instances': [
+ ('x86_64', 'kvm', 'hvm')]}
+ service = {'disabled': False}
+ host = fakes.FakeHostState('host1', 'compute',
+ {'free_ram_mb': 1024, 'capabilities': capabilities,
+ 'service': service})
+ self.assertFalse(filt_cls.host_passes(host, filter_properties))
+
+ def test_compute_filter_passes_partial_inst_props(self):
+ self._stub_service_is_up(True)
+ filt_cls = self.class_map['ComputeFilter']()
+ inst_meta = {'system_metadata': {'image_architecture': 'x86_64',
+ 'image_vm_mode': 'hvm'}}
+ req_spec = {'instance_properties': inst_meta}
+ filter_properties = {'instance_type': {'memory_mb': 1024},
+ 'request_spec': req_spec}
+ capabilities = {'enabled': True,
+ 'supported_instances': [
+ ('x86_64', 'kvm', 'hvm')]}
+ service = {'disabled': False}
+ host = fakes.FakeHostState('host1', 'compute',
+ {'free_ram_mb': 1024, 'capabilities': capabilities,
+ 'service': service})
+ self.assertTrue(filt_cls.host_passes(host, filter_properties))
+
+ def test_compute_filter_fails_partial_inst_props(self):
+ self._stub_service_is_up(True)
+ filt_cls = self.class_map['ComputeFilter']()
+ inst_meta = {'system_metadata': {'image_architecture': 'x86_64',
+ 'image_vm_mode': 'hvm'}}
+ req_spec = {'instance_properties': inst_meta}
+ filter_properties = {'instance_type': {'memory_mb': 1024},
+ 'request_spec': req_spec}
+ capabilities = {'enabled': True,
+ 'supported_instances': [
+ ('x86_64', 'xen', 'xen')]}
+ service = {'disabled': False}
+ host = fakes.FakeHostState('host1', 'compute',
+ {'free_ram_mb': 1024, 'capabilities': capabilities,
+ 'service': service})
+ self.assertFalse(filt_cls.host_passes(host, filter_properties))
+
+ def test_compute_filter_passes_without_inst_props(self):
+ self._stub_service_is_up(True)
+ filt_cls = self.class_map['ComputeFilter']()
+ filter_properties = {'instance_type': {'memory_mb': 1024},
+ 'request_spec': {}}
+ capabilities = {'enabled': True,
+ 'supported_instances': [
+ ('x86_64', 'kvm', 'hvm')]}
+ service = {'disabled': False}
+ host = fakes.FakeHostState('host1', 'compute',
+ {'free_ram_mb': 1024, 'capabilities': capabilities,
+ 'service': service})
+ self.assertTrue(filt_cls.host_passes(host, filter_properties))
+
+ def test_compute_filter_fails_without_host_props(self):
+ self._stub_service_is_up(True)
+ filt_cls = self.class_map['ComputeFilter']()
+ inst_meta = {'system_metadata': {'image_architecture': 'x86_64',
+ 'image_hypervisor_type': 'kvm',
+ 'image_vm_mode': 'hvm'}}
+ req_spec = {'instance_properties': inst_meta}
+ filter_properties = {'instance_type': {'memory_mb': 1024},
+ 'request_spec': req_spec}
+ capabilities = {'enabled': True}
+ service = {'disabled': False}
+ host = fakes.FakeHostState('host1', 'compute',
+ {'free_ram_mb': 1024, 'capabilities': capabilities,
+ 'service': service})
+ self.assertFalse(filt_cls.host_passes(host, filter_properties))
+
def test_compute_filter_passes_extra_specs_noop(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['ComputeCapabilitiesFilter']()