]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
rbd: implement get_volume_stats()
authorJosh Durgin <josh.durgin@inktank.com>
Wed, 20 Feb 2013 01:11:41 +0000 (17:11 -0800)
committerJosh Durgin <josh.durgin@inktank.com>
Wed, 20 Feb 2013 07:15:18 +0000 (23:15 -0800)
Report raw capacity because replication level may change, and the
cluster may be used by more than one service. If the command fails for
any reason, return unknown capacity.

Change-Id: If6871124301b51e4768ea64c4d8e5109e0804db8
Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
cinder/tests/test_rbd.py
cinder/volume/drivers/rbd.py

index 8285fc34445ab40e08204f99db5b1e31cc6fdcc4..f0f1266513e997aaf4dbecd694a50875e7bc9c8a 100644 (file)
@@ -30,6 +30,7 @@ from cinder.tests.image import fake as fake_image
 from cinder.tests.test_volume import DriverTestCase
 from cinder.volume import configuration as conf
 from cinder.volume.drivers.rbd import RBDDriver
+from cinder.volume.drivers.rbd import VERSION as DRIVER_VERSION
 
 LOG = logging.getLogger(__name__)
 
@@ -38,6 +39,38 @@ class FakeImageService:
     def download(self, context, image_id, path):
         pass
 
+RADOS_DF_OUT = """
+{
+   "total_space" : "958931232",
+   "total_used" : "123906196",
+   "total_objects" : "4221",
+   "total_avail" : "787024012",
+   "pools" : [
+      {
+         "name" : "volumes",
+         "categories" : [
+            {
+               "write_bytes" : "226833",
+               "size_kb" : "17038386",
+               "read_bytes" : "221865",
+               "num_objects" : "4186",
+               "name" : "",
+               "size_bytes" : "17447306589",
+               "write_kb" : "20302730",
+               "num_object_copies" : "8372",
+               "read_kb" : "30",
+               "num_objects_unfound" : "0",
+               "num_object_clones" : "9",
+               "num_objects_missing_on_primary" : "0",
+               "num_objects_degraded" : "0"
+            }
+         ],
+         "id" : "4"
+      }
+   ]
+}
+"""
+
 
 class RBDTestCase(test.TestCase):
 
@@ -117,6 +150,36 @@ class RBDTestCase(test.TestCase):
         self.flags(volume_tmp_dir='/var/run/cinder/tmp')
         self._copy_image()
 
+    def test_update_volume_stats(self):
+        def fake_stats(*args):
+            return RADOS_DF_OUT, ''
+        self.stubs.Set(self.driver, '_execute', fake_stats)
+        expected = dict(
+            volume_backend_name='RBD',
+            vendor_name='Open Source',
+            driver_version=DRIVER_VERSION,
+            storage_protocol='ceph',
+            total_capacity_gb=914,
+            free_capacity_gb=750,
+            reserved_percentage=0)
+        actual = self.driver.get_volume_stats(True)
+        self.assertDictMatch(expected, actual)
+
+    def test_update_volume_stats_error(self):
+        def fake_exc(*args):
+            raise exception.ProcessExecutionError()
+        self.stubs.Set(self.driver, '_execute', fake_exc)
+        expected = dict(
+            volume_backend_name='RBD',
+            vendor_name='Open Source',
+            driver_version=DRIVER_VERSION,
+            storage_protocol='ceph',
+            total_capacity_gb='unknown',
+            free_capacity_gb='unknown',
+            reserved_percentage=0)
+        actual = self.driver.get_volume_stats(True)
+        self.assertDictMatch(expected, actual)
+
 
 class ManagedRBDTestCase(DriverTestCase):
     driver_name = "cinder.volume.drivers.rbd.RBDDriver"
index b467f53e9a5204820b188c7605c6fcb279568c43..0fb3559153147988ec84c5ecd56fd30409c7ea9a 100644 (file)
@@ -15,6 +15,7 @@
 RADOS Block Device Driver
 """
 
+import json
 import os
 import tempfile
 import urllib
@@ -49,12 +50,22 @@ rbd_opts = [
 FLAGS = flags.FLAGS
 FLAGS.register_opts(rbd_opts)
 
+VERSION = '1.0'
+
 
 class RBDDriver(driver.VolumeDriver):
     """Implements RADOS block device (RBD) volume commands"""
     def __init__(self, *args, **kwargs):
         super(RBDDriver, self).__init__(*args, **kwargs)
         self.configuration.append_config_values(rbd_opts)
+        self._stats = dict(
+            volume_backend_name='RBD',
+            vendor_name='Open Source',
+            driver_version=VERSION,
+            storage_protocol='ceph',
+            total_capacity_gb='unknown',
+            free_capacity_gb='unknown',
+            reserved_percentage=0)
 
     def check_for_setup_error(self):
         """Returns an error if prerequisites aren't met"""
@@ -65,6 +76,29 @@ class RBDDriver(driver.VolumeDriver):
                                  self.configuration.rbd_pool)
             raise exception.VolumeBackendAPIException(data=exception_message)
 
+    def _update_volume_stats(self):
+        stats = dict(
+            total_capacity_gb='unknown',
+            free_capacity_gb='unknown')
+        try:
+            stdout, _err = self._execute('rados', 'df', '--format', 'json')
+            new_stats = json.loads(stdout)
+            total = int(new_stats['total_space']) / 1024 ** 2
+            free = int(new_stats['total_avail']) / 1024 ** 2
+            stats['total_capacity_gb'] = total
+            stats['free_capacity_gb'] = free
+        except exception.ProcessExecutionError:
+            # just log and return unknown capacities
+            LOG.exception(_('error refreshing volume stats'))
+        self._stats.update(stats)
+
+    def get_volume_stats(self, refresh=False):
+        """Return the current state of the volume service. If 'refresh' is
+           True, run the update first."""
+        if refresh:
+            self._update_volume_stats()
+        return self._stats
+
     def _supports_layering(self):
         stdout, _ = self._execute('rbd', '--help')
         return 'clone' in stdout