Merge "Prepare filters for using RequestSpec object"
This commit is contained in:
commit
1254e3978a
|
@ -27,13 +27,13 @@ LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
class BaseFilter(object):
|
class BaseFilter(object):
|
||||||
"""Base class for all filter classes."""
|
"""Base class for all filter classes."""
|
||||||
def _filter_one(self, obj, filter_properties):
|
def _filter_one(self, obj, spec_obj):
|
||||||
"""Return True if it passes the filter, False otherwise.
|
"""Return True if it passes the filter, False otherwise.
|
||||||
Override this in a subclass.
|
Override this in a subclass.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def filter_all(self, filter_obj_list, filter_properties):
|
def filter_all(self, filter_obj_list, spec_obj):
|
||||||
"""Yield objects that pass the filter.
|
"""Yield objects that pass the filter.
|
||||||
|
|
||||||
Can be overridden in a subclass, if you need to base filtering
|
Can be overridden in a subclass, if you need to base filtering
|
||||||
|
@ -41,7 +41,7 @@ class BaseFilter(object):
|
||||||
_filter_one() to filter a single object.
|
_filter_one() to filter a single object.
|
||||||
"""
|
"""
|
||||||
for obj in filter_obj_list:
|
for obj in filter_obj_list:
|
||||||
if self._filter_one(obj, filter_properties):
|
if self._filter_one(obj, spec_obj):
|
||||||
yield obj
|
yield obj
|
||||||
|
|
||||||
# Set to true in a subclass if a filter only needs to be run once
|
# Set to true in a subclass if a filter only needs to be run once
|
||||||
|
@ -65,7 +65,7 @@ class BaseFilterHandler(loadables.BaseLoader):
|
||||||
This class should be subclassed where one needs to use filters.
|
This class should be subclassed where one needs to use filters.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_filtered_objects(self, filters, objs, filter_properties, index=0):
|
def get_filtered_objects(self, filters, objs, spec_obj, index=0):
|
||||||
list_objs = list(objs)
|
list_objs = list(objs)
|
||||||
LOG.debug("Starting with %d host(s)", len(list_objs))
|
LOG.debug("Starting with %d host(s)", len(list_objs))
|
||||||
# Track the hosts as they are removed. The 'full_filter_results' list
|
# Track the hosts as they are removed. The 'full_filter_results' list
|
||||||
|
@ -82,7 +82,7 @@ class BaseFilterHandler(loadables.BaseLoader):
|
||||||
if filter_.run_filter_for_index(index):
|
if filter_.run_filter_for_index(index):
|
||||||
cls_name = filter_.__class__.__name__
|
cls_name = filter_.__class__.__name__
|
||||||
start_count = len(list_objs)
|
start_count = len(list_objs)
|
||||||
objs = filter_.filter_all(list_objs, filter_properties)
|
objs = filter_.filter_all(list_objs, spec_obj)
|
||||||
if objs is None:
|
if objs is None:
|
||||||
LOG.debug("Filter %s says to stop filtering", cls_name)
|
LOG.debug("Filter %s says to stop filtering", cls_name)
|
||||||
return
|
return
|
||||||
|
@ -104,9 +104,17 @@ class BaseFilterHandler(loadables.BaseLoader):
|
||||||
{'cls_name': cls_name, 'obj_len': len(list_objs)})
|
{'cls_name': cls_name, 'obj_len': len(list_objs)})
|
||||||
if not list_objs:
|
if not list_objs:
|
||||||
# Log the filtration history
|
# Log the filtration history
|
||||||
rspec = filter_properties.get("request_spec", {})
|
# NOTE(sbauza): Since the Cells scheduler still provides a legacy
|
||||||
inst_props = rspec.get("instance_properties", {})
|
# dictionary for filter_props, and since we agreed on not modifying
|
||||||
msg_dict = {"inst_uuid": inst_props.get("uuid", ""),
|
# the Cells scheduler to support that because of Cells v2, we
|
||||||
|
# prefer to define a compatible way to address both types
|
||||||
|
if isinstance(spec_obj, dict):
|
||||||
|
rspec = spec_obj.get("request_spec", {})
|
||||||
|
inst_props = rspec.get("instance_properties", {})
|
||||||
|
inst_uuid = inst_props.get("uuid", "")
|
||||||
|
else:
|
||||||
|
inst_uuid = spec_obj.instance_uuid
|
||||||
|
msg_dict = {"inst_uuid": inst_uuid,
|
||||||
"str_results": str(full_filter_results),
|
"str_results": str(full_filter_results),
|
||||||
}
|
}
|
||||||
full_msg = ("Filtering removed all hosts for the request with "
|
full_msg = ("Filtering removed all hosts for the request with "
|
||||||
|
|
|
@ -127,23 +127,12 @@ class FilterScheduler(driver.Scheduler):
|
||||||
|
|
||||||
selected_hosts = []
|
selected_hosts = []
|
||||||
num_instances = spec_obj.num_instances
|
num_instances = spec_obj.num_instances
|
||||||
# TODO(sbauza): Modify the interfaces for HostManager and filters to
|
# NOTE(sbauza): Adding one field for any out-of-tree need
|
||||||
# accept the RequestSpec object directly (in a later patch hopefully)
|
spec_obj.config_options = config_options
|
||||||
filter_properties = spec_obj.to_legacy_filter_properties_dict()
|
|
||||||
# NOTE(sbauza): Adding temporarly some keys since filters are
|
|
||||||
# directly using it - until we provide directly RequestSpec
|
|
||||||
filter_properties.update(
|
|
||||||
{'request_spec': spec_obj.to_legacy_request_spec_dict(),
|
|
||||||
'instance_type': spec_obj.flavor})
|
|
||||||
# TODO(sbauza): Adding two keys not used in-tree but which will be
|
|
||||||
# provided as non-fields for the RequestSpec once we provide it to the
|
|
||||||
# filters
|
|
||||||
filter_properties.update({'context': context,
|
|
||||||
'config_options': config_options})
|
|
||||||
for num in range(num_instances):
|
for num in range(num_instances):
|
||||||
# Filter local hosts based on requirements ...
|
# Filter local hosts based on requirements ...
|
||||||
hosts = self.host_manager.get_filtered_hosts(hosts,
|
hosts = self.host_manager.get_filtered_hosts(hosts,
|
||||||
filter_properties, index=num)
|
spec_obj, index=num)
|
||||||
if not hosts:
|
if not hosts:
|
||||||
# Can't get any more locally.
|
# Can't get any more locally.
|
||||||
break
|
break
|
||||||
|
@ -151,7 +140,7 @@ class FilterScheduler(driver.Scheduler):
|
||||||
LOG.debug("Filtered %(hosts)s", {'hosts': hosts})
|
LOG.debug("Filtered %(hosts)s", {'hosts': hosts})
|
||||||
|
|
||||||
weighed_hosts = self.host_manager.get_weighed_hosts(hosts,
|
weighed_hosts = self.host_manager.get_weighed_hosts(hosts,
|
||||||
filter_properties)
|
spec_obj)
|
||||||
|
|
||||||
LOG.debug("Weighed %(hosts)s", {'hosts': weighed_hosts})
|
LOG.debug("Weighed %(hosts)s", {'hosts': weighed_hosts})
|
||||||
|
|
||||||
|
@ -169,8 +158,10 @@ class FilterScheduler(driver.Scheduler):
|
||||||
# Now consume the resources so the filter/weights
|
# Now consume the resources so the filter/weights
|
||||||
# will change for the next instance.
|
# will change for the next instance.
|
||||||
chosen_host.obj.consume_from_request(spec_obj)
|
chosen_host.obj.consume_from_request(spec_obj)
|
||||||
if filter_properties.get('group_updated') is True:
|
if spec_obj.instance_group is not None:
|
||||||
filter_properties['group_hosts'].add(chosen_host.obj.host)
|
spec_obj.instance_group.hosts.append(chosen_host.obj.host)
|
||||||
|
# hosts has to be not part of the updates when saving
|
||||||
|
spec_obj.instance_group.obj_reset_changes(['hosts'])
|
||||||
return selected_hosts
|
return selected_hosts
|
||||||
|
|
||||||
def _get_all_host_states(self, context):
|
def _get_all_host_states(self, context):
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
"""
|
"""
|
||||||
Scheduler host filters
|
Scheduler host filters
|
||||||
"""
|
"""
|
||||||
|
import functools
|
||||||
|
|
||||||
from nova import filters
|
from nova import filters
|
||||||
|
from nova import objects
|
||||||
|
|
||||||
|
|
||||||
class BaseHostFilter(filters.BaseFilter):
|
class BaseHostFilter(filters.BaseFilter):
|
||||||
|
@ -45,3 +47,31 @@ def all_filters():
|
||||||
and should return a list of all filter classes available.
|
and should return a list of all filter classes available.
|
||||||
"""
|
"""
|
||||||
return HostFilterHandler().get_all_classes()
|
return HostFilterHandler().get_all_classes()
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(sbauza): Remove that decorator once all filters are using RequestSpec
|
||||||
|
# object directly.
|
||||||
|
def compat_legacy_props(function):
|
||||||
|
"""Decorator for returning a legacy filter_properties dictionary.
|
||||||
|
|
||||||
|
This is used for keeping unchanged the existing filters without yet using
|
||||||
|
the RequestSpec fields by returning a legacy dictionary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@functools.wraps(function)
|
||||||
|
def decorated_host_passes(self, host_state, filter_properties):
|
||||||
|
if isinstance(filter_properties, objects.RequestSpec):
|
||||||
|
legacy_props = filter_properties.to_legacy_filter_properties_dict()
|
||||||
|
legacy_props.update({'request_spec': (
|
||||||
|
filter_properties.to_legacy_request_spec_dict()),
|
||||||
|
'instance_type': filter_properties.flavor})
|
||||||
|
# TODO(sbauza): Adding two keys not used in-tree but which will be
|
||||||
|
# provided as non-fields for the RequestSpec once we provide it to
|
||||||
|
# the filters
|
||||||
|
legacy_props.update(
|
||||||
|
{'context': filter_properties._context,
|
||||||
|
'config_options': filter_properties.config_options})
|
||||||
|
filter_properties = legacy_props
|
||||||
|
return function(self, host_state, filter_properties)
|
||||||
|
|
||||||
|
return decorated_host_passes
|
||||||
|
|
|
@ -30,6 +30,7 @@ class DifferentHostFilter(filters.BaseHostFilter):
|
||||||
# The hosts the instances are running on doesn't change within a request
|
# The hosts the instances are running on doesn't change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ class SameHostFilter(filters.BaseHostFilter):
|
||||||
# The hosts the instances are running on doesn't change within a request
|
# The hosts the instances are running on doesn't change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
||||||
|
|
||||||
|
@ -67,6 +69,7 @@ class SimpleCIDRAffinityFilter(filters.BaseHostFilter):
|
||||||
# The address of a host doesn't change within a request
|
# The address of a host doesn't change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
scheduler_hints = filter_properties.get('scheduler_hints') or {}
|
||||||
|
|
||||||
|
@ -87,6 +90,7 @@ class _GroupAntiAffinityFilter(filters.BaseHostFilter):
|
||||||
"""Schedule the instance on a different host from a set of group
|
"""Schedule the instance on a different host from a set of group
|
||||||
hosts.
|
hosts.
|
||||||
"""
|
"""
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
# Only invoke the filter is 'anti-affinity' is configured
|
# Only invoke the filter is 'anti-affinity' is configured
|
||||||
policies = filter_properties.get('group_policies', [])
|
policies = filter_properties.get('group_policies', [])
|
||||||
|
@ -113,6 +117,7 @@ class ServerGroupAntiAffinityFilter(_GroupAntiAffinityFilter):
|
||||||
class _GroupAffinityFilter(filters.BaseHostFilter):
|
class _GroupAffinityFilter(filters.BaseHostFilter):
|
||||||
"""Schedule the instance on to host from a set of group hosts.
|
"""Schedule the instance on to host from a set of group hosts.
|
||||||
"""
|
"""
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
# Only invoke the filter is 'affinity' is configured
|
# Only invoke the filter is 'affinity' is configured
|
||||||
policies = filter_properties.get('group_policies', [])
|
policies = filter_properties.get('group_policies', [])
|
||||||
|
|
|
@ -40,6 +40,7 @@ class AggregateImagePropertiesIsolation(filters.BaseHostFilter):
|
||||||
# Aggregate data and instance type does not change within a request
|
# Aggregate data and instance type does not change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Checks a host in an aggregate that metadata key/value match
|
"""Checks a host in an aggregate that metadata key/value match
|
||||||
with image properties.
|
with image properties.
|
||||||
|
|
|
@ -33,6 +33,7 @@ class AggregateInstanceExtraSpecsFilter(filters.BaseHostFilter):
|
||||||
# Aggregate data and instance type does not change within a request
|
# Aggregate data and instance type does not change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
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 instance_type
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ class AggregateMultiTenancyIsolation(filters.BaseHostFilter):
|
||||||
# Aggregate data and tenant do not change within a request
|
# Aggregate data and tenant do not change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""If a host is in an aggregate that has the metadata key
|
"""If a host is in an aggregate that has the metadata key
|
||||||
"filter_tenant_id" it can only create instances from that tenant(s).
|
"filter_tenant_id" it can only create instances from that tenant(s).
|
||||||
|
|
|
@ -23,5 +23,6 @@ class AllHostsFilter(filters.BaseHostFilter):
|
||||||
# list of hosts doesn't change within a request
|
# list of hosts doesn't change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -36,6 +36,7 @@ class AvailabilityZoneFilter(filters.BaseHostFilter):
|
||||||
# Availability zones do not change within a request
|
# Availability zones do not change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
spec = filter_properties.get('request_spec', {})
|
spec = filter_properties.get('request_spec', {})
|
||||||
props = spec.get('instance_properties', {})
|
props = spec.get('instance_properties', {})
|
||||||
|
|
|
@ -92,6 +92,7 @@ class ComputeCapabilitiesFilter(filters.BaseHostFilter):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
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 instance_type."""
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
|
@ -34,6 +34,7 @@ class ComputeFilter(filters.BaseHostFilter):
|
||||||
# Host state does not change within a request
|
# Host state does not change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Returns True for only active compute nodes."""
|
"""Returns True for only active compute nodes."""
|
||||||
service = host_state.service
|
service = host_state.service
|
||||||
|
|
|
@ -29,6 +29,7 @@ class BaseCoreFilter(filters.BaseHostFilter):
|
||||||
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
|
def _get_cpu_allocation_ratio(self, host_state, filter_properties):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Return True if host has sufficient CPU cores."""
|
"""Return True if host has sufficient CPU cores."""
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
|
@ -35,6 +35,7 @@ class DiskFilter(filters.BaseHostFilter):
|
||||||
def _get_disk_allocation_ratio(self, host_state, filter_properties):
|
def _get_disk_allocation_ratio(self, host_state, filter_properties):
|
||||||
return CONF.disk_allocation_ratio
|
return CONF.disk_allocation_ratio
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Filter based on disk usage."""
|
"""Filter based on disk usage."""
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
|
@ -25,6 +25,7 @@ LOG = logging.getLogger(__name__)
|
||||||
class ExactCoreFilter(filters.BaseHostFilter):
|
class ExactCoreFilter(filters.BaseHostFilter):
|
||||||
"""Exact Core Filter."""
|
"""Exact Core Filter."""
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Return True if host has the exact number of CPU cores."""
|
"""Return True if host has the exact number of CPU cores."""
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
|
@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__)
|
||||||
class ExactDiskFilter(filters.BaseHostFilter):
|
class ExactDiskFilter(filters.BaseHostFilter):
|
||||||
"""Exact Disk Filter."""
|
"""Exact Disk Filter."""
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Return True if host has the exact amount of disk available."""
|
"""Return True if host has the exact amount of disk available."""
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
|
@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__)
|
||||||
class ExactRamFilter(filters.BaseHostFilter):
|
class ExactRamFilter(filters.BaseHostFilter):
|
||||||
"""Exact RAM Filter."""
|
"""Exact RAM Filter."""
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Return True if host has the exact amount of RAM available."""
|
"""Return True if host has the exact amount of RAM available."""
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
|
@ -95,6 +95,7 @@ class ImagePropertiesFilter(filters.BaseHostFilter):
|
||||||
'hypervisor_version': hypervisor_version})
|
'hypervisor_version': hypervisor_version})
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Check if host passes specified image properties.
|
"""Check if host passes specified image properties.
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ class IoOpsFilter(filters.BaseHostFilter):
|
||||||
def _get_max_io_ops_per_host(self, host_state, filter_properties):
|
def _get_max_io_ops_per_host(self, host_state, filter_properties):
|
||||||
return CONF.max_io_ops_per_host
|
return CONF.max_io_ops_per_host
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Use information about current vm and task states collected from
|
"""Use information about current vm and task states collected from
|
||||||
compute node statistics to decide whether to filter.
|
compute node statistics to decide whether to filter.
|
||||||
|
|
|
@ -39,6 +39,7 @@ class IsolatedHostsFilter(filters.BaseHostFilter):
|
||||||
# The configuration values do not change within a request
|
# The configuration values do not change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Result Matrix with 'restrict_isolated_hosts_to_isolated_images' set
|
"""Result Matrix with 'restrict_isolated_hosts_to_isolated_images' set
|
||||||
to True::
|
to True::
|
||||||
|
|
|
@ -126,6 +126,7 @@ class JsonFilter(filters.BaseHostFilter):
|
||||||
result = method(self, cooked_args)
|
result = method(self, cooked_args)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Return a list of hosts that can fulfill the requirements
|
"""Return a list of hosts that can fulfill the requirements
|
||||||
specified in the query.
|
specified in the query.
|
||||||
|
|
|
@ -43,6 +43,7 @@ class MetricsFilter(filters.BaseHostFilter):
|
||||||
name="metrics.weight_setting")
|
name="metrics.weight_setting")
|
||||||
self.keys = set([x[0] for x in opts])
|
self.keys = set([x[0] for x in opts])
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
metrics_on_host = set(m.name for m in host_state.metrics)
|
metrics_on_host = set(m.name for m in host_state.metrics)
|
||||||
if not self.keys.issubset(metrics_on_host):
|
if not self.keys.issubset(metrics_on_host):
|
||||||
|
|
|
@ -36,6 +36,7 @@ class NumInstancesFilter(filters.BaseHostFilter):
|
||||||
def _get_max_instances_per_host(self, host_state, filter_properties):
|
def _get_max_instances_per_host(self, host_state, filter_properties):
|
||||||
return CONF.max_instances_per_host
|
return CONF.max_instances_per_host
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
num_instances = host_state.num_instances
|
num_instances = host_state.num_instances
|
||||||
max_instances = self._get_max_instances_per_host(
|
max_instances = self._get_max_instances_per_host(
|
||||||
|
|
|
@ -18,6 +18,7 @@ from nova.virt import hardware
|
||||||
class NUMATopologyFilter(filters.BaseHostFilter):
|
class NUMATopologyFilter(filters.BaseHostFilter):
|
||||||
"""Filter on requested NUMA topology."""
|
"""Filter on requested NUMA topology."""
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
ram_ratio = host_state.ram_allocation_ratio
|
ram_ratio = host_state.ram_allocation_ratio
|
||||||
cpu_ratio = host_state.cpu_allocation_ratio
|
cpu_ratio = host_state.cpu_allocation_ratio
|
||||||
|
|
|
@ -40,6 +40,7 @@ class PciPassthroughFilter(filters.BaseHostFilter):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Return true if the host has the required PCI devices."""
|
"""Return true if the host has the required PCI devices."""
|
||||||
request_spec = filter_properties.get('request_spec', {})
|
request_spec = filter_properties.get('request_spec', {})
|
||||||
|
|
|
@ -28,6 +28,7 @@ class BaseRamFilter(filters.BaseHostFilter):
|
||||||
def _get_ram_allocation_ratio(self, host_state, filter_properties):
|
def _get_ram_allocation_ratio(self, host_state, filter_properties):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Only return hosts with sufficient available RAM."""
|
"""Only return hosts with sufficient available RAM."""
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
|
@ -25,6 +25,7 @@ class RetryFilter(filters.BaseHostFilter):
|
||||||
purposes
|
purposes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Skip nodes that have already been attempted."""
|
"""Skip nodes that have already been attempted."""
|
||||||
retry = filter_properties.get('retry', None)
|
retry = filter_properties.get('retry', None)
|
||||||
|
|
|
@ -265,6 +265,7 @@ class TrustedFilter(filters.BaseHostFilter):
|
||||||
# The hosts the instances are running on doesn't change within a request
|
# The hosts the instances are running on doesn't change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
instance_type = filter_properties.get('instance_type', {})
|
instance_type = filter_properties.get('instance_type', {})
|
||||||
extra = instance_type.get('extra_specs', {})
|
extra = instance_type.get('extra_specs', {})
|
||||||
|
|
|
@ -25,6 +25,7 @@ class TypeAffinityFilter(filters.BaseHostFilter):
|
||||||
(spread) set to 1 (default).
|
(spread) set to 1 (default).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
"""Dynamically limits hosts to one instance type
|
"""Dynamically limits hosts to one instance type
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ class AggregateTypeAffinityFilter(filters.BaseHostFilter):
|
||||||
# Aggregate data does not change within a request
|
# Aggregate data does not change within a request
|
||||||
run_filter_once_per_request = True
|
run_filter_once_per_request = True
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
def host_passes(self, host_state, filter_properties):
|
def host_passes(self, host_state, filter_properties):
|
||||||
instance_type = filter_properties.get('instance_type')
|
instance_type = filter_properties.get('instance_type')
|
||||||
|
|
||||||
|
|
|
@ -449,7 +449,7 @@ class HostManager(object):
|
||||||
raise exception.SchedulerHostFilterNotFound(filter_name=msg)
|
raise exception.SchedulerHostFilterNotFound(filter_name=msg)
|
||||||
return good_filters
|
return good_filters
|
||||||
|
|
||||||
def get_filtered_hosts(self, hosts, filter_properties,
|
def get_filtered_hosts(self, hosts, spec_obj,
|
||||||
filter_class_names=None, index=0):
|
filter_class_names=None, index=0):
|
||||||
"""Filter hosts and return only ones passing all filters."""
|
"""Filter hosts and return only ones passing all filters."""
|
||||||
|
|
||||||
|
@ -499,9 +499,9 @@ class HostManager(object):
|
||||||
filters = self.default_filters
|
filters = self.default_filters
|
||||||
else:
|
else:
|
||||||
filters = self._choose_host_filters(filter_class_names)
|
filters = self._choose_host_filters(filter_class_names)
|
||||||
ignore_hosts = filter_properties.get('ignore_hosts', [])
|
ignore_hosts = spec_obj.ignore_hosts or []
|
||||||
force_hosts = filter_properties.get('force_hosts', [])
|
force_hosts = spec_obj.force_hosts or []
|
||||||
force_nodes = filter_properties.get('force_nodes', [])
|
force_nodes = spec_obj.force_nodes or []
|
||||||
|
|
||||||
if ignore_hosts or force_hosts or force_nodes:
|
if ignore_hosts or force_hosts or force_nodes:
|
||||||
# NOTE(deva): we can't assume "host" is unique because
|
# NOTE(deva): we can't assume "host" is unique because
|
||||||
|
@ -523,12 +523,12 @@ class HostManager(object):
|
||||||
hosts = six.itervalues(name_to_cls_map)
|
hosts = six.itervalues(name_to_cls_map)
|
||||||
|
|
||||||
return self.filter_handler.get_filtered_objects(filters,
|
return self.filter_handler.get_filtered_objects(filters,
|
||||||
hosts, filter_properties, index)
|
hosts, spec_obj, index)
|
||||||
|
|
||||||
def get_weighed_hosts(self, hosts, weight_properties):
|
def get_weighed_hosts(self, hosts, spec_obj):
|
||||||
"""Weigh the hosts."""
|
"""Weigh the hosts."""
|
||||||
return self.weight_handler.get_weighed_objects(self.weighers,
|
return self.weight_handler.get_weighed_objects(self.weighers,
|
||||||
hosts, weight_properties)
|
hosts, spec_obj)
|
||||||
|
|
||||||
def get_all_host_states(self, context):
|
def get_all_host_states(self, context):
|
||||||
"""Returns a list of HostStates that represents all the hosts
|
"""Returns a list of HostStates that represents all the hosts
|
||||||
|
|
|
@ -74,7 +74,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||||
os_type='Linux',
|
os_type='Linux',
|
||||||
uuid='fake-uuid',
|
uuid='fake-uuid',
|
||||||
pci_requests=None,
|
pci_requests=None,
|
||||||
numa_topology=None)
|
numa_topology=None,
|
||||||
|
instance_group=None)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
weighed_hosts = self.driver._schedule(self.context, spec_obj)
|
weighed_hosts = self.driver._schedule(self.context, spec_obj)
|
||||||
self.assertEqual(len(weighed_hosts), 10)
|
self.assertEqual(len(weighed_hosts), 10)
|
||||||
|
@ -143,7 +144,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||||
ephemeral_gb=0,
|
ephemeral_gb=0,
|
||||||
vcpus=1),
|
vcpus=1),
|
||||||
pci_requests=None,
|
pci_requests=None,
|
||||||
numa_topology=None)
|
numa_topology=None,
|
||||||
|
instance_group=None)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
hosts = self.driver._schedule(self.context, spec_obj)
|
hosts = self.driver._schedule(self.context, spec_obj)
|
||||||
|
|
||||||
|
@ -178,7 +180,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||||
ephemeral_gb=0,
|
ephemeral_gb=0,
|
||||||
vcpus=1),
|
vcpus=1),
|
||||||
pci_requests=None,
|
pci_requests=None,
|
||||||
numa_topology=None)
|
numa_topology=None,
|
||||||
|
instance_group=None)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
hosts = self.driver._schedule(self.context, spec_obj)
|
hosts = self.driver._schedule(self.context, spec_obj)
|
||||||
|
|
||||||
|
@ -222,7 +225,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
||||||
ephemeral_gb=0,
|
ephemeral_gb=0,
|
||||||
vcpus=1),
|
vcpus=1),
|
||||||
pci_requests=None,
|
pci_requests=None,
|
||||||
numa_topology=None)
|
numa_topology=None,
|
||||||
|
instance_group=None)
|
||||||
|
|
||||||
self.stubs.Set(weights.HostWeightHandler,
|
self.stubs.Set(weights.HostWeightHandler,
|
||||||
'get_weighed_objects', _fake_weigh_objects)
|
'get_weighed_objects', _fake_weigh_objects)
|
||||||
|
|
|
@ -23,6 +23,7 @@ from six.moves import range
|
||||||
|
|
||||||
from nova import filters
|
from nova import filters
|
||||||
from nova import loadables
|
from nova import loadables
|
||||||
|
from nova import objects
|
||||||
from nova import test
|
from nova import test
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,18 +47,18 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_filter_all(self):
|
def test_filter_all(self):
|
||||||
filter_obj_list = ['obj1', 'obj2', 'obj3']
|
filter_obj_list = ['obj1', 'obj2', 'obj3']
|
||||||
filter_properties = 'fake_filter_properties'
|
spec_obj = objects.RequestSpec()
|
||||||
base_filter = filters.BaseFilter()
|
base_filter = filters.BaseFilter()
|
||||||
|
|
||||||
self.mox.StubOutWithMock(base_filter, '_filter_one')
|
self.mox.StubOutWithMock(base_filter, '_filter_one')
|
||||||
|
|
||||||
base_filter._filter_one('obj1', filter_properties).AndReturn(True)
|
base_filter._filter_one('obj1', spec_obj).AndReturn(True)
|
||||||
base_filter._filter_one('obj2', filter_properties).AndReturn(False)
|
base_filter._filter_one('obj2', spec_obj).AndReturn(False)
|
||||||
base_filter._filter_one('obj3', filter_properties).AndReturn(True)
|
base_filter._filter_one('obj3', spec_obj).AndReturn(True)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
result = base_filter.filter_all(filter_obj_list, filter_properties)
|
result = base_filter.filter_all(filter_obj_list, spec_obj)
|
||||||
self.assertTrue(inspect.isgenerator(result))
|
self.assertTrue(inspect.isgenerator(result))
|
||||||
self.assertEqual(['obj1', 'obj3'], list(result))
|
self.assertEqual(['obj1', 'obj3'], list(result))
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
# call filter_all() with generators returned from previous calls
|
# call filter_all() with generators returned from previous calls
|
||||||
# to filter_all().
|
# to filter_all().
|
||||||
filter_obj_list = ['obj1', 'obj2', 'obj3']
|
filter_obj_list = ['obj1', 'obj2', 'obj3']
|
||||||
filter_properties = 'fake_filter_properties'
|
spec_obj = objects.RequestSpec()
|
||||||
base_filter = filters.BaseFilter()
|
base_filter = filters.BaseFilter()
|
||||||
|
|
||||||
self.mox.StubOutWithMock(base_filter, '_filter_one')
|
self.mox.StubOutWithMock(base_filter, '_filter_one')
|
||||||
|
@ -83,16 +84,16 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
# After that, 'obj3' gets yielded 'total_iterations' number of
|
# After that, 'obj3' gets yielded 'total_iterations' number of
|
||||||
# times.
|
# times.
|
||||||
for x in range(total_iterations):
|
for x in range(total_iterations):
|
||||||
base_filter._filter_one('obj1', filter_properties).AndReturn(True)
|
base_filter._filter_one('obj1', spec_obj).AndReturn(True)
|
||||||
base_filter._filter_one('obj2', filter_properties).AndReturn(False)
|
base_filter._filter_one('obj2', spec_obj).AndReturn(False)
|
||||||
for x in range(total_iterations):
|
for x in range(total_iterations):
|
||||||
base_filter._filter_one('obj3', filter_properties).AndReturn(True)
|
base_filter._filter_one('obj3', spec_obj).AndReturn(True)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
objs = iter(filter_obj_list)
|
objs = iter(filter_obj_list)
|
||||||
for x in range(total_iterations):
|
for x in range(total_iterations):
|
||||||
# Pass in generators returned from previous calls.
|
# Pass in generators returned from previous calls.
|
||||||
objs = base_filter.filter_all(objs, filter_properties)
|
objs = base_filter.filter_all(objs, spec_obj)
|
||||||
self.assertTrue(inspect.isgenerator(objs))
|
self.assertTrue(inspect.isgenerator(objs))
|
||||||
self.assertEqual(['obj1', 'obj3'], list(objs))
|
self.assertEqual(['obj1', 'obj3'], list(objs))
|
||||||
|
|
||||||
|
@ -100,7 +101,7 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
filter_objs_initial = ['initial', 'filter1', 'objects1']
|
filter_objs_initial = ['initial', 'filter1', 'objects1']
|
||||||
filter_objs_second = ['second', 'filter2', 'objects2']
|
filter_objs_second = ['second', 'filter2', 'objects2']
|
||||||
filter_objs_last = ['last', 'filter3', 'objects3']
|
filter_objs_last = ['last', 'filter3', 'objects3']
|
||||||
filter_properties = 'fake_filter_properties'
|
spec_obj = objects.RequestSpec()
|
||||||
|
|
||||||
def _fake_base_loader_init(*args, **kwargs):
|
def _fake_base_loader_init(*args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
@ -122,10 +123,10 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
||||||
filt1_mock.filter_all(filter_objs_initial,
|
filt1_mock.filter_all(filter_objs_initial,
|
||||||
filter_properties).AndReturn(filter_objs_second)
|
spec_obj).AndReturn(filter_objs_second)
|
||||||
filt2_mock.run_filter_for_index(0).AndReturn(True)
|
filt2_mock.run_filter_for_index(0).AndReturn(True)
|
||||||
filt2_mock.filter_all(filter_objs_second,
|
filt2_mock.filter_all(filter_objs_second,
|
||||||
filter_properties).AndReturn(filter_objs_last)
|
spec_obj).AndReturn(filter_objs_last)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
filter_mocks = [filt1_mock, filt2_mock]
|
filter_mocks = [filt1_mock, filt2_mock]
|
||||||
result = filter_handler.get_filtered_objects(filter_mocks,
|
result = filter_handler.get_filtered_objects(filter_mocks,
|
||||||
filter_objs_initial,
|
filter_objs_initial,
|
||||||
filter_properties)
|
spec_obj)
|
||||||
self.assertEqual(filter_objs_last, result)
|
self.assertEqual(filter_objs_last, result)
|
||||||
|
|
||||||
def test_get_filtered_objects_for_index(self):
|
def test_get_filtered_objects_for_index(self):
|
||||||
|
@ -142,7 +143,7 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
"""
|
"""
|
||||||
filter_objs_initial = ['initial', 'filter1', 'objects1']
|
filter_objs_initial = ['initial', 'filter1', 'objects1']
|
||||||
filter_objs_second = ['second', 'filter2', 'objects2']
|
filter_objs_second = ['second', 'filter2', 'objects2']
|
||||||
filter_properties = 'fake_filter_properties'
|
spec_obj = objects.RequestSpec()
|
||||||
|
|
||||||
def _fake_base_loader_init(*args, **kwargs):
|
def _fake_base_loader_init(*args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
@ -164,7 +165,7 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
||||||
filt1_mock.filter_all(filter_objs_initial,
|
filt1_mock.filter_all(filter_objs_initial,
|
||||||
filter_properties).AndReturn(filter_objs_second)
|
spec_obj).AndReturn(filter_objs_second)
|
||||||
# return false so filter_all will not be called
|
# return false so filter_all will not be called
|
||||||
filt2_mock.run_filter_for_index(0).AndReturn(False)
|
filt2_mock.run_filter_for_index(0).AndReturn(False)
|
||||||
|
|
||||||
|
@ -174,11 +175,11 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
filter_mocks = [filt1_mock, filt2_mock]
|
filter_mocks = [filt1_mock, filt2_mock]
|
||||||
filter_handler.get_filtered_objects(filter_mocks,
|
filter_handler.get_filtered_objects(filter_mocks,
|
||||||
filter_objs_initial,
|
filter_objs_initial,
|
||||||
filter_properties)
|
spec_obj)
|
||||||
|
|
||||||
def test_get_filtered_objects_none_response(self):
|
def test_get_filtered_objects_none_response(self):
|
||||||
filter_objs_initial = ['initial', 'filter1', 'objects1']
|
filter_objs_initial = ['initial', 'filter1', 'objects1']
|
||||||
filter_properties = 'fake_filter_properties'
|
spec_obj = objects.RequestSpec()
|
||||||
|
|
||||||
def _fake_base_loader_init(*args, **kwargs):
|
def _fake_base_loader_init(*args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
@ -200,26 +201,86 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
filt1_mock.run_filter_for_index(0).AndReturn(True)
|
||||||
filt1_mock.filter_all(filter_objs_initial,
|
filt1_mock.filter_all(filter_objs_initial,
|
||||||
filter_properties).AndReturn(None)
|
spec_obj).AndReturn(None)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
filter_handler = filters.BaseFilterHandler(filters.BaseFilter)
|
filter_handler = filters.BaseFilterHandler(filters.BaseFilter)
|
||||||
filter_mocks = [filt1_mock, filt2_mock]
|
filter_mocks = [filt1_mock, filt2_mock]
|
||||||
result = filter_handler.get_filtered_objects(filter_mocks,
|
result = filter_handler.get_filtered_objects(filter_mocks,
|
||||||
filter_objs_initial,
|
filter_objs_initial,
|
||||||
filter_properties)
|
spec_obj)
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_get_filtered_objects_info_log_none_returned(self):
|
def test_get_filtered_objects_info_log_none_returned(self):
|
||||||
LOG = filters.LOG
|
LOG = filters.LOG
|
||||||
|
|
||||||
class FilterA(filters.BaseFilter):
|
class FilterA(filters.BaseFilter):
|
||||||
def filter_all(self, list_objs, filter_properties):
|
def filter_all(self, list_objs, spec_obj):
|
||||||
# return all but the first object
|
# return all but the first object
|
||||||
return list_objs[1:]
|
return list_objs[1:]
|
||||||
|
|
||||||
class FilterB(filters.BaseFilter):
|
class FilterB(filters.BaseFilter):
|
||||||
def filter_all(self, list_objs, filter_properties):
|
def filter_all(self, list_objs, spec_obj):
|
||||||
|
# return an empty list
|
||||||
|
return []
|
||||||
|
|
||||||
|
filter_a = FilterA()
|
||||||
|
filter_b = FilterB()
|
||||||
|
all_filters = [filter_a, filter_b]
|
||||||
|
hosts = ["Host0", "Host1", "Host2"]
|
||||||
|
fake_uuid = "uuid"
|
||||||
|
spec_obj = objects.RequestSpec(instance_uuid=fake_uuid)
|
||||||
|
with mock.patch.object(LOG, "info") as mock_log:
|
||||||
|
result = self.filter_handler.get_filtered_objects(
|
||||||
|
all_filters, hosts, spec_obj)
|
||||||
|
self.assertFalse(result)
|
||||||
|
# FilterA should leave Host1 and Host2; FilterB should leave None.
|
||||||
|
exp_output = ("['FilterA: (start: 3, end: 2)', "
|
||||||
|
"'FilterB: (start: 2, end: 0)']")
|
||||||
|
cargs = mock_log.call_args[0][0]
|
||||||
|
self.assertIn("with instance ID '%s'" % fake_uuid, cargs)
|
||||||
|
self.assertIn(exp_output, cargs)
|
||||||
|
|
||||||
|
def test_get_filtered_objects_debug_log_none_returned(self):
|
||||||
|
LOG = filters.LOG
|
||||||
|
|
||||||
|
class FilterA(filters.BaseFilter):
|
||||||
|
def filter_all(self, list_objs, spec_obj):
|
||||||
|
# return all but the first object
|
||||||
|
return list_objs[1:]
|
||||||
|
|
||||||
|
class FilterB(filters.BaseFilter):
|
||||||
|
def filter_all(self, list_objs, spec_obj):
|
||||||
|
# return an empty list
|
||||||
|
return []
|
||||||
|
|
||||||
|
filter_a = FilterA()
|
||||||
|
filter_b = FilterB()
|
||||||
|
all_filters = [filter_a, filter_b]
|
||||||
|
hosts = ["Host0", "Host1", "Host2"]
|
||||||
|
fake_uuid = "uuid"
|
||||||
|
spec_obj = objects.RequestSpec(instance_uuid=fake_uuid)
|
||||||
|
with mock.patch.object(LOG, "debug") as mock_log:
|
||||||
|
result = self.filter_handler.get_filtered_objects(
|
||||||
|
all_filters, hosts, spec_obj)
|
||||||
|
self.assertFalse(result)
|
||||||
|
# FilterA should leave Host1 and Host2; FilterB should leave None.
|
||||||
|
exp_output = ("[('FilterA', [('Host1', ''), ('Host2', '')]), " +
|
||||||
|
"('FilterB', None)]")
|
||||||
|
cargs = mock_log.call_args[0][0]
|
||||||
|
self.assertIn("with instance ID '%s'" % fake_uuid, cargs)
|
||||||
|
self.assertIn(exp_output, cargs)
|
||||||
|
|
||||||
|
def test_get_filtered_objects_compatible_with_filt_props_dicts(self):
|
||||||
|
LOG = filters.LOG
|
||||||
|
|
||||||
|
class FilterA(filters.BaseFilter):
|
||||||
|
def filter_all(self, list_objs, spec_obj):
|
||||||
|
# return all but the first object
|
||||||
|
return list_objs[1:]
|
||||||
|
|
||||||
|
class FilterB(filters.BaseFilter):
|
||||||
|
def filter_all(self, list_objs, spec_obj):
|
||||||
# return an empty list
|
# return an empty list
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@ -240,34 +301,3 @@ class FiltersTestCase(test.NoDBTestCase):
|
||||||
cargs = mock_log.call_args[0][0]
|
cargs = mock_log.call_args[0][0]
|
||||||
self.assertIn("with instance ID '%s'" % fake_uuid, cargs)
|
self.assertIn("with instance ID '%s'" % fake_uuid, cargs)
|
||||||
self.assertIn(exp_output, cargs)
|
self.assertIn(exp_output, cargs)
|
||||||
|
|
||||||
def test_get_filtered_objects_debug_log_none_returned(self):
|
|
||||||
LOG = filters.LOG
|
|
||||||
|
|
||||||
class FilterA(filters.BaseFilter):
|
|
||||||
def filter_all(self, list_objs, filter_properties):
|
|
||||||
# return all but the first object
|
|
||||||
return list_objs[1:]
|
|
||||||
|
|
||||||
class FilterB(filters.BaseFilter):
|
|
||||||
def filter_all(self, list_objs, filter_properties):
|
|
||||||
# return an empty list
|
|
||||||
return []
|
|
||||||
|
|
||||||
filter_a = FilterA()
|
|
||||||
filter_b = FilterB()
|
|
||||||
all_filters = [filter_a, filter_b]
|
|
||||||
hosts = ["Host0", "Host1", "Host2"]
|
|
||||||
fake_uuid = "uuid"
|
|
||||||
filt_props = {"request_spec": {"instance_properties": {
|
|
||||||
"uuid": fake_uuid}}}
|
|
||||||
with mock.patch.object(LOG, "debug") as mock_log:
|
|
||||||
result = self.filter_handler.get_filtered_objects(
|
|
||||||
all_filters, hosts, filt_props)
|
|
||||||
self.assertFalse(result)
|
|
||||||
# FilterA should leave Host1 and Host2; FilterB should leave None.
|
|
||||||
exp_output = ("[('FilterA', [('Host1', ''), ('Host2', '')]), " +
|
|
||||||
"('FilterB', None)]")
|
|
||||||
cargs = mock_log.call_args[0][0]
|
|
||||||
self.assertIn("with instance ID '%s'" % fake_uuid, cargs)
|
|
||||||
self.assertIn(exp_output, cargs)
|
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
"""
|
"""
|
||||||
Tests For Scheduler Host Filters.
|
Tests For Scheduler Host Filters.
|
||||||
"""
|
"""
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from nova import objects
|
||||||
from nova.scheduler import filters
|
from nova.scheduler import filters
|
||||||
from nova.scheduler.filters import all_hosts_filter
|
from nova.scheduler.filters import all_hosts_filter
|
||||||
from nova.scheduler.filters import compute_filter
|
from nova.scheduler.filters import compute_filter
|
||||||
|
@ -36,3 +38,27 @@ class HostFiltersTestCase(test.NoDBTestCase):
|
||||||
filt_cls = all_hosts_filter.AllHostsFilter()
|
filt_cls = all_hosts_filter.AllHostsFilter()
|
||||||
host = fakes.FakeHostState('host1', 'node1', {})
|
host = fakes.FakeHostState('host1', 'node1', {})
|
||||||
self.assertTrue(filt_cls.host_passes(host, {}))
|
self.assertTrue(filt_cls.host_passes(host, {}))
|
||||||
|
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'to_legacy_request_spec_dict')
|
||||||
|
@mock.patch.object(objects.RequestSpec, 'to_legacy_filter_properties_dict')
|
||||||
|
def test_compat_legacy_props(self, to_props, to_spec):
|
||||||
|
fake_flavor = objects.Flavor()
|
||||||
|
fake_context = mock.Mock()
|
||||||
|
fake_spec = objects.RequestSpec(context=fake_context,
|
||||||
|
flavor=fake_flavor)
|
||||||
|
fake_spec.config_options = None
|
||||||
|
to_props.return_value = {'prop1': 'val1'}
|
||||||
|
to_spec.return_value = {'spec1': 'val2'}
|
||||||
|
|
||||||
|
@filters.compat_legacy_props
|
||||||
|
def fake_host_passes(self, host_state, filter_properties):
|
||||||
|
# NOTE(sbauza): Convenient way to verify the passed properties
|
||||||
|
return filter_properties
|
||||||
|
|
||||||
|
expected = {'prop1': 'val1',
|
||||||
|
'request_spec': {'spec1': 'val2'},
|
||||||
|
'instance_type': fake_flavor,
|
||||||
|
'context': fake_context,
|
||||||
|
'config_options': None}
|
||||||
|
self.assertEqual(expected,
|
||||||
|
fake_host_passes('self', 'host_state', fake_spec))
|
||||||
|
|
|
@ -207,7 +207,10 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
self.assertEqual(set(info['expected_objs']), set(result))
|
self.assertEqual(set(info['expected_objs']), set(result))
|
||||||
|
|
||||||
def test_get_filtered_hosts(self):
|
def test_get_filtered_hosts(self):
|
||||||
fake_properties = {'moo': 1, 'cow': 2}
|
fake_properties = objects.RequestSpec(ignore_hosts=[],
|
||||||
|
instance_uuid='fake-uuid1',
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
info = {'expected_objs': self.fake_hosts,
|
info = {'expected_objs': self.fake_hosts,
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -220,7 +223,10 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
@mock.patch.object(FakeFilterClass2, '_filter_one', return_value=True)
|
@mock.patch.object(FakeFilterClass2, '_filter_one', return_value=True)
|
||||||
def test_get_filtered_hosts_with_specified_filters(self, mock_filter_one):
|
def test_get_filtered_hosts_with_specified_filters(self, mock_filter_one):
|
||||||
fake_properties = {'moo': 1, 'cow': 2}
|
fake_properties = objects.RequestSpec(ignore_hosts=[],
|
||||||
|
instance_uuid='fake-uuid1',
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
specified_filters = ['FakeFilterClass1', 'FakeFilterClass2']
|
specified_filters = ['FakeFilterClass1', 'FakeFilterClass2']
|
||||||
info = {'expected_objs': self.fake_hosts,
|
info = {'expected_objs': self.fake_hosts,
|
||||||
|
@ -232,8 +238,12 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
self._verify_result(info, result)
|
self._verify_result(info, result)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore(self):
|
def test_get_filtered_hosts_with_ignore(self):
|
||||||
fake_properties = {'ignore_hosts': ['fake_host1', 'fake_host3',
|
fake_properties = objects.RequestSpec(
|
||||||
'fake_host5', 'fake_multihost']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=['fake_host1', 'fake_host3',
|
||||||
|
'fake_host5', 'fake_multihost'],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
# [1] and [3] are host2 and host4
|
# [1] and [3] are host2 and host4
|
||||||
info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]],
|
info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]],
|
||||||
|
@ -245,8 +255,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
self._verify_result(info, result)
|
self._verify_result(info, result)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_hosts(self):
|
def test_get_filtered_hosts_with_force_hosts(self):
|
||||||
fake_properties = {'force_hosts': ['fake_host1', 'fake_host3',
|
fake_properties = objects.RequestSpec(
|
||||||
'fake_host5']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_host1', 'fake_host3', 'fake_host5'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
# [0] and [2] are host1 and host3
|
# [0] and [2] are host1 and host3
|
||||||
info = {'expected_objs': [self.fake_hosts[0], self.fake_hosts[2]],
|
info = {'expected_objs': [self.fake_hosts[0], self.fake_hosts[2]],
|
||||||
|
@ -258,7 +271,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
self._verify_result(info, result, False)
|
self._verify_result(info, result, False)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_no_matching_force_hosts(self):
|
def test_get_filtered_hosts_with_no_matching_force_hosts(self):
|
||||||
fake_properties = {'force_hosts': ['fake_host5', 'fake_host6']}
|
fake_properties = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_host5', 'fake_host6'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
info = {'expected_objs': [],
|
info = {'expected_objs': [],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -270,8 +287,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore_and_force_hosts(self):
|
def test_get_filtered_hosts_with_ignore_and_force_hosts(self):
|
||||||
# Ensure ignore_hosts processed before force_hosts in host filters.
|
# Ensure ignore_hosts processed before force_hosts in host filters.
|
||||||
fake_properties = {'force_hosts': ['fake_host3', 'fake_host1'],
|
fake_properties = objects.RequestSpec(
|
||||||
'ignore_hosts': ['fake_host1']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=['fake_host1'],
|
||||||
|
force_hosts=['fake_host3', 'fake_host1'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
# only fake_host3 should be left.
|
# only fake_host3 should be left.
|
||||||
info = {'expected_objs': [self.fake_hosts[2]],
|
info = {'expected_objs': [self.fake_hosts[2]],
|
||||||
|
@ -284,7 +304,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_host_and_many_nodes(self):
|
def test_get_filtered_hosts_with_force_host_and_many_nodes(self):
|
||||||
# Ensure all nodes returned for a host with many nodes
|
# Ensure all nodes returned for a host with many nodes
|
||||||
fake_properties = {'force_hosts': ['fake_multihost']}
|
fake_properties = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_multihost'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
info = {'expected_objs': [self.fake_hosts[4], self.fake_hosts[5],
|
info = {'expected_objs': [self.fake_hosts[4], self.fake_hosts[5],
|
||||||
self.fake_hosts[6], self.fake_hosts[7]],
|
self.fake_hosts[6], self.fake_hosts[7]],
|
||||||
|
@ -296,8 +320,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
self._verify_result(info, result, False)
|
self._verify_result(info, result, False)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_nodes(self):
|
def test_get_filtered_hosts_with_force_nodes(self):
|
||||||
fake_properties = {'force_nodes': ['fake-node2', 'fake-node4',
|
fake_properties = objects.RequestSpec(
|
||||||
'fake-node9']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=['fake-node2', 'fake-node4', 'fake-node9'])
|
||||||
|
|
||||||
# [5] is fake-node2, [7] is fake-node4
|
# [5] is fake-node2, [7] is fake-node4
|
||||||
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
||||||
|
@ -310,8 +337,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_hosts_and_nodes(self):
|
def test_get_filtered_hosts_with_force_hosts_and_nodes(self):
|
||||||
# Ensure only overlapping results if both force host and node
|
# Ensure only overlapping results if both force host and node
|
||||||
fake_properties = {'force_hosts': ['fake_host1', 'fake_multihost'],
|
fake_properties = objects.RequestSpec(
|
||||||
'force_nodes': ['fake-node2', 'fake-node9']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake-host1', 'fake_multihost'],
|
||||||
|
force_nodes=['fake-node2', 'fake-node9'])
|
||||||
|
|
||||||
# [5] is fake-node2
|
# [5] is fake-node2
|
||||||
info = {'expected_objs': [self.fake_hosts[5]],
|
info = {'expected_objs': [self.fake_hosts[5]],
|
||||||
|
@ -324,8 +354,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_hosts_and_wrong_nodes(self):
|
def test_get_filtered_hosts_with_force_hosts_and_wrong_nodes(self):
|
||||||
# Ensure non-overlapping force_node and force_host yield no result
|
# Ensure non-overlapping force_node and force_host yield no result
|
||||||
fake_properties = {'force_hosts': ['fake_multihost'],
|
fake_properties = objects.RequestSpec(
|
||||||
'force_nodes': ['fake-node']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_multihost'],
|
||||||
|
force_nodes=['fake-node'])
|
||||||
|
|
||||||
info = {'expected_objs': [],
|
info = {'expected_objs': [],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -337,8 +370,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore_hosts_and_force_nodes(self):
|
def test_get_filtered_hosts_with_ignore_hosts_and_force_nodes(self):
|
||||||
# Ensure ignore_hosts can coexist with force_nodes
|
# Ensure ignore_hosts can coexist with force_nodes
|
||||||
fake_properties = {'force_nodes': ['fake-node4', 'fake-node2'],
|
fake_properties = objects.RequestSpec(
|
||||||
'ignore_hosts': ['fake_host1', 'fake_host2']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=['fake_host1', 'fake_host2'],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=['fake-node4', 'fake-node2'])
|
||||||
|
|
||||||
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -350,8 +386,11 @@ class HostManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore_hosts_and_force_same_nodes(self):
|
def test_get_filtered_hosts_with_ignore_hosts_and_force_same_nodes(self):
|
||||||
# Ensure ignore_hosts is processed before force_nodes
|
# Ensure ignore_hosts is processed before force_nodes
|
||||||
fake_properties = {'force_nodes': ['fake_node4', 'fake_node2'],
|
fake_properties = objects.RequestSpec(
|
||||||
'ignore_hosts': ['fake_multihost']}
|
instance_uuid='fake-uuid1',
|
||||||
|
ignore_hosts=['fake_multihost'],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=['fake_node4', 'fake_node2'])
|
||||||
|
|
||||||
info = {'expected_objs': [],
|
info = {'expected_objs': [],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -885,9 +924,9 @@ class HostStateTestCase(test.NoDBTestCase):
|
||||||
numa_fit_mock.return_value = fake_numa_topology
|
numa_fit_mock.return_value = fake_numa_topology
|
||||||
instance_init_mock.return_value = fake_instance
|
instance_init_mock.return_value = fake_instance
|
||||||
spec_obj = objects.RequestSpec(
|
spec_obj = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0,
|
flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0,
|
||||||
vcpus=0),
|
vcpus=0),
|
||||||
uuid='fake-uuid',
|
|
||||||
numa_topology=fake_numa_topology,
|
numa_topology=fake_numa_topology,
|
||||||
pci_requests=objects.InstancePCIRequests(requests=[]))
|
pci_requests=objects.InstancePCIRequests(requests=[]))
|
||||||
host = host_manager.HostState("fakehost", "fakenode")
|
host = host_manager.HostState("fakehost", "fakenode")
|
||||||
|
@ -905,9 +944,9 @@ class HostStateTestCase(test.NoDBTestCase):
|
||||||
second_numa_topology = objects.InstanceNUMATopology(
|
second_numa_topology = objects.InstanceNUMATopology(
|
||||||
cells=[objects.InstanceNUMACell()])
|
cells=[objects.InstanceNUMACell()])
|
||||||
spec_obj = objects.RequestSpec(
|
spec_obj = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0,
|
flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0,
|
||||||
vcpus=0),
|
vcpus=0),
|
||||||
uuid='fake-uuid',
|
|
||||||
numa_topology=second_numa_topology,
|
numa_topology=second_numa_topology,
|
||||||
pci_requests=objects.InstancePCIRequests(requests=[]))
|
pci_requests=objects.InstancePCIRequests(requests=[]))
|
||||||
second_host_numa_topology = mock.Mock()
|
second_host_numa_topology = mock.Mock()
|
||||||
|
@ -936,6 +975,7 @@ class HostStateTestCase(test.NoDBTestCase):
|
||||||
for r in fake_requests],
|
for r in fake_requests],
|
||||||
instance_uuid='fake-uuid')
|
instance_uuid='fake-uuid')
|
||||||
req_spec = objects.RequestSpec(
|
req_spec = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
project_id='12345',
|
project_id='12345',
|
||||||
numa_topology=inst_topology,
|
numa_topology=inst_topology,
|
||||||
pci_requests=fake_requests_obj,
|
pci_requests=fake_requests_obj,
|
||||||
|
@ -968,6 +1008,7 @@ class HostStateTestCase(test.NoDBTestCase):
|
||||||
for r in fake_requests],
|
for r in fake_requests],
|
||||||
instance_uuid='fake-uuid')
|
instance_uuid='fake-uuid')
|
||||||
req_spec = objects.RequestSpec(
|
req_spec = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
project_id='12345',
|
project_id='12345',
|
||||||
numa_topology=None,
|
numa_topology=None,
|
||||||
pci_requests=fake_requests_obj,
|
pci_requests=fake_requests_obj,
|
||||||
|
|
|
@ -329,7 +329,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
self.assertEqual(set(info['expected_objs']), set(result))
|
self.assertEqual(set(info['expected_objs']), set(result))
|
||||||
|
|
||||||
def test_get_filtered_hosts(self):
|
def test_get_filtered_hosts(self):
|
||||||
fake_properties = {'moo': 1, 'cow': 2}
|
fake_properties = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
info = {'expected_objs': self.fake_hosts,
|
info = {'expected_objs': self.fake_hosts,
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -342,7 +346,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
|
|
||||||
@mock.patch.object(FakeFilterClass2, '_filter_one', return_value=True)
|
@mock.patch.object(FakeFilterClass2, '_filter_one', return_value=True)
|
||||||
def test_get_filtered_hosts_with_specified_filters(self, mock_filter_one):
|
def test_get_filtered_hosts_with_specified_filters(self, mock_filter_one):
|
||||||
fake_properties = {'moo': 1, 'cow': 2}
|
fake_properties = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
specified_filters = ['FakeFilterClass1', 'FakeFilterClass2']
|
specified_filters = ['FakeFilterClass1', 'FakeFilterClass2']
|
||||||
info = {'expected_objs': self.fake_hosts,
|
info = {'expected_objs': self.fake_hosts,
|
||||||
|
@ -354,8 +362,12 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
self._verify_result(info, result)
|
self._verify_result(info, result)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore(self):
|
def test_get_filtered_hosts_with_ignore(self):
|
||||||
fake_properties = {'ignore_hosts': ['fake_host1', 'fake_host3',
|
fake_properties = objects.RequestSpec(
|
||||||
'fake_host5', 'fake_multihost']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=['fake_host1', 'fake_host3',
|
||||||
|
'fake_host5', 'fake_multihost'],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
# [1] and [3] are host2 and host4
|
# [1] and [3] are host2 and host4
|
||||||
info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]],
|
info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]],
|
||||||
|
@ -367,8 +379,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
self._verify_result(info, result)
|
self._verify_result(info, result)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_hosts(self):
|
def test_get_filtered_hosts_with_force_hosts(self):
|
||||||
fake_properties = {'force_hosts': ['fake_host1', 'fake_host3',
|
fake_properties = objects.RequestSpec(
|
||||||
'fake_host5']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_host1', 'fake_host3', 'fake_host5'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
# [0] and [2] are host1 and host3
|
# [0] and [2] are host1 and host3
|
||||||
info = {'expected_objs': [self.fake_hosts[0], self.fake_hosts[2]],
|
info = {'expected_objs': [self.fake_hosts[0], self.fake_hosts[2]],
|
||||||
|
@ -380,7 +395,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
self._verify_result(info, result, False)
|
self._verify_result(info, result, False)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_no_matching_force_hosts(self):
|
def test_get_filtered_hosts_with_no_matching_force_hosts(self):
|
||||||
fake_properties = {'force_hosts': ['fake_host5', 'fake_host6']}
|
fake_properties = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_host5', 'fake_host6'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
info = {'expected_objs': [],
|
info = {'expected_objs': [],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -392,8 +411,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore_and_force_hosts(self):
|
def test_get_filtered_hosts_with_ignore_and_force_hosts(self):
|
||||||
# Ensure ignore_hosts processed before force_hosts in host filters.
|
# Ensure ignore_hosts processed before force_hosts in host filters.
|
||||||
fake_properties = {'force_hosts': ['fake_host3', 'fake_host1'],
|
fake_properties = objects.RequestSpec(
|
||||||
'ignore_hosts': ['fake_host1']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=['fake_host1'],
|
||||||
|
force_hosts=['fake_host3', 'fake_host1'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
# only fake_host3 should be left.
|
# only fake_host3 should be left.
|
||||||
info = {'expected_objs': [self.fake_hosts[2]],
|
info = {'expected_objs': [self.fake_hosts[2]],
|
||||||
|
@ -406,7 +428,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_host_and_many_nodes(self):
|
def test_get_filtered_hosts_with_force_host_and_many_nodes(self):
|
||||||
# Ensure all nodes returned for a host with many nodes
|
# Ensure all nodes returned for a host with many nodes
|
||||||
fake_properties = {'force_hosts': ['fake_multihost']}
|
fake_properties = objects.RequestSpec(
|
||||||
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_multihost'],
|
||||||
|
force_nodes=[])
|
||||||
|
|
||||||
info = {'expected_objs': [self.fake_hosts[4], self.fake_hosts[5],
|
info = {'expected_objs': [self.fake_hosts[4], self.fake_hosts[5],
|
||||||
self.fake_hosts[6], self.fake_hosts[7]],
|
self.fake_hosts[6], self.fake_hosts[7]],
|
||||||
|
@ -418,8 +444,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
self._verify_result(info, result, False)
|
self._verify_result(info, result, False)
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_nodes(self):
|
def test_get_filtered_hosts_with_force_nodes(self):
|
||||||
fake_properties = {'force_nodes': ['fake-node2', 'fake-node4',
|
fake_properties = objects.RequestSpec(
|
||||||
'fake-node9']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=['fake-node2', 'fake-node4', 'fake-node9'])
|
||||||
|
|
||||||
# [5] is fake-node2, [7] is fake-node4
|
# [5] is fake-node2, [7] is fake-node4
|
||||||
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
||||||
|
@ -432,8 +461,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_hosts_and_nodes(self):
|
def test_get_filtered_hosts_with_force_hosts_and_nodes(self):
|
||||||
# Ensure only overlapping results if both force host and node
|
# Ensure only overlapping results if both force host and node
|
||||||
fake_properties = {'force_hosts': ['fake_host1', 'fake_multihost'],
|
fake_properties = objects.RequestSpec(
|
||||||
'force_nodes': ['fake-node2', 'fake-node9']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_host1', 'fake_multihost'],
|
||||||
|
force_nodes=['fake-node2', 'fake-node9'])
|
||||||
|
|
||||||
# [5] is fake-node2
|
# [5] is fake-node2
|
||||||
info = {'expected_objs': [self.fake_hosts[5]],
|
info = {'expected_objs': [self.fake_hosts[5]],
|
||||||
|
@ -446,8 +478,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_force_hosts_and_wrong_nodes(self):
|
def test_get_filtered_hosts_with_force_hosts_and_wrong_nodes(self):
|
||||||
# Ensure non-overlapping force_node and force_host yield no result
|
# Ensure non-overlapping force_node and force_host yield no result
|
||||||
fake_properties = {'force_hosts': ['fake_multihost'],
|
fake_properties = objects.RequestSpec(
|
||||||
'force_nodes': ['fake-node']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=[],
|
||||||
|
force_hosts=['fake_multihost'],
|
||||||
|
force_nodes=['fake-node'])
|
||||||
|
|
||||||
info = {'expected_objs': [],
|
info = {'expected_objs': [],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -459,8 +494,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore_hosts_and_force_nodes(self):
|
def test_get_filtered_hosts_with_ignore_hosts_and_force_nodes(self):
|
||||||
# Ensure ignore_hosts can coexist with force_nodes
|
# Ensure ignore_hosts can coexist with force_nodes
|
||||||
fake_properties = {'force_nodes': ['fake-node4', 'fake-node2'],
|
fake_properties = objects.RequestSpec(
|
||||||
'ignore_hosts': ['fake_host1', 'fake_host2']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=['fake_host1', 'fake_host2'],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=['fake-node4', 'fake-node2'])
|
||||||
|
|
||||||
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
info = {'expected_objs': [self.fake_hosts[5], self.fake_hosts[7]],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
@ -472,8 +510,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase):
|
||||||
|
|
||||||
def test_get_filtered_hosts_with_ignore_hosts_and_force_same_nodes(self):
|
def test_get_filtered_hosts_with_ignore_hosts_and_force_same_nodes(self):
|
||||||
# Ensure ignore_hosts is processed before force_nodes
|
# Ensure ignore_hosts is processed before force_nodes
|
||||||
fake_properties = {'force_nodes': ['fake_node4', 'fake_node2'],
|
fake_properties = objects.RequestSpec(
|
||||||
'ignore_hosts': ['fake_multihost']}
|
instance_uuid='fake-uuid',
|
||||||
|
ignore_hosts=['fake_multihost'],
|
||||||
|
force_hosts=[],
|
||||||
|
force_nodes=['fake_node4', 'fake_node2'])
|
||||||
|
|
||||||
info = {'expected_objs': [],
|
info = {'expected_objs': [],
|
||||||
'expected_fprops': fake_properties}
|
'expected_fprops': fake_properties}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
Filters internal interface changed using now the RequestSpec NovaObject
|
||||||
|
instead of an old filter_properties dictionary.
|
||||||
|
In case you run out-of-tree filters, you need to modify the host_passes()
|
||||||
|
method to accept a new RequestSpec object and modify the filter internals
|
||||||
|
to use that new object. You can see other in-tree filters for getting the
|
||||||
|
logic or ask for help in #openstack-nova IRC channel.
|
Loading…
Reference in New Issue