Merge "Make InstanceGroup object favor the API database"

This commit is contained in:
Jenkins 2016-08-20 20:06:22 +00:00 committed by Gerrit Code Review
commit 419ae224a9
3 changed files with 533 additions and 85 deletions

View File

@ -12,11 +12,18 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
from oslo_db import exception as db_exc
from oslo_utils import uuidutils from oslo_utils import uuidutils
from oslo_utils import versionutils 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.compute import utils as compute_utils
from nova import db 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 exception
from nova import objects from nova import objects
from nova.objects import base from nova.objects import base
@ -26,6 +33,79 @@ from nova.objects import fields
LAZY_LOAD_FIELDS = ['hosts'] 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 # TODO(berrange): Remove NovaObjectDictCompat
@base.NovaObjectRegistry.register @base.NovaObjectRegistry.register
class InstanceGroup(base.NovaPersistentObject, base.NovaObject, class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
@ -74,8 +154,15 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
for field in instance_group.fields: for field in instance_group.fields:
if field in LAZY_LOAD_FIELDS: if field in LAZY_LOAD_FIELDS:
continue continue
if field == 'deleted': # This is needed to handle db models from both the api
instance_group.deleted = db_inst['deleted'] == db_inst['id'] # 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: else:
instance_group[field] = db_inst[field] instance_group[field] = db_inst[field]
@ -83,6 +170,114 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
instance_group.obj_reset_changes() instance_group.obj_reset_changes()
return instance_group 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): def obj_load_attr(self, attrname):
# NOTE(sbauza): Only hosts could be lazy-loaded right now # NOTE(sbauza): Only hosts could be lazy-loaded right now
if attrname != 'hosts': if attrname != 'hosts':
@ -94,27 +289,39 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
@base.remotable_classmethod @base.remotable_classmethod
def get_by_uuid(cls, context, uuid): def get_by_uuid(cls, context, uuid):
db_inst = db.instance_group_get(context, uuid) db_group = None
return cls._from_db_object(context, cls(), db_inst) 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 @base.remotable_classmethod
def get_by_name(cls, context, name): def get_by_name(cls, context, name):
# TODO(russellb) We need to get the group by name here. There's no try:
# db.api method for this yet. Come back and optimize this by db_group = cls._get_from_db_by_name(context, name)
# adding a new query by name. This is unnecessarily expensive if a except exception.InstanceGroupNotFound:
# tenant has lots of groups. igs = InstanceGroupList._get_main_by_project_id(context,
igs = objects.InstanceGroupList.get_by_project_id(context,
context.project_id) context.project_id)
for ig in igs: for ig in igs:
if ig.name == name: if ig.name == name:
return ig return ig
raise exception.InstanceGroupNotFound(group_uuid=name) raise exception.InstanceGroupNotFound(group_uuid=name)
return cls._from_db_object(context, cls(), db_group)
@base.remotable_classmethod @base.remotable_classmethod
def get_by_instance_uuid(cls, context, instance_uuid): def get_by_instance_uuid(cls, context, instance_uuid):
db_inst = db.instance_group_get_by_instance(context, instance_uuid) db_group = None
return cls._from_db_object(context, cls(), db_inst) 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 @classmethod
def get_by_hint(cls, context, hint): def get_by_hint(cls, context, hint):
@ -147,9 +354,12 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
payload = dict(updates) payload = dict(updates)
payload['server_group_id'] = self.uuid payload['server_group_id'] = self.uuid
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.instance_group_update(self._context, self.uuid, updates)
db_inst = db.instance_group_get(self._context, self.uuid) db_group = db.instance_group_get(self._context, self.uuid)
self._from_db_object(self._context, self, db_inst) self._from_db_object(self._context, self, db_group)
compute_utils.notify_about_server_group_update(self._context, compute_utils.notify_about_server_group_update(self._context,
"update", payload) "update", payload)
@ -173,10 +383,21 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
policies = updates.pop('policies', None) policies = updates.pop('policies', None)
members = updates.pop('members', None) members = updates.pop('members', None)
db_inst = db.instance_group_create(self._context, updates, 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, policies=policies,
members=members) members=members)
self._from_db_object(self._context, self, db_inst) self._from_db_object(self._context, self, db_group)
payload['server_group_id'] = self.uuid payload['server_group_id'] = self.uuid
compute_utils.notify_about_server_group_update(self._context, compute_utils.notify_about_server_group_update(self._context,
"create", payload) "create", payload)
@ -184,6 +405,9 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
@base.remotable @base.remotable
def destroy(self): def destroy(self):
payload = {'server_group_id': self.uuid} payload = {'server_group_id': self.uuid}
try:
self._destroy_in_db(self._context, self.uuid)
except exception.InstanceGroupNotFound:
db.instance_group_delete(self._context, self.uuid) db.instance_group_delete(self._context, self.uuid)
self.obj_reset_changes() self.obj_reset_changes()
compute_utils.notify_about_server_group_update(self._context, compute_utils.notify_about_server_group_update(self._context,
@ -193,6 +417,11 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
def add_members(cls, context, group_uuid, instance_uuids): def add_members(cls, context, group_uuid, instance_uuids):
payload = {'server_group_id': group_uuid, payload = {'server_group_id': group_uuid,
'instance_uuids': instance_uuids} 'instance_uuids': 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, members = db.instance_group_members_add(context, group_uuid,
instance_uuids) instance_uuids)
compute_utils.notify_about_server_group_update(context, compute_utils.notify_about_server_group_update(context,
@ -244,14 +473,32 @@ class InstanceGroupList(base.ObjectListBase, base.NovaObject):
'objects': fields.ListOfObjectsField('InstanceGroup'), '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 @base.remotable_classmethod
def get_by_project_id(cls, context, project_id): 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, return base.obj_make_list(context, cls(context), objects.InstanceGroup,
groups) api_db_groups + main_db_groups)
@base.remotable_classmethod @base.remotable_classmethod
def get_all(cls, context): 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, 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): class _TestInstanceGroupObject(object):
@mock.patch('nova.db.instance_group_get', return_value=_INST_GROUP_DB) @mock.patch('nova.db.instance_group_get', return_value=_INST_GROUP_DB)
def test_get_by_uuid(self, mock_db_get): @mock.patch('nova.objects.InstanceGroup._get_from_db_by_uuid',
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx, 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) _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['members'], obj.members)
self.assertEqual(_INST_GROUP_DB['policies'], obj.policies) self.assertEqual(_INST_GROUP_DB['policies'], obj.policies)
self.assertEqual(_DB_UUID, obj.uuid) self.assertEqual(_DB_UUID, obj.uuid)
@ -58,18 +60,21 @@ class _TestInstanceGroupObject(object):
@mock.patch('nova.db.instance_group_get_by_instance', @mock.patch('nova.db.instance_group_get_by_instance',
return_value=_INST_GROUP_DB) 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( 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_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') @mock.patch('nova.db.instance_group_get')
def test_refresh(self, mock_db_get): def test_refresh(self, mock_db_get):
changed_group = copy.deepcopy(_INST_GROUP_DB) changed_group = copy.deepcopy(_INST_GROUP_DB)
changed_group['name'] = 'new_name' changed_group['name'] = 'new_name'
mock_db_get.side_effect = [_INST_GROUP_DB, changed_group] 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) _DB_UUID)
self.assertEqual(_INST_GROUP_DB['name'], obj.name) self.assertEqual(_INST_GROUP_DB['name'], obj.name)
obj.refresh() obj.refresh()
@ -79,22 +84,22 @@ class _TestInstanceGroupObject(object):
@mock.patch('nova.compute.utils.notify_about_server_group_update') @mock.patch('nova.compute.utils.notify_about_server_group_update')
@mock.patch('nova.db.instance_group_update') @mock.patch('nova.db.instance_group_update')
@mock.patch('nova.db.instance_group_get') @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 = copy.deepcopy(_INST_GROUP_DB)
changed_group['name'] = 'new_name' changed_group['name'] = 'new_name'
mock_db_get.side_effect = [_INST_GROUP_DB, changed_group] 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) _DB_UUID)
self.assertEqual(obj.name, 'fake_name') self.assertEqual(obj.name, 'fake_name')
obj.name = 'new_name' obj.name = 'new_name'
obj.policies = ['policy1'] # Remove policy 2 obj.policies = ['policy1'] # Remove policy 2
obj.members = ['instance_id1'] # Remove member 2 obj.members = ['instance_id1'] # Remove member 2
obj.save() 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', {'name': 'new_name',
'members': ['instance_id1'], 'members': ['instance_id1'],
'policies': ['policy1']}) 'policies': ['policy1']})
mock_notify.assert_called_once_with(mock.sentinel.ctx, "update", mock_notify.assert_called_once_with(self.context, "update",
{'name': 'new_name', {'name': 'new_name',
'members': ['instance_id1'], 'members': ['instance_id1'],
'policies': ['policy1'], 'policies': ['policy1'],
@ -103,10 +108,10 @@ class _TestInstanceGroupObject(object):
@mock.patch('nova.compute.utils.notify_about_server_group_update') @mock.patch('nova.compute.utils.notify_about_server_group_update')
@mock.patch('nova.db.instance_group_update') @mock.patch('nova.db.instance_group_update')
@mock.patch('nova.db.instance_group_get') @mock.patch('nova.db.instance_group_get')
def test_save_without_hosts(self, mock_db_get, mock_db_update, def test_save_without_hosts_main(self, mock_db_get, mock_db_update,
mock_notify): mock_notify):
mock_db_get.side_effect = [_INST_GROUP_DB, _INST_GROUP_DB] 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'] obj.hosts = ['fake-host1']
self.assertRaises(exception.InstanceGroupSaveException, self.assertRaises(exception.InstanceGroupSaveException,
obj.save) obj.save)
@ -118,9 +123,10 @@ class _TestInstanceGroupObject(object):
self.assertFalse(mock_notify.called) self.assertFalse(mock_notify.called)
@mock.patch('nova.compute.utils.notify_about_server_group_update') @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): 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.uuid = _DB_UUID
obj.name = _INST_GROUP_DB['name'] obj.name = _INST_GROUP_DB['name']
obj.user_id = _INST_GROUP_DB['user_id'] obj.user_id = _INST_GROUP_DB['user_id']
@ -133,7 +139,7 @@ class _TestInstanceGroupObject(object):
obj.deleted = False obj.deleted = False
obj.create() obj.create()
mock_db_create.assert_called_once_with( mock_db_create.assert_called_once_with(
mock.sentinel.ctx, self.context,
{'uuid': _DB_UUID, {'uuid': _DB_UUID,
'name': _INST_GROUP_DB['name'], 'name': _INST_GROUP_DB['name'],
'user_id': _INST_GROUP_DB['user_id'], 'user_id': _INST_GROUP_DB['user_id'],
@ -146,7 +152,7 @@ class _TestInstanceGroupObject(object):
members=_INST_GROUP_DB['members'], members=_INST_GROUP_DB['members'],
policies=_INST_GROUP_DB['policies']) policies=_INST_GROUP_DB['policies'])
mock_notify.assert_called_once_with( mock_notify.assert_called_once_with(
mock.sentinel.ctx, "create", self.context, "create",
{'uuid': _DB_UUID, {'uuid': _DB_UUID,
'name': _INST_GROUP_DB['name'], 'name': _INST_GROUP_DB['name'],
'user_id': _INST_GROUP_DB['user_id'], 'user_id': _INST_GROUP_DB['user_id'],
@ -162,60 +168,64 @@ class _TestInstanceGroupObject(object):
self.assertRaises(exception.ObjectActionError, obj.create) self.assertRaises(exception.ObjectActionError, obj.create)
@mock.patch('nova.compute.utils.notify_about_server_group_update') @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): 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.uuid = _DB_UUID
obj.destroy() obj.destroy()
mock_db_delete.assert_called_once_with(mock.sentinel.ctx, _DB_UUID) mock_db_delete.assert_called_once_with(self.context, _DB_UUID)
mock_notify.assert_called_once_with(mock.sentinel.ctx, "delete", mock_notify.assert_called_once_with(self.context, "delete",
{'server_group_id': _DB_UUID}) {'server_group_id': _DB_UUID})
@mock.patch('nova.compute.utils.notify_about_server_group_update') @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): def test_add_members(self, mock_members_add_db, mock_notify):
mock_members_add_db.return_value = [mock.sentinel.members] fake_member_models = [{'instance_uuid': mock.sentinel.uuid}]
members = objects.InstanceGroup.add_members(mock.sentinel.ctx, fake_member_uuids = [mock.sentinel.uuid]
mock_members_add_db.return_value = fake_member_models
members = objects.InstanceGroup.add_members(self.context,
_DB_UUID, _DB_UUID,
mock.sentinel.members) fake_member_uuids)
self.assertEqual([mock.sentinel.members], members) self.assertEqual(fake_member_uuids, members)
mock_members_add_db.assert_called_once_with( mock_members_add_db.assert_called_once_with(
mock.sentinel.ctx, self.context,
_DB_UUID, _DB_UUID,
mock.sentinel.members) fake_member_uuids)
mock_notify.assert_called_once_with( mock_notify.assert_called_once_with(
mock.sentinel.ctx, "addmember", self.context, "addmember",
{'instance_uuids': mock.sentinel.members, {'instance_uuids': fake_member_uuids,
'server_group_id': _DB_UUID}) 'server_group_id': _DB_UUID})
@mock.patch('nova.objects.InstanceList.get_by_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_count_members_by_user(self, mock_get_db, mock_il_get): def test_count_members_by_user(self, mock_get_db, mock_il_get):
mock_il_get.return_value = [mock.ANY] 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 = { expected_filters = {
'uuid': ['instance_id1', 'instance_id2'], 'uuid': ['instance_id1', 'instance_id2'],
'user_id': 'fake_user', 'user_id': 'fake_user',
'deleted': False 'deleted': False
} }
self.assertEqual(1, obj.count_members_by_user('fake_user')) 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) filters=expected_filters)
@mock.patch('nova.objects.InstanceList.get_by_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): def test_get_hosts(self, mock_get_db, mock_il_get):
mock_il_get.return_value = [objects.Instance(host='host1'), mock_il_get.return_value = [objects.Instance(host='host1'),
objects.Instance(host='host2'), objects.Instance(host='host2'),
objects.Instance(host=None)] 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() hosts = obj.get_hosts()
self.assertEqual(['instance_id1', 'instance_id2'], obj.members) self.assertEqual(['instance_id1', 'instance_id2'], obj.members)
expected_filters = { expected_filters = {
'uuid': ['instance_id1', 'instance_id2'], 'uuid': ['instance_id1', 'instance_id2'],
'deleted': False 'deleted': False
} }
mock_il_get.assert_called_once_with(mock.sentinel.ctx, mock_il_get.assert_called_once_with(self.context,
filters=expected_filters) filters=expected_filters)
self.assertEqual(2, len(hosts)) self.assertEqual(2, len(hosts))
self.assertIn('host1', hosts) self.assertIn('host1', hosts)
@ -228,12 +238,11 @@ class _TestInstanceGroupObject(object):
'uuid': set(['instance_id2']), 'uuid': set(['instance_id2']),
'deleted': False 'deleted': False
} }
mock_il_get.assert_called_once_with(mock.sentinel.ctx, mock_il_get.assert_called_once_with(self.context,
filters=expected_filters) filters=expected_filters)
@mock.patch('nova.db.instance_group_get', return_value=_INST_GROUP_DB) def test_obj_make_compatible(self):
def test_obj_make_compatible(self, mock_db_get): obj = objects.InstanceGroup(self.context, **_INST_GROUP_DB)
obj = objects.InstanceGroup.get_by_uuid(mock.sentinel.ctx, _DB_UUID)
obj_primitive = obj.obj_to_primitive() obj_primitive = obj.obj_to_primitive()
self.assertNotIn('metadetails', obj_primitive) self.assertNotIn('metadetails', obj_primitive)
obj.obj_make_compatible(obj_primitive, '1.6') 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'), mock_get_by_filt.return_value = [objects.Instance(host='host1'),
objects.Instance(host='host2')] 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.assertEqual(2, len(obj.hosts))
self.assertIn('host1', obj.hosts) self.assertIn('host1', obj.hosts)
self.assertIn('host2', obj.hosts) self.assertIn('host2', obj.hosts)
self.assertNotIn('hosts', obj.obj_what_changed()) self.assertNotIn('hosts', obj.obj_what_changed())
def test_load_anything_else_but_hosts(self): 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') self.assertRaises(exception.ObjectActionError, getattr, obj, 'members')
@ -283,22 +292,29 @@ def _mock_db_list_get(*args):
class _TestInstanceGroupListObject(object): class _TestInstanceGroupListObject(object):
@mock.patch('nova.db.instance_group_get_all') @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 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)) 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') @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 mock_db_get.side_effect = _mock_db_list_get
objects.InstanceGroupList.get_by_project_id( 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_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') @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 mock_db_get.side_effect = _mock_db_list_get
# Need the project_id value set, otherwise we'd use mock.sentinel # Need the project_id value set, otherwise we'd use mock.sentinel
mock_ctx = mock.MagicMock() 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_uuid')
@mock.patch('nova.objects.InstanceGroup.get_by_name') @mock.patch('nova.objects.InstanceGroup.get_by_name')
def test_get_by_hint(self, mock_name, mock_uuid): def test_get_by_hint(self, mock_name, mock_uuid):
objects.InstanceGroup.get_by_hint(mock.sentinel.ctx, _DB_UUID) objects.InstanceGroup.get_by_hint(self.context, _DB_UUID)
mock_uuid.assert_called_once_with(mock.sentinel.ctx, _DB_UUID) mock_uuid.assert_called_once_with(self.context, _DB_UUID)
objects.InstanceGroup.get_by_hint(mock.sentinel.ctx, 'name') objects.InstanceGroup.get_by_hint(self.context, 'name')
mock_name.assert_called_once_with(mock.sentinel.ctx, 'name') mock_name.assert_called_once_with(self.context, 'name')
class TestInstanceGroupListObject(test_objects._LocalTest, class TestInstanceGroupListObject(test_objects._LocalTest,