Hi Daniel, Attached is a patch that implements filtering on (architecture, hypervisor_type, vm_mode) tuple as was discussed in this previous patch
https://review.openstack.org/#/c/9110/ CC'ing Chuck since he is the author of the ArchFilter patch. Feedback appreciated before sending this off to gerrit. Regards, Jim
>From bc96fdf618a2b9426f4c5db59fc087f849ac9873 Mon Sep 17 00:00:00 2001 From: Jim Fehlig <jfeh...@suse.com> Date: Mon, 25 Jun 2012 15:54:43 -0600 Subject: [PATCH] Add more host checks to the compute filter As discussed in a previous version of this patch [1], this change adds checks in the ComputeFilter to verify hosts can support the (architecture, hypervisor_type, vm_mode) tuple specified in the instance properties. Adding these checks to the compute filter seems consistent with its definition [2]: "ComputeFilter - checks that the capabilities provided by the compute service satisfy the extra specifications, associated with the instance type." [1] https://review.openstack.org/#/c/9110/ [2] https://github.com/openstack/nova/blob/master/doc/source/devref/filter_scheduler.rst Change-Id: I1fcd7f9c706184701ca02f7d1672541d26c07f31 --- nova/compute/api.py | 4 +- .../versions/108_instance_hypervisor_type.py | 46 ++++++ nova/db/sqlalchemy/models.py | 1 + nova/scheduler/filters/arch_filter.py | 44 ------ nova/scheduler/filters/compute_filter.py | 56 ++++++- nova/tests/scheduler/test_host_filters.py | 160 +++++++++++++------- 6 files changed, 211 insertions(+), 100 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 1e3ebf1..008bdd6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -323,7 +323,9 @@ class API(base.Base): return value options_from_image = {'os_type': prop('os_type'), - 'vm_mode': prop('vm_mode')} + 'architecture': prop('architecture'), + 'vm_mode': prop('vm_mode'), + 'hypervisor_type': prop('hypervisor_type')} # If instance doesn't have auto_disk_config overridden by request, use # whatever the image indicates diff --git a/nova/db/sqlalchemy/migrate_repo/versions/108_instance_hypervisor_type.py b/nova/db/sqlalchemy/migrate_repo/versions/108_instance_hypervisor_type.py new file mode 100644 index 0000000..f68a6a4 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/108_instance_hypervisor_type.py @@ -0,0 +1,46 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 OpenStack LLC. +# +# 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 sqlalchemy import Column, Integer, MetaData, String, Table + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + # add column: + instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False) + ) + hypervisor_type = Column('hypervisor_type', + String(length=255, convert_unicode=False, + assert_unicode=None, unicode_error=None, + _warn_on_bytestring=False), + nullable=True) + + instances.create_column(hypervisor_type) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + # drop column: + instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False) + ) + + instances.drop_column('hypervisor_type') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 3359891..30f23e6 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -253,6 +253,7 @@ class Instance(BASE, NovaBase): os_type = Column(String(255)) architecture = Column(String(255)) + hypervisor_type = Column(String(255)) vm_mode = Column(String(255)) uuid = Column(String(36)) diff --git a/nova/scheduler/filters/arch_filter.py b/nova/scheduler/filters/arch_filter.py deleted file mode 100644 index 1f11d07..0000000 --- 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 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 5409d3d..bf67b96 100644 --- a/nova/scheduler/filters/compute_filter.py +++ b/nova/scheduler/filters/compute_filter.py @@ -22,7 +22,8 @@ LOG = logging.getLogger(__name__) class ComputeFilter(filters.BaseHostFilter): - """HostFilter hard-coded to work with InstanceType records.""" + """HostFilter hard-coded to work with InstanceType and + InstanceProperties records.""" def _satisfies_extra_specs(self, capabilities, instance_type): """Check that the capabilities provided by the compute service @@ -38,8 +39,49 @@ class ComputeFilter(filters.BaseHostFilter): return False return True + def _satisfies_arch(self, capabilities, instance_props): + """Check that the compute service satisfies the requested + instance architecture.""" + inst_arch = instance_props.get('architecture') + if not inst_arch: + return True + + inst_arch = utils.sys_platform_translate(inst_arch) + cpu_info = capabilities.get('cpu_info') + if not cpu_info: + return False + + permitted_instances = cpu_info.get('permitted_instance_types', None) + if permitted_instances and inst_arch in permitted_instances: + return True + return False + + def _satisfies_hypervisor(self, capabilities, instance_props): + """Check that the compute service satisfies the requested + instance hypervisor.""" + inst_h_type = instance_props.get('hypervisor_type', None) + if not inst_h_type: + return True + + if inst_h_type == capabilities.get('hypervisor_type', None): + return True + return False + + def _satisfies_vm_mode(self, capabilities, instance_props): + """Check that the compute service satisfies the requested + instance vm_mode.""" + inst_vm_mode = instance_props.get('vm_mode', None) + if not inst_vm_mode: + return True + + if capabilities.get(inst_vm_mode, None): + return True + return False + def host_passes(self, host_state, filter_properties): """Return a list of hosts that can create instance_type.""" + spec = filter_properties.get('request_spec', {}) + instance_props = spec.get('instance_properties', {}) instance_type = filter_properties.get('instance_type') if host_state.topic != 'compute' or not instance_type: return True @@ -57,4 +99,16 @@ class ComputeFilter(filters.BaseHostFilter): LOG.debug(_("%(host_state)s fails instance_type extra_specs " "requirements"), locals()) return False + if not self._satisfies_arch(capabilities, instance_props): + LOG.debug(_("%(host_state)s fails instance_properties " + "architecture requirement"), locals()) + return False + if not self._satisfies_hypervisor(capabilities, instance_props): + LOG.debug(_("%(host_state)s fails instance_properties " + "hypervisor requirement"), locals()) + return False + if not self._satisfies_vm_mode(capabilities, instance_props): + LOG.debug(_("%(host_state)s fails instance_properties " + "vm_mode requirement"), locals()) + return False return True diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index c6fabc1..9dd3410 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -387,6 +387,112 @@ class HostFiltersTestCase(test.TestCase): self.assertFalse(filt_cls.host_passes(host, filter_properties)) + def test_compute_filter_passes_same_arch(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['ComputeFilter']() + req_spec = {'instance_properties': {'architecture': 'x86_64'}} + permitted_instances = ['x86_64'] + filter_properties = {'instance_type': {'memory_mb': 1024}, + 'request_spec': req_spec} + capabilities = {'enabled': True, + 'cpu_info': { + 'permitted_instance_types': permitted_instances + } + } + 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_arch(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['ComputeFilter']() + req_spec = {'instance_properties': {'architecture': 'x86_64'}} + permitted_instances = ['arm'] + filter_properties = {'instance_type': {'memory_mb': 1024}, + 'request_spec': req_spec} + capabilities = {'enabled': True, + 'cpu_info': { + 'permitted_instance_types': permitted_instances + } + } + 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_fails_without_requested_arch(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['ComputeFilter']() + req_spec = {'instance_properties': {'architecture': 'x86_64'}} + permitted_instances = [] + filter_properties = {'instance_type': {'memory_mb': 1024}, + 'request_spec': req_spec} + capabilities = {'enabled': True, + 'cpu_info': { + 'permitted_instance_types': permitted_instances + } + } + 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_same_hypervisor(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['ComputeFilter']() + req_spec = {'instance_properties': {'hypervisor_type': 'KVM'}} + capabilities = {'enabled': True, 'hypervisor_type': 'KVM'} + service = {'disabled': False} + filter_properties = {'instance_type': {'memory_mb': 1024}, + 'request_spec': req_spec} + 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_hypervisor(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['ComputeFilter']() + req_spec = {'instance_properties': {'hypervisor_type': 'KVM'}} + capabilities = {'enabled': True, 'hypervisor_type': 'Xen'} + service = {'disabled': False} + filter_properties = {'instance_type': {'memory_mb': 1024}, + 'request_spec': req_spec} + 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_same_vmmode(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['ComputeFilter']() + req_spec = {'instance_properties': {'vm_mode': 'pv'}} + capabilities = {'enabled': True, 'pv': True, 'hvm': True} + service = {'disabled': False} + filter_properties = {'instance_type': {'memory_mb': 1024}, + 'request_spec': req_spec} + 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_vmmode(self): + self._stub_service_is_up(True) + filt_cls = self.class_map['ComputeFilter']() + req_spec = {'instance_properties': {'vm_mode': 'pv'}} + capabilities = {'enabled': True} + service = {'disabled': False} + filter_properties = {'instance_type': {'memory_mb': 1024}, + 'request_spec': req_spec} + host = fakes.FakeHostState('host1', 'compute', + {'free_ram_mb': 1024, 'capabilities': capabilities, + 'service': service}) + self.assertFalse(filt_cls.host_passes(host, filter_properties)) + def test_isolated_hosts_fails_isolated_on_non_isolated(self): self.flags(isolated_images=['isolated'], isolated_hosts=['isolated']) filt_cls = self.class_map['IsolatedHostsFilter']() @@ -831,57 +937,3 @@ class HostFiltersTestCase(test.TestCase): request = self._make_zone_request('bad') 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)) -- 1.7.10.4
_______________________________________________ Mailing list: https://launchpad.net/~openstack Post to : openstack@lists.launchpad.net Unsubscribe : https://launchpad.net/~openstack More help : https://help.launchpad.net/ListHelp