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
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.",

View File

@ -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:

View File

@ -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")

View File

@ -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))

View File

@ -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'}

View File

@ -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))