Fix quotas retrieval for shares and snapshots tables

After refactoring manila-ui plugin to adapt
to the tabbed quotas interface in horizon,
we stopped accesing the usages data structure
which (in horizon side) carries quotas information.
In this patch set we perform quotas control by
querying manilaclient directly.

Change-Id: I2896d54fde0d5e3975afbb7274faadf2a1a81d7c
Closes-Bug: #1783232
Co-Authored-By: Tom Barron <tpb@dyncloud.net>
This commit is contained in:
Victoria Martinez de la Cruz 2018-07-30 23:38:48 -03:00
parent e89a96d412
commit 1ed6c85a86
7 changed files with 73 additions and 76 deletions

View File

@ -21,7 +21,6 @@ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import tables
from openstack_dashboard.usage import quotas
from manila_ui.api import manila
@ -57,8 +56,13 @@ class CreateShareSnapshot(tables.LinkAction):
return {"project_id": project_id}
def allowed(self, request, share=None):
usages = quotas.tenant_quota_usages(request)
if usages['snapshots']['available'] <= 0:
usages = manila.tenant_absolute_limits(request)
snapshots_allowed = (usages['maxTotalShareSnapshots'] >
usages['totalShareSnapshotsUsed'] and
usages['maxTotalSnapshotGigabytes'] >
usages['totalSnapshotGigabytesUsed'])
if not snapshots_allowed:
if "disabled" not in self.classes:
self.classes = [c for c in self.classes] + ['disabled']
self.verbose_name = string_concat(
@ -145,6 +149,7 @@ class EditShareSnapshot(tables.LinkAction):
class ShareSnapshotShareNameColumn(tables.Column):
def get_link_url(self, snapshot):
return reverse(self.link, args=(snapshot.share_id,))

View File

@ -21,7 +21,6 @@ from django.utils.translation import ungettext_lazy
from horizon import exceptions
from horizon import messages
from horizon import tables
from openstack_dashboard.usage import quotas
from manila_ui.api import manila
from manila_ui.dashboards.project.share_snapshots import tables as ss_tables
@ -87,8 +86,13 @@ class CreateShare(tables.LinkAction):
policy_rules = (("share", "share:create"),)
def allowed(self, request, share=None):
usages = quotas.tenant_quota_usages(request)
if usages['shares']['available'] <= 0:
usages = manila.tenant_absolute_limits(request)
shares_allowed = (usages['maxTotalShares'] >
usages['totalSharesUsed'] and
usages['maxTotalShareGigabytes'] >
usages['totalShareGigabytesUsed'])
if not shares_allowed:
if "disabled" not in self.classes:
self.classes = [c for c in self.classes] + ['disabled']
self.verbose_name = string_concat(self.verbose_name, ' ',
@ -97,6 +101,7 @@ class CreateShare(tables.LinkAction):
self.verbose_name = _("Create Share")
classes = [c for c in self.classes if c != "disabled"]
self.classes = classes
return True

View File

@ -18,7 +18,6 @@ from django.urls import reverse
import mock
from openstack_dashboard.api import keystone as api_keystone
from openstack_dashboard.api import neutron as api_neutron
from openstack_dashboard.usage import quotas
from manila_ui.api import manila as api_manila
from manila_ui.dashboards.admin import utils
@ -53,9 +52,6 @@ class SharesTests(test.BaseAdminViewTests):
api_manila, "share_snapshot_list", mock.Mock(return_value=snaps))
self.mock_object(
api_neutron, "is_service_enabled", mock.Mock(return_value=[True]))
self.mock_object(
quotas, "tenant_quota_usages",
mock.Mock(return_value=test_data.quota_usage))
res = self.client.get(INDEX_URL)

View File

@ -17,7 +17,9 @@ import ddt
from django.core.handlers import wsgi
import mock
from manila_ui.api import manila as api_manila
from manila_ui.dashboards.project.share_snapshots import tables
from manila_ui.tests.dashboards.project import test_data
from manila_ui.tests import helpers as base
@ -36,23 +38,38 @@ class CreateSnapshotTests(base.APITestCase):
return type("Share", (object, ), kwargs)()
@ddt.data(True, False)
@mock.patch('openstack_dashboard.usage.quotas.tenant_quota_usages')
def test_allowed_with_snapshot_support_attr(self, snapshot_support,
mock_quota_usages):
mock_quota_usages.return_value = {'snapshots': {'available': 1}}
def test_allowed_with_snapshot_support_attr(self, snapshot_support):
self.mock_object(
api_manila, "tenant_absolute_limits",
mock.Mock(return_value=test_data.limits))
share = self._get_fake_share(snapshot_support=snapshot_support)
result = self.create_snapshot.allowed(self.request, share)
self.assertEqual(snapshot_support, result)
mock_quota_usages.assert_called_once_with(self.request)
@mock.patch('openstack_dashboard.usage.quotas.tenant_quota_usages')
def test_allowed_no_snapshot_support_attr(self, mock_quota_usages):
mock_quota_usages.return_value = {'snapshots': {'available': 1}}
def test_allowed_no_snapshot_support_attr(self):
self.mock_object(
api_manila, "tenant_absolute_limits",
mock.Mock(return_value=test_data.limits))
share = self._get_fake_share()
result = self.create_snapshot.allowed(self.request, share)
self.assertNotIn('disabled', self.create_snapshot.classes)
self.assertTrue(result)
def test_allowed_no_snapshot_support_attr_no_quota(self):
self.mock_object(
api_manila, "tenant_absolute_limits",
mock.Mock(return_value=test_data.limits_negative))
share = self._get_fake_share()
result = self.create_snapshot.allowed(self.request, share)
self.assertIn('disabled', self.create_snapshot.classes)
self.assertTrue(result)
mock_quota_usages.assert_called_once_with(self.request)

View File

@ -18,7 +18,6 @@ from django.urls import reverse
from horizon import messages as horizon_messages
import mock
from openstack_dashboard.api import neutron
from openstack_dashboard.usage import quotas
import six
from manila_ui.api import manila as api_manila
@ -55,6 +54,9 @@ class ShareViewTests(test.APITestCase):
self.mock_object(horizon_messages, "success")
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
self.request = wsgi.WSGIRequest(FAKE_ENVIRON)
self.mock_object(
api_manila, "tenant_absolute_limits",
mock.Mock(return_value=test_data.limits))
def test_index(self):
snaps = [test_data.snapshot, test_data.snapshot_mount_support]
@ -70,10 +72,10 @@ class ShareViewTests(test.APITestCase):
api_manila, "share_network_list",
mock.Mock(return_value=share_networks))
self.mock_object(
neutron, "is_service_enabled", mock.Mock(return_value=[True]))
api_manila, "tenant_absolute_limits",
mock.Mock(return_value=test_data.limits))
self.mock_object(
quotas, "tenant_quota_usages",
mock.Mock(return_value=test_data.quota_usage))
neutron, "is_service_enabled", mock.Mock(return_value=[True]))
res = self.client.get(INDEX_URL)
@ -83,6 +85,7 @@ class ShareViewTests(test.APITestCase):
mock.ANY, detailed=True)
api_manila.share_list.assert_called_with(mock.ANY)
api_manila.share_network_list.assert_called_with(mock.ANY)
api_manila.tenant_absolute_limits.assert_called_with(mock.ANY)
@mock.patch.object(api_manila, 'availability_zone_list')
def test_create_share(self, az_list):
@ -354,14 +357,7 @@ class ShareViewTests(test.APITestCase):
def test_extend_share_get(self):
share = test_data.share
usage_limit = {
'maxTotalShareGigabytes': 250,
'totalShareGigabytesUsed': 20,
}
url = reverse('horizon:project:shares:extend', args=[share.id])
self.mock_object(
api_manila, "tenant_absolute_limits",
mock.Mock(return_value=usage_limit))
self.mock_object(
neutron, "is_service_enabled", mock.Mock(return_value=[True]))
@ -373,15 +369,8 @@ class ShareViewTests(test.APITestCase):
def test_extend_share_open_form_successfully(self):
self.share.size = 5
usage_limit = {
'maxTotalShareGigabytes': self.share.size + 50,
'totalShareGigabytesUsed': self.share.size,
}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(api_manila, "share_extend")
self.mock_object(
api_manila, 'tenant_absolute_limits',
mock.Mock(return_value=usage_limit))
response = self.client.get(url)
@ -392,15 +381,8 @@ class ShareViewTests(test.APITestCase):
api_manila.tenant_absolute_limits.assert_called_once_with(mock.ANY)
def test_extend_share_get_with_api_exception(self):
usage_limit = {
'maxTotalShareGigabytes': self.share.size + 50,
'totalShareGigabytesUsed': self.share.size,
}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(api_manila, "share_extend")
self.mock_object(
api_manila, 'tenant_absolute_limits',
mock.Mock(return_value=usage_limit))
self.mock_object(
api_manila, "share_get",
mock.Mock(return_value=Exception('Fake share NotFound exception')))
@ -421,6 +403,7 @@ class ShareViewTests(test.APITestCase):
usage_limit = {
'maxTotalShareGigabytes': self.share.size + 50,
'totalShareGigabytesUsed': self.share.size,
}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(api_manila, "share_extend")
@ -443,11 +426,11 @@ class ShareViewTests(test.APITestCase):
def test_extend_share_post_with_invalid_value(self, new_size):
self.share.size = 5
form_data = {'new_size': new_size}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
usage_limit = {
'maxTotalShareGigabytes': self.share.size + 50,
'totalShareGigabytesUsed': self.share.size,
}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(api_manila, "share_extend")
self.mock_object(
api_manila, 'tenant_absolute_limits',
@ -464,17 +447,10 @@ class ShareViewTests(test.APITestCase):
def test_extend_share_post_with_api_exception(self):
self.share.size = 5
form_data = {'new_size': 30}
usage_limit = {
'maxTotalShareGigabytes': self.share.size + 50,
'totalShareGigabytesUsed': self.share.size,
}
url = reverse('horizon:project:shares:extend', args=[self.share.id])
self.mock_object(
api_manila, "share_extend",
mock.Mock(return_value=Exception('Fake API exception')))
self.mock_object(
api_manila, 'tenant_absolute_limits',
mock.Mock(return_value=usage_limit))
response = self.client.post(url, form_data)

View File

@ -13,7 +13,6 @@
# under the License.
import collections
from manilaclient.v2 import quotas
from manilaclient.v2 import security_services
from manilaclient.v2 import share_export_locations
from manilaclient.v2 import share_group_snapshots
@ -28,9 +27,6 @@ from manilaclient.v2 import share_snapshots
from manilaclient.v2 import share_types
from manilaclient.v2 import shares
from openstack_dashboard import api
from openstack_dashboard.usage import quotas as usage_quotas
class FakeAPIClient(object):
client = "fake_client"
@ -474,24 +470,23 @@ share_group_snapshot_nameless = share_group_snapshots.ShareGroupSnapshot(
'members': []}
)
# Quota Sets
quota_data = dict(shares='1',
share_snapshots='1',
share_gigabytes='1000')
quota = quotas.QuotaSet(quotas.QuotaSetManager(FakeAPIClient), quota_data)
# Quota Usages
quota_usage_data = {'gigabytes': {'used': 0, 'quota': 1000},
'shares': {'used': 0, 'quota': 10},
'snapshots': {'used': 0, 'quota': 10},
'share_networks': {'used': 0, 'quota': 10}}
quota_usage = usage_quotas.QuotaUsage()
for k, v in quota_usage_data.items():
quota_usage.add_quota(api.base.Quota(k, v['quota']))
quota_usage.tally(k, v['used'])
# Manila Limits
limits = {"absolute": {"totalSharesUsed": 1,
"totalShareGigabytesUsed": 5,
"maxTotalShareGigabytes": 1000,
"maxTotalShares": 10}}
limits = {"totalSharesUsed": 1,
"totalShareSnapshotsUsed": 1,
"totalShareGigabytesUsed": 500,
"totalSnapshotGigabytesUsed": 500,
"maxTotalShares": 10,
"maxTotalShareSnapshots": 10,
"maxTotalShareGigabytes": 1000,
"maxTotalSnapshotGigabytes": 1000,
}
limits_negative = {"totalSharesUsed": 10,
"totalShareSnapshotsUsed": 10,
"totalShareGigabytesUsed": 1000,
"totalSnapshotGigabytesUsed": 1000,
"maxTotalShares": 10,
"maxTotalShareSnapshots": 10,
"maxTotalShareGigabytes": 1000,
"maxTotalSnapshotGigabytes": 1000,
}

View File

@ -0,0 +1,3 @@
fixes:
- |
Fixed missing "Create Share" button on the "Shares" dashboard.