Browse Source

Merge "Enable snapshot quota setting"

tags/2013.2.b1^0
Jenkins 6 years ago
parent
commit
144fda877f

+ 6
- 0
horizon/templates/horizon/common/_quota_summary.html View File

@@ -30,6 +30,12 @@
30 30
           {% blocktrans with used=usage.quotas.volumes.used|intcomma available=usage.quotas.volumes.quota|intcomma %} Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
31 31
         </strong>
32 32
       </div>
33
+      <div class="d3_quota_bar">
34
+        <div class="d3_pie_chart" data-used="{% widthratio usage.quotas.snapshots.used usage.quotas.snapshots.quota 100 %}"></div>
35
+        <strong>{% trans "Available Snapshots" %} <br />
36
+          {% blocktrans with used=usage.quotas.snapshots.used|intcomma available=usage.quotas.snapshots.quota|intcomma %} Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
37
+        </strong>
38
+      </div>
33 39
       <div class="d3_quota_bar">
34 40
         <div class="d3_pie_chart" data-used="{% widthratio usage.quotas.gigabytes.used usage.quotas.gigabytes.quota 100 %}"></div>
35 41
         <strong>{% trans "Available Volume Storage" %} <br />

+ 2
- 1
openstack_dashboard/dashboards/admin/info/tests.py View File

@@ -31,7 +31,7 @@ class ServicesViewTests(test.BaseAdminViewTests):
31 31
         api.nova.default_quota_get(IsA(http.HttpRequest),
32 32
                                    self.tenant.id).AndReturn(self.quotas.nova)
33 33
         api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id) \
34
-                .AndReturn(self.quotas.nova)
34
+                .AndReturn(self.cinder_quotas.first())
35 35
 
36 36
         self.mox.ReplayAll()
37 37
 
@@ -60,6 +60,7 @@ class ServicesViewTests(test.BaseAdminViewTests):
60 60
                                  '<Quota: (floating_ips, 1)>',
61 61
                                  '<Quota: (fixed_ips, 10)>',
62 62
                                  '<Quota: (instances, 10)>',
63
+                                 '<Quota: (snapshots, 1)>',
63 64
                                  '<Quota: (volumes, 1)>',
64 65
                                  '<Quota: (cores, 10)>',
65 66
                                  '<Quota: (security_groups, 10)>',

+ 8
- 2
openstack_dashboard/dashboards/admin/projects/tests.py View File

@@ -58,9 +58,12 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
58 58
         return project_info
59 59
 
60 60
     def _get_quota_info(self, quota):
61
+        cinder_quota = self.cinder_quotas.first()
61 62
         quota_data = {}
62
-        for field in quotas.QUOTA_FIELDS:
63
+        for field in quotas.NOVA_QUOTA_FIELDS:
63 64
             quota_data[field] = int(quota.get(field).limit)
65
+        for field in quotas.CINDER_QUOTA_FIELDS:
66
+            quota_data[field] = int(cinder_quota.get(field).limit)
64 67
         return quota_data
65 68
 
66 69
     def _get_workflow_data(self, project, quota):
@@ -403,9 +406,12 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
403 406
 
404 407
 class UpdateProjectWorkflowTests(test.BaseAdminViewTests):
405 408
     def _get_quota_info(self, quota):
409
+        cinder_quota = self.cinder_quotas.first()
406 410
         quota_data = {}
407
-        for field in quotas.QUOTA_FIELDS:
411
+        for field in quotas.NOVA_QUOTA_FIELDS:
408 412
             quota_data[field] = int(quota.get(field).limit)
413
+        for field in quotas.CINDER_QUOTA_FIELDS:
414
+            quota_data[field] = int(cinder_quota.get(field).limit)
409 415
         return quota_data
410 416
 
