]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Enable use of filter_function in PureISCIDriver
authorDaniel Wilson <daniel.wilson@purestorage.com>
Wed, 22 Apr 2015 21:07:02 +0000 (14:07 -0700)
committerPatrick East <patrick.east@purestorage.com>
Thu, 23 Apr 2015 16:44:44 +0000 (09:44 -0700)
Adding filter_function to capabilities of PureISCIDriver.  Return
total_volumes as well for use with filter_function.

This will open a way for admins to prevent Cinder from scheduling
volumes on a Pure backend once it has reached the array’s limits. The
primary use case for this is volume limits. An example of a
filter_function that will prevent this issue would be:

filter_function="capabilities.total_volumes < 500”

This will prevent any issues with Purity versions that are limited to
500 volumes.

Without that filter_function the scheduler will continue to try and
schedule volume creation if space is available instead of selecting
another backend. The end result would be volume creation fails and the
volume is in an error state even though there are backends available
that could have handled it.

DocImpact: This means the PureISCSIDriver will now respect the
filter_function config option. Special Pure specific fields to filter
off of need to be added to the driver config manual.

Change-Id: I1368b74f79eb8c8560cf86df27f9af9541963a87
Closes-Bug: 1437082

cinder/tests/unit/test_pure.py
cinder/volume/drivers/pure.py

index 7b3e22e43f2c88abb06e97634c5668ffc6b45e4c..1208e887017dfc783f3968915b69613112185dc5 100644 (file)
@@ -656,9 +656,12 @@ class PureISCSIDriverTestCase(test.TestCase):
         self.assertFalse(self.array.list_host_connections.called)
         self.assertFalse(self.array.delete_host.called)
 
+    @mock.patch(DRIVER_OBJ + ".get_filter_function", autospec=True)
     @mock.patch(DRIVER_OBJ + "._get_provisioned_space", autospec=True)
-    def test_get_volume_stats(self, mock_space):
-        mock_space.return_value = PROVISIONED_CAPACITY * units.Gi
+    def test_get_volume_stats(self, mock_space, mock_filter):
+        filter_function = "capabilities.total_volumes < 10"
+        mock_space.return_value = (PROVISIONED_CAPACITY * units.Gi, 100)
+        mock_filter.return_value = filter_function
         self.assertEqual(self.driver.get_volume_stats(), {})
         self.array.get.return_value = SPACE_INFO
         result = {"volume_backend_name": VOLUME_BACKEND_NAME,
@@ -672,15 +675,20 @@ class PureISCSIDriverTestCase(test.TestCase):
                   "thin_provisioning_support": True,
                   "provisioned_capacity": PROVISIONED_CAPACITY,
                   "max_over_subscription_ratio": (PROVISIONED_CAPACITY /
-                                                  USED_SPACE)
+                                                  USED_SPACE),
+                  "total_volumes": 100,
+                  "filter_function": filter_function
                   }
         real_result = self.driver.get_volume_stats(refresh=True)
         self.assertDictMatch(result, real_result)
         self.assertDictMatch(result, self.driver._stats)
 
+    @mock.patch(DRIVER_OBJ + ".get_filter_function", autospec=True)
     @mock.patch(DRIVER_OBJ + "._get_provisioned_space", autospec=True)
-    def test_get_volume_stats_empty_array(self, mock_space):
-        mock_space.return_value = PROVISIONED_CAPACITY * units.Gi
+    def test_get_volume_stats_empty_array(self, mock_space, mock_filter):
+        filter_function = "capabilities.total_volumes < 10"
+        mock_space.return_value = (PROVISIONED_CAPACITY * units.Gi, 100)
+        mock_filter.return_value = filter_function
         self.assertEqual(self.driver.get_volume_stats(), {})
         self.array.get.return_value = SPACE_INFO_EMPTY
         result = {"volume_backend_name": VOLUME_BACKEND_NAME,
@@ -693,15 +701,21 @@ class PureISCSIDriverTestCase(test.TestCase):
                   "consistencygroup_support": True,
                   "thin_provisioning_support": True,
                   "provisioned_capacity": PROVISIONED_CAPACITY,
-                  "max_over_subscription_ratio": DEFAULT_OVER_SUBSCRIPTION
+                  "max_over_subscription_ratio": DEFAULT_OVER_SUBSCRIPTION,
+                  "total_volumes": 100,
+                  "filter_function": filter_function
                   }
         real_result = self.driver.get_volume_stats(refresh=True)
         self.assertDictMatch(result, real_result)
         self.assertDictMatch(result, self.driver._stats)
 
+    @mock.patch(DRIVER_OBJ + ".get_filter_function", autospec=True)
     @mock.patch(DRIVER_OBJ + "._get_provisioned_space", autospec=True)
-    def test_get_volume_stats_nothing_provisioned(self, mock_space):
-        mock_space.return_value = 0
+    def test_get_volume_stats_nothing_provisioned(self, mock_space,
+                                                  mock_filter):
+        filter_function = "capabilities.total_volumes < 10"
+        mock_space.return_value = (0, 0)
+        mock_filter.return_value = filter_function
         self.assertEqual(self.driver.get_volume_stats(), {})
         self.array.get.return_value = SPACE_INFO
         result = {"volume_backend_name": VOLUME_BACKEND_NAME,
@@ -714,7 +728,9 @@ class PureISCSIDriverTestCase(test.TestCase):
                   "consistencygroup_support": True,
                   "thin_provisioning_support": True,
                   "provisioned_capacity": 0,
-                  "max_over_subscription_ratio": DEFAULT_OVER_SUBSCRIPTION
+                  "max_over_subscription_ratio": DEFAULT_OVER_SUBSCRIPTION,
+                  "total_volumes": 0,
+                  "filter_function": filter_function
                   }
         real_result = self.driver.get_volume_stats(refresh=True)
         self.assertDictMatch(result, real_result)
index c35b7df1e1f5fb9dd42e01762265d44406ae2eb5..5cc6c453ac77b375418948186780709a4185f2b3 100644 (file)
@@ -465,7 +465,8 @@ class PureISCSIDriver(san.SanISCSIDriver):
         total_capacity = float(info["capacity"]) / units.Gi
         used_space = float(info["total"]) / units.Gi
         free_space = float(total_capacity - used_space)
-        provisioned_space = float(self._get_provisioned_space()) / units.Gi
+        prov_space, total_vols = self._get_provisioned_space()
+        provisioned_space = float(prov_space) / units.Gi
         # If array is empty we can not calculate a max oversubscription ratio.
         # In this case we choose 20 as a default value for the ratio.  Once
         # some volumes are actually created and some data is stored on the
@@ -485,14 +486,16 @@ class PureISCSIDriver(san.SanISCSIDriver):
                 "consistencygroup_support": True,
                 "thin_provisioning_support": True,
                 "provisioned_capacity": provisioned_space,
-                "max_over_subscription_ratio": thin_provisioning
+                "max_over_subscription_ratio": thin_provisioning,
+                "total_volumes": total_vols,
+                "filter_function": self.get_filter_function()
                 }
         self._stats = data
 
     def _get_provisioned_space(self):
         """Sum up provisioned size of all volumes on array"""
         volumes = self._array.list_volumes(pending=True)
-        return sum(item["size"] for item in volumes)
+        return sum(item["size"] for item in volumes), len(volumes)
 
     def extend_volume(self, volume, new_size):
         """Extend volume to new_size."""