]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Retyping volume got error under max vol limit
authorwanghao <wanghao749@huawei.com>
Sat, 10 Oct 2015 03:18:02 +0000 (11:18 +0800)
committerwanghao <wanghao749@huawei.com>
Tue, 22 Dec 2015 03:18:33 +0000 (11:18 +0800)
After reaching the max volume limit and
retyping one of volumes, cinder will raise
error: "VolumeLimitExceeded".

This error shouldn't be occurring since the
volume type limit is -1 and retyping doesn't
change the volume quota, it changes the volume
type quota.

Change-Id: I5f25c47158ac24bef94078457a84248daa67e80f
Closes-Bug: #1504719

cinder/quota_utils.py
cinder/tests/unit/test_quota.py
cinder/tests/unit/test_volume.py
cinder/volume/api.py
cinder/volume/manager.py

index 1d5db033cda3ec9bc9a8de3020168f2e3b12d9b7..ca5c08175164f9ece019b68d09ab03e6b6e3f36c 100644 (file)
@@ -24,13 +24,19 @@ LOG = logging.getLogger(__name__)
 QUOTAS = quota.QUOTAS
 
 
-def get_volume_type_reservation(ctxt, volume, type_id):
+def get_volume_type_reservation(ctxt, volume, type_id,
+                                reserve_vol_type_only=False):
     # Reserve quotas for the given volume type
     try:
         reserve_opts = {'volumes': 1, 'gigabytes': volume['size']}
         QUOTAS.add_volume_type_opts(ctxt,
                                     reserve_opts,
                                     type_id)
+        # If reserve_vol_type_only is True, just reserve volume_type quota,
+        # not volume quota.
+        if reserve_vol_type_only:
+            reserve_opts.pop('volumes')
+            reserve_opts.pop('gigabytes')
         # Note that usually the project_id on the volume will be the same as
         # the project_id in the context. But, if they are different then the
         # reservations must be recorded against the project_id that owns the
index ca6532df89d55da27190be296ac76b08184fa910..7e5a9871f6ce9dbfd5c6ff27975a68270614f851 100644 (file)
@@ -1861,3 +1861,23 @@ class QuotaVolumeTypeReservationTestCase(test.TestCase):
                                              project_id='vol_project_id',
                                              gigabytes='1',
                                              volumes=1)
+
+    @mock.patch.object(quota.QUOTAS, 'reserve')
+    def test_volume_type_reservation_with_type_only(self, mock_reserve):
+        my_context = FakeContext('MyProject', None)
+        volume = {'name': 'my_vol_name',
+                  'id': 'my_vol_id',
+                  'size': '1',
+                  'project_id': 'vol_project_id',
+                  }
+        quota_utils.get_volume_type_reservation(my_context,
+                                                volume,
+                                                self.volume_type['id'],
+                                                reserve_vol_type_only=True)
+        vtype_volume_quota = "%s_%s" % ('volumes', self.volume_type['name'])
+        vtype_size_quota = "%s_%s" % ('gigabytes', self.volume_type['name'])
+        reserve_opts = {vtype_volume_quota: 1,
+                        vtype_size_quota: volume['size']}
+        mock_reserve.assert_called_once_with(my_context,
+                                             project_id='vol_project_id',
+                                             **reserve_opts)
index 344715517ed8659d470c495de03f29bdc1061072..535fdf9a42086a3fffcc55fb750ab44dc1818757 100644 (file)
@@ -4808,7 +4808,7 @@ class VolumeMigrationTestCase(VolumeTestCase):
 
     def _retype_volume_exec(self, driver, snap=False, policy='on-demand',
                             migrate_exc=False, exc=None, diff_equal=False,
-                            replica=False):
+                            replica=False, reserve_vol_type_only=False):
         elevated = context.get_admin_context()
         project_id = self.context.project_id
 
@@ -4839,6 +4839,17 @@ class VolumeMigrationTestCase(VolumeTestCase):
         QUOTAS.add_volume_type_opts(self.context,
                                     reserve_opts,
                                     vol_type['id'])
