Skip to content

Commit

Permalink
Merge "Aggregate: Hosts isolation based on image properties"
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenkins authored and openstack-gerrit committed Jan 7, 2014
2 parents 3912d7a + 0ca6b81 commit 8b9f924
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 1 deletion.
4 changes: 3 additions & 1 deletion doc/source/devref/filter_scheduler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ There are some standard filter classes to use (:mod:`nova.scheduler.filters`):
* |GroupAffinityFilter| - ensures that each instance in group is on a same
host with one of the instance host in a group.
* |AggregateMultiTenancyIsolation| - isolate tenants in specific aggregates.
* |AggregateImagePropertiesIsolation| - isolates hosts based on image
properties and aggregate metadata.

Now we can focus on these standard filter classes in details. I will pass the
simplest ones, such as |AllHostsFilter|, |CoreFilter| and |RamFilter| are,
Expand Down Expand Up @@ -330,5 +332,5 @@ in :mod:``nova.tests.scheduler``.
.. |AggregateTypeAffinityFilter| replace:: :class:`AggregateTypeAffinityFilter <nova.scheduler.filters.type_filter.AggregateTypeAffinityFilter>`
.. |AggregateInstanceExtraSpecsFilter| replace:: :class:`AggregateInstanceExtraSpecsFilter <nova.scheduler.filters.aggregate_instance_extra_specs.AggregateInstanceExtraSpecsFilter>`
.. |AggregateMultiTenancyIsolation| replace:: :class:`AggregateMultiTenancyIsolation <nova.scheduler.filters.aggregate_multitenancy_isolation.AggregateMultiTenancyIsolation>`

.. |RamWeigher| replace:: :class:`RamWeigher <nova.scheduler.weights.all_weighers.RamWeigher>`
.. |AggregateImagePropertiesIsolation| replace:: :class:`AggregateImagePropertiesIsolation <nova.scheduler.filters.aggregate_image_properties_isolation.AggregateImagePropertiesIsolation>`
13 changes: 13 additions & 0 deletions etc/nova/nova.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,19 @@
#scheduler_host_subset_size=1


#
# Options defined in nova.scheduler.filters.aggregate_image_properties_isolation
#

# Force the filter to consider only keys matching the given
# namespace. (string value)
#aggregate_image_properties_isolation_namespace=<None>

# The separator used between the namespace and keys (string
# value)
#aggregate_image_properties_isolation_separator=.


#
# Options defined in nova.scheduler.filters.core_filter
#
Expand Down
68 changes: 68 additions & 0 deletions nova/scheduler/filters/aggregate_image_properties_isolation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright (c) 2013 Cloudwatt
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from oslo.config import cfg

from nova import db
from nova.openstack.common.gettextutils import _
from nova.openstack.common import log as logging
from nova.scheduler import filters

opts = [
cfg.StrOpt('aggregate_image_properties_isolation_namespace',
help='Force the filter to consider only keys matching '
'the given namespace.'),
cfg.StrOpt('aggregate_image_properties_isolation_separator',
default=".",
help='The separator used between the namespace and keys'),
]
CONF = cfg.CONF
CONF.register_opts(opts)

LOG = logging.getLogger(__name__)


class AggregateImagePropertiesIsolation(filters.BaseHostFilter):
"""AggregateImagePropertiesIsolation works with image properties."""

# Aggregate data and instance type does not change within a request
run_filter_once_per_request = True

def host_passes(self, host_state, filter_properties):
"""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', {})
context = filter_properties['context'].elevated()
metadata = db.aggregate_metadata_get_by_host(context, host_state.host)

for key, options in metadata.iteritems():
if (cfg_namespace and
not key.startswith(cfg_namespace + cfg_separator)):
continue
prop = image_props.get(key)
if prop and prop not in options:
LOG.debug(_("%(host_state)s fails image aggregate properties "
"requirements. Property %(prop)s does not "
"match %(options)s."),
{'host_state': host_state,
'prop': prop,
'options': options})
return False
return True
102 changes: 102 additions & 0 deletions nova/tests/scheduler/test_host_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1643,3 +1643,105 @@ def test_pci_passthrough_comopute_stats(self):
self.pci_request_result = True
self.assertRaises(AttributeError, filt_cls.host_passes,
host, filter_properties)

def test_aggregate_image_properties_isolation_passes(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['AggregateImagePropertiesIsolation']()
aggr_meta = {'foo': 'bar'}
self._create_aggregate_with_host(name='fake1',
metadata=aggr_meta,
hosts=['host1'])
filter_properties = {'context': self.context,
'request_spec': {
'image': {
'properties': {'foo': 'bar'}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(filt_cls.host_passes(host, filter_properties))

def test_aggregate_image_properties_isolation_multi_props_passes(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['AggregateImagePropertiesIsolation']()
aggr_meta = {'foo': 'bar', 'foo2': 'bar2'}
self._create_aggregate_with_host(name='fake1',
metadata=aggr_meta,
hosts=['host1'])
filter_properties = {'context': self.context,
'request_spec': {
'image': {
'properties': {'foo': 'bar',
'foo2': 'bar2'}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(filt_cls.host_passes(host, filter_properties))

def test_aggregate_image_properties_isolation_props_with_meta_passes(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['AggregateImagePropertiesIsolation']()
aggr_meta = {'foo': 'bar'}
self._create_aggregate_with_host(name='fake1',
metadata=aggr_meta,
hosts=['host1'])
filter_properties = {'context': self.context,
'request_spec': {
'image': {
'properties': {}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(filt_cls.host_passes(host, filter_properties))

def test_aggregate_image_properties_isolation_props_imgprops_passes(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['AggregateImagePropertiesIsolation']()
aggr_meta = {}
self._create_aggregate_with_host(name='fake1',
metadata=aggr_meta,
hosts=['host1'])
filter_properties = {'context': self.context,
'request_spec': {
'image': {
'properties': {'foo': 'bar'}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(filt_cls.host_passes(host, filter_properties))

def test_aggregate_image_properties_isolation_props_not_match_fails(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['AggregateImagePropertiesIsolation']()
aggr_meta = {'foo': 'bar'}
self._create_aggregate_with_host(name='fake1',
metadata=aggr_meta,
hosts=['host1'])
filter_properties = {'context': self.context,
'request_spec': {
'image': {
'properties': {'foo': 'no-bar'}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertFalse(filt_cls.host_passes(host, filter_properties))

def test_aggregate_image_properties_isolation_props_not_match2_fails(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['AggregateImagePropertiesIsolation']()
aggr_meta = {'foo': 'bar', 'foo2': 'bar2'}
self._create_aggregate_with_host(name='fake1',
metadata=aggr_meta,
hosts=['host1'])
filter_properties = {'context': self.context,
'request_spec': {
'image': {
'properties': {'foo': 'bar',
'foo2': 'bar3'}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertFalse(filt_cls.host_passes(host, filter_properties))

def test_aggregate_image_properties_isolation_props_namespace(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['AggregateImagePropertiesIsolation']()
self.flags(aggregate_image_properties_isolation_namespace="np")
aggr_meta = {'np.foo': 'bar', 'foo2': 'bar2'}
self._create_aggregate_with_host(name='fake1',
metadata=aggr_meta,
hosts=['host1'])
filter_properties = {'context': self.context,
'request_spec': {
'image': {
'properties': {'np.foo': 'bar',
'foo2': 'bar3'}}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(filt_cls.host_passes(host, filter_properties))

0 comments on commit 8b9f924

Please sign in to comment.