Merge "Support specifing multiple values for aggregate keys"

This commit is contained in:
Jenkins
2015-03-03 14:34:31 +00:00
committed by Gerrit Code Review
7 changed files with 74 additions and 10 deletions
+7 -2
View File
@@ -32,6 +32,8 @@ There are some standard filter classes to use (:mod:`nova.scheduler.filters`):
image properties contained in the instance.
* |AvailabilityZoneFilter| - filters hosts by availability zone. It passes
hosts matching the availability zone specified in the instance properties.
Use a comma to specify multiple zones. The filter will then ensure it matches
any zone specified.
* |ComputeCapabilitiesFilter| - checks that the capabilities provided by the
host compute service satisfy any extra specifications associated with the
instance type. It passes hosts that can create the specified instance type.
@@ -71,7 +73,8 @@ There are some standard filter classes to use (:mod:`nova.scheduler.filters`):
have no scope or are scoped with ``aggregate_instance_extra_specs``).
It passes hosts that can create the specified instance type.
The extra specifications can have the same operators as
|ComputeCapabilitiesFilter|.
|ComputeCapabilitiesFilter|. To specify multiple values for the same key
use a comma. E.g., "value1,value2"
* |ComputeFilter| - passes all hosts that are operational and enabled.
* |CoreFilter| - filters based on CPU core utilization. It passes hosts with
sufficient number of CPU cores.
@@ -148,8 +151,10 @@ There are some standard filter classes to use (:mod:`nova.scheduler.filters`):
ServerGroupAntiAffinityFilter. The difference is that when you create the server
group, you should specify a policy of 'affinity'.
* |AggregateMultiTenancyIsolation| - isolate tenants in specific aggregates.
To specify multiple tenants use a comma. Eg. "tenant1,tenant2"
* |AggregateImagePropertiesIsolation| - isolates hosts based on image
properties and aggregate metadata.
properties and aggregate metadata. Use a comma to specify multiple values for the
same property. The filter will then ensure at least one value matches.
* |MetricsFilter| - filters hosts based on metrics weight_setting. Only hosts with
the available metrics are passed.
* |NUMATopologyFilter| - filters hosts based on the NUMA topology requested by the
+7 -2
View File
@@ -36,7 +36,10 @@ def aggregate_values_from_db(context, host, key_name):
def aggregate_metadata_get_by_host(context, host, key=None):
"""Returns a dict of all metadata for a specific host."""
"""Returns a dict of all metadata for a specific host.
Specify multiple values for the same key using a comma
"""
# TODO(pmurray): DB query in filter is a performance hit. Will need a
# general solution here.
aggrlist = objects.AggregateList.get_by_host(
@@ -45,7 +48,9 @@ def aggregate_metadata_get_by_host(context, host, key=None):
metadata = collections.defaultdict(set)
for aggr in aggrlist:
for k, v in aggr.metadata.iteritems():
metadata[k].add(v)
values = v.split(',')
for value in values:
metadata[k].add(value.strip())
return metadata
@@ -33,6 +33,15 @@ class TestAggImagePropsIsolationFilter(test.NoDBTestCase):
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(self.filt_cls.host_passes(host, filter_properties))
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'}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(self.filt_cls.host_passes(host, filter_properties))
def test_aggregate_image_properties_isolation_multi_props_passes(self,
agg_mock):
agg_mock.return_value = {'foo': 'bar', 'foo2': 'bar2'}
@@ -53,6 +53,18 @@ class TestAggregateInstanceExtraSpecsFilter(test.NoDBTestCase):
}
self._do_test_aggregate_filter_extra_specs(especs, passes=True)
def test_aggregate_filter_passes_extra_specs_simple_comma(self, agg_mock):
agg_mock.return_value = {'opt1': '1,3', 'opt2': '2'}
especs = {
# Un-scoped extra spec
'opt1': '1',
# Scoped extra spec that applies to this filter
'aggregate_instance_extra_specs:opt1': '3',
# Scoped extra spec that does not apply to this filter
'trust:trusted_host': 'true',
}
self._do_test_aggregate_filter_extra_specs(especs, passes=True)
def test_aggregate_filter_passes_with_key_same_as_scope(self, agg_mock):
agg_mock.return_value = {'aggregate_instance_extra_specs': '1'}
especs = {
@@ -26,7 +26,18 @@ class TestAggregateMultitenancyIsolationFilter(test.NoDBTestCase):
def test_aggregate_multi_tenancy_isolation_with_meta_passes(self,
agg_mock):
agg_mock.return_value = {'filter_tenant_id': 'my_tenantid'}
agg_mock.return_value = {'filter_tenant_id': set(['my_tenantid'])}
filter_properties = {'context': mock.sentinel.ctx,
'request_spec': {
'instance_properties': {
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(self.filt_cls.host_passes(host, filter_properties))
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': {
@@ -35,7 +46,17 @@ class TestAggregateMultitenancyIsolationFilter(test.NoDBTestCase):
self.assertTrue(self.filt_cls.host_passes(host, filter_properties))
def test_aggregate_multi_tenancy_isolation_fails(self, agg_mock):
agg_mock.return_value = {'filter_tenant_id': 'other_tenantid'}
agg_mock.return_value = {'filter_tenant_id': set(['other_tenantid'])}
filter_properties = {'context': mock.sentinel.ctx,
'request_spec': {
'instance_properties': {
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertFalse(self.filt_cls.host_passes(host, filter_properties))
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': {
@@ -41,6 +41,12 @@ class TestAvailabilityZoneFilter(test.NoDBTestCase):
host = fakes.FakeHostState('host1', 'node1', {})
self.assertTrue(self.filt_cls.host_passes(host, request))
def test_availability_zone_filter_same_comma(self, agg_mock):
agg_mock.return_value = {'availability_zone': 'nova,nova2'}
request = self._make_zone_request('nova')
host = fakes.FakeHostState('host1', 'node1', {})
self.assertTrue(self.filt_cls.host_passes(host, request))
def test_availability_zone_filter_different(self, agg_mock):
agg_mock.return_value = {'availability_zone': 'nova'}
request = self._make_zone_request('bad')
@@ -32,6 +32,12 @@ _AGGREGATE_FIXTURES = [
hosts=['fake-host'],
metadata={'k1': '3', 'k2': '4'},
),
objects.Aggregate(
id=3,
name='bar',
hosts=['fake-host'],
metadata={'k1': '6,7', 'k2': '8, 9'},
),
]
@@ -56,7 +62,7 @@ class UtilsTestCase(test.NoDBTestCase):
get_by_host.assert_called_with(context.elevated(),
'fake-host', key='k1')
self.assertEqual(set(['1', '3']), values)
self.assertEqual(set(['1', '3', '6,7']), values)
@mock.patch("nova.objects.aggregate.AggregateList.get_by_host")
def test_aggregate_metadata_get_by_host_no_key(self, get_by_host):
@@ -69,9 +75,9 @@ class UtilsTestCase(test.NoDBTestCase):
get_by_host.assert_called_with(context.elevated(),
'fake-host', key=None)
self.assertIn('k1', metadata)
self.assertEqual(set(['1', '3']), metadata['k1'])
self.assertEqual(set(['1', '3', '7', '6']), metadata['k1'])
self.assertIn('k2', metadata)
self.assertEqual(set(['2', '4']), metadata['k2'])
self.assertEqual(set(['9', '8', '2', '4']), metadata['k2'])
@mock.patch("nova.objects.aggregate.AggregateList.get_by_host")
def test_aggregate_metadata_get_by_host_with_key(self, get_by_host):
@@ -85,7 +91,7 @@ class UtilsTestCase(test.NoDBTestCase):
get_by_host.assert_called_with(context.elevated(),
'fake-host', key='k1')
self.assertIn('k1', metadata)
self.assertEqual(set(['1', '3']), metadata['k1'])
self.assertEqual(set(['1', '3', '7', '6']), metadata['k1'])
@mock.patch("nova.objects.aggregate.AggregateList.get_by_host")
def test_aggregate_metadata_get_by_host_empty_result(self, get_by_host):