Modify Aggregate filters for RequestSpec

Change-Id: I032d718fbe034ee898c661acd7bbadb6eb73e2f9
Partially-Implements: blueprint request-spec-object-mitaka
This commit is contained in:
Sylvain Bauza
2015-07-16 13:38:16 +02:00
parent 15389435aa
commit 5fbc515f27
6 changed files with 91 additions and 100 deletions

View File

@@ -40,16 +40,14 @@ class AggregateImagePropertiesIsolation(filters.BaseHostFilter):
# Aggregate data and instance type does not change within a request # Aggregate data and instance type does not change within a request
run_filter_once_per_request = True run_filter_once_per_request = True
@filters.compat_legacy_props def host_passes(self, host_state, spec_obj):
def host_passes(self, host_state, filter_properties):
"""Checks a host in an aggregate that metadata key/value match """Checks a host in an aggregate that metadata key/value match
with image properties. with image properties.
""" """
cfg_namespace = CONF.aggregate_image_properties_isolation_namespace cfg_namespace = CONF.aggregate_image_properties_isolation_namespace
cfg_separator = CONF.aggregate_image_properties_isolation_separator cfg_separator = CONF.aggregate_image_properties_isolation_separator
spec = filter_properties.get('request_spec', {}) image_props = spec_obj.image.properties if spec_obj.image else {}
image_props = spec.get('image', {}).get('properties', {})
metadata = utils.aggregate_metadata_get_by_host(host_state) metadata = utils.aggregate_metadata_get_by_host(host_state)
for key, options in six.iteritems(metadata): for key, options in six.iteritems(metadata):
@@ -57,7 +55,10 @@ class AggregateImagePropertiesIsolation(filters.BaseHostFilter):
not key.startswith(cfg_namespace + cfg_separator)): not key.startswith(cfg_namespace + cfg_separator)):
continue continue
prop = image_props.get(key) 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 " LOG.debug("%(host_state)s fails image aggregate properties "
"requirements. Property %(prop)s does not " "requirements. Property %(prop)s does not "
"match %(options)s.", "match %(options)s.",

View File

@@ -33,22 +33,22 @@ class AggregateInstanceExtraSpecsFilter(filters.BaseHostFilter):
# Aggregate data and instance type does not change within a request # Aggregate data and instance type does not change within a request
run_filter_once_per_request = True run_filter_once_per_request = True
@filters.compat_legacy_props def host_passes(self, host_state, spec_obj):
def host_passes(self, host_state, filter_properties):
"""Return a list of hosts that can create instance_type """Return a list of hosts that can create instance_type
Check that the extra specs associated with the instance type match Check that the extra specs associated with the instance type match
the metadata provided by aggregates. If not present return False. 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 # If 'extra_specs' is not present or extra_specs are empty then we
# need not proceed further # 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 return True
metadata = utils.aggregate_metadata_get_by_host(host_state) 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 # Either not scope format, or aggregate_instance_extra_specs scope
scope = key.split(':', 1) scope = key.split(':', 1)
if len(scope) > 1: if len(scope) > 1:

View File

@@ -28,8 +28,7 @@ class AggregateMultiTenancyIsolation(filters.BaseHostFilter):
# Aggregate data and tenant do not change within a request # Aggregate data and tenant do not change within a request
run_filter_once_per_request = True run_filter_once_per_request = True
@filters.compat_legacy_props def host_passes(self, host_state, spec_obj):
def host_passes(self, host_state, filter_properties):
"""If a host is in an aggregate that has the metadata key """If a host is in an aggregate that has the metadata key
"filter_tenant_id" it can only create instances from that tenant(s). "filter_tenant_id" it can only create instances from that tenant(s).
A host can be in different aggregates. 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 If a host doesn't belong to an aggregate with the metadata key
"filter_tenant_id" it can create instances from all tenants. "filter_tenant_id" it can create instances from all tenants.
""" """
spec = filter_properties.get('request_spec', {}) tenant_id = spec_obj.project_id
props = spec.get('instance_properties', {})
tenant_id = props.get('project_id')
metadata = utils.aggregate_metadata_get_by_host(host_state, metadata = utils.aggregate_metadata_get_by_host(host_state,
key="filter_tenant_id") key="filter_tenant_id")

View File

