Make InstanceGroup object favor the API database

This makes the InstanceGroup object load first from the API database,
falling back to the main database as necessary. Creates happen in the
API database only now.

Part of blueprint cells-instance-groups-api-db

Change-Id: I2fe7524818e92a4d9a55f84633e70091a42531ea
This commit is contained in:
melanie witt 2016-06-17 09:31:05 +00:00 committed by Dan Smith
parent f3d001f36c
commit c5c4f71aa0
3 changed files with 533 additions and 85 deletions

View File

@ -12,11 +12,18 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from oslo_db import exception as db_exc
from oslo_utils import uuidutils
from oslo_utils import versionutils
from sqlalchemy.orm import contains_eager
from sqlalchemy.orm import joinedload
from nova.compute import utils as compute_utils
from nova import db
from nova.db.sqlalchemy import api as db_api
from nova.db.sqlalchemy import api_models
from nova import exception
from nova import objects
from nova.objects import base
@ -26,6 +33,79 @@ from nova.objects import fields
LAZY_LOAD_FIELDS = ['hosts']
def _instance_group_get_query(context, id_field=None, id=None):
query = context.session.query(api_models.InstanceGroup).\
options(joinedload('_policies')).\
options(joinedload('_members'))
if not context.is_admin:
query = query.filter_by(project_id=context.project_id)
if id and id_field:
query = query.filter(id_field == id)
return query
def _instance_group_model_get_query(context, model_class, group_id):
return context.session.query(model_class).filter_by(group_id=group_id)
def _instance_group_model_add(context, model_class, items, item_models, field,
group_id, append_to_models=None):
models = []
already_existing = set()
for db_item in item_models:
already_existing.add(getattr(db_item, field))
models.append(db_item)
for item in items:
if item in already_existing:
continue
model = model_class()
values = {'group_id': group_id}
values[field] = item
model.update(values)
context.session.add(model)
if append_to_models:
append_to_models.append(model)
models.append(model)
return models
def _instance_group_policies_add(context, group, policies):
query = _instance_group_model_get_query(context,
api_models.InstanceGroupPolicy,
group.id)
query = query.filter(
api_models.InstanceGroupPolicy.policy.in_(set(policies)))
return _instance_group_model_add(context, api_models.InstanceGroupPolicy,
policies, query.all(), 'policy', group.id,
append_to_models=group._policies)
def _instance_group_members_add(context, group, members):
query = _instance_group_model_get_query(context,
api_models.InstanceGroupMember,
group.id)
query = query.filter(
api_models.InstanceGroupMember.instance_uuid.in_(set(members)))
return _instance_group_model_add(context, api_models.InstanceGroupMember,
members, query.all(), 'instance_uuid',
group.id, append_to_models=group._members)
def _instance_group_members_add_by_uuid(context, group_uuid, members):
# NOTE(melwitt): The condition on the join limits the number of members
# returned to only those we wish to check as already existing.
group = context.session.query(api_models.InstanceGroup).\
outerjoin(api_models.InstanceGroupMember,
api_models.InstanceGroupMember.instance_uuid.in_(set(members))).\
filter(api_models.InstanceGroup.uuid == group_uuid).\
options(contains_eager('_members')).first()
if not group:
raise exception.InstanceGroupNotFound(group_uuid=group_uuid)
return _instance_group_model_add(context, api_models.InstanceGroupMember,
members, group._members, 'instance_uuid',
group.id)
# TODO(berrange): Remove NovaObjectDictCompat
@base.NovaObjectRegistry.register
class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
@ -74,8 +154,15 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
for field in instance_group.fields:
if field in LAZY_LOAD_FIELDS:
continue
if field == 'deleted':
instance_group.deleted = db_inst['deleted'] == db_inst['id']
# This is needed to handle db models from both the api
# database and the main database. In the migration to
# the api database, we have removed soft-delete, so
# the object fields for delete must be filled in with
# default values for db models from the api database.
ignore = {'deleted': False,
'deleted_at': None}
if field in ignore and not hasattr(db_inst, field):
instance_group[field] = ignore[field]
else:
instance_group[field] = db_inst[field]
@ -83,6 +170,114 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
instance_group.obj_reset_changes()
return instance_group
@staticmethod
@db_api.api_context_manager.reader
def _get_from_db_by_uuid(context, uuid):
grp = _instance_group_get_query(context,
id_field=api_models.InstanceGroup.uuid,
id=uuid).first()
if not grp:
raise exception.InstanceGroupNotFound(group_uuid=uuid)
return grp
@staticmethod
@db_api.api_context_manager.reader
def _get_from_db_by_id(context, id):
grp = _instance_group_get_query(context,
id_field=api_models.InstanceGroup.id,
id=id).first()
if not grp:
raise exception.InstanceGroupNotFound(group_uuid=id)
return grp
@staticmethod
@db_api.api_context_manager.reader
def _get_from_db_by_name(context, name):
grp = _instance_group_get_query(context).filter_by(name=name).first()
if not grp:
raise exception.InstanceGroupNotFound(group_uuid=name)
return grp
@staticmethod
@db_api.api_context_manager.reader
def _get_from_db_by_instance(context, instance_uuid):
grp_member = context.session.query(api_models.InstanceGroupMember).\
filter_by(instance_uuid=instance_uuid).first()
if not grp_member:
raise exception.InstanceGroupNotFound(group_uuid='')
grp = InstanceGroup._get_from_db_by_id(context, grp_member.group_id)
return grp
@staticmethod
@db_api.api_context_manager.writer
def _save_in_db(context, group_uuid, values):
grp = _instance_group_get_query(context,
id_field=api_models.InstanceGroup.uuid,
id=group_uuid).first()
if not grp:
raise exception.InstanceGroupNotFound(group_uuid=group_uuid)
values_copy = copy.copy(values)
policies = values_copy.pop('policies', None)
members = values_copy.pop('members', None)
grp.update(values_copy)
if policies is not None:
_instance_group_policies_add(context, grp, policies)
if members is not None:
_instance_group_members_add(context, grp, members)
return grp
@staticmethod
@db_api.api_context_manager.writer
def _create_in_db(context, values, policies=None, members=None):
try:
group = api_models.InstanceGroup()
group.update(values)
group.save(context.session)
except db_exc.DBDuplicateEntry:
raise exception.InstanceGroupIdExists(group_uuid=values['uuid'])
if policies:
group._policies = _instance_group_policies_add(context, group,
policies)
else:
group._policies = []
if members:
group._members = _instance_group_members_add(context, group,
members)
else:
group._members = []
return group
@staticmethod
@db_api.api_context_manager.writer
def _destroy_in_db(context, group_uuid):
qry = _instance_group_get_query(context,
id_field=api_models.InstanceGroup.uuid,
id=group_uuid)
if qry.count() == 0:
raise exception.InstanceGroupNotFound(group_uuid=group_uuid)
# Delete policies and members
group_id = qry.first().id
instance_models = [api_models.InstanceGroupPolicy,
api_models.InstanceGroupMember]
for model in instance_models:
context.session.query(model).filter_by(group_id=group_id).delete()
qry.delete()
@staticmethod
@db_api.api_context_manager.writer
def _add_members_in_db(context, group_uuid, members):
return _instance_group_members_add_by_uuid(context, group_uuid,
members)
def obj_load_attr(self, attrname):
# NOTE(sbauza): Only hosts could be lazy-loaded right now
if attrname != 'hosts':
@ -94,27 +289,39 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
@base.remotable_classmethod
def get_by_uuid(cls, context, uuid):
db_inst = db.instance_group_get(context, uuid)
return cls._from_db_object(context, cls(), db_inst)
db_group = None
try:
db_group = cls._get_from_db_by_uuid(context, uuid)
except exception.InstanceGroupNotFound:
pass
if db_group is None:
db_group = db.instance_group_get(context, uuid)
return cls._from_db_object(context, cls(), db_group)
@base.remotable_classmethod
def get_by_name(cls, context, name):
# TODO(russellb) We need to get the group by name here. There's no
# db.api method for this yet. Come back and optimize this by
# adding a new query by name. This is unnecessarily expensive if a
# tenant has lots of groups.
igs = objects.InstanceGroupList.get_by_project_id(context,
context.project_id)
for ig in igs:
if ig.name == name:
return ig
raise exception.InstanceGroupNotFound(group_uuid=name)
try:
db_group = cls._get_from_db_by_name(context, name)
except exception.InstanceGroupNotFound:
igs = InstanceGroupList._get_main_by_project_id(context,
context.project_id)
for ig in igs:
if ig.name == name:
return ig
raise exception.InstanceGroupNotFound(group_uuid=name)
return cls._from_db_object(context, cls(), db_group)
@base.remotable_classmethod
def get_by_instance_uuid(cls, context, instance_uuid):
db_inst = db.instance_group_get_by_instance(context, instance_uuid)
return cls._from_db_object(context, cls(), db_inst)
db_group = None
try:
db_group = cls._get_from_db_by_instance(context, instance_uuid)
except exception.InstanceGroupNotFound:
pass
if db_group is None:
db_group = db.instance_group_get_by_instance(context,
instance_uuid)
return cls._from_db_object(context, cls(), db_group)
@classmethod
def get_by_hint(cls, context, hint):
@ -147,9 +354,12 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
payload = dict(updates)
payload['server_group_id'] = self.uuid
db.instance_group_update(self._context, self.uuid, updates)
db_inst = db.instance_group_get(self._context, self.uuid)
self._from_db_object(self._context, self, db_inst)
try:
db_group = self._save_in_db(self._context, self.uuid, updates)
except exception.InstanceGroupNotFound:
db.instance_group_update(self._context, self.uuid, updates)
db_group = db.instance_group_get(self._context, self.uuid)
self._from_db_object(self._context, self, db_group)
compute_utils.notify_about_server_group_update(self._context,
"update", payload)
@ -173,10 +383,21 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
policies = updates.pop('policies', None)
members = updates.pop('members', None)
db_inst = db.instance_group_create(self._context, updates,
policies=policies,
members=members)
self._from_db_object(self._context, self, db_inst)
if 'uuid' not in updates:
self.uuid = uuidutils.generate_uuid()
updates['uuid'] = self.uuid
try:
db.instance_group_get(self._context, self.uuid)
raise exception.ObjectActionError(
action='create',
reason='already created in main')
except exception.InstanceGroupNotFound:
pass
db_group = self._create_in_db(self._context, updates,
policies=policies,
members=members)
self._from_db_object(self._context, self, db_group)
payload['server_group_id'] = self.uuid
compute_utils.notify_about_server_group_update(self._context,
"create", payload)
@ -184,7 +405,10 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
@base.remotable
def destroy(self):
payload = {'server_group_id': self.uuid}
db.instance_group_delete(self._context, self.uuid)
try:
self._destroy_in_db(self._context, self.uuid)
except exception.InstanceGroupNotFound:
db.instance_group_delete(self._context, self.uuid)
self.obj_reset_changes()
compute_utils.notify_about_server_group_update(self._context,
"delete", payload)
@ -193,8 +417,13 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
def add_members(cls, context, group_uuid, instance_uuids):
payload = {'server_group_id': group_uuid,
'instance_uuids': instance_uuids}
members = db.instance_group_members_add(context, group_uuid,
instance_uuids)
try:
members = cls._add_members_in_db(context, group_uuid,
instance_uuids)
members = [member['instance_uuid'] for member in members]
except exception.InstanceGroupNotFound:
members = db.instance_group_members_add(context, group_uuid,
instance_uuids)
compute_utils.notify_about_server_group_update(context,
"addmember", payload)
return list(members)
@ -244,14 +473,32 @@ class InstanceGroupList(base.ObjectListBase, base.NovaObject):
'objects': fields.ListOfObjectsField('InstanceGroup'),
}
@staticmethod
@db_api.api_context_manager.reader
def _get_from_db(context, project_id=None):
query = _instance_group_get_query(context)
if project_id is not None:
query = query.filter_by(project_id=project_id)
return query.all()
@classmethod
def _get_main_by_project_id(cls, context, project_id):
main_db_groups = db.instance_group_get_all_by_project_id(context,
project_id)
return base.obj_make_list(context, cls(context), objects.InstanceGroup,
main_db_groups)
@base.remotable_classmethod
def get_by_project_id(cls, context, project_id):
groups = db.instance_group_get_all_by_project_id(context, project_id)
api_db_groups = cls._get_from_db(context, project_id=project_id)
main_db_groups = db.instance_group_get_all_by_project_id(context,
project_id)
return base.obj_make_list(context, cls(context), objects.InstanceGroup,
groups)
api_db_groups + main_db_groups)
@base.remotable_classmethod
def get_all(cls, context):
groups = db.instance_group_get_all(context)
api_db_groups = cls._get_from_db(context)
main_db_groups = db.instance_group_get_all(context)
return base.obj_make_list(context, cls(context), objects.InstanceGroup,
groups)
api_db_groups + main_db_groups)

