diff options
author | Sylvain Bauza <sbauza@redhat.com> | 2015-07-16 13:19:19 +0200 |
---|---|---|
committer | Sylvain Bauza <sbauza@redhat.com> | 2015-11-16 22:41:02 +0100 |
commit | aeae7040c7b4533fd4d5521d4c5172cb2fb598e7 (patch) | |
tree | 07d089219ca033e6296ba569848ef508fdac72db | |
parent | 4c06d9073cc3f3854c88314f155e739872807ef8 (diff) | |
download | nova-aeae7040c7b4533fd4d5521d4c5172cb2fb598e7.tar.gz |
Prepare filters for using RequestSpec object
Now that the FilterScheduler is using a RequestSpec object,
we need to change the filters for backporting the object
into the legacy dicts before changing each of them.
Release Notes will be updated to mention that custom filters
need to be modified to either use a RequestSpec object or
primitive them into a dict.
Partially-Implements: blueprint request-spec-object-mitaka
UpgradeImpact - see the reno file attached.
Change-Id: I14f18c4507498b1d24a9318fafc97193861ca0b6
35 files changed, 318 insertions, 108 deletions
diff --git a/nova/filters.py b/nova/filters.py index d4bbe1851c..4793e229c7 100644 --- a/nova/filters.py +++ b/nova/filters.py @@ -27,13 +27,13 @@ LOG = logging.getLogger(__name__) class BaseFilter(object): """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. Override this in a subclass. """ 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. 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. """ for obj in filter_obj_list: - if self._filter_one(obj, filter_properties): + if self._filter_one(obj, spec_obj): yield obj # 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. """ - 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) LOG.debug("Starting with %d host(s)", len(list_objs)) # 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): cls_name = filter_.__class__.__name__ 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: LOG.debug("Filter %s says to stop filtering", cls_name) return @@ -104,9 +104,17 @@ class BaseFilterHandler(loadables.BaseLoader): {'cls_name': cls_name, 'obj_len': len(list_objs)}) if not list_objs: # Log the filtration history - rspec = filter_properties.get("request_spec", {}) - inst_props = rspec.get("instance_properties", {}) - msg_dict = {"inst_uuid": inst_props.get("uuid", ""), + # NOTE(sbauza): Since the Cells scheduler still provides a legacy + # dictionary for filter_props, and since we agreed on not modifying + # 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), } full_msg = ("Filtering removed all hosts for the request with " diff --git a/nova/scheduler/filter_scheduler.py b/nova/scheduler/filter_scheduler.py index 378496254c..9e470cd49b 100644 --- a/nova/scheduler/filter_scheduler.py +++ b/nova/scheduler/filter_scheduler.py @@ -127,23 +127,12 @@ class FilterScheduler(driver.Scheduler): selected_hosts = [] num_instances = spec_obj.num_instances - # TODO(sbauza): Modify the interfaces for HostManager and filters to - # accept the RequestSpec object directly (in a later patch hopefully) - 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}) + # NOTE(sbauza): Adding one field for any out-of-tree need + spec_obj.config_options = config_options for num in range(num_instances): # Filter local hosts based on requirements ... hosts = self.host_manager.get_filtered_hosts(hosts, - filter_properties, index=num) + spec_obj, index=num) if not hosts: # Can't get any more locally. break @@ -151,7 +140,7 @@ class FilterScheduler(driver.Scheduler): LOG.debug("Filtered %(hosts)s", {'hosts': hosts}) weighed_hosts = self.host_manager.get_weighed_hosts(hosts, - filter_properties) + spec_obj) LOG.debug("Weighed %(hosts)s", {'hosts': weighed_hosts}) @@ -169,8 +158,10 @@ class FilterScheduler(driver.Scheduler): # Now consume the resources so the filter/weights # will change for the next instance. chosen_host.obj.consume_from_request(spec_obj) - if filter_properties.get('group_updated') is True: - filter_properties['group_hosts'].add(chosen_host.obj.host) + if spec_obj.instance_group is not None: + 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 def _get_all_host_states(self, context): diff --git a/nova/scheduler/filters/__init__.py b/nova/scheduler/filters/__init__.py index 1359913e1b..c82729c957 100644 --- a/nova/scheduler/filters/__init__.py +++ b/nova/scheduler/filters/__init__.py @@ -16,8 +16,10 @@ """ Scheduler host filters """ +import functools from nova import filters +from nova import objects class BaseHostFilter(filters.BaseFilter): @@ -45,3 +47,31 @@ def all_filters(): and should return a list of all filter classes available. """ 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 diff --git a/nova/scheduler/filters/affinity_filter.py b/nova/scheduler/filters/affinity_filter.py index 3470e532c7..22fc508ba6 100644 --- a/nova/scheduler/filters/affinity_filter.py +++ b/nova/scheduler/filters/affinity_filter.py @@ -30,6 +30,7 @@ class DifferentHostFilter(filters.BaseHostFilter): # The hosts the instances are running on doesn't change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): 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 run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): 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 run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): 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 hosts. """ + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): # Only invoke the filter is 'anti-affinity' is configured policies = filter_properties.get('group_policies', []) @@ -113,6 +117,7 @@ class ServerGroupAntiAffinityFilter(_GroupAntiAffinityFilter): class _GroupAffinityFilter(filters.BaseHostFilter): """Schedule the instance on to host from a set of group hosts. """ + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): # Only invoke the filter is 'affinity' is configured policies = filter_properties.get('group_policies', []) diff --git a/nova/scheduler/filters/aggregate_image_properties_isolation.py b/nova/scheduler/filters/aggregate_image_properties_isolation.py index c56ecb8229..623c5db520 100644 --- a/nova/scheduler/filters/aggregate_image_properties_isolation.py +++ b/nova/scheduler/filters/aggregate_image_properties_isolation.py @@ -40,6 +40,7 @@ class AggregateImagePropertiesIsolation(filters.BaseHostFilter): # Aggregate data and instance type does not change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Checks a host in an aggregate that metadata key/value match with image properties. diff --git a/nova/scheduler/filters/aggregate_instance_extra_specs.py b/nova/scheduler/filters/aggregate_instance_extra_specs.py index b7181393a3..eae56ce716 100644 --- a/nova/scheduler/filters/aggregate_instance_extra_specs.py +++ b/nova/scheduler/filters/aggregate_instance_extra_specs.py @@ -33,6 +33,7 @@ class AggregateInstanceExtraSpecsFilter(filters.BaseHostFilter): # Aggregate data and instance type does not change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return a list of hosts that can create instance_type diff --git a/nova/scheduler/filters/aggregate_multitenancy_isolation.py b/nova/scheduler/filters/aggregate_multitenancy_isolation.py index 385a17acf1..32124f76cf 100644 --- a/nova/scheduler/filters/aggregate_multitenancy_isolation.py +++ b/nova/scheduler/filters/aggregate_multitenancy_isolation.py @@ -28,6 +28,7 @@ class AggregateMultiTenancyIsolation(filters.BaseHostFilter): # Aggregate data and tenant do not change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """If a host is in an aggregate that has the metadata key "filter_tenant_id" it can only create instances from that tenant(s). diff --git a/nova/scheduler/filters/all_hosts_filter.py b/nova/scheduler/filters/all_hosts_filter.py index 71d136f431..190e46dd69 100644 --- a/nova/scheduler/filters/all_hosts_filter.py +++ b/nova/scheduler/filters/all_hosts_filter.py @@ -23,5 +23,6 @@ class AllHostsFilter(filters.BaseHostFilter): # list of hosts doesn't change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): return True diff --git a/nova/scheduler/filters/availability_zone_filter.py b/nova/scheduler/filters/availability_zone_filter.py index f3eec3f792..03800ab00c 100644 --- a/nova/scheduler/filters/availability_zone_filter.py +++ b/nova/scheduler/filters/availability_zone_filter.py @@ -36,6 +36,7 @@ class AvailabilityZoneFilter(filters.BaseHostFilter): # Availability zones do not change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): spec = filter_properties.get('request_spec', {}) props = spec.get('instance_properties', {}) diff --git a/nova/scheduler/filters/compute_capabilities_filter.py b/nova/scheduler/filters/compute_capabilities_filter.py index 51a4381d07..bef34f6683 100644 --- a/nova/scheduler/filters/compute_capabilities_filter.py +++ b/nova/scheduler/filters/compute_capabilities_filter.py @@ -92,6 +92,7 @@ class ComputeCapabilitiesFilter(filters.BaseHostFilter): return False return True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return a list of hosts that can create instance_type.""" instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/filters/compute_filter.py b/nova/scheduler/filters/compute_filter.py index ec754c098a..bbfd66095b 100644 --- a/nova/scheduler/filters/compute_filter.py +++ b/nova/scheduler/filters/compute_filter.py @@ -34,6 +34,7 @@ class ComputeFilter(filters.BaseHostFilter): # Host state does not change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Returns True for only active compute nodes.""" service = host_state.service diff --git a/nova/scheduler/filters/core_filter.py b/nova/scheduler/filters/core_filter.py index 1fed2c0340..c323dee3ca 100644 --- a/nova/scheduler/filters/core_filter.py +++ b/nova/scheduler/filters/core_filter.py @@ -29,6 +29,7 @@ class BaseCoreFilter(filters.BaseHostFilter): def _get_cpu_allocation_ratio(self, host_state, filter_properties): raise NotImplementedError + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return True if host has sufficient CPU cores.""" instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/filters/disk_filter.py b/nova/scheduler/filters/disk_filter.py index d87baaede5..e623616733 100644 --- a/nova/scheduler/filters/disk_filter.py +++ b/nova/scheduler/filters/disk_filter.py @@ -35,6 +35,7 @@ class DiskFilter(filters.BaseHostFilter): def _get_disk_allocation_ratio(self, host_state, filter_properties): return CONF.disk_allocation_ratio + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Filter based on disk usage.""" instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/filters/exact_core_filter.py b/nova/scheduler/filters/exact_core_filter.py index 800ab8a7ec..43150509e6 100644 --- a/nova/scheduler/filters/exact_core_filter.py +++ b/nova/scheduler/filters/exact_core_filter.py @@ -25,6 +25,7 @@ LOG = logging.getLogger(__name__) class ExactCoreFilter(filters.BaseHostFilter): """Exact Core Filter.""" + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return True if host has the exact number of CPU cores.""" instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/filters/exact_disk_filter.py b/nova/scheduler/filters/exact_disk_filter.py index 7a8c5b5889..3b5e562794 100644 --- a/nova/scheduler/filters/exact_disk_filter.py +++ b/nova/scheduler/filters/exact_disk_filter.py @@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__) class ExactDiskFilter(filters.BaseHostFilter): """Exact Disk Filter.""" + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return True if host has the exact amount of disk available.""" instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/filters/exact_ram_filter.py b/nova/scheduler/filters/exact_ram_filter.py index 71aab99cd7..3d7baf4794 100644 --- a/nova/scheduler/filters/exact_ram_filter.py +++ b/nova/scheduler/filters/exact_ram_filter.py @@ -23,6 +23,7 @@ LOG = logging.getLogger(__name__) class ExactRamFilter(filters.BaseHostFilter): """Exact RAM Filter.""" + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return True if host has the exact amount of RAM available.""" instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/filters/image_props_filter.py b/nova/scheduler/filters/image_props_filter.py index b43d33588d..57c01f2a94 100644 --- a/nova/scheduler/filters/image_props_filter.py +++ b/nova/scheduler/filters/image_props_filter.py @@ -95,6 +95,7 @@ class ImagePropertiesFilter(filters.BaseHostFilter): 'hypervisor_version': hypervisor_version}) return False + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Check if host passes specified image properties. diff --git a/nova/scheduler/filters/io_ops_filter.py b/nova/scheduler/filters/io_ops_filter.py index 7e6c7d93ff..5fca44c42f 100644 --- a/nova/scheduler/filters/io_ops_filter.py +++ b/nova/scheduler/filters/io_ops_filter.py @@ -39,6 +39,7 @@ class IoOpsFilter(filters.BaseHostFilter): def _get_max_io_ops_per_host(self, host_state, filter_properties): return CONF.max_io_ops_per_host + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Use information about current vm and task states collected from compute node statistics to decide whether to filter. diff --git a/nova/scheduler/filters/isolated_hosts_filter.py b/nova/scheduler/filters/isolated_hosts_filter.py index a77cd2f919..5b93634f70 100644 --- a/nova/scheduler/filters/isolated_hosts_filter.py +++ b/nova/scheduler/filters/isolated_hosts_filter.py @@ -39,6 +39,7 @@ class IsolatedHostsFilter(filters.BaseHostFilter): # The configuration values do not change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Result Matrix with 'restrict_isolated_hosts_to_isolated_images' set to True:: diff --git a/nova/scheduler/filters/json_filter.py b/nova/scheduler/filters/json_filter.py index e3f59b2be0..95ce0eabd4 100644 --- a/nova/scheduler/filters/json_filter.py +++ b/nova/scheduler/filters/json_filter.py @@ -126,6 +126,7 @@ class JsonFilter(filters.BaseHostFilter): result = method(self, cooked_args) return result + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return a list of hosts that can fulfill the requirements specified in the query. diff --git a/nova/scheduler/filters/metrics_filter.py b/nova/scheduler/filters/metrics_filter.py index 92b92ac965..7eb8e3ec67 100644 --- a/nova/scheduler/filters/metrics_filter.py +++ b/nova/scheduler/filters/metrics_filter.py @@ -43,6 +43,7 @@ class MetricsFilter(filters.BaseHostFilter): name="metrics.weight_setting") self.keys = set([x[0] for x in opts]) + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): metrics_on_host = set(m.name for m in host_state.metrics) if not self.keys.issubset(metrics_on_host): diff --git a/nova/scheduler/filters/num_instances_filter.py b/nova/scheduler/filters/num_instances_filter.py index 603bf65429..aa9856e585 100644 --- a/nova/scheduler/filters/num_instances_filter.py +++ b/nova/scheduler/filters/num_instances_filter.py @@ -36,6 +36,7 @@ class NumInstancesFilter(filters.BaseHostFilter): def _get_max_instances_per_host(self, host_state, filter_properties): return CONF.max_instances_per_host + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): num_instances = host_state.num_instances max_instances = self._get_max_instances_per_host( diff --git a/nova/scheduler/filters/numa_topology_filter.py b/nova/scheduler/filters/numa_topology_filter.py index ee83e62c0d..43cec74298 100644 --- a/nova/scheduler/filters/numa_topology_filter.py +++ b/nova/scheduler/filters/numa_topology_filter.py @@ -18,6 +18,7 @@ from nova.virt import hardware class NUMATopologyFilter(filters.BaseHostFilter): """Filter on requested NUMA topology.""" + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): ram_ratio = host_state.ram_allocation_ratio cpu_ratio = host_state.cpu_allocation_ratio diff --git a/nova/scheduler/filters/pci_passthrough_filter.py b/nova/scheduler/filters/pci_passthrough_filter.py index e3a366085e..266f52840a 100644 --- a/nova/scheduler/filters/pci_passthrough_filter.py +++ b/nova/scheduler/filters/pci_passthrough_filter.py @@ -40,6 +40,7 @@ class PciPassthroughFilter(filters.BaseHostFilter): """ + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Return true if the host has the required PCI devices.""" request_spec = filter_properties.get('request_spec', {}) diff --git a/nova/scheduler/filters/ram_filter.py b/nova/scheduler/filters/ram_filter.py index 3e7fbf5074..735215dbbd 100644 --- a/nova/scheduler/filters/ram_filter.py +++ b/nova/scheduler/filters/ram_filter.py @@ -28,6 +28,7 @@ class BaseRamFilter(filters.BaseHostFilter): def _get_ram_allocation_ratio(self, host_state, filter_properties): raise NotImplementedError + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Only return hosts with sufficient available RAM.""" instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/filters/retry_filter.py b/nova/scheduler/filters/retry_filter.py index d2f715e75b..0a9692ef7f 100644 --- a/nova/scheduler/filters/retry_filter.py +++ b/nova/scheduler/filters/retry_filter.py @@ -25,6 +25,7 @@ class RetryFilter(filters.BaseHostFilter): purposes """ + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Skip nodes that have already been attempted.""" retry = filter_properties.get('retry', None) diff --git a/nova/scheduler/filters/trusted_filter.py b/nova/scheduler/filters/trusted_filter.py index 438b8da524..b9acad6da5 100644 --- a/nova/scheduler/filters/trusted_filter.py +++ b/nova/scheduler/filters/trusted_filter.py @@ -265,6 +265,7 @@ class TrustedFilter(filters.BaseHostFilter): # The hosts the instances are running on doesn't change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): instance_type = filter_properties.get('instance_type', {}) extra = instance_type.get('extra_specs', {}) diff --git a/nova/scheduler/filters/type_filter.py b/nova/scheduler/filters/type_filter.py index e593cf071a..9170f41904 100644 --- a/nova/scheduler/filters/type_filter.py +++ b/nova/scheduler/filters/type_filter.py @@ -25,6 +25,7 @@ class TypeAffinityFilter(filters.BaseHostFilter): (spread) set to 1 (default). """ + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): """Dynamically limits hosts to one instance type @@ -48,6 +49,7 @@ class AggregateTypeAffinityFilter(filters.BaseHostFilter): # Aggregate data does not change within a request run_filter_once_per_request = True + @filters.compat_legacy_props def host_passes(self, host_state, filter_properties): instance_type = filter_properties.get('instance_type') diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py index b8ab26dcab..7c8d31818a 100644 --- a/nova/scheduler/host_manager.py +++ b/nova/scheduler/host_manager.py @@ -449,7 +449,7 @@ class HostManager(object): raise exception.SchedulerHostFilterNotFound(filter_name=msg) 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 hosts and return only ones passing all filters.""" @@ -499,9 +499,9 @@ class HostManager(object): filters = self.default_filters else: filters = self._choose_host_filters(filter_class_names) - ignore_hosts = filter_properties.get('ignore_hosts', []) - force_hosts = filter_properties.get('force_hosts', []) - force_nodes = filter_properties.get('force_nodes', []) + ignore_hosts = spec_obj.ignore_hosts or [] + force_hosts = spec_obj.force_hosts or [] + force_nodes = spec_obj.force_nodes or [] if ignore_hosts or force_hosts or force_nodes: # NOTE(deva): we can't assume "host" is unique because @@ -523,12 +523,12 @@ class HostManager(object): hosts = six.itervalues(name_to_cls_map) 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.""" return self.weight_handler.get_weighed_objects(self.weighers, - hosts, weight_properties) + hosts, spec_obj) def get_all_host_states(self, context): """Returns a list of HostStates that represents all the hosts diff --git a/nova/tests/unit/scheduler/test_filter_scheduler.py b/nova/tests/unit/scheduler/test_filter_scheduler.py index 99ec63a5ef..261b44af7c 100644 --- a/nova/tests/unit/scheduler/test_filter_scheduler.py +++ b/nova/tests/unit/scheduler/test_filter_scheduler.py @@ -74,7 +74,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): os_type='Linux', uuid='fake-uuid', pci_requests=None, - numa_topology=None) + numa_topology=None, + instance_group=None) self.mox.ReplayAll() weighed_hosts = self.driver._schedule(self.context, spec_obj) self.assertEqual(len(weighed_hosts), 10) @@ -143,7 +144,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): ephemeral_gb=0, vcpus=1), pci_requests=None, - numa_topology=None) + numa_topology=None, + instance_group=None) self.mox.ReplayAll() hosts = self.driver._schedule(self.context, spec_obj) @@ -178,7 +180,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): ephemeral_gb=0, vcpus=1), pci_requests=None, - numa_topology=None) + numa_topology=None, + instance_group=None) self.mox.ReplayAll() hosts = self.driver._schedule(self.context, spec_obj) @@ -222,7 +225,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): ephemeral_gb=0, vcpus=1), pci_requests=None, - numa_topology=None) + numa_topology=None, + instance_group=None) self.stubs.Set(weights.HostWeightHandler, 'get_weighed_objects', _fake_weigh_objects) diff --git a/nova/tests/unit/scheduler/test_filters.py b/nova/tests/unit/scheduler/test_filters.py index f4ec41a9d1..7f87b25d4b 100644 --- a/nova/tests/unit/scheduler/test_filters.py +++ b/nova/tests/unit/scheduler/test_filters.py @@ -23,6 +23,7 @@ from six.moves import range from nova import filters from nova import loadables +from nova import objects from nova import test @@ -46,18 +47,18 @@ class FiltersTestCase(test.NoDBTestCase): def test_filter_all(self): filter_obj_list = ['obj1', 'obj2', 'obj3'] - filter_properties = 'fake_filter_properties' + spec_obj = objects.RequestSpec() base_filter = filters.BaseFilter() self.mox.StubOutWithMock(base_filter, '_filter_one') - base_filter._filter_one('obj1', filter_properties).AndReturn(True) - base_filter._filter_one('obj2', filter_properties).AndReturn(False) - base_filter._filter_one('obj3', filter_properties).AndReturn(True) + base_filter._filter_one('obj1', spec_obj).AndReturn(True) + base_filter._filter_one('obj2', spec_obj).AndReturn(False) + base_filter._filter_one('obj3', spec_obj).AndReturn(True) 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.assertEqual(['obj1', 'obj3'], list(result)) @@ -67,7 +68,7 @@ class FiltersTestCase(test.NoDBTestCase): # call filter_all() with generators returned from previous calls # to filter_all(). filter_obj_list = ['obj1', 'obj2', 'obj3'] - filter_properties = 'fake_filter_properties' + spec_obj = objects.RequestSpec() base_filter = filters.BaseFilter() self.mox.StubOutWithMock(base_filter, '_filter_one') @@ -83,16 +84,16 @@ class FiltersTestCase(test.NoDBTestCase): # After that, 'obj3' gets yielded 'total_iterations' number of # times. for x in range(total_iterations): - base_filter._filter_one('obj1', filter_properties).AndReturn(True) - base_filter._filter_one('obj2', filter_properties).AndReturn(False) + base_filter._filter_one('obj1', spec_obj).AndReturn(True) + base_filter._filter_one('obj2', spec_obj).AndReturn(False) 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() objs = iter(filter_obj_list) for x in range(total_iterations): # 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.assertEqual(['obj1', 'obj3'], list(objs)) @@ -100,7 +101,7 @@ class FiltersTestCase(test.NoDBTestCase): filter_objs_initial = ['initial', 'filter1', 'objects1'] filter_objs_second = ['second', 'filter2', 'objects2'] filter_objs_last = ['last', 'filter3', 'objects3'] - filter_properties = 'fake_filter_properties' + spec_obj = objects.RequestSpec() def _fake_base_loader_init(*args, **kwargs): pass @@ -122,10 +123,10 @@ class FiltersTestCase(test.NoDBTestCase): filt1_mock.run_filter_for_index(0).AndReturn(True) 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.filter_all(filter_objs_second, - filter_properties).AndReturn(filter_objs_last) + spec_obj).AndReturn(filter_objs_last) self.mox.ReplayAll() @@ -133,7 +134,7 @@ class FiltersTestCase(test.NoDBTestCase): filter_mocks = [filt1_mock, filt2_mock] result = filter_handler.get_filtered_objects(filter_mocks, filter_objs_initial, - filter_properties) + spec_obj) self.assertEqual(filter_objs_last, result) def test_get_filtered_objects_for_index(self): @@ -142,7 +143,7 @@ class FiltersTestCase(test.NoDBTestCase): """ filter_objs_initial = ['initial', 'filter1', 'objects1'] filter_objs_second = ['second', 'filter2', 'objects2'] - filter_properties = 'fake_filter_properties' + spec_obj = objects.RequestSpec() def _fake_base_loader_init(*args, **kwargs): pass @@ -164,7 +165,7 @@ class FiltersTestCase(test.NoDBTestCase): filt1_mock.run_filter_for_index(0).AndReturn(True) 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 filt2_mock.run_filter_for_index(0).AndReturn(False) @@ -174,11 +175,11 @@ class FiltersTestCase(test.NoDBTestCase): filter_mocks = [filt1_mock, filt2_mock] filter_handler.get_filtered_objects(filter_mocks, filter_objs_initial, - filter_properties) + spec_obj) def test_get_filtered_objects_none_response(self): filter_objs_initial = ['initial', 'filter1', 'objects1'] - filter_properties = 'fake_filter_properties' + spec_obj = objects.RequestSpec() def _fake_base_loader_init(*args, **kwargs): pass @@ -200,26 +201,26 @@ class FiltersTestCase(test.NoDBTestCase): filt1_mock.run_filter_for_index(0).AndReturn(True) filt1_mock.filter_all(filter_objs_initial, - filter_properties).AndReturn(None) + spec_obj).AndReturn(None) self.mox.ReplayAll() filter_handler = filters.BaseFilterHandler(filters.BaseFilter) filter_mocks = [filt1_mock, filt2_mock] result = filter_handler.get_filtered_objects(filter_mocks, filter_objs_initial, - filter_properties) + spec_obj) self.assertIsNone(result) def test_get_filtered_objects_info_log_none_returned(self): LOG = filters.LOG 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 list_objs[1:] 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 [] @@ -228,11 +229,10 @@ class FiltersTestCase(test.NoDBTestCase): all_filters = [filter_a, filter_b] hosts = ["Host0", "Host1", "Host2"] fake_uuid = "uuid" - filt_props = {"request_spec": {"instance_properties": { - "uuid": fake_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, filt_props) + 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)', " @@ -245,12 +245,12 @@ class FiltersTestCase(test.NoDBTestCase): LOG = filters.LOG 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 list_objs[1:] 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 [] @@ -259,11 +259,10 @@ class FiltersTestCase(test.NoDBTestCase): all_filters = [filter_a, filter_b] hosts = ["Host0", "Host1", "Host2"] fake_uuid = "uuid" - filt_props = {"request_spec": {"instance_properties": { - "uuid": fake_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, filt_props) + all_filters, hosts, spec_obj) self.assertFalse(result) # FilterA should leave Host1 and Host2; FilterB should leave None. exp_output = ("[('FilterA', [('Host1', ''), ('Host2', '')]), " + @@ -271,3 +270,34 @@ class FiltersTestCase(test.NoDBTestCase): 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 [] + + 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, "info") 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: (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) diff --git a/nova/tests/unit/scheduler/test_host_filters.py b/nova/tests/unit/scheduler/test_host_filters.py index caed938aa3..682d3a4e6c 100644 --- a/nova/tests/unit/scheduler/test_host_filters.py +++ b/nova/tests/unit/scheduler/test_host_filters.py @@ -14,7 +14,9 @@ """ Tests For Scheduler Host Filters. """ +import mock +from nova import objects from nova.scheduler import filters from nova.scheduler.filters import all_hosts_filter from nova.scheduler.filters import compute_filter @@ -36,3 +38,27 @@ class HostFiltersTestCase(test.NoDBTestCase): filt_cls = all_hosts_filter.AllHostsFilter() host = fakes.FakeHostState('host1', 'node1', {}) 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)) diff --git a/nova/tests/unit/scheduler/test_host_manager.py b/nova/tests/unit/scheduler/test_host_manager.py index 99146352fc..b3bfa8dcde 100644 --- a/nova/tests/unit/scheduler/test_host_manager.py +++ b/nova/tests/unit/scheduler/test_host_manager.py @@ -207,7 +207,10 @@ class HostManagerTestCase(test.NoDBTestCase): self.assertEqual(set(info['expected_objs']), set(result)) 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, 'expected_fprops': fake_properties} @@ -220,7 +223,10 @@ class HostManagerTestCase(test.NoDBTestCase): @mock.patch.object(FakeFilterClass2, '_filter_one', return_value=True) 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'] info = {'expected_objs': self.fake_hosts, @@ -232,8 +238,12 @@ class HostManagerTestCase(test.NoDBTestCase): self._verify_result(info, result) def test_get_filtered_hosts_with_ignore(self): - fake_properties = {'ignore_hosts': ['fake_host1', 'fake_host3', - 'fake_host5', 'fake_multihost']} + fake_properties = objects.RequestSpec( + 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 info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]], @@ -245,8 +255,11 @@ class HostManagerTestCase(test.NoDBTestCase): self._verify_result(info, result) def test_get_filtered_hosts_with_force_hosts(self): - fake_properties = {'force_hosts': ['fake_host1', 'fake_host3', - 'fake_host5']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid1', + ignore_hosts=[], + force_hosts=['fake_host1', 'fake_host3', 'fake_host5'], + force_nodes=[]) # [0] and [2] are host1 and host3 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) 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': [], 'expected_fprops': fake_properties} @@ -270,8 +287,11 @@ class HostManagerTestCase(test.NoDBTestCase): def test_get_filtered_hosts_with_ignore_and_force_hosts(self): # Ensure ignore_hosts processed before force_hosts in host filters. - fake_properties = {'force_hosts': ['fake_host3', 'fake_host1'], - 'ignore_hosts': ['fake_host1']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid1', + ignore_hosts=['fake_host1'], + force_hosts=['fake_host3', 'fake_host1'], + force_nodes=[]) # only fake_host3 should be left. 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): # 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], self.fake_hosts[6], self.fake_hosts[7]], @@ -296,8 +320,11 @@ class HostManagerTestCase(test.NoDBTestCase): self._verify_result(info, result, False) def test_get_filtered_hosts_with_force_nodes(self): - fake_properties = {'force_nodes': ['fake-node2', 'fake-node4', - 'fake-node9']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid1', + ignore_hosts=[], + force_hosts=[], + force_nodes=['fake-node2', 'fake-node4', 'fake-node9']) # [5] is fake-node2, [7] is fake-node4 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): # Ensure only overlapping results if both force host and node - fake_properties = {'force_hosts': ['fake_host1', 'fake_multihost'], - 'force_nodes': ['fake-node2', 'fake-node9']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid1', + ignore_hosts=[], + force_hosts=['fake-host1', 'fake_multihost'], + force_nodes=['fake-node2', 'fake-node9']) # [5] is fake-node2 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): # Ensure non-overlapping force_node and force_host yield no result - fake_properties = {'force_hosts': ['fake_multihost'], - 'force_nodes': ['fake-node']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid1', + ignore_hosts=[], + force_hosts=['fake_multihost'], + force_nodes=['fake-node']) info = {'expected_objs': [], 'expected_fprops': fake_properties} @@ -337,8 +370,11 @@ class HostManagerTestCase(test.NoDBTestCase): def test_get_filtered_hosts_with_ignore_hosts_and_force_nodes(self): # Ensure ignore_hosts can coexist with force_nodes - fake_properties = {'force_nodes': ['fake-node4', 'fake-node2'], - 'ignore_hosts': ['fake_host1', 'fake_host2']} + fake_properties = objects.RequestSpec( + 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]], '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): # Ensure ignore_hosts is processed before force_nodes - fake_properties = {'force_nodes': ['fake_node4', 'fake_node2'], - 'ignore_hosts': ['fake_multihost']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid1', + ignore_hosts=['fake_multihost'], + force_hosts=[], + force_nodes=['fake_node4', 'fake_node2']) info = {'expected_objs': [], 'expected_fprops': fake_properties} @@ -885,9 +924,9 @@ class HostStateTestCase(test.NoDBTestCase): numa_fit_mock.return_value = fake_numa_topology instance_init_mock.return_value = fake_instance spec_obj = objects.RequestSpec( + instance_uuid='fake-uuid', flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0, vcpus=0), - uuid='fake-uuid', numa_topology=fake_numa_topology, pci_requests=objects.InstancePCIRequests(requests=[])) host = host_manager.HostState("fakehost", "fakenode") @@ -905,9 +944,9 @@ class HostStateTestCase(test.NoDBTestCase): second_numa_topology = objects.InstanceNUMATopology( cells=[objects.InstanceNUMACell()]) spec_obj = objects.RequestSpec( + instance_uuid='fake-uuid', flavor=objects.Flavor(root_gb=0, ephemeral_gb=0, memory_mb=0, vcpus=0), - uuid='fake-uuid', numa_topology=second_numa_topology, pci_requests=objects.InstancePCIRequests(requests=[])) second_host_numa_topology = mock.Mock() @@ -936,6 +975,7 @@ class HostStateTestCase(test.NoDBTestCase): for r in fake_requests], instance_uuid='fake-uuid') req_spec = objects.RequestSpec( + instance_uuid='fake-uuid', project_id='12345', numa_topology=inst_topology, pci_requests=fake_requests_obj, @@ -968,6 +1008,7 @@ class HostStateTestCase(test.NoDBTestCase): for r in fake_requests], instance_uuid='fake-uuid') req_spec = objects.RequestSpec( + instance_uuid='fake-uuid', project_id='12345', numa_topology=None, pci_requests=fake_requests_obj, diff --git a/nova/tests/unit/scheduler/test_ironic_host_manager.py b/nova/tests/unit/scheduler/test_ironic_host_manager.py index 04f7d12b5c..631710a429 100644 --- a/nova/tests/unit/scheduler/test_ironic_host_manager.py +++ b/nova/tests/unit/scheduler/test_ironic_host_manager.py @@ -329,7 +329,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase): self.assertEqual(set(info['expected_objs']), set(result)) 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, 'expected_fprops': fake_properties} @@ -342,7 +346,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase): @mock.patch.object(FakeFilterClass2, '_filter_one', return_value=True) 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'] info = {'expected_objs': self.fake_hosts, @@ -354,8 +362,12 @@ class IronicHostManagerTestFilters(test.NoDBTestCase): self._verify_result(info, result) def test_get_filtered_hosts_with_ignore(self): - fake_properties = {'ignore_hosts': ['fake_host1', 'fake_host3', - 'fake_host5', 'fake_multihost']} + fake_properties = objects.RequestSpec( + 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 info = {'expected_objs': [self.fake_hosts[1], self.fake_hosts[3]], @@ -367,8 +379,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase): self._verify_result(info, result) def test_get_filtered_hosts_with_force_hosts(self): - fake_properties = {'force_hosts': ['fake_host1', 'fake_host3', - 'fake_host5']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid', + ignore_hosts=[], + force_hosts=['fake_host1', 'fake_host3', 'fake_host5'], + force_nodes=[]) # [0] and [2] are host1 and host3 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) 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': [], 'expected_fprops': fake_properties} @@ -392,8 +411,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase): def test_get_filtered_hosts_with_ignore_and_force_hosts(self): # Ensure ignore_hosts processed before force_hosts in host filters. - fake_properties = {'force_hosts': ['fake_host3', 'fake_host1'], - 'ignore_hosts': ['fake_host1']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid', + ignore_hosts=['fake_host1'], + force_hosts=['fake_host3', 'fake_host1'], + force_nodes=[]) # only fake_host3 should be left. 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): # 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], self.fake_hosts[6], self.fake_hosts[7]], @@ -418,8 +444,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase): self._verify_result(info, result, False) def test_get_filtered_hosts_with_force_nodes(self): - fake_properties = {'force_nodes': ['fake-node2', 'fake-node4', - 'fake-node9']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid', + ignore_hosts=[], + force_hosts=[], + force_nodes=['fake-node2', 'fake-node4', 'fake-node9']) # [5] is fake-node2, [7] is fake-node4 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): # Ensure only overlapping results if both force host and node - fake_properties = {'force_hosts': ['fake_host1', 'fake_multihost'], - 'force_nodes': ['fake-node2', 'fake-node9']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid', + ignore_hosts=[], + force_hosts=['fake_host1', 'fake_multihost'], + force_nodes=['fake-node2', 'fake-node9']) # [5] is fake-node2 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): # Ensure non-overlapping force_node and force_host yield no result - fake_properties = {'force_hosts': ['fake_multihost'], - 'force_nodes': ['fake-node']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid', + ignore_hosts=[], + force_hosts=['fake_multihost'], + force_nodes=['fake-node']) info = {'expected_objs': [], 'expected_fprops': fake_properties} @@ -459,8 +494,11 @@ class IronicHostManagerTestFilters(test.NoDBTestCase): def test_get_filtered_hosts_with_ignore_hosts_and_force_nodes(self): # Ensure ignore_hosts can coexist with force_nodes - fake_properties = {'force_nodes': ['fake-node4', 'fake-node2'], - 'ignore_hosts': ['fake_host1', 'fake_host2']} + fake_properties = objects.RequestSpec( + 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]], '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): # Ensure ignore_hosts is processed before force_nodes - fake_properties = {'force_nodes': ['fake_node4', 'fake_node2'], - 'ignore_hosts': ['fake_multihost']} + fake_properties = objects.RequestSpec( + instance_uuid='fake-uuid', + ignore_hosts=['fake_multihost'], + force_hosts=[], + force_nodes=['fake_node4', 'fake_node2']) info = {'expected_objs': [], 'expected_fprops': fake_properties} diff --git a/releasenotes/notes/filters_use_reqspec-9f92b9c0ead76093.yaml b/releasenotes/notes/filters_use_reqspec-9f92b9c0ead76093.yaml new file mode 100644 index 0000000000..297c2f828f --- /dev/null +++ b/releasenotes/notes/filters_use_reqspec-9f92b9c0ead76093.yaml @@ -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. |