Merge "Support specifing multiple values for aggregate keys"
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user