Merge "Delete consistency group failed"

This commit is contained in:
Jenkins 2014-09-13 15:30:44 +00:00 committed by Gerrit Code Review
commit 39f59d4eba
4 changed files with 157 additions and 14 deletions

View File

@ -156,7 +156,7 @@ class API(base.Base):
filter_properties_list.append(filter_properties)
# Update quota for consistencygroups
self.update_quota(context, group['id'])
self.update_quota(context, group['id'], 1)
self._cast_create_consistencygroup(context, group['id'],
request_spec_list,
@ -219,21 +219,35 @@ class API(base.Base):
request_spec_list=request_spec_list,
filter_properties_list=filter_properties_list)
def update_quota(self, context, group_id):
reserve_opts = {'consistencygroups': 1}
def update_quota(self, context, group_id, num, project_id=None):
reserve_opts = {'consistencygroups': num}
try:
reservations = CGQUOTAS.reserve(context, **reserve_opts)
CGQUOTAS.commit(context, reservations)
reservations = CGQUOTAS.reserve(context,
project_id=project_id,
**reserve_opts)
if reservations:
CGQUOTAS.commit(context, reservations)
except Exception:
with excutils.save_and_reraise_exception():
try:
self.db.consistencygroup_destroy(context, group_id)
self.db.consistencygroup_destroy(context.elevated(),
group_id)
finally:
LOG.error(_("Failed to update quota for creating"
LOG.error(_("Failed to update quota for "
"consistency group %s."), group_id)
@wrap_check_policy
def delete(self, context, group, force=False):
if not group['host']:
self.update_quota(context, group['id'], -1, group['project_id'])
msg = ("No host for consistency group %s. Deleting from "
"the database.") % group['id']
LOG.debug(msg)
self.db.consistencygroup_destroy(context.elevated(), group['id'])
return
if not force and group['status'] not in ["available", "error"]:
msg = _("Consistency group status must be available or error, "
"but current status is: %s") % group['status']

View File

@ -20,13 +20,14 @@ Tests for consistency group code.
import json
from xml.dom import minidom
import mock
import webob
import cinder.consistencygroup
from cinder import context
from cinder import db
from cinder import test
from cinder.tests.api import fakes
import cinder.volume
class ConsistencyGroupsAPITestCase(test.TestCase):
@ -34,10 +35,7 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
def setUp(self):
super(ConsistencyGroupsAPITestCase, self).setUp()
self.volume_api = cinder.volume.API()
self.context = context.get_admin_context()
self.context.project_id = 'fake'
self.context.user_id = 'fake'
self.cg_api = cinder.consistencygroup.API()
@staticmethod
def _create_consistencygroup(
@ -45,6 +43,7 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
description='this is a test consistency group',
volume_type_id='123456',
availability_zone='az1',
host='fakehost',
status='creating'):
"""Create a consistency group object."""
consistencygroup = {}
@ -54,8 +53,8 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
consistencygroup['name'] = name
consistencygroup['description'] = description
consistencygroup['volume_type_id'] = volume_type_id
consistencygroup['host'] = host
consistencygroup['status'] = status
consistencygroup['host'] = 'fakehost'
return db.consistencygroup_create(
context.get_admin_context(),
consistencygroup)['id']
@ -378,3 +377,51 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
db.consistencygroup_destroy(context.get_admin_context(),
consistencygroup_id)
def test_delete_consistencygroup_no_host(self):
consistencygroup_id = self._create_consistencygroup(
host=None,
status='error')
req = webob.Request.blank('/v2/fake/consistencygroups/%s/delete' %
consistencygroup_id)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"force": True}}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 202)
cg = db.consistencygroup_get(
context.get_admin_context(read_deleted='yes'),
consistencygroup_id)
self.assertEqual(cg['status'], 'deleted')
self.assertEqual(cg['host'], None)
def test_create_delete_consistencygroup_update_quota(self):
ctxt = context.RequestContext('fake', 'fake', auth_token=True)
name = 'mycg'
description = 'consistency group 1'
fake_type = {'id': '1', 'name': 'fake_type'}
self.stubs.Set(db, 'volume_types_get_by_name_or_id',
mock.Mock(return_value=[fake_type]))
self.stubs.Set(self.cg_api,
'_cast_create_consistencygroup',
mock.Mock())
self.stubs.Set(self.cg_api, 'update_quota',
mock.Mock())
cg = self.cg_api.create(ctxt, name, description, fake_type['name'])
self.cg_api.update_quota.assert_called_once_with(
ctxt, cg['id'], 1)
self.assertEqual(cg['status'], 'creating')
self.assertEqual(cg['host'], None)
self.cg_api.update_quota.reset_mock()
cg['status'] = 'error'
self.cg_api.delete(ctxt, cg)
self.cg_api.update_quota.assert_called_once_with(
ctxt, cg['id'], -1, ctxt.project_id)
cg = db.consistencygroup_get(
context.get_admin_context(read_deleted='yes'),
cg['id'])
self.assertEqual(cg['status'], 'deleted')