411 417
     @test.create_stubs({api.keystone: ('get_default_role',

+ 1
- 0
openstack_dashboard/dashboards/admin/projects/workflows.py View File

@@ -51,6 +51,7 @@ class UpdateProjectQuotaAction(workflows.Action):
51 51
     injected_file_content_bytes = forms.IntegerField(min_value=-1,
52 52
                                                      label=ifcb_label)
53 53
     volumes = forms.IntegerField(min_value=-1, label=_("Volumes"))
54
+    snapshots = forms.IntegerField(min_value=-1, label=_("Snapshots"))
54 55
     gigabytes = forms.IntegerField(min_value=-1, label=_("Gigabytes"))
55 56
     ram = forms.IntegerField(min_value=-1, label=_("RAM (MB)"))
56 57
     floating_ips = forms.IntegerField(min_value=-1, label=_("Floating IPs"))

+ 7
- 0
openstack_dashboard/dashboards/project/images_and_snapshots/volume_snapshots/tests.py View File

@@ -25,14 +25,21 @@ from mox import IsA
25 25
 from openstack_dashboard import api
26 26
 from openstack_dashboard.api import cinder
27 27
 from openstack_dashboard.test import helpers as test
28
+from openstack_dashboard.usage import quotas
28 29
 
29 30
 
30 31
 INDEX_URL = reverse('horizon:project:images_and_snapshots:index')
31 32
 
32 33
 
33 34
 class VolumeSnapshotsViewTests(test.TestCase):
35
+    @test.create_stubs({quotas: ('tenant_quota_usages',)})
34 36
     def test_create_snapshot_get(self):
35 37
         volume = self.volumes.first()
38
+        usage = {'gigabytes': {'available': 250},
39
+                 'snapshots': {'available': 6}}
40
+        quotas.tenant_quota_usages(IsA(http.HttpRequest)).AndReturn(usage)
41
+        self.mox.ReplayAll()
42
+
36 43
         url = reverse('horizon:project:volumes:create_snapshot',
37 44
                       args=[volume.id])
38 45
         res = self.client.get(url)

+ 2
- 32
openstack_dashboard/dashboards/project/volumes/templates/volumes/_create.html View File

@@ -1,5 +1,5 @@
1 1
 {% extends "horizon/common/_modal_form.html" %}
2
-{% load i18n horizon humanize %}
2
+{% load i18n %}
3 3
 {% load url from future %}
4 4
 
5 5
 {% block form_id %}{% endblock %}
@@ -16,38 +16,8 @@
16 16
   </div>
17 17
 
18 18
   <div class="right quota-dynamic">
19
-    <h3>{% trans "Description" %}:</h3>
20
-
21
-    <p>{% trans "Volumes are block devices that can be attached to instances." %}</p>
22
-
23
-    <h3>{% trans "Volume Quotas" %}</h3>
24
-
25
-    <div class="quota_title clearfix">
26
-      <strong>{% trans "Total Gigabytes" %} <span>({{ usages.gigabytes.used|intcomma }} {% trans "GB" %})</span></strong>
27
-      <p>{{ usages.gigabytes.available|quota:_("GB")|intcomma }}</p>
28
-    </div>
29
-
30
-    <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">
31
-    </div>
32
-
33
-    <div class="quota_title clearfix">
34
-      <strong>{% trans "Number of Volumes" %} <span>({{ usages.volumes.used|intcomma }})</span></strong>
35
-      <p>{{ usages.volumes.available|quota|intcomma }}</p>
36
-    </div>
37
-
38
-    <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">
39
-    </div>
19
+    {% include "project/volumes/_quota.html" with usages=usages %}
40 20
   </div>
41
-
42
-  <script type="text/javascript" charset="utf-8">
43
-    if(typeof horizon.Quota !== 'undefined') {
44
-      horizon.Quota.init();
45
-    } else {
46
-      addHorizonLoadEvent(function() {
47
-        horizon.Quota.init();
48
-      });
49
-    }
50
-  </script>
51 21
 {% endblock %}
52 22
 
53 23
 {% block modal-footer %}

+ 8
- 9
openstack_dashboard/dashboards/project/volumes/templates/volumes/_create_snapshot.html View File

@@ -9,15 +9,14 @@
9 9
 {% block modal-header %}{% trans "Create Volume Snapshot" %}{% endblock %}
10 10
 
11 11
 {% block modal-body %}
12
-<div class="left">
13
-  <fieldset>
14
-  {% include "horizon/common/_form_fields.html" %}
15
-  </fieldset>
16
-</div>
17
-<div class="right">
18
-  <h3>{% trans "Description" %}:</h3>
19
-  <p>{% trans "Volumes are block devices that can be attached to instances." %}</p>
20
-</div>
12
+  <div class="left">
13
+    <fieldset>
14
+    {% include "horizon/common/_form_fields.html" %}
15
+    </fieldset>
16
+  </div>
17
+  <div class="right quota-dynamic">
18
+    {% include "project/volumes/_quota.html" with usages=usages snapshot_quota=True %}
19
+  </div>
21 20
 {% endblock %}
22 21
 
23 22
 {% block modal-footer %}

+ 43
- 0
openstack_dashboard/dashboards/project/volumes/templates/volumes/_quota.html View File

@@ -0,0 +1,43 @@
1
+{% load i18n horizon humanize %}
2
+
3
+<h3>{% trans "Description" %}:</h3>
4
+
5
+<p>{% trans "Volumes are block devices that can be attached to instances." %}</p>
6
+
7
+<h3>{% trans "Volume Quotas" %}</h3>
8
+
9
+<div class="quota_title clearfix">
10
+  <strong>{% trans "Total Gigabytes" %} <span>({{ usages.gigabytes.used|intcomma }} {% trans "GB" %})</span></strong>
11
+  <p>{{ usages.gigabytes.available|quota:_("GB")|intcomma }}</p>
12
+</div>
13
+
14
+<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">
15
+</div>
16
+
17
+{% if snapshot_quota %}
18
+  <div class="quota_title clearfix">
19
+    <strong>{% trans "Number of Snapshots" %} <span>({{ usages.snapshots.used|intcomma }})</span></strong>
20
+    <p>{{ usages.snapshots.available|quota|intcomma }}</p>
21
+  </div>
22
+
23
+  <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">
24
+  </div>
25
+{% else %}
26
+  <div class="quota_title clearfix">
27
+    <strong>{% trans "Number of Volumes" %} <span>({{ usages.volumes.used|intcomma }})</span></strong>
28
+    <p>{{ usages.volumes.available|quota|intcomma }}</p>
29
+  </div>
30
+
31
+  <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">
32
+  </div>
33
+{% endif %}
34
+
35
+<script type="text/javascript" charset="utf-8">
36
+  if(typeof horizon.Quota !== 'undefined') {
37
+    horizon.Quota.init();
38
+  } else {
39
+    addHorizonLoadEvent(function() {
40
+      horizon.Quota.init();
41
+    });
42
+  }
43
+</script>

+ 4
- 0
openstack_dashboard/dashboards/project/volumes/views.py View File

@@ -113,6 +113,10 @@ class CreateSnapshotView(forms.ModalFormView):
113 113
     def get_context_data(self, **kwargs):
114 114
         context = super(CreateSnapshotView, self).get_context_data(**kwargs)
115 115
         context['volume_id'] = self.kwargs['volume_id']
116
+        try:
117
+            context['usages'] = quotas.tenant_quota_usages(self.request)
118
+        except:
119
+            exceptions.handle(self.request)
116 120
         return context
117 121
 
118 122
     def get_initial(self):

+ 45
- 0
openstack_dashboard/test/test_data/cinder_data.py View File

@@ -0,0 +1,45 @@
1
+# Copyright 2012 Nebula, Inc.
2
+#
3
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+#    not use this file except in compliance with the License. You may obtain
5
+#    a copy of the License at
6
+#
7
+#         http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+#    Unless required by applicable law or agreed to in writing, software
10
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+#    License for the specific language governing permissions and limitations
13
+#    under the License.
14
+
15
+from cinderclient.v1 import quotas
16
+from openstack_dashboard.api.base import Quota, QuotaSet as QuotaSetWrapper
17
+from openstack_dashboard.usage.quotas import QuotaUsage
18
+from .utils import TestDataContainer
19
+
20
+
21
+def data(TEST):
22
+    TEST.cinder_quotas = TestDataContainer()
23
+    TEST.cinder_quota_usages = TestDataContainer()
24
+
25
+    # Quota Sets
26
+    quota_data = dict(volumes='1',
27
+                      snapshots='1',
28
+                      gigabytes='1000')
29
+    quota = quotas.QuotaSet(quotas.QuotaSetManager(None), quota_data)
30
+    #TEST.quotas.cinder = QuotaSetWrapper(quota)
31
+    TEST.cinder_quotas.add(QuotaSetWrapper(quota))
32
+
33
+    # Quota Usages
34
+    quota_usage_data = {'gigabytes': {'used': 0,
35
+                                      'quota': 1000},
36
+                        'instances': {'used': 0,
37
+                                      'quota': 10},
38
+                        'snapshots': {'used': 0,
39
+                                      'quota': 10}}
40
+    quota_usage = QuotaUsage()
41
+    for k, v in quota_usage_data.items():
42
+        quota_usage.add_quota(Quota(k, v['quota']))
43
+        quota_usage.tally(k, v['used'])
44
+
45
+    TEST.cinder_quota_usages.add(quota_usage)

+ 2
- 0
openstack_dashboard/test/test_data/utils.py View File

@@ -18,6 +18,7 @@ def load_test_data(load_onto=None):
18 18
     from . import glance_data
19 19
     from . import keystone_data
20 20
     from . import nova_data
21
+    from . import cinder_data
21 22
     from . import quantum_data
22 23
     from . import swift_data
23 24
     from . import heat_data
@@ -27,6 +28,7 @@ def load_test_data(load_onto=None):
27 28
                keystone_data.data,
28 29
                glance_data.data,
29 30
                nova_data.data,
31
+               cinder_data.data,
30 32
                quantum_data.data,
31 33
                swift_data.data,
32 34
                heat_data.data)

+ 12
- 4
openstack_dashboard/test/tests/quotas.py View File

@@ -45,6 +45,8 @@ class QuotaTests(test.APITestCase):
45 45
                   'cores': {'available': 8, 'used': 2, 'quota': 10}}
46 46
         if with_volume:
47 47
             quotas.update({'volumes': {'available': 0, 'used': 3, 'quota': 1},
48
+                           'snapshots': {'available': 0, 'used': 3,
49
+                                         'quota': 1},
48 50
                            'gigabytes': {'available': 920, 'used': 80,
49 51
                                          'quota': 1000}})
50 52
         return quotas
@@ -54,7 +56,8 @@ class QuotaTests(test.APITestCase):
54 56
                                    'tenant_quota_get',),
