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):
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)
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']
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.")