switching to use limits instead of quotas

Fixes bug 1178694

Change-Id: I6fad061faf84079492338016fd2bada9a2e434f3
This commit is contained in:
Eric Peterson 2013-05-16 09:56:24 -06:00 committed by ericpeterson-l
parent 9254c32527
commit d4b0ab4aa3
18 changed files with 310 additions and 231 deletions

View File

@ -0,0 +1,39 @@
{% load i18n horizon humanize sizeformat %}
<div class="quota-dynamic">
<h3>{% trans "Limit Summary" %}</h3>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalInstancesUsed usage.limits.maxTotalInstances 100 %}"></div>
<strong>{% trans "Available Instances" %} <br />
{% blocktrans with used=usage.limits.totalInstancesUsed|intcomma available=usage.limits.maxTotalInstances|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalCoresUsed usage.limits.maxTotalCores 100 %}"></div>
<strong>{% trans "Available VCPUs" %} <br />
{% blocktrans with used=usage.limits.totalCoresUsed|intcomma available=usage.limits.maxTotalCores|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalRAMUsed usage.limits.maxTotalRAMSize 100 %}"></div>
<strong>{% trans "Available RAM" %} <br />
{% blocktrans with used=usage.limits.totalRAMUsed|intcomma available=usage.limits.maxTotalRAMSize|intcomma %}Used <span> {{ used }} MB </span> of <span> {{ available }} MB </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalFloatingIpsUsed usage.limits.maxTotalFloatingIps 100 %}"></div>
<strong>{% trans "Available Floating IPs" %} <br />
{% blocktrans with used=usage.limits.totalFloatingIpsUsed|intcomma available=usage.limits.maxTotalFloatingIps|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.limits.totalSecurityGroupsUsed usage.limits.maxSecurityGroups 100 %}"></div>
<strong>{% trans "Available Security Groups" %} <br />
{% blocktrans with used=usage.limits.totalSecurityGroupsUsed|intcomma available=usage.limits.maxSecurityGroups|intcomma%}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
</div>

View File

@ -1,46 +0,0 @@
{% load i18n horizon humanize sizeformat %}
<div class="quota-dynamic">
<h3>{% trans "Quota Summary" %}</h3>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.instances.used usage.quotas.instances.quota 100 %}"></div>
<strong>{% trans "Available Instances" %} <br />
{% blocktrans with used=usage.quotas.instances.used|intcomma available=usage.quotas.instances.quota|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.cores.used usage.quotas.cores.quota 100 %}"></div>
<strong>{% trans "Available VCPUs" %} <br />
{% blocktrans with used=usage.quotas.cores.used|intcomma available=usage.quotas.cores.quota|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.ram.used usage.quotas.ram.quota 100 %}"></div>
<strong>{% trans "Available RAM" %} <br />
{% blocktrans with used=usage.quotas.ram.used|intcomma available=usage.quotas.ram.quota|intcomma %}Used <span> {{ used }} MB </span> of <span> {{ available }} MB </span>{% endblocktrans %}
</strong>
</div>
{% if usage.quotas.volumes %}
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.volumes.used usage.quotas.volumes.quota 100 %}"></div>
<strong>{% trans "Available Volumes" %} <br />
{% blocktrans with used=usage.quotas.volumes.used|intcomma available=usage.quotas.volumes.quota|intcomma %} Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.snapshots.used usage.quotas.snapshots.quota 100 %}"></div>
<strong>{% trans "Available Snapshots" %} <br />
{% blocktrans with used=usage.quotas.snapshots.used|intcomma available=usage.quotas.snapshots.quota|intcomma %} Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
</strong>
</div>
<div class="d3_quota_bar">
<div class="d3_pie_chart" data-used="{% widthratio usage.quotas.gigabytes.used usage.quotas.gigabytes.quota 100 %}"></div>
<strong>{% trans "Available Volume Storage" %} <br />
{% blocktrans with used=usage.quotas.gigabytes.used|intcomma available=usage.quotas.gigabytes.quota|intcomma%}Used <span> {{ used }} GB </span> of <span> {{ available }} GB</span>{% endblocktrans %}
</strong>
</div>
{% endif %}
</div>

View File

@ -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

View File

@ -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)

View File

