Merge "Fix manila OverQuota issue while managing shares" into stable/train
This commit is contained in:
commit
5e2fc65268
|
@ -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,
|
def quota_reserve(context, resources, quotas, user_quotas, share_type_quotas,
|
||||||
deltas, expire, until_refresh, max_age,
|
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."""
|
"""Check quotas and create appropriate reservations."""
|
||||||
return IMPL.quota_reserve(
|
return IMPL.quota_reserve(
|
||||||
context, resources, quotas, user_quotas, share_type_quotas, deltas,
|
context, resources, quotas, user_quotas, share_type_quotas, deltas,
|
||||||
expire, until_refresh, max_age, project_id=project_id, user_id=user_id,
|
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,
|
def reservation_commit(context, reservations, project_id=None, user_id=None,
|
||||||
|
|
|
@ -914,16 +914,19 @@ def _get_project_quota_usages(context, session, project_id):
|
||||||
@require_context
|
@require_context
|
||||||
def quota_reserve(context, resources, project_quotas, user_quotas,
|
def quota_reserve(context, resources, project_quotas, user_quotas,
|
||||||
share_type_quotas, deltas, expire, until_refresh,
|
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(
|
user_reservations = _quota_reserve(
|
||||||
context, resources, project_quotas, user_quotas,
|
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:
|
if share_type_id:
|
||||||
try:
|
try:
|
||||||
st_reservations = _quota_reserve(
|
st_reservations = _quota_reserve(
|
||||||
context, resources, project_quotas, share_type_quotas,
|
context, resources, project_quotas, share_type_quotas,
|
||||||
deltas, expire, until_refresh, max_age, project_id,
|
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:
|
except exception.OverQuota:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
# rollback previous reservations
|
# 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)
|
@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
|
||||||
def _quota_reserve(context, resources, project_quotas, user_or_st_quotas,
|
def _quota_reserve(context, resources, project_quotas, user_or_st_quotas,
|
||||||
deltas, expire, until_refresh,
|
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()
|
elevated = context.elevated()
|
||||||
session = get_session()
|
session = get_session()
|
||||||
with session.begin():
|
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_quotas[res] < delta +
|
||||||
user_or_st_usages[res].total)]
|
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,
|
# NOTE(Vek): The quota check needs to be in the transaction,
|
||||||
# but the transaction doesn't fail just because
|
# but the transaction doesn't fail just because
|
||||||
# we're over quota, so the OverQuota raise is
|
# we're over quota, so the OverQuota raise is
|
||||||
|
|
|
@ -378,7 +378,8 @@ class DbQuotaDriver(object):
|
||||||
return {k: v['limit'] for k, v in quotas.items()}
|
return {k: v['limit'] for k, v in quotas.items()}
|
||||||
|
|
||||||
def reserve(self, context, resources, deltas, expire=None,
|
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.
|
"""Check quotas and reserve resources.
|
||||||
|
|
||||||
For counting quotas--those quotas for which there is a usage
|
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,
|
context, resources, quotas, user_quotas, share_type_quotas,
|
||||||
deltas, expire, CONF.until_refresh, CONF.max_age,
|
deltas, expire, CONF.until_refresh, CONF.max_age,
|
||||||
project_id=project_id, user_id=user_id,
|
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,
|
def commit(self, context, reservations, project_id=None, user_id=None,
|
||||||
share_type_id=None):
|
share_type_id=None):
|
||||||
|
@ -870,7 +871,7 @@ class QuotaEngine(object):
|
||||||
return res.count(context, *args, **kwargs)
|
return res.count(context, *args, **kwargs)
|
||||||
|
|
||||||
def reserve(self, context, expire=None, project_id=None, user_id=None,
|
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.
|
"""Check quotas and reserve resources.
|
||||||
|
|
||||||
For counting quotas--those quotas for which there is a usage
|
For counting quotas--those quotas for which there is a usage
|
||||||
|
@ -911,6 +912,7 @@ class QuotaEngine(object):
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
share_type_id=share_type_id,
|
share_type_id=share_type_id,
|
||||||
|
overquota_allowed=overquota_allowed
|
||||||
)
|
)
|
||||||
|
|
||||||
LOG.debug("Created reservations %s", reservations)
|
LOG.debug("Created reservations %s", reservations)
|
||||||
|
|
|
@ -2420,6 +2420,12 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||||
msg = _("Driver cannot calculate share size.")
|
msg = _("Driver cannot calculate share size.")
|
||||||
raise exception.InvalidShare(reason=msg)
|
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(
|
reservations = QUOTAS.reserve(
|
||||||
context,
|
context,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
|
@ -2427,6 +2433,7 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||||
shares=1,
|
shares=1,
|
||||||
gigabytes=share_update['size'],
|
gigabytes=share_update['size'],
|
||||||
share_type_id=share_instance['share_type_id'],
|
share_type_id=share_instance['share_type_id'],
|
||||||
|
overquota_allowed=True
|
||||||
)
|
)
|
||||||
QUOTAS.commit(
|
QUOTAS.commit(
|
||||||
context, reservations, project_id=project_id,
|
context, reservations, project_id=project_id,
|
||||||
|
|
|
@ -2621,6 +2621,14 @@ class ShareManagerTestCase(test.TestCase):
|
||||||
share = db_utils.create_share(replication_type=replication_type)
|
share = db_utils.create_share(replication_type=replication_type)
|
||||||
share_id = share['id']
|
share_id = share['id']
|
||||||
driver_options = {'fake': 'fake'}
|
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)
|
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(
|
self.share_manager.db.share_update.assert_called_once_with(
|
||||||
utils.IsAMatcher(context.RequestContext),
|
utils.IsAMatcher(context.RequestContext),
|
||||||
share_id, valid_share_data)
|
share_id, valid_share_data)
|
||||||
|
quota.QUOTAS.reserve.assert_called_once_with(
|
||||||
|
mock.ANY, **expected_deltas)
|
||||||
|
|
||||||
def test_update_quota_usages_new(self):
|
def test_update_quota_usages_new(self):
|
||||||
self.mock_object(self.share_manager.db, 'quota_usage_get',
|
self.mock_object(self.share_manager.db, 'quota_usage_get',
|
||||||
|
|
|
@ -353,6 +353,7 @@ class DbQuotaDriverTestCase(test.TestCase):
|
||||||
'project_id': self.ctxt.project_id,
|
'project_id': self.ctxt.project_id,
|
||||||
'user_id': self.ctxt.user_id,
|
'user_id': self.ctxt.user_id,
|
||||||
'share_type_id': None,
|
'share_type_id': None,
|
||||||
|
'overquota_allowed': False
|
||||||
}
|
}
|
||||||
expected_kwargs.update(kwargs)
|
expected_kwargs.update(kwargs)
|
||||||
st_quotas = st_quotas if kwargs.get('share_type_id') else {}
|
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.driver.reserve.assert_called_once_with(
|
||||||
self.ctxt, self.engine._resources, {'delta1': 1, 'delta2': 2},
|
self.ctxt, self.engine._resources, {'delta1': 1, 'delta2': 2},
|
||||||
expire='fake_expire', project_id=self.project_id,
|
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])
|
@ddt.data(Exception('FakeException'), [None])
|
||||||
def test_commit(self, side_effect):
|
def test_commit(self, side_effect):
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue