switching to use limits instead of quotas
Fixes bug 1178694 Change-Id: I6fad061faf84079492338016fd2bada9a2e434f3
This commit is contained in:
parent
9254c32527
commit
d4b0ab4aa3
39
horizon/templates/horizon/common/_limit_summary.html
Normal file
39
horizon/templates/horizon/common/_limit_summary.html
Normal 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>
|
@ -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>
|
|
@ -144,3 +144,15 @@ def volume_type_create(request, name):
|
|||||||
|
|
||||||
def volume_type_delete(request, volume_type_id):
|
def volume_type_delete(request, volume_type_id):
|
||||||
return cinderclient(request).volume_types.delete(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
|
||||||
|
@ -31,27 +31,25 @@ from horizon.templatetags.sizeformat import mbformat
|
|||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard import usage
|
from openstack_dashboard import usage
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
|
|
||||||
|
|
||||||
INDEX_URL = reverse('horizon:project:overview:index')
|
INDEX_URL = reverse('horizon:project:overview:index')
|
||||||
|
|
||||||
|
|
||||||
class UsageViewTests(test.BaseAdminViewTests):
|
class UsageViewTests(test.BaseAdminViewTests):
|
||||||
@test.create_stubs({api.nova: ('usage_list',),
|
@test.create_stubs({api.nova: ('usage_list', 'tenant_absolute_limits', ),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.keystone: ('tenant_list',)})
|
api.keystone: ('tenant_list',)})
|
||||||
def test_usage(self):
|
def test_usage(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
quota_data = self.quota_usages.first()
|
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.tenants.list())
|
.AndReturn(self.tenants.list())
|
||||||
api.nova.usage_list(IsA(http.HttpRequest),
|
api.nova.usage_list(IsA(http.HttpRequest),
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndReturn([usage_obj])
|
.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()
|
self.mox.ReplayAll()
|
||||||
res = self.client.get(reverse('horizon:admin:overview:index'))
|
res = self.client.get(reverse('horizon:admin:overview:index'))
|
||||||
self.assertTemplateUsed(res, 'admin/overview/usage.html')
|
self.assertTemplateUsed(res, 'admin/overview/usage.html')
|
||||||
@ -70,20 +68,19 @@ class UsageViewTests(test.BaseAdminViewTests):
|
|||||||
usage_obj.vcpu_hours,
|
usage_obj.vcpu_hours,
|
||||||
usage_obj.total_local_gb_usage))
|
usage_obj.total_local_gb_usage))
|
||||||
|
|
||||||
@test.create_stubs({api.nova: ('usage_list',),
|
@test.create_stubs({api.nova: ('usage_list', 'tenant_absolute_limits', ),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.keystone: ('tenant_list',)})
|
api.keystone: ('tenant_list',)})
|
||||||
def test_usage_csv(self):
|
def test_usage_csv(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
quota_data = self.quota_usages.first()
|
|
||||||
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
api.keystone.tenant_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.tenants.list())
|
.AndReturn(self.tenants.list())
|
||||||
api.nova.usage_list(IsA(http.HttpRequest),
|
api.nova.usage_list(IsA(http.HttpRequest),
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndReturn([usage_obj, usage_obj])
|
.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()
|
self.mox.ReplayAll()
|
||||||
csv_url = reverse('horizon:admin:overview:index') + "?format=csv"
|
csv_url = reverse('horizon:admin:overview:index') + "?format=csv"
|
||||||
res = self.client.get(csv_url)
|
res = self.client.get(csv_url)
|
||||||
|
@ -16,26 +16,26 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="quota-dynamic">
|
<div class="quota-dynamic">
|
||||||
<h4>{% trans "Project Quotas" %}</h4>
|
<h4>{% trans "Project Limits" %}</h4>
|
||||||
<div class="quota_title clearfix">
|
<div class="quota_title clearfix">
|
||||||
<strong>{% trans "Number of Instances" %}</strong>
|
<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>
|
||||||
<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>
|
||||||
|
|
||||||
<div class="quota_title clearfix">
|
<div class="quota_title clearfix">
|
||||||
<strong>{% trans "Number of VCPUs" %}</strong>
|
<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>
|
||||||
<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>
|
||||||
|
|
||||||
<div class="quota_title clearfix">
|
<div class="quota_title clearfix">
|
||||||
<strong>{% trans "Total RAM" %}</strong>
|
<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>
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ from mox import IsA, IgnoreArg
|
|||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.api import cinder
|
from openstack_dashboard.api import cinder
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
from .tables import LaunchLink
|
from .tables import LaunchLink
|
||||||
from .tabs import InstanceDetailTabs
|
from .tabs import InstanceDetailTabs
|
||||||
from .workflows import LaunchInstance
|
from .workflows import LaunchInstance
|
||||||
@ -798,14 +797,13 @@ class InstanceTests(test.TestCase):
|
|||||||
@test.create_stubs({api.nova: ('flavor_list',
|
@test.create_stubs({api.nova: ('flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'security_group_list',
|
'security_group_list',
|
||||||
|
'tenant_absolute_limits',
|
||||||
'availability_zone_list',),
|
'availability_zone_list',),
|
||||||
cinder: ('volume_snapshot_list',
|
cinder: ('volume_snapshot_list',
|
||||||
'volume_list',),
|
'volume_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
api.glance: ('image_list_detailed',)})
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_launch_instance_get(self):
|
def test_launch_instance_get(self):
|
||||||
quota_usages = self.quota_usages.first()
|
|
||||||
image = self.images.first()
|
image = self.images.first()
|
||||||
|
|
||||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||||
@ -827,8 +825,8 @@ class InstanceTests(test.TestCase):
|
|||||||
api.quantum.network_list(IsA(http.HttpRequest),
|
api.quantum.network_list(IsA(http.HttpRequest),
|
||||||
shared=True) \
|
shared=True) \
|
||||||
.AndReturn(self.networks.list()[1:])
|
.AndReturn(self.networks.list()[1:])
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndReturn(quota_usages)
|
.AndReturn(self.limits['absolute'])
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.flavors.list())
|
.AndReturn(self.flavors.list())
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
@ -861,7 +859,6 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.nova: ('flavor_list',
|
api.nova: ('flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'security_group_list',
|
'security_group_list',
|
||||||
@ -941,8 +938,8 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.nova: ('flavor_list',
|
api.nova: ('flavor_list',
|
||||||
|
'tenant_absolute_limits',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'security_group_list',
|
'security_group_list',
|
||||||
'availability_zone_list',
|
'availability_zone_list',
|
||||||
@ -963,7 +960,8 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.flavors.list())
|
.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),
|
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||||
filters={'is_public': True,
|
filters={'is_public': True,
|
||||||
'status': 'active'}) \
|
'status': 'active'}) \
|
||||||
@ -1021,7 +1019,6 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.nova: ('flavor_list',
|
api.nova: ('flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'security_group_list',
|
'security_group_list',
|
||||||
@ -1105,7 +1102,6 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.nova: ('server_create',
|
api.nova: ('server_create',
|
||||||
'flavor_list',
|
'flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
@ -1191,11 +1187,11 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.nova: ('flavor_list',
|
api.nova: ('flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'availability_zone_list',
|
'availability_zone_list',
|
||||||
'security_group_list',),
|
'security_group_list',
|
||||||
|
'tenant_absolute_limits',),
|
||||||
cinder: ('volume_list',
|
cinder: ('volume_list',
|
||||||
'volume_snapshot_list',)})
|
'volume_snapshot_list',)})
|
||||||
def test_launch_instance_post_no_images_available(self):
|
def test_launch_instance_post_no_images_available(self):
|
||||||
@ -1208,7 +1204,8 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.flavors.list())
|
.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),
|
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||||
filters={'is_public': True,
|
filters={'is_public': True,
|
||||||
'status': 'active'}) \
|
'status': 'active'}) \
|
||||||
@ -1262,12 +1259,12 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
cinder: ('volume_list',
|
cinder: ('volume_list',
|
||||||
'volume_snapshot_list',),
|
'volume_snapshot_list',),
|
||||||
api.nova: ('flavor_list',
|
api.nova: ('flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'security_group_list',
|
'security_group_list',
|
||||||
|
'tenant_absolute_limits',
|
||||||
'availability_zone_list',)})
|
'availability_zone_list',)})
|
||||||
def test_launch_flavorlist_error(self):
|
def test_launch_flavorlist_error(self):
|
||||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||||
@ -1289,8 +1286,8 @@ class InstanceTests(test.TestCase):
|
|||||||
api.quantum.network_list(IsA(http.HttpRequest),
|
api.quantum.network_list(IsA(http.HttpRequest),
|
||||||
shared=True) \
|
shared=True) \
|
||||||
.AndReturn(self.networks.list()[1:])
|
.AndReturn(self.networks.list()[1:])
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.quota_usages.first())
|
.AndReturn(self.limits['absolute'])
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndRaise(self.exceptions.nova)
|
.AndRaise(self.exceptions.nova)
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
@ -1390,10 +1387,10 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.nova: ('flavor_list',
|
api.nova: ('flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'security_group_list',
|
'security_group_list',
|
||||||
|
'tenant_absolute_limits',
|
||||||
'availability_zone_list',),
|
'availability_zone_list',),
|
||||||
cinder: ('volume_list',
|
cinder: ('volume_list',
|
||||||
'volume_snapshot_list',)})
|
'volume_snapshot_list',)})
|
||||||
@ -1438,8 +1435,8 @@ class InstanceTests(test.TestCase):
|
|||||||
|
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.flavors.list())
|
.AndReturn(self.flavors.list())
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.quota_usages.first())
|
.AndReturn(self.limits['absolute'])
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -1514,15 +1511,14 @@ class InstanceTests(test.TestCase):
|
|||||||
@test.create_stubs({api.nova: ('flavor_list',
|
@test.create_stubs({api.nova: ('flavor_list',
|
||||||
'keypair_list',
|
'keypair_list',
|
||||||
'security_group_list',
|
'security_group_list',
|
||||||
'availability_zone_list',),
|
'availability_zone_list',
|
||||||
|
'tenant_absolute_limits',),
|
||||||
cinder: ('volume_snapshot_list',
|
cinder: ('volume_snapshot_list',
|
||||||
'volume_list',),
|
'volume_list',),
|
||||||
quotas: ('tenant_quota_usages',),
|
|
||||||
api.quantum: ('network_list',),
|
api.quantum: ('network_list',),
|
||||||
api.glance: ('image_list_detailed',)})
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_select_default_keypair_if_only_one(self):
|
def test_select_default_keypair_if_only_one(self):
|
||||||
keypair = self.keypairs.first()
|
keypair = self.keypairs.first()
|
||||||
quota_usages = self.quota_usages.first()
|
|
||||||
image = self.images.first()
|
image = self.images.first()
|
||||||
|
|
||||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||||
@ -1544,8 +1540,8 @@ class InstanceTests(test.TestCase):
|
|||||||
api.quantum.network_list(IsA(http.HttpRequest),
|
api.quantum.network_list(IsA(http.HttpRequest),
|
||||||
shared=True) \
|
shared=True) \
|
||||||
.AndReturn(self.networks.list()[1:])
|
.AndReturn(self.networks.list()[1:])
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(quota_usages)
|
.AndReturn(self.limits['absolute'])
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.flavors.list())
|
.AndReturn(self.flavors.list())
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
@ -1627,19 +1623,18 @@ class InstanceTests(test.TestCase):
|
|||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
@test.create_stubs({api.nova: ('server_get',
|
@test.create_stubs({api.nova: ('server_get',
|
||||||
'flavor_list',),
|
'flavor_list',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'tenant_absolute_limits')})
|
||||||
def test_instance_resize_get(self):
|
def test_instance_resize_get(self):
|
||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
|
|
||||||
api.nova.server_get(IsA(http.HttpRequest), server.id) \
|
api.nova.server_get(IsA(http.HttpRequest), server.id) \
|
||||||
.AndReturn(server)
|
.AndReturn(server)
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.flavors.list())
|
.AndReturn(self.flavors.list())
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.flavors.list())
|
.AndReturn(self.flavors.list())
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest)) \
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||||
.AndReturn(self.quota_usages.first())
|
.AndReturn(self.limits['absolute'])
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ from horizon.utils import validators
|
|||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.api import cinder
|
from openstack_dashboard.api import cinder
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
from ...images_and_snapshots.utils import get_available_images
|
from ...images_and_snapshots.utils import get_available_images
|
||||||
|
|
||||||
|
|
||||||
@ -293,7 +292,7 @@ class SetInstanceDetailsAction(workflows.Action):
|
|||||||
def get_help_text(self):
|
def get_help_text(self):
|
||||||
extra = {}
|
extra = {}
|
||||||
try:
|
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'])
|
extra['usages_json'] = json.dumps(extra['usages'])
|
||||||
flavors = json.dumps([f._info for f in
|
flavors = json.dumps([f._info for f in
|
||||||
api.nova.flavor_list(self.request)])
|
api.nova.flavor_list(self.request)])
|
||||||
|
@ -27,7 +27,6 @@ from horizon import workflows
|
|||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -72,7 +71,7 @@ class SetFlavorChoiceAction(workflows.Action):
|
|||||||
def get_help_text(self):
|
def get_help_text(self):
|
||||||
extra = {}
|
extra = {}
|
||||||
try:
|
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'])
|
extra['usages_json'] = json.dumps(extra['usages'])
|
||||||
flavors = json.dumps([f._info for f in
|
flavors = json.dumps([f._info for f in
|
||||||
api.nova.flavor_list(self.request)])
|
api.nova.flavor_list(self.request)])
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% include "horizon/common/_quota_summary.html" %}
|
{% include "horizon/common/_limit_summary.html" %}
|
||||||
{% include "horizon/common/_usage_summary.html" %}
|
{% include "horizon/common/_usage_summary.html" %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -29,7 +29,6 @@ from mox import IsA, Func
|
|||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard import usage
|
from openstack_dashboard import usage
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
from openstack_dashboard.usage import quotas
|
|
||||||
|
|
||||||
|
|
||||||
INDEX_URL = reverse('horizon:project:overview:index')
|
INDEX_URL = reverse('horizon:project:overview:index')
|
||||||
@ -39,14 +38,14 @@ class UsageViewTests(test.TestCase):
|
|||||||
def test_usage(self):
|
def test_usage(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
quota_data = self.quota_usages.first()
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
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,
|
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndReturn(usage_obj)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||||
@ -57,14 +56,14 @@ class UsageViewTests(test.TestCase):
|
|||||||
def test_unauthorized(self):
|
def test_unauthorized(self):
|
||||||
exc = self.exceptions.nova_unauthorized
|
exc = self.exceptions.nova_unauthorized
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
quota_data = self.quota_usages.first()
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
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,
|
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
|
||||||
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
datetime.datetime(now.year, now.month, 1, 0, 0, 0),
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndRaise(exc)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:project:overview:index')
|
url = reverse('horizon:project:overview:index')
|
||||||
@ -76,16 +75,16 @@ class UsageViewTests(test.TestCase):
|
|||||||
def test_usage_csv(self):
|
def test_usage_csv(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
quota_data = self.quota_usages.first()
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
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)
|
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
timestamp,
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndReturn(usage_obj)
|
.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()
|
self.mox.ReplayAll()
|
||||||
res = self.client.get(reverse('horizon:project:overview:index') +
|
res = self.client.get(reverse('horizon:project:overview:index') +
|
||||||
@ -95,16 +94,16 @@ class UsageViewTests(test.TestCase):
|
|||||||
|
|
||||||
def test_usage_exception_usage(self):
|
def test_usage_exception_usage(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
quota_data = self.quota_usages.first()
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
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)
|
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
timestamp,
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndRaise(self.exceptions.nova)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||||
@ -115,15 +114,15 @@ class UsageViewTests(test.TestCase):
|
|||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
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)
|
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
timestamp,
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndReturn(usage_obj)
|
.AndReturn(usage_obj)
|
||||||
quotas.tenant_quota_usages(IsA(http.HttpRequest))\
|
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||||
.AndRaise(self.exceptions.nova)
|
.AndRaise(self.exceptions.nova)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||||
@ -133,16 +132,16 @@ class UsageViewTests(test.TestCase):
|
|||||||
def test_usage_default_tenant(self):
|
def test_usage_default_tenant(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
usage_obj = api.nova.NovaUsage(self.usages.first())
|
usage_obj = api.nova.NovaUsage(self.usages.first())
|
||||||
quota_data = self.quota_usages.first()
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'usage_get')
|
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)
|
timestamp = datetime.datetime(now.year, now.month, 1, 0, 0, 0)
|
||||||
api.nova.usage_get(IsA(http.HttpRequest),
|
api.nova.usage_get(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
timestamp,
|
timestamp,
|
||||||
Func(usage.almost_now)) \
|
Func(usage.almost_now)) \
|
||||||
.AndReturn(usage_obj)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:project:overview:index'))
|
res = self.client.get(reverse('horizon:project:overview:index'))
|
||||||
|
@ -164,7 +164,15 @@ class CreateForm(forms.SelfHandlingForm):
|
|||||||
# error message when the quota is exceeded when trying to create
|
# error message when the quota is exceeded when trying to create
|
||||||
# a volume, so we need to check for that scenario here before we
|
# a volume, so we need to check for that scenario here before we
|
||||||
# send it off to try and create.
|
# 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
|
snapshot_id = None
|
||||||
image_id = None
|
image_id = None
|
||||||
@ -196,14 +204,14 @@ class CreateForm(forms.SelfHandlingForm):
|
|||||||
if type(data['size']) is str:
|
if type(data['size']) is str:
|
||||||
data['size'] = int(data['size'])
|
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 '
|
error_message = _('A volume of %(req)iGB cannot be created as '
|
||||||
'you only have %(avail)iGB of your quota '
|
'you only have %(avail)iGB of your quota '
|
||||||
'available.')
|
'available.')
|
||||||
params = {'req': data['size'],
|
params = {'req': data['size'],
|
||||||
'avail': usages['gigabytes']['available']}
|
'avail': availableGB}
|
||||||
raise ValidationError(error_message % params)
|
raise ValidationError(error_message % params)
|
||||||
elif usages['volumes']['available'] <= 0:
|
elif availableVol <= 0:
|
||||||
error_message = _('You are already using all of your available'
|
error_message = _('You are already using all of your available'
|
||||||
' volumes.')
|
' volumes.')
|
||||||
raise ValidationError(error_message)
|
raise ValidationError(error_message)
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right quota-dynamic">
|
<div class="right quota-dynamic">
|
||||||
{% include "project/volumes/_quota.html" with usages=usages %}
|
{% include "project/volumes/_limits.html" with usages=usages %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -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>
|
@ -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>
|
|
@ -23,7 +23,7 @@ from django.conf import settings
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.forms import widgets
|
from django.forms import widgets
|
||||||
|
|
||||||
from mox import IsA
|
from mox import IsA, IgnoreArg
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.api import cinder
|
from openstack_dashboard.api import cinder
|
||||||
@ -34,13 +34,16 @@ from openstack_dashboard.usage import quotas
|
|||||||
class VolumeViewTests(test.TestCase):
|
class VolumeViewTests(test.TestCase):
|
||||||
@test.create_stubs({cinder: ('volume_create',
|
@test.create_stubs({cinder: ('volume_create',
|
||||||
'volume_snapshot_list',
|
'volume_snapshot_list',
|
||||||
'volume_type_list',),
|
'volume_type_list',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'volume_list',),
|
||||||
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume(self):
|
def test_create_volume(self):
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
volume_type = self.volume_types.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',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'description': u'This is a volume I am making for a test.',
|
||||||
'method': u'CreateForm',
|
'method': u'CreateForm',
|
||||||
@ -50,7 +53,10 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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)).\
|
cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_snapshots.list())
|
AndReturn(self.volume_snapshots.list())
|
||||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||||
@ -80,12 +86,14 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_create',
|
@test.create_stubs({cinder: ('volume_create',
|
||||||
'volume_snapshot_list',
|
'volume_snapshot_list',
|
||||||
'volume_type_list',),
|
'volume_type_list',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'volume_list',),
|
||||||
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume_dropdown(self):
|
def test_create_volume_dropdown(self):
|
||||||
volume = self.volumes.first()
|
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',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'description': u'This is a volume I am making for a test.',
|
||||||
'method': u'CreateForm',
|
'method': u'CreateForm',
|
||||||
@ -107,7 +115,10 @@ class VolumeViewTests(test.TestCase):
|
|||||||
filters={'property-owner_id': self.tenant.id,
|
filters={'property-owner_id': self.tenant.id,
|
||||||
'status': 'active'}) \
|
'status': 'active'}) \
|
||||||
.AndReturn([[], False])
|
.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),
|
cinder.volume_create(IsA(http.HttpRequest),
|
||||||
formData['size'],
|
formData['size'],
|
||||||
formData['name'],
|
formData['name'],
|
||||||
@ -129,11 +140,13 @@ class VolumeViewTests(test.TestCase):
|
|||||||
@test.create_stubs({cinder: ('volume_create',
|
@test.create_stubs({cinder: ('volume_create',
|
||||||
'volume_snapshot_get',
|
'volume_snapshot_get',
|
||||||
'volume_get',
|
'volume_get',
|
||||||
'volume_type_list',),
|
'volume_type_list',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'tenant_absolute_limits',
|
||||||
|
'volume_list',)})
|
||||||
def test_create_volume_from_snapshot(self):
|
def test_create_volume_from_snapshot(self):
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
usage_limit = {'maxTotalVolumeGigabytes': 250,
|
||||||
|
'maxTotalVolumes': 6}
|
||||||
snapshot = self.volume_snapshots.first()
|
snapshot = self.volume_snapshots.first()
|
||||||
formData = {'name': u'A Volume I Am Making',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'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)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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),
|
cinder.volume_snapshot_get(IsA(http.HttpRequest),
|
||||||
str(snapshot.id)).AndReturn(snapshot)
|
str(snapshot.id)).AndReturn(snapshot)
|
||||||
cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\
|
cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\
|
||||||
@ -173,12 +189,14 @@ class VolumeViewTests(test.TestCase):
|
|||||||
'volume_snapshot_list',
|
'volume_snapshot_list',
|
||||||
'volume_snapshot_get',
|
'volume_snapshot_get',
|
||||||
'volume_get',
|
'volume_get',
|
||||||
'volume_type_list',),
|
'volume_type_list',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'volume_list',),
|
||||||
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume_from_snapshot_dropdown(self):
|
def test_create_volume_from_snapshot_dropdown(self):
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
usage_limit = {'maxTotalVolumeGigabytes': 250,
|
||||||
|
'maxTotalVolumes': 6}
|
||||||
snapshot = self.volume_snapshots.first()
|
snapshot = self.volume_snapshots.first()
|
||||||
formData = {'name': u'A Volume I Am Making',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'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,
|
filters={'property-owner_id': self.tenant.id,
|
||||||
'status': 'active'}) \
|
'status': 'active'}) \
|
||||||
.AndReturn([[], False])
|
.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),
|
cinder.volume_snapshot_get(IsA(http.HttpRequest),
|
||||||
str(snapshot.id)).AndReturn(snapshot)
|
str(snapshot.id)).AndReturn(snapshot)
|
||||||
cinder.volume_create(IsA(http.HttpRequest),
|
cinder.volume_create(IsA(http.HttpRequest),
|
||||||
@ -224,11 +245,13 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_snapshot_get',
|
@test.create_stubs({cinder: ('volume_snapshot_get',
|
||||||
'volume_type_list',
|
'volume_type_list',
|
||||||
'volume_get',),
|
'volume_get',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'volume_list',),
|
||||||
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume_from_snapshot_invalid_size(self):
|
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()
|
snapshot = self.volume_snapshots.first()
|
||||||
formData = {'name': u'A Volume I Am Making',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'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)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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),
|
cinder.volume_snapshot_get(IsA(http.HttpRequest),
|
||||||
str(snapshot.id)).AndReturn(snapshot)
|
str(snapshot.id)).AndReturn(snapshot)
|
||||||
cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\
|
cinder.volume_get(IsA(http.HttpRequest), snapshot.volume_id).\
|
||||||
AndReturn(self.volumes.first())
|
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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -256,12 +285,14 @@ class VolumeViewTests(test.TestCase):
|
|||||||
"snapshot size (40GB)")
|
"snapshot size (40GB)")
|
||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_create',
|
@test.create_stubs({cinder: ('volume_create',
|
||||||
'volume_type_list',),
|
'volume_type_list',
|
||||||
api.glance: ('image_get',),
|
'tenant_absolute_limits',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'volume_list',),
|
||||||
|
api.glance: ('image_get',)})
|
||||||
def test_create_volume_from_image(self):
|
def test_create_volume_from_image(self):
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
usage_limit = {'maxTotalVolumeGigabytes': 200,
|
||||||
|
'maxTotalVolumes': 6}
|
||||||
image = self.images.first()
|
image = self.images.first()
|
||||||
formData = {'name': u'A Volume I Am Making',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'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)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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),
|
api.glance.image_get(IsA(http.HttpRequest),
|
||||||
str(image.id)).AndReturn(image)
|
str(image.id)).AndReturn(image)
|
||||||
cinder.volume_create(IsA(http.HttpRequest),
|
cinder.volume_create(IsA(http.HttpRequest),
|
||||||
@ -298,13 +332,15 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_create',
|
@test.create_stubs({cinder: ('volume_create',
|
||||||
'volume_type_list',
|
'volume_type_list',
|
||||||
'volume_snapshot_list',),
|
'volume_snapshot_list',
|
||||||
|
'tenant_absolute_limits',
|
||||||
|
'volume_list',),
|
||||||
api.glance: ('image_get',
|
api.glance: ('image_get',
|
||||||
'image_list_detailed'),
|
'image_list_detailed')})
|
||||||
quotas: ('tenant_quota_usages',)})
|
|
||||||
def test_create_volume_from_image_dropdown(self):
|
def test_create_volume_from_image_dropdown(self):
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
usage = {'gigabytes': {'available': 250}, 'volumes': {'available': 6}}
|
usage_limit = {'maxTotalVolumeGigabytes': 200,
|
||||||
|
'maxTotalVolumes': 6}
|
||||||
image = self.images.first()
|
image = self.images.first()
|
||||||
formData = {'name': u'A Volume I Am Making',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'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,
|
filters={'property-owner_id': self.tenant.id,
|
||||||
'status': 'active'}) \
|
'status': 'active'}) \
|
||||||
.AndReturn([[], False])
|
.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),
|
api.glance.image_get(IsA(http.HttpRequest),
|
||||||
str(image.id)).AndReturn(image)
|
str(image.id)).AndReturn(image)
|
||||||
cinder.volume_create(IsA(http.HttpRequest),
|
cinder.volume_create(IsA(http.HttpRequest),
|
||||||
@ -349,12 +388,13 @@ class VolumeViewTests(test.TestCase):
|
|||||||
redirect_url = reverse('horizon:project:volumes:index')
|
redirect_url = reverse('horizon:project:volumes:index')
|
||||||
self.assertRedirectsNoFollow(res, redirect_url)
|
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',
|
api.glance: ('image_get',
|
||||||
'image_list_detailed'),
|
'image_list_detailed')})
|
||||||
quotas: ('tenant_quota_usages',)})
|
|
||||||
def test_create_volume_from_image_invalid_size(self):
|
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()
|
image = self.images.first()
|
||||||
formData = {'name': u'A Volume I Am Making',
|
formData = {'name': u'A Volume I Am Making',
|
||||||
'description': u'This is a volume I am making for a test.',
|
'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)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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),
|
api.glance.image_get(IsA(http.HttpRequest),
|
||||||
str(image.id)).AndReturn(image)
|
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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -379,11 +425,12 @@ class VolumeViewTests(test.TestCase):
|
|||||||
"The volume size cannot be less than the "
|
"The volume size cannot be less than the "
|
||||||
"image size (20.0 GB)")
|
"image size (20.0 GB)")
|
||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',),
|
@test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits', 'volume_list',),
|
||||||
quotas: ('tenant_quota_usages',)})
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume_gb_used_over_alloted_quota(self):
|
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!',
|
formData = {'name': u'This Volume Is Huge!',
|
||||||
'description': u'This is a volume that is just too big!',
|
'description': u'This is a volume that is just too big!',
|
||||||
'method': u'CreateForm',
|
'method': u'CreateForm',
|
||||||
@ -391,7 +438,10 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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)).\
|
cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_snapshots.list())
|
AndReturn(self.volume_snapshots.list())
|
||||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||||
@ -402,7 +452,10 @@ class VolumeViewTests(test.TestCase):
|
|||||||
filters={'property-owner_id': self.tenant.id,
|
filters={'property-owner_id': self.tenant.id,
|
||||||
'status': 'active'}) \
|
'status': 'active'}) \
|
||||||
.AndReturn([[], False])
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -410,15 +463,15 @@ class VolumeViewTests(test.TestCase):
|
|||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, formData)
|
||||||
|
|
||||||
expected_error = [u'A volume of 5000GB cannot be created as you only'
|
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)
|
self.assertEqual(res.context['form'].errors['__all__'], expected_error)
|
||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',),
|
@test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits', 'volume_list',),
|
||||||
quotas: ('tenant_quota_usages',)})
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume_number_over_alloted_quota(self):
|
def test_create_volume_number_over_alloted_quota(self):
|
||||||
usage = {'gigabytes': {'available': 100, 'used': 20},
|
usage_limit = {'maxTotalVolumeGigabytes': 100,
|
||||||
'volumes': {'available': 0}}
|
'maxTotalVolumes': len(self.volumes.list())}
|
||||||
formData = {'name': u'Too Many...',
|
formData = {'name': u'Too Many...',
|
||||||
'description': u'We have no volumes left!',
|
'description': u'We have no volumes left!',
|
||||||
'method': u'CreateForm',
|
'method': u'CreateForm',
|
||||||
@ -426,7 +479,10 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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)).\
|
cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_snapshots.list())
|
AndReturn(self.volume_snapshots.list())
|
||||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||||
@ -437,7 +493,10 @@ class VolumeViewTests(test.TestCase):
|
|||||||
filters={'property-owner_id': self.tenant.id,
|
filters={'property-owner_id': self.tenant.id,
|
||||||
'status': 'active'}) \
|
'status': 'active'}) \
|
||||||
.AndReturn([[], False])
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -450,13 +509,16 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_create',
|
@test.create_stubs({cinder: ('volume_create',
|
||||||
'volume_snapshot_list',
|
'volume_snapshot_list',
|
||||||
'volume_type_list',),
|
'volume_type_list',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits',
|
||||||
quotas: ('tenant_quota_usages',)})
|
'volume_list',),
|
||||||
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume_encrypted(self):
|
def test_create_volume_encrypted(self):
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
volume_type = self.volume_types.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',
|
formData = {'name': u'An Encrypted Volume',
|
||||||
'description': u'This volume has metadata for encryption.',
|
'description': u'This volume has metadata for encryption.',
|
||||||
'method': u'CreateForm',
|
'method': u'CreateForm',
|
||||||
@ -471,7 +533,10 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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)).\
|
cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_snapshots.list())
|
AndReturn(self.volume_snapshots.list())
|
||||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||||
@ -501,9 +566,9 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
settings.OPENSTACK_HYPERVISOR_FEATURES['can_encrypt_volumes'] = PREV
|
settings.OPENSTACK_HYPERVISOR_FEATURES['can_encrypt_volumes'] = PREV
|
||||||
|
|
||||||
@test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',),
|
@test.create_stubs({cinder: ('volume_snapshot_list', 'volume_type_list',
|
||||||
api.glance: ('image_list_detailed',),
|
'tenant_absolute_limits', 'volume_list',),
|
||||||
quotas: ('tenant_quota_usages',)})
|
api.glance: ('image_list_detailed',)})
|
||||||
def test_create_volume_cannot_encrypt(self):
|
def test_create_volume_cannot_encrypt(self):
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
volume_type = self.volume_types.first()
|
volume_type = self.volume_types.first()
|
||||||
@ -522,11 +587,16 @@ class VolumeViewTests(test.TestCase):
|
|||||||
|
|
||||||
volume = self.volumes.first()
|
volume = self.volumes.first()
|
||||||
volume_type = self.volume_types.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)).\
|
cinder.volume_type_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_types.list())
|
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)).\
|
cinder.volume_snapshot_list(IsA(http.HttpRequest)).\
|
||||||
AndReturn(self.volume_snapshots.list())
|
AndReturn(self.volume_snapshots.list())
|
||||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||||
|
@ -99,7 +99,15 @@ class CreateView(forms.ModalFormView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(CreateView, self).get_context_data(**kwargs)
|
context = super(CreateView, self).get_context_data(**kwargs)
|
||||||
try:
|
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:
|
except:
|
||||||
exceptions.handle(self.request)
|
exceptions.handle(self.request)
|
||||||
return context
|
return context
|
||||||
|
@ -32,6 +32,7 @@ class BaseUsage(object):
|
|||||||
self.request = request
|
self.request = request
|
||||||
self.summary = {}
|
self.summary = {}
|
||||||
self.usage_list = []
|
self.usage_list = []
|
||||||
|
self.limits = {}
|
||||||
self.quotas = {}
|
self.quotas = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -82,6 +83,13 @@ class BaseUsage(object):
|
|||||||
'year': self.today.year})
|
'year': self.today.year})
|
||||||
return self.form
|
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):
|
def get_usage_list(self, start, end):
|
||||||
raise NotImplementedError("You must define a get_usage method.")
|
raise NotImplementedError("You must define a get_usage method.")
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class UsageView(tables.DataTableView):
|
|||||||
tenant_id = self.kwargs.get('tenant_id', self.request.user.tenant_id)
|
tenant_id = self.kwargs.get('tenant_id', self.request.user.tenant_id)
|
||||||
self.usage = self.usage_class(self.request, tenant_id)
|
self.usage = self.usage_class(self.request, tenant_id)
|
||||||
self.usage.summarize(*self.usage.get_date_range())
|
self.usage.summarize(*self.usage.get_date_range())
|
||||||
self.usage.get_quotas()
|
self.usage.get_limits()
|
||||||
self.kwargs['usage'] = self.usage
|
self.kwargs['usage'] = self.usage
|
||||||
return self.usage.usage_list
|
return self.usage.usage_list
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user