Make server groups api aware of multiple cells for membership

This teaches the server groups api to query all of the cells that have
an instance claiming to be a member of the group. Instead of polling all
cells, this collects the subset of cells that actually have members,
according to our InstanceMapping data, and then checks for the deleted-ness
in the actual cell database.

This augments the tests to have one instance in each cell, plus an instance
that is not yet scheduled in any cell to give us coverage of all the
possibilities.

This replaces the previously-flawed and now reverted change:

  If571f9e7c9d0ef6265a249ff808d30a24ab462af

Change-Id: Idd2e35bc95ed98ebc0340ff62e109e23c8adcb21
This commit is contained in:
Dan Smith 2017-04-17 08:26:17 -07:00
parent c3597c87d8
commit 1226c57884
2 changed files with 84 additions and 17 deletions
nova
api/openstack/compute
tests/unit/api/openstack/compute

@ -15,6 +15,8 @@
"""The Server Group API Extension."""
import collections
from oslo_log import log as logging
import webob
from webob import exc
@ -25,6 +27,7 @@ from nova.api.openstack.compute.schemas import server_groups as schema
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api import validation
from nova import context as nova_context
import nova.exception
from nova.i18n import _
from nova import objects
@ -41,6 +44,40 @@ def _authorize_context(req, action):
return context
def _get_not_deleted(context, uuids):
mappings = objects.InstanceMappingList.get_by_instance_uuids(
context, uuids)
inst_by_cell = collections.defaultdict(list)
cell_mappings = {}
found_inst_uuids = []
# Get a master list of cell mappings, and a list of instance
# uuids organized by cell
for im in mappings:
if not im.cell_mapping:
# Not scheduled yet, so just throw it in the final list
# and move on
found_inst_uuids.append(im.instance_uuid)
continue
if im.cell_mapping.uuid not in cell_mappings:
cell_mappings[im.cell_mapping.uuid] = im.cell_mapping
inst_by_cell[im.cell_mapping.uuid].append(im.instance_uuid)
# Query each cell for the instances that are inside, building
# a list of non-deleted instance uuids.
for cell_uuid, cell_mapping in cell_mappings.items():
inst_uuids = inst_by_cell[cell_uuid]
LOG.debug('Querying cell %(cell)s for %(num)i instances',
{'cell': cell_mapping.identity, 'num': len(uuids)})
filters = {'uuid': inst_uuids, 'deleted': False}
with nova_context.target_cell(context, cell_mapping) as ctx:
found_inst_uuids.extend([
inst.uuid for inst in objects.InstanceList.get_by_filters(
ctx, filters=filters)])
return found_inst_uuids
class ServerGroupController(wsgi.Controller):
"""The Server group API controller for the OpenStack API."""
@ -59,10 +96,7 @@ class ServerGroupController(wsgi.Controller):
members = []
if group.members:
# Display the instances that are not deleted.
filters = {'uuid': group.members, 'deleted': False}
instances = objects.InstanceList.get_by_filters(
context, filters=filters)
members = [instance.uuid for instance in instances]
members = _get_not_deleted(context, group.members)
server_group['members'] = members
# Add project id information to the response data for
# API version v2.13

@ -24,6 +24,7 @@ from nova import exception
from nova import objects
from nova.policies import server_groups as sg_policies
from nova import test
from nova.tests import fixtures
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit import policy_fixture
from nova.tests import uuidsentinel
@ -75,7 +76,8 @@ def server_group_db(sg):
return AttrDict(attrs)
class ServerGroupTestV21(test.TestCase):
class ServerGroupTestV21(test.NoDBTestCase):
USES_DB_SELF = True
validation_error = exception.ValidationError
def setUp(self):
@ -86,6 +88,22 @@ class ServerGroupTestV21(test.TestCase):
self.foo_req = fakes.HTTPRequest.blank('', project_id='foo')
self.policy = self.useFixture(policy_fixture.RealPolicyFixture())
self.useFixture(fixtures.Database(database='api'))
cells = fixtures.CellDatabases()
cells.add_cell_database(uuidsentinel.cell1)
cells.add_cell_database(uuidsentinel.cell2)
self.useFixture(cells)
ctxt = context.get_admin_context()
self.cells = {}
for uuid in (uuidsentinel.cell1, uuidsentinel.cell2):
cm = objects.CellMapping(context=ctxt,
uuid=uuid,
database_connection=uuid,
transport_url=uuid)
cm.create()
self.cells[cm.uuid] = cm
def _setup_controller(self):
self.controller = sg_v21.ServerGroupController()
@ -138,14 +156,21 @@ class ServerGroupTestV21(test.TestCase):
"Policy doesn't allow %s to be performed." % rule_name,
exc.format_message())
def _create_instance(self, context):
instance = objects.Instance(context=context,
image_ref=uuidsentinel.fake_image_ref,
node='node1', reservation_id='a',
host='host1', project_id='fake',
vm_state='fake',
system_metadata={'key': 'value'})
instance.create()
def _create_instance(self, ctx, cell):
with context.target_cell(ctx, cell) as cctx:
instance = objects.Instance(context=cctx,
image_ref=uuidsentinel.fake_image_ref,
node='node1', reservation_id='a',
host='host1', project_id='fake',
vm_state='fake',
system_metadata={'key': 'value'})
instance.create()
im = objects.InstanceMapping(context=ctx,
project_id=ctx.project_id,
user_id=ctx.user_id,
cell_mapping=cell,
instance_uuid=instance.uuid)
im.create()
return instance
def _create_instance_group(self, context, members):
@ -156,7 +181,11 @@ class ServerGroupTestV21(test.TestCase):
return ig.uuid
def _create_groups_and_instances(self, ctx):
instances = [self._create_instance(ctx), self._create_instance(ctx)]
cell1 = self.cells[uuidsentinel.cell1]
cell2 = self.cells[uuidsentinel.cell2]
instances = [self._create_instance(ctx, cell=cell1),
self._create_instance(ctx, cell=cell2),
self._create_instance(ctx, cell=None)]
members = [instance.uuid for instance in instances]
ig_uuid = self._create_instance_group(ctx, members)
return (ig_uuid, instances, members)
@ -291,7 +320,7 @@ class ServerGroupTestV21(test.TestCase):
(ig_uuid, instances, members) = self._create_groups_and_instances(ctx)
res_dict = self.controller.show(self.req, ig_uuid)
result_members = res_dict['server_group']['members']
self.assertEqual(2, len(result_members))
self.assertEqual(3, len(result_members))
for member in members:
self.assertIn(member, result_members)
@ -304,7 +333,11 @@ class ServerGroupTestV21(test.TestCase):
(ig_uuid, instances, members) = self._create_groups_and_instances(ctx)
# delete an instance
instances[1].destroy()
im = objects.InstanceMapping.get_by_instance_uuid(ctx,
instances[1].uuid)
with context.target_cell(ctx, im.cell_mapping):
instances[1].destroy()
# check that the instance does not exist
self.assertRaises(exception.InstanceNotFound,
objects.Instance.get_by_uuid,
@ -312,7 +345,7 @@ class ServerGroupTestV21(test.TestCase):
res_dict = self.controller.show(self.req, ig_uuid)
result_members = res_dict['server_group']['members']
# check that only the active instance is displayed
self.assertEqual(1, len(result_members))
self.assertEqual(2, len(result_members))
self.assertIn(instances[0].uuid, result_members)
def test_display_members_rbac_default(self):