diff --git a/openstack_dashboard/dashboards/admin/aggregates/tests.py b/openstack_dashboard/dashboards/admin/aggregates/tests.py index 97d169785b..eba59240bb 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/tests.py +++ b/openstack_dashboard/dashboards/admin/aggregates/tests.py @@ -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() diff --git a/openstack_dashboard/dashboards/admin/overview/tests.py b/openstack_dashboard/dashboards/admin/overview/tests.py index 70957e3ff0..0d128030c6 100644 --- a/openstack_dashboard/dashboards/admin/overview/tests.py +++ b/openstack_dashboard/dashboards/admin/overview/tests.py @@ -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" diff --git a/openstack_dashboard/dashboards/identity/projects/tests.py b/openstack_dashboard/dashboards/identity/projects/tests.py index dd266bb300..16dd161bf8 100644 --- a/openstack_dashboard/dashboards/identity/projects/tests.py +++ b/openstack_dashboard/dashboards/identity/projects/tests.py @@ -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 diff --git a/openstack_dashboard/dashboards/project/overview/views.py b/openstack_dashboard/dashboards/project/overview/views.py index fde93b0b43..1ab45af6d1 100644 --- a/openstack_dashboard/dashboards/project/overview/views.py +++ b/openstack_dashboard/dashboards/project/overview/views.py @@ -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' diff --git a/openstack_dashboard/usage/__init__.py b/openstack_dashboard/usage/__init__.py index 063c27dd34..6276eec51b 100644 --- a/openstack_dashboard/usage/__init__.py +++ b/openstack_dashboard/usage/__init__.py @@ -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', ] diff --git a/openstack_dashboard/usage/base.py b/openstack_dashboard/usage/base.py index ddeca35f94..ab0a1bd3b7 100644 --- a/openstack_dashboard/usage/base.py +++ b/openstack_dashboard/usage/base.py @@ -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 diff --git a/openstack_dashboard/usage/views.py b/openstack_dashboard/usage/views.py index 2ff75d4d0c..b14af98c65 100644 --- a/openstack_dashboard/usage/views.py +++ b/openstack_dashboard/usage/views.py @@ -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