Tabbify quota default panel

Part of blueprint horizon-plugin-tab-for-info-and-quotas
Change-Id: I71e23835161244b2173a6444cc00d94639df2f1b
This commit is contained in:
Akihiro Motoki 2018-01-15 07:07:26 +09:00
parent d09170e02a
commit a965f42c7b
3 changed files with 161 additions and 60 deletions

View File

@ -42,9 +42,18 @@ class UpdateDefaultQuotas(tables.LinkAction):
return quotas.enabled_quotas(request)
def get_quota_name(quota):
class UpdateDefaultComputeQuotas(UpdateDefaultQuotas):
name = 'update_compute_defaults'
step = 'update_default_compute_quotas'
class UpdateDefaultVolumeQuotas(UpdateDefaultQuotas):
name = 'update_volume_defaults'
step = 'update_default_volume_quotas'
def get_compute_quota_name(quota):
QUOTA_NAMES = {
# Nova
'injected_file_content_bytes': _('Injected File Content Bytes'),
'injected_file_path_bytes': _('Length of Injected File Path'),
'metadata_items': _('Metadata Items'),
@ -55,7 +64,26 @@ def get_quota_name(quota):
'key_pairs': _('Key Pairs'),
'server_group_members': _('Server Group Members'),
'server_groups': _('Server Groups'),
# Cinder
}
return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title())
class ComputeQuotasTable(tables.DataTable):
name = tables.Column(get_compute_quota_name, verbose_name=_('Quota Name'))
limit = tables.Column("limit", verbose_name=_('Limit'))
def get_object_id(self, obj):
return obj.name
class Meta(object):
name = "compute_quotas"
verbose_name = _("Compute Quotas")
table_actions = (QuotaFilterAction, UpdateDefaultComputeQuotas)
multi_select = False
def get_volume_quota_name(quota):
QUOTA_NAMES = {
'volumes': _('Volumes'),
'snapshots': _('Volume Snapshots'),
'backups': _('Backups'),
@ -64,14 +92,6 @@ def get_quota_name(quota):
'per_volume_gigabytes': _('Per Volume Size (GiB)'),
'groups': _('Volume Groups'),
'dm-crypt': _('dm-crypt'),
# Neutron
'network': _('Networks'),
'subnet': _('Subnets'),
'port': _('Ports'),
'router': _('Routers'),
'floatingip': _('Floating IPs'),
'security_group': _('Security Groups'),
'security_group_rule': _('Security Group Rules'),
}
QUOTA_DYNAMIC_NAMES = {
@ -89,15 +109,45 @@ def get_quota_name(quota):
return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title())
class QuotasTable(tables.DataTable):
name = tables.Column(get_quota_name, verbose_name=_('Quota Name'))
class VolumeQuotasTable(tables.DataTable):
name = tables.Column(get_volume_quota_name, verbose_name=_('Quota Name'))
limit = tables.Column("limit", verbose_name=_('Limit'))
def get_object_id(self, obj):
return obj.name
class Meta(object):
name = "quotas"
verbose_name = _("Quotas")
name = "volume_quotas"
verbose_name = _("Volume Quotas")
table_actions = (QuotaFilterAction, UpdateDefaultQuotas)
multi_select = False
def get_network_quota_name(quota):
QUOTA_NAMES = {
'network': _('Networks'),
'subnet': _('Subnets'),
'port': _('Ports'),
'subnetpool': _('Subnet Pool'),
'router': _('Routers'),
'floatingip': _('Floating IPs'),
'security_group': _('Security Groups'),
'security_group_rule': _('Security Group Rules'),
'rbac_policy': _('RBAC Policies'),
'trunk': _('Trunks'),
}
return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title())
class NetworkQuotasTable(tables.DataTable):
name = tables.Column(get_network_quota_name, verbose_name=_('Quota Name'))
limit = tables.Column("limit", verbose_name=_('Limit'))
def get_object_id(self, obj):
return obj.name
class Meta(object):
name = "network_quotas"
verbose_name = _("Network Quotas")
table_actions = (QuotaFilterAction,)
multi_select = False

View File

