Fix quotas issue during volume transfer

When admin transfers a volume with volume type from
one tenant to another, it correctly updates the quota
for the default values, but does not update for the
volume type values.

This patch fixes the problem to update quotas for
the volume type values.

Change-Id: If444639cea8e74a415220df43fccfb155ca89216
Closes-Bug: #1501855
This commit is contained in:
Mitsuhiro Tanino 2015-11-13 15:01:32 -05:00
parent 1369c3c67a
commit 173fd8604c
2 changed files with 54 additions and 7 deletions

View File

@ -19,11 +19,15 @@ import mock
from cinder import context
from cinder import exception
from cinder import objects
from cinder import quota
from cinder import test
from cinder.tests.unit import utils
from cinder.transfer import api as transfer_api
QUOTAS = quota.QUOTAS
class VolumeTransferTestCase(test.TestCase):
"""Test cases for volume transfer code."""
def setUp(self):
@ -64,8 +68,9 @@ class VolumeTransferTestCase(test.TestCase):
self.assertEqual('in-use', volume['status'], 'Unexpected state')
@mock.patch('cinder.volume.utils.notify_about_volume_usage')
def test_transfer_accept(self, mock_notify):
def test_transfer_accept_invalid_authkey(self, mock_notify):
svc = self.start_service('volume', host='test_host')
self.addCleanup(svc.stop)
tx_api = transfer_api.API()
volume = utils.create_volume(self.ctxt, updated_at=self.updated_at)
transfer = tx_api.create(self.ctxt, volume.id, 'Description')
@ -81,6 +86,17 @@ class VolumeTransferTestCase(test.TestCase):
tx_api.accept,
self.ctxt, transfer['id'], 'wrong')
@mock.patch('cinder.volume.utils.notify_about_volume_usage')
def test_transfer_accept_invalid_volume(self, mock_notify):
svc = self.start_service('volume', host='test_host')
self.addCleanup(svc.stop)
tx_api = transfer_api.API()
volume = utils.create_volume(self.ctxt, updated_at=self.updated_at)
transfer = tx_api.create(self.ctxt, volume.id, 'Description')
volume = objects.Volume.get_by_id(self.ctxt, volume.id)
self.assertEqual('awaiting-transfer', volume['status'],
'Unexpected state')
calls = [mock.call(self.ctxt, mock.ANY, "transfer.create.start"),
mock.call(self.ctxt, mock.ANY, "transfer.create.end")]
mock_notify.assert_has_calls(calls)
@ -100,6 +116,18 @@ class VolumeTransferTestCase(test.TestCase):
mock_notify.assert_has_calls(calls)
self.assertEqual(3, mock_notify.call_count)
@mock.patch.object(QUOTAS, "reserve")
@mock.patch.object(QUOTAS, "add_volume_type_opts")
@mock.patch('cinder.volume.utils.notify_about_volume_usage')
def test_transfer_accept(self, mock_notify, mock_quota_voltype,
mock_quota_reserve):
svc = self.start_service('volume', host='test_host')
self.addCleanup(svc.stop)
tx_api = transfer_api.API()
volume = utils.create_volume(self.ctxt, volume_type_id='12345',
updated_at=self.updated_at)
transfer = tx_api.create(self.ctxt, volume.id, 'Description')
self.ctxt.user_id = 'new_user_id'
self.ctxt.project_id = 'new_project_id'
response = tx_api.accept(self.ctxt,
@ -117,9 +145,22 @@ class VolumeTransferTestCase(test.TestCase):
calls = [mock.call(self.ctxt, mock.ANY, "transfer.accept.start"),
mock.call(self.ctxt, mock.ANY, "transfer.accept.end")]
mock_notify.assert_has_calls(calls)
self.assertEqual(5, mock_notify.call_count)
# The notify_about_volume_usage is called twice at create(),
# and twice at accept().
self.assertEqual(4, mock_notify.call_count)
svc.stop()
# Check QUOTAS reservation calls
# QUOTAS.add_volume_type_opts
reserve_opt = {'volumes': 1, 'gigabytes': 1}
release_opt = {'volumes': -1, 'gigabytes': -1}
calls = [mock.call(self.ctxt, reserve_opt, '12345'),
mock.call(self.ctxt, release_opt, '12345')]
mock_quota_voltype.assert_has_calls(calls)
# QUOTAS.reserve
calls = [mock.call(mock.ANY, **reserve_opt),
mock.call(mock.ANY, project_id='project_id', **release_opt)]
mock_quota_reserve.assert_has_calls(calls)
def test_transfer_get(self):
tx_api = transfer_api.API()

View File

@ -165,8 +165,11 @@ class API(base.Base):
"transfer.accept.start")
try:
reservations = QUOTAS.reserve(context, volumes=1,
gigabytes=vol_ref['size'])
reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
QUOTAS.add_volume_type_opts(context,
reserve_opts,
vol_ref.volume_type_id)
reservations = QUOTAS.reserve(context, **reserve_opts)
except exception.OverQuota as e:
overs = e.kwargs['overs']
usages = e.kwargs['usages']
@ -196,10 +199,13 @@ class API(base.Base):
raise exception.VolumeLimitExceeded(allowed=quotas['volumes'])
try:
donor_id = vol_ref['project_id']
reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
QUOTAS.add_volume_type_opts(context,
reserve_opts,
vol_ref.volume_type_id)
donor_reservations = QUOTAS.reserve(context.elevated(),
project_id=donor_id,
volumes=-1,
gigabytes=-vol_ref['size'])
**reserve_opts)
except Exception:
donor_reservations = None
LOG.exception(_LE("Failed to update quota donating volume"