usage: split out the limit related logic into ProjectUsageView
Previously limit information was always retrieved but it is not used in the admin overview. This commit moves the limit related logic into a separate class ProjectUsageView. testing of usage.views is covered by individual panel tests, so UTs for panels which uses UsageView rather than the new ProjectUsageView need to be updated in this commit. blueprint make-quotas-great-again Change-Id: Id93b66d11e3b0c98d1fbb0454bb43984c2999d92
This commit is contained in:
parent
794fec40f9
commit
788388c486
@ -204,26 +204,8 @@ class AggregatesViewTests(test.BaseAdminViewTests):
|
||||
|
||||
@mock.patch('openstack_dashboard.api.nova.extension_supported',
|
||||
mock.Mock(return_value=False))
|
||||
@test.create_stubs({api.nova: ('aggregate_details_list',
|
||||
'availability_zone_list',
|
||||
'tenant_absolute_limits',),
|
||||
api.cinder: ('tenant_absolute_limits',),
|
||||
api.neutron: ('is_extension_supported',
|
||||
'tenant_floating_ip_list',
|
||||
'security_group_list'),
|
||||
api.keystone: ('tenant_list',)})
|
||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||
def test_panel_not_available(self):
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)). \
|
||||
MultipleTimes().AndReturn(self.limits['absolute'])
|
||||
api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)). \
|
||||
MultipleTimes().AndReturn(self.cinder_limits['absolute'])
|
||||
api.neutron.\
|
||||
is_extension_supported(IsA(http.HttpRequest), 'security-group'). \
|
||||
MultipleTimes().AndReturn(True)
|
||||
api.neutron.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.floating_ips.list())
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.tenants.list())
|
||||
self.mox.ReplayAll()
|
||||
|
@ -40,14 +40,8 @@ class UsageViewTests(test.BaseAdminViewTests):
|
||||
|
||||
def _stub_api_calls(self, nova_stu_enabled):
|
||||
self.mox.StubOutWithMock(api.nova, 'usage_list')
|
||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||
self.mox.StubOutWithMock(api.nova, 'extension_supported')
|
||||
self.mox.StubOutWithMock(api.keystone, 'tenant_list')
|
||||
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
|
||||
self.mox.StubOutWithMock(api.neutron, 'floating_ip_supported')
|
||||
self.mox.StubOutWithMock(api.neutron, 'tenant_floating_ip_list')
|
||||
self.mox.StubOutWithMock(api.neutron, 'security_group_list')
|
||||
self.mox.StubOutWithMock(api.cinder, 'tenant_absolute_limits')
|
||||
|
||||
api.nova.extension_supported(
|
||||
'SimpleTenantUsage', IsA(http.HttpRequest)) \
|
||||
@ -99,18 +93,6 @@ class UsageViewTests(test.BaseAdminViewTests):
|
||||
now.month,
|
||||
now.day, 23, 59, 59, 0)) \
|
||||
.AndReturn(usage_list)
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
|
||||
.AndReturn(self.limits['absolute'])
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'security-group').AndReturn(True)
|
||||
api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.neutron.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.floating_ips.list())
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.cinder_limits['absolute'])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
res = self.client.get(reverse('horizon:admin:overview:index'))
|
||||
@ -195,18 +177,6 @@ class UsageViewTests(test.BaseAdminViewTests):
|
||||
now.month,
|
||||
now.day, 23, 59, 59, 0)) \
|
||||
.AndReturn(usage_obj)
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True)\
|
||||
.AndReturn(self.limits['absolute'])
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'security-group').AndReturn(True)
|
||||
api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.neutron.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.floating_ips.list())
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.cinder_limits['absolute'])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
csv_url = reverse('horizon:admin:overview:index') + "?format=csv"
|
||||
|
@ -1529,31 +1529,12 @@ class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
|
||||
class UsageViewTests(test.BaseAdminViewTests):
|
||||
def _stub_nova_api_calls(self, nova_stu_enabled=True):
|
||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
||||
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
|
||||
self.mox.StubOutWithMock(api.nova, 'extension_supported')
|
||||
self.mox.StubOutWithMock(api.cinder, 'tenant_absolute_limits')
|
||||
|
||||
api.nova.extension_supported(
|
||||
'SimpleTenantUsage', IsA(http.HttpRequest)) \
|
||||
.AndReturn(nova_stu_enabled)
|
||||
|
||||
def _stub_neutron_api_calls(self, neutron_sg_enabled=True):
|
||||
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
|
||||
self.mox.StubOutWithMock(api.neutron, 'floating_ip_supported')
|
||||
self.mox.StubOutWithMock(api.neutron, 'tenant_floating_ip_list')
|
||||
if neutron_sg_enabled:
|
||||
self.mox.StubOutWithMock(api.neutron, 'security_group_list')
|
||||
api.neutron.is_extension_supported(
|
||||
IsA(http.HttpRequest),
|
||||
'security-group').AndReturn(neutron_sg_enabled)
|
||||
api.neutron.floating_ip_supported(IsA(http.HttpRequest)) \
|
||||
.AndReturn(True)
|
||||
api.neutron.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.floating_ips.list())
|
||||
if neutron_sg_enabled:
|
||||
api.neutron.security_group_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.security_groups.list())
|
||||
|
||||
def test_usage_csv(self):
|
||||
self._test_usage_csv(nova_stu_enabled=True)
|
||||
|
||||
@ -1583,11 +1564,6 @@ class UsageViewTests(test.BaseAdminViewTests):
|
||||
api.nova.usage_get(IsA(http.HttpRequest),
|
||||
self.tenant.id,
|
||||
start, end).AndReturn(usage_obj)
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True)\
|
||||
.AndReturn(self.limits['absolute'])
|
||||
api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.cinder_limits['absolute'])
|
||||
self._stub_neutron_api_calls()
|
||||
self.mox.ReplayAll()
|
||||
|
||||
project_id = self.tenants.first().id
|
||||
|
@ -53,7 +53,7 @@ class ProjectUsageCsvRenderer(csvbase.BaseCsvResponse):
|
||||
capfirst(state_label))
|
||||
|
||||
|
||||
class ProjectOverview(usage.UsageView):
|
||||
class ProjectOverview(usage.ProjectUsageView):
|
||||
table_class = usage.ProjectUsageTable
|
||||
usage_class = usage.ProjectUsage
|
||||
template_name = 'project/overview/usage.html'
|
||||
|
@ -18,6 +18,7 @@ from openstack_dashboard.usage.base import ProjectUsage
|
||||
from openstack_dashboard.usage.tables import BaseUsageTable
|
||||
from openstack_dashboard.usage.tables import GlobalUsageTable
|
||||
from openstack_dashboard.usage.tables import ProjectUsageTable
|
||||
from openstack_dashboard.usage.views import ProjectUsageView
|
||||
from openstack_dashboard.usage.views import UsageView
|
||||
|
||||
|
||||
@ -28,5 +29,6 @@ __all__ = [
|
||||
'BaseUsageTable',
|
||||
'GlobalUsageTable',
|
||||
'ProjectUsageTable',
|
||||
'ProjectUsageView',
|
||||
'UsageView',
|
||||
]
|
||||
|
@ -33,8 +33,6 @@ class BaseUsage(object):
|
||||
self.request = request
|
||||
self.summary = {}
|
||||
self.usage_list = []
|
||||
self.limits = {}
|
||||
self.quotas = {}
|
||||
|
||||
@property
|
||||
def today(self):
|
||||
@ -111,101 +109,6 @@ class BaseUsage(object):
|
||||
req.session['usage_end'] = end
|
||||
return self.form
|
||||
|
||||
def _get_neutron_usage(self, limits, resource_name):
|
||||
resource_map = {
|
||||
'floatingip': {
|
||||
'api': api.neutron.tenant_floating_ip_list,
|
||||
'limit_name': 'totalFloatingIpsUsed',
|
||||
'message': _('Unable to retrieve floating IP addresses.')
|
||||
},
|
||||
'security_group': {
|
||||
'api': api.neutron.security_group_list,
|
||||
'limit_name': 'totalSecurityGroupsUsed',
|
||||
'message': _('Unable to retrieve security groups.')
|
||||
}
|
||||
}
|
||||
|
||||
resource = resource_map[resource_name]
|
||||
try:
|
||||
method = resource['api']
|
||||
current_used = len(method(self.request))
|
||||
except Exception:
|
||||
current_used = 0
|
||||
msg = resource['message']
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
limits[resource['limit_name']] = current_used
|
||||
|
||||
def _set_neutron_limit(self, limits, neutron_quotas, resource_name):
|
||||
limit_name_map = {
|
||||
'floatingip': 'maxTotalFloatingIps',
|
||||
'security_group': 'maxSecurityGroups',
|
||||
}
|
||||
if neutron_quotas is None:
|
||||
resource_max = float("inf")
|
||||
else:
|
||||
resource_max = getattr(neutron_quotas.get(resource_name),
|
||||
'limit', float("inf"))
|
||||
if resource_max == -1:
|
||||
resource_max = float("inf")
|
||||
|
||||
limits[limit_name_map[resource_name]] = resource_max
|
||||
|
||||
def get_neutron_limits(self):
|
||||
if not api.base.is_service_enabled(self.request, 'network'):
|
||||
return
|
||||
try:
|
||||
neutron_quotas_supported = (
|
||||
api.neutron.is_quotas_extension_supported(self.request))
|
||||
neutron_sg_used = (
|
||||
api.neutron.is_extension_supported(self.request,
|
||||
'security-group'))
|
||||
if api.neutron.floating_ip_supported(self.request):
|
||||
self._get_neutron_usage(self.limits, 'floatingip')
|
||||
if neutron_sg_used:
|
||||
self._get_neutron_usage(self.limits, 'security_group')
|
||||
# Quotas are an optional extension in Neutron. If it isn't
|
||||
# enabled, assume the floating IP limit is infinite.
|
||||
if neutron_quotas_supported:
|
||||
neutron_quotas = api.neutron.tenant_quota_get(self.request,
|
||||
self.project_id)
|
||||
else:
|
||||
neutron_quotas = None
|
||||
except Exception:
|
||||
# Assume neutron security group and quotas are enabled
|
||||
# because they are enabled in most Neutron plugins.
|
||||
neutron_sg_used = True
|
||||
neutron_quotas = None
|
||||
msg = _('Unable to retrieve network quota information.')
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
self._set_neutron_limit(self.limits, neutron_quotas, 'floatingip')
|
||||
if neutron_sg_used:
|
||||
self._set_neutron_limit(self.limits, neutron_quotas,
|
||||
'security_group')
|
||||
|
||||
def get_cinder_limits(self):
|
||||
"""Get volume limits if cinder is enabled."""
|
||||
if not api.cinder.is_volume_service_enabled(self.request):
|
||||
return
|
||||
try:
|
||||
self.limits.update(api.cinder.tenant_absolute_limits(self.request))
|
||||
except Exception:
|
||||
msg = _("Unable to retrieve volume limit information.")
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
return
|
||||
|
||||
def get_limits(self):
|
||||
try:
|
||||
self.limits = api.nova.tenant_absolute_limits(self.request,
|
||||
reserved=True)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve limit information."))
|
||||
self.get_neutron_limits()
|
||||
self.get_cinder_limits()
|
||||
|
||||
def get_usage_list(self, start, end):
|
||||
return []
|
||||
|
||||
@ -260,6 +163,11 @@ class ProjectUsage(BaseUsage):
|
||||
attrs = ('memory_mb', 'vcpus', 'uptime',
|
||||
'hours', 'local_gb')
|
||||
|
||||
def __init__(self, request, project_id=None):
|
||||
super(ProjectUsage, self).__init__(request, project_id)
|
||||
self.limits = {}
|
||||
self.quotas = {}
|
||||
|
||||
def get_usage_list(self, start, end):
|
||||
show_deleted = self.request.GET.get('show_deleted',
|
||||
self.show_deleted)
|
||||
@ -281,3 +189,98 @@ class ProjectUsage(BaseUsage):
|
||||
instances.append(server_usage)
|
||||
usage.server_usages = instances
|
||||
return (usage,)
|
||||
|
||||
def get_limits(self):
|
||||
try:
|
||||
self.limits = api.nova.tenant_absolute_limits(self.request,
|
||||
reserved=True)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve limit information."))
|
||||
self._get_neutron_limits()
|
||||
self._get_cinder_limits()
|
||||
|
||||
def _get_neutron_usage(self, limits, resource_name):
|
||||
resource_map = {
|
||||
'floatingip': {
|
||||
'api': api.neutron.tenant_floating_ip_list,
|
||||
'limit_name': 'totalFloatingIpsUsed',
|
||||
'message': _('Unable to retrieve floating IP addresses.')
|
||||
},
|
||||
'security_group': {
|
||||
'api': api.neutron.security_group_list,
|
||||
'limit_name': 'totalSecurityGroupsUsed',
|
||||
'message': _('Unable to retrieve security groups.')
|
||||
}
|
||||
}
|
||||
|
||||
resource = resource_map[resource_name]
|
||||
try:
|
||||
method = resource['api']
|
||||
current_used = len(method(self.request))
|
||||
except Exception:
|
||||
current_used = 0
|
||||
msg = resource['message']
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
limits[resource['limit_name']] = current_used
|
||||
|
||||
def _set_neutron_limit(self, limits, neutron_quotas, resource_name):
|
||||
limit_name_map = {
|
||||
'floatingip': 'maxTotalFloatingIps',
|
||||
'security_group': 'maxSecurityGroups',
|
||||
}
|
||||
if neutron_quotas is None:
|
||||
resource_max = float("inf")
|
||||
else:
|
||||
resource_max = getattr(neutron_quotas.get(resource_name),
|
||||
'limit', float("inf"))
|
||||
if resource_max == -1:
|
||||
resource_max = float("inf")
|
||||
|
||||
limits[limit_name_map[resource_name]] = resource_max
|
||||
|
||||
def _get_neutron_limits(self):
|
||||
if not api.base.is_service_enabled(self.request, 'network'):
|
||||
return
|
||||
try:
|
||||
neutron_quotas_supported = (
|
||||
api.neutron.is_quotas_extension_supported(self.request))
|
||||
neutron_sg_used = (
|
||||
api.neutron.is_extension_supported(self.request,
|
||||
'security-group'))
|
||||
if api.neutron.floating_ip_supported(self.request):
|
||||
self._get_neutron_usage(self.limits, 'floatingip')
|
||||
if neutron_sg_used:
|
||||
self._get_neutron_usage(self.limits, 'security_group')
|
||||
# Quotas are an optional extension in Neutron. If it isn't
|
||||
# enabled, assume the floating IP limit is infinite.
|
||||
if neutron_quotas_supported:
|
||||
neutron_quotas = api.neutron.tenant_quota_get(self.request,
|
||||
self.project_id)
|
||||
else:
|
||||
neutron_quotas = None
|
||||
except Exception:
|
||||
# Assume neutron security group and quotas are enabled
|
||||
# because they are enabled in most Neutron plugins.
|
||||
neutron_sg_used = True
|
||||
neutron_quotas = None
|
||||
msg = _('Unable to retrieve network quota information.')
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
self._set_neutron_limit(self.limits, neutron_quotas, 'floatingip')
|
||||
if neutron_sg_used:
|
||||
self._set_neutron_limit(self.limits, neutron_quotas,
|
||||
'security_group')
|
||||
|
||||
def _get_cinder_limits(self):
|
||||
"""Get volume limits if cinder is enabled."""
|
||||
if not api.cinder.is_volume_service_enabled(self.request):
|
||||
return
|
||||
try:
|
||||
self.limits.update(api.cinder.tenant_absolute_limits(self.request))
|
||||
except Exception:
|
||||
msg = _("Unable to retrieve volume limit information.")
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
return
|
||||
|
@ -48,7 +48,6 @@ class UsageView(tables.DataTableView):
|
||||
self.request.user.tenant_id)
|
||||
self.usage = self.usage_class(self.request, project_id)
|
||||
self.usage.summarize(*self.usage.get_date_range())
|
||||
self.usage.get_limits()
|
||||
self.kwargs['usage'] = self.usage
|
||||
return self.usage.usage_list
|
||||
except Exception:
|
||||
@ -61,7 +60,33 @@ class UsageView(tables.DataTableView):
|
||||
context['table'].kwargs['usage'] = self.usage
|
||||
context['form'] = self.usage.form
|
||||
context['usage'] = self.usage
|
||||
context['charts'] = []
|
||||
|
||||
try:
|
||||
context['simple_tenant_usage_enabled'] = \
|
||||
api.nova.extension_supported('SimpleTenantUsage', self.request)
|
||||
except Exception:
|
||||
context['simple_tenant_usage_enabled'] = True
|
||||
return context
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
if self.request.GET.get('format', 'html') == 'csv':
|
||||
render_class = self.csv_response_class
|
||||
response_kwargs.setdefault("filename", "usage.csv")
|
||||
else:
|
||||
render_class = self.response_class
|
||||
context = self.render_context_with_title(context)
|
||||
resp = render_class(request=self.request,
|
||||
template=self.get_template_names(),
|
||||
context=context,
|
||||
content_type=self.get_content_type(),
|
||||
**response_kwargs)
|
||||
return resp
|
||||
|
||||
|
||||
class ProjectUsageView(UsageView):
|
||||
|
||||
def _get_charts_data(self):
|
||||
charts = []
|
||||
|
||||
# (Used key, Max key, Human Readable Name, text to display when
|
||||
# describing the quota by default it is 'Used')
|
||||
@ -85,7 +110,7 @@ class UsageView(tables.DataTableView):
|
||||
text = pgettext_lazy('Label in the limit summary', 'Used')
|
||||
if len(t) > 3:
|
||||
text = t[3]
|
||||
context['charts'].append({
|
||||
charts.append({
|
||||
'type': t[0],
|
||||
'name': t[2],
|
||||
'used': self.usage.limits[t[0]],
|
||||
@ -93,23 +118,18 @@ class UsageView(tables.DataTableView):
|
||||
'text': text
|
||||
})
|
||||
|
||||
try:
|
||||
context['simple_tenant_usage_enabled'] = \
|
||||
api.nova.extension_supported('SimpleTenantUsage', self.request)
|
||||
except Exception:
|
||||
context['simple_tenant_usage_enabled'] = True
|
||||
return charts
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ProjectUsageView, self).get_context_data(**kwargs)
|
||||
context['charts'] = self._get_charts_data()
|
||||
return context
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
if self.request.GET.get('format', 'html') == 'csv':
|
||||
render_class = self.csv_response_class
|
||||
response_kwargs.setdefault("filename", "usage.csv")
|
||||
else:
|
||||
render_class = self.response_class
|
||||
context = self.render_context_with_title(context)
|
||||
resp = render_class(request=self.request,
|
||||
template=self.get_template_names(),
|
||||
context=context,
|
||||
content_type=self.get_content_type(),
|
||||
**response_kwargs)
|
||||
return resp
|
||||
def get_data(self):
|
||||
data = super(ProjectUsageView, self).get_data()
|
||||
try:
|
||||
self.usage.get_limits()
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve limits information.'))
|
||||
return data
|
||||
|
Loading…
Reference in New Issue
Block a user