cinder/cinder/tests/unit/objects/test_consistencygroup.py

311 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.
from unittest 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)