diff --git a/horizon/templates/horizon/common/_limit_summary.html b/horizon/templates/horizon/common/_limit_summary.html new file mode 100644 index 0000000000..d11f428193 --- /dev/null +++ b/horizon/templates/horizon/common/_limit_summary.html @@ -0,0 +1,39 @@ +{% load i18n horizon humanize sizeformat %} + +
+

{% trans "Limit Summary" %}

+
+
+ {% trans "Available Instances" %}
+ {% blocktrans with used=usage.limits.totalInstancesUsed|intcomma available=usage.limits.maxTotalInstances|intcomma %}Used {{ used }} of {{ available }} {% endblocktrans %} +
+
+ +
+
+ {% trans "Available VCPUs" %}
+ {% blocktrans with used=usage.limits.totalCoresUsed|intcomma available=usage.limits.maxTotalCores|intcomma %}Used {{ used }} of {{ available }} {% endblocktrans %} +
+
+ +
+
+ {% trans "Available RAM" %}
+ {% blocktrans with used=usage.limits.totalRAMUsed|intcomma available=usage.limits.maxTotalRAMSize|intcomma %}Used {{ used }} MB of {{ available }} MB {% endblocktrans %} +
+
+ +
+
+ {% trans "Available Floating IPs" %}
+ {% blocktrans with used=usage.limits.totalFloatingIpsUsed|intcomma available=usage.limits.maxTotalFloatingIps|intcomma %}Used {{ used }} of {{ available }} {% endblocktrans %} +
+
+ +
+
+ {% trans "Available Security Groups" %}
+ {% blocktrans with used=usage.limits.totalSecurityGroupsUsed|intcomma available=usage.limits.maxSecurityGroups|intcomma%}Used {{ used }} of {{ available }} {% endblocktrans %} +
+
+
diff --git a/horizon/templates/horizon/common/_quota_summary.html b/horizon/templates/horizon/common/_quota_summary.html deleted file mode 100644 index 026af423ff..0000000000 --- a/horizon/templates/horizon/common/_quota_summary.html +++ /dev/null @@ -1,46 +0,0 @@ -{% load i18n horizon humanize sizeformat %} - -
-

{% trans "Quota Summary" %}

