702868fd00
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
310 lines
14 KiB
Python
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)
|