diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-08-22 07:02:45 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-08-22 07:02:45 +0000 |
| commit | b090bdd0887317bb4f1f2a8ecec577b16fb94363 (patch) | |
| tree | eddf432cbc8beb1873d27c698424013f9aa9501c | |
| parent | 962efab370f168bb077447eeb15d43063c8b2272 (diff) | |
| parent | 5ea7db9b7195132df4d8efea0a8e41e4f994b23c (diff) | |
| download | nova-b090bdd0887317bb4f1f2a8ecec577b16fb94363.tar.gz nova-b090bdd0887317bb4f1f2a8ecec577b16fb94363.tar.xz nova-b090bdd0887317bb4f1f2a8ecec577b16fb94363.zip | |
Merge "Introduce ImagePropertiesFilter scheduler filter"
| -rw-r--r-- | doc/source/devref/filter_scheduler.rst | 27 | ||||
| -rw-r--r-- | etc/nova/nova.conf.sample | 2 | ||||
| -rw-r--r-- | nova/scheduler/filters/arch_filter.py | 44 | ||||
| -rw-r--r-- | nova/scheduler/filters/compute_filter.py | 57 | ||||
| -rw-r--r-- | nova/scheduler/filters/image_props_filter.py | 86 | ||||
| -rw-r--r-- | nova/scheduler/host_manager.py | 3 | ||||
| -rw-r--r-- | nova/tests/scheduler/test_host_filters.py | 151 |
7 files changed, 146 insertions, 224 deletions
diff --git a/doc/source/devref/filter_scheduler.rst b/doc/source/devref/filter_scheduler.rst index d6ceb08ef..807fdceec 100644 --- a/doc/source/devref/filter_scheduler.rst +++ b/doc/source/devref/filter_scheduler.rst @@ -27,8 +27,9 @@ There are some standard filter classes to use (:mod:`nova.scheduler.filters`): * |AllHostsFilter| - frankly speaking, this filter does no operation. It passes all the available hosts. -* |ArchFilter| - filters hosts based on architecture. It passes hosts - that can support the architecture specified in the instance properties. +* |ImagePropertiesFilter| - filters hosts based on properties defined + on the instance's image. It passes hosts that can support the specified + image properties contained in the instance. * |AvailabilityZoneFilter| - filters hosts by availability zone. It passes hosts matching the availability zone specfied in the instance properties. * |ComputeCapabilityFilter| - checks that the capabilities provided by the @@ -86,10 +87,17 @@ scheduler with availability zones support and can configure availability zones on each compute host. This classes method `host_passes` returns `True` if availability zone mentioned in request is the same on the current compute host. -The |ArchFilter| filters hosts based on the architecture specified in the -instance properties. E.g., an instance might require a host that supports -the arm architecture. The |ArchFilter| will only pass hosts that can -support the architecture requested by the instance. +The |ImagePropertiesFilter| filters hosts based on the architecture, +hypervisor type, and virtual machine mode specified in the +instance. E.g., an instance might require a host that supports the arm +architecture on a qemu compute host. The |ImagePropertiesFilter| will only +pass hosts that can statisfy this request. These instance +properties are populated from properties define on the instance's image. +E.g. an image can be decorated with these properties using +`glance image-update img-uuid --property architecture=arm --property +hypervisor_type=qemu` +Only hosts that statify these requirements will pass the +|ImagePropertiesFilter|. |ComputeCapabilitesFilter| checks if the host satisfies any 'extra specs' specfied on the instance type. The 'extra specs' can contain key/value pairs, @@ -160,11 +168,12 @@ The default values for these settings in nova.conf are: :: --scheduler_available_filters=nova.scheduler.filters.standard_filters - --scheduler_default_filters=RamFilter,ComputeFilter,AvailabilityZoneFilter,ComputeCapabilityFilter + --scheduler_default_filters=RamFilter,ComputeFilter,AvailabilityZoneFilter,ComputeCapabilityFilter,ImagePropertiesFilter With this configuration, all filters in `nova.scheduler.filters` would be available, and by default the |RamFilter|, |ComputeFilter|, -|AvailabilityZoneFilter|, and |ComputeCapabilityFilter| would be used. +|AvailabilityZoneFilter|, |ComputeCapabilityFilter|, and +|ImagePropertiesFilter| would be used. If you want to create **your own filter** you just need to inherit from |BaseHostFilter| and implement one method: @@ -278,7 +287,7 @@ P.S.: you can find more examples of using Filter Scheduler and standard filters in :mod:`nova.tests.scheduler`. .. |AllHostsFilter| replace:: :class:`AllHostsFilter <nova.scheduler.filters.all_hosts_filter.AllHostsFilter>` -.. |ArchFilter| replace:: :class:`ArchFilter <nova.scheduler.filters.arch_filter.ArchFilter>` +.. |ImagePropertiesFilter| replace:: :class:`ImagePropertiesFilter <nova.scheduler.filters.image_props_filter.ImagePropertiesFilter>` .. |AvailabilityZoneFilter| replace:: :class:`AvailabilityZoneFilter <nova.scheduler.filters.availability_zone_filter.AvailabilityZoneFilter>` .. |BaseHostFilter| replace:: :class:`BaseHostFilter <nova.scheduler.filters.BaseHostFilter>` .. |ComputeCapabilitiesFilter| replace:: :class:`ComputeCapabilitiesFilter <nova.scheduler.filters.compute_capabilities_filter.ComputeCapabilitiesFilter>` diff --git a/etc/nova/nova.conf.sample b/etc/nova/nova.conf.sample index 32f33e557..178ed5830 100644 --- a/etc/nova/nova.conf.sample +++ b/etc/nova/nova.conf.sample @@ -1208,7 +1208,7 @@ #### "nova.scheduler.filters.standard_filters" maps to all #### filters included with nova. -# scheduler_default_filters=AvailabilityZoneFilter,RamFilter,ComputeFilter,ComputeCapabilitiesFilter +# scheduler_default_filters=AvailabilityZoneFilter,RamFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter #### (ListOpt) Which filter class names to use for filtering hosts when not #### specified in the request. diff --git a/nova/scheduler/filters/arch_filter.py b/nova/scheduler/filters/arch_filter.py deleted file mode 100644 index 625ce2909..000000000 --- a/nova/scheduler/filters/arch_filter.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2011-2012 OpenStack, LLC -# Copyright (c) 2012 Canonical Ltd -# 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.openstack.common import log as logging -from nova.scheduler import filters -from nova import utils - - -LOG = logging.getLogger(__name__) - - -class ArchFilter(filters.BaseHostFilter): - """Filter out hosts that can not support the guest architecture. - Note: This is supported for libvirt only now. - """ - - def host_passes(self, host_state, filter_properties): - spec = filter_properties.get('request_spec', {}) - props = spec.get('instance_properties', {}) - - cpu_info = host_state.capabilities.get('cpu_info') - permitted_instances = cpu_info.get('permitted_instance_types', None) - - instance_arch = utils.sys_platform_translate(props.get('architecture')) - - if permitted_instances and instance_arch in permitted_instances: - return True - - LOG.warn(_('%(host_state)s fails permitted_instance_types'), locals()) - return False diff --git a/nova/scheduler/filters/compute_filter.py b/nova/scheduler/filters/compute_filter.py index c0ee98762..2d7c898d6 100644 --- a/nova/scheduler/filters/compute_filter.py +++ b/nova/scheduler/filters/compute_filter.py @@ -22,59 +22,10 @@ LOG = logging.getLogger(__name__) class ComputeFilter(filters.BaseHostFilter): - """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 + """Filter on active Compute nodes""" def host_passes(self, host_state, filter_properties): - """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', {}) + """Returns True for only active compute nodes""" instance_type = filter_properties.get('instance_type') if host_state.topic != 'compute' or not instance_type: return True @@ -89,8 +40,4 @@ 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/scheduler/filters/image_props_filter.py b/nova/scheduler/filters/image_props_filter.py new file mode 100644 index 000000000..5eb710882 --- /dev/null +++ b/nova/scheduler/filters/image_props_filter.py @@ -0,0 +1,86 @@ +# Copyright (c) 2011-2012 OpenStack, LLC +# Copyright (c) 2012 Canonical Ltd +# Copyright (c) 2012 SUSE LINUX Products GmbH +# 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.openstack.common import log as logging +from nova.scheduler import filters +from nova import utils + + +LOG = logging.getLogger(__name__) + + +class ImagePropertiesFilter(filters.BaseHostFilter): + """Filter compute nodes that satisfy instance image properties. + + The ImagePropertiesFilter filters compute nodes that satisfy + any architecture, hpervisor type, or virtual machine mode properties + specified on the instance's image properties. Image properties are + contained in the image dictionary in the request_spec. + """ + + def _instance_supported(self, capabilities, image_props): + img_arch = image_props.get('architecture', None) + img_h_type = image_props.get('hypervisor_type', None) + img_vm_mode = image_props.get('vm_mode', None) + checked_img_props = (img_arch, img_h_type, img_vm_mode) + + # Supported if no compute-related instance properties are specified + if not any(checked_img_props): + 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 %(image_props)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(checked_img_props, supp_inst): + LOG.debug(_("Instance properties %(image_props)s " + "are satisfied by compute host capabilities " + "%(capabilities)s"), locals()) + return True + + LOG.debug(_("Instance contains properties %(image_props)s " + "that are not provided by the compute node " + "capabilities %(capabilities)s"), locals()) + return False + + def host_passes(self, host_state, filter_properties): + """Check if host passes specified image properties. + + Returns True for compute nodes that satisfy image properties + contained in the request_spec. + """ + spec = filter_properties.get('request_spec', {}) + image_props = spec.get('image', {}).get('properties', {}) + capabilities = host_state.capabilities + + if not self._instance_supported(capabilities, image_props): + LOG.debug(_("%(host_state)s does not support requested " + "instance_properties"), locals()) + return False + return True diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py index f373a2ef3..33ba2c160 100644 --- a/nova/scheduler/host_manager.py +++ b/nova/scheduler/host_manager.py @@ -47,7 +47,8 @@ host_manager_opts = [ 'AvailabilityZoneFilter', 'RamFilter', 'ComputeFilter', - 'ComputeCapabilitiesFilter' + 'ComputeCapabilitiesFilter', + 'ImagePropertiesFilter' ], help='Which filter class names to use for filtering hosts ' 'when not specified in the request.'), diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index 5e42fed6f..49486162a 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -412,104 +412,81 @@ class HostFiltersTestCase(test.TestCase): 'service': service}) self.assertTrue(filt_cls.host_passes(host, filter_properties)) - def test_compute_filter_passes_same_inst_props(self): + def test_image_properties_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} + filt_cls = self.class_map['ImagePropertiesFilter']() + img_props = {'properties': {'_architecture': 'x86_64', + 'hypervisor_type': 'kvm', + 'vm_mode': 'hvm'}} + filter_properties = {'request_spec': {'image': img_props}} 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}) + {'capabilities': capabilities}) self.assertTrue(filt_cls.host_passes(host, filter_properties)) - def test_compute_filter_fails_different_inst_props(self): + def test_image_properties_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} + filt_cls = self.class_map['ImagePropertiesFilter']() + img_props = {'properties': {'architecture': 'arm', + 'hypervisor_type': 'qemu', + 'vm_mode': 'hvm'}} + filter_properties = {'request_spec': {'image': img_props}} 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}) + {'capabilities': capabilities}) self.assertFalse(filt_cls.host_passes(host, filter_properties)) - def test_compute_filter_passes_partial_inst_props(self): + def test_image_properties_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} + filt_cls = self.class_map['ImagePropertiesFilter']() + img_props = {'properties': {'architecture': 'x86_64', + 'vm_mode': 'hvm'}} + filter_properties = {'request_spec': {'image': img_props}} 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}) + {'capabilities': capabilities}) self.assertTrue(filt_cls.host_passes(host, filter_properties)) - def test_compute_filter_fails_partial_inst_props(self): + def test_image_properties_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} + filt_cls = self.class_map['ImagePropertiesFilter']() + img_props = {'properties': {'architecture': 'x86_64', + 'vm_mode': 'hvm'}} + filter_properties = {'request_spec': {'image': img_props}} 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}) + {'capabilities': capabilities}) self.assertFalse(filt_cls.host_passes(host, filter_properties)) - def test_compute_filter_passes_without_inst_props(self): + def test_image_properties_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': {}} + filt_cls = self.class_map['ImagePropertiesFilter']() + filter_properties = {'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}) + {'capabilities': capabilities}) self.assertTrue(filt_cls.host_passes(host, filter_properties)) - def test_compute_filter_fails_without_host_props(self): + def test_image_properties_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} + filt_cls = self.class_map['ImagePropertiesFilter']() + img_props = {'properties': {'architecture': 'x86_64', + 'hypervisor_type': 'kvm', + 'vm_mode': 'hvm'}} + filter_properties = {'request_spec': {'image': img_props}} capabilities = {'enabled': True} - service = {'disabled': False} host = fakes.FakeHostState('host1', 'compute', - {'free_ram_mb': 1024, 'capabilities': capabilities, - 'service': service}) + {'capabilities': capabilities}) self.assertFalse(filt_cls.host_passes(host, filter_properties)) def _do_test_compute_filter_extra_specs(self, ecaps, especs, passes): @@ -1256,60 +1233,6 @@ class HostFiltersTestCase(test.TestCase): host = fakes.FakeHostState('host1', 'compute', {'service': service}) self.assertFalse(filt_cls.host_passes(host, request)) - def test_arch_filter_same(self): - permitted_instances = ['x86_64'] - filt_cls = self.class_map['ArchFilter']() - filter_properties = { - 'request_spec': { - 'instance_properties': {'architecture': 'x86_64'} - } - } - capabilities = {'enabled': True, - 'cpu_info': { - 'permitted_instance_types': permitted_instances - } - } - service = {'disabled': False} - host = fakes.FakeHostState('host1', 'compute', - {'capabilities': capabilities, 'service': service}) - self.assertTrue(filt_cls.host_passes(host, filter_properties)) - - def test_arch_filter_different(self): - permitted_instances = ['arm'] - filt_cls = self.class_map['ArchFilter']() - filter_properties = { - 'request_spec': { - 'instance_properties': {'architecture': 'x86_64'} - } - } - capabilities = {'enabled': True, - 'cpu_info': { - 'permitted_instance_types': permitted_instances - } - } - service = {'disabled': False} - host = fakes.FakeHostState('host1', 'compute', - {'capabilities': capabilities, 'service': service}) - self.assertFalse(filt_cls.host_passes(host, filter_properties)) - - def test_arch_filter_without_permitted_instances(self): - permitted_instances = [] - filt_cls = self.class_map['ArchFilter']() - filter_properties = { - 'request_spec': { - 'instance_properties': {'architecture': 'x86_64'} - } - } - capabilities = {'enabled': True, - 'cpu_info': { - 'permitted_instance_types': permitted_instances - } - } - service = {'disabled': False} - host = fakes.FakeHostState('host1', 'compute', - {'capabilities': capabilities, 'service': service}) - self.assertFalse(filt_cls.host_passes(host, filter_properties)) - def test_retry_filter_disabled(self): """Test case where retry/re-scheduling is disabled""" filt_cls = self.class_map['RetryFilter']() |
