From 20056edf5a751111af863d5273c5f81741aecfc8 Mon Sep 17 00:00:00 2001 From: Johannes Kulik Date: Fri, 27 Oct 2023 14:53:51 +0200 Subject: [PATCH] scheduler: Add weighers for same shard (live-)migration We want to prefer staying in the same shard on live-migration and on offline migration, because migrating VMs with volumes attached induces overhead. Therefore, we add `PreferSameShardOnLiveMigrateWeigher` and `PreferSameShardOnMigrateWeigher`. Since we already had that functionality available for resizes through `PreferSameShardOnResizeWeigher`, we refactored the weigher into a base class configured by class attribute `_TYPE`. Change-Id: I0d28b675562ec220163b8a159802c2126280c272 --- nova/conf/scheduler.py | 60 +++- .../{resize_same_shard.py => same_shard.py} | 46 ++- .../weights/test_weights_resize_same_shard.py | 150 -------- .../weights/test_weights_same_shard.py | 322 ++++++++++++++++++ 4 files changed, 417 insertions(+), 161 deletions(-) rename nova/scheduler/weights/{resize_same_shard.py => same_shard.py} (59%) delete mode 100644 nova/tests/unit/scheduler/weights/test_weights_resize_same_shard.py create mode 100644 nova/tests/unit/scheduler/weights/test_weights_same_shard.py diff --git a/nova/conf/scheduler.py b/nova/conf/scheduler.py index 520a87c9f9f..f50b474ddce 100644 --- a/nova/conf/scheduler.py +++ b/nova/conf/scheduler.py @@ -477,12 +477,70 @@ This option is only used by the FilterScheduler and its subclasses; if you use a different scheduler, this option has no effect. Also note that this setting -only affects scheduling if the 'resize_same_shard' weigher is enabled. +only affects scheduling if the 'PreferSameShardOnResizeWeigher' weigher is +enabled. + +Possible values: + +* An integer or float value, where the value corresponds to the multipler + ratio for this weigher. + +Related options: + +* prefer_same_shard_live_migrate_weight_multiplier +* prefer_same_shard_migrate_weight_multiplier +"""), + cfg.FloatOpt("prefer_same_shard_live_migrate_weight_multiplier", + default=1.0, + help=""" +Prefer scheduling on same-shard on live-migrate weight multiplier. + +This option determines how strongly the previous shard should be preferred for +scheduling a live-migrating instance. A positive value will result in the +scheduler preferring the same shard that the instance was previously running +on. A negative value would prefer all other shards over the instance's previous +aggregate. + +This option is only used by the FilterScheduler and its subclasses; if you use +a different scheduler, this option has no effect. Also note that this setting +only affects scheduling if the 'PreferSameShardOnLiveMigrateWeigher' weigher is +enabled. + +Possible values: + +* An integer or float value, where the value corresponds to the multipler + ratio for this weigher. + +Related options: + +* prefer_same_shard_resize_weight_multiplier +* prefer_same_shard_migrate_weight_multiplier +"""), + cfg.FloatOpt("prefer_same_shard_migrate_weight_multiplier", + default=1.0, + help=""" +Prefer scheduling on same-shard on migrate weight multiplier. + +This option determines how strongly the previous shard should be preferred for +scheduling a offline migrating instance. A positive value will result in the +scheduler preferring the same shard that the instance was previously running +on. A negative value would prefer all other shards over the instance's previous +aggregate. + +This option is only used by the FilterScheduler and its subclasses; if you use +a different scheduler, this option has no effect. Also note that this setting +only affects scheduling if the 'PreferSameShardOnLiveMigrateWeigher' weigher is +enabled. Possible values: * An integer or float value, where the value corresponds to the multipler ratio for this weigher. + +Related options: + +* prefer_same_shard_resize_weight_multiplier +* prefer_same_shard_live_migrate_weight_multiplier """), cfg.FloatOpt("hv_ram_class_weight_multiplier", default=1.0, diff --git a/nova/scheduler/weights/resize_same_shard.py b/nova/scheduler/weights/same_shard.py similarity index 59% rename from nova/scheduler/weights/resize_same_shard.py rename to nova/scheduler/weights/same_shard.py index 86b2c796a52..f33364960a7 100644 --- a/nova/scheduler/weights/resize_same_shard.py +++ b/nova/scheduler/weights/same_shard.py @@ -13,12 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. """ -Prefer resize to same shard weigher. Resizing an instance gives the current -shard aggregate a higher priority. +Prefer same shard weigher. Resizing/live-migrating an instance gives the +current shard aggregate a higher priority. -This weigher ignores any instances which are: - * new instances (or unshelving) - * not resizing (a.k.a. migrating or rebuilding) +We split the functionality by action taken on the instance, so we can apply +different weights per action. """ from oslo_log import log as logging @@ -31,21 +30,36 @@ CONF = nova.conf.CONF -class PreferSameShardOnResizeWeigher(weights.BaseHostWeigher): +class _SameShardWeigherBase(weights.BaseHostWeigher): minval = 0 _SHARD_PREFIX = 'vc-' + _TYPE = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self._TYPE is None: + raise NotImplementedError( + 'Children of _SameShardWeigherBase need to set _TYPE') def weight_multiplier(self, host_state): """Override the weight multiplier.""" + name = f"prefer_same_shard_{self._TYPE}_weight_multiplier" return utils.get_weight_multiplier( - host_state, 'prefer_same_host_resize_weight_multiplier', - CONF.filter_scheduler.prefer_same_shard_resize_weight_multiplier) + host_state, name, + getattr(CONF.filter_scheduler, name)) def _weigh_object(self, host_state, request_spec): - """Return 1 for about-to-be-resized instances where the shard is the + """Return 1 for instances scheduled for _TYPE where the shard is the instance's current shard. Return 0 otherwise. """ - if not utils.request_is_resize(request_spec): + # check the type of the request matches the class' _TYPE + check_type_fn_name = f"request_is_{self._TYPE}" + check_type_fn = getattr(utils, check_type_fn_name, None) + if check_type_fn is None: + raise NotImplementedError( + f"nova.scheduler.utils.{check_type_fn_name}() does not exist " + f"for {self.__class__} with _TYPE {self._TYPE}") + if not check_type_fn(request_spec): return 0.0 if utils.is_non_vmware_spec(request_spec): @@ -70,3 +84,15 @@ def _weigh_object(self, host_state, request_spec): if instance_host in host_shard_aggr.hosts: return 1.0 return 0.0 + + +class PreferSameShardOnResizeWeigher(_SameShardWeigherBase): + _TYPE = 'resize' + + +class PreferSameShardOnLiveMigrateWeigher(_SameShardWeigherBase): + _TYPE = 'live_migrate' + + +class PreferSameShardOnMigrateWeigher(_SameShardWeigherBase): + _TYPE = 'migrate' diff --git a/nova/tests/unit/scheduler/weights/test_weights_resize_same_shard.py b/nova/tests/unit/scheduler/weights/test_weights_resize_same_shard.py deleted file mode 100644 index 262b4d90b1a..00000000000 --- a/nova/tests/unit/scheduler/weights/test_weights_resize_same_shard.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright 2022 SAP -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -Tests for scheduler prefer-resize-to-same-host weigher. -""" -import mock - -from nova import objects -from nova.scheduler import weights -from nova.scheduler.weights import resize_same_shard as same_shard -from nova import test -from nova.tests.unit.scheduler import fakes -from oslo_utils.fixture import uuidsentinel as uuids - - -class PreferSameShardOnResizeWeigherTestCase(test.NoDBTestCase): - def setUp(self): - super(PreferSameShardOnResizeWeigherTestCase, self).setUp() - self.weight_handler = weights.HostWeightHandler() - self.weighers = [same_shard.PreferSameShardOnResizeWeigher()] - - self.instance1 = objects.Instance(host='host1', uuid=uuids.instance_1) - self.instance2 = objects.Instance(host='host1', uuid=uuids.instance_2) - self.instance3 = objects.Instance(host='host2', uuid=uuids.instance_3) - self.instance4 = objects.Instance(host='host3', uuid=uuids.instance_4) - self.aggs1 = [objects.Aggregate(id=1, - name='az-a', - hosts=['host1', 'host3'], - metadata={}), - objects.Aggregate(id=1, - name='vc-a-0', - hosts=['host1', 'host3'], - metadata={})] - self.aggs2 = [objects.Aggregate(id=1, - name='vc-a-1', - hosts=['host2'], - metadata={})] - - self.hosts = [ - fakes.FakeHostState('host1', 'compute', {'aggregates': self.aggs1, - 'instances': { - self.instance1.uuid: self.instance1, - self.instance2.uuid: self.instance2, - }}), - fakes.FakeHostState('host2', 'compute', {'aggregates': self.aggs2, - 'instances': { - self.instance3.uuid: self.instance3, - }}), - fakes.FakeHostState('host3', 'compute', {'aggregates': self.aggs1, - 'instances': { - self.instance4.uuid: self.instance4, - }}), - ] - - def test_prefer_resize_to_same_shard(self): - self.flags(prefer_same_shard_resize_weight_multiplier=1.0, - group='filter_scheduler') - request_spec = objects.RequestSpec( - instance_uuid=self.instance1.uuid, - scheduler_hints=dict(_nova_check_type=['resize'], - source_host=['host1'])) - weighed_hosts = self.weight_handler.get_weighed_objects( - self.weighers, self.hosts, request_spec) - self.assertEqual(1.0, weighed_hosts[0].weight) - self.assertEqual(1.0, weighed_hosts[1].weight) - self.assertEqual(0.0, weighed_hosts[2].weight) - self.assertEqual('host1', weighed_hosts[0].obj.host) - self.assertEqual('host3', weighed_hosts[1].obj.host) - self.assertEqual('host2', weighed_hosts[2].obj.host) - - def test_prefer_resize_to_different_shard(self): - self.flags(prefer_same_shard_resize_weight_multiplier=-1.0, - group='filter_scheduler') - request_spec = objects.RequestSpec( - instance_uuid=self.instance1.uuid, - scheduler_hints=dict(_nova_check_type=['resize'], - source_host=['host1'])) - weighed_hosts = self.weight_handler.get_weighed_objects( - self.weighers, self.hosts, request_spec) - self.assertEqual(0.0, weighed_hosts[0].weight) - self.assertEqual(-1.0, weighed_hosts[1].weight) - self.assertEqual(-1.0, weighed_hosts[2].weight) - self.assertEqual('host2', weighed_hosts[0].obj.host) - self.assertEqual('host1', weighed_hosts[1].obj.host) - self.assertEqual('host3', weighed_hosts[2].obj.host) - - def test_ignore_scheduling_new_instance(self): - # Explicitly calling resize with new instance and test - # should fail as 'source_host' is missing. - self.flags(prefer_same_shard_resize_weight_multiplier=1.0, - group='filter_scheduler') - request_spec = objects.RequestSpec( - instance_uuid=uuids.fake_new_instance, - scheduler_hints={'_nova_check_type': ['resize']}) - weighed_hosts = self.weight_handler.get_weighed_objects( - self.weighers, self.hosts, request_spec) - self.assertEqual(0.0, weighed_hosts[0].weight) - self.assertEqual(0.0, weighed_hosts[1].weight) - self.assertEqual(0.0, weighed_hosts[2].weight) - - def test_ignore_rebuilding_instance(self): - self.flags(prefer_same_shard_resize_weight_multiplier=1.0, - group='filter_scheduler') - request_spec = objects.RequestSpec( - instance_uuid=self.instance1.uuid, - scheduler_hints={'_nova_check_type': ['rebuild']}) - weighed_hosts = self.weight_handler.get_weighed_objects( - self.weighers, self.hosts, request_spec) - self.assertEqual(0.0, weighed_hosts[0].weight) - self.assertEqual(0.0, weighed_hosts[1].weight) - self.assertEqual(0.0, weighed_hosts[2].weight) - - def test_ignore_non_resizing_instance(self): - self.flags(prefer_same_shard_resize_weight_multiplier=1.0, - group='filter_scheduler') - request_spec = objects.RequestSpec( - instance_uuid=self.instance1.uuid) - weighed_hosts = self.weight_handler.get_weighed_objects( - self.weighers, self.hosts, request_spec) - self.assertEqual(0.0, weighed_hosts[0].weight) - self.assertEqual(0.0, weighed_hosts[1].weight) - self.assertEqual(0.0, weighed_hosts[2].weight) - - @mock.patch('nova.scheduler.utils.is_non_vmware_spec', return_value=True) - def test_ignore_non_vmware_instance(self, mock_is_non_vmware_spec): - self.flags(prefer_same_shard_resize_weight_multiplier=1.0, - group='filter_scheduler') - request_spec = objects.RequestSpec( - instance_uuid=self.instance1.uuid, - scheduler_hints=dict(_nova_check_type=['resize'], - source_host=['host1'],)) - weighed_hosts = self.weight_handler.get_weighed_objects( - self.weighers, self.hosts, request_spec) - mock_is_non_vmware_spec.assert_has_calls( - len(self.hosts) * [mock.call(request_spec)]) - self.assertEqual(0.0, weighed_hosts[0].weight) - self.assertEqual(0.0, weighed_hosts[1].weight) - self.assertEqual(0.0, weighed_hosts[2].weight) diff --git a/nova/tests/unit/scheduler/weights/test_weights_same_shard.py b/nova/tests/unit/scheduler/weights/test_weights_same_shard.py new file mode 100644 index 00000000000..5e021e5cd6b --- /dev/null +++ b/nova/tests/unit/scheduler/weights/test_weights_same_shard.py @@ -0,0 +1,322 @@ +# Copyright 2022 SAP +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +""" +Tests for scheduler prefer-same-shard weighers. +""" +import ddt +import mock + +from nova import objects +from nova.scheduler import weights +from nova.scheduler.weights import same_shard +from nova import test +from nova.tests.unit.scheduler import fakes +from oslo_utils.fixture import uuidsentinel as uuids + + +class PreferSameShardOnResizeWeigherTestCase(test.NoDBTestCase): + def setUp(self): + super(PreferSameShardOnResizeWeigherTestCase, self).setUp() + self.weight_handler = weights.HostWeightHandler() + self.weighers = [same_shard.PreferSameShardOnResizeWeigher()] + + self.instance1 = objects.Instance(host='host1', uuid=uuids.instance_1) + self.instance2 = objects.Instance(host='host1', uuid=uuids.instance_2) + self.instance3 = objects.Instance(host='host2', uuid=uuids.instance_3) + self.instance4 = objects.Instance(host='host3', uuid=uuids.instance_4) + self.aggs1 = [objects.Aggregate(id=1, + name='az-a', + hosts=['host1', 'host3'], + metadata={}), + objects.Aggregate(id=1, + name='vc-a-0', + hosts=['host1', 'host3'], + metadata={})] + self.aggs2 = [objects.Aggregate(id=1, + name='vc-a-1', + hosts=['host2'], + metadata={})] + + self.hosts = [ + fakes.FakeHostState('host1', 'compute', {'aggregates': self.aggs1, + 'instances': { + self.instance1.uuid: self.instance1, + self.instance2.uuid: self.instance2, + }}), + fakes.FakeHostState('host2', 'compute', {'aggregates': self.aggs2, + 'instances': { + self.instance3.uuid: self.instance3, + }}), + fakes.FakeHostState('host3', 'compute', {'aggregates': self.aggs1, + 'instances': { + self.instance4.uuid: self.instance4, + }}), + ] + + def test_prefer_resize_to_same_shard(self): + self.flags(prefer_same_shard_resize_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + instance_uuid=self.instance1.uuid, + scheduler_hints=dict(_nova_check_type=[self.weighers[0]._TYPE], + source_host=['host1'])) + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + self.assertEqual(1.0, weighed_hosts[0].weight) + self.assertEqual(1.0, weighed_hosts[1].weight) + self.assertEqual(0.0, weighed_hosts[2].weight) + self.assertEqual('host1', weighed_hosts[0].obj.host) + self.assertEqual('host3', weighed_hosts[1].obj.host) + self.assertEqual('host2', weighed_hosts[2].obj.host) + + def test_prefer_resize_to_different_shard(self): + self.flags(prefer_same_shard_resize_weight_multiplier=-1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + instance_uuid=self.instance1.uuid, + scheduler_hints=dict(_nova_check_type=[self.weighers[0]._TYPE], + source_host=['host1'])) + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + self.assertEqual(0.0, weighed_hosts[0].weight) + self.assertEqual(-1.0, weighed_hosts[1].weight) + self.assertEqual(-1.0, weighed_hosts[2].weight) + self.assertEqual('host2', weighed_hosts[0].obj.host) + self.assertEqual('host1', weighed_hosts[1].obj.host) + self.assertEqual('host3', weighed_hosts[2].obj.host) + + def test_ignore_scheduling_new_instance(self): + # Explicitly calling resize with new instance and test + # should fail as 'source_host' is missing. + self.flags(prefer_same_shard_resize_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + instance_uuid=uuids.fake_new_instance, + scheduler_hints={'_nova_check_type': [self.weighers[0]._TYPE]}) + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + self.assertEqual(0.0, weighed_hosts[0].weight) + self.assertEqual(0.0, weighed_hosts[1].weight) + self.assertEqual(0.0, weighed_hosts[2].weight) + + def test_ignore_rebuilding_instance(self): + self.flags(prefer_same_shard_resize_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + instance_uuid=self.instance1.uuid, + scheduler_hints={'_nova_check_type': ['rebuild']}) + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + self.assertEqual(0.0, weighed_hosts[0].weight) + self.assertEqual(0.0, weighed_hosts[1].weight) + self.assertEqual(0.0, weighed_hosts[2].weight) + + def test_ignore_non_resizing_instance(self): + self.flags(prefer_same_shard_resize_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + instance_uuid=self.instance1.uuid) + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + self.assertEqual(0.0, weighed_hosts[0].weight) + self.assertEqual(0.0, weighed_hosts[1].weight) + self.assertEqual(0.0, weighed_hosts[2].weight) + + @mock.patch('nova.scheduler.utils.is_non_vmware_spec', return_value=True) + def test_ignore_non_vmware_instance(self, mock_is_non_vmware_spec): + self.flags(prefer_same_shard_resize_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + scheduler_hints=dict(_nova_check_type=[self.weighers[0]._TYPE], + source_host=['host1'])) + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + mock_is_non_vmware_spec.assert_has_calls( + len(self.hosts) * [mock.call(request_spec)]) + self.assertEqual(0.0, weighed_hosts[0].weight) + self.assertEqual(0.0, weighed_hosts[1].weight) + self.assertEqual(0.0, weighed_hosts[2].weight) + + +@ddt.ddt +class PreferSameShardOnLiveMigrateWeigherTestCase(test.NoDBTestCase): + + def setUp(self): + super().setUp() + self.weight_handler = weights.HostWeightHandler() + self.weighers = [same_shard.PreferSameShardOnLiveMigrateWeigher()] + # we need at least two for the weigher to be called + self.hosts = [ + fakes.FakeHostState('host1', 'compute', {}), + fakes.FakeHostState('host2', 'compute', {})] + + @mock.patch('nova.scheduler.utils.is_non_vmware_spec', return_value=True) + @ddt.data('resize', 'migrate', 'rebuild', None) + def test_ignore_non_live_migrating_instance(self, type_, mock_vmware): + """our first step is checking the request-spec type matches""" + self.flags(prefer_same_shard_live_migrate_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec(scheduler_hints={}) + if type_ is not None: + request_spec.scheduler_hints['_nova_check_type'] = [type_] + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + # make sure we bail before in the type check + mock_vmware.assert_not_called() + self.assertEqual(0.0, weighed_hosts[0].weight) + + def test_live_migrating_instance_same_shard(self): + """in the same shard we should get a high weight""" + self.hosts[0].aggregates = [ + objects.Aggregate(id=1, name='vc-a-0', hosts=['host1', 'host2'], + metadata={})] + self.flags(prefer_same_shard_live_migrate_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + scheduler_hints={ + '_nova_check_type': [self.weighers[0]._TYPE], + 'source_host': ['host2']}, + flavor=objects.Flavor(extra_specs={})) + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + self.assertEqual(1.0, weighed_hosts[0].weight) + + def test_live_migrating_instance_other_shard(self): + """in another shard we should get a low weight""" + self.hosts[0].aggregates = [ + objects.Aggregate(id=1, name='vc-a-0', hosts=['host1'], + metadata={})] + objects.Instance(host='host2', uuid=uuids.instance1) + self.flags(prefer_same_shard_live_migrate_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + scheduler_hints={ + '_nova_check_type': [self.weighers[0]._TYPE], + 'source_host': ['host2']}, + flavor=objects.Flavor(extra_specs={})) + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + self.assertEqual(0.0, weighed_hosts[0].weight) + + def test_weight_taken_into_account(self): + """if we change the weight, a different weight is returned""" + self.flags(prefer_same_shard_live_migrate_weight_multiplier=-1.0, + group='filter_scheduler') + self.hosts[0].aggregates = [ + objects.Aggregate(id=1, name='vc-a-0', hosts=['host1', 'host2'], + metadata={})] + request_spec = objects.RequestSpec( + scheduler_hints={ + '_nova_check_type': [self.weighers[0]._TYPE], + 'source_host': ['host2']}, + flavor=objects.Flavor(extra_specs={})) + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + self.assertEqual((-1.0, 'host1'), + (weighed_hosts[1].weight, weighed_hosts[1].obj.host)) + + +@ddt.ddt +class PreferSameShardOnMigrateWeigherTestCase(test.NoDBTestCase): + + def setUp(self): + super().setUp() + self.weight_handler = weights.HostWeightHandler() + self.weighers = [same_shard.PreferSameShardOnMigrateWeigher()] + # we need at least two for the weigher to be called + self.hosts = [ + fakes.FakeHostState('host1', 'compute', {}), + fakes.FakeHostState('host2', 'compute', {})] + + @mock.patch('nova.scheduler.utils.is_non_vmware_spec', return_value=True) + @ddt.data('resize', 'live_migrate', 'rebuild', None) + def test_ignore_non_migrating_instance(self, type_, mock_vmware): + """our first step is checking the request-spec type matches""" + self.flags(prefer_same_shard_migrate_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec(scheduler_hints={}) + if type_ is not None: + request_spec.scheduler_hints['_nova_check_type'] = [type_] + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + # make sure we bail before in the type check + mock_vmware.assert_not_called() + self.assertEqual(0.0, weighed_hosts[0].weight) + + def test__migrating_instance_same_shard(self): + """in the same shard we should get a high weight""" + self.flags(prefer_same_shard_migrate_weight_multiplier=1.0, + group='filter_scheduler') + self.hosts[0].aggregates = [ + objects.Aggregate(id=1, name='vc-a-0', hosts=['host1', 'host2'], + metadata={})] + request_spec = objects.RequestSpec( + scheduler_hints={ + '_nova_check_type': [self.weighers[0]._TYPE], + 'source_host': ['host2']}, + flavor=objects.Flavor(extra_specs={})) + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + self.assertEqual(1.0, weighed_hosts[0].weight) + + def test_migrating_instance_other_shard(self): + """in another shard we should get a low weight""" + self.hosts[0].aggregates = [ + objects.Aggregate(id=1, name='vc-a-0', hosts=['host1'], + metadata={})] + objects.Instance(host='host2', uuid=uuids.instance1) + self.flags(prefer_same_shard_migrate_weight_multiplier=1.0, + group='filter_scheduler') + request_spec = objects.RequestSpec( + scheduler_hints={ + '_nova_check_type': [self.weighers[0]._TYPE], + 'source_host': ['host2']}, + flavor=objects.Flavor(extra_specs={})) + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + self.assertEqual(0.0, weighed_hosts[0].weight) + + def test_weight_taken_into_account(self): + """if we change the weight, a different weight is returned""" + self.flags(prefer_same_shard_migrate_weight_multiplier=-1.0, + group='filter_scheduler') + self.hosts[0].aggregates = [ + objects.Aggregate(id=1, name='vc-a-0', hosts=['host1', 'host2'], + metadata={})] + request_spec = objects.RequestSpec( + scheduler_hints={ + '_nova_check_type': [self.weighers[0]._TYPE], + 'source_host': ['host2']}, + flavor=objects.Flavor(extra_specs={})) + + weighed_hosts = self.weight_handler.get_weighed_objects( + self.weighers, self.hosts, request_spec) + + self.assertEqual((-1.0, 'host1'), + (weighed_hosts[1].weight, weighed_hosts[1].obj.host))