Fix manila quota operations

Some manila quota names overlap with Cinder quota names. It leads to
mess of read/update operations. So, introduce unique quota names for
Manila UI and translate them into manila quota names there and back.

Change-Id: I4e12cfab8200aba83f2d1ca607b230f6c69d30bc
Closes-Bug: #1679960
(cherry picked from commit 7501353d8d)
(cherry picked from commit 11065527f2)
This commit is contained in:
Valeriy Ponomaryov 2017-04-27 20:46:28 +03:00 committed by Tom Barron
parent 4ae1bc8432
commit 1c783c5fd3
5 changed files with 207 additions and 20 deletions

View File

@ -306,7 +306,20 @@ def tenant_quota_get(request, tenant_id):
return base.QuotaSet(manilaclient(request).quotas.get(tenant_id))
def _map_quota_names_for_update(data):
mapping = {
'share_gigabytes': 'gigabytes',
'share_snapshots': 'snapshots',
'share_snapshot_gigabytes': 'snapshot_gigabytes',
}
for k, v in mapping.items():
if k in data:
data[v] = data.pop(k)
return data
def tenant_quota_update(request, tenant_id, **kwargs):
_map_quota_names_for_update(kwargs)
return manilaclient(request).quotas.update(tenant_id, **kwargs)
@ -315,6 +328,7 @@ def default_quota_get(request, tenant_id):
def default_quota_update(request, **kwargs):
_map_quota_names_for_update(kwargs)
manilaclient(request).quota_classes.update(DEFAULT_QUOTA_NAME, **kwargs)

View File

