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):
|
||||
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 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)
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)])
|
||||
|
@ -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)])
|
||||
|
@ -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 %}
|
||||
|
@ -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'))
|
||||
|
@ -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)
|
||||
|
@ -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 %}
|
||||
|
||||
|
@ -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.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),
|
||||
|
@ -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
|
||||
|
@ -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.")
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user