diff options
author | Haomai Wang <haomai@unitedstack.com> | 2013-09-02 15:25:51 +0800 |
---|---|---|
committer | Yaguang Tang <yaguang.tang@canonical.com> | 2014-08-18 22:41:06 +0800 |
commit | 69c86d4fe15c33a6d96550e11d4453d2c4839ef3 (patch) | |
tree | d4d6198239e5bc755a255f800d598c9cc2fee819 | |
parent | 0e4dd1bfc4d910a7ccf7e100d4c1a30663dc06b7 (diff) | |
download | nova-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.py | 6 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_imagebackend.py | 19 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_libvirt.py | 6 | ||||
-rw-r--r-- | nova/virt/libvirt/driver.py | 2 | ||||
-rw-r--r-- | nova/virt/libvirt/imagebackend.py | 84 |
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) |