diff --git a/nova/scheduler/filters/aggregate_image_properties_isolation.py b/nova/scheduler/filters/aggregate_image_properties_isolation.py index 623c5db52..af13d268f 100644 --- a/nova/scheduler/filters/aggregate_image_properties_isolation.py +++ b/nova/scheduler/filters/aggregate_image_properties_isolation.py @@ -40,16 +40,14 @@ class AggregateImagePropertiesIsolation(filters.BaseHostFilter): # Aggregate data and instance type does not change within a request run_filter_once_per_request = True - @filters.compat_legacy_props - def host_passes(self, host_state, filter_properties): + def host_passes(self, host_state, spec_obj): """Checks a host in an aggregate that metadata key/value match with image properties. """ cfg_namespace = CONF.aggregate_image_properties_isolation_namespace cfg_separator = CONF.aggregate_image_properties_isolation_separator - spec = filter_properties.get('request_spec', {}) - image_props = spec.get('image', {}).get('properties', {}) + image_props = spec_obj.image.properties if spec_obj.image else {} metadata = utils.aggregate_metadata_get_by_host(host_state) for key, options in six.iteritems(metadata): @@ -57,7 +55,10 @@ class AggregateImagePropertiesIsolation(filters.BaseHostFilter): not key.startswith(cfg_namespace + cfg_separator)): continue prop = image_props.get(key) - if prop and prop not in options: + # NOTE(sbauza): Aggregate metadata is only strings, we need to + # stringify the property to match with the option + # TODO(sbauza): Fix that very ugly pattern matching + if prop and str(prop) not in options: LOG.debug("%(host_state)s fails image aggregate properties " "requirements. Property %(prop)s does not " "match %(options)s.", diff --git a/nova/scheduler/filters/aggregate_instance_extra_specs.py b/nova/scheduler/filters/aggregate_instance_extra_specs.py index 5c283bda7..b57bcaf17 100644 --- a/nova/scheduler/filters/aggregate_instance_extra_specs.py +++ b/nova/scheduler/filters/aggregate_instance_extra_specs.py @@ -33,22 +33,22 @@ class AggregateInstanceExtraSpecsFilter(filters.BaseHostFilter): # Aggregate data and instance type does not change within a request run_filter_once_per_request = True - @filters.compat_legacy_props - def host_passes(self, host_state, filter_properties): + def host_passes(self, host_state, spec_obj): """Return a list of hosts that can create instance_type Check that the extra specs associated with the instance type match the metadata provided by aggregates. If not present return False. """ - instance_type = filter_properties.get('instance_type') + instance_type = spec_obj.flavor # If 'extra_specs' is not present or extra_specs are empty then we # need not proceed further - if not instance_type.get('extra_specs'): + if (not instance_type.obj_attr_is_set('extra_specs') + or not instance_type.extra_specs): return True metadata = utils.aggregate_metadata_get_by_host(host_state) - for key, req in six.iteritems(instance_type['extra_specs']): + for key, req in six.iteritems(instance_type.extra_specs): # Either not scope format, or aggregate_instance_extra_specs scope scope = key.split(':', 1) if len(scope) > 1: diff --git a/nova/scheduler/filters/aggregate_multitenancy_isolation.py b/nova/scheduler/filters/aggregate_multitenancy_isolation.py index 32124f76c..2e2278741 100644 --- a/nova/scheduler/filters/aggregate_multitenancy_isolation.py +++ b/nova/scheduler/filters/aggregate_multitenancy_isolation.py @@ -28,8 +28,7 @@ class AggregateMultiTenancyIsolation(filters.BaseHostFilter): # Aggregate data and tenant do not change within a request run_filter_once_per_request = True - @filters.compat_legacy_props - def host_passes(self, host_state, filter_properties): + def host_passes(self, host_state, spec_obj): """If a host is in an aggregate that has the metadata key "filter_tenant_id" it can only create instances from that tenant(s). A host can be in different aggregates. @@ -37,9 +36,7 @@ class AggregateMultiTenancyIsolation(filters.BaseHostFilter): If a host doesn't belong to an aggregate with the metadata key "filter_tenant_id" it can create instances from all tenants. """ - spec = filter_properties.get('request_spec', {}) - props = spec.get('instance_properties', {}) - tenant_id = props.get('project_id') + tenant_id = spec_obj.project_id metadata = utils.aggregate_metadata_get_by_host(host_state, key="filter_tenant_id") diff --git a/nova/tests/unit/scheduler/filters/test_aggregate_image_properties_isolation_filters.py b/nova/tests/unit/scheduler/filters/test_aggregate_image_properties_isolation_filters.py index 2cb829d0d..025de7795 100644 --- a/nova/tests/unit/scheduler/filters/test_aggregate_image_properties_isolation_filters.py +++ b/nova/tests/unit/scheduler/filters/test_aggregate_image_properties_isolation_filters.py @@ -12,6 +12,7 @@ import mock +from nova import objects from nova.scheduler.filters import aggregate_image_properties_isolation as aipi from nova import test from nova.tests.unit.scheduler import fakes @@ -25,83 +26,80 @@ class TestAggImagePropsIsolationFilter(test.NoDBTestCase): self.filt_cls = aipi.AggregateImagePropertiesIsolation() def test_aggregate_image_properties_isolation_passes(self, agg_mock): - agg_mock.return_value = {'foo': 'bar'} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {'foo': 'bar'}}}} + agg_mock.return_value = {'hw_vm_mode': 'hvm'} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps( + hw_vm_mode='hvm'))) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_image_properties_isolation_passes_comma(self, agg_mock): - agg_mock.return_value = {'foo': 'bar,bar2'} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {'foo': 'bar'}}}} + agg_mock.return_value = {'hw_vm_mode': 'hvm,xen'} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps( + hw_vm_mode='hvm'))) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_image_properties_isolation_multi_props_passes(self, agg_mock): - agg_mock.return_value = {'foo': 'bar', 'foo2': 'bar2'} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {'foo': 'bar', - 'foo2': 'bar2'}}}} + agg_mock.return_value = {'hw_vm_mode': 'hvm', 'hw_cpu_cores': '2'} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps( + hw_vm_mode='hvm', hw_cpu_cores=2))) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_image_properties_isolation_props_with_meta_passes(self, agg_mock): - agg_mock.return_value = {'foo': 'bar'} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {}}}} + agg_mock.return_value = {'hw_vm_mode': 'hvm'} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps())) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_image_properties_isolation_props_imgprops_passes(self, agg_mock): agg_mock.return_value = {} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {'foo': 'bar'}}}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps( + hw_vm_mode='hvm'))) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_image_properties_isolation_props_not_match_fails(self, agg_mock): - agg_mock.return_value = {'foo': 'bar'} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {'foo': 'no-bar'}}}} + agg_mock.return_value = {'hw_vm_mode': 'hvm'} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps( + hw_vm_mode='xen'))) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertFalse(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_image_properties_isolation_props_not_match2_fails(self, agg_mock): - agg_mock.return_value = {'foo': 'bar', 'foo2': 'bar2'} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {'foo': 'bar', - 'foo2': 'bar3'}}}} + agg_mock.return_value = {'hw_vm_mode': 'hvm', 'hw_cpu_cores': '1'} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps( + hw_vm_mode='hvm', hw_cpu_cores=2))) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertFalse(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_image_properties_isolation_props_namespace(self, agg_mock): - self.flags(aggregate_image_properties_isolation_namespace="np") - agg_mock.return_value = {'np.foo': 'bar', 'foo2': 'bar2'} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'image': { - 'properties': {'np.foo': 'bar', - 'foo2': 'bar3'}}}} + self.flags(aggregate_image_properties_isolation_namespace="hw") + self.flags(aggregate_image_properties_isolation_separator="_") + agg_mock.return_value = {'hw_vm_mode': 'hvm', 'img_owner_id': 'foo'} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + image=objects.ImageMeta(properties=objects.ImageMetaProps( + hw_vm_mode='hvm', img_owner_id='wrong'))) host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) diff --git a/nova/tests/unit/scheduler/filters/test_aggregate_instance_extra_specs_filters.py b/nova/tests/unit/scheduler/filters/test_aggregate_instance_extra_specs_filters.py index efffab8e1..51d1cab12 100644 --- a/nova/tests/unit/scheduler/filters/test_aggregate_instance_extra_specs_filters.py +++ b/nova/tests/unit/scheduler/filters/test_aggregate_instance_extra_specs_filters.py @@ -12,6 +12,7 @@ import mock +from nova import objects from nova.scheduler.filters import aggregate_instance_extra_specs as agg_specs from nova import test from nova.tests.unit.scheduler import fakes @@ -27,28 +28,31 @@ class TestAggregateInstanceExtraSpecsFilter(test.NoDBTestCase): def test_aggregate_filter_passes_no_extra_specs(self, agg_mock): capabilities = {'opt1': 1, 'opt2': 2} - filter_properties = {'context': mock.sentinel.ctx, 'instance_type': - {'memory_mb': 1024}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + flavor=objects.Flavor(memory_mb=1024)) host = fakes.FakeHostState('host1', 'node1', capabilities) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) self.assertFalse(agg_mock.called) def test_aggregate_filter_passes_empty_extra_specs(self, agg_mock): capabilities = {'opt1': 1, 'opt2': 2} - filter_properties = {'context': mock.sentinel.ctx, 'instance_type': - {'memory_mb': 1024, 'extra_specs': {}}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + flavor=objects.Flavor(memory_mb=1024, extra_specs={})) host = fakes.FakeHostState('host1', 'node1', capabilities) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) self.assertFalse(agg_mock.called) def _do_test_aggregate_filter_extra_specs(self, especs, passes): - filter_properties = {'context': mock.sentinel.ctx, - 'instance_type': {'memory_mb': 1024, 'extra_specs': especs}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, + flavor=objects.Flavor(memory_mb=1024, extra_specs=especs)) host = fakes.FakeHostState('host1', 'node1', {'free_ram_mb': 1024}) assertion = self.assertTrue if passes else self.assertFalse - assertion(self.filt_cls.host_passes(host, filter_properties)) + assertion(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_filter_passes_extra_specs_simple(self, agg_mock): agg_mock.return_value = {'opt1': '1', 'opt2': '2'} diff --git a/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py b/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py index 0316c2e5c..6e6ae9a42 100644 --- a/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py +++ b/nova/tests/unit/scheduler/filters/test_aggregate_multitenancy_isolation_filters.py @@ -12,6 +12,7 @@ import mock +from nova import objects from nova.scheduler.filters import aggregate_multitenancy_isolation as ami from nova import test from nova.tests.unit.scheduler import fakes @@ -27,48 +28,38 @@ class TestAggregateMultitenancyIsolationFilter(test.NoDBTestCase): def test_aggregate_multi_tenancy_isolation_with_meta_passes(self, agg_mock): agg_mock.return_value = {'filter_tenant_id': set(['my_tenantid'])} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'instance_properties': { - 'project_id': 'my_tenantid'}}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, project_id='my_tenantid') host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_multi_tenancy_isolation_with_meta_passes_comma(self, agg_mock): agg_mock.return_value = {'filter_tenant_id': set(['my_tenantid', 'mytenantid2'])} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'instance_properties': { - 'project_id': 'my_tenantid'}}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, project_id='my_tenantid') host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_multi_tenancy_isolation_fails(self, agg_mock): agg_mock.return_value = {'filter_tenant_id': set(['other_tenantid'])} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'instance_properties': { - 'project_id': 'my_tenantid'}}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, project_id='my_tenantid') host = fakes.FakeHostState('host1', 'compute', {}) - self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertFalse(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_multi_tenancy_isolation_fails_comma(self, agg_mock): agg_mock.return_value = {'filter_tenant_id': set(['other_tenantid', 'other_tenantid2'])} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'instance_properties': { - 'project_id': 'my_tenantid'}}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, project_id='my_tenantid') host = fakes.FakeHostState('host1', 'compute', {}) - self.assertFalse(self.filt_cls.host_passes(host, filter_properties)) + self.assertFalse(self.filt_cls.host_passes(host, spec_obj)) def test_aggregate_multi_tenancy_isolation_no_meta_passes(self, agg_mock): agg_mock.return_value = {} - filter_properties = {'context': mock.sentinel.ctx, - 'request_spec': { - 'instance_properties': { - 'project_id': 'my_tenantid'}}} + spec_obj = objects.RequestSpec( + context=mock.sentinel.ctx, project_id='my_tenantid') host = fakes.FakeHostState('host1', 'compute', {}) - self.assertTrue(self.filt_cls.host_passes(host, filter_properties)) + self.assertTrue(self.filt_cls.host_passes(host, spec_obj))