55 57
                         api.network: ('tenant_floating_ip_list',),
56 58
                         quotas: ('is_service_enabled',),
57
-                        cinder: ('volume_list', 'tenant_quota_get',)})
59
+                        cinder: ('volume_list', 'volume_snapshot_list',
60
+                                 'tenant_quota_get',)})
58 61
     def test_tenant_quota_usages(self):
59 62
         quotas.is_service_enabled(IsA(http.HttpRequest),
60 63
                                   'volume').AndReturn(True)
@@ -68,8 +71,10 @@ class QuotaTests(test.APITestCase):
68 71
                 .AndReturn([self.servers.list(), False])
69 72
         cinder.volume_list(IsA(http.HttpRequest)) \
70 73
                 .AndReturn(self.volumes.list())
74
+        cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
75
+                .AndReturn(self.snapshots.list())
71 76
         cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
72
-            .AndReturn(self.quotas.first())
77
+            .AndReturn(self.cinder_quotas.first())
73 78
 
74 79
         self.mox.ReplayAll()
75 80
 
@@ -139,7 +144,8 @@ class QuotaTests(test.APITestCase):
139 144
                                    'tenant_quota_get',),
140 145
                         api.network: ('tenant_floating_ip_list',),
141 146
                         quotas: ('is_service_enabled',),
