Filter support
"""
-import inspect
-
-from stevedore import extension
+from cinder.openstack.common.scheduler import base_handler
class BaseFilter(object):
def filter_all(self, filter_obj_list, filter_properties):
"""Yield objects that pass the filter.
- Can be overriden in a subclass, if you need to base filtering
+ Can be overridden in a subclass, if you need to base filtering
decisions on all objects. Otherwise, one can just override
_filter_one() to filter a single object.
"""
yield obj
-class BaseFilterHandler(object):
- """ Base class to handle loading filter classes.
+class BaseFilterHandler(base_handler.BaseHandler):
+ """Base class to handle loading filter classes.
This class should be subclassed where one needs to use filters.
"""
- def __init__(self, filter_class_type, filter_namespace):
- self.namespace = filter_namespace
- self.filter_class_type = filter_class_type
- self.filter_manager = extension.ExtensionManager(filter_namespace)
-
- def _is_correct_class(self, obj):
- """Return whether an object is a class of the correct type and
- is not prefixed with an underscore.
- """
- return (inspect.isclass(obj) and
- not obj.__name__.startswith('_') and
- issubclass(obj, self.filter_class_type))
-
- def get_all_classes(self):
- return [x.plugin for x in self.filter_manager
- if self._is_correct_class(x.plugin)]
def get_filtered_objects(self, filter_classes, objs,
filter_properties):
--- /dev/null
+# Copyright (c) 2011-2013 OpenStack Foundation.
+# 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.
+"""
+A common base for handling extension classes.
+
+Used by BaseFilterHandler and BaseWeightHandler
+"""
+
+import inspect
+
+from stevedore import extension
+
+
+class BaseHandler(object):
+ """Base class to handle loading filter and weight classes."""
+ def __init__(self, modifier_class_type, modifier_namespace):
+ self.namespace = modifier_namespace
+ self.modifier_class_type = modifier_class_type
+ self.extension_manager = extension.ExtensionManager(modifier_namespace)
+
+ def _is_correct_class(self, cls):
+ """Return whether an object is a class of the correct type and
+ is not prefixed with an underscore.
+ """
+ return (inspect.isclass(cls) and
+ not cls.__name__.startswith('_') and
+ issubclass(cls, self.modifier_class_type))
+
+ def get_all_classes(self):
+ # We use a set, as some classes may have an entrypoint of their own,
+ # and also be returned by a function such as 'all_filters' for example
+ return [ext.plugin for ext in self.extension_manager if
+ self._is_correct_class(ext.plugin)]
Pluggable Weighing support
"""
-import inspect
-
-from stevedore import extension
+from cinder.openstack.common.scheduler import base_handler
class WeighedObject(object):
"""Base class for pluggable weighers."""
def _weight_multiplier(self):
"""How weighted this weigher should be. Normally this would
- be overriden in a subclass based on a config value.
+ be overridden in a subclass based on a config value.
"""
return 1.0
self._weigh_object(obj.obj, weight_properties))
-class BaseWeightHandler(object):
+class BaseWeightHandler(base_handler.BaseHandler):
object_class = WeighedObject
- def __init__(self, weighed_object_type, weight_namespace):
- self.namespace = weight_namespace
- self.weighed_object_type = weighed_object_type
- self.weight_manager = extension.ExtensionManager(weight_namespace)
-
- def _is_correct_class(self, obj):
- """Return whether an object is a class of the correct type and
- is not prefixed with an underscore.
- """
- return (inspect.isclass(obj) and
- not obj.__name__.startswith('_') and
- issubclass(obj, self.weighed_object_type))
-
- def get_all_classes(self):
- return [x.plugin for x in self.weight_manager
- if self._is_correct_class(x.plugin)]
-
def get_weighed_objects(self, weigher_classes, obj_list,
weighing_properties):
"""Return a sorted (highest score first) list of WeighedObjects."""
"""
from cinder.openstack.common import log as logging
-from cinder.openstack.common.scheduler import filter
+from cinder.openstack.common.scheduler import base_filter
LOG = logging.getLogger(__name__)
-class BaseHostFilter(filter.BaseFilter):
+class BaseHostFilter(base_filter.BaseFilter):
"""Base class for host filters."""
def _filter_one(self, obj, filter_properties):
"""Return True if the object passes the filter, otherwise False."""
raise NotImplementedError()
-class HostFilterHandler(filter.BaseFilterHandler):
+class HostFilterHandler(base_filter.BaseFilterHandler):
def __init__(self, namespace):
super(HostFilterHandler, self).__init__(BaseHostFilter, namespace)
def host_passes(self, host_state, filter_properties):
spec = filter_properties.get('request_spec', {})
- props = spec.get('resource_properties', [])
+ props = spec.get('resource_properties', {})
availability_zone = props.get('availability_zone')
if availability_zone:
# License for the specific language governing permissions and limitations
# under the License.
+import six
+
+from cinder.openstack.common.gettextutils import _ # noqa
from cinder.openstack.common import log as logging
from cinder.openstack.common.scheduler import filters
from cinder.openstack.common.scheduler.filters import extra_specs_ops
-
LOG = logging.getLogger(__name__)
"""HostFilter to work with resource (instance & volume) type records."""
def _satisfies_extra_specs(self, capabilities, resource_type):
- """Check that the capabilities provided by the services
- satisfy the extra specs associated with the instance type"""
+ """Check that the capabilities provided by the services satisfy
+ the extra specs associated with the resource type.
+ """
extra_specs = resource_type.get('extra_specs', [])
if not extra_specs:
return True
- for key, req in extra_specs.iteritems():
+ for key, req in six.iteritems(extra_specs):
# Either not scope format, or in capabilities scope
scope = key.split(':')
if len(scope) > 1 and scope[0] != "capabilities":
del scope[0]
cap = capabilities
- for index in range(0, len(scope)):
+ for index in range(len(scope)):
try:
cap = cap.get(scope[index], None)
except AttributeError:
if cap is None:
return False
if not extra_specs_ops.match(cap, req):
+ LOG.debug(_("extra_spec requirement '%(req)s' does not match "
+ "'%(cap)s'"), {'req': req, 'cap': cap})
return False
return True
def host_passes(self, host_state, filter_properties):
- """Return a list of hosts that can create instance_type."""
+ """Return a list of hosts that can create resource_type."""
# Note(zhiteng) Currently only Cinder and Nova are using
# this filter, so the resource type is either instance or
# volume.
resource_type = filter_properties.get('resource_type')
if not self._satisfies_extra_specs(host_state.capabilities,
resource_type):
+ LOG.debug(_("%(host_state)s fails resource_type extra_specs "
+ "requirements"), {'host_state': host_state})
return False
return True
-# Copyright (c) 2012 OpenStack Foundation
+# Copyright (c) 2011 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# License for the specific language governing permissions and limitations
# under the License.
+from cinder.openstack.common.gettextutils import _ # noqa
from cinder.openstack.common import log as logging
from cinder.openstack.common.scheduler import filters
LOG = logging.getLogger(__name__)
-class RetryFilter(filters.BaseHostFilter):
- """Filter out nodes that have already been attempted for scheduling
- purposes
+class IgnoreAttemptedHostsFilter(filters.BaseHostFilter):
+ """Filter out previously attempted hosts
+
+ A host passes this filter if it has not already been attempted for
+ scheduling. The scheduler needs to add previously attempted hosts
+ to the 'retry' key of filter_properties in order for this to work
+ correctly. For example:
+ {
+ 'retry': {
+ 'hosts': ['host1', 'host2'],
+ 'num_attempts': 3,
+ }
+ }
"""
def host_passes(self, host_state, filter_properties):
"""Skip nodes that have already been attempted."""
- retry = filter_properties.get('retry', None)
- if not retry:
+ attempted = filter_properties.get('retry', None)
+ if not attempted:
# Re-scheduling is disabled
- LOG.debug("Re-scheduling is disabled")
+ LOG.debug(_("Re-scheduling is disabled."))
return True
- hosts = retry.get('hosts', [])
+ hosts = attempted.get('hosts', [])
host = host_state.host
passes = host not in hosts
pass_msg = "passes" if passes else "fails"
LOG.debug(_("Host %(host)s %(pass_msg)s. Previously tried hosts: "
- "%(hosts)s") %
- {'host': host, 'pass_msg': pass_msg, 'hosts': hosts})
-
- # Host passes if it's not in the list of previously attempted hosts:
+ "%(hosts)s") % {'host': host,
+ 'pass_msg': pass_msg,
+ 'hosts': hosts})
return passes
import operator
+import six
+
from cinder.openstack.common import jsonutils
from cinder.openstack.common.scheduler import filters
return self._op_compare(args, operator.gt)
def _in(self, args):
- """First term is in set of remaining terms"""
+ """First term is in set of remaining terms."""
return self._op_compare(args, operator.contains)
def _less_than_equal(self, args):
for arg in query[1:]:
if isinstance(arg, list):
arg = self._process_filter(arg, host_state)
- elif isinstance(arg, basestring):
+ elif isinstance(arg, six.string_types):
arg = self._parse_string(arg, host_state)
if arg is not None:
cooked_args.append(arg)
"""
-from cinder.openstack.common.scheduler import weight
+from cinder.openstack.common.scheduler import base_weight
-class WeighedHost(weight.WeighedObject):
+class WeighedHost(base_weight.WeighedObject):
def to_dict(self):
return {
'weight': self.weight,
(self.obj.host, self.weight))
-class BaseHostWeigher(weight.BaseWeigher):
+class BaseHostWeigher(base_weight.BaseWeigher):
"""Base class for host weights."""
pass
-class HostWeightHandler(weight.BaseWeightHandler):
+class HostWeightHandler(base_weight.BaseWeightHandler):
object_class = WeighedHost
def __init__(self, namespace):
'updated_at': None,
'service': service})
self.assertTrue(filt_cls.host_passes(host, filter_properties))
-
- def test_retry_filter_disabled(self):
- # Test case where retry/re-scheduling is disabled.
- filt_cls = self.class_map['RetryFilter']()
- host = fakes.FakeHostState('host1', {})
- filter_properties = {}
- self.assertTrue(filt_cls.host_passes(host, filter_properties))
-
- def test_retry_filter_pass(self):
- # Node not previously tried.
- filt_cls = self.class_map['RetryFilter']()
- host = fakes.FakeHostState('host1', {})
- retry = dict(num_attempts=2, hosts=['host2'])
- filter_properties = dict(retry=retry)
- self.assertTrue(filt_cls.host_passes(host, filter_properties))
-
- def test_retry_filter_fail(self):
- # Node was already tried.
- filt_cls = self.class_map['RetryFilter']()
- host = fakes.FakeHostState('host1', {})
- retry = dict(num_attempts=1, hosts=['host1'])
- filter_properties = dict(retry=retry)
- self.assertFalse(filt_cls.host_passes(host, filter_properties))
CapabilitiesFilter = cinder.openstack.common.scheduler.filters.capabilities_filter:CapabilitiesFilter
CapacityFilter = cinder.scheduler.filters.capacity_filter:CapacityFilter
JsonFilter = cinder.openstack.common.scheduler.filters.json_filter:JsonFilter
- RetryFilter = cinder.scheduler.filters.retry_filter:RetryFilter
+ RetryFilter = cinder.openstack.common.scheduler.filters.ignore_attempted_hosts_filter:IgnoreAttemptedHostsFilter
cinder.scheduler.weights =
AllocatedCapacityWeigher = cinder.scheduler.weights.capacity:AllocatedCapacityWeigher
CapacityWeigher = cinder.scheduler.weights.capacity:CapacityWeigher