Merge "Fix manila OverQuota issue while managing shares" into stable/train

This commit is contained in:
Zuul 2021-06-02 17:59:25 +00:00 committed by Gerrit Code Review
commit 5e2fc65268
7 changed files with 61 additions and 10 deletions

View File

@ -250,12 +250,13 @@ def quota_usage_update(context, project_id, user_id, resource,
def quota_reserve(context, resources, quotas, user_quotas, share_type_quotas,
deltas, expire, until_refresh, max_age,
project_id=None, user_id=None, share_type_id=None):
project_id=None, user_id=None, share_type_id=None,
overquota_allowed=False):
"""Check quotas and create appropriate reservations."""
return IMPL.quota_reserve(
context, resources, quotas, user_quotas, share_type_quotas, deltas,
expire, until_refresh, max_age, project_id=project_id, user_id=user_id,
share_type_id=share_type_id)
share_type_id=share_type_id, overquota_allowed=overquota_allowed)
def reservation_commit(context, reservations, project_id=None, user_id=None,

View File

@ -914,16 +914,19 @@ def _get_project_quota_usages(context, session, project_id):
@require_context
def quota_reserve(context, resources, project_quotas, user_quotas,
share_type_quotas, deltas, expire, until_refresh,
max_age, project_id=None, user_id=None, share_type_id=None):
max_age, project_id=None, user_id=None, share_type_id=None,
overquota_allowed=False):
user_reservations = _quota_reserve(
context, resources, project_quotas, user_quotas,
deltas, expire, until_refresh, max_age, project_id, user_id=user_id)
deltas, expire, until_refresh, max_age, project_id, user_id=user_id,
overquota_allowed=overquota_allowed)
if share_type_id:
try:
st_reservations = _quota_reserve(
context, resources, project_quotas, share_type_quotas,
deltas, expire, until_refresh, max_age, project_id,
share_type_id=share_type_id)
share_type_id=share_type_id,
overquota_allowed=overquota_allowed)
except exception.OverQuota:
with excutils.save_and_reraise_exception():
# rollback previous reservations
@ -937,7 +940,8 @@ def quota_reserve(context, resources, project_quotas, user_quotas,
@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
def _quota_reserve(context, resources, project_quotas, user_or_st_quotas,
deltas, expire, until_refresh,
max_age, project_id=None, user_id=None, share_type_id=None):
max_age, project_id=None, user_id=None, share_type_id=None,
overquota_allowed=False):
elevated = context.elevated()
session = get_session()
with session.begin():
@ -1085,6 +1089,21 @@ def _quota_reserve(context, resources, project_quotas, user_or_st_quotas,
user_or_st_quotas[res] < delta +
user_or_st_usages[res].total)]
# NOTE(carloss): If OverQuota is allowed, there is no problem to exceed
# the quotas, so we reset the overs list and LOG it.
if overs and overquota_allowed:
msg = _("The service has identified one or more exceeded "
"quotas. Please check the quotas for project "
"%(project_id)s, user %(user_id)s and share type "
"%(share_type_id)s, and adjust them if "
"necessary.") % {
"project_id": project_id,
"user_id": user_id,
"share_type_id": share_type_id
}
LOG.warning(msg)
overs = []
# NOTE(Vek): The quota check needs to be in the transaction,
# but the transaction doesn't fail just because
# we're over quota, so the OverQuota raise is

View File