@@ -12,6 +12,7 @@
import mock import mock
from nova import objects
from nova.scheduler.filters import aggregate_image_properties_isolation as aipi from nova.scheduler.filters import aggregate_image_properties_isolation as aipi
from nova import test from nova import test
from nova.tests.unit.scheduler import fakes from nova.tests.unit.scheduler import fakes
@@ -25,83 +26,80 @@ class TestAggImagePropsIsolationFilter(test.NoDBTestCase):
self.filt_cls = aipi.AggregateImagePropertiesIsolation() self.filt_cls = aipi.AggregateImagePropertiesIsolation()
def test_aggregate_image_properties_isolation_passes(self, agg_mock): def test_aggregate_image_properties_isolation_passes(self, agg_mock):
agg_mock.return_value = {'foo': 'bar'} agg_mock.return_value = {'hw_vm_mode': 'hvm'}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx,
'image': { image=objects.ImageMeta(properties=objects.ImageMetaProps(
'properties': {'foo': 'bar'}}}} hw_vm_mode='hvm')))
host = fakes.FakeHostState('host1', 'compute', {}) 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): def test_aggregate_image_properties_isolation_passes_comma(self, agg_mock):
agg_mock.return_value = {'foo': 'bar,bar2'} agg_mock.return_value = {'hw_vm_mode': 'hvm,xen'}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx,
'image': { image=objects.ImageMeta(properties=objects.ImageMetaProps(
'properties': {'foo': 'bar'}}}} hw_vm_mode='hvm')))
host = fakes.FakeHostState('host1', 'compute', {}) 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, def test_aggregate_image_properties_isolation_multi_props_passes(self,
agg_mock): agg_mock):
agg_mock.return_value = {'foo': 'bar', 'foo2': 'bar2'} agg_mock.return_value = {'hw_vm_mode': 'hvm', 'hw_cpu_cores': '2'}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx,
'image': { image=objects.ImageMeta(properties=objects.ImageMetaProps(
'properties': {'foo': 'bar', hw_vm_mode='hvm', hw_cpu_cores=2)))
'foo2': 'bar2'}}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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, def test_aggregate_image_properties_isolation_props_with_meta_passes(self,
agg_mock): agg_mock):
agg_mock.return_value = {'foo': 'bar'} agg_mock.return_value = {'hw_vm_mode': 'hvm'}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx,
'image': { image=objects.ImageMeta(properties=objects.ImageMetaProps()))
'properties': {}}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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, def test_aggregate_image_properties_isolation_props_imgprops_passes(self,
agg_mock): agg_mock):
agg_mock.return_value = {} agg_mock.return_value = {}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx,
'image': { image=objects.ImageMeta(properties=objects.ImageMetaProps(
'properties': {'foo': 'bar'}}}} hw_vm_mode='hvm')))
host = fakes.FakeHostState('host1', 'compute', {}) 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, def test_aggregate_image_properties_isolation_props_not_match_fails(self,
agg_mock): agg_mock):
agg_mock.return_value = {'foo': 'bar'} agg_mock.return_value = {'hw_vm_mode': 'hvm'}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx,
'image': { image=objects.ImageMeta(properties=objects.ImageMetaProps(
'properties': {'foo': 'no-bar'}}}} hw_vm_mode='xen')))
host = fakes.FakeHostState('host1', 'compute', {}) 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, def test_aggregate_image_properties_isolation_props_not_match2_fails(self,
agg_mock): agg_mock):
agg_mock.return_value = {'foo': 'bar', 'foo2': 'bar2'} agg_mock.return_value = {'hw_vm_mode': 'hvm', 'hw_cpu_cores': '1'}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx,
'image': { image=objects.ImageMeta(properties=objects.ImageMetaProps(
'properties': {'foo': 'bar', hw_vm_mode='hvm', hw_cpu_cores=2)))
'foo2': 'bar3'}}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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, def test_aggregate_image_properties_isolation_props_namespace(self,
agg_mock): agg_mock):
self.flags(aggregate_image_properties_isolation_namespace="np") self.flags(aggregate_image_properties_isolation_namespace="hw")
agg_mock.return_value = {'np.foo': 'bar', 'foo2': 'bar2'} self.flags(aggregate_image_properties_isolation_separator="_")
filter_properties = {'context': mock.sentinel.ctx, agg_mock.return_value = {'hw_vm_mode': 'hvm', 'img_owner_id': 'foo'}
'request_spec': { spec_obj = objects.RequestSpec(
'image': { context=mock.sentinel.ctx,
'properties': {'np.foo': 'bar', image=objects.ImageMeta(properties=objects.ImageMetaProps(
'foo2': 'bar3'}}}} hw_vm_mode='hvm', img_owner_id='wrong')))
host = fakes.FakeHostState('host1', 'compute', {}) 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))

