]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Over subscription for Pure Storage iSCSI driver.
authorDaniel Wilson <daniel.wilson@purestorage.com>
Thu, 19 Feb 2015 18:35:05 +0000 (10:35 -0800)
committerDaniel Wilson <daniel.wilson@purestorage.com>
Wed, 25 Feb 2015 00:43:56 +0000 (16:43 -0800)
Add support for over subscription in Pure Storage iSCSI driver.

Implements: blueprint pure-iscsi-over-subscription-thin-provisioning
Change-Id: Ia9de828ad718c0790f4f04ee2a2f1eb4c3fe2788

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

index fb6ba618e8defdcba019995b34abb09577795c22..52c4732eb6e0ba631922348ce0441b108123bac9 100644 (file)
@@ -103,11 +103,16 @@ PORTS_WITHOUT = [NON_ISCSI_PORT]
 VOLUME_CONNECTIONS = [{"host": "h1", "name": VOLUME["name"] + "-cinder"},
                       {"host": "h2", "name": VOLUME["name"] + "-cinder"},
                       ]
-TOTAL_SPACE = 50.0
-FREE_SPACE = 32.1
-SPACE_INFO = {"capacity": TOTAL_SPACE * units.Gi,
-              "total": (TOTAL_SPACE - FREE_SPACE) * units.Gi,
+TOTAL_CAPACITY = 50.0
+USED_SPACE = 32.1
+PROVISIONED_CAPACITY = 70.0
+DEFAULT_OVER_SUBSCRIPTION = 20
+SPACE_INFO = {"capacity": TOTAL_CAPACITY * units.Gi,
+              "total": USED_SPACE * units.Gi
               }
+SPACE_INFO_EMPTY = {"capacity": TOTAL_CAPACITY * units.Gi,
+                    "total": 0
+                    }
 
 
 class FakePureStorageHTTPError(Exception):
@@ -570,17 +575,65 @@ class PureISCSIDriverTestCase(test.TestCase):
         self.assertFalse(self.array.list_host_connections.called)
         self.assertFalse(self.array.delete_host.called)
 
-    def test_get_volume_stats(self):
+    @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
         self.assertEqual(self.driver.get_volume_stats(), {})
         self.array.get.return_value = SPACE_INFO
         result = {"volume_backend_name": VOLUME_BACKEND_NAME,
                   "vendor_name": "Pure Storage",
                   "driver_version": self.driver.VERSION,
                   "storage_protocol": "iSCSI",
-                  "total_capacity_gb": TOTAL_SPACE,
-                  "free_capacity_gb": FREE_SPACE,
+                  "total_capacity_gb": TOTAL_CAPACITY,
+                  "free_capacity_gb": TOTAL_CAPACITY - USED_SPACE,
                   "reserved_percentage": 0,
-                  "consistencygroup_support": True
+                  "consistencygroup_support": True,
+                  "thin_provisioning_support": True,
+                  "provisioned_capacity": PROVISIONED_CAPACITY,
+                  "max_over_subscription_ratio": (PROVISIONED_CAPACITY /
+                                                  USED_SPACE)
+                  }
+        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_provisioned_space", autospec=True)
+    def test_get_volume_stats_empty_array(self, mock_space):
+        mock_space.return_value = PROVISIONED_CAPACITY * units.Gi
+        self.assertEqual(self.driver.get_volume_stats(), {})
+        self.array.get.return_value = SPACE_INFO_EMPTY
+        result = {"volume_backend_name": VOLUME_BACKEND_NAME,
+                  "vendor_name": "Pure Storage",
+                  "driver_version": self.driver.VERSION,
+                  "storage_protocol": "iSCSI",
+                  "total_capacity_gb": TOTAL_CAPACITY,
+                  "free_capacity_gb": TOTAL_CAPACITY,
+                  "reserved_percentage": 0,
+                  "consistencygroup_support": True,
+                  "thin_provisioning_support": True,
+                  "provisioned_capacity": PROVISIONED_CAPACITY,
+                  "max_over_subscription_ratio": DEFAULT_OVER_SUBSCRIPTION
+                  }
+        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_provisioned_space", autospec=True)
+    def test_get_volume_stats_nothing_provisioned(self, mock_space):
+        mock_space.return_value = 0
+        self.assertEqual(self.driver.get_volume_stats(), {})
+        self.array.get.return_value = SPACE_INFO
+        result = {"volume_backend_name": VOLUME_BACKEND_NAME,
+                  "vendor_name": "Pure Storage",
+                  "driver_version": self.driver.VERSION,
+                  "storage_protocol": "iSCSI",
+                  "total_capacity_gb": TOTAL_CAPACITY,
+                  "free_capacity_gb": TOTAL_CAPACITY - USED_SPACE,
+                  "reserved_percentage": 0,
+                  "consistencygroup_support": True,
+                  "thin_provisioning_support": True,
+                  "provisioned_capacity": 0,
+                  "max_over_subscription_ratio": DEFAULT_OVER_SUBSCRIPTION
                   }
         real_result = self.driver.get_volume_stats(refresh=True)
         self.assertDictMatch(result, real_result)
index 190a514f7317547850e4f8f7cd3a97d9f0ad08e1..af87a249293efe460f370f1a9db2b9d001893b31 100644 (file)
@@ -99,7 +99,7 @@ def _generate_purity_host_name(name):
 class PureISCSIDriver(san.SanISCSIDriver):
     """Performs volume management on Pure Storage FlashArray."""
 
-    VERSION = "2.0.3"
+    VERSION = "2.0.4"
 
     SUPPORTED_REST_API_VERSIONS = ['1.2', '1.3', '1.4']
 
@@ -382,19 +382,38 @@ class PureISCSIDriver(san.SanISCSIDriver):
     def _update_stats(self):
         """Set self._stats with relevant information."""
         info = self._array.get(space=True)
-        total = float(info["capacity"]) / units.Gi
-        free = float(info["capacity"] - info["total"]) / units.Gi
+        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
+        # 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
+        # array a much more accurate number will be presented based on current
+        # usage.
+        if used_space == 0 or provisioned_space == 0:
+            thin_provisioning = 20
+        else:
+            thin_provisioning = provisioned_space / used_space
         data = {"volume_backend_name": self._backend_name,
                 "vendor_name": "Pure Storage",
                 "driver_version": self.VERSION,
                 "storage_protocol": "iSCSI",
-                "total_capacity_gb": total,
-                "free_capacity_gb": free,
+                "total_capacity_gb": total_capacity,
+                "free_capacity_gb": free_space,
                 "reserved_percentage": 0,
-                "consistencygroup_support": True
+                "consistencygroup_support": True,
+                "thin_provisioning_support": True,
+                "provisioned_capacity": provisioned_space,
+                "max_over_subscription_ratio": thin_provisioning
                 }
         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)
+
     def extend_volume(self, volume, new_size):
         """Extend volume to new_size."""
         LOG.debug("Enter PureISCSIDriver.extend_volume.")