diff --git a/horizon/templates/horizon/common/_limit_summary.html b/horizon/templates/horizon/common/_limit_summary.html index 9cc96dea4d..494fbb998c 100644 --- a/horizon/templates/horizon/common/_limit_summary.html +++ b/horizon/templates/horizon/common/_limit_summary.html @@ -3,30 +3,33 @@ {% spaceless %} <div class="quota-dynamic limit-summary"> <h3 class="quota-heading">{% trans "Limit Summary" %}</h3> - {% for chart in charts %} - {% if forloop.first or forloop.counter0|divisibleby:6 %} - <div class="row"> - {% endif %} - <div class="d3_quota_bar col-lg-2 col-md-4 col-sm-4 col-xs-6"> - <div class="pie-chart-usage" data-used="{% quotapercent chart.used chart.quota %}"></div> - <div class="quota_title" title="{{ chart.name }}" data-toggle="tooltip"> {{ chart.name }}</div> - <div class="quota_subtitle"> - {% if chart.quota|quotainf != '-1' %} - {% blocktrans trimmed with usedphrase=chart.text used=chart.used_display available=chart.quota_display %} - {{ usedphrase }} {{ used }} of {{ available }} - {% endblocktrans %} - {% else %} - {% blocktrans trimmed with usedphrase=chart.text used=chart.used_display %} - {{ usedphrase }} {{ used }} (No Limit) - {% endblocktrans %} - {% endif %} + {% for section in charts %} + <h4>{{ section.title }}</h4> + {% for chart in section.charts %} + {% if forloop.first or forloop.counter0|divisibleby:6 %} + <div class="row"> + {% endif %} + <div class="d3_quota_bar col-lg-2 col-md-4 col-sm-4 col-xs-6"> + <div class="pie-chart-usage" data-used="{% quotapercent chart.used chart.quota %}"></div> + <div class="quota_title" title="{{ chart.name }}" data-toggle="tooltip"> {{ chart.name }}</div> + <div class="quota_subtitle"> + {% if chart.quota|quotainf != '-1' %} + {% blocktrans trimmed with usedphrase=chart.text used=chart.used_display available=chart.quota_display %} + {{ usedphrase }} {{ used }} of {{ available }} + {% endblocktrans %} + {% else %} + {% blocktrans trimmed with usedphrase=chart.text used=chart.used_display %} + {{ usedphrase }} {{ used }} (No Limit) + {% endblocktrans %} + {% endif %} + </div> + </div> + {% if forloop.last or forloop.counter|divisibleby:6 %} + {% if not forloop.first %} </div> - </div> - {% if forloop.last or forloop.counter|divisibleby:6 %} - {% if not forloop.first %} - </div> - {% endif %} - {% endif %} - {% endfor %} + {% endif %} + {% endif %} + {% endfor %} + {% endfor %} </div> {% endspaceless %} diff --git a/openstack_dashboard/dashboards/project/overview/tests.py b/openstack_dashboard/dashboards/project/overview/tests.py index 947b62c3c6..d01f136860 100644 --- a/openstack_dashboard/dashboards/project/overview/tests.py +++ b/openstack_dashboard/dashboards/project/overview/tests.py @@ -266,8 +266,40 @@ class UsageViewTests(test.TestCase): return res def test_usage_charts_created(self): - res = self._test_usage_charts() + res = self._test_usage_charts( + quota_usage_overrides={'floatingip': {'quota': -1, 'used': 1234}}) self.assertIn('charts', res.context) + charts = res.context['charts'] + + self.assertEqual(['Compute', 'Volume', 'Network'], + [c['title'] for c in charts]) + + compute_charts = [c for c in charts if c['title'] == 'Compute'][0] + chart_ram = [c for c in compute_charts['charts'] + if c['type'] == 'ram'][0] + # Check mb_float_format filter is applied + self.assertEqual(10000, chart_ram['quota']) + self.assertEqual('9.8GB', chart_ram['quota_display']) + self.assertEqual(0, chart_ram['used']) + self.assertEqual('0Bytes', chart_ram['used_display']) + + volume_charts = [c for c in charts if c['title'] == 'Volume'][0] + chart_gigabytes = [c for c in volume_charts['charts'] + if c['type'] == 'gigabytes'][0] + # Check diskgbformat filter is applied + self.assertEqual(1000, chart_gigabytes['quota']) + self.assertEqual('1000GB', chart_gigabytes['quota_display']) + self.assertEqual(0, chart_gigabytes['used']) + self.assertEqual('0Bytes', chart_gigabytes['used_display']) + + network_charts = [c for c in charts if c['title'] == 'Network'][0] + chart_fip = [c for c in network_charts['charts'] + if c['type'] == 'floatingip'][0] + # Check intcomma default filter is applied + self.assertEqual(float('inf'), chart_fip['quota']) + self.assertEqual(float('inf'), chart_fip['quota_display']) + self.assertEqual(1234, chart_fip['used']) + self.assertEqual('1,234', chart_fip['used_display']) def test_usage_charts_infinite_quota(self): res = self._test_usage_charts( diff --git a/openstack_dashboard/usage/views.py b/openstack_dashboard/usage/views.py index f010f606e0..5d1ea8f201 100644 --- a/openstack_dashboard/usage/views.py +++ b/openstack_dashboard/usage/views.py @@ -100,21 +100,37 @@ ChartDef = collections.namedtuple( # If None is specified, the default filter 'intcomma' will be applied. # if you want to apply no filters, specify an empty tuple or list. CHART_DEFS = [ - ChartDef("instances", _("Instances"), None, None), - ChartDef("cores", _("VCPUs"), None, None), - ChartDef("ram", _("RAM"), None, (sizeformat.mb_float_format,)), - ChartDef("volumes", _("Volumes"), None, None), - ChartDef("snapshots", _("Volume Snapshots"), None, None), - ChartDef("gigabytes", _("Volume Storage"), None, - (sizeformat.diskgbformat,)), - ChartDef("floatingip", _("Floating IPs"), - pgettext_lazy('Label in the limit summary', "Allocated"), - None), - ChartDef("security_group", _("Security Groups"), None, None), - ChartDef("security_group_rule", _("Security Group Rules"), None, None), - ChartDef("network", _("Networks"), None, None), - ChartDef("port", _("Ports"), None, None), - ChartDef("router", _("Routers"), None, None), + { + 'title': _("Compute"), + 'charts': [ + ChartDef("instances", _("Instances"), None, None), + ChartDef("cores", _("VCPUs"), None, None), + ChartDef("ram", _("RAM"), None, (sizeformat.mb_float_format,)), + ] + }, + { + 'title': _("Volume"), + 'charts': [ + ChartDef("volumes", _("Volumes"), None, None), + ChartDef("snapshots", _("Volume Snapshots"), None, None), + ChartDef("gigabytes", _("Volume Storage"), None, + (sizeformat.diskgbformat,)), + ] + }, + { + 'title': _("Network"), + 'charts': [ + ChartDef("floatingip", _("Floating IPs"), + pgettext_lazy('Label in the limit summary', "Allocated"), + None), + ChartDef("security_group", _("Security Groups"), None, None), + ChartDef("security_group_rule", _("Security Group Rules"), + None, None), + ChartDef("network", _("Networks"), None, None), + ChartDef("port", _("Ports"), None, None), + ChartDef("router", _("Routers"), None, None), + ] + }, ] @@ -129,8 +145,18 @@ def _apply_filters(value, filters): class ProjectUsageView(UsageView): def _get_charts_data(self): + chart_sections = [] + for section in CHART_DEFS: + chart_data = self._process_chart_section(section['charts']) + chart_sections.append({ + 'title': section['title'], + 'charts': chart_data + }) + return chart_sections + + def _process_chart_section(self, chart_defs): charts = [] - for t in CHART_DEFS: + for t in chart_defs: if t.quota_key not in self.usage.limits: continue key = t.quota_key @@ -149,6 +175,8 @@ class ProjectUsageView(UsageView): quota_display = None if quota != float('inf'): quota_display = _apply_filters(quota, filters) + else: + quota_display = quota charts.append({ 'type': key,