View File

@@ -12,6 +12,7 @@
import mock import mock
from nova import objects
from nova.scheduler.filters import aggregate_instance_extra_specs as agg_specs from nova.scheduler.filters import aggregate_instance_extra_specs as agg_specs
from nova import test from nova import test
from nova.tests.unit.scheduler import fakes 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): def test_aggregate_filter_passes_no_extra_specs(self, agg_mock):
capabilities = {'opt1': 1, 'opt2': 2} capabilities = {'opt1': 1, 'opt2': 2}
filter_properties = {'context': mock.sentinel.ctx, 'instance_type': spec_obj = objects.RequestSpec(
{'memory_mb': 1024}} context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024))
host = fakes.FakeHostState('host1', 'node1', capabilities) 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) self.assertFalse(agg_mock.called)
def test_aggregate_filter_passes_empty_extra_specs(self, agg_mock): def test_aggregate_filter_passes_empty_extra_specs(self, agg_mock):
capabilities = {'opt1': 1, 'opt2': 2} capabilities = {'opt1': 1, 'opt2': 2}
filter_properties = {'context': mock.sentinel.ctx, 'instance_type': spec_obj = objects.RequestSpec(
{'memory_mb': 1024, 'extra_specs': {}}} context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024, extra_specs={}))
host = fakes.FakeHostState('host1', 'node1', capabilities) 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) self.assertFalse(agg_mock.called)
def _do_test_aggregate_filter_extra_specs(self, especs, passes): def _do_test_aggregate_filter_extra_specs(self, especs, passes):
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'instance_type': {'memory_mb': 1024, 'extra_specs': especs}} context=mock.sentinel.ctx,
flavor=objects.Flavor(memory_mb=1024, extra_specs=especs))
host = fakes.FakeHostState('host1', 'node1', host = fakes.FakeHostState('host1', 'node1',
{'free_ram_mb': 1024}) {'free_ram_mb': 1024})
assertion = self.assertTrue if passes else self.assertFalse 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): def test_aggregate_filter_passes_extra_specs_simple(self, agg_mock):
agg_mock.return_value = {'opt1': '1', 'opt2': '2'} agg_mock.return_value = {'opt1': '1', 'opt2': '2'}

View File

@@ -12,6 +12,7 @@
import mock import mock
from nova import objects
from nova.scheduler.filters import aggregate_multitenancy_isolation as ami from nova.scheduler.filters import aggregate_multitenancy_isolation as ami
from nova import test from nova import test
from nova.tests.unit.scheduler import fakes 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, def test_aggregate_multi_tenancy_isolation_with_meta_passes(self,
agg_mock): agg_mock):
agg_mock.return_value = {'filter_tenant_id': set(['my_tenantid'])} agg_mock.return_value = {'filter_tenant_id': set(['my_tenantid'])}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx, project_id='my_tenantid')
'instance_properties': {
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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, def test_aggregate_multi_tenancy_isolation_with_meta_passes_comma(self,
agg_mock): agg_mock):
agg_mock.return_value = {'filter_tenant_id': agg_mock.return_value = {'filter_tenant_id':
set(['my_tenantid', 'mytenantid2'])} set(['my_tenantid', 'mytenantid2'])}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx, project_id='my_tenantid')
'instance_properties': {
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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): def test_aggregate_multi_tenancy_isolation_fails(self, agg_mock):
agg_mock.return_value = {'filter_tenant_id': set(['other_tenantid'])} agg_mock.return_value = {'filter_tenant_id': set(['other_tenantid'])}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx, project_id='my_tenantid')
'instance_properties': {
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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): def test_aggregate_multi_tenancy_isolation_fails_comma(self, agg_mock):
agg_mock.return_value = {'filter_tenant_id': agg_mock.return_value = {'filter_tenant_id':
set(['other_tenantid', 'other_tenantid2'])} set(['other_tenantid', 'other_tenantid2'])}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx, project_id='my_tenantid')
'instance_properties': {
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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): def test_aggregate_multi_tenancy_isolation_no_meta_passes(self, agg_mock):
agg_mock.return_value = {} agg_mock.return_value = {}
filter_properties = {'context': mock.sentinel.ctx, spec_obj = objects.RequestSpec(
'request_spec': { context=mock.sentinel.ctx, project_id='my_tenantid')
'instance_properties': {
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {}) 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))