rbd: send ceph monitor addresses with connection info

Previously we relied on a ceph configuration file on the compute host
for this information. Sending the info directly from cinder makes more
complex setups with multiple ceph clusters talking to the same compute
hosts possible.

Refresh the monitor addresses for each initialize_connection() call,
since monitors may be added or removed while cinder-volume is
running.

Fixes: bug 1077817
Signed-off-by: Josh Durgin <josh.durgin@inktank.com>

Change-Id: I34a1fa16ce1f4524ba25832faf3129303e755100
This commit is contained in:
Josh Durgin
2013-05-24 18:18:30 -07:00
parent b782597725
commit 483b84e42b
2 changed files with 76 additions and 0 deletions

View File

@@ -35,6 +35,34 @@ from cinder.volume.drivers.rbd import VERSION as DRIVER_VERSION
LOG = logging.getLogger(__name__)
CEPH_MON_DUMP = """dumped monmap epoch 1
{ "epoch": 1,
"fsid": "33630410-6d93-4d66-8e42-3b953cf194aa",
"modified": "2013-05-22 17:44:56.343618",
"created": "2013-05-22 17:44:56.343618",
"mons": [
{ "rank": 0,
"name": "a",
"addr": "[::1]:6789\/0"},
{ "rank": 1,
"name": "b",
"addr": "[::1]:6790\/0"},
{ "rank": 2,
"name": "c",
"addr": "[::1]:6791\/0"},
{ "rank": 3,
"name": "d",
"addr": "127.0.0.1:6792\/0"},
{ "rank": 4,
"name": "e",
"addr": "example.com:6791\/0"}],
"quorum": [
0,
1,
2]}
"""
class FakeImageService:
def download(self, context, image_id, path):
pass
@@ -190,6 +218,34 @@ class RBDTestCase(test.TestCase):
actual = self.driver.get_volume_stats(True)
self.assertDictMatch(expected, actual)
def test_get_mon_addrs(self):
self.stubs.Set(self.driver, '_execute',
lambda *a: (CEPH_MON_DUMP, ''))
hosts = ['::1', '::1', '::1', '127.0.0.1', 'example.com']
ports = ['6789', '6790', '6791', '6792', '6791']
self.assertEqual((hosts, ports), self.driver._get_mon_addrs())
def test_initialize_connection(self):
name = 'volume-00000001'
hosts = ['::1', '::1', '::1', '127.0.0.1', 'example.com']
ports = ['6789', '6790', '6791', '6792', '6791']
self.stubs.Set(self.driver, '_get_mon_addrs', lambda: (hosts, ports))
expected = {
'driver_volume_type': 'rbd',
'data': {
'name': '%s/%s' % (self.configuration.rbd_pool,
name),
'hosts': hosts,
'ports': ports,
'auth_enabled': False,
'auth_username': None,
'secret_type': 'ceph',
'secret_uuid': None,
}
}
actual = self.driver.initialize_connection(dict(name=name), None)
self.assertDictMatch(expected, actual)
class ManagedRBDTestCase(DriverTestCase):
driver_name = "cinder.volume.drivers.rbd.RBDDriver"

View File

@@ -65,6 +65,23 @@ class RBDDriver(driver.VolumeDriver):
self.configuration.rbd_pool)
raise exception.VolumeBackendAPIException(data=exception_message)
def _get_mon_addrs(self):
args = ['ceph', 'mon', 'dump', '--format=json']
out, _ = self._execute(*args)
lines = out.split('\n')
if lines[0].startswith('dumped monmap epoch'):
lines = lines[1:]
monmap = json.loads('\n'.join(lines))
addrs = [mon['addr'] for mon in monmap['mons']]
hosts = []
ports = []
for addr in addrs:
host_port = addr[:addr.rindex('/')]
host, port = host_port.rsplit(':', 1)
hosts.append(host.strip('[]'))
ports.append(port)
return hosts, ports
def _update_volume_stats(self):
stats = {'vendor_name': 'Open Source',
'driver_version': VERSION,
@@ -199,11 +216,14 @@ class RBDDriver(driver.VolumeDriver):
pass
def initialize_connection(self, volume, connector):
hosts, ports = self._get_mon_addrs()
return {
'driver_volume_type': 'rbd',
'data': {
'name': '%s/%s' % (self.configuration.rbd_pool,
volume['name']),
'hosts': hosts,
'ports': ports,
'auth_enabled': (self.configuration.rbd_secret_uuid
is not None),
'auth_username': self.configuration.rbd_user,