-
-
- {% trans "Available Instances" %}
- {% blocktrans with used=usage.quotas.instances.used|intcomma available=usage.quotas.instances.quota|intcomma %}Used {{ used }} of {{ available }} {% endblocktrans %} -
-
- -
-
- {% trans "Available VCPUs" %}
- {% blocktrans with used=usage.quotas.cores.used|intcomma available=usage.quotas.cores.quota|intcomma %}Used {{ used }} of {{ available }} {% endblocktrans %} -
-
- -
-
- {% trans "Available RAM" %}
- {% blocktrans with used=usage.quotas.ram.used|intcomma available=usage.quotas.ram.quota|intcomma %}Used {{ used }} MB of {{ available }} MB {% endblocktrans %} -
-
- - {% if usage.quotas.volumes %} -
-
- {% trans "Available Volumes" %}
- {% blocktrans with used=usage.quotas.volumes.used|intcomma available=usage.quotas.volumes.quota|intcomma %} Used {{ used }} of {{ available }} {% endblocktrans %} -
-
-
-
- {% trans "Available Snapshots" %}
- {% blocktrans with used=usage.quotas.snapshots.used|intcomma available=usage.quotas.snapshots.quota|intcomma %} Used {{ used }} of {{ available }} {% endblocktrans %} -
-
-
-
- {% trans "Available Volume Storage" %}
- {% blocktrans with used=usage.quotas.gigabytes.used|intcomma available=usage.quotas.gigabytes.quota|intcomma%}Used {{ used }} GB of {{ available }} GB{% endblocktrans %} -
-
- {% endif %} -
diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py index 45f36b7c4c..9983a6862c 100644 --- a/openstack_dashboard/api/cinder.py +++ b/openstack_dashboard/api/cinder.py @@ -144,3 +144,15 @@ def volume_type_create(request, name): def volume_type_delete(request, volume_type_id): return cinderclient(request).volume_types.delete(volume_type_id) + + +def tenant_absolute_limits(request): + limits = cinderclient(request).limits.get().absolute + limits_dict = {} + for limit in limits: + # -1 is used to represent unlimited quotas + if limit.value == -1: + limits_dict[limit.name] = float("inf") + else: + limits_dict[limit.name] = limit.value + return limits_dict diff --git a/openstack_dashboard/dashboards/admin/overview/tests.py b/openstack_dashboard/dashboards/admin/overview/tests.py index 692888513d..aae1628f0e 100644 --- a/openstack_dashboard/dashboards/admin/overview/tests.py +++ b/openstack_dashboard/dashboards/admin/overview/tests.py @@ -31,27 +31,25 @@ from horizon.templatetags.sizeformat import mbformat from openstack_dashboard import api from openstack_dashboard import usage from openstack_dashboard.test import helpers as test -from openstack_dashboard.usage import quotas INDEX_URL = reverse('horizon:project:overview:index') class UsageViewTests(test.BaseAdminViewTests): - @test.create_stubs({api.nova: ('usage_list',), - quotas: ('tenant_quota_usages',), + @test.create_stubs({api.nova: ('usage_list', 'tenant_absolute_limits', ), api.keystone: ('tenant_list',)}) def test_usage(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - quota_data = self.quota_usages.first() api.keystone.tenant_list(IsA(http.HttpRequest)) \ .AndReturn(self.tenants.list()) api.nova.usage_list(IsA(http.HttpRequest), datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ .AndReturn([usage_obj]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() res = self.client.get(reverse('horizon:admin:overview:index')) self.assertTemplateUsed(res, 'admin/overview/usage.html') @@ -70,20 +68,19 @@ class UsageViewTests(test.BaseAdminViewTests): usage_obj.vcpu_hours, usage_obj.total_local_gb_usage)) - @test.create_stubs({api.nova: ('usage_list',), - quotas: ('tenant_quota_usages',), + @test.create_stubs({api.nova: ('usage_list', 'tenant_absolute_limits', ), api.keystone: ('tenant_list',)}) def test_usage_csv(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - quota_data = self.quota_usages.first() api.keystone.tenant_list(IsA(http.HttpRequest)) \ .AndReturn(self.tenants.list()) api.nova.usage_list(IsA(http.HttpRequest), datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ .AndReturn([usage_obj, usage_obj]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() csv_url = reverse('horizon:admin:overview:index') + "?format=csv" res = self.client.get(csv_url) diff --git a/openstack_dashboard/dashboards/project/instances/templates/instances/_launch_details_help.html b/openstack_dashboard/dashboards/project/instances/templates/instances/_launch_details_help.html index 71b1ccae37..a2911aa5bf 100644 --- a/openstack_dashboard/dashboards/project/instances/templates/instances/_launch_details_help.html +++ b/openstack_dashboard/dashboards/project/instances/templates/instances/_launch_details_help.html @@ -16,26 +16,26 @@
-

{% trans "Project Quotas" %}

+

{% trans "Project Limits" %}

{% trans "Number of Instances" %} - {% blocktrans with used=usages.instances.used|intcomma quota=usages.instances.quota|intcomma %}

{{ used }} of {{ quota }} Used

{% endblocktrans %} + {% blocktrans with used=usages.totalInstancesUsed|intcomma quota=usages.maxTotalInstances|intcomma %}

{{ used }} of {{ quota }} Used

{% endblocktrans %}
-
+
{% trans "Number of VCPUs" %} - {% blocktrans with used=usages.cores.used|intcomma quota=usages.cores.quota|intcomma %}

{{ used }} of {{ quota }} Used

{% endblocktrans %} + {% blocktrans with used=usages.totalCoresUsed|intcomma quota=usages.maxTotalCores|intcomma %}

{{ used }} of {{ quota }} Used

{% endblocktrans %}
-
+
{% trans "Total RAM" %} - {% blocktrans with used=usages.ram.used|intcomma quota=usages.ram.quota|intcomma %}

{{ used }} of {{ quota }} MB Used

{% endblocktrans %} + {% blocktrans with used=usages.totalRAMUsed|intcomma quota=usages.maxTotalRAMSize|intcomma %}

{{ used }} of {{ quota }} MB Used

