From ea1edcb027fea9d609326847be3cafb380534a56 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 22 May 2013 21:58:01 +0200 Subject: [PATCH] Enable snapshot quota setting Change-Id: Ie8bcd2d2fddf717a29328bc802a2fda80198183b Fixes: bug #1177249 --- .../horizon/common/_quota_summary.html | 6 +++ .../dashboards/admin/info/tests.py | 3 +- .../dashboards/admin/projects/tests.py | 10 ++++- .../dashboards/admin/projects/workflows.py | 1 + .../volume_snapshots/tests.py | 7 +++ .../volumes/templates/volumes/_create.html | 34 +------------- .../templates/volumes/_create_snapshot.html | 17 ++++--- .../volumes/templates/volumes/_quota.html | 43 ++++++++++++++++++ .../dashboards/project/volumes/views.py | 4 ++ .../test/test_data/cinder_data.py | 45 +++++++++++++++++++ openstack_dashboard/test/test_data/utils.py | 2 + openstack_dashboard/test/tests/quotas.py | 16 +++++-- openstack_dashboard/usage/quotas.py | 3 ++ 13 files changed, 143 insertions(+), 48 deletions(-) create mode 100644 openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html create mode 100644 openstack_dashboard/test/test_data/cinder_data.py diff --git a/horizon/templates/horizon/common/_quota_summary.html b/horizon/templates/horizon/common/_quota_summary.html index 7ebbbc51d1..026af423ff 100644 --- a/horizon/templates/horizon/common/_quota_summary.html +++ b/horizon/templates/horizon/common/_quota_summary.html @@ -30,6 +30,12 @@ {% blocktrans with used=usage.quotas.volumes.used|intcomma available=usage.quotas.volumes.quota|intcomma %} Used {{ used }} of {{ available }} {% endblocktrans %} +
+
+ {% trans "Available Snapshots" %}
+ {% blocktrans with used=usage.quotas.snapshots.used|intcomma available=usage.quotas.snapshots.quota|intcomma %} Used {{ used }} of {{ available }} {% endblocktrans %} +
+
{% trans "Available Volume Storage" %}
diff --git a/openstack_dashboard/dashboards/admin/info/tests.py b/openstack_dashboard/dashboards/admin/info/tests.py index 9bb88fdf30..162268c8cd 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() @@ -59,6 +59,7 @@ class ServicesViewTests(test.BaseAdminViewTests): '', '', '', + '', '', '', '', diff --git a/openstack_dashboard/dashboards/admin/projects/tests.py b/openstack_dashboard/dashboards/admin/projects/tests.py index a8ec8cab17..5baf03d442 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 2337da57c6..b9b39812c0 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 27800b43d8..16e4d4e204 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 fc64394bdc..b4670e6ba1 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 @@
-

{% trans "Description" %}:

- -

{% trans "Volumes are block devices that can be attached to instances." %}

- -

{% trans "Volume Quotas" %}

- -
- {% trans "Total Gigabytes" %} ({{ usages.gigabytes.used|intcomma }} {% trans "GB" %}) -

{{ usages.gigabytes.available|quota:_("GB")|intcomma }}

-
- -
-
- -
- {% trans "Number of Volumes" %} ({{ usages.volumes.used|intcomma }}) -

{{ usages.volumes.available|quota|intcomma }}

-
- -
-
+ {% include "project/volumes/_quota.html" with usages=usages %}
- - {% 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 446a0d0b8d..97c97bff1c 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 %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description" %}:

-

{% trans "Volumes are block devices that can be attached to instances." %}

-
+
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+ {% include "project/volumes/_quota.html" with usages=usages snapshot_quota=True %} +
{% 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 0000000000..f145385224 --- /dev/null +++ b/openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html @@ -0,0 +1,43 @@ +{% load i18n horizon humanize %} + +

{% trans "Description" %}:

+ +

{% trans "Volumes are block devices that can be attached to instances." %}

+ +

{% trans "Volume Quotas" %}

+ +
+ {% trans "Total Gigabytes" %} ({{ usages.gigabytes.used|intcomma }} {% trans "GB" %}) +

{{ usages.gigabytes.available|quota:_("GB")|intcomma }}

+
+ +
+
+ +{% if snapshot_quota %} +
+ {% trans "Number of Snapshots" %} ({{ usages.snapshots.used|intcomma }}) +

{{ usages.snapshots.available|quota|intcomma }}

+
+ +
+
+{% else %} +
+ {% trans "Number of Volumes" %} ({{ usages.volumes.used|intcomma }}) +

{{ usages.volumes.available|quota|intcomma }}

+
+ +
+
+{% endif %} + + diff --git a/openstack_dashboard/dashboards/project/volumes/views.py b/openstack_dashboard/dashboards/project/volumes/views.py index fedaeea378..c423488bc0 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 0000000000..7ae08cb792 --- /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 1a080aa956..ac73408fb7 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 1c65f40cad..29061a5dfc 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 99245097c6..68ec9b3b5c 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]: