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,
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']
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):
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(
description='this is a test consistency group',
volume_type_id='123456',
availability_zone='az1',
+ host='fakehost',
status='creating'):
"""Create a consistency group object."""
consistencygroup = {}
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']
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')
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):
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"))