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__)
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):
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"
RADOS Block Device Driver
"""
+import json
import os
import tempfile
import urllib
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"""
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