Merge "Fix quotas retrieval for shares and snapshots tables"
This commit is contained in:
commit
01a7b5773a
|
@ -21,7 +21,6 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.translation import ungettext_lazy
|
from django.utils.translation import ungettext_lazy
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
|
|
||||||
from manila_ui.api import manila
|
from manila_ui.api import manila
|
||||||
|
|
||||||
|
@ -57,8 +56,13 @@ class CreateShareSnapshot(tables.LinkAction):
|
||||||
return {"project_id": project_id}
|
return {"project_id": project_id}
|
||||||
|
|
||||||
def allowed(self, request, share=None):
|
def allowed(self, request, share=None):
|
||||||
usages = quotas.tenant_quota_usages(request)
|
usages = manila.tenant_absolute_limits(request)
|
||||||
if usages['snapshots']['available'] <= 0:
|
snapshots_allowed = (usages['maxTotalShareSnapshots'] >
|
||||||
|
usages['totalShareSnapshotsUsed'] and
|
||||||
|
usages['maxTotalSnapshotGigabytes'] >
|
||||||
|
usages['totalSnapshotGigabytesUsed'])
|
||||||
|
|
||||||
|
if not snapshots_allowed:
|
||||||
if "disabled" not in self.classes:
|
if "disabled" not in self.classes:
|
||||||
self.classes = [c for c in self.classes] + ['disabled']
|
self.classes = [c for c in self.classes] + ['disabled']
|
||||||
self.verbose_name = string_concat(
|
self.verbose_name = string_concat(
|
||||||
|
@ -145,6 +149,7 @@ class EditShareSnapshot(tables.LinkAction):
|
||||||
|
|
||||||
|
|
||||||
class ShareSnapshotShareNameColumn(tables.Column):
|
class ShareSnapshotShareNameColumn(tables.Column):
|
||||||
|
|
||||||
def get_link_url(self, snapshot):
|
def get_link_url(self, snapshot):
|
||||||
return reverse(self.link, args=(snapshot.share_id,))
|
return reverse(self.link, args=(snapshot.share_id,))
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ from django.utils.translation import ungettext_lazy
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import messages
|
from horizon import messages
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
|
|
||||||
from manila_ui.api import manila
|
from manila_ui.api import manila
|
||||||
from manila_ui.dashboards.project.share_snapshots import tables as ss_tables
|
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"),)
|
policy_rules = (("share", "share:create"),)
|
||||||
|
|
||||||
def allowed(self, request, share=None):
|
def allowed(self, request, share=None):
|
||||||
usages = quotas.tenant_quota_usages(request)
|
usages = manila.tenant_absolute_limits(request)
|
||||||
if usages['shares']['available'] <= 0:
|
shares_allowed = (usages['maxTotalShares'] >
|
||||||
|
usages['totalSharesUsed'] and
|
||||||
|
usages['maxTotalShareGigabytes'] >
|
||||||
|
usages['totalShareGigabytesUsed'])
|
||||||
|
|
||||||
|
if not shares_allowed:
|
||||||
if "disabled" not in self.classes:
|
if "disabled" not in self.classes:
|
||||||
self.classes = [c for c in self.classes] + ['disabled']
|
self.classes = [c for c in self.classes] + ['disabled']
|
||||||
self.verbose_name = string_concat(self.verbose_name, ' ',
|
self.verbose_name = string_concat(self.verbose_name, ' ',
|
||||||
|
@ -97,6 +101,7 @@ class CreateShare(tables.LinkAction):
|
||||||
self.verbose_name = _("Create Share")
|
self.verbose_name = _("Create Share")
|
||||||
classes = [c for c in self.classes if c != "disabled"]
|
classes = [c for c in self.classes if c != "disabled"]
|
||||||
self.classes = classes
|
self.classes = classes
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ from django.urls import reverse
|
||||||
import mock
|
import mock
|
||||||
from openstack_dashboard.api import keystone as api_keystone
|
from openstack_dashboard.api import keystone as api_keystone
|
||||||
from openstack_dashboard.api import neutron as api_neutron
|
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.api import manila as api_manila
|
||||||
from manila_ui.dashboards.admin import utils
|
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))
|
api_manila, "share_snapshot_list", mock.Mock(return_value=snaps))
|
||||||
self.mock_object(
|
self.mock_object(
|
||||||
api_neutron, "is_service_enabled", mock.Mock(return_value=[True]))
|
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)
|
res = self.client.get(INDEX_URL)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@ import ddt
|
||||||
from django.core.handlers import wsgi
|
from django.core.handlers import wsgi
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from manila_ui.api import manila as api_manila
|
||||||
from manila_ui.dashboards.project.share_snapshots import tables
|
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
|
from manila_ui.tests import helpers as base
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,23 +38,38 @@ class CreateSnapshotTests(base.APITestCase):
|
||||||
return type("Share", (object, ), kwargs)()
|
return type("Share", (object, ), kwargs)()
|
||||||
|
|
||||||
@ddt.data(True, False)
|
@ddt.data(True, False)
|
||||||
@mock.patch('openstack_dashboard.usage.quotas.tenant_quota_usages')
|
def test_allowed_with_snapshot_support_attr(self, snapshot_support):
|
||||||
def test_allowed_with_snapshot_support_attr(self, snapshot_support,
|
self.mock_object(
|
||||||
mock_quota_usages):
|
api_manila, "tenant_absolute_limits",
|
||||||
mock_quota_usages.return_value = {'snapshots': {'available': 1}}
|
mock.Mock(return_value=test_data.limits))
|
||||||
|
|
||||||
share = self._get_fake_share(snapshot_support=snapshot_support)
|
share = self._get_fake_share(snapshot_support=snapshot_support)
|
||||||
|
|
||||||
result = self.create_snapshot.allowed(self.request, share)
|
result = self.create_snapshot.allowed(self.request, share)
|
||||||
|
|
||||||
self.assertEqual(snapshot_support, result)
|
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):
|
||||||
def test_allowed_no_snapshot_support_attr(self, mock_quota_usages):
|
self.mock_object(
|
||||||
mock_quota_usages.return_value = {'snapshots': {'available': 1}}
|
api_manila, "tenant_absolute_limits",
|
||||||
|
mock.Mock(return_value=test_data.limits))
|
||||||
share = self._get_fake_share()
|
share = self._get_fake_share()
|
||||||
|
|
||||||
result = self.create_snapshot.allowed(self.request, 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)
|
self.assertTrue(result)
|
||||||
mock_quota_usages.assert_called_once_with(self.request)
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ from django.urls import reverse
|
||||||
from horizon import messages as horizon_messages
|
from horizon import messages as horizon_messages
|
||||||
import mock
|
import mock
|
||||||
from openstack_dashboard.api import neutron
|
from openstack_dashboard.api import neutron
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from manila_ui.api import manila as api_manila
|
from manila_ui.api import manila as api_manila
|
||||||
|
@ -55,6 +54,9 @@ class ShareViewTests(test.APITestCase):
|
||||||
self.mock_object(horizon_messages, "success")
|
self.mock_object(horizon_messages, "success")
|
||||||
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
|
FAKE_ENVIRON = {'REQUEST_METHOD': 'GET', 'wsgi.input': 'fake_input'}
|
||||||
self.request = wsgi.WSGIRequest(FAKE_ENVIRON)
|
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):
|
def test_index(self):
|
||||||
snaps = [test_data.snapshot, test_data.snapshot_mount_support]
|
snaps = [test_data.snapshot, test_data.snapshot_mount_support]
|
||||||
|
@ -70,10 +72,10 @@ class ShareViewTests(test.APITestCase):
|
||||||
api_manila, "share_network_list",
|
api_manila, "share_network_list",
|
||||||
mock.Mock(return_value=share_networks))
|
mock.Mock(return_value=share_networks))
|
||||||
self.mock_object(
|
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(
|
self.mock_object(
|
||||||
quotas, "tenant_quota_usages",
|
neutron, "is_service_enabled", mock.Mock(return_value=[True]))
|
||||||
mock.Mock(return_value=test_data.quota_usage))
|
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
|
|
||||||
|
@ -83,6 +85,7 @@ class ShareViewTests(test.APITestCase):
|
||||||
mock.ANY, detailed=True)
|
mock.ANY, detailed=True)
|
||||||
api_manila.share_list.assert_called_with(mock.ANY)
|
api_manila.share_list.assert_called_with(mock.ANY)
|
||||||
api_manila.share_network_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')
|
@mock.patch.object(api_manila, 'availability_zone_list')
|
||||||
def test_create_share(self, az_list):
|
def test_create_share(self, az_list):
|
||||||
|
@ -354,14 +357,7 @@ class ShareViewTests(test.APITestCase):
|
||||||
|
|
||||||
def test_extend_share_get(self):
|
def test_extend_share_get(self):
|
||||||
share = test_data.share
|
share = test_data.share
|
||||||
usage_limit = {
|
|
||||||
'maxTotalShareGigabytes': 250,
|
|
||||||
'totalShareGigabytesUsed': 20,
|
|
||||||
}
|
|
||||||
url = reverse('horizon:project:shares:extend', args=[share.id])
|
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(
|
self.mock_object(
|
||||||
neutron, "is_service_enabled", mock.Mock(return_value=[True]))
|
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):
|
def test_extend_share_open_form_successfully(self):
|
||||||
self.share.size = 5
|
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])
|
url = reverse('horizon:project:shares:extend', args=[self.share.id])
|
||||||
self.mock_object(api_manila, "share_extend")
|
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)
|
response = self.client.get(url)
|
||||||
|
|
||||||
|
@ -392,15 +381,8 @@ class ShareViewTests(test.APITestCase):
|
||||||
api_manila.tenant_absolute_limits.assert_called_once_with(mock.ANY)
|
api_manila.tenant_absolute_limits.assert_called_once_with(mock.ANY)
|
||||||
|
|
||||||
def test_extend_share_get_with_api_exception(self):
|
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])
|
url = reverse('horizon:project:shares:extend', args=[self.share.id])
|
||||||
self.mock_object(api_manila, "share_extend")
|
self.mock_object(api_manila, "share_extend")
|
||||||
self.mock_object(
|
|
||||||
api_manila, 'tenant_absolute_limits',
|
|
||||||
mock.Mock(return_value=usage_limit))
|
|
||||||
self.mock_object(
|
self.mock_object(
|
||||||
api_manila, "share_get",
|
api_manila, "share_get",
|
||||||
mock.Mock(return_value=Exception('Fake share NotFound exception')))
|
mock.Mock(return_value=Exception('Fake share NotFound exception')))
|
||||||
|
@ -421,6 +403,7 @@ class ShareViewTests(test.APITestCase):
|
||||||
usage_limit = {
|
usage_limit = {
|
||||||
'maxTotalShareGigabytes': self.share.size + 50,
|
'maxTotalShareGigabytes': self.share.size + 50,
|
||||||
'totalShareGigabytesUsed': self.share.size,
|
'totalShareGigabytesUsed': self.share.size,
|
||||||
|
|
||||||
}
|
}
|
||||||
url = reverse('horizon:project:shares:extend', args=[self.share.id])
|
url = reverse('horizon:project:shares:extend', args=[self.share.id])
|
||||||
self.mock_object(api_manila, "share_extend")
|
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):
|
def test_extend_share_post_with_invalid_value(self, new_size):
|
||||||
self.share.size = 5
|
self.share.size = 5
|
||||||
form_data = {'new_size': new_size}
|
form_data = {'new_size': new_size}
|
||||||
|
url = reverse('horizon:project:shares:extend', args=[self.share.id])
|
||||||
usage_limit = {
|
usage_limit = {
|
||||||
'maxTotalShareGigabytes': self.share.size + 50,
|
'maxTotalShareGigabytes': self.share.size + 50,
|
||||||
'totalShareGigabytesUsed': self.share.size,
|
'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, "share_extend")
|
||||||
self.mock_object(
|
self.mock_object(
|
||||||
api_manila, 'tenant_absolute_limits',
|
api_manila, 'tenant_absolute_limits',
|
||||||
|
@ -464,17 +447,10 @@ class ShareViewTests(test.APITestCase):
|
||||||
def test_extend_share_post_with_api_exception(self):
|
def test_extend_share_post_with_api_exception(self):
|
||||||
self.share.size = 5
|
self.share.size = 5
|
||||||
form_data = {'new_size': 30}
|
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])
|
url = reverse('horizon:project:shares:extend', args=[self.share.id])
|
||||||
self.mock_object(
|
self.mock_object(
|
||||||
api_manila, "share_extend",
|
api_manila, "share_extend",
|
||||||
mock.Mock(return_value=Exception('Fake API exception')))
|
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)
|
response = self.client.post(url, form_data)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from manilaclient.v2 import quotas
|
|
||||||
from manilaclient.v2 import security_services
|
from manilaclient.v2 import security_services
|
||||||
from manilaclient.v2 import share_export_locations
|
from manilaclient.v2 import share_export_locations
|
||||||
from manilaclient.v2 import share_group_snapshots
|
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 share_types
|
||||||
from manilaclient.v2 import shares
|
from manilaclient.v2 import shares
|
||||||
|
|
||||||
from openstack_dashboard import api
|
|
||||||
from openstack_dashboard.usage import quotas as usage_quotas
|
|
||||||
|
|
||||||
|
|
||||||
class FakeAPIClient(object):
|
class FakeAPIClient(object):
|
||||||
client = "fake_client"
|
client = "fake_client"
|
||||||
|
@ -474,24 +470,23 @@ share_group_snapshot_nameless = share_group_snapshots.ShareGroupSnapshot(
|
||||||
'members': []}
|
'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
|
# Manila Limits
|
||||||
limits = {"absolute": {"totalSharesUsed": 1,
|
limits = {"totalSharesUsed": 1,
|
||||||
"totalShareGigabytesUsed": 5,
|
"totalShareSnapshotsUsed": 1,
|
||||||
"maxTotalShareGigabytes": 1000,
|
"totalShareGigabytesUsed": 500,
|
||||||
"maxTotalShares": 10}}
|
"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,
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fixed missing "Create Share" button on the "Shares" dashboard.
|
Loading…
Reference in New Issue