{% endblocktrans %}
-
+
diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 9a8faf4c56..11b2a9427e 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -30,7 +30,6 @@ from mox import IsA, IgnoreArg from openstack_dashboard import api from openstack_dashboard.api import cinder from openstack_dashboard.test import helpers as test -from openstack_dashboard.usage import quotas from .tables import LaunchLink from .tabs import InstanceDetailTabs from .workflows import LaunchInstance @@ -798,14 +797,13 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('flavor_list', 'keypair_list', 'security_group_list', + 'tenant_absolute_limits', 'availability_zone_list',), cinder: ('volume_snapshot_list', 'volume_list',), - quotas: ('tenant_quota_usages',), api.quantum: ('network_list',), api.glance: ('image_list_detailed',)}) def test_launch_instance_get(self): - quota_usages = self.quota_usages.first() image = self.images.first() cinder.volume_list(IsA(http.HttpRequest)) \ @@ -827,8 +825,8 @@ class InstanceTests(test.TestCase): api.quantum.network_list(IsA(http.HttpRequest), shared=True) \ .AndReturn(self.networks.list()[1:]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)) \ - .AndReturn(quota_usages) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) api.nova.flavor_list(IsA(http.HttpRequest)) \ @@ -861,7 +859,6 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',), api.quantum: ('network_list',), - quotas: ('tenant_quota_usages',), api.nova: ('flavor_list', 'keypair_list', 'security_group_list', @@ -941,8 +938,8 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',), api.quantum: ('network_list',), - quotas: ('tenant_quota_usages',), api.nova: ('flavor_list', + 'tenant_absolute_limits', 'keypair_list', 'security_group_list', 'availability_zone_list', @@ -963,7 +960,8 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn({}) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ + .AndReturn(self.limits['absolute']) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -1021,7 +1019,6 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',), api.quantum: ('network_list',), - quotas: ('tenant_quota_usages',), api.nova: ('flavor_list', 'keypair_list', 'security_group_list', @@ -1105,7 +1102,6 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',), api.quantum: ('network_list',), - quotas: ('tenant_quota_usages',), api.nova: ('server_create', 'flavor_list', 'keypair_list', @@ -1191,11 +1187,11 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',), api.quantum: ('network_list',), - quotas: ('tenant_quota_usages',), api.nova: ('flavor_list', 'keypair_list', 'availability_zone_list', - 'security_group_list',), + 'security_group_list', + 'tenant_absolute_limits',), cinder: ('volume_list', 'volume_snapshot_list',)}) def test_launch_instance_post_no_images_available(self): @@ -1208,7 +1204,8 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn({}) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ + .AndReturn(self.limits['absolute']) api.glance.image_list_detailed(IsA(http.HttpRequest), filters={'is_public': True, 'status': 'active'}) \ @@ -1262,12 +1259,12 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',), api.quantum: ('network_list',), - quotas: ('tenant_quota_usages',), cinder: ('volume_list', 'volume_snapshot_list',), api.nova: ('flavor_list', 'keypair_list', 'security_group_list', + 'tenant_absolute_limits', 'availability_zone_list',)}) def test_launch_flavorlist_error(self): cinder.volume_list(IsA(http.HttpRequest)) \ @@ -1289,8 +1286,8 @@ class InstanceTests(test.TestCase): api.quantum.network_list(IsA(http.HttpRequest), shared=True) \ .AndReturn(self.networks.list()[1:]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)) \ - .AndReturn(self.quota_usages.first()) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ + .AndReturn(self.limits['absolute']) api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndRaise(self.exceptions.nova) api.nova.flavor_list(IsA(http.HttpRequest)) \ @@ -1390,10 +1387,10 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.glance: ('image_list_detailed',), api.quantum: ('network_list',), - quotas: ('tenant_quota_usages',), api.nova: ('flavor_list', 'keypair_list', 'security_group_list', + 'tenant_absolute_limits', 'availability_zone_list',), cinder: ('volume_list', 'volume_snapshot_list',)}) @@ -1438,8 +1435,8 @@ class InstanceTests(test.TestCase): api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)) \ - .AndReturn(self.quota_usages.first()) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() @@ -1514,15 +1511,14 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('flavor_list', 'keypair_list', 'security_group_list', - 'availability_zone_list',), + 'availability_zone_list', + 'tenant_absolute_limits',), cinder: ('volume_snapshot_list', 'volume_list',), - quotas: ('tenant_quota_usages',), api.quantum: ('network_list',), api.glance: ('image_list_detailed',)}) def test_select_default_keypair_if_only_one(self): keypair = self.keypairs.first() - quota_usages = self.quota_usages.first() image = self.images.first() cinder.volume_list(IsA(http.HttpRequest)) \ @@ -1544,8 +1540,8 @@ class InstanceTests(test.TestCase): api.quantum.network_list(IsA(http.HttpRequest), shared=True) \ .AndReturn(self.networks.list()[1:]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)) \ - .AndReturn(quota_usages) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ + .AndReturn(self.limits['absolute']) api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) api.nova.flavor_list(IsA(http.HttpRequest)) \ @@ -1627,19 +1623,18 @@ class InstanceTests(test.TestCase): self.assertRedirectsNoFollow(res, INDEX_URL) @test.create_stubs({api.nova: ('server_get', - 'flavor_list',), - quotas: ('tenant_quota_usages',)}) + 'flavor_list', + 'tenant_absolute_limits')}) def test_instance_resize_get(self): server = self.servers.first() - api.nova.server_get(IsA(http.HttpRequest), server.id) \ .AndReturn(server) api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)) \ - .AndReturn(self.quota_usages.first()) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py b/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py index 3aa1e46919..d030f1ed7b 100644 --- a/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py +++ b/openstack_dashboard/dashboards/project/instances/workflows/create_instance.py @@ -32,7 +32,6 @@ from horizon.utils import validators from openstack_dashboard import api from openstack_dashboard.api import cinder -from openstack_dashboard.usage import quotas from ...images_and_snapshots.utils import get_available_images @@ -293,7 +292,7 @@ class SetInstanceDetailsAction(workflows.Action): def get_help_text(self): extra = {} try: - extra['usages'] = quotas.tenant_quota_usages(self.request) + extra['usages'] = api.nova.tenant_absolute_limits(self.request) extra['usages_json'] = json.dumps(extra['usages']) flavors = json.dumps([f._info for f in api.nova.flavor_list(self.request)]) diff --git a/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py b/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py index b6a07c1d2c..241bb99a70 100644 --- a/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py +++ b/openstack_dashboard/dashboards/project/instances/workflows/resize_instance.py @@ -27,7 +27,6 @@ from horizon import workflows from horizon import forms from openstack_dashboard import api -from openstack_dashboard.usage import quotas LOG = logging.getLogger(__name__) @@ -72,7 +71,7 @@ class SetFlavorChoiceAction(workflows.Action): def get_help_text(self): extra = {} try: - extra['usages'] = quotas.tenant_quota_usages(self.request) + extra['usages'] = api.nova.tenant_absolute_limits(self.request) extra['usages_json'] = json.dumps(extra['usages']) flavors = json.dumps([f._info for f in api.nova.flavor_list(self.request)]) diff --git a/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html b/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html index 9ec814573f..3651499a1f 100644 --- a/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html +++ b/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html @@ -7,7 +7,7 @@ {% endblock page_header %} {% block main %} - {% include "horizon/common/_quota_summary.html" %} + {% include "horizon/common/_limit_summary.html" %} {% include "horizon/common/_usage_summary.html" %} {{ table.render }} {% endblock %} diff --git a/openstack_dashboard/dashboards/project/overview/tests.py b/openstack_dashboard/dashboards/project/overview/tests.py index 386ce88d2b..bd16c0ae5d 100644 --- a/openstack_dashboard/dashboards/project/overview/tests.py +++ b/openstack_dashboard/dashboards/project/overview/tests.py @@ -29,7 +29,6 @@ from mox import IsA, Func from openstack_dashboard import api from openstack_dashboard import usage from openstack_dashboard.test import helpers as test -from openstack_dashboard.usage import quotas INDEX_URL = reverse('horizon:project:overview:index') @@ -39,14 +38,14 @@ class UsageViewTests(test.TestCase): def test_usage(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - quota_data = self.quota_usages.first() self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ .AndReturn(usage_obj) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index')) @@ -57,14 +56,14 @@ class UsageViewTests(test.TestCase): def test_unauthorized(self): exc = self.exceptions.nova_unauthorized now = timezone.now() - quota_data = self.quota_usages.first() self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, datetime.datetime(now.year, now.month, 1, 0, 0, 0), Func(usage.almost_now)) \ .AndRaise(exc) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() url = reverse('horizon:project:overview:index') @@ -76,16 +75,16 @@ class UsageViewTests(test.TestCase): def test_usage_csv(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - quota_data = self.quota_usages.first() self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, timestamp, Func(usage.almost_now)) \ .AndReturn(usage_obj) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index') + @@ -95,16 +94,16 @@ class UsageViewTests(test.TestCase): def test_usage_exception_usage(self): now = timezone.now() - quota_data = self.quota_usages.first() self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, timestamp, Func(usage.almost_now)) \ .AndRaise(self.exceptions.nova) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index')) @@ -115,15 +114,15 @@ class UsageViewTests(test.TestCase): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, timestamp, Func(usage.almost_now)) \ .AndReturn(usage_obj) - quotas.tenant_quota_usages(IsA(http.HttpRequest))\ - .AndRaise(self.exceptions.nova) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndRaise(self.exceptions.nova) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index')) @@ -133,16 +132,16 @@ class UsageViewTests(test.TestCase): def test_usage_default_tenant(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - quota_data = self.quota_usages.first() self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(quotas, 'tenant_quota_usages') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, timestamp, Func(usage.almost_now)) \ .AndReturn(usage_obj) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(quota_data) + api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ + .AndReturn(self.limits['absolute']) self.mox.ReplayAll() res = self.client.get(reverse('horizon:project:overview:index')) diff --git a/openstack_dashboard/dashboards/project/volumes/forms.py b/openstack_dashboard/dashboards/project/volumes/forms.py index 66ec2c7f25..b243f17315 100644 --- a/openstack_dashboard/dashboards/project/volumes/forms.py +++ b/openstack_dashboard/dashboards/project/volumes/forms.py @@ -164,7 +164,15 @@ class CreateForm(forms.SelfHandlingForm): # error message when the quota is exceeded when trying to create # a volume, so we need to check for that scenario here before we # send it off to try and create. - usages = quotas.tenant_quota_usages(request) + usages = cinder.tenant_absolute_limits(self.request) + volumes = cinder.volume_list(self.request) + total_size = sum([getattr(volume, 'size', 0) for volume + in volumes]) + usages['gigabytesUsed'] = total_size + usages['volumesUsed'] = len(volumes) + availableGB = usages['maxTotalVolumeGigabytes'] -\ + usages['gigabytesUsed'] + availableVol = usages['maxTotalVolumes'] - usages['volumesUsed'] snapshot_id = None image_id = None @@ -196,14 +204,14 @@ class CreateForm(forms.SelfHandlingForm): if type(data['size']) is str: data['size'] = int(data['size']) - if usages['gigabytes']['available'] < data['size']: + if availableGB < data['size']: error_message = _('A volume of %(req)iGB cannot be created as ' 'you only have %(avail)iGB of your quota ' 'available.') params = {'req': data['size'], - 'avail': usages['gigabytes']['available']} + 'avail': availableGB} raise ValidationError(error_message % params) - elif usages['volumes']['available'] <= 0: + elif availableVol <= 0: error_message = _('You are already using all of your available' ' volumes.') raise ValidationError(error_message) diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html index b4670e6ba1..8966bf8500 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html @@ -16,7 +16,7 @@
- {% include "project/volumes/_quota.html" with usages=usages %} + {% include "project/volumes/_limits.html" with usages=usages %}
{% endblock %} diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_limits.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_limits.html new file mode 100644 index 0000000000..b2fbdbd0d1 --- /dev/null +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_limits.html @@ -0,0 +1,34 @@ +{% load i18n horizon humanize %} + +