@ -69,16 +69,20 @@ def wrap(orig_func):
MANILA_QUOTA_FIELDS = (
"shares",
"snapshots",
"gigabytes",
"share_gigabytes",
"share_snapshots",
"share_snapshot_gigabytes",
"share_networks",
)
MANILA_QUOTA_NAMES = {
'shares': _('Shares'),
'share_gigabytes': _('Share gigabytes'),
'share_snapshots': _('Share snapshots'),
'share_snapshot_gigabytes': _('Share snapshot gigabytes'),
'share_networks': _('Shares Networks'),
}
quotas.QUOTA_FIELDS = quotas.QUOTA_FIELDS + MANILA_QUOTA_FIELDS
quotas.QUOTA_FIELDS += MANILA_QUOTA_FIELDS
def _get_manila_disabled_quotas(request):
@ -96,7 +100,15 @@ def _get_manila_quota_data(request, method_name, disabled_quotas=None,
if disabled_quotas is None:
disabled_quotas = _get_manila_disabled_quotas(request)
if 'shares' not in disabled_quotas:
return getattr(manila, method_name)(request, tenant_id)
manila_quotas = getattr(manila, method_name)(request, tenant_id)
for quota in manila_quotas:
if quota.name == 'gigabytes':
quota.name = 'share_gigabytes'
elif quota.name == 'snapshots':
quota.name = 'share_snapshots'
elif quota.name == 'snapshot_gigabytes':
quota.name = 'share_snapshot_gigabytes'
return manila_quotas
else:
return None
@ -141,9 +153,10 @@ def tenant_quota_usages(f, request, tenant_id=None):
sn_l = manila.share_network_list(request)
gig_s = sum([int(v.size) for v in shares])
gig_ss = sum([int(v.size) for v in snapshots])
usages.tally('gigabytes', gig_s + gig_ss)
usages.tally('shares', len(shares))
usages.tally('snapshots', len(snapshots))
usages.tally('share_gigabytes', gig_s)
usages.tally('share_snapshots', len(snapshots))
usages.tally('share_snapshot_gigabytes', gig_ss)
usages.tally('share_networks', len(sn_l))
return usages
@ -159,11 +172,14 @@ def tenant_limit_usages(f, request):
limits.update(manila.tenant_absolute_limits(request))
shares = manila.share_list(request)
snapshots = manila.share_snapshot_list(request)
share_networks = manila.share_network_list(request)
total_s_size = sum([getattr(share, 'size', 0) for share in shares])
total_ss_size = sum([getattr(ss, 'size', 0) for ss in snapshots])
limits['totalGigabytesUsed'] = total_s_size + total_ss_size
limits['totalSharesUsed'] = len(shares)
limits['totalShareGigabytesUsed'] = total_s_size
limits['totalSnapshotsUsed'] = len(snapshots)
limits['totalSnapshotGigabytesUsed'] = total_ss_size
limits['totalShareNetworksUsed'] = len(share_networks)
except Exception:
msg = _("Unable to retrieve share limit information.")
horizon.exceptions.handle(request, msg)
@ -187,11 +203,17 @@ def get_quota_name(f, quota):
class ManilaUpdateDefaultQuotaAction(
default_workflows.UpdateDefaultQuotasAction):
shares = horizon.forms.IntegerField(min_value=-1, label=_("Shares"))
share_networks = horizon.forms.IntegerField(min_value=-1,
label=_("Share Networks"))
share_gigabytes = horizon.forms.IntegerField(
min_value=-1, label=_("Share gigabytes"))
share_snapshots = horizon.forms.IntegerField(
min_value=-1, label=_("Share snapshots"))
share_snapshot_gigabytes = horizon.forms.IntegerField(
min_value=-1, label=_("Share snapshot gigabytes"))
share_networks = horizon.forms.IntegerField(
min_value=-1, label=_("Share Networks"))
class Meta(object):
name = _("Default Quota")
name = _("Default Quotas")
slug = 'update_default_quotas'
help_text = _("From here you can update the default quotas "
"(max limits).")
@ -221,8 +243,8 @@ class ManilaUpdateDefaultQuotas(default_workflows.UpdateDefaultQuotas):
return True
default_views.UpdateDefaultQuotasView.workflow_class = \
ManilaUpdateDefaultQuotas
default_views.UpdateDefaultQuotasView.workflow_class = (
ManilaUpdateDefaultQuotas)
#
# Add manila fields to Identity/Projects/Modify Quotas
@ -232,16 +254,22 @@ default_views.UpdateDefaultQuotasView.workflow_class = \
class ManilaUpdateProjectQuotaAction(
project_workflows.UpdateProjectQuotaAction):
shares = horizon.forms.IntegerField(min_value=-1, label=_("Shares"))
share_networks = horizon.forms.IntegerField(min_value=-1,
label=_("Share Networks"))
share_gigabytes = horizon.forms.IntegerField(
min_value=-1, label=_("Share gigabytes"))
share_snapshots = horizon.forms.IntegerField(
min_value=-1, label=_("Share snapshots"))
share_snapshot_gigabytes = horizon.forms.IntegerField(
min_value=-1, label=_("Share snapshot gigabytes"))
share_networks = horizon.forms.IntegerField(
min_value=-1, label=_("Share Networks"))
class Meta(object):
name = _("Quota")
slug = 'update_quotas'
help_text = _("Set maximum quotas for the project.")
project_workflows.UpdateProjectQuota.action_class = \
ManilaUpdateProjectQuotaAction
project_workflows.UpdateProjectQuota.action_class = (
ManilaUpdateProjectQuotaAction)
project_workflows.UpdateProjectQuota.contributes = quotas.QUOTA_FIELDS
@ -274,16 +302,23 @@ project_views.UpdateProjectView.workflow_class = ManilaUpdateProject
class ManilaCreateProjectQuotaAction(
project_workflows.CreateProjectQuotaAction):
shares = horizon.forms.IntegerField(min_value=-1, label=_("Shares"))
share_networks = horizon.forms.IntegerField(min_value=-1,
label=_("Share Networks"))
share_gigabytes = horizon.forms.IntegerField(
min_value=-1, label=_("Share gigabytes"))
share_snapshots = horizon.forms.IntegerField(
min_value=-1, label=_("Share snapshots"))
share_snapshot_gigabytes = horizon.forms.IntegerField(
min_value=-1, label=_("Share snapshot gigabytes"))
share_networks = horizon.forms.IntegerField(
min_value=-1, label=_("Share Networks"))
class Meta(object):
name = _("Quota")
slug = 'create_quotas'
help_text = _("Set maximum quotas for the project.")
project_workflows.CreateProjectQuota.action_class = \
ManilaCreateProjectQuotaAction
project_workflows.CreateProjectQuota.action_class = (
ManilaCreateProjectQuotaAction)
project_workflows.CreateProjectQuota.contributes = quotas.QUOTA_FIELDS

View File

@ -249,3 +249,68 @@ class ManilaApiTests(base.APITestCase):
(self.manilaclient.shares.migration_get_progress.
assert_called_once_with('fake_share'))
@ddt.data(
({'share_gigabytes': 333}, {'gigabytes': 333}),
({'share_snapshot_gigabytes': 444}, {'snapshot_gigabytes': 444}),
({'share_snapshots': 14}, {'snapshots': 14}),
({'snapshots': 14}, {'snapshots': 14}),
({'gigabytes': 14}, {'gigabytes': 14}),
({'snapshot_gigabytes': 314}, {'snapshot_gigabytes': 314}),
({'shares': 24}, {'shares': 24}),
({'share_networks': 14}, {'share_networks': 14}),
)
@ddt.unpack
def test_tenant_quota_update(self, provided_kwargs, expected_kwargs):
tenant_id = 'fake_tenant_id'
api.tenant_quota_update(self.request, tenant_id, **provided_kwargs)
self.manilaclient.quotas.update.assert_called_once_with(
tenant_id, **expected_kwargs)
self.manilaclient.quota_classes.update.assert_not_called()
@ddt.data(
({'share_gigabytes': 333}, {'gigabytes': 333}),
({'share_snapshot_gigabytes': 444}, {'snapshot_gigabytes': 444}),
({'share_snapshots': 14}, {'snapshots': 14}),
({'snapshots': 14}, {'snapshots': 14}),
({'gigabytes': 14}, {'gigabytes': 14}),
({'snapshot_gigabytes': 314}, {'snapshot_gigabytes': 314}),
({'shares': 24}, {'shares': 24}),
({'share_networks': 14}, {'share_networks': 14}),
)
@ddt.unpack
def test_default_quota_update(self, provided_kwargs, expected_kwargs):
api.default_quota_update(self.request, **provided_kwargs)
self.manilaclient.quota_classes.update.assert_called_once_with(
api.DEFAULT_QUOTA_NAME, **expected_kwargs)
@ddt.data(
{},
{"name": "foo_name"},
{"description": "foo_desc"},
{"neutron_net_id": "foo_neutron_net_id"},
{"neutron_subnet_id": "foo_neutron_subnet_id"},
{"nova_net_id": "foo_nova_net_id"},
{"name": "foo_name", "description": "foo_desc",
"neutron_net_id": "foo_neutron_net_id",
"neutron_subnet_id": "foo_neutron_subnet_id",
"nova_net_id": "foo_nova_net_id"},
)
@ddt.unpack
def test_share_network_create(self, **kwargs):
expected_kwargs = {
"name": None,
"description": None,
"neutron_net_id": None,
"neutron_subnet_id": None,
"nova_net_id": None,
}
expected_kwargs.update(kwargs)
api.share_network_create(self.request, **kwargs)
mock_sn_create = self.manilaclient.share_networks.create
mock_sn_create.assert_called_once_with(**expected_kwargs)

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from django.core.urlresolvers import reverse
from django.utils import translation
import mock
@ -133,3 +134,71 @@ class PieChartsTests(test.TestCase):
"text": chart["text"]},
expected_charts.pop(name, "NotFound")
)
@ddt.ddt
class QuotaTests(test.TestCase):
@ddt.data(
shares.ManilaUpdateDefaultQuotaAction,
shares.ManilaUpdateProjectQuotaAction,
shares.ManilaCreateProjectQuotaAction,
)
def test_manila_quota_action(self, class_ref):
self.mock_object(
quotas, 'get_disabled_quotas', mock.Mock(return_value=[]))
class_instance = class_ref(self.request, 'foo')
expected_fields = set([
'shares', 'share_gigabytes', 'share_snapshots',
'share_snapshot_gigabytes', 'share_networks',
])
# NOTE(vponomaryov): iterate over reversed list of visible fields
# because manila's fields are at the end always.
for vf in reversed(class_instance.visible_fields()):
if expected_fields and vf.name in expected_fields:
self.assertEqual(-1, vf.field.min_value)
self.assertIsInstance(
vf.field, shares.horizon.forms.IntegerField)
expected_fields.remove(vf.name)
self.assertSetEqual(set([]), expected_fields)
self.assertTrue(quotas.get_disabled_quotas.called)
@ddt.data('default_quota_get', 'tenant_quota_get')
def test__get_manila_quota_data(self, method_name):
fake_quotas = [
type('Fake', (object, ), {'name': name})
for name in ('gigabytes', 'snapshots', 'snapshot_gigabytes')
]
self.mock_object(
api_manila, method_name, mock.Mock(return_value=fake_quotas))
self.mock_object(
shares, '_get_manila_disabled_quotas',
mock.Mock(return_value=[]))
result = shares._get_manila_quota_data(
self.request, method_name)
expected = [
'share_gigabytes',
'share_snapshot_gigabytes',
'share_snapshots',
]
self.assertEqual(3, len(result))
self.assertEqual(
expected,
sorted([element.name for element in result]))
getattr(api_manila, method_name).assert_called_once_with(
self.request, self.request.user.tenant_id)
shares._get_manila_disabled_quotas.asssert_called_once_with(
self.request)
def test_manila_quota_fields(self):
expected_fields = (
"shares",
"share_gigabytes",
"share_snapshots",
"share_snapshot_gigabytes",
"share_networks",
)
for ef in expected_fields:
self.assertIn(ef, shares.quotas.QUOTA_FIELDS)

View File

@ -0,0 +1,4 @@
---
fixes:
- Fixed quota names overlapping with Cinder's quotas. Now it is possible
to read/write any manila quotas not messing up Cinder's quotas.