View File

@ -3104,8 +3104,86 @@ class VolumeTestCase(BaseVolumeTestCase):
db.cgsnapshot_get,
self.context,
cgsnapshot_id)
self.volume.delete_consistencygroup(self.context, group_id)
def test_delete_consistencygroup_correct_host(self):
"""Test consistencygroup can be deleted.
Test consistencygroup can be deleted when volumes are on
the correct volume node.
"""
rval = {'status': 'available'}
driver.VolumeDriver.create_consistencygroup = \
mock.Mock(return_value=rval)
rval = {'status': 'deleted'}, []
driver.VolumeDriver.delete_consistencygroup = \
mock.Mock(return_value=rval)
group = tests_utils.create_consistencygroup(
self.context,
availability_zone=CONF.storage_availability_zone,
volume_type='type1,type2')
group_id = group['id']
volume = tests_utils.create_volume(
self.context,
consistencygroup_id = group_id,
host='host1@backend1#pool1',
status = 'creating',
size = 1)
self.volume.host = 'host1@backend1'
volume_id = volume['id']
self.volume.create_volume(self.context, volume_id)
self.volume.delete_consistencygroup(self.context, group_id)
cg = db.consistencygroup_get(
context.get_admin_context(read_deleted='yes'),
group_id)
self.assertEqual(cg['status'], 'deleted')
self.assertRaises(exception.NotFound,
db.consistencygroup_get,
self.context,
group_id)
def test_delete_consistencygroup_wrong_host(self):
"""Test consistencygroup cannot be deleted.
Test consistencygroup cannot be deleted when volumes in the
group are not local to the volume node.
"""
rval = {'status': 'available'}
driver.VolumeDriver.create_consistencygroup = \
mock.Mock(return_value=rval)
group = tests_utils.create_consistencygroup(
self.context,
availability_zone=CONF.storage_availability_zone,
volume_type='type1,type2')
group_id = group['id']
volume = tests_utils.create_volume(
self.context,
consistencygroup_id = group_id,
host='host1@backend1#pool1',
status = 'creating',
size = 1)
self.volume.host = 'host1@backend2'
volume_id = volume['id']
self.volume.create_volume(self.context, volume_id)
self.assertRaises(exception.InvalidVolume,
self.volume.delete_consistencygroup,
self.context,
group_id)
cg = db.consistencygroup_get(self.context,
group_id)
# Group is not deleted
self.assertEqual(cg['status'], 'available')
class CopyVolumeToImageTestCase(BaseVolumeTestCase):
def fake_local_path(self, volume):

View File

@ -1669,7 +1669,11 @@ class VolumeManager(manager.SchedulerDependentManager):
if volume_ref['attach_status'] == "attached":
# Volume is still attached, need to detach first
raise exception.VolumeAttached(volume_id=volume_ref['id'])
if volume_ref['host'] != self.host:
# self.host is 'host@backend'
# volume_ref['host'] is 'host@backend#pool'
# Extract host before doing comparison
new_host = vol_utils.extract_host(volume_ref['host'])
if new_host != self.host:
raise exception.InvalidVolume(
reason=_("Volume is not local to this node"))