{% trans "Description" %}:

+ +

{% trans "Volumes are block devices that can be attached to instances." %}

+ +

{% trans "Volume Limits" %}

+ +
+ {% trans "Total Gigabytes" %} ({{ usages.gigabytesUsed|intcomma }} {% trans "GB" %}) +

{{ usages.maxTotalVolumeGigabytes|quota:_("GB")|intcomma }}

+
+ +
+
+ +
+ {% trans "Number of Volumes" %} ({{ usages.volumesUsed|intcomma }}) +

{{ usages.maxTotalVolumes|quota|intcomma }}

+
+ +
+
+ + + diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html deleted file mode 100644 index f145385224..0000000000 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html +++ /dev/null @@ -1,43 +0,0 @@ -{% load i18n horizon humanize %} - -

{% trans "Description" %}:

- -

{% trans "Volumes are block devices that can be attached to instances." %}

- -

{% trans "Volume Quotas" %}

- -
- {% trans "Total Gigabytes" %} ({{ usages.gigabytes.used|intcomma }} {% trans "GB" %}) -

{{ usages.gigabytes.available|quota:_("GB")|intcomma }}

-
- -
-
- -{% if snapshot_quota %} -
- {% trans "Number of Snapshots" %} ({{ usages.snapshots.used|intcomma }}) -

