summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-11-09 23:18:08 +0000
committerGerrit Code Review <review@openstack.org>2018-11-09 23:18:08 +0000
commit7b019103155efc938e336c58110ebd6896169881 (patch)
tree1ec3f614600a10380f94a8d512646b65e817c53d
parent65314df4d04f7c48650c9f030e16a209fd142949 (diff)
parent1a266633e1077fe4a9101bb456a69ab89689be4b (diff)
Merge "Make scheduler.utils.setup_instance_group query all cells" into stable/pikestable/pike
-rw-r--r--nova/objects/instance_group.py8
-rw-r--r--nova/scheduler/utils.py54
-rw-r--r--nova/tests/functional/test_server_group.py11
-rw-r--r--nova/tests/unit/objects/test_instance_group.py3
-rw-r--r--nova/tests/unit/scheduler/test_scheduler_utils.py3
5 files changed, 68 insertions, 11 deletions
diff --git a/nova/objects/instance_group.py b/nova/objects/instance_group.py
index 4f111f2..d351ec2 100644
--- a/nova/objects/instance_group.py
+++ b/nova/objects/instance_group.py
@@ -15,6 +15,7 @@
15import copy 15import copy
16 16
17from oslo_db import exception as db_exc 17from oslo_db import exception as db_exc
18from oslo_log import log as logging
18from oslo_utils import uuidutils 19from oslo_utils import uuidutils
19from oslo_utils import versionutils 20from oslo_utils import versionutils
20from sqlalchemy.orm import contains_eager 21from sqlalchemy.orm import contains_eager
@@ -32,6 +33,7 @@ from nova.objects import fields
32 33
33 34
34LAZY_LOAD_FIELDS = ['hosts'] 35LAZY_LOAD_FIELDS = ['hosts']
36LOG = logging.getLogger(__name__)
35 37
36 38
37def _instance_group_get_query(context, id_field=None, id=None): 39def _instance_group_get_query(context, id_field=None, id=None):
@@ -297,6 +299,12 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
297 raise exception.ObjectActionError( 299 raise exception.ObjectActionError(
298 action='obj_load_attr', reason='unable to load %s' % attrname) 300 action='obj_load_attr', reason='unable to load %s' % attrname)
299 301
302 LOG.debug("Lazy-loading '%(attr)s' on %(name)s uuid %(uuid)s",
303 {'attr': attrname,
304 'name': self.obj_name(),
305 'uuid': self.uuid,
306 })
307
300 self.hosts = self.get_hosts() 308 self.hosts = self.get_hosts()
301 self.obj_reset_changes(['hosts']) 309 self.obj_reset_changes(['hosts'])
302 310
diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py
index ae48e17..d7d53d9 100644
--- a/nova/scheduler/utils.py
+++ b/nova/scheduler/utils.py
@@ -25,6 +25,7 @@ from oslo_serialization import jsonutils
25from nova.compute import flavors 25from nova.compute import flavors
26from nova.compute import utils as compute_utils 26from nova.compute import utils as compute_utils
27import nova.conf 27import nova.conf
28from nova import context as nova_context
28from nova import exception 29from nova import exception
29from nova.i18n import _, _LE, _LW 30from nova.i18n import _, _LE, _LW
30from nova import objects 31from nova import objects
@@ -544,12 +545,44 @@ def _get_group_details(context, instance_uuid, user_group_hosts=None):
544 msg = _("ServerGroupSoftAntiAffinityWeigher not configured") 545 msg = _("ServerGroupSoftAntiAffinityWeigher not configured")
545 LOG.error(msg) 546 LOG.error(msg)
546 raise exception.UnsupportedPolicyException(reason=msg) 547 raise exception.UnsupportedPolicyException(reason=msg)
547 group_hosts = set(group.get_hosts()) 548 # NOTE(melwitt): If the context is already targeted to a cell (during a
549 # move operation), we don't need to scatter-gather.
550 if context.db_connection:
551 # We don't need to target the group object's context because it was
552 # retrieved with the targeted context earlier in this method.
553 group_hosts = set(group.get_hosts())
554 else:
555 group_hosts = set(_get_instance_group_hosts_all_cells(context,
556 group))
548 user_hosts = set(user_group_hosts) if user_group_hosts else set() 557 user_hosts = set(user_group_hosts) if user_group_hosts else set()
549 return GroupDetails(hosts=user_hosts | group_hosts, 558 return GroupDetails(hosts=user_hosts | group_hosts,
550 policies=group.policies, members=group.members) 559 policies=group.policies, members=group.members)
551 560
552 561
562def _get_instance_group_hosts_all_cells(context, instance_group):
563 def get_hosts_in_cell(cell_context):
564 # NOTE(melwitt): The obj_alternate_context is going to mutate the
565 # cell_instance_group._context and to do this in a scatter-gather
566 # with multiple parallel greenthreads, we need the instance groups
567 # to be separate object copies.
568 cell_instance_group = instance_group.obj_clone()
569 with cell_instance_group.obj_alternate_context(cell_context):
570 return cell_instance_group.get_hosts()
571
572 results = nova_context.scatter_gather_skip_cell0(context,
573 get_hosts_in_cell)
574 hosts = []
575 for result in results.values():
576 # TODO(melwitt): We will need to handle scenarios where an exception
577 # is raised while targeting a cell and when a cell does not respond
578 # as part of the "handling of a down cell" spec:
579 # https://blueprints.launchpad.net/nova/+spec/handling-down-cell
580 if result not in (nova_context.did_not_respond_sentinel,
581 nova_context.raised_exception_sentinel):
582 hosts.extend(result)
583 return hosts
584
585
553def setup_instance_group(context, request_spec): 586def setup_instance_group(context, request_spec):
554 """Add group_hosts and group_policies fields to filter_properties dict 587 """Add group_hosts and group_policies fields to filter_properties dict
555 based on instance uuids provided in request_spec, if those instances are 588 based on instance uuids provided in request_spec, if those instances are
@@ -557,11 +590,30 @@ def setup_instance_group(context, request_spec):
557 590
558 :param request_spec: Request spec 591 :param request_spec: Request spec
559 """ 592 """
593 # NOTE(melwitt): Proactively query for the instance group hosts instead of
594 # relying on a lazy-load via the 'hosts' field of the InstanceGroup object.
595 if (request_spec.instance_group and
596 'hosts' not in request_spec.instance_group):
597 group = request_spec.instance_group
598 # If the context is already targeted to a cell (during a move
599 # operation), we don't need to scatter-gather. We do need to use
600 # obj_alternate_context here because the RequestSpec is queried at the
601 # start of a move operation in compute/api, before the context has been
602 # targeted.
603 if context.db_connection:
604 with group.obj_alternate_context(context):
605 group.hosts = group.get_hosts()
606 else:
607 group.hosts = _get_instance_group_hosts_all_cells(context, group)
608
560 if request_spec.instance_group and request_spec.instance_group.hosts: 609 if request_spec.instance_group and request_spec.instance_group.hosts:
561 group_hosts = request_spec.instance_group.hosts 610 group_hosts = request_spec.instance_group.hosts
562 else: 611 else:
563 group_hosts = None 612 group_hosts = None
564 instance_uuid = request_spec.instance_uuid 613 instance_uuid = request_spec.instance_uuid
614 # This queries the group details for the group where the instance is a
615 # member. The group_hosts passed in are the hosts that contain members of
616 # the requested instance group.
565 group_info = _get_group_details(context, instance_uuid, group_hosts) 617 group_info = _get_group_details(context, instance_uuid, group_hosts)
566 if group_info is not None: 618 if group_info is not None:
567 request_spec.instance_group.hosts = list(group_info.hosts) 619 request_spec.instance_group.hosts = list(group_info.hosts)
diff --git a/nova/tests/functional/test_server_group.py b/nova/tests/functional/test_server_group.py
index ce4cea1..d5cbbe9 100644
--- a/nova/tests/functional/test_server_group.py
+++ b/nova/tests/functional/test_server_group.py
@@ -922,12 +922,7 @@ class ServerGroupTestMultiCell(ServerGroupTestBase):
922 # cell1. So boot the server to cell2 where the group member cannot be 922 # cell1. So boot the server to cell2 where the group member cannot be
923 # found as a result of the default setting. 923 # found as a result of the default setting.
924 self._boot_a_server_to_group(created_group, az='cell2') 924 self._boot_a_server_to_group(created_group, az='cell2')
925 # TODO(melwitt): Uncomment this when the bug is fixed.
926 # self._boot_a_server_to_group(created_group, az='cell1',
927 # expected_status='ERROR')
928 # TODO(melwitt): Remove this when the bug is fixed.
929 # Boot a server to cell1 with the affinity policy. This should fail 925 # Boot a server to cell1 with the affinity policy. This should fail
930 # because a group member has landed in cell2 already. But it passes 926 # because group members found in cell2 should violate the policy.
931 # because of the bug -- the query for group members doesn't find any 927 self._boot_a_server_to_group(created_group, az='cell1',
932 # members in cell2 because the query isn't multi-cell enabled. 928 expected_status='ERROR')
933 self._boot_a_server_to_group(created_group, az='cell1')
diff --git a/nova/tests/unit/objects/test_instance_group.py b/nova/tests/unit/objects/test_instance_group.py
index d542c18..f8f3382 100644
--- a/nova/tests/unit/objects/test_instance_group.py
+++ b/nova/tests/unit/objects/test_instance_group.py
@@ -253,7 +253,8 @@ class _TestInstanceGroupObject(object):
253 mock_get_by_filt.return_value = [objects.Instance(host='host1'), 253 mock_get_by_filt.return_value = [objects.Instance(host='host1'),
254 objects.Instance(host='host2')] 254 objects.Instance(host='host2')]
255 255
256 obj = objects.InstanceGroup(self.context, members=['uuid1']) 256 obj = objects.InstanceGroup(self.context, members=['uuid1'],
257 uuid=uuids.group)
257 self.assertEqual(2, len(obj.hosts)) 258 self.assertEqual(2, len(obj.hosts))
258 self.assertIn('host1', obj.hosts) 259 self.assertIn('host1', obj.hosts)
259 self.assertIn('host2', obj.hosts) 260 self.assertIn('host2', obj.hosts)
diff --git a/nova/tests/unit/scheduler/test_scheduler_utils.py b/nova/tests/unit/scheduler/test_scheduler_utils.py
index 12dc39f..c0813a0 100644
--- a/nova/tests/unit/scheduler/test_scheduler_utils.py
+++ b/nova/tests/unit/scheduler/test_scheduler_utils.py
@@ -21,6 +21,7 @@ import six
21 21
22from nova.compute import flavors 22from nova.compute import flavors
23from nova.compute import utils as compute_utils 23from nova.compute import utils as compute_utils
24from nova import context as nova_context
24from nova import exception 25from nova import exception
25from nova import objects 26from nova import objects
26from nova import rpc 27from nova import rpc
@@ -35,7 +36,7 @@ class SchedulerUtilsTestCase(test.NoDBTestCase):
35 """Test case for scheduler utils methods.""" 36 """Test case for scheduler utils methods."""
36 def setUp(self): 37 def setUp(self):
37 super(SchedulerUtilsTestCase, self).setUp() 38 super(SchedulerUtilsTestCase, self).setUp()
38 self.context = 'fake-context' 39 self.context = nova_context.get_context()
39 40
40 def test_build_request_spec_without_image(self): 41 def test_build_request_spec_without_image(self):
41 instance = {'uuid': uuids.instance} 42 instance = {'uuid': uuids.instance}