diff --git a/nova/scheduler/weights/affinity.py b/nova/scheduler/weights/affinity.py index 90297f51a4d..8e47cabdebc 100644 --- a/nova/scheduler/weights/affinity.py +++ b/nova/scheduler/weights/affinity.py @@ -55,6 +55,7 @@ def _weigh_object(self, host_state, request_spec): class ServerGroupSoftAffinityWeigher(_SoftAffinityWeigherBase): policy_name = 'soft-affinity' warning_sent = False + _SHARD_PREFIX = 'vc-' def weight_multiplier(self): if (CONF.filter_scheduler.soft_affinity_weight_multiplier < 0 and @@ -70,6 +71,39 @@ def weight_multiplier(self): return CONF.filter_scheduler.soft_affinity_weight_multiplier + def _weigh_object(self, host_state, request_spec): + weight = super(ServerGroupSoftAffinityWeigher, self)._weigh_object( + host_state, request_spec) + + # if the host contained servers from the same group, return that weight + if weight: + return weight + + # get shards of the host + host_shard_aggrs = [aggr for aggr in host_state.aggregates + if aggr.name.startswith(self._SHARD_PREFIX)] + if not host_shard_aggrs: + LOG.warning('No aggregates found for host %(host)s.', + {'host': host_state.host}) + return 0 + + if len(host_shard_aggrs) > 1: + LOG.warning('More than one host aggregates found for ' + 'host %(host)s, selecting first.', + {'host': host_state.host}) + host_shard_aggr = host_shard_aggrs[0] + + group_hosts = None + if request_spec.instance_group and request_spec.instance_group.hosts: + group_hosts = set(request_spec.instance_group.hosts) + if not group_hosts: + return 0 + # group_hosts doesn't contain our host, because otherwise we would + # have returned a weighed already as we would have instances + if group_hosts & set(host_shard_aggr.hosts): + return 0.5 + return 0 + class ServerGroupSoftAntiAffinityWeigher(_SoftAffinityWeigherBase): policy_name = 'soft-anti-affinity' diff --git a/nova/tests/unit/scheduler/weights/test_weights_affinity.py b/nova/tests/unit/scheduler/weights/test_weights_affinity.py index 6f7ea7f5ff5..7ba7fea4032 100644 --- a/nova/tests/unit/scheduler/weights/test_weights_affinity.py +++ b/nova/tests/unit/scheduler/weights/test_weights_affinity.py @@ -29,7 +29,8 @@ def setUp(self): self.weight_handler = weights.HostWeightHandler() self.weighers = [] - def _get_weighed_host(self, hosts, policy, group='default'): + def _get_weighed_host(self, hosts, policy, group='default', + expected_host=None, match_host=False): if group == 'default': members = ['member1', 'member2', 'member3', 'member4', 'member5', 'member6', 'member7'] @@ -38,17 +39,27 @@ def _get_weighed_host(self, hosts, policy, group='default'): request_spec = objects.RequestSpec( instance_group=objects.InstanceGroup( policy=policy, - members=members)) - return self.weight_handler.get_weighed_objects(self.weighers, - hosts, - request_spec)[0] + members=members, + hosts=[h.host for h in hosts])) + hosts = self.weight_handler.get_weighed_objects(self.weighers, + hosts, + request_spec) + if not match_host: + return hosts[0] + else: + for host in hosts: + if host.obj.host == expected_host: + return host def _get_all_hosts(self): + aggs1 = [objects.Aggregate(id=1, name='vc-a-1', hosts=['host1'])] + aggs2 = [objects.Aggregate(id=2, name='vc-a-2', hosts=['host3']), + objects.Aggregate(id=2, name='vc-a-3', hosts=['host4'])] host_values = [ ('host1', 'node1', {'instances': { 'member1': mock.sentinel, 'instance13': mock.sentinel - }}), + }, 'aggregates': aggs1}), ('host2', 'node2', {'instances': { 'member2': mock.sentinel, 'member3': mock.sentinel, @@ -60,12 +71,12 @@ def _get_all_hosts(self): }}), ('host3', 'node3', {'instances': { 'instance15': mock.sentinel - }}), + }, 'aggregates': aggs2}), ('host4', 'node4', {'instances': { 'member6': mock.sentinel, 'member7': mock.sentinel, 'instance16': mock.sentinel - }})] + }, 'aggregates': aggs2})] return [fakes.FakeHostState(host, node, values) for host, node, values in host_values] @@ -105,6 +116,20 @@ def test_soft_affinity_weight_multiplier_positive_value(self): expected_weight=2.0, expected_host='host2') + def test_soft_affinity_weight_multiplier_same_shards(self): + """For host, which does not contain servers of server-group, + but in same shard as the servers in the server-group, weight + is 0.5. Due to normalization smallest weight become 0.0 + """ + self.flags(soft_affinity_weight_multiplier=2.0, + group='filter_scheduler') + expected_weight = 0.0 + hostinfo_list = self._get_all_hosts() + weighed_host = self._get_weighed_host(hostinfo_list, + policy='soft-affinity', group='default', + expected_host='host3', match_host=True) + self.assertEqual(expected_weight, weighed_host.weight) + @mock.patch.object(affinity, 'LOG') def test_soft_affinity_weight_multiplier_negative_value(self, mock_log): self.flags(soft_affinity_weight_multiplier=-1.0, @@ -116,7 +141,8 @@ def test_soft_affinity_weight_multiplier_negative_value(self, mock_log): self._do_test(policy='soft-affinity', expected_weight=0.0, expected_host='host3') - self.assertEqual(1, mock_log.warning.call_count) + # one from _weigh_object() and two from weight_multiplier() + self.assertEqual(3, mock_log.warning.call_count) def test_running_twice(self): """Run the weighing twice for different groups each run