@ -17,28 +17,73 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tabs
from openstack_dashboard.usage import quotas
from openstack_dashboard import api
from openstack_dashboard.dashboards.admin.defaults import tables
class DefaultQuotasTab(tabs.TableTab):
table_classes = (tables.QuotasTable,)
name = _("Default Quotas")
slug = "quotas"
class ComputeQuotasTab(tabs.TableTab):
table_classes = (tables.ComputeQuotasTable,)
name = _("Compute Quotas")
slug = "compute_quotas"
template_name = ("horizon/common/_detail_table.html")
def get_quotas_data(self):
def get_compute_quotas_data(self):
request = self.tab_group.request
tenant_id = request.user.tenant_id
try:
data = quotas.get_default_quota_data(request)
data = api.nova.default_quota_get(request, tenant_id)
except Exception:
data = []
exceptions.handle(self.request, _('Unable to get quota info.'))
exceptions.handle(self.request,
_('Unable to get nova quota info.'))
return data
def allowed(self, request):
return api.base.is_service_enabled(request, 'compute')
class VolumeQuotasTab(tabs.TableTab):
table_classes = (tables.VolumeQuotasTable,)
name = _("Volume Quotas")
slug = "volume_quotas"
template_name = ("horizon/common/_detail_table.html")
def get_volume_quotas_data(self):
request = self.tab_group.request
tenant_id = request.user.tenant_id
try:
data = api.cinder.default_quota_get(request, tenant_id)
except Exception:
data = []
exceptions.handle(self.request,
_('Unable to get cinder quota info.'))
return data
def allowed(self, request):
return api.cinder.is_volume_service_enabled(request)
class NetworkQuotasTab(tabs.TableTab):
table_classes = (tables.NetworkQuotasTable,)
name = _("Network Quotas")
slug = "network_quotas"
template_name = ("horizon/common/_detail_table.html")
def get_network_quotas_data(self):
request = self.tab_group.request
try:
data = api.neutron.default_quota_get(request)
except Exception:
data = []
exceptions.handle(self.request,
_('Unable to get neutron quota info.'))
return data
def allowed(self, request):
return api.base.is_service_enabled(request, 'network')
class DefaultsTabs(tabs.TabGroup):
slug = "defaults"
tabs = (DefaultQuotasTab,)
tabs = (ComputeQuotasTab, VolumeQuotasTab, NetworkQuotasTab)
sticky = True

View File

@ -25,16 +25,6 @@ INDEX_URL = reverse('horizon:admin:defaults:index')
class ServicesViewTests(test.BaseAdminViewTests):
def test_index(self):
self._test_index(neutron_enabled=True)
def test_index_with_neutron_disabled(self):
self._test_index(neutron_enabled=False)
def test_index_with_neutron_sg_disabled(self):
self._test_index(neutron_enabled=True,
neutron_sg_enabled=False)
def _test_index(self, neutron_enabled=True, neutron_sg_enabled=True):
# Neutron does not have an API for getting default system
# quotas. When not using Neutron, the floating ips quotas
# should be in the list.
@ -42,27 +32,24 @@ class ServicesViewTests(test.BaseAdminViewTests):
self.mox.StubOutWithMock(api.cinder, 'default_quota_get')
self.mox.StubOutWithMock(api.cinder, 'is_volume_service_enabled')
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
if neutron_enabled:
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
self.mox.StubOutWithMock(api.neutron, 'is_router_enabled')
self.mox.StubOutWithMock(api.neutron, 'default_quota_get')
self.mox.StubOutWithMock(quotas, 'enabled_quotas')
api.cinder.is_volume_service_enabled(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \
.MultipleTimes().AndReturn(True)
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
.MultipleTimes().AndReturn(neutron_enabled)
.MultipleTimes().AndReturn(True)
compute_quotas = [q.name for q in self.quotas.nova]
quotas.enabled_quotas(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(compute_quotas)
api.nova.default_quota_get(IsA(http.HttpRequest),
self.tenant.id).AndReturn(self.quotas.nova)
api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id) \
.AndReturn(self.cinder_quotas.first())
if neutron_enabled:
api.neutron.is_extension_supported(
IsA(http.HttpRequest),
'security-group').MultipleTimes().AndReturn(neutron_sg_enabled)
api.neutron.is_router_enabled(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
api.neutron.default_quota_get(
IsA(http.HttpRequest)).AndReturn(self.neutron_quotas.first())
self.mox.ReplayAll()
@ -70,21 +57,40 @@ class ServicesViewTests(test.BaseAdminViewTests):
self.assertTemplateUsed(res, 'admin/defaults/index.html')
quotas_tab = res.context['tab_group'].get_tab('quotas')
expected_tabs = ['<Quota: (injected_file_content_bytes, 1)>',
expected_data = [
'<Quota: (injected_file_content_bytes, 1)>',
'<Quota: (metadata_items, 1)>',
'<Quota: (injected_files, 1)>',
'<Quota: (gigabytes, 1000)>',
'<Quota: (ram, 10000)>',
'<Quota: (instances, 10)>',
'<Quota: (snapshots, 1)>',
'<Quota: (volumes, 1)>',
'<Quota: (cores, 10)>',
'<Quota: (key_pairs, 100)>',
'<Quota: (injected_file_path_bytes, 255)>']
'<Quota: (injected_file_path_bytes, 255)>',
]
self._check_quotas_data(res, 'compute_quotas', expected_data)
self.assertQuerysetEqual(quotas_tab._tables['quotas'].data,
expected_tabs,
expected_data = [
'<Quota: (gigabytes, 1000)>',
'<Quota: (snapshots, 1)>',
'<Quota: (volumes, 1)>',
]
self._check_quotas_data(res, 'volume_quotas', expected_data)
expected_data = [
'<Quota: (network, 10)>',
'<Quota: (subnet, 10)>',
'<Quota: (port, 50)>',
'<Quota: (router, 10)>',
'<Quota: (floatingip, 50)>',
'<Quota: (security_group, 20)>',
'<Quota: (security_group_rule, 100)>',
]
self._check_quotas_data(res, 'network_quotas', expected_data)
def _check_quotas_data(self, res, slug, expected_data):
quotas_tab = res.context['tab_group'].get_tab(slug)
self.assertQuerysetEqual(quotas_tab._tables[slug].data,
expected_data,
ordered=False)