@ -378,7 +378,8 @@ class DbQuotaDriver(object):
return {k: v['limit'] for k, v in quotas.items()}
def reserve(self, context, resources, deltas, expire=None,
project_id=None, user_id=None, share_type_id=None):
project_id=None, user_id=None, share_type_id=None,
overquota_allowed=False):
"""Check quotas and reserve resources.
For counting quotas--those quotas for which there is a usage
@ -459,7 +460,7 @@ class DbQuotaDriver(object):
context, resources, quotas, user_quotas, share_type_quotas,
deltas, expire, CONF.until_refresh, CONF.max_age,
project_id=project_id, user_id=user_id,
share_type_id=share_type_id)
share_type_id=share_type_id, overquota_allowed=overquota_allowed)
def commit(self, context, reservations, project_id=None, user_id=None,
share_type_id=None):
@ -870,7 +871,7 @@ class QuotaEngine(object):
return res.count(context, *args, **kwargs)
def reserve(self, context, expire=None, project_id=None, user_id=None,
share_type_id=None, **deltas):
share_type_id=None, overquota_allowed=False, **deltas):
"""Check quotas and reserve resources.
For counting quotas--those quotas for which there is a usage
@ -911,6 +912,7 @@ class QuotaEngine(object):
project_id=project_id,
user_id=user_id,
share_type_id=share_type_id,
overquota_allowed=overquota_allowed
)
LOG.debug("Created reservations %s", reservations)

View File

@ -2420,6 +2420,12 @@ class ShareManager(manager.SchedulerDependentManager):
msg = _("Driver cannot calculate share size.")
raise exception.InvalidShare(reason=msg)
# NOTE(carloss): Allowing OverQuota to do not compromise this
# operation. If this hit OverQuota error while managing a share,
# the admin would need to reset the state of the share and
# delete or force delete the share (bug 1863298). Allowing
# OverQuota makes this operation work properly and the admin will
# need to adjust quotas afterwards.
reservations = QUOTAS.reserve(
context,
project_id=project_id,
@ -2427,6 +2433,7 @@ class ShareManager(manager.SchedulerDependentManager):
shares=1,
gigabytes=share_update['size'],
share_type_id=share_instance['share_type_id'],
overquota_allowed=True
)
QUOTAS.commit(
context, reservations, project_id=project_id,

View File

@ -2621,6 +2621,14 @@ class ShareManagerTestCase(test.TestCase):
share = db_utils.create_share(replication_type=replication_type)
share_id = share['id']
driver_options = {'fake': 'fake'}
expected_deltas = {
'project_id': share['project_id'],
'user_id': self.context.user_id,
'shares': 1,
'gigabytes': driver_data['size'],
'share_type_id': share['instance']['share_type_id'],
'overquota_allowed': True
}
self.share_manager.manage_share(self.context, share_id, driver_options)
@ -2644,6 +2652,8 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.db.share_update.assert_called_once_with(
utils.IsAMatcher(context.RequestContext),
share_id, valid_share_data)
quota.QUOTAS.reserve.assert_called_once_with(
mock.ANY, **expected_deltas)
def test_update_quota_usages_new(self):
self.mock_object(self.share_manager.db, 'quota_usage_get',

View File

@ -353,6 +353,7 @@ class DbQuotaDriverTestCase(test.TestCase):
'project_id': self.ctxt.project_id,
'user_id': self.ctxt.user_id,
'share_type_id': None,
'overquota_allowed': False
}
expected_kwargs.update(kwargs)
st_quotas = st_quotas if kwargs.get('share_type_id') else {}
@ -624,7 +625,8 @@ class QuotaEngineTestCase(test.TestCase):
self.driver.reserve.assert_called_once_with(
self.ctxt, self.engine._resources, {'delta1': 1, 'delta2': 2},
expire='fake_expire', project_id=self.project_id,
user_id=self.user_id, share_type_id=self.share_type_id)
user_id=self.user_id, share_type_id=self.share_type_id,
overquota_allowed=False)
@ddt.data(Exception('FakeException'), [None])
def test_commit(self, side_effect):

View File

@ -0,0 +1,10 @@
---
fixes:
- |
Fixed an issue while bringing shares under Manila management. Now, when
a share is being managed and there is no available quota to complete
this operation, the service will allow the quotas to be exceeded and the
operation will be completed. The administrator will need to adjust the
quotas after. Please see
`Launchpad bug <https://bugs.launchpad.net/manila/+bug/1863298>`_ for
more details.