From fbedf60a432773716ddb02f990fc744380fee469 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Fri, 5 Jul 2013 15:20:50 -0400 Subject: [PATCH] Support scoped keys in aggregate extra specs filter Update AggregateInstanceExtraSpecs to support scoped keys in flavor extra_specs. Otherwise, you can't use this filter in combination with other filters that act on un-scoped extra specs, because they may conflict. This recently came up on the mailing list: http://lists.openstack.org/pipermail/openstack-dev/2013-July/011421.html Fix bug 1198290. DocImpact - See updates to filter_scheduler.rst. Change-Id: I03d1d3268c800dc6982ffa4b13f8b9489428b991 --- doc/source/devref/filter_scheduler.rst | 3 ++- .../filters/aggregate_instance_extra_specs.py | 14 +++++++---- nova/tests/scheduler/test_host_filters.py | 23 +++++++++++++++---- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/doc/source/devref/filter_scheduler.rst b/doc/source/devref/filter_scheduler.rst index 4fca56736352..1f4cd5640f98 100644 --- a/doc/source/devref/filter_scheduler.rst +++ b/doc/source/devref/filter_scheduler.rst @@ -67,7 +67,8 @@ There are some standard filter classes to use (:mod:`nova.scheduler.filters`): * |AggregateInstanceExtraSpecsFilter| - checks that the aggregate metadata satisfies any extra specifications associated with the instance type (that - have no scope). It passes hosts that can create the specified instance type. + 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|. * |ComputeFilter| - passes all hosts that are operational and enabled. diff --git a/nova/scheduler/filters/aggregate_instance_extra_specs.py b/nova/scheduler/filters/aggregate_instance_extra_specs.py index 3cb74573a06a..07c557f42ebc 100644 --- a/nova/scheduler/filters/aggregate_instance_extra_specs.py +++ b/nova/scheduler/filters/aggregate_instance_extra_specs.py @@ -22,6 +22,8 @@ from nova.scheduler.filters import extra_specs_ops LOG = logging.getLogger(__name__) +_SCOPE = 'aggregate_instance_extra_specs' + class AggregateInstanceExtraSpecsFilter(filters.BaseHostFilter): """AggregateInstanceExtraSpecsFilter works with InstanceType records.""" @@ -40,10 +42,14 @@ class AggregateInstanceExtraSpecsFilter(filters.BaseHostFilter): metadata = db.aggregate_metadata_get_by_host(context, host_state.host) for key, req in instance_type['extra_specs'].iteritems(): - # NOTE(jogo) any key containing a scope (scope is terminated - # by a `:') will be ignored by this filter. (bug 1039386) - if key.count(':'): - continue + # Either not scope format, or aggregate_instance_extra_specs scope + scope = key.split(':', 1) + if len(scope) > 1: + if scope[0] != _SCOPE: + continue + else: + del scope[0] + key = scope[0] aggregate_vals = metadata.get(key, None) if not aggregate_vals: LOG.debug(_("%(host_state)s fails instance_type extra_specs " diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index 8832f5c2bb8d..3d674a4f33d3 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -860,11 +860,26 @@ class HostFiltersTestCase(test.NoDBTestCase): self.assertFalse(filt_cls.host_passes(host, filter_properties)) def test_aggregate_filter_passes_extra_specs_simple(self): + especs = { + # Un-scoped extra spec + 'opt1': '1', + # Scoped extra spec that applies to this filter + 'aggregate_instance_extra_specs:opt2': '2', + # Scoped extra spec that does not apply to this filter + 'trust:trusted_host': 'true', + } self._do_test_aggregate_filter_extra_specs( - emeta={'opt1': '1', 'opt2': '2'}, - especs={'opt1': '1', 'opt2': '2', - 'trust:trusted_host': 'true'}, - passes=True) + emeta={'opt1': '1', 'opt2': '2'}, especs=especs, passes=True) + + def test_aggregate_filter_passes_with_key_same_as_scope(self): + especs = { + # Un-scoped extra spec, make sure we don't blow up if it + # happens to match our scope. + 'aggregate_instance_extra_specs': '1', + } + self._do_test_aggregate_filter_extra_specs( + emeta={'aggregate_instance_extra_specs': '1'}, + especs=especs, passes=True) def test_aggregate_filter_fails_extra_specs_simple(self): self._do_test_aggregate_filter_extra_specs(