142
-                        cinder: ('volume_list', 'tenant_quota_get',)})
147
+                        cinder: ('volume_list', 'volume_snapshot_list',
148
+                                 'tenant_quota_get',)})
143 149
     def test_tenant_quota_usages_unlimited_quota(self):
144 150
         inf_quota = self.quotas.first()
145 151
         inf_quota['ram'] = -1
@@ -156,8 +162,10 @@ class QuotaTests(test.APITestCase):
156 162
                 .AndReturn([self.servers.list(), False])
157 163
         cinder.volume_list(IsA(http.HttpRequest)) \
158 164
                 .AndReturn(self.volumes.list())
165
+        cinder.volume_snapshot_list(IsA(http.HttpRequest)) \
166
+                .AndReturn(self.snapshots.list())
159 167
         cinder.tenant_quota_get(IsA(http.HttpRequest), '1') \
160
-            .AndReturn(inf_quota)
168
+            .AndReturn(self.cinder_quotas.first())
161 169
 
162 170
         self.mox.ReplayAll()
163 171
 

+ 3
- 0
openstack_dashboard/usage/quotas.py View File

@@ -19,6 +19,7 @@ NOVA_QUOTA_FIELDS = ("metadata_items",
19 19
                      "security_group_rules",)
20 20
 
21 21
 CINDER_QUOTA_FIELDS = ("volumes",
22
+                       "snapshots",
22 23
                        "gigabytes",)
23 24
 
24 25
 QUOTA_FIELDS = NOVA_QUOTA_FIELDS + CINDER_QUOTA_FIELDS
@@ -136,8 +137,10 @@ def tenant_quota_usages(request):
136 137
 
137 138
     if 'volumes' not in disabled_quotas:
138 139
         volumes = cinder.volume_list(request)
140
+        snapshots = cinder.volume_snapshot_list(request)
139 141
         usages.tally('gigabytes', sum([int(v.size) for v in volumes]))
140 142
         usages.tally('volumes', len(volumes))
143
+        usages.tally('snapshots', len(snapshots))
141 144
 
142 145
     # Sum our usage based on the flavors of the instances.
143 146
     for flavor in [flavors[instance.flavor['id']] for instance in instances]:

Loading…
Cancel
Save