+
{% 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 9a8faf4c5..11b2a9427 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 3aa1e4691..d030f1ed7 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 b6a07c1d2..241bb99a7 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 9ec814573..3651499a1 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 386ce88d2..bd16c0ae5 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 66ec2c7f2..b243f1731 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 b4670e6ba..8966bf850 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 000000000..b2fbdbd0d
--- /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 f14538522..000000000
--- 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 bf8f5f973..f18888e26 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 c423488bc..edd0fe275 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 829a85bfe..71015d8f2 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 bb6cf635c..38d02a9b2 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