Revert "Move libvirt RBD utilities to a new file"
This reverts commit cb8968400f
.
The series of patches involved with adding this feature introduced
an unexpected dependency on glance's v2 API, which we do not
currently support. Triggering a user-facing bug quickly, and leaving
some uncertainty about what else is likely to come in the future,
a revert of this code was decided given the short time to -rc1.
Change-Id: I5e5d897f7294a7d4bda76ed392f503d0552f45e9
Related-bug: 1291014
This commit is contained in:
@@ -27,7 +27,6 @@ from nova import test
|
||||
from nova.tests import fake_processutils
|
||||
from nova.tests.virt.libvirt import fake_libvirt_utils
|
||||
from nova.virt.libvirt import imagebackend
|
||||
from nova.virt.libvirt import rbd_utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@@ -522,8 +521,14 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
group='libvirt')
|
||||
self.libvirt_utils = imagebackend.libvirt_utils
|
||||
self.utils = imagebackend.utils
|
||||
self.mox.StubOutWithMock(rbd_utils, 'rbd')
|
||||
self.mox.StubOutWithMock(rbd_utils, 'rados')
|
||||
self.rbd = self.mox.CreateMockAnything()
|
||||
self.rados = self.mox.CreateMockAnything()
|
||||
|
||||
def prepare_mocks(self):
|
||||
fn = self.mox.CreateMockAnything()
|
||||
self.mox.StubOutWithMock(imagebackend, 'rbd')
|
||||
self.mox.StubOutWithMock(imagebackend, 'rados')
|
||||
return fn
|
||||
|
||||
def test_cache(self):
|
||||
image = self.image_class(self.INSTANCE, self.NAME)
|
||||
@@ -591,10 +596,10 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_create_image(self):
|
||||
fn = self.mox.CreateMockAnything()
|
||||
fn(max_size=None, target=self.TEMPLATE_PATH)
|
||||
fn = self.prepare_mocks()
|
||||
fn(max_size=None, rbd=self.rbd, target=self.TEMPLATE_PATH)
|
||||
|
||||
rbd_utils.rbd.RBD_FEATURE_LAYERING = 1
|
||||
self.rbd.RBD_FEATURE_LAYERING = 1
|
||||
|
||||
self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||
imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
@@ -607,7 +612,7 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
image = self.image_class(self.INSTANCE, self.NAME)
|
||||
image.create_image(fn, self.TEMPLATE_PATH, None)
|
||||
image.create_image(fn, self.TEMPLATE_PATH, None, rbd=self.rbd)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@@ -616,6 +621,8 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
|
||||
fake_processutils.fake_execute_clear_log()
|
||||
fake_processutils.stub_out_processutils_execute(self.stubs)
|
||||
self.mox.StubOutWithMock(imagebackend, 'rbd')
|
||||
self.mox.StubOutWithMock(imagebackend, 'rados')
|
||||
image = self.image_class(self.INSTANCE, self.NAME)
|
||||
|
||||
def fake_fetch(target, *args, **kwargs):
|
||||
@@ -689,8 +696,6 @@ class BackendTestCase(test.NoDBTestCase):
|
||||
pool = "FakePool"
|
||||
self.flags(images_rbd_pool=pool, group='libvirt')
|
||||
self.flags(images_rbd_ceph_conf=conf, group='libvirt')
|
||||
self.mox.StubOutWithMock(rbd_utils, 'rbd')
|
||||
self.mox.StubOutWithMock(rbd_utils, 'rados')
|
||||
self._test_image('rbd', imagebackend.Rbd, imagebackend.Rbd)
|
||||
|
||||
def test_image_default(self):
|
||||
|
@@ -1,158 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import mock
|
||||
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import units
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import rbd_utils
|
||||
|
||||
|
||||
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 RBDTestCase(test.NoDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(RBDTestCase, self).setUp()
|
||||
|
||||
self.mock_rbd = mock.Mock()
|
||||
self.mock_rados = mock.Mock()
|
||||
self.mock_rados.Rados = mock.Mock
|
||||
self.mock_rados.Rados.ioctx = mock.Mock()
|
||||
|
||||
self.mock_rbd.RBD = mock.Mock
|
||||
self.mock_rbd.Image = mock.Mock
|
||||
self.mock_rbd.Image.close = mock.Mock()
|
||||
self.mock_rbd.RBD.Error = Exception
|
||||
self.mock_rados.Error = Exception
|
||||
|
||||
self.rbd_pool = 'rbd'
|
||||
self.driver = rbd_utils.RBDDriver(self.rbd_pool, None, None,
|
||||
rbd_lib=self.mock_rbd,
|
||||
rados_lib=self.mock_rados)
|
||||
|
||||
self.volume_name = u'volume-00000001'
|
||||
|
||||
def tearDown(self):
|
||||
super(RBDTestCase, self).tearDown()
|
||||
|
||||
def test_get_mon_addrs(self):
|
||||
with mock.patch.object(utils, 'execute') as mock_execute:
|
||||
mock_execute.return_value = (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_resize(self):
|
||||
size = 1024
|
||||
|
||||
with mock.patch.object(rbd_utils, 'RBDVolumeProxy') as proxy_init:
|
||||
proxy = proxy_init.return_value
|
||||
proxy.__enter__.return_value = proxy
|
||||
self.driver.resize(self.volume_name, size)
|
||||
proxy.resize.assert_called_once_with(size * units.Ki)
|
||||
|
||||
def test_rbd_volume_proxy_init(self):
|
||||
with mock.patch.object(self.driver, '_connect_to_rados') as \
|
||||
mock_connect_from_rados:
|
||||
with mock.patch.object(self.driver, '_disconnect_from_rados') as \
|
||||
mock_disconnect_from_rados:
|
||||
mock_connect_from_rados.return_value = (None, None)
|
||||
mock_disconnect_from_rados.return_value = (None, None)
|
||||
|
||||
with rbd_utils.RBDVolumeProxy(self.driver, self.volume_name):
|
||||
mock_connect_from_rados.assert_called_once()
|
||||
self.assertFalse(mock_disconnect_from_rados.called)
|
||||
|
||||
mock_disconnect_from_rados.assert_called_once()
|
||||
|
||||
def test_connect_to_rados(self):
|
||||
self.mock_rados.Rados.connect = mock.Mock()
|
||||
self.mock_rados.Rados.shutdown = mock.Mock()
|
||||
self.mock_rados.Rados.open_ioctx = mock.Mock()
|
||||
self.mock_rados.Rados.open_ioctx.return_value = \
|
||||
self.mock_rados.Rados.ioctx
|
||||
|
||||
# default configured pool
|
||||
ret = self.driver._connect_to_rados()
|
||||
self.assertTrue(self.mock_rados.Rados.connect.called)
|
||||
self.assertTrue(self.mock_rados.Rados.open_ioctx.called)
|
||||
self.assertIsInstance(ret[0], self.mock_rados.Rados)
|
||||
self.assertEqual(ret[1], self.mock_rados.Rados.ioctx)
|
||||
self.mock_rados.Rados.open_ioctx.assert_called_with(self.rbd_pool)
|
||||
|
||||
# different pool
|
||||
ret = self.driver._connect_to_rados('alt_pool')
|
||||
self.assertTrue(self.mock_rados.Rados.connect.called)
|
||||
self.assertTrue(self.mock_rados.Rados.open_ioctx.called)
|
||||
self.assertIsInstance(ret[0], self.mock_rados.Rados)
|
||||
self.assertEqual(ret[1], self.mock_rados.Rados.ioctx)
|
||||
self.mock_rados.Rados.open_ioctx.assert_called_with('alt_pool')
|
||||
|
||||
# error
|
||||
self.mock_rados.Rados.open_ioctx.reset_mock()
|
||||
self.mock_rados.Rados.shutdown.reset_mock()
|
||||
self.mock_rados.Rados.open_ioctx.side_effect = self.mock_rados.Error
|
||||
self.assertRaises(self.mock_rados.Error, self.driver._connect_to_rados)
|
||||
self.mock_rados.Rados.open_ioctx.assert_called_once()
|
||||
self.mock_rados.Rados.shutdown.assert_called_once()
|
||||
|
||||
def test_ceph_args(self):
|
||||
self.driver.rbd_user = None
|
||||
self.driver.ceph_conf = None
|
||||
self.assertEqual([], self.driver.ceph_args())
|
||||
|
||||
self.driver.rbd_user = 'foo'
|
||||
self.driver.ceph_conf = None
|
||||
self.assertEqual(['--id', 'foo'], self.driver.ceph_args())
|
||||
|
||||
self.driver.rbd_user = None
|
||||
self.driver.ceph_conf = '/path/bar.conf'
|
||||
self.assertEqual(['--conf', '/path/bar.conf'],
|
||||
self.driver.ceph_args())
|
||||
|
||||
self.driver.rbd_user = 'foo'
|
||||
self.driver.ceph_conf = '/path/bar.conf'
|
||||
self.assertEqual(['--id', 'foo', '--conf', '/path/bar.conf'],
|
||||
self.driver.ceph_args())
|
@@ -25,15 +25,24 @@ from nova import exception
|
||||
from nova.openstack.common import excutils
|
||||
from nova.openstack.common import fileutils
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import units
|
||||
from nova import utils
|
||||
from nova.virt.disk import api as disk
|
||||
from nova.virt import images
|
||||
from nova.virt.libvirt import config as vconfig
|
||||
from nova.virt.libvirt import rbd_utils
|
||||
from nova.virt.libvirt import utils as libvirt_utils
|
||||
|
||||
|
||||
try:
|
||||
import rados
|
||||
import rbd
|
||||
except ImportError:
|
||||
rados = None
|
||||
rbd = None
|
||||
|
||||
|
||||
__imagebackend_opts = [
|
||||
cfg.StrOpt('images_type',
|
||||
default='default',
|
||||
@@ -76,8 +85,6 @@ CONF = cfg.CONF
|
||||
CONF.register_opts(__imagebackend_opts, 'libvirt')
|
||||
CONF.import_opt('image_cache_subdirectory_name', 'nova.virt.imagecache')
|
||||
CONF.import_opt('preallocate_images', 'nova.virt.driver')
|
||||
CONF.import_opt('rbd_user', 'nova.virt.libvirt.volume', group='libvirt')
|
||||
CONF.import_opt('rbd_secret_uuid', 'nova.virt.libvirt.volume', group='libvirt')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@@ -416,6 +423,51 @@ class Lvm(Image):
|
||||
run_as_root=True)
|
||||
|
||||
|
||||
class RBDVolumeProxy(object):
|
||||
"""Context manager for dealing with an existing rbd volume.
|
||||
|
||||
This handles connecting to rados and opening an ioctx automatically, and
|
||||
otherwise acts like a librbd Image object.
|
||||
|
||||
The underlying librados client and ioctx can be accessed as the attributes
|
||||
'client' and 'ioctx'.
|
||||
"""
|
||||
def __init__(self, driver, name, pool=None):
|
||||
client, ioctx = driver._connect_to_rados(pool)
|
||||
try:
|
||||
self.volume = driver.rbd.Image(ioctx, str(name), snapshot=None)
|
||||
except driver.rbd.Error:
|
||||
LOG.exception(_("error opening rbd image %s"), name)
|
||||
driver._disconnect_from_rados(client, ioctx)
|
||||
raise
|
||||
self.driver = driver
|
||||
self.client = client
|
||||
self.ioctx = ioctx
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type_, value, traceback):
|
||||
try:
|
||||
self.volume.close()
|
||||
finally:
|
||||
self.driver._disconnect_from_rados(self.client, self.ioctx)
|
||||
|
||||
def __getattr__(self, attrib):
|
||||
return getattr(self.volume, attrib)
|
||||
|
||||
|
||||
def ascii_str(s):
|
||||
"""Convert a string to ascii, or return None if the input is None.
|
||||
|
||||
This is useful when a parameter is None by default, or a string. LibRBD
|
||||
only accepts ascii, hence the need for conversion.
|
||||
"""
|
||||
if s is None:
|
||||
return s
|
||||
return str(s)
|
||||
|
||||
|
||||
class Rbd(Image):
|
||||
def __init__(self, instance=None, disk_name=None, path=None, **kwargs):
|
||||
super(Rbd, self).__init__("block", "rbd", is_block_dev=True)
|
||||
@@ -432,15 +484,10 @@ class Rbd(Image):
|
||||
' images_rbd_pool'
|
||||
' flag to use rbd images.'))
|
||||
self.pool = CONF.libvirt.images_rbd_pool
|
||||
self.rbd_user = CONF.libvirt.rbd_user
|
||||
self.ceph_conf = CONF.libvirt.images_rbd_ceph_conf
|
||||
|
||||
self.driver = rbd_utils.RBDDriver(
|
||||
pool=self.pool,
|
||||
ceph_conf=self.ceph_conf,
|
||||
rbd_user=self.rbd_user,
|
||||
rbd_lib=kwargs.get('rbd'),
|
||||
rados_lib=kwargs.get('rados'))
|
||||
self.ceph_conf = ascii_str(CONF.libvirt.images_rbd_ceph_conf)
|
||||
self.rbd_user = ascii_str(CONF.libvirt.rbd_user)
|
||||
self.rbd = kwargs.get('rbd', rbd)
|
||||
self.rados = kwargs.get('rados', rados)
|
||||
|
||||
self.path = 'rbd:%s/%s' % (self.pool, self.rbd_name)
|
||||
if self.rbd_user:
|
||||
@@ -448,6 +495,52 @@ class Rbd(Image):
|
||||
if self.ceph_conf:
|
||||
self.path += ':conf=' + self.ceph_conf
|
||||
|
||||
def _connect_to_rados(self, pool=None):
|
||||
client = self.rados.Rados(rados_id=self.rbd_user,
|
||||
conffile=self.ceph_conf)
|
||||
try:
|
||||
client.connect()
|
||||
pool_to_open = str(pool or self.pool)
|
||||
ioctx = client.open_ioctx(pool_to_open)
|
||||
return client, ioctx
|
||||
except self.rados.Error:
|
||||
# shutdown cannot raise an exception
|
||||
client.shutdown()
|
||||
raise
|
||||
|
||||
def _disconnect_from_rados(self, client, ioctx):
|
||||
# closing an ioctx cannot raise an exception
|
||||
ioctx.close()
|
||||
client.shutdown()
|
||||
|
||||
def _supports_layering(self):
|
||||
return hasattr(self.rbd, 'RBD_FEATURE_LAYERING')
|
||||
|
||||
def _ceph_args(self):
|
||||
args = []
|
||||
if self.rbd_user:
|
||||
args.extend(['--id', self.rbd_user])
|
||||
if self.ceph_conf:
|
||||
args.extend(['--conf', self.ceph_conf])
|
||||
return args
|
||||
|
||||
def _get_mon_addrs(self):
|
||||
args = ['ceph', 'mon', 'dump', '--format=json'] + self._ceph_args()
|
||||
out, _ = utils.execute(*args)
|
||||
lines = out.split('\n')
|
||||
if lines[0].startswith('dumped monmap epoch'):
|
||||
lines = lines[1:]
|
||||
monmap = jsonutils.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 backend_location(self):
|
||||
return self.pool, self.rbd_name
|
||||
|
||||
@@ -463,7 +556,7 @@ class Rbd(Image):
|
||||
"""
|
||||
info = vconfig.LibvirtConfigGuestDisk()
|
||||
|
||||
hosts, ports = self.driver.get_mon_addrs()
|
||||
hosts, ports = self._get_mon_addrs()
|
||||
info.device_type = device_type
|
||||
info.driver_format = 'raw'
|
||||
info.driver_cache = cache_mode
|
||||
@@ -496,7 +589,16 @@ class Rbd(Image):
|
||||
|
||||
return False
|
||||
|
||||
def _resize(self, volume_name, size):
|
||||
size = int(size) * units.Ki
|
||||
|
||||
with RBDVolumeProxy(self, volume_name) as vol:
|
||||
vol.resize(size)
|
||||
|
||||
def create_image(self, prepare_template, base, size, *args, **kwargs):
|
||||
if self.rbd is None:
|
||||
raise RuntimeError(_('rbd python libraries not found'))
|
||||
|
||||
if not os.path.exists(base):
|
||||
prepare_template(target=base, max_size=size, *args, **kwargs)
|
||||
else:
|
||||
@@ -505,15 +607,15 @@ class Rbd(Image):
|
||||
# keep using the command line import instead of librbd since it
|
||||
# detects zeroes to preserve sparseness in the image
|
||||
args = ['--pool', self.pool, base, self.rbd_name]
|
||||
if self.driver.supports_layering():
|
||||
if self._supports_layering():
|
||||
args += ['--new-format']
|
||||
args += self.driver.ceph_args()
|
||||
args += self._ceph_args()
|
||||
libvirt_utils.import_rbd_image(*args)
|
||||
|
||||
base_size = disk.get_disk_size(base)
|
||||
|
||||
if size and size > base_size:
|
||||
self.driver.resize(self.rbd_name, size)
|
||||
self._resize(self.rbd_name, size)
|
||||
|
||||
def snapshot_extract(self, target, out_format):
|
||||
images.convert_image(self.path, target, out_format)
|
||||
|
@@ -1,141 +0,0 @@
|
||||
# Copyright 2012 Grid Dynamics
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
try:
|
||||
import rados
|
||||
import rbd
|
||||
except ImportError:
|
||||
rados = None
|
||||
rbd = None
|
||||
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import units
|
||||
from nova import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RBDVolumeProxy(object):
|
||||
"""Context manager for dealing with an existing rbd volume.
|
||||
|
||||
This handles connecting to rados and opening an ioctx automatically, and
|
||||
otherwise acts like a librbd Image object.
|
||||
|
||||
The underlying librados client and ioctx can be accessed as the attributes
|
||||
'client' and 'ioctx'.
|
||||
"""
|
||||
def __init__(self, driver, name, pool=None):
|
||||
client, ioctx = driver._connect_to_rados(pool)
|
||||
try:
|
||||
self.volume = driver.rbd.Image(ioctx, str(name), snapshot=None)
|
||||
except driver.rbd.Error:
|
||||
LOG.exception(_("error opening rbd image %s"), name)
|
||||
driver._disconnect_from_rados(client, ioctx)
|
||||
raise
|
||||
self.driver = driver
|
||||
self.client = client
|
||||
self.ioctx = ioctx
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type_, value, traceback):
|
||||
try:
|
||||
self.volume.close()
|
||||
finally:
|
||||
self.driver._disconnect_from_rados(self.client, self.ioctx)
|
||||
|
||||
def __getattr__(self, attrib):
|
||||
return getattr(self.volume, attrib)
|
||||
|
||||
|
||||
def ascii_str(s):
|
||||
"""Convert a string to ascii, or return None if the input is None.
|
||||
|
||||
This is useful when a parameter is None by default, or a string. LibRBD
|
||||
only accepts ascii, hence the need for conversion.
|
||||
"""
|
||||
if s is None:
|
||||
return s
|
||||
return str(s)
|
||||
|
||||
|
||||
class RBDDriver(object):
|
||||
|
||||
def __init__(self, pool, ceph_conf, rbd_user,
|
||||
rbd_lib=None, rados_lib=None):
|
||||
self.pool = pool.encode('utf8')
|
||||
self.ceph_conf = ceph_conf.encode('utf8') if ceph_conf else None
|
||||
self.rbd_user = rbd_user.encode('utf8') if rbd_user else None
|
||||
self.rbd = rbd_lib or rbd
|
||||
self.rados = rados_lib or rados
|
||||
if self.rbd is None:
|
||||
raise RuntimeError(_('rbd python libraries not found'))
|
||||
|
||||
def _connect_to_rados(self, pool=None):
|
||||
client = self.rados.Rados(rados_id=self.rbd_user,
|
||||
conffile=self.ceph_conf)
|
||||
try:
|
||||
client.connect()
|
||||
pool_to_open = str(pool or self.pool)
|
||||
ioctx = client.open_ioctx(pool_to_open)
|
||||
return client, ioctx
|
||||
except self.rados.Error:
|
||||
# shutdown cannot raise an exception
|
||||
client.shutdown()
|
||||
raise
|
||||
|
||||
def _disconnect_from_rados(self, client, ioctx):
|
||||
# closing an ioctx cannot raise an exception
|
||||
ioctx.close()
|
||||
client.shutdown()
|
||||
|
||||
def supports_layering(self):
|
||||
return hasattr(self.rbd, 'RBD_FEATURE_LAYERING')
|
||||
|
||||
def ceph_args(self):
|
||||
args = []
|
||||
if self.rbd_user:
|
||||
args.extend(['--id', self.rbd_user])
|
||||
if self.ceph_conf:
|
||||
args.extend(['--conf', self.ceph_conf])
|
||||
return args
|
||||
|
||||
def get_mon_addrs(self):
|
||||
args = ['ceph', 'mon', 'dump', '--format=json'] + self.ceph_args()
|
||||
out, _ = utils.execute(*args)
|
||||
lines = out.split('\n')
|
||||
if lines[0].startswith('dumped monmap epoch'):
|
||||
lines = lines[1:]
|
||||
monmap = jsonutils.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 size(self, name):
|
||||
with RBDVolumeProxy(self, name) as vol:
|
||||
return vol.size()
|
||||
|
||||
def resize(self, volume_name, size):
|
||||
size = int(size) * units.Ki
|
||||
|
||||
with RBDVolumeProxy(self, volume_name) as vol:
|
||||
vol.resize(size)
|
Reference in New Issue
Block a user