cinder/cinder/tests/unit/objects/test_consistencygroup.py
xing-yang 702868fd00 Remove cgsnapshot_id before snapshot.save
There are a few more places where information is missing for
consistencygroup or cgsnapshot due to the CG migration changes.
This patch tries to fix them.

cgsnapshot_id should be removed from the snapshot object before
snapshot.save is called in the exception block because cgsnapshot
does not exist in the db.

cgsnapshot object is added to the snapshot object when snapshots
are passed to the driver in create/delete cgsnapshot.

Change-Id: I253a98b8a2058a00c2c577927c0b701c2f88c81b
Closes-Bug: #1662684
2016-12-03 16:23:43 -05:00

310 lines
14 KiB
Python

# Copyright 2015 Yahoo Inc.
#
# 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_utils import timeutils
import pytz
import six
from cinder import exception
from cinder import objects
from cinder.objects import fields
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import fake_volume
from cinder.tests.unit import objects as test_objects
fake_consistencygroup = {
'id': fake.CONSISTENCY_GROUP_ID,
'user_id': fake.USER_ID,
'project_id': fake.PROJECT_ID,
'host': 'fake_host',
'availability_zone': 'fake_az',
'name': 'fake_name',
'description': 'fake_description',
'volume_type_id': fake.VOLUME_TYPE_ID,
'status': fields.ConsistencyGroupStatus.CREATING,
'cgsnapshot_id': fake.CGSNAPSHOT_ID,
'source_cgid': None,
}
fake_cgsnapshot = {
'id': fake.CGSNAPSHOT_ID,
'user_id': fake.USER_ID,
'project_id': fake.PROJECT_ID,
'name': 'fake_name',
'description': 'fake_description',
'status': 'creating',
'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
}
fake_group = {
'id': fake.GROUP_ID,
'user_id': fake.USER_ID,
'project_id': fake.PROJECT_ID,
'host': 'fake_host',
'availability_zone': 'fake_az',
'name': 'fake_name',
'description': 'fake_description',
'group_type_id': fake.GROUP_TYPE_ID,
'status': fields.GroupStatus.CREATING,
}
class TestConsistencyGroup(test_objects.BaseObjectsTestCase):
@mock.patch('cinder.db.sqlalchemy.api.consistencygroup_get',
return_value=fake_consistencygroup)
def test_get_by_id(self, consistencygroup_get):
consistencygroup = objects.ConsistencyGroup.get_by_id(
self.context, fake.CONSISTENCY_GROUP_ID)
self._compare(self, fake_consistencygroup, consistencygroup)
consistencygroup_get.assert_called_once_with(
self.context, fake.CONSISTENCY_GROUP_ID)
@mock.patch('cinder.db.sqlalchemy.api.model_query')
def test_get_by_id_no_existing_id(self, model_query):
model_query().filter_by().first.return_value = None
self.assertRaises(exception.ConsistencyGroupNotFound,
objects.ConsistencyGroup.get_by_id, self.context,
123)
@mock.patch('cinder.db.consistencygroup_create',
return_value=fake_consistencygroup)
def test_create(self, consistencygroup_create):
fake_cg = fake_consistencygroup.copy()
del fake_cg['id']
consistencygroup = objects.ConsistencyGroup(context=self.context,
**fake_cg)
consistencygroup.create()
self._compare(self, fake_consistencygroup, consistencygroup)
@mock.patch('cinder.db.group_create',
return_value=fake_group)
def test_create_from_group(self, group_create):
fake_grp = fake_group.copy()
del fake_grp['id']
group = objects.Group(context=self.context,
**fake_grp)
group.create()
volumes_objs = [objects.Volume(context=self.context, id=i)
for i in [fake.VOLUME_ID, fake.VOLUME2_ID,
fake.VOLUME3_ID]]
volumes = objects.VolumeList(objects=volumes_objs)
group.volumes = volumes
consistencygroup = objects.ConsistencyGroup()
consistencygroup.from_group(group)
self.assertEqual(group.id, consistencygroup.id)
self.assertEqual(group.name, consistencygroup.name)
def test_create_with_id_except_exception(self, ):
consistencygroup = objects.ConsistencyGroup(
context=self.context, **{'id': fake.CONSISTENCY_GROUP_ID})
self.assertRaises(exception.ObjectActionError, consistencygroup.create)
@mock.patch('cinder.db.consistencygroup_update')
def test_save(self, consistencygroup_update):
consistencygroup = objects.ConsistencyGroup._from_db_object(
self.context, objects.ConsistencyGroup(), fake_consistencygroup)
consistencygroup.status = fields.ConsistencyGroupStatus.AVAILABLE
consistencygroup.save()
consistencygroup_update.assert_called_once_with(
self.context,
consistencygroup.id,
{'status': fields.ConsistencyGroupStatus.AVAILABLE})
def test_save_with_cgsnapshots(self):
consistencygroup = objects.ConsistencyGroup._from_db_object(
self.context, objects.ConsistencyGroup(), fake_consistencygroup)
cgsnapshots_objs = [objects.CGSnapshot(context=self.context, id=i)
for i in [fake.CGSNAPSHOT_ID, fake.CGSNAPSHOT2_ID,
fake.CGSNAPSHOT3_ID]]
cgsnapshots = objects.CGSnapshotList(objects=cgsnapshots_objs)
consistencygroup.name = 'foobar'
consistencygroup.cgsnapshots = cgsnapshots
self.assertEqual({'name': 'foobar',
'cgsnapshots': cgsnapshots},
consistencygroup.obj_get_changes())
self.assertRaises(exception.ObjectActionError, consistencygroup.save)
def test_save_with_volumes(self):
consistencygroup = objects.ConsistencyGroup._from_db_object(
self.context, objects.ConsistencyGroup(), fake_consistencygroup)
volumes_objs = [objects.Volume(context=self.context, id=i)
for i in [fake.VOLUME_ID, fake.VOLUME2_ID,
fake.VOLUME3_ID]]
volumes = objects.VolumeList(objects=volumes_objs)
consistencygroup.name = 'foobar'
consistencygroup.volumes = volumes
self.assertEqual({'name': 'foobar',
'volumes': volumes},
consistencygroup.obj_get_changes())
self.assertRaises(exception.ObjectActionError, consistencygroup.save)
@mock.patch('cinder.objects.cgsnapshot.CGSnapshotList.get_all_by_group')
@mock.patch('cinder.objects.volume.VolumeList.get_all_by_group')
def test_obj_load_attr(self, mock_vol_get_all_by_group,
mock_cgsnap_get_all_by_group):
consistencygroup = objects.ConsistencyGroup._from_db_object(
self.context, objects.ConsistencyGroup(), fake_consistencygroup)
# Test cgsnapshots lazy-loaded field
cgsnapshots_objs = [objects.CGSnapshot(context=self.context, id=i)
for i in [fake.CGSNAPSHOT_ID, fake.CGSNAPSHOT2_ID,
fake.CGSNAPSHOT3_ID]]
cgsnapshots = objects.CGSnapshotList(context=self.context,
objects=cgsnapshots_objs)
mock_cgsnap_get_all_by_group.return_value = cgsnapshots
self.assertEqual(cgsnapshots, consistencygroup.cgsnapshots)
mock_cgsnap_get_all_by_group.assert_called_once_with(
self.context, consistencygroup.id)
# Test volumes lazy-loaded field
volume_objs = [objects.Volume(context=self.context, id=i)
for i in [fake.VOLUME_ID, fake.VOLUME2_ID,
fake.VOLUME3_ID]]
volumes = objects.VolumeList(context=self.context, objects=volume_objs)
mock_vol_get_all_by_group.return_value = volumes
self.assertEqual(volumes, consistencygroup.volumes)
mock_vol_get_all_by_group.assert_called_once_with(self.context,
consistencygroup.id)
@mock.patch('oslo_utils.timeutils.utcnow', return_value=timeutils.utcnow())
@mock.patch('cinder.db.sqlalchemy.api.consistencygroup_destroy')
def test_destroy(self, consistencygroup_destroy, utcnow_mock):
consistencygroup_destroy.return_value = {
'status': fields.ConsistencyGroupStatus.DELETED,
'deleted': True,
'deleted_at': utcnow_mock.return_value}
consistencygroup = objects.ConsistencyGroup(
context=self.context, id=fake.CONSISTENCY_GROUP_ID)
consistencygroup.destroy()
self.assertTrue(consistencygroup_destroy.called)
admin_context = consistencygroup_destroy.call_args[0][0]
self.assertTrue(admin_context.is_admin)
self.assertTrue(consistencygroup.deleted)
self.assertEqual(fields.ConsistencyGroupStatus.DELETED,
consistencygroup.status)
self.assertEqual(utcnow_mock.return_value.replace(tzinfo=pytz.UTC),
consistencygroup.deleted_at)
@mock.patch('cinder.db.sqlalchemy.api.consistencygroup_get')
def test_refresh(self, consistencygroup_get):
db_cg1 = fake_consistencygroup.copy()
db_cg2 = db_cg1.copy()
db_cg2['description'] = 'foobar'
# On the second consistencygroup_get, return the ConsistencyGroup with
# an updated description
consistencygroup_get.side_effect = [db_cg1, db_cg2]
cg = objects.ConsistencyGroup.get_by_id(self.context,
fake.CONSISTENCY_GROUP_ID)
self._compare(self, db_cg1, cg)
# description was updated, so a ConsistencyGroup refresh should have a
# new value for that field
cg.refresh()
self._compare(self, db_cg2, cg)
if six.PY3:
call_bool = mock.call.__bool__()
else:
call_bool = mock.call.__nonzero__()
consistencygroup_get.assert_has_calls([
mock.call(
self.context,
fake.CONSISTENCY_GROUP_ID),
call_bool,
mock.call(
self.context,
fake.CONSISTENCY_GROUP_ID)])
def test_from_db_object_with_all_expected_attributes(self):
expected_attrs = ['volumes', 'cgsnapshots']
db_volumes = [fake_volume.fake_db_volume(admin_metadata={},
volume_metadata={})]
db_cgsnaps = [fake_cgsnapshot.copy()]
db_cg = fake_consistencygroup.copy()
db_cg['volumes'] = db_volumes
db_cg['cgsnapshots'] = db_cgsnaps
cg = objects.ConsistencyGroup._from_db_object(
self.context, objects.ConsistencyGroup(), db_cg, expected_attrs)
self.assertEqual(len(db_volumes), len(cg.volumes))
self._compare(self, db_volumes[0], cg.volumes[0])
self.assertEqual(len(db_cgsnaps), len(cg.cgsnapshots))
self._compare(self, db_cgsnaps[0], cg.cgsnapshots[0])
class TestConsistencyGroupList(test_objects.BaseObjectsTestCase):
@mock.patch('cinder.db.consistencygroup_get_all',
return_value=[fake_consistencygroup])
def test_get_all(self, consistencygroup_get_all):
consistencygroups = objects.ConsistencyGroupList.get_all(self.context)
self.assertEqual(1, len(consistencygroups))
TestConsistencyGroup._compare(self, fake_consistencygroup,
consistencygroups[0])
@mock.patch('cinder.db.consistencygroup_get_all_by_project',
return_value=[fake_consistencygroup])
def test_get_all_by_project(self, consistencygroup_get_all_by_project):
consistencygroups = objects.ConsistencyGroupList.get_all_by_project(
self.context, self.project_id)
self.assertEqual(1, len(consistencygroups))
TestConsistencyGroup._compare(self, fake_consistencygroup,
consistencygroups[0])
@mock.patch('cinder.db.consistencygroup_get_all',
return_value=[fake_consistencygroup])
def test_get_all_with_pagination(self, consistencygroup_get_all):
consistencygroups = objects.ConsistencyGroupList.get_all(
self.context, filters={'id': 'fake'}, marker=None, limit=1,
offset=None, sort_keys='id', sort_dirs='asc')
self.assertEqual(1, len(consistencygroups))
consistencygroup_get_all.assert_called_once_with(
self.context, filters={'id': 'fake'}, marker=None, limit=1,
offset=None, sort_keys='id', sort_dirs='asc')
TestConsistencyGroup._compare(self, fake_consistencygroup,
consistencygroups[0])
@mock.patch('cinder.db.consistencygroup_get_all_by_project',
return_value=[fake_consistencygroup])
def test_get_all_by_project_with_pagination(
self, consistencygroup_get_all_by_project):
consistencygroups = objects.ConsistencyGroupList.get_all_by_project(
self.context, self.project_id, filters={'id': 'fake'}, marker=None,
limit=1, offset=None, sort_keys='id', sort_dirs='asc')
self.assertEqual(1, len(consistencygroups))
consistencygroup_get_all_by_project.assert_called_once_with(
self.context, self.project_id, filters={'id': 'fake'}, marker=None,
limit=1, offset=None, sort_keys='id', sort_dirs='asc')
TestConsistencyGroup._compare(self, fake_consistencygroup,
consistencygroups[0])
@mock.patch('cinder.db.consistencygroup_include_in_cluster')
def test_include_in_cluster(self, include_mock):
filters = {'host': mock.sentinel.host,
'cluster_name': mock.sentinel.cluster_name}
cluster = 'new_cluster'
objects.ConsistencyGroupList.include_in_cluster(self.context, cluster,
**filters)
include_mock.assert_called_once_with(self.context, cluster, True,
**filters)
@mock.patch('cinder.db.consistencygroup_include_in_cluster')
def test_include_in_cluster_specify_partial(self, include_mock):
filters = {'host': mock.sentinel.host,
'cluster_name': mock.sentinel.cluster_name}
cluster = 'new_cluster'
objects.ConsistencyGroupList.include_in_cluster(
self.context, cluster, mock.sentinel.partial_rename, **filters)
include_mock.assert_called_once_with(
self.context, cluster, mock.sentinel.partial_rename, **filters)