Merge "Make InstanceGroup object favor the API database"
This commit is contained in:
commit
419ae224a9
@ -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,
|
||||
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
|
||||
|
||||
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_inst = db.instance_group_get(self._context, self.uuid)
|
||||
self._from_db_object(self._context, self, db_inst)
|
||||
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,
|
||||
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_inst)
|
||||
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,6 +405,9 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
|
||||
@base.remotable
|
||||
def destroy(self):
|
||||
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)
|
||||
self.obj_reset_changes()
|
||||
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):
|
||||
payload = {'server_group_id': group_uuid,
|
||||
'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,
|
||||
instance_uuids)
|
||||
compute_utils.notify_about_server_group_update(context,
|
||||
@ -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)
|
||||
|
185
nova/tests/functional/db/test_instance_group.py
Normal file
185
nova/tests/functional/db/test_instance_group.py
Normal 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)
|
@ -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,
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user