+        if reserve_vol_type_only:
+            reserve_opts.pop('volumes')
+            reserve_opts.pop('gigabytes')
+            try:
+                usage = db.quota_usage_get(elevated, project_id, 'volumes')
+                total_volumes_in_use = usage.in_use
+                usage = db.quota_usage_get(elevated, project_id, 'gigabytes')
+                total_gigabytes_in_use = usage.in_use
+            except exception.QuotaUsageNotFound:
+                total_volumes_in_use = 0
+                total_gigabytes_in_use = 0
         reservations = QUOTAS.reserve(self.context,
                                       project_id=project_id,
                                       **reserve_opts)
@@ -4887,6 +4898,20 @@ class VolumeMigrationTestCase(VolumeTestCase):
         except exception.QuotaUsageNotFound:
             volumes_in_use = 0
 
+        # Get new in_use after retype, it should not be changed.
+        if reserve_vol_type_only:
+            try:
+                usage = db.quota_usage_get(elevated, project_id, 'volumes')
+                new_total_volumes_in_use = usage.in_use
+                usage = db.quota_usage_get(elevated, project_id, 'gigabytes')
+                new_total_gigabytes_in_use = usage.in_use
+            except exception.QuotaUsageNotFound:
+                new_total_volumes_in_use = 0
+                new_total_gigabytes_in_use = 0
+            self.assertEqual(total_volumes_in_use, new_total_volumes_in_use)
+            self.assertEqual(total_gigabytes_in_use,
+                             new_total_gigabytes_in_use)
+
         # check properties
         if driver or diff_equal:
             self.assertEqual(vol_type['id'], volume.volume_type_id)
@@ -4929,6 +4954,9 @@ class VolumeMigrationTestCase(VolumeTestCase):
     def test_retype_volume_migration_equal_types(self):
         self._retype_volume_exec(False, diff_equal=True)
 
+    def test_retype_volume_with_type_only(self):
+        self._retype_volume_exec(True, reserve_vol_type_only=True)
+
     def test_migrate_driver_not_initialized(self):
         volume = tests_utils.create_volume(self.context, size=0,
                                            host=CONF.host)
index 830febb3f9b66fed6a49b4663ee9ae28ea2fe7a1..015c796198f285787dbb10d1c51941f31cd4a373 100644 (file)
@@ -1503,8 +1503,8 @@ class API(base.Base):
         # We're checking here in so that we can report any quota issues as
         # early as possible, but won't commit until we change the type. We
         # pass the reservations onward in case we need to roll back.
-        reservations = quota_utils.get_volume_type_reservation(context, volume,
-                                                               vol_type_id)
+        reservations = quota_utils.get_volume_type_reservation(
+            context, volume, vol_type_id, reserve_vol_type_only=True)
 
         # Get old reservations
         try:
@@ -1512,6 +1512,11 @@ class API(base.Base):
             QUOTAS.add_volume_type_opts(context,
                                         reserve_opts,
                                         old_vol_type_id)
+            # NOTE(wanghao): We don't need to reserve volumes and gigabytes
+            # quota for retyping operation since they didn't changed, just
+            # reserve volume_type and type gigabytes is fine.
+            reserve_opts.pop('volumes')
+            reserve_opts.pop('gigabytes')
             old_reservations = QUOTAS.reserve(context,
                                               project_id=volume.project_id,
                                               **reserve_opts)
index b9fd6b9804d32e7d25d5c2caf94cc8bd6b568af3..8533e2db67a9b2ce1e1cf590ae0f4456f02b4a40 100644 (file)
@@ -2120,6 +2120,11 @@ class VolumeManager(manager.SchedulerDependentManager):
                 QUOTAS.add_volume_type_opts(context,
                                             reserve_opts,
                                             volume.volume_type_id)
+                # NOTE(wanghao): We don't need to reserve volumes and gigabytes
+                # quota for retyping operation since they didn't changed, just
+                # reserve volume_type and type gigabytes is fine.
+                reserve_opts.pop('volumes')
+                reserve_opts.pop('gigabytes')
                 old_reservations = QUOTAS.reserve(context,
                                                   project_id=project_id,
                                                   **reserve_opts)