Add quota limit check and enhance roll back for cg create
when a consistency group is created from a source consistency group and the number of volumes associated with the source CG exceeds the volumes quota, the creation of the CG will fail and will leave volumes in "creating". This patch add the quota limit check and also let the new created volumes can be deleted if error occured. Change-Id: I4f2ce1999c0eee798e2f4650a940d7335c548d40 Closes-bug: #1609889
This commit is contained in:
parent
b877bca044
commit
e9e9934c74
@ -45,6 +45,7 @@ CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CGQUOTAS = quota.CGQUOTAS
|
||||
QUOTAS = quota.QUOTAS
|
||||
VALID_REMOVE_VOL_FROM_CG_STATUS = (
|
||||
'available',
|
||||
'in-use',
|
||||
@ -232,6 +233,16 @@ class API(base.Base):
|
||||
"will be created.")
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
|
||||
try:
|
||||
values = {'volumes': len(snapshots)}
|
||||
QUOTAS.limit_check(context, project_id=context.project_id,
|
||||
**values)
|
||||
except exception.OverQuota as e:
|
||||
group.destroy()
|
||||
quotas = e.kwargs['quotas']
|
||||
raise exception.VolumeLimitExceeded(
|
||||
allowed=e.kwargs['overs'], limit=quotas['volumes'])
|
||||
|
||||
for snapshot in snapshots:
|
||||
kwargs = {}
|
||||
kwargs['availability_zone'] = group.availability_zone
|
||||
@ -265,6 +276,10 @@ class API(base.Base):
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
try:
|
||||
new_vols = self.db.volume_get_all_by_group(context,
|
||||
group.id)
|
||||
for vol in new_vols:
|
||||
self.volume_api.delete(context, vol, force=True)
|
||||
group.destroy()
|
||||
finally:
|
||||
LOG.error(_LE("Error occurred when creating consistency "
|
||||
@ -295,6 +310,16 @@ class API(base.Base):
|
||||
"will be created.")
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
|
||||
try:
|
||||
values = {'volumes': len(source_vols)}
|
||||
QUOTAS.limit_check(context, project_id=context.project_id,
|
||||
**values)
|
||||
except exception.OverQuota as e:
|
||||
group.destroy()
|
||||
quotas = e.kwargs['quotas']
|
||||
raise exception.VolumeLimitExceeded(
|
||||
allowed=e.kwargs['overs'], limit=quotas['volumes'])
|
||||
|
||||
for source_vol in source_vols:
|
||||
kwargs = {}
|
||||
kwargs['availability_zone'] = group.availability_zone
|
||||
@ -328,6 +353,10 @@ class API(base.Base):
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
try:
|
||||
new_vols = self.db.volume_get_all_by_group(context,
|
||||
group.id)
|
||||
for vol in new_vols:
|
||||
self.volume_api.delete(context, vol, force=True)
|
||||
group.destroy()
|
||||
finally:
|
||||
LOG.error(_LE("Error occurred when creating consistency "
|
||||
|
@ -1133,9 +1133,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
|
||||
|
||||
consistencygroup.destroy()
|
||||
|
||||
@mock.patch('cinder.quota.QuotaEngine.limit_check')
|
||||
@mock.patch(
|
||||
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
|
||||
def test_create_consistencygroup_from_src(self, mock_validate):
|
||||
def test_create_consistencygroup_from_src(self, mock_validate, mock_quota):
|
||||
self.mock_object(volume_api.API, "create", v2_fakes.fake_volume_create)
|
||||
|
||||
consistencygroup = utils.create_consistencygroup(self.ctxt)
|
||||
@ -1178,7 +1179,8 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
|
||||
consistencygroup.destroy()
|
||||
cgsnapshot.destroy()
|
||||
|
||||
def test_create_consistencygroup_from_src_cg(self):
|
||||
@mock.patch('cinder.quota.QuotaEngine.limit_check')
|
||||
def test_create_consistencygroup_from_src_cg(self, mock_quota):
|
||||
self.mock_object(volume_api.API, "create", v2_fakes.fake_volume_create)
|
||||
|
||||
source_cg = utils.create_consistencygroup(self.ctxt)
|
||||
@ -1434,11 +1436,12 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
|
||||
self.assertEqual(404, res_dict['itemNotFound']['code'])
|
||||
self.assertIsNotNone(res_dict['itemNotFound']['message'])
|
||||
|
||||
@mock.patch('cinder.quota.QuotaEngine.limit_check')
|
||||
@mock.patch.object(volume_api.API, 'create',
|
||||
side_effect=exception.CinderException(
|
||||
'Create volume failed.'))
|
||||
def test_create_consistencygroup_from_src_cgsnapshot_create_volume_failed(
|
||||
self, mock_create):
|
||||
self, mock_create, mock_quota):
|
||||
consistencygroup = utils.create_consistencygroup(self.ctxt)
|
||||
volume_id = utils.create_volume(
|
||||
self.ctxt,
|
||||
@ -1475,11 +1478,12 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
|
||||
consistencygroup.destroy()
|
||||
cgsnapshot.destroy()
|
||||
|
||||
@mock.patch('cinder.quota.QuotaEngine.limit_check')
|
||||
@mock.patch.object(volume_api.API, 'create',
|
||||
side_effect=exception.CinderException(
|
||||
'Create volume failed.'))
|
||||
def test_create_consistencygroup_from_src_cg_create_volume_failed(
|
||||
self, mock_create):
|
||||
self, mock_create, mock_quota):
|
||||
source_cg = utils.create_consistencygroup(self.ctxt)
|
||||
volume_id = utils.create_volume(
|
||||
self.ctxt,
|
||||
@ -1505,3 +1509,88 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
|
||||
|
||||
db.volume_destroy(self.ctxt.elevated(), volume_id)
|
||||
source_cg.destroy()
|
||||
|
||||
@mock.patch('cinder.quota.QuotaEngine.limit_check')
|
||||
def test_create_consistencygroup_from_src_cg_over_quota(self, mock_quota):
|
||||
self.mock_object(volume_api.API, "create", v2_fakes.fake_volume_create)
|
||||
|
||||
source_cg = utils.create_consistencygroup(self.ctxt)
|
||||
volume_id = utils.create_volume(
|
||||
self.ctxt,
|
||||
consistencygroup_id=source_cg.id)['id']
|
||||
|
||||
mock_quota.side_effect = exception.OverQuota(
|
||||
overs=10, quotas='volumes', usages={})
|
||||
|
||||
test_cg_name = 'test cg'
|
||||
body = {"consistencygroup-from-src": {"name": test_cg_name,
|
||||
"description":
|
||||
"Consistency Group 1",
|
||||
"source_cgid": source_cg.id}}
|
||||
req = webob.Request.blank('/v2/%s/consistencygroups/create_from_src' %
|
||||
fake.PROJECT_ID)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
res = req.get_response(fakes.wsgi_app(
|
||||
fake_auth_context=self.user_ctxt))
|
||||
res_dict = jsonutils.loads(res.body)
|
||||
|
||||
self.assertEqual(400, res.status_int)
|
||||
self.assertIn('message', res_dict['badRequest'])
|
||||
|
||||
cg = objects.ConsistencyGroupList.get_all(self.ctxt)
|
||||
# The new cg has been deleted already.
|
||||
self.assertEqual(1, len(cg))
|
||||
|
||||
db.volume_destroy(self.ctxt.elevated(), volume_id)
|
||||
source_cg.destroy()
|
||||
|
||||
@mock.patch('cinder.quota.QuotaEngine.limit_check')
|
||||
@mock.patch(
|
||||
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
|
||||
def test_create_consistencygroup_from_src_cgsnapshot_over_quota(
|
||||
self, mock_validate, mock_quota):
|
||||
self.mock_object(volume_api.API, "create", v2_fakes.fake_volume_create)
|
||||
|
||||
consistencygroup = utils.create_consistencygroup(self.ctxt)
|
||||
volume_id = utils.create_volume(
|
||||
self.ctxt,
|
||||
consistencygroup_id=consistencygroup.id)['id']
|
||||
cgsnapshot = utils.create_cgsnapshot(
|
||||
self.ctxt, consistencygroup_id=consistencygroup.id)
|
||||
snapshot = utils.create_snapshot(
|
||||
self.ctxt,
|
||||
volume_id,
|
||||
cgsnapshot_id=cgsnapshot.id,
|
||||
status=fields.SnapshotStatus.AVAILABLE)
|
||||
|
||||
mock_quota.side_effect = exception.OverQuota(
|
||||
overs=10, quotas='volumes', usages={})
|
||||
|
||||
test_cg_name = 'test cg'
|
||||
body = {"consistencygroup-from-src": {"name": test_cg_name,
|
||||
"description":
|
||||
"Consistency Group 1",
|
||||
"cgsnapshot_id": cgsnapshot.id}}
|
||||
req = webob.Request.blank('/v2/%s/consistencygroups/create_from_src' %
|
||||
fake.PROJECT_ID)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
res = req.get_response(fakes.wsgi_app(
|
||||
fake_auth_context=self.user_ctxt))
|
||||
res_dict = jsonutils.loads(res.body)
|
||||
|
||||
self.assertEqual(400, res.status_int)
|
||||
self.assertIn('message', res_dict['badRequest'])
|
||||
self.assertTrue(mock_validate.called)
|
||||
|
||||
cg = objects.ConsistencyGroupList.get_all(self.ctxt)
|
||||
# The new cg has been deleted already.
|
||||
self.assertEqual(1, len(cg))
|
||||
|
||||
snapshot.destroy()
|
||||
db.volume_destroy(self.ctxt.elevated(), volume_id)
|
||||
consistencygroup.destroy()
|
||||
cgsnapshot.destroy()
|
||||
|
Loading…
x
Reference in New Issue
Block a user