diff options
author | Jenkins <jenkins@review.openstack.org> | 2013-05-29 07:11:43 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2013-05-29 07:11:43 +0000 |
commit | 144fda877fb1a757626fe074f0f80d7ee7e44e34 (patch) | |
tree | 0055928b6f8d729ae1739e2473be84d79766f29b | |
parent | 1a6ca7bfc65e86e48cb0422310f36baee6a695b3 (diff) | |
parent | ea1edcb027fea9d609326847be3cafb380534a56 (diff) | |
download | horizon-2013.2.b1.tar.gz |
Merge "Enable snapshot quota setting"2013.2.b1
13 files changed, 143 insertions, 48 deletions
diff --git a/horizon/templates/horizon/common/_quota_summary.html b/horizon/templates/horizon/common/_quota_summary.html index 7ebbbc51d..026af423f 100644 --- a/horizon/templates/horizon/common/_quota_summary.html +++ b/horizon/templates/horizon/common/_quota_summary.html @@ -31,6 +31,12 @@ </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 %} diff --git a/openstack_dashboard/dashboards/admin/info/tests.py b/openstack_dashboard/dashboards/admin/info/tests.py index ae974d2c6..6180bb5a4 100644 --- a/openstack_dashboard/dashboards/admin/info/tests.py +++ b/openstack_dashboard/dashboards/admin/info/tests.py @@ -31,7 +31,7 @@ class ServicesViewTests(test.BaseAdminViewTests): api.nova.default_quota_get(IsA(http.HttpRequest), self.tenant.id).AndReturn(self.quotas.nova) api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id) \ - .AndReturn(self.quotas.nova) + .AndReturn(self.cinder_quotas.first()) self.mox.ReplayAll() @@ -60,6 +60,7 @@ class ServicesViewTests(test.BaseAdminViewTests): '<Quota: (floating_ips, 1)>', '<Quota: (fixed_ips, 10)>', '<Quota: (instances, 10)>', + '<Quota: (snapshots, 1)>', '<Quota: (volumes, 1)>', '<Quota: (cores, 10)>', '<Quota: (security_groups, 10)>', diff --git a/openstack_dashboard/dashboards/admin/projects/tests.py b/openstack_dashboard/dashboards/admin/projects/tests.py index a8ec8cab1..5baf03d44 100644 --- a/openstack_dashboard/dashboards/admin/projects/tests.py +++ b/openstack_dashboard/dashboards/admin/projects/tests.py @@ -58,9 +58,12 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests): return project_info def _get_quota_info(self, quota): + cinder_quota = self.cinder_quotas.first() quota_data = {} - for field in quotas.QUOTA_FIELDS: + for field in quotas.NOVA_QUOTA_FIELDS: quota_data[field] = int(quota.get(field).limit) + for field in quotas.CINDER_QUOTA_FIELDS: + quota_data[field] = int(cinder_quota.get(field).limit) return quota_data def _get_workflow_data(self, project, quota): @@ -403,9 +406,12 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests): class UpdateProjectWorkflowTests(test.BaseAdminViewTests): def _get_quota_info(self, quota): + cinder_quota = self.cinder_quotas.first() quota_data = {} - for field in quotas.QUOTA_FIELDS: + for field in quotas.NOVA_QUOTA_FIELDS: quota_data[field] = int(quota.get(field).limit) + for field in quotas.CINDER_QUOTA_FIELDS: + quota_data[field] = int(cinder_quota.get(field).limit) return quota_data @test.create_stubs({api.keystone: ('get_default_role', diff --git a/openstack_dashboard/dashboards/admin/projects/workflows.py b/openstack_dashboard/dashboards/admin/projects/workflows.py index 2337da57c..b9b39812c 100644 --- a/openstack_dashboard/dashboards/admin/projects/workflows.py +++ b/openstack_dashboard/dashboards/admin/projects/workflows.py @@ -51,6 +51,7 @@ class UpdateProjectQuotaAction(workflows.Action): injected_file_content_bytes = forms.IntegerField(min_value=-1, label=ifcb_label) volumes = forms.IntegerField(min_value=-1, label=_("Volumes")) + snapshots = forms.IntegerField(min_value=-1, label=_("Snapshots")) gigabytes = forms.IntegerField(min_value=-1, label=_("Gigabytes")) ram = forms.IntegerField(min_value=-1, label=_("RAM (MB)")) floating_ips = forms.IntegerField(min_value=-1, label=_("Floating IPs")) diff --git a/openstack_dashboard/dashboards/project/images_and_snapshots/volume_snapshots/tests.py b/openstack_dashboard/dashboards/project/images_and_snapshots/volume_snapshots/tests.py index 27800b43d..16e4d4e20 100644 --- a/openstack_dashboard/dashboards/project/images_and_snapshots/volume_snapshots/tests.py +++ b/openstack_dashboard/dashboards/project/images_and_snapshots/volume_snapshots/tests.py @@ -25,14 +25,21 @@ from mox import IsA 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 INDEX_URL = reverse('horizon:project:images_and_snapshots:index') class VolumeSnapshotsViewTests(test.TestCase): + @test.create_stubs({quotas: ('tenant_quota_usages',)}) def test_create_snapshot_get(self): volume = self.volumes.first() + usage = {'gigabytes': {'available': 250}, + 'snapshots': {'available': 6}} + quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage) + self.mox.ReplayAll() + url = reverse('horizon:project:volumes:create_snapshot', args=[volume.id]) res = self.client.get(url) diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html index fc64394bd..b4670e6ba 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html @@ -1,5 +1,5 @@ {% extends "horizon/common/_modal_form.html" %} -{% load i18n horizon humanize %} +{% load i18n %} {% load url from future %} {% block form_id %}{% endblock %} @@ -16,38 +16,8 @@ </div> <div class="right quota-dynamic"> - <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> - - <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> + {% include "project/volumes/_quota.html" with usages=usages %} </div> - - <script type="text/javascript" charset="utf-8"> - if(typeof horizon.Quota !== 'undefined') { - horizon.Quota.init(); - } else { - addHorizonLoadEvent(function() { - horizon.Quota.init(); - }); - } - </script> {% endblock %} {% block modal-footer %} diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create_snapshot.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create_snapshot.html index 446a0d0b8..97c97bff1 100644 --- a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create_snapshot.html +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_create_snapshot.html @@ -9,15 +9,14 @@ {% block modal-header %}{% trans "Create Volume Snapshot" %}{% endblock %} {% block modal-body %} -<div class="left"> - <fieldset> - {% include "horizon/common/_form_fields.html" %} - </fieldset> -</div> -<div class="right"> - <h3>{% trans "Description" %}:</h3> - <p>{% trans "Volumes are block devices that can be attached to instances." %}</p> -</div> + <div class="left"> + <fieldset> + {% include "horizon/common/_form_fields.html" %} + </fieldset> + </div> + <div class="right quota-dynamic"> + {% include "project/volumes/_quota.html" with usages=usages snapshot_quota=True %} + </div> {% endblock %} {% block modal-footer %} diff --git a/openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html new file mode 100644 index 000000000..f14538522 --- /dev/null +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html @@ -0,0 +1,43 @@ +{% 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> diff --git a/openstack_dashboard/dashboards/project/volumes/views.py b/openstack_dashboard/dashboards/project/volumes/views.py index fedaeea37..c423488bc 100644 --- a/openstack_dashboard/dashboards/project/volumes/views.py +++ b/openstack_dashboard/dashboards/project/volumes/views.py @@ -113,6 +113,10 @@ class CreateSnapshotView(forms.ModalFormView): def get_context_data(self, **kwargs): context = super(CreateSnapshotView, self).get_context_data(**kwargs) context['volume_id'] = self.kwargs['volume_id'] + try: + context['usages'] = quotas.tenant_quota_usages(self.request) + except: + exceptions.handle(self.request) return context def get_initial(self): diff --git a/openstack_dashboard/test/test_data/cinder_data.py b/openstack_dashboard/test/test_data/cinder_data.py new file mode 100644 index 000000000..7ae08cb79 --- /dev/null +++ b/openstack_dashboard/test/test_data/cinder_data.py @@ -0,0 +1,45 @@ +# Copyright 2012 Nebula, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from cinderclient.v1 import quotas +from openstack_dashboard.api.base import Quota, QuotaSet as QuotaSetWrapper +from openstack_dashboard.usage.quotas import QuotaUsage +from .utils import TestDataContainer + + +def data(TEST): + TEST.cinder_quotas = TestDataContainer() + TEST.cinder_quota_usages = TestDataContainer() + + # Quota Sets + quota_data = dict(volumes='1', + snapshots='1', + gigabytes='1000') + quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data) + #TEST.quotas.cinder = QuotaSetWrapper(quota) + TEST.cinder_quotas.add(QuotaSetWrapper(quota)) + + # Quota Usages + quota_usage_data = {'gigabytes': {'used': 0, + 'quota': 1000}, + 'instances': {'used': 0, + 'quota': 10}, + 'snapshots': {'used': 0, + 'quota': 10}} + quota_usage = QuotaUsage() + for k, v in quota_usage_data.items(): + quota_usage.add_quota(Quota(k, v['quota'])) + quota_usage.tally(k, v['used']) + + TEST.cinder_quota_usages.add(quota_usage) diff --git a/openstack_dashboard/test/test_data/utils.py b/openstack_dashboard/test/test_data/utils.py index 1a080aa95..ac73408fb 100644 --- a/openstack_dashboard/test/test_data/utils.py +++ b/openstack_dashboard/test/test_data/utils.py @@ -18,6 +18,7 @@ def load_test_data(load_onto=None): from . import glance_data from . import keystone_data from . import nova_data + from . import cinder_data from . import quantum_data from . import swift_data from . import heat_data @@ -27,6 +28,7 @@ def load_test_data(load_onto=None): keystone_data.data, glance_data.data, nova_data.data, + cinder_data.data, quantum_data.data, swift_data.data, heat_data.data) diff --git a/openstack_dashboard/test/tests/quotas.py b/openstack_dashboard/test/tests/quotas.py index 1c65f40ca..29061a5df 100644 --- a/openstack_dashboard/test/tests/quotas.py +++ b/openstack_dashboard/test/tests/quotas.py @@ -45,6 +45,8 @@ class QuotaTests(test.APITestCase): 'cores': {'available': 8, 'used': 2, 'quota': 10}} if with_volume: quotas.update({'volumes': {'available': 0, 'used': 3, 'quota': 1}, + 'snapshots': {'available': 0, 'used': 3, + 'quota': 1}, 'gigabytes': {'available': 920, 'used': 80, 'quota': 1000}}) return quotas @@ -54,7 +56,8 @@ class QuotaTests(test.APITestCase): 'tenant_quota_get',), api.network: ('tenant_floating_ip_list',), quotas: ('is_service_enabled',), - cinder: ('volume_list', 'tenant_quota_get',)}) + cinder: ('volume_list', 'volume_snapshot_list', + 'tenant_quota_get',)}) def test_tenant_quota_usages(self): quotas.is_service_enabled(IsA(http.HttpRequest), 'volume').AndReturn(True) @@ -68,8 +71,10 @@ class QuotaTests(test.APITestCase): .AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest)) \ .AndReturn(self.volumes.list()) + cinder.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.snapshots.list()) cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \ - .AndReturn(self.quotas.first()) + .AndReturn(self.cinder_quotas.first()) self.mox.ReplayAll() @@ -139,7 +144,8 @@ class QuotaTests(test.APITestCase): 'tenant_quota_get',), api.network: ('tenant_floating_ip_list',), quotas: ('is_service_enabled',), - cinder: ('volume_list', 'tenant_quota_get',)}) + cinder: ('volume_list', 'volume_snapshot_list', + 'tenant_quota_get',)}) def test_tenant_quota_usages_unlimited_quota(self): inf_quota = self.quotas.first() inf_quota['ram'] = -1 @@ -156,8 +162,10 @@ class QuotaTests(test.APITestCase): .AndReturn([self.servers.list(), False]) cinder.volume_list(IsA(http.HttpRequest)) \ .AndReturn(self.volumes.list()) + cinder.volume_snapshot_list(IsA(http.HttpRequest)) \ + .AndReturn(self.snapshots.list()) cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \ - .AndReturn(inf_quota) + .AndReturn(self.cinder_quotas.first()) self.mox.ReplayAll() diff --git a/openstack_dashboard/usage/quotas.py b/openstack_dashboard/usage/quotas.py index 99245097c..68ec9b3b5 100644 --- a/openstack_dashboard/usage/quotas.py +++ b/openstack_dashboard/usage/quotas.py @@ -19,6 +19,7 @@ NOVA_QUOTA_FIELDS = ("metadata_items", "security_group_rules",) CINDER_QUOTA_FIELDS = ("volumes", + "snapshots", "gigabytes",) QUOTA_FIELDS = NOVA_QUOTA_FIELDS + CINDER_QUOTA_FIELDS @@ -136,8 +137,10 @@ def tenant_quota_usages(request): if 'volumes' not in disabled_quotas: volumes = cinder.volume_list(request) + snapshots = cinder.volume_snapshot_list(request) usages.tally('gigabytes', sum([int(v.size) for v in volumes])) usages.tally('volumes', len(volumes)) + usages.tally('snapshots', len(snapshots)) # Sum our usage based on the flavors of the instances. for flavor in [flavors[instance.flavor['id']] for instance in instances]: |