summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaomai Wang <haomai@unitedstack.com>2013-09-02 15:25:51 +0800
committerYaguang Tang <yaguang.tang@canonical.com>2014-08-18 22:41:06 +0800
commit69c86d4fe15c33a6d96550e11d4453d2c4839ef3 (patch)
treed4d6198239e5bc755a255f800d598c9cc2fee819
parent0e4dd1bfc4d910a7ccf7e100d4c1a30663dc06b7 (diff)
downloadnova-69c86d4fe15c33a6d96550e11d4453d2c4839ef3.tar.gz
Fix incorrect root partition size and compatible volume name
When using rbd backend, it needs to resize volume size if size presents. Else we will get wrong image size. Rbd volume always uses uuid as the main pattern of volume name, now change to it is necessary. This patch also include a follow up fix of the original patch. https://review.openstack.org/#/c/92512/ Fix bug 1219658 (cherry picked from commit 1b66a47b5f4cbcdf1a9a1ec38532474588a3fee2) Conflicts: nova/virt/libvirt/imagebackend.py Change-Id: I3dc8d639b066eac35d190b8d4687c41c9f4d5824
-rw-r--r--nova/tests/virt/libvirt/fake_libvirt_utils.py6
-rw-r--r--nova/tests/virt/libvirt/test_imagebackend.py19
-rw-r--r--nova/tests/virt/libvirt/test_libvirt.py6
-rw-r--r--nova/virt/libvirt/driver.py2
-rw-r--r--nova/virt/libvirt/imagebackend.py84
5 files changed, 109 insertions, 8 deletions
diff --git a/nova/tests/virt/libvirt/fake_libvirt_utils.py b/nova/tests/virt/libvirt/fake_libvirt_utils.py
index 1872922674..21ee7c47b9 100644
--- a/nova/tests/virt/libvirt/fake_libvirt_utils.py
+++ b/nova/tests/virt/libvirt/fake_libvirt_utils.py
@@ -203,8 +203,10 @@ def pick_disk_driver_name(hypervisor_version, is_block_dev=False):
def list_rbd_volumes(pool):
- fake_volumes = ['fakeinstancename.local', 'fakeinstancename.swap',
- 'fakeinstancename', 'wronginstancename']
+ fake_volumes = ['875a8070-d0b9-4949-8b31-104d125c9a64.local',
+ '875a8070-d0b9-4949-8b31-104d125c9a64.swap',
+ '875a8070-d0b9-4949-8b31-104d125c9a64',
+ 'wrong875a8070-d0b9-4949-8b31-104d125c9a64']
return fake_volumes
diff --git a/nova/tests/virt/libvirt/test_imagebackend.py b/nova/tests/virt/libvirt/test_imagebackend.py
index 5424f7b300..3935fe404e 100644
--- a/nova/tests/virt/libvirt/test_imagebackend.py
+++ b/nova/tests/virt/libvirt/test_imagebackend.py
@@ -20,6 +20,7 @@ import shutil
import tempfile
import fixtures
+import mock
from oslo.config import cfg
from inspect import getargspec
@@ -736,10 +737,12 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
self.libvirt_utils = imagebackend.libvirt_utils
self.utils = imagebackend.utils
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):
@@ -830,6 +833,9 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
self.rbd.RBD_FEATURE_LAYERING = 1
+ self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
+ imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
+ ).AndReturn(self.SIZE)
rbd_name = "%s/%s" % (self.INSTANCE['name'], self.NAME)
cmd = ('--pool', self.POOL, self.TEMPLATE_PATH,
rbd_name, '--new-format', '--id', self.USER,
@@ -848,11 +854,15 @@ 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):
return
+ def fake_resize(rbd_name, size):
+ return
+
self.stubs.Set(os.path, 'exists', lambda _: True)
self.stubs.Set(image, 'check_image_exists', lambda: True)
@@ -864,6 +874,15 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
self.assertEqual(getargspec(imagebackend.Image.libvirt_info),
getargspec(self.image_class.libvirt_info))
+ def test_resize(self):
+ image = self.image_class(self.INSTANCE, self.NAME)
+ with mock.patch.object(imagebackend, "RBDVolumeProxy") as mock_proxy:
+ volume_mock = mock.Mock()
+ mock_proxy.side_effect = [mock_proxy]
+ mock_proxy.__enter__.side_effect = [volume_mock]
+ image._resize(image.rbd_name, self.SIZE)
+ volume_mock.resize.assert_called_once_with(self.SIZE)
+
class BackendTestCase(test.NoDBTestCase):
INSTANCE = {'name': 'fake-instance',
diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py
index 2ff1d2aafe..e4ba9ac3b1 100644
--- a/nova/tests/virt/libvirt/test_libvirt.py
+++ b/nova/tests/virt/libvirt/test_libvirt.py
@@ -4381,8 +4381,10 @@ class LibvirtConnTestCase(test.TestCase):
def fake_get_info(instance_name):
return {'state': power_state.SHUTDOWN, 'id': -1}
- fake_volumes = ['fakeinstancename.local', 'fakeinstancename.swap',
- 'fakeinstancename', 'wronginstancename']
+ fake_volumes = ['875a8070-d0b9-4949-8b31-104d125c9a64.local',
+ '875a8070-d0b9-4949-8b31-104d125c9a64.swap',
+ '875a8070-d0b9-4949-8b31-104d125c9a64',
+ 'wrong875a8070-d0b9-4949-8b31-104d125c9a64']
fake_pool = 'fake_pool'
fake_instance = {'name': 'fakeinstancename', 'id': 'instanceid',
'uuid': '875a8070-d0b9-4949-8b31-104d125c9a64'}
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index 4b78564170..40462b3de1 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -952,7 +952,7 @@ class LibvirtDriver(driver.ComputeDriver):
def _cleanup_rbd(self, instance):
pool = CONF.libvirt_images_rbd_pool
volumes = libvirt_utils.list_rbd_volumes(pool)
- pattern = instance['name']
+ pattern = instance['uuid']
def belongs_to_instance(disk):
return disk.startswith(pattern)
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
index 3aee01067d..ca46add554 100644
--- a/nova/virt/libvirt/imagebackend.py
+++ b/nova/virt/libvirt/imagebackend.py
@@ -35,8 +35,10 @@ from nova.virt.libvirt import utils as libvirt_utils
try:
+ import rados
import rbd
except ImportError:
+ rados = None
rbd = None
@@ -468,6 +470,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)
@@ -477,21 +524,41 @@ class Rbd(Image):
except IndexError:
raise exception.InvalidDevicePath(path=path)
else:
- self.rbd_name = '%s_%s' % (instance['name'], disk_name)
+ self.rbd_name = '%s_%s' % (instance['uuid'], disk_name)
if not CONF.libvirt_images_rbd_pool:
raise RuntimeError(_('You should specify'
' libvirt_images_rbd_pool'
' flag to use rbd images.'))
self.pool = CONF.libvirt_images_rbd_pool
- self.ceph_conf = CONF.libvirt_images_rbd_ceph_conf
+ self.ceph_conf = ascii_str(CONF.libvirt_images_rbd_ceph_conf)
+ self.rbd_user = ascii_str(CONF.rbd_user)
self.rbd = kwargs.get('rbd', rbd)
+ self.rados = kwargs.get('rados', rados)
+
+ 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 = []
- args.extend(['--id', CONF.rbd_user])
+ args.extend(['--id', self.rbd_user])
args.extend(['--conf', self.ceph_conf])
return args
@@ -557,6 +624,12 @@ class Rbd(Image):
return False
+ def _resize(self, volume_name, size):
+ size = int(size)
+
+ 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'))
@@ -580,6 +653,11 @@ class Rbd(Image):
args += self._ceph_args()
libvirt_utils.import_rbd_image(*args)
+ base_size = disk.get_disk_size(base)
+
+ if size and size > base_size:
+ self._resize(self.rbd_name, size)
+
def snapshot_extract(self, target, out_format):
snap = 'rbd:%s/%s' % (self.pool, self.rbd_name)
images.convert_image(snap, target, out_format)