{{ usages.snapshots.available|quota|intcomma }}

-
- -
-
-{% else %} -
- {% trans "Number of Volumes" %} ({{ usages.volumes.used|intcomma }}) -

{{ usages.volumes.available|quota|intcomma }}

-
- -
-
-{% endif %} - - diff --git a/openstack_dashboard/dashboards/project/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/tests.py index bf8f5f973b..f18888e26f 100644 --- a/openstack_dashboard/dashboards/project/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/tests.py @@ -23,7 +23,7 @@ from django.conf import settings from django.core.urlresolvers import reverse from django.forms import widgets -from mox import IsA +from mox import IsA, IgnoreArg from openstack_dashboard import api from openstack_dashboard.api import cinder @@ -34,13 +34,16 @@ from openstack_dashboard.usage import quotas class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_create', 'volume_snapshot_list', - 'volume_type_list',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + 'volume_type_list', + 'tenant_absolute_limits', + 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume(self): volume = self.volumes.first() volume_type = self.volume_types.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 250, + 'gigabytesUsed': 20, + 'maxTotalVolumes': 6} formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', 'method': u'CreateForm', @@ -50,7 +53,10 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), @@ -80,12 +86,14 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_create', 'volume_snapshot_list', - 'volume_type_list',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + 'volume_type_list', + 'tenant_absolute_limits', + 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume_dropdown(self): volume = self.volumes.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 250, + 'maxTotalVolumes': 6} formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', 'method': u'CreateForm', @@ -107,7 +115,10 @@ class VolumeViewTests(test.TestCase): filters={'property-owner_id': self.tenant.id, 'status': 'active'}) \ .AndReturn([[], False]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_create(IsA(http.HttpRequest), formData['size'], formData['name'], @@ -129,11 +140,13 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_create', 'volume_snapshot_get', 'volume_get', - 'volume_type_list',), - quotas: ('tenant_quota_usages',)}) + 'volume_type_list', + 'tenant_absolute_limits', + 'volume_list',)}) def test_create_volume_from_snapshot(self): volume = self.volumes.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 250, + 'maxTotalVolumes': 6} snapshot = self.volume_snapshots.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -144,7 +157,10 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_get(IsA(http.HttpRequest), str(snapshot.id)).AndReturn(snapshot) cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\ @@ -173,12 +189,14 @@ class VolumeViewTests(test.TestCase): 'volume_snapshot_list', 'volume_snapshot_get', 'volume_get', - 'volume_type_list',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + 'volume_type_list', + 'tenant_absolute_limits', + 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume_from_snapshot_dropdown(self): volume = self.volumes.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 250, + 'maxTotalVolumes': 6} snapshot = self.volume_snapshots.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -200,7 +218,10 @@ class VolumeViewTests(test.TestCase): filters={'property-owner_id': self.tenant.id, 'status': 'active'}) \ .AndReturn([[], False]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_get(IsA(http.HttpRequest), str(snapshot.id)).AndReturn(snapshot) cinder.volume_create(IsA(http.HttpRequest), @@ -224,11 +245,13 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_snapshot_get', 'volume_type_list', - 'volume_get',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + 'volume_get', + 'tenant_absolute_limits', + 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume_from_snapshot_invalid_size(self): - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 100, + 'maxTotalVolumes': 6} snapshot = self.volume_snapshots.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -237,12 +260,18 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_get(IsA(http.HttpRequest), str(snapshot.id)).AndReturn(snapshot) cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\ AndReturn(self.volumes.first()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) self.mox.ReplayAll() @@ -256,12 +285,14 @@ class VolumeViewTests(test.TestCase): "snapshot size (40GB)") @test.create_stubs({cinder: ('volume_create', - 'volume_type_list',), - api.glance: ('image_get',), - quotas: ('tenant_quota_usages',)}) + 'volume_type_list', + 'tenant_absolute_limits', + 'volume_list',), + api.glance: ('image_get',)}) def test_create_volume_from_image(self): volume = self.volumes.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 200, + 'maxTotalVolumes': 6} image = self.images.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -272,7 +303,10 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) api.glance.image_get(IsA(http.HttpRequest), str(image.id)).AndReturn(image) cinder.volume_create(IsA(http.HttpRequest), @@ -298,13 +332,15 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_create', 'volume_type_list', - 'volume_snapshot_list',), + 'volume_snapshot_list', + 'tenant_absolute_limits', + 'volume_list',), api.glance: ('image_get', - 'image_list_detailed'), - quotas: ('tenant_quota_usages',)}) + 'image_list_detailed')}) def test_create_volume_from_image_dropdown(self): volume = self.volumes.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 200, + 'maxTotalVolumes': 6} image = self.images.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -327,7 +363,10 @@ class VolumeViewTests(test.TestCase): filters={'property-owner_id': self.tenant.id, 'status': 'active'}) \ .AndReturn([[], False]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)) \ + .AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)) \ + .AndReturn(self.volumes.list()) api.glance.image_get(IsA(http.HttpRequest), str(image.id)).AndReturn(image) cinder.volume_create(IsA(http.HttpRequest), @@ -349,12 +388,13 @@ class VolumeViewTests(test.TestCase): redirect_url = reverse('horizon:project:volumes:index') self.assertRedirectsNoFollow(res, redirect_url) - @test.create_stubs({cinder: ('volume_type_list',), + @test.create_stubs({cinder: ('volume_type_list', 'tenant_absolute_limits', + 'volume_list',), api.glance: ('image_get', - 'image_list_detailed'), - quotas: ('tenant_quota_usages',)}) + 'image_list_detailed')}) def test_create_volume_from_image_invalid_size(self): - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 100, + 'maxTotalVolumes': 6} image = self.images.first() formData = {'name': u'A Volume I Am Making', 'description': u'This is a volume I am making for a test.', @@ -363,10 +403,16 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) api.glance.image_get(IsA(http.HttpRequest), str(image.id)).AndReturn(image) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) self.mox.ReplayAll() @@ -379,11 +425,12 @@ class VolumeViewTests(test.TestCase): "The volume size cannot be less than the " "image size (20.0 GB)") - @test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + @test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list', + 'tenant_absolute_limits', 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume_gb_used_over_alloted_quota(self): - usage = {'gigabytes': {'available': 100, 'used': 20}} + usage_limit = {'maxTotalVolumeGigabytes': 100, + 'maxTotalVolumes': 6} formData = {'name': u'This Volume Is Huge!', 'description': u'This is a volume that is just too big!', 'method': u'CreateForm', @@ -391,7 +438,10 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), @@ -402,7 +452,10 @@ class VolumeViewTests(test.TestCase): filters={'property-owner_id': self.tenant.id, 'status': 'active'}) \ .AndReturn([[], False]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) self.mox.ReplayAll() @@ -410,15 +463,15 @@ class VolumeViewTests(test.TestCase): res = self.client.post(url, formData) expected_error = [u'A volume of 5000GB cannot be created as you only' - ' have 100GB of your quota available.'] + ' have 20GB of your quota available.'] self.assertEqual(res.context['form'].errors['__all__'], expected_error) - @test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + @test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list', + 'tenant_absolute_limits', 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume_number_over_alloted_quota(self): - usage = {'gigabytes': {'available': 100, 'used': 20}, - 'volumes': {'available': 0}} + usage_limit = {'maxTotalVolumeGigabytes': 100, + 'maxTotalVolumes': len(self.volumes.list())} formData = {'name': u'Too Many...', 'description': u'We have no volumes left!', 'method': u'CreateForm', @@ -426,7 +479,10 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), @@ -437,7 +493,10 @@ class VolumeViewTests(test.TestCase): filters={'property-owner_id': self.tenant.id, 'status': 'active'}) \ .AndReturn([[], False]) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) self.mox.ReplayAll() @@ -450,13 +509,16 @@ class VolumeViewTests(test.TestCase): @test.create_stubs({cinder: ('volume_create', 'volume_snapshot_list', - 'volume_type_list',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + 'volume_type_list', + 'tenant_absolute_limits', + 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume_encrypted(self): volume = self.volumes.first() volume_type = self.volume_types.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 250, + 'gigabytesUsed': 20, + 'maxTotalVolumes': 6} formData = {'name': u'An Encrypted Volume', 'description': u'This volume has metadata for encryption.', 'method': u'CreateForm', @@ -471,7 +533,10 @@ class VolumeViewTests(test.TestCase): cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), @@ -501,9 +566,9 @@ class VolumeViewTests(test.TestCase): settings.OPENSTACK_HYPERVISOR_FEATURES['can_encrypt_volumes'] = PREV - @test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',), - api.glance: ('image_list_detailed',), - quotas: ('tenant_quota_usages',)}) + @test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list', + 'tenant_absolute_limits', 'volume_list',), + api.glance: ('image_list_detailed',)}) def test_create_volume_cannot_encrypt(self): volume = self.volumes.first() volume_type = self.volume_types.first() @@ -522,11 +587,16 @@ class VolumeViewTests(test.TestCase): volume = self.volumes.first() volume_type = self.volume_types.first() - usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}} + usage_limit = {'maxTotalVolumeGigabytes': 250, + 'gigabytesUsed': 20, + 'maxTotalVolumes': 6} cinder.volume_type_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_types.list()) - quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + cinder.tenant_absolute_limits(IsA(http.HttpRequest)).\ + AndReturn(usage_limit) + cinder.volume_list(IsA(http.HttpRequest)).\ + AndReturn(self.volumes.list()) cinder.volume_snapshot_list(IsA(http.HttpRequest)).\ AndReturn(self.volume_snapshots.list()) api.glance.image_list_detailed(IsA(http.HttpRequest), diff --git a/openstack_dashboard/dashboards/project/volumes/views.py b/openstack_dashboard/dashboards/project/volumes/views.py index c423488bc0..edd0fe275a 100644 --- a/openstack_dashboard/dashboards/project/volumes/views.py +++ b/openstack_dashboard/dashboards/project/volumes/views.py @@ -99,7 +99,15 @@ class CreateView(forms.ModalFormView): def get_context_data(self, **kwargs): context = super(CreateView, self).get_context_data(**kwargs) try: - context['usages'] = quotas.tenant_quota_usages(self.request) + tenant_id = self.kwargs.get('tenant_id', + self.request.user.tenant_id) + context['usages'] = cinder.tenant_absolute_limits(self.request) + volumes = cinder.volume_list(self.request) + total_size = sum([getattr(volume, 'size', 0) for volume + in volumes]) + context['usages']['gigabytesUsed'] = total_size + context['usages']['volumesUsed'] = len(volumes) + except: exceptions.handle(self.request) return context diff --git a/openstack_dashboard/usage/base.py b/openstack_dashboard/usage/base.py index 829a85bfe5..71015d8f26 100644 --- a/openstack_dashboard/usage/base.py +++ b/openstack_dashboard/usage/base.py @@ -32,6 +32,7 @@ class BaseUsage(object): self.request = request self.summary = {} self.usage_list = [] + self.limits = {} self.quotas = {} @property @@ -82,6 +83,13 @@ class BaseUsage(object): 'year': self.today.year}) return self.form + def get_limits(self): + try: + self.limits = api.nova.tenant_absolute_limits(self.request) + except: + exceptions.handle(self.request, + _("Unable to retrieve limit information.")) + def get_usage_list(self, start, end): raise NotImplementedError("You must define a get_usage method.") diff --git a/openstack_dashboard/usage/views.py b/openstack_dashboard/usage/views.py index bb6cf635c1..38d02a9b24 100644 --- a/openstack_dashboard/usage/views.py +++ b/openstack_dashboard/usage/views.py @@ -31,7 +31,7 @@ class UsageView(tables.DataTableView): tenant_id = self.kwargs.get('tenant_id', self.request.user.tenant_id) self.usage = self.usage_class(self.request, tenant_id) self.usage.summarize(*self.usage.get_date_range()) - self.usage.get_quotas() + self.usage.get_limits() self.kwargs['usage'] = self.usage return self.usage.usage_list