@ -16,26 +16,26 @@
</table>
<div class="quota-dynamic">
<h4>{% trans "Project Quotas" %}</h4>
<h4>{% trans "Project Limits" %}</h4>
<div class="quota_title clearfix">
<strong>{% trans "Number of Instances" %}</strong>
{% blocktrans with used=usages.instances.used|intcomma quota=usages.instances.quota|intcomma %}<p>{{ used }} of {{ quota }} Used</p>{% endblocktrans %}
{% blocktrans with used=usages.totalInstancesUsed|intcomma quota=usages.maxTotalInstances|intcomma %}<p>{{ used }} of {{ quota }} Used</p>{% endblocktrans %}
</div>
<div id="quota_instances" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.instances.quota }}" data-quota-used="{{ usages.instances.used }}">
<div id="quota_instances" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.maxTotalInstances }}" data-quota-used="{{ usages.totalInstancesUsed }}">
</div>
<div class="quota_title clearfix">
<strong>{% trans "Number of VCPUs" %}</strong>
{% blocktrans with used=usages.cores.used|intcomma quota=usages.cores.quota|intcomma %}<p>{{ used }} of {{ quota }} Used</p>{% endblocktrans %}
{% blocktrans with used=usages.totalCoresUsed|intcomma quota=usages.maxTotalCores|intcomma %}<p>{{ used }} of {{ quota }} Used</p>{% endblocktrans %}
</div>
<div id="quota_vcpus" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.cores.quota }}" data-quota-used="{{ usages.cores.used }}">
<div id="quota_vcpus" class="quota_bar" data-progress-indicator-flavor data-quota-limit="{{ usages.maxTotalCores }}" data-quota-used="{{ usages.totalCoresUsed }}">
</div>
<div class="quota_title clearfix">
<strong>{% trans "Total RAM" %}</strong>
{% blocktrans with used=usages.ram.used|intcomma quota=usages.ram.quota|intcomma %}<p>{{ used }} of {{ quota }} MB Used</p>{% endblocktrans %}
{% blocktrans with used=usages.totalRAMUsed|intcomma quota=usages.maxTotalRAMSize|intcomma %}<p>{{ used }} of {{ quota }} MB Used</p>{% endblocktrans %}
</div>
<div id="quota_ram" data-progress-indicator-flavor data-quota-limit="{{ usages.ram.quota }}" data-quota-used="{{ usages.ram.used }}" class="quota_bar">
<div id="quota_ram" data-progress-indicator-flavor data-quota-limit="{{ usages.maxTotalRAMSize }}" data-quota-used="{{ usages.totalRAMUsed }}" class="quota_bar">
</div>
</div>

View File

@ -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()

View File

@ -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)])

View File

@ -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)])

View File

@ -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 %}

View File

@ -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'))

View File

@ -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)

View File

@ -16,7 +16,7 @@
</div>
<div class="right quota-dynamic">
{% include "project/volumes/_quota.html" with usages=usages %}
{% include "project/volumes/_limits.html" with usages=usages %}
</div>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% load i18n horizon humanize %}
<h3>{% trans "Description" %}:</h3>
<p>{% trans "Volumes are block devices that can be attached to instances." %}</p>
<h3>{% trans "Volume Limits" %}</h3>
<div class="quota_title clearfix">
<strong>{% trans "Total Gigabytes" %} <span>({{ usages.gigabytesUsed|intcomma }} {% trans "GB" %})</span></strong>
<p>{{ usages.maxTotalVolumeGigabytes|quota:_("GB")|intcomma }}</p>
</div>
<div id="quota_size" data-progress-indicator-for="id_size" data-quota-limit="{{ usages.maxTotalVolumeGigabytes }}" data-quota-used="{{ usages.gigabytesUsed }}" class="quota_bar">
</div>
<div class="quota_title clearfix">
<strong>{% trans "Number of Volumes" %} <span>({{ usages.volumesUsed|intcomma }})</span></strong>
<p>{{ usages.maxTotalVolumes|quota|intcomma }}</p>
</div>
<div id="quota_volumes" data-progress-indicator-step-by="1" data-quota-limit="{{ usages.maxTotalVolumes}}" data-quota-used="{{ usages.volumesUsed }}" class="quota_bar">
</div>
<script type="text/javascript" charset="utf-8">
if(typeof horizon.Quota !== 'undefined') {
horizon.Quota.init();
} else {
addHorizonLoadEvent(function() {
horizon.Quota.init();
});
}
</script>

View File

@ -1,43 +0,0 @@
{% load i18n horizon humanize %}
<h3>{% trans "Description" %}:</h3>
<p>{% trans "Volumes are block devices that can be attached to instances." %}</p>
<h3>{% trans "Volume Quotas" %}</h3>
<div class="quota_title clearfix">
<strong>{% trans "Total Gigabytes" %} <span>({{ usages.gigabytes.used|intcomma }} {% trans "GB" %})</span></strong>
<p>{{ usages.gigabytes.available|quota:_("GB")|intcomma }}</p>
</div>
<div id="quota_size" data-progress-indicator-for="id_size" data-quota-limit="{{ usages.gigabytes.quota }}" data-quota-used="{{ usages.gigabytes.used }}" class="quota_bar">
</div>
{% if snapshot_quota %}
<div class="quota_title clearfix">
<strong>{% trans "Number of Snapshots" %} <span>({{ usages.snapshots.used|intcomma }})</span></strong>
<p>{{ usages.snapshots.available|quota|intcomma }}</p>
</div>
<div id="quota_snapshots" data-progress-indicator-step-by="1" data-quota-limit="{{ usages.snapshots.quota }}" data-quota-used="{{ usages.snapshots.used }}" class="quota_bar">
</div>
{% else %}
<div class="quota_title clearfix">
<strong>{% trans "Number of Volumes" %} <span>({{ usages.volumes.used|intcomma }})</span></strong>
<p>{{ usages.volumes.available|quota|intcomma }}</p>
</div>
<div id="quota_volumes" data-progress-indicator-step-by="1" data-quota-limit="{{ usages.volumes.quota }}" data-quota-used="{{ usages.volumes.used }}" class="quota_bar">
</div>
{% endif %}
<script type="text/javascript" charset="utf-8">
if(typeof horizon.Quota !== 'undefined') {
horizon.Quota.init();
} else {
addHorizonLoadEvent(function() {
horizon.Quota.init();
});
}
</script>

View File

@ -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),

View File

@ -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

View File

@ -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.")

View File

@ -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