From 7755b759b4a0ba06378afd7b64153e4a83f2ce9e Mon Sep 17 00:00:00 2001 From: Sylvain Bauza Date: Tue, 10 May 2016 13:13:34 +0200 Subject: [PATCH] Verify requested_destination in the scheduler Now that the RequestSpec provides a field for a requested destination, we can know if the admin user provided a destination to verify (and not force it). In order to verify this destination, we want to run the filters for only this host, so that if the filters are not okay for that possibility, the scheduler should raise a NoValidHost exception. For the moment, nothing is setting this field but the next patch will provide an API-level modification for putting it if the provided destination exists (and changing the behaviour if the admin provides the host field for evacuate and live-migrate) Change-Id: Ifb803c708e89b7101ded48571e1af49c33117758 Partially-Implements: blueprint check-destination-on-migrations --- nova/scheduler/host_manager.py | 23 +++++++++++++ .../tests/unit/scheduler/test_host_manager.py | 34 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py index f2b308117602..2e9aac71ad74 100644 --- a/nova/scheduler/host_manager.py +++ b/nova/scheduler/host_manager.py @@ -505,10 +505,33 @@ class HostManager(object): "'force_nodes' value of '%s'") LOG.info(msg % forced_nodes_str) + def _get_hosts_matching_request(hosts, requested_destination): + (host, node) = (requested_destination.host, + requested_destination.node) + requested_nodes = [x for x in hosts + if x.host == host and x.nodename == node] + if requested_nodes: + LOG.info(_LI('Host filter only checking host %(host)s and ' + 'node %(node)s') % {'host': host, 'node': node}) + else: + # NOTE(sbauza): The API level should prevent the user from + # providing a wrong destination but let's make sure a wrong + # destination doesn't trample the scheduler still. + LOG.info(_LI('No hosts matched due to not matching requested ' + 'destination (%(host)s, %(node)s)' + ) % {'host': host, 'node': node}) + return iter(requested_nodes) + ignore_hosts = spec_obj.ignore_hosts or [] force_hosts = spec_obj.force_hosts or [] force_nodes = spec_obj.force_nodes or [] + requested_node = spec_obj.requested_destination + if requested_node is not None: + # NOTE(sbauza): Reduce a potentially long set of hosts as much as + # possible to any requested destination nodes before passing the + # list to the filters + hosts = _get_hosts_matching_request(hosts, requested_node) if ignore_hosts or force_hosts or force_nodes: # NOTE(deva): we can't assume "host" is unique because # one host may have many nodes. diff --git a/nova/tests/unit/scheduler/test_host_manager.py b/nova/tests/unit/scheduler/test_host_manager.py index 75417fccc713..35b8cce8943d 100644 --- a/nova/tests/unit/scheduler/test_host_manager.py +++ b/nova/tests/unit/scheduler/test_host_manager.py @@ -222,6 +222,40 @@ class HostManagerTestCase(test.NoDBTestCase): fake_properties) self._verify_result(info, result) + def test_get_filtered_hosts_with_requested_destination(self): + dest = objects.Destination(host='fake_host1', node='fake-node') + fake_properties = objects.RequestSpec(requested_destination=dest, + ignore_hosts=[], + instance_uuid='fake-uuid1', + force_hosts=[], + force_nodes=[]) + + info = {'expected_objs': [self.fake_hosts[0]], + 'expected_fprops': fake_properties} + + self._mock_get_filtered_hosts(info) + + result = self.host_manager.get_filtered_hosts(self.fake_hosts, + fake_properties) + self._verify_result(info, result) + + def test_get_filtered_hosts_with_wrong_requested_destination(self): + dest = objects.Destination(host='dummy', node='fake-node') + fake_properties = objects.RequestSpec(requested_destination=dest, + ignore_hosts=[], + instance_uuid='fake-uuid1', + force_hosts=[], + force_nodes=[]) + + info = {'expected_objs': [], + 'expected_fprops': fake_properties} + + self._mock_get_filtered_hosts(info) + + result = self.host_manager.get_filtered_hosts(self.fake_hosts, + fake_properties) + self._verify_result(info, result) + def test_get_filtered_hosts_with_ignore(self): fake_properties = objects.RequestSpec( instance_uuid=uuids.instance,