View File

@ -0,0 +1,185 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_versionedobjects import fixture as ovo_fixture
from nova import context
from nova.db.sqlalchemy import api as db_api
from nova import exception
from nova import objects
from nova.objects import base
from nova import test
from nova.tests import uuidsentinel as uuids
class InstanceGroupObjectTestCase(test.TestCase):
def setUp(self):
super(InstanceGroupObjectTestCase, self).setUp()
self.context = context.RequestContext('fake-user', 'fake-project')
def _api_group(self, **values):
group = objects.InstanceGroup(context=self.context,
user_id=self.context.user_id,
project_id=self.context.project_id,
name='foogroup',
policies=['foo1', 'foo2'],
members=['memberfoo'])
group.update(values)
group.create()
return group
def _main_group(self, policies=None, members=None, **values):
vals = {
'user_id': self.context.user_id,
'project_id': self.context.project_id,
'name': 'foogroup',
}
vals.update(values)
policies = policies or ['foo1', 'foo2']
members = members or ['memberfoo']
return db_api.instance_group_create(self.context, vals,
policies=policies,
members=members)
def test_create(self):
create_group = self._api_group()
db_group = create_group._get_from_db_by_uuid(self.context,
create_group.uuid)
ovo_fixture.compare_obj(self, create_group, db_group,
allow_missing=('deleted', 'deleted_at'))
def test_create_duplicate_in_main(self):
self._main_group(uuid=uuids.main)
self.assertRaises(exception.ObjectActionError,
self._api_group, uuid=uuids.main)
def test_destroy(self):
create_group = self._api_group()
create_group.destroy()
self.assertRaises(exception.InstanceGroupNotFound,
create_group._get_from_db_by_uuid, self.context,
create_group.uuid)
def test_destroy_main(self):
db_group = self._main_group()
create_group = objects.InstanceGroup._from_db_object(
self.context, objects.InstanceGroup(), db_group)
create_group.destroy()
self.assertRaises(exception.InstanceGroupNotFound,
db_api.instance_group_get, self.context,
create_group.uuid)
@mock.patch('nova.compute.utils.notify_about_server_group_update')
def test_save(self, _mock_notify):
create_group = self._api_group()
create_group.policies = ['bar1', 'bar2']
create_group.members = ['memberbar1', 'memberbar2']
create_group.name = 'anewname'
create_group.save()
db_group = create_group._get_from_db_by_uuid(self.context,
create_group.uuid)
ovo_fixture.compare_obj(self, create_group, db_group,
allow_missing=('deleted', 'deleted_at'))
@mock.patch('nova.compute.utils.notify_about_server_group_update')
def test_save_main(self, _mock_notify):
db_group = self._main_group()
create_group = objects.InstanceGroup._from_db_object(
self.context, objects.InstanceGroup(), db_group)
create_group.policies = ['bar1', 'bar2']
create_group.members = ['memberbar1', 'memberbar2']
create_group.name = 'anewname'
create_group.save()
db_group = db_api.instance_group_get(self.context, create_group.uuid)
ovo_fixture.compare_obj(self, create_group, db_group)
def test_add_members(self):
create_group = self._api_group()
new_member = ['memberbar']
objects.InstanceGroup.add_members(self.context, create_group.uuid,
new_member)
db_group = create_group._get_from_db_by_uuid(self.context,
create_group.uuid)
self.assertEqual(create_group.members + new_member, db_group.members)
def test_add_members_main(self):
db_group = self._main_group()
create_group = objects.InstanceGroup._from_db_object(
self.context, objects.InstanceGroup(), db_group)
new_member = ['memberbar']
objects.InstanceGroup.add_members(self.context, create_group.uuid,
new_member)
db_group = db_api.instance_group_get(self.context, create_group.uuid)
self.assertEqual(create_group.members + new_member, db_group.members)
def test_add_members_to_group_with_no_members(self):
create_group = self._api_group(members=[])
new_member = ['memberbar']
objects.InstanceGroup.add_members(self.context, create_group.uuid,
new_member)
db_group = create_group._get_from_db_by_uuid(self.context,
create_group.uuid)
self.assertEqual(new_member, db_group.members)
def test_get_by_uuid(self):
create_group = self._api_group()
get_group = objects.InstanceGroup.get_by_uuid(self.context,
create_group.uuid)
self.assertTrue(base.obj_equal_prims(create_group, get_group))
def test_get_by_uuid_main(self):
db_group = self._main_group()
get_group = objects.InstanceGroup.get_by_uuid(self.context,
db_group.uuid)
ovo_fixture.compare_obj(self, get_group, db_group)
def test_get_by_name(self):
create_group = self._api_group()
get_group = objects.InstanceGroup.get_by_name(self.context,
create_group.name)
self.assertTrue(base.obj_equal_prims(create_group, get_group))
def test_get_by_name_main(self):
db_group = self._main_group()
get_group = objects.InstanceGroup.get_by_name(self.context,
db_group.name)
ovo_fixture.compare_obj(self, get_group, db_group)
def test_get_by_instance_uuid(self):
create_group = self._api_group(members=[uuids.instance])
get_group = objects.InstanceGroup.get_by_instance_uuid(self.context,
uuids.instance)
self.assertTrue(base.obj_equal_prims(create_group, get_group))
def test_get_by_instance_uuid_main(self):
db_group = self._main_group(members=[uuids.instance])
get_group = objects.InstanceGroup.get_by_instance_uuid(self.context,
uuids.instance)
ovo_fixture.compare_obj(self, get_group, db_group)
def test_get_by_project_id(self):
create_group = self._api_group()
db_group = self._main_group()
get_groups = objects.InstanceGroupList.get_by_project_id(
self.context, self.context.project_id)
self.assertEqual(2, len(get_groups))
self.assertTrue(base.obj_equal_prims(create_group, get_groups[0]))
ovo_fixture.compare_obj(self, get_groups[1], db_group)
def test_get_all(self):
create_group = self._api_group()
db_group = self._main_group()
get_groups = objects.InstanceGroupList.get_all(self.context)
self.assertEqual(2, len(get_groups))
self.assertTrue(base.obj_equal_prims(create_group, get_groups[0]))
ovo_fixture.compare_obj(self, get_groups[1], db_group)

View File

@ -45,10 +45,12 @@ _INST_GROUP_DB = {
class _TestInstanceGroupObject(object):
@mock.patch('nova.db.instance_group_get', return_value=_INST_GROUP_DB)
def test_get_by_uuid(self, mock_db_get):
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx,
@mock.patch('nova.objects.InstanceGroup._get_from_db_by_uuid',
side_effect=exception.InstanceGroupNotFound(group_uuid=_DB_UUID))
def test_get_by_uuid_main(self, mock_api_get, mock_db_get):
obj = objects.InstanceGroup.get_by_uuid(self.context,
_DB_UUID)
mock_db_get.assert_called_once_with(mock.sentinel.ctx, _DB_UUID)
mock_db_get.assert_called_once_with(self.context, _DB_UUID)
self.assertEqual(_INST_GROUP_DB['members'], obj.members)
self.assertEqual(_INST_GROUP_DB['policies'], obj.policies)
self.assertEqual(_DB_UUID, obj.uuid)
@ -58,18 +60,21 @@ class _TestInstanceGroupObject(object):
@mock.patch('nova.db.instance_group_get_by_instance',
return_value=_INST_GROUP_DB)
def test_get_by_instance_uuid(self, mock_db_get):
@mock.patch('nova.objects.InstanceGroup._get_from_db_by_instance')
def test_get_by_instance_uuid_main(self, mock_api_get, mock_db_get):
error = exception.InstanceGroupNotFound(group_uuid='')
mock_api_get.side_effect = error
objects.InstanceGroup.get_by_instance_uuid(
mock.sentinel.ctx, mock.sentinel.instance_uuid)
self.context, mock.sentinel.instance_uuid)
mock_db_get.assert_called_once_with(
mock.sentinel.ctx, mock.sentinel.instance_uuid)
self.context, mock.sentinel.instance_uuid)
@mock.patch('nova.db.instance_group_get')
def test_refresh(self, mock_db_get):
changed_group = copy.deepcopy(_INST_GROUP_DB)
changed_group['name'] = 'new_name'
mock_db_get.side_effect = [_INST_GROUP_DB, changed_group]
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx,
obj = objects.InstanceGroup.get_by_uuid(self.context,
_DB_UUID)
self.assertEqual(_INST_GROUP_DB['name'], obj.name)
obj.refresh()
@ -79,22 +84,22 @@ class _TestInstanceGroupObject(object):
@mock.patch('nova.compute.utils.notify_about_server_group_update')
@mock.patch('nova.db.instance_group_update')
@mock.patch('nova.db.instance_group_get')
def test_save(self, mock_db_get, mock_db_update, mock_notify):
def test_save_main(self, mock_db_get, mock_db_update, mock_notify):
changed_group = copy.deepcopy(_INST_GROUP_DB)
changed_group['name'] = 'new_name'
mock_db_get.side_effect = [_INST_GROUP_DB, changed_group]
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx,
obj = objects.InstanceGroup.get_by_uuid(self.context,
_DB_UUID)
self.assertEqual(obj.name, 'fake_name')
obj.name = 'new_name'
obj.policies = ['policy1'] # Remove policy 2
obj.members = ['instance_id1'] # Remove member 2
obj.save()
mock_db_update.assert_called_once_with(mock.sentinel.ctx, _DB_UUID,
mock_db_update.assert_called_once_with(self.context, _DB_UUID,
{'name': 'new_name',
'members': ['instance_id1'],
'policies': ['policy1']})
mock_notify.assert_called_once_with(mock.sentinel.ctx, "update",
mock_notify.assert_called_once_with(self.context, "update",
{'name': 'new_name',
'members': ['instance_id1'],
'policies': ['policy1'],
@ -103,10 +108,10 @@ class _TestInstanceGroupObject(object):
@mock.patch('nova.compute.utils.notify_about_server_group_update')
@mock.patch('nova.db.instance_group_update')
@mock.patch('nova.db.instance_group_get')
def test_save_without_hosts(self, mock_db_get, mock_db_update,
mock_notify):
def test_save_without_hosts_main(self, mock_db_get, mock_db_update,
mock_notify):
mock_db_get.side_effect = [_INST_GROUP_DB, _INST_GROUP_DB]
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx, _DB_UUID)
obj = objects.InstanceGroup.get_by_uuid(self.context, _DB_UUID)
obj.hosts = ['fake-host1']
self.assertRaises(exception.InstanceGroupSaveException,
obj.save)
@ -118,9 +123,10 @@ class _TestInstanceGroupObject(object):
self.assertFalse(mock_notify.called)
@mock.patch('nova.compute.utils.notify_about_server_group_update')
@mock.patch('nova.db.instance_group_create', return_value=_INST_GROUP_DB)
@mock.patch('nova.objects.InstanceGroup._create_in_db',
return_value=_INST_GROUP_DB)
def test_create(self, mock_db_create, mock_notify):
obj = objects.InstanceGroup(context=mock.sentinel.ctx)
obj = objects.InstanceGroup(context=self.context)
obj.uuid = _DB_UUID
obj.name = _INST_GROUP_DB['name']
obj.user_id = _INST_GROUP_DB['user_id']
@ -133,7 +139,7 @@ class _TestInstanceGroupObject(object):
obj.deleted = False
obj.create()
mock_db_create.assert_called_once_with(
mock.sentinel.ctx,
self.context,
{'uuid': _DB_UUID,
'name': _INST_GROUP_DB['name'],
'user_id': _INST_GROUP_DB['user_id'],
@ -146,7 +152,7 @@ class _TestInstanceGroupObject(object):
members=_INST_GROUP_DB['members'],
policies=_INST_GROUP_DB['policies'])
mock_notify.assert_called_once_with(
mock.sentinel.ctx, "create",
self.context, "create",
{'uuid': _DB_UUID,
'name': _INST_GROUP_DB['name'],
'user_id': _INST_GROUP_DB['user_id'],
@ -162,60 +168,64 @@ class _TestInstanceGroupObject(object):
self.assertRaises(exception.ObjectActionError, obj.create)
@mock.patch('nova.compute.utils.notify_about_server_group_update')
@mock.patch('nova.db.instance_group_delete')
@mock.patch('nova.objects.InstanceGroup._destroy_in_db')
def test_destroy(self, mock_db_delete, mock_notify):
obj = objects.InstanceGroup(context=mock.sentinel.ctx)
obj = objects.InstanceGroup(context=self.context)
obj.uuid = _DB_UUID
obj.destroy()
mock_db_delete.assert_called_once_with(mock.sentinel.ctx, _DB_UUID)
mock_notify.assert_called_once_with(mock.sentinel.ctx, "delete",
mock_db_delete.assert_called_once_with(self.context, _DB_UUID)
mock_notify.assert_called_once_with(self.context, "delete",
{'server_group_id': _DB_UUID})
@mock.patch('nova.compute.utils.notify_about_server_group_update')
@mock.patch('nova.db.instance_group_members_add')
@mock.patch('nova.objects.InstanceGroup._add_members_in_db')
def test_add_members(self, mock_members_add_db, mock_notify):
mock_members_add_db.return_value = [mock.sentinel.members]
members = objects.InstanceGroup.add_members(mock.sentinel.ctx,
fake_member_models = [{'instance_uuid': mock.sentinel.uuid}]
fake_member_uuids = [mock.sentinel.uuid]
mock_members_add_db.return_value = fake_member_models
members = objects.InstanceGroup.add_members(self.context,
_DB_UUID,
mock.sentinel.members)
self.assertEqual([mock.sentinel.members], members)
fake_member_uuids)
self.assertEqual(fake_member_uuids, members)
mock_members_add_db.assert_called_once_with(
mock.sentinel.ctx,
self.context,
_DB_UUID,
mock.sentinel.members)
fake_member_uuids)
mock_notify.assert_called_once_with(
mock.sentinel.ctx, "addmember",
{'instance_uuids': mock.sentinel.members,
self.context, "addmember",
{'instance_uuids': fake_member_uuids,
'server_group_id': _DB_UUID})
@mock.patch('nova.objects.InstanceList.get_by_filters')
@mock.patch('nova.db.instance_group_get', return_value=_INST_GROUP_DB)
@mock.patch('nova.objects.InstanceGroup._get_from_db_by_uuid',
return_value=_INST_GROUP_DB)
def test_count_members_by_user(self, mock_get_db, mock_il_get):
mock_il_get.return_value = [mock.ANY]
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx, _DB_UUID)
obj = objects.InstanceGroup.get_by_uuid(self.context, _DB_UUID)
expected_filters = {
'uuid': ['instance_id1', 'instance_id2'],
'user_id': 'fake_user',
'deleted': False
}
self.assertEqual(1, obj.count_members_by_user('fake_user'))
mock_il_get.assert_called_once_with(mock.sentinel.ctx,
mock_il_get.assert_called_once_with(self.context,
filters=expected_filters)
@mock.patch('nova.objects.InstanceList.get_by_filters')
@mock.patch('nova.db.instance_group_get', return_value=_INST_GROUP_DB)
@mock.patch('nova.objects.InstanceGroup._get_from_db_by_uuid',
return_value=_INST_GROUP_DB)
def test_get_hosts(self, mock_get_db, mock_il_get):
mock_il_get.return_value = [objects.Instance(host='host1'),
objects.Instance(host='host2'),
objects.Instance(host=None)]
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx, _DB_UUID)
obj = objects.InstanceGroup.get_by_uuid(self.context, _DB_UUID)
hosts = obj.get_hosts()
self.assertEqual(['instance_id1', 'instance_id2'], obj.members)
expected_filters = {
'uuid': ['instance_id1', 'instance_id2'],
'deleted': False
}
mock_il_get.assert_called_once_with(mock.sentinel.ctx,
mock_il_get.assert_called_once_with(self.context,
filters=expected_filters)
self.assertEqual(2, len(hosts))
self.assertIn('host1', hosts)
@ -228,12 +238,11 @@ class _TestInstanceGroupObject(object):
'uuid': set(['instance_id2']),
'deleted': False
}
mock_il_get.assert_called_once_with(mock.sentinel.ctx,
mock_il_get.assert_called_once_with(self.context,
filters=expected_filters)
@mock.patch('nova.db.instance_group_get', return_value=_INST_GROUP_DB)
def test_obj_make_compatible(self, mock_db_get):
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx, _DB_UUID)
def test_obj_make_compatible(self):
obj = objects.InstanceGroup(self.context, **_INST_GROUP_DB)
obj_primitive = obj.obj_to_primitive()
self.assertNotIn('metadetails', obj_primitive)
obj.obj_make_compatible(obj_primitive, '1.6')
@ -244,14 +253,14 @@ class _TestInstanceGroupObject(object):
mock_get_by_filt.return_value = [objects.Instance(host='host1'),
objects.Instance(host='host2')]
obj = objects.InstanceGroup(mock.sentinel.ctx, members=['uuid1'])
obj = objects.InstanceGroup(self.context, members=['uuid1'])
self.assertEqual(2, len(obj.hosts))
self.assertIn('host1', obj.hosts)
self.assertIn('host2', obj.hosts)
self.assertNotIn('hosts', obj.obj_what_changed())
def test_load_anything_else_but_hosts(self):
obj = objects.InstanceGroup(mock.sentinel.ctx)
obj = objects.InstanceGroup(self.context)
self.assertRaises(exception.ObjectActionError, getattr, obj, 'members')
@ -283,22 +292,29 @@ def _mock_db_list_get(*args):
class _TestInstanceGroupListObject(object):
@mock.patch('nova.db.instance_group_get_all')
def test_list_all(self, mock_db_get):
@mock.patch('nova.objects.InstanceGroupList._get_from_db')
def test_list_all_main(self, mock_api_get, mock_db_get):
mock_api_get.return_value = []
mock_db_get.side_effect = _mock_db_list_get
inst_list = objects.InstanceGroupList.get_all(mock.sentinel.ctx)
inst_list = objects.InstanceGroupList.get_all(self.context)
self.assertEqual(4, len(inst_list.objects))
mock_db_get.assert_called_once_with(mock.sentinel.ctx)
mock_db_get.assert_called_once_with(self.context)
@mock.patch('nova.db.instance_group_get_all_by_project_id')
def test_list_by_project_id(self, mock_db_get):
@mock.patch('nova.objects.InstanceGroupList._get_from_db')
def test_list_by_project_id_main(self, mock_api_get, mock_db_get):
mock_api_get.return_value = []
mock_db_get.side_effect = _mock_db_list_get
objects.InstanceGroupList.get_by_project_id(
mock.sentinel.ctx, mock.sentinel.project_id)
self.context, mock.sentinel.project_id)
mock_db_get.assert_called_once_with(
mock.sentinel.ctx, mock.sentinel.project_id)
self.context, mock.sentinel.project_id)
@mock.patch('nova.db.instance_group_get_all_by_project_id')
def test_get_by_name(self, mock_db_get):
@mock.patch('nova.objects.InstanceGroup._get_from_db_by_name')
def test_get_by_name_main(self, mock_api_get, mock_db_get):
error = exception.InstanceGroupNotFound(group_uuid='f1')
mock_api_get.side_effect = error
mock_db_get.side_effect = _mock_db_list_get
# Need the project_id value set, otherwise we'd use mock.sentinel
mock_ctx = mock.MagicMock()
@ -313,10 +329,10 @@ class _TestInstanceGroupListObject(object):
@mock.patch('nova.objects.InstanceGroup.get_by_uuid')
@mock.patch('nova.objects.InstanceGroup.get_by_name')
def test_get_by_hint(self, mock_name, mock_uuid):
objects.InstanceGroup.get_by_hint(mock.sentinel.ctx, _DB_UUID)
mock_uuid.assert_called_once_with(mock.sentinel.ctx, _DB_UUID)
objects.InstanceGroup.get_by_hint(mock.sentinel.ctx, 'name')
mock_name.assert_called_once_with(mock.sentinel.ctx, 'name')
objects.InstanceGroup.get_by_hint(self.context, _DB_UUID)
mock_uuid.assert_called_once_with(self.context, _DB_UUID)
objects.InstanceGroup.get_by_hint(self.context, 'name')
mock_name.assert_called_once_with(self.context, 'name')
class TestInstanceGroupListObject(test_objects._LocalTest,