Check server group policy on migrate/evacuate

This patch changes server group checking in the instance group setup.
Previously, it checked for the presence of the 'group' scheduler hint
as an indication that the instance was a member of a group.  However,
that hint is only present on initial boot.  It is not present when the
scheduler is being used to pick a destination for a migration or
evactuate.

The updated logic checks the database to see if the instance is a
member of a group.  For performance reasons, it only does this is
server group functionality is enabled in the scheduler (group filters
are enabled).

This change makes it so server group policy enforcement will still
occur for migrations and evactuations, instead of only on first boot.

It also adds the call to setup_instance_group in evacuate, resize,
cold migrate and live migrate methods because now it is possible to
access the group setup.

Implements blueprint anti-affinity-on-migration

Closes-bug: #1379451

Change-Id: I6e9ea6ba69e8658fa5126a3488ee12cf2437460c
This commit is contained in:
Sylvain Bauza 2014-11-18 17:34:51 +01:00
parent 6a23d68209
commit 459ca56de2
6 changed files with 191 additions and 69 deletions

View File

@ -503,6 +503,8 @@ class ComputeTaskManager(base.Base):
quotas = objects.Quotas.from_reservations(context,
reservations,
instance=instance)
scheduler_utils.setup_instance_group(context, request_spec,
filter_properties)
try:
scheduler_utils.populate_retry(filter_properties, instance['uuid'])
hosts = self.scheduler_client.select_destinations(
@ -598,16 +600,8 @@ class ComputeTaskManager(base.Base):
# 2.0 of the RPC API.
request_spec = scheduler_utils.build_request_spec(context, image,
instances)
# NOTE(sbauza): filter_properties['hints'] can be None
hints = filter_properties.get('scheduler_hints', {}) or {}
group_hint = hints.get('group')
group_hosts = filter_properties.get('group_hosts')
group_info = scheduler_utils.setup_instance_group(context, group_hint,
group_hosts)
if isinstance(group_info, tuple):
filter_properties['group_updated'] = True
(filter_properties['group_hosts'],
filter_properties['group_policies']) = group_info
scheduler_utils.setup_instance_group(context, request_spec,
filter_properties)
# TODO(danms): Remove this in version 2.0 of the RPC API
if (requested_networks and
not isinstance(requested_networks,
@ -663,6 +657,8 @@ class ComputeTaskManager(base.Base):
*instances):
request_spec = scheduler_utils.build_request_spec(context, image,
instances)
scheduler_utils.setup_instance_group(context, request_spec,
filter_properties)
hosts = self.scheduler_client.select_destinations(context,
request_spec, filter_properties)
return hosts
@ -751,6 +747,8 @@ class ComputeTaskManager(base.Base):
request_spec = scheduler_utils.build_request_spec(context,
image_ref,
[instance])
scheduler_utils.setup_instance_group(context, request_spec,
filter_properties)
try:
hosts = self.scheduler_client.select_destinations(context,
request_spec,

View File

@ -156,6 +156,8 @@ class LiveMigrationTask(object):
while host is None:
self._check_not_over_max_retries(attempted_hosts)
filter_properties = {'ignore_hosts': attempted_hosts}
scheduler_utils.setup_instance_group(self.context, request_spec,
filter_properties)
host = self.scheduler_client.select_destinations(self.context,
request_spec, filter_properties)[0]['host']
try:

View File

@ -246,11 +246,12 @@ _SUPPORTS_AFFINITY = None
_SUPPORTS_ANTI_AFFINITY = None
def setup_instance_group(context, group_hint, user_group_hosts=None):
"""Provides group_hosts and group_policies sets related to the group
provided by hint if corresponding filters are enabled.
def _get_group_details(context, instance_uuids, user_group_hosts=None):
"""Provide group_hosts and group_policies sets related to instances if
those instances are belonging to a group and if corresponding filters are
enabled.
:param group_hint: 'group' scheduler hint
:param instance_uuids: list of instance uuids
:param user_group_hosts: Hosts from the group or empty set
:returns: None or tuple (group_hosts, group_policies)
@ -263,19 +264,47 @@ def setup_instance_group(context, group_hint, user_group_hosts=None):
if _SUPPORTS_ANTI_AFFINITY is None:
_SUPPORTS_ANTI_AFFINITY = validate_filter(
'ServerGroupAntiAffinityFilter')
if group_hint:
group = objects.InstanceGroup.get_by_hint(context, group_hint)
policies = set(('anti-affinity', 'affinity'))
if any((policy in policies) for policy in group.policies):
if ('affinity' in group.policies and not _SUPPORTS_AFFINITY):
msg = _("ServerGroupAffinityFilter not configured")
LOG.error(msg)
raise exception.NoValidHost(reason=msg)
if ('anti-affinity' in group.policies and
not _SUPPORTS_ANTI_AFFINITY):
msg = _("ServerGroupAntiAffinityFilter not configured")
LOG.error(msg)
raise exception.NoValidHost(reason=msg)
group_hosts = set(group.get_hosts(context))
user_hosts = set(user_group_hosts) if user_group_hosts else set()
return (user_hosts | group_hosts, group.policies)
_supports_server_groups = any((_SUPPORTS_AFFINITY,
_SUPPORTS_ANTI_AFFINITY))
if not _supports_server_groups or not instance_uuids:
return
try:
# NOTE(sbauza) If there are multiple instance UUIDs, it's a boot
# request and they will all be in the same group, so it's safe to
# only check the first one.
group = objects.InstanceGroup.get_by_instance_uuid(context,
instance_uuids[0])
except exception.InstanceGroupNotFound:
return
policies = set(('anti-affinity', 'affinity'))
if any((policy in policies) for policy in group.policies):
if (not _SUPPORTS_AFFINITY and 'affinity' in group.policies):
msg = _("ServerGroupAffinityFilter not configured")
LOG.error(msg)
raise exception.NoValidHost(reason=msg)
if (not _SUPPORTS_ANTI_AFFINITY and 'anti-affinity' in group.policies):
msg = _("ServerGroupAntiAffinityFilter not configured")
LOG.error(msg)
raise exception.NoValidHost(reason=msg)
group_hosts = set(group.get_hosts(context))
user_hosts = set(user_group_hosts) if user_group_hosts else set()
return (user_hosts | group_hosts, group.policies)
def setup_instance_group(context, request_spec, filter_properties):
"""Add group_hosts and group_policies fields to filter_properties dict
based on instance uuids provided in request_spec, if those instances are
belonging to a group.
:param request_spec: Request spec
:param filter_properties: Filter properties
"""
group_hosts = filter_properties.get('group_hosts')
instance_uuids = request_spec.get('instance_uuids')
group_info = _get_group_details(context, instance_uuids, group_hosts)
if group_info is not None:
filter_properties['group_updated'] = True
(filter_properties['group_hosts'],
filter_properties['group_policies']) = group_info

View File

@ -231,6 +231,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
def test_find_destination_works(self):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
@ -242,6 +243,8 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
@ -255,6 +258,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance['image_ref'] = ''
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
@ -263,6 +267,8 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
scheduler_utils.build_request_spec(self.context, None,
mox.IgnoreArg()).AndReturn({})
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
@ -275,6 +281,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
def _test_find_destination_retry_hypervisor_raises(self, error):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
@ -286,12 +293,16 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
self.task._check_compatible_with_source_hypervisor("host1")\
.AndRaise(error)
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host, "host1"]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host2'}])
@ -313,6 +324,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.flags(migrate_max_retries=1)
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
@ -324,6 +336,8 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
@ -331,6 +345,8 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.task._call_livem_checks_on_host("host1")\
.AndRaise(exception.Invalid)
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host, "host1"]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host2'}])
@ -344,6 +360,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.flags(migrate_max_retries=0)
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
@ -354,6 +371,8 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
@ -366,6 +385,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
def test_find_destination_when_runs_out_of_hosts(self):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
compute_utils.get_image_metadata(self.context,
@ -373,6 +393,8 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
scheduler_utils.setup_instance_group(
self.context, {}, {'ignore_hosts': [self.instance_host]})
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndRaise(
exception.NoValidHost(reason=""))

View File

@ -1186,6 +1186,7 @@ class _BaseTaskTestCase(object):
def test_cold_migrate(self):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(
self.conductor_manager.compute_rpcapi, 'prep_resize')
self.mox.StubOutWithMock(self.conductor_manager.scheduler_client,
@ -1206,6 +1207,8 @@ class _BaseTaskTestCase(object):
[mox.IsA(objects.Instance)],
instance_type=flavor).AndReturn(request_spec)
scheduler_utils.setup_instance_group(self.context, request_spec, {})
hosts = [dict(host='host1', nodename=None, limits={})]
self.conductor_manager.scheduler_client.select_destinations(
self.context, request_spec,
@ -1255,14 +1258,14 @@ class _BaseTaskTestCase(object):
self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
'build_and_run_instance')
scheduler_utils.setup_instance_group(self.context, None, None)
spec = {'image': {'fake_data': 'should_pass_silently'},
'instance_properties': jsonutils.to_primitive(instances[0]),
'instance_type': instance_type_p,
'instance_uuids': [inst.uuid for inst in instances],
'num_instances': 2}
scheduler_utils.setup_instance_group(self.context, spec, {})
self.conductor_manager.scheduler_client.select_destinations(
self.context, {'image': {'fake_data': 'should_pass_silently'},
'instance_properties': jsonutils.to_primitive(
instances[0]),
'instance_type': instance_type_p,
'instance_uuids': [inst.uuid for inst in instances],
'num_instances': 2},
self.context, spec,
{'retry': {'num_attempts': 1, 'hosts': []}}).AndReturn(
[{'host': 'host1', 'nodename': 'node1', 'limits': []},
{'host': 'host2', 'nodename': 'node2', 'limits': []}])
@ -1349,7 +1352,7 @@ class _BaseTaskTestCase(object):
scheduler_utils.build_request_spec(self.context, image,
mox.IgnoreArg()).AndReturn(spec)
scheduler_utils.setup_instance_group(self.context, None, None)
scheduler_utils.setup_instance_group(self.context, spec, {})
self.conductor_manager.scheduler_client.select_destinations(
self.context, spec,
{'retry': {'num_attempts': 1,
@ -1569,12 +1572,14 @@ class _BaseTaskTestCase(object):
with contextlib.nested(
mock.patch.object(self.conductor_manager.compute_rpcapi,
'rebuild_instance'),
mock.patch.object(scheduler_utils, 'setup_instance_group',
return_value=False),
mock.patch.object(self.conductor_manager.scheduler_client,
'select_destinations',
return_value=[{'host': expected_host}]),
mock.patch('nova.scheduler.utils.build_request_spec',
return_value=request_spec)
) as (rebuild_mock, select_dest_mock, bs_mock):
) as (rebuild_mock, sig_mock, select_dest_mock, bs_mock):
self.conductor_manager.rebuild_instance(context=self.context,
instance=inst_obj,
**rebuild_args)
@ -1598,12 +1603,14 @@ class _BaseTaskTestCase(object):
with contextlib.nested(
mock.patch.object(self.conductor_manager.compute_rpcapi,
'rebuild_instance'),
mock.patch.object(scheduler_utils, 'setup_instance_group',
return_value=False),
mock.patch.object(self.conductor_manager.scheduler_client,
'select_destinations',
side_effect=exc.NoValidHost(reason='')),
mock.patch('nova.scheduler.utils.build_request_spec',
return_value=request_spec)
) as (rebuild_mock, select_dest_mock, bs_mock):
) as (rebuild_mock, sig_mock, select_dest_mock, bs_mock):
self.assertRaises(exc.NoValidHost,
self.conductor_manager.rebuild_instance,
context=self.context, instance=inst_obj,
@ -1781,6 +1788,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.conductor.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.conductor,
@ -1795,6 +1803,9 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.context, image, [inst_obj],
instance_type=flavor).AndReturn(request_spec)
scheduler_utils.setup_instance_group(self.context, request_spec,
filter_props)
exc_info = exc.NoValidHost(reason="")
self.conductor.scheduler_client.select_destinations(
@ -1838,6 +1849,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.conductor.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.conductor,
@ -1852,6 +1864,9 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.context, image, [inst_obj],
instance_type=flavor).AndReturn(request_spec)
scheduler_utils.setup_instance_group(self.context, request_spec,
filter_props)
exc_info = exc.NoValidHost(reason="")
self.conductor.scheduler_client.select_destinations(
@ -1897,10 +1912,12 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
return_value=image),
mock.patch.object(scheduler_utils, 'build_request_spec',
return_value=request_spec),
mock.patch.object(scheduler_utils, 'setup_instance_group',
return_value=False),
mock.patch.object(self.conductor.scheduler_client,
'select_destinations',
side_effect=exc.NoValidHost(reason=""))
) as (image_mock, brs_mock, select_dest_mock):
) as (image_mock, brs_mock, sig_mock, select_dest_mock):
nvh = self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate, self.context,
inst_obj, flavor, filter_props, [resvs])
@ -1921,6 +1938,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_utils, 'setup_instance_group')
self.mox.StubOutWithMock(self.conductor.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(scheduler_utils,
@ -1939,6 +1957,9 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.context, image, [inst_obj],
instance_type='flavor').AndReturn(request_spec)
scheduler_utils.setup_instance_group(self.context, request_spec,
filter_props)
expected_filter_props = {'retry': {'num_attempts': 1,
'hosts': []},
'context': None}
@ -2001,10 +2022,12 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
return_value=image),
mock.patch.object(scheduler_utils, 'build_request_spec',
return_value=request_spec),
mock.patch.object(scheduler_utils, 'setup_instance_group',
return_value=False),
mock.patch.object(self.conductor.scheduler_client,
'select_destinations',
side_effect=exc.NoValidHost(reason=""))
) as (image_mock, brs_mock, select_dest_mock):
) as (image_mock, brs_mock, sig_mock, select_dest_mock):
nvh = self.assertRaises(exc.NoValidHost,
self.conductor._cold_migrate, self.context,
inst_obj, flavor_new, filter_props,
@ -2029,7 +2052,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
scheduler_utils.build_request_spec(self.context, image,
mox.IgnoreArg()).AndReturn(spec)
scheduler_utils.setup_instance_group(self.context, None, None)
scheduler_utils.setup_instance_group(self.context, spec, {})
self.conductor_manager.scheduler_client.select_destinations(
self.context, spec,
{'retry': {'num_attempts': 1, 'hosts': []}}).AndReturn(
@ -2105,8 +2128,11 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
block_device_mapping='block_device_mapping',
legacy_bdm=False)
# NOTE(sbauza): Due to populate_retry() later in the code,
# filter_properties is dynamically modified
setup_instance_group.assert_called_once_with(
self.context, None, None)
self.context, spec, {'retry': {'num_attempts': 1,
'hosts': []}})
build_and_run_instance.assert_called_once_with(self.context,
instance=instances[1], host='host2', image={'fake-data':
'should_pass_silently'}, request_spec=spec,

View File

@ -245,33 +245,49 @@ class SchedulerUtilsTestCase(test.NoDBTestCase):
group.policies = [policy]
return group
def _group_details_in_filter_properties(self, group, func='get_by_uuid',
hint=None, policy=None):
group_hint = hint
def _get_group_details(self, group, policy=None):
group_hosts = ['hostB']
with contextlib.nested(
mock.patch.object(objects.InstanceGroup, func, return_value=group),
mock.patch.object(objects.InstanceGroup, 'get_by_instance_uuid',
return_value=group),
mock.patch.object(objects.InstanceGroup, 'get_hosts',
return_value=['hostA']),
) as (get_group, get_hosts):
scheduler_utils._SUPPORTS_ANTI_AFFINITY = None
scheduler_utils._SUPPORTS_AFFINITY = None
group_info = scheduler_utils.setup_instance_group(
self.context, group_hint, group_hosts)
group_info = scheduler_utils._get_group_details(
self.context, ['fake_uuid'], group_hosts)
self.assertEqual(
(set(['hostA', 'hostB']), [policy]),
group_info)
def test_group_details_in_filter_properties(self):
def test_get_group_details(self):
for policy in ['affinity', 'anti-affinity']:
group = self._create_server_group(policy)
self._group_details_in_filter_properties(group, func='get_by_uuid',
hint=group.uuid,
policy=policy)
self._get_group_details(group, policy=policy)
def _group_filter_with_filter_not_configured(self, policy):
self.flags(scheduler_default_filters=['f1', 'f2'])
def test_get_group_details_with_no_affinity_filters(self):
self.flags(scheduler_default_filters=['fake'])
scheduler_utils._SUPPORTS_ANTI_AFFINITY = None
scheduler_utils._SUPPORTS_AFFINITY = None
group_info = scheduler_utils._get_group_details(self.context,
['fake-uuid'])
self.assertIsNone(group_info)
def test_get_group_details_with_no_instance_uuids(self):
self.flags(scheduler_default_filters=['fake'])
scheduler_utils._SUPPORTS_ANTI_AFFINITY = None
scheduler_utils._SUPPORTS_AFFINITY = None
group_info = scheduler_utils._get_group_details(self.context, None)
self.assertIsNone(group_info)
def _get_group_details_with_filter_not_configured(self, policy):
wrong_filter = {
'affinity': 'ServerGroupAntiAffinityFilter',
'anti-affinity': 'ServerGroupAffinityFilter',
}
self.flags(scheduler_default_filters=[wrong_filter[policy]])
instance = fake_instance.fake_instance_obj(self.context,
params={'host': 'hostA'})
@ -282,7 +298,7 @@ class SchedulerUtilsTestCase(test.NoDBTestCase):
group.policies = [policy]
with contextlib.nested(
mock.patch.object(objects.InstanceGroup, 'get_by_uuid',
mock.patch.object(objects.InstanceGroup, 'get_by_instance_uuid',
return_value=group),
mock.patch.object(objects.InstanceGroup, 'get_hosts',
return_value=['hostA']),
@ -290,20 +306,49 @@ class SchedulerUtilsTestCase(test.NoDBTestCase):
scheduler_utils._SUPPORTS_ANTI_AFFINITY = None
scheduler_utils._SUPPORTS_AFFINITY = None
self.assertRaises(exception.NoValidHost,
scheduler_utils.setup_instance_group,
self.context, group.uuid)
scheduler_utils._get_group_details,
self.context, ['fake-uuid'])
def test_group_filter_with_filter_not_configured(self):
def test_get_group_details_with_filter_not_configured(self):
policies = ['anti-affinity', 'affinity']
for policy in policies:
self._group_filter_with_filter_not_configured(policy)
self._get_group_details_with_filter_not_configured(policy)
def test_group_uuid_details_in_filter_properties(self):
group = self._create_server_group()
self._group_details_in_filter_properties(group, 'get_by_uuid',
group.uuid, 'anti-affinity')
@mock.patch.object(scheduler_utils, '_get_group_details')
def test_setup_instance_group_in_filter_properties(self, mock_ggd):
mock_ggd.return_value = (set(['hostA', 'hostB']), ['policy'])
spec = {'instance_uuids': ['fake-uuid']}
filter_props = {'group_hosts': ['hostC']}
def test_group_name_details_in_filter_properties(self):
group = self._create_server_group()
self._group_details_in_filter_properties(group, 'get_by_name',
group.name, 'anti-affinity')
scheduler_utils.setup_instance_group(self.context, spec, filter_props)
mock_ggd.assert_called_once_with(self.context, ['fake-uuid'],
['hostC'])
expected_filter_props = {'group_updated': True,
'group_hosts': set(['hostA', 'hostB']),
'group_policies': ['policy']}
self.assertEqual(expected_filter_props, filter_props)
@mock.patch.object(scheduler_utils, '_get_group_details')
def test_setup_instance_group_with_no_group(self, mock_ggd):
mock_ggd.return_value = None
spec = {'instance_uuids': ['fake-uuid']}
filter_props = {'group_hosts': ['hostC']}
scheduler_utils.setup_instance_group(self.context, spec, filter_props)
mock_ggd.assert_called_once_with(self.context, ['fake-uuid'],
['hostC'])
self.assertNotIn('group_updated', filter_props)
self.assertNotIn('group_policies', filter_props)
self.assertEqual(['hostC'], filter_props['group_hosts'])
@mock.patch.object(scheduler_utils, '_get_group_details')
def test_setup_instance_group_with_filter_not_configured(self, mock_ggd):
mock_ggd.side_effect = exception.NoValidHost(reason='whatever')
spec = {'instance_uuids': ['fake-uuid']}
filter_props = {'group_hosts': ['hostC']}
self.assertRaises(exception.NoValidHost,
scheduler_utils.setup_instance_group,
self.context, spec, filter_props)