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:
parent
6a23d68209
commit
459ca56de2
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=""))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue