From cbf3bfcf6339fe4d1fe4e279363db4f899e170d3 Mon Sep 17 00:00:00 2001 From: Josh Durgin Date: Tue, 19 Feb 2013 17:11:41 -0800 Subject: [PATCH] rbd: implement get_volume_stats() 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 --- cinder/tests/test_rbd.py | 63 ++++++++++++++++++++++++++++++++++++ cinder/volume/drivers/rbd.py | 34 +++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/cinder/tests/test_rbd.py b/cinder/tests/test_rbd.py index 8285fc344..f0f126651 100644 --- a/cinder/tests/test_rbd.py +++ b/cinder/tests/test_rbd.py @@ -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" diff --git a/cinder/volume/drivers/rbd.py b/cinder/volume/drivers/rbd.py index b467f53e9..0fb355915 100644 --- a/cinder/volume/drivers/rbd.py +++ b/cinder/volume/drivers/rbd.py @@ -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 -- 2.45.2