From 5453d4b7a10c1a18172d8424004069f4e906e4c5 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 29 Oct 2014 19:57:20 +0000 Subject: [PATCH] virt: convert disk API over to use nova.virt.image.model Convert the nova.virt.disk.api methods over to use the nova.virt.image.model classes instead of taking a file path and use_cow boolean as parameters. Related-Bug: #1257674 Change-Id: I34e2f29e1f068636036226a7745d88640f724d93 --- nova/tests/unit/virt/disk/test_api.py | 17 ++-- nova/tests/unit/virt/disk/test_inject.py | 27 ++++-- nova/tests/unit/virt/libvirt/test_driver.py | 95 +++++++++++-------- .../unit/virt/libvirt/test_imagebackend.py | 11 ++- nova/virt/disk/api.py | 85 ++++++++--------- nova/virt/libvirt/driver.py | 54 +++++++---- nova/virt/libvirt/imagebackend.py | 13 ++- 7 files changed, 177 insertions(+), 125 deletions(-) diff --git a/nova/tests/unit/virt/disk/test_api.py b/nova/tests/unit/virt/disk/test_api.py index 9175d3c6db6c..bcd0273a90a3 100644 --- a/nova/tests/unit/virt/disk/test_api.py +++ b/nova/tests/unit/virt/disk/test_api.py @@ -78,8 +78,9 @@ class APITestCase(test.NoDBTestCase): fake_import_fails)) imgfile = tempfile.NamedTemporaryFile() + image = imgmodel.LocalFileImage(imgfile, imgmodel.FORMAT_QCOW2) self.addCleanup(imgfile.close) - self.assertFalse(api.is_image_extendable(imgfile, use_cow=True)) + self.assertFalse(api.is_image_extendable(image)) def test_resize2fs_success(self): imgfile = tempfile.NamedTemporaryFile() @@ -115,7 +116,6 @@ class APITestCase(test.NoDBTestCase): imgfile = tempfile.NamedTemporaryFile() imgsize = 10 device = "/dev/sdh" - use_cow = True image = imgmodel.LocalFileImage(imgfile, imgmodel.FORMAT_QCOW2) self.flags(resize_fs_using_block_device=True) @@ -134,14 +134,14 @@ class APITestCase(test.NoDBTestCase): api.can_resize_image(imgfile, imgsize).AndReturn(True) utils.execute('qemu-img', 'resize', imgfile, imgsize) - api.is_image_extendable(imgfile, use_cow).AndReturn(True) + api.is_image_extendable(image).AndReturn(True) mount.Mount.instance_for_format(image, None, None).AndReturn(mounter) mounter.get_dev().AndReturn(True) api.resize2fs(mounter.device, run_as_root=True, check_exit_code=[0]) mounter.unget_dev() self.mox.ReplayAll() - api.extend(imgfile, imgsize, use_cow=use_cow) + api.extend(image, imgsize) @mock.patch.object(api, 'can_resize_image', return_value=True) @mock.patch.object(api, 'is_image_extendable') @@ -150,10 +150,11 @@ class APITestCase(test.NoDBTestCase): mock_can_resize_image): imgfile = tempfile.NamedTemporaryFile() imgsize = 10 + image = imgmodel.LocalFileImage(imgfile, imgmodel.FORMAT_QCOW2) self.flags(resize_fs_using_block_device=False) - api.extend(imgfile, imgsize, use_cow=True) + api.extend(image, imgsize) mock_can_resize_image.assert_called_once_with(imgfile, imgsize) mock_execute.assert_called_once_with('qemu-img', 'resize', imgfile, @@ -163,7 +164,7 @@ class APITestCase(test.NoDBTestCase): def test_extend_raw_success(self): imgfile = tempfile.NamedTemporaryFile() imgsize = 10 - use_cow = False + image = imgmodel.LocalFileImage(imgfile, imgmodel.FORMAT_RAW) self.mox.StubOutWithMock(api, 'can_resize_image') self.mox.StubOutWithMock(utils, 'execute') @@ -172,11 +173,11 @@ class APITestCase(test.NoDBTestCase): api.can_resize_image(imgfile, imgsize).AndReturn(True) utils.execute('qemu-img', 'resize', imgfile, imgsize) - api.is_image_extendable(imgfile, use_cow).AndReturn(True) + api.is_image_extendable(image).AndReturn(True) api.resize2fs(imgfile, run_as_root=False, check_exit_code=[0]) self.mox.ReplayAll() - api.extend(imgfile, imgsize, use_cow=use_cow) + api.extend(image, imgsize) HASH_VFAT = utils.get_hash_str(api.FS_FORMAT_VFAT)[:7] HASH_EXT4 = utils.get_hash_str(api.FS_FORMAT_EXT4)[:7] diff --git a/nova/tests/unit/virt/disk/test_inject.py b/nova/tests/unit/virt/disk/test_inject.py index 9a27293c30fb..b65ac83db46c 100644 --- a/nova/tests/unit/virt/disk/test_inject.py +++ b/nova/tests/unit/virt/disk/test_inject.py @@ -35,25 +35,34 @@ class VirtDiskTest(test.NoDBTestCase): imgmodel.FORMAT_QCOW2) def test_inject_data(self): - self.assertTrue(diskapi.inject_data("/some/file", use_cow=True)) + self.assertTrue(diskapi.inject_data( + imgmodel.LocalFileImage("/some/file", imgmodel.FORMAT_QCOW2))) - self.assertTrue(diskapi.inject_data("/some/file", - mandatory=('files',))) + self.assertTrue(diskapi.inject_data( + imgmodel.LocalFileImage("/some/file", imgmodel.FORMAT_RAW), + mandatory=('files',))) - self.assertTrue(diskapi.inject_data("/some/file", key="mysshkey", - mandatory=('key',))) + self.assertTrue(diskapi.inject_data( + imgmodel.LocalFileImage("/some/file", imgmodel.FORMAT_RAW), + key="mysshkey", + mandatory=('key',))) os_name = os.name os.name = 'nt' # Cause password injection to fail self.assertRaises(exception.NovaException, diskapi.inject_data, - "/some/file", admin_password="p", + imgmodel.LocalFileImage("/some/file", + imgmodel.FORMAT_RAW), + admin_password="p", mandatory=('admin_password',)) - self.assertFalse(diskapi.inject_data("/some/file", admin_password="p")) + self.assertFalse(diskapi.inject_data( + imgmodel.LocalFileImage("/some/file", imgmodel.FORMAT_RAW), + admin_password="p")) os.name = os_name - self.assertFalse(diskapi.inject_data("/some/fail/file", - key="mysshkey")) + self.assertFalse(diskapi.inject_data( + imgmodel.LocalFileImage("/some/fail/file", imgmodel.FORMAT_RAW), + key="mysshkey")) def test_inject_data_key(self): diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index ab0f04daa078..5d89012cb16a 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -81,6 +81,7 @@ from nova.virt import driver from nova.virt import fake from nova.virt import firewall as base_firewall from nova.virt import hardware +from nova.virt.image import model as imgmodel from nova.virt.libvirt import blockinfo from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import driver as libvirt_driver @@ -7168,9 +7169,9 @@ class LibvirtConnTestCase(test.NoDBTestCase): self.flags(virt_type="lxc", group='libvirt') - def check_setup_container(path, container_dir=None, use_cow=False): - self.assertEqual(path, '/dev/path/to/dev') - self.assertTrue(use_cow) + def check_setup_container(image, container_dir=None): + self.assertEqual(image.path, '/dev/path/to/dev') + self.assertEqual(image.format, imgmodel.FORMAT_QCOW2) return '/dev/nbd1' bdm = { @@ -9978,9 +9979,14 @@ class LibvirtConnTestCase(test.NoDBTestCase): mock_ensure_tree.assert_has_calls([mock.call('/tmp/rootfs')]) drvr.image_backend.image.assert_has_calls([mock.call(mock_instance, 'disk')]) - setup_container_call = mock.call('/tmp/test.img', - container_dir='/tmp/rootfs', - use_cow=CONF.use_cow_images) + + fmt = imgmodel.FORMAT_RAW + if CONF.use_cow_images: + fmt = imgmodel.FORMAT_QCOW2 + + setup_container_call = mock.call( + imgmodel.LocalFileImage('/tmp/test.img', fmt), + container_dir='/tmp/rootfs') mock_setup_container.assert_has_calls([setup_container_call]) mock_get_info.assert_has_calls([mock.call(mock_instance)]) mock_clean.assert_has_calls([mock.call(container_dir='/tmp/rootfs')]) @@ -10041,9 +10047,14 @@ class LibvirtConnTestCase(test.NoDBTestCase): mock_ensure_tree.assert_has_calls([mock.call('/tmp/rootfs')]) drvr.image_backend.image.assert_has_calls([mock.call(mock_instance, 'disk')]) - setup_container_call = mock.call('/tmp/test.img', - container_dir='/tmp/rootfs', - use_cow=CONF.use_cow_images) + + fmt = imgmodel.FORMAT_RAW + if CONF.use_cow_images: + fmt = imgmodel.FORMAT_QCOW2 + + setup_container_call = mock.call( + imgmodel.LocalFileImage('/tmp/test.img', fmt), + container_dir='/tmp/rootfs') mock_setup_container.assert_has_calls([setup_container_call]) mock_get_info.assert_has_calls([mock.call(mock_instance)]) mock_clean.assert_has_calls([mock.call(container_dir='/tmp/rootfs')]) @@ -10090,9 +10101,14 @@ class LibvirtConnTestCase(test.NoDBTestCase): mock_ensure_tree.assert_has_calls([mock.call('/tmp/rootfs')]) drvr.image_backend.image.assert_has_calls([mock.call(mock_instance, 'disk')]) - setup_container_call = mock.call('/tmp/test.img', - container_dir='/tmp/rootfs', - use_cow=CONF.use_cow_images) + + fmt = imgmodel.FORMAT_RAW + if CONF.use_cow_images: + fmt = imgmodel.FORMAT_QCOW2 + + setup_container_call = mock.call( + imgmodel.LocalFileImage('/tmp/test.img', fmt), + container_dir='/tmp/rootfs') mock_setup_container.assert_has_calls([setup_container_call]) mock_get_info.assert_has_calls([mock.call(mock_instance)]) teardown_call = mock.call(container_dir='/tmp/rootfs') @@ -11688,17 +11704,17 @@ class LibvirtDriverTestCase(test.NoDBTestCase): @mock.patch('nova.virt.disk.api.extend') def test_disk_resize_raw(self, mock_extend): - info = {'type': 'raw', 'path': '/test/disk'} + image = imgmodel.LocalFileImage("/test/disk", + imgmodel.FORMAT_RAW) - self.drvr._disk_resize(info, 50) - mock_extend.assert_called_once_with(info['path'], 50, use_cow=False) + self.drvr._disk_resize(image, 50) + mock_extend.assert_called_once_with(image, 50) @mock.patch('nova.virt.disk.api.can_resize_image') @mock.patch('nova.virt.disk.api.is_image_extendable') @mock.patch('nova.virt.disk.api.extend') def test_disk_resize_qcow2( self, mock_extend, mock_can_resize, mock_is_image_extendable): - info = {'type': 'qcow2', 'path': '/test/disk'} with contextlib.nested( mock.patch.object( @@ -11710,12 +11726,15 @@ class LibvirtDriverTestCase(test.NoDBTestCase): mock_can_resize.return_value = True mock_is_image_extendable.return_value = True - self.drvr._disk_resize(info, 50) + imageqcow2 = imgmodel.LocalFileImage("/test/disk", + imgmodel.FORMAT_QCOW2) + imageraw = imgmodel.LocalFileImage("/test/disk", + imgmodel.FORMAT_RAW) + self.drvr._disk_resize(imageqcow2, 50) - mock_disk_qcow2_to_raw.assert_called_once_with(info['path']) - mock_extend.assert_called_once_with( - info['path'], 50, use_cow=False) - mock_disk_raw_to_qcow2.assert_called_once_with(info['path']) + mock_disk_qcow2_to_raw.assert_called_once_with(imageqcow2.path) + mock_extend.assert_called_once_with(imageraw, 50) + mock_disk_raw_to_qcow2.assert_called_once_with(imageqcow2.path) def _test_finish_migration(self, power_on, resize_instance=False): """Test for nova.virt.libvirt.libvirt_driver.LivirtConnection @@ -11761,7 +11780,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase): else: return hardware.InstanceInfo(state=power_state.SHUTDOWN) - def fake_disk_resize(info, size): + def fake_disk_resize(image, size): self.fake_disk_resize_called = True self.flags(use_cow_images=True) @@ -12065,7 +12084,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase): @mock.patch('nova.virt.netutils.get_injected_network_template') @mock.patch('nova.virt.disk.api.inject_data') - def _test_inject_data(self, driver_params, disk_params, + def _test_inject_data(self, driver_params, path, disk_params, disk_inject_data, inj_network, called=True): class ImageBackend(object): @@ -12081,7 +12100,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase): inj_network.side_effect = fake_inj_network image_backend = ImageBackend() - image_backend.path = disk_params[0] + image_backend.path = path with mock.patch.object( self.drvr.image_backend, @@ -12093,8 +12112,9 @@ class LibvirtDriverTestCase(test.NoDBTestCase): if called: disk_inject_data.assert_called_once_with( + mock.ANY, *disk_params, - partition=None, mandatory=('files',), use_cow=True) + partition=None, mandatory=('files',)) self.assertEqual(disk_inject_data.called, called) @@ -12112,18 +12132,18 @@ class LibvirtDriverTestCase(test.NoDBTestCase): driver_params = self._test_inject_data_default_driver_params() driver_params['admin_pass'] = 'foobar' disk_params = [ - '/path', # injection_path None, # key None, # net {}, # metadata 'foobar', # admin_pass None, # files ] - self._test_inject_data(driver_params, disk_params) + self._test_inject_data(driver_params, "/path", disk_params) # Test with the configuration setted to false. self.flags(inject_password=False, group='libvirt') - self._test_inject_data(driver_params, disk_params, called=False) + self._test_inject_data(driver_params, "/path", + disk_params, called=False) def test_inject_data_key(self): driver_params = self._test_inject_data_default_driver_params() @@ -12131,18 +12151,18 @@ class LibvirtDriverTestCase(test.NoDBTestCase): self.flags(inject_key=True, group='libvirt') disk_params = [ - '/path', # injection_path 'key-content', # key None, # net {}, # metadata None, # admin_pass None, # files ] - self._test_inject_data(driver_params, disk_params) + self._test_inject_data(driver_params, "/path", disk_params) # Test with the configuration setted to false. self.flags(inject_key=False, group='libvirt') - self._test_inject_data(driver_params, disk_params, called=False) + self._test_inject_data(driver_params, "/path", + disk_params, called=False) def test_inject_data_metadata(self): instance_metadata = {'metadata': {'data': 'foo'}} @@ -12150,52 +12170,49 @@ class LibvirtDriverTestCase(test.NoDBTestCase): **instance_metadata ) disk_params = [ - '/path', # injection_path None, # key None, # net {'data': 'foo'}, # metadata None, # admin_pass None, # files ] - self._test_inject_data(driver_params, disk_params) + self._test_inject_data(driver_params, "/path", disk_params) def test_inject_data_files(self): driver_params = self._test_inject_data_default_driver_params() driver_params['files'] = ['file1', 'file2'] disk_params = [ - '/path', # injection_path None, # key None, # net {}, # metadata None, # admin_pass ['file1', 'file2'], # files ] - self._test_inject_data(driver_params, disk_params) + self._test_inject_data(driver_params, "/path", disk_params) def test_inject_data_net(self): driver_params = self._test_inject_data_default_driver_params() driver_params['network_info'] = {'net': 'eno1'} disk_params = [ - '/path', # injection_path None, # key {'net': 'eno1'}, # net {}, # metadata None, # admin_pass None, # files ] - self._test_inject_data(driver_params, disk_params) + self._test_inject_data(driver_params, "/path", disk_params) def test_inject_not_exist_image(self): driver_params = self._test_inject_data_default_driver_params() disk_params = [ - '/fail/path', # injection_path 'key-content', # key None, # net None, # metadata None, # admin_pass None, # files ] - self._test_inject_data(driver_params, disk_params, called=False) + self._test_inject_data(driver_params, "/fail/path", + disk_params, called=False) def _test_attach_detach_interface(self, method, power_state, expected_flags): diff --git a/nova/tests/unit/virt/libvirt/test_imagebackend.py b/nova/tests/unit/virt/libvirt/test_imagebackend.py index c223501520aa..f92a10603bc9 100644 --- a/nova/tests/unit/virt/libvirt/test_imagebackend.py +++ b/nova/tests/unit/virt/libvirt/test_imagebackend.py @@ -35,6 +35,7 @@ from nova.openstack.common import imageutils from nova import test from nova.tests.unit import fake_processutils from nova.tests.unit.virt.libvirt import fake_libvirt_utils +from nova.virt.image import model as imgmodel from nova.virt import images from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import imagebackend @@ -258,7 +259,8 @@ class RawTestCase(_ImageTestCase, test.NoDBTestCase): fn = self.prepare_mocks() fn(max_size=self.SIZE, target=self.TEMPLATE_PATH, image_id=None) imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.PATH) - imagebackend.disk.extend(self.PATH, self.SIZE, use_cow=False) + image = imgmodel.LocalFileImage(self.PATH, imgmodel.FORMAT_RAW) + imagebackend.disk.extend(image, self.SIZE) self.mox.ReplayAll() image = self.image_class(self.INSTANCE, self.NAME) @@ -413,7 +415,8 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): imagebackend.Image.verify_base_size(self.TEMPLATE_PATH, self.SIZE) imagebackend.libvirt_utils.create_cow_image(self.TEMPLATE_PATH, self.PATH) - imagebackend.disk.extend(self.PATH, self.SIZE, use_cow=True) + image = imgmodel.LocalFileImage(self.PATH, imgmodel.FORMAT_QCOW2) + imagebackend.disk.extend(image, self.SIZE) self.mox.ReplayAll() image = self.image_class(self.INSTANCE, self.NAME) @@ -460,7 +463,9 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase): imagebackend.Image.verify_base_size(self.TEMPLATE_PATH, self.SIZE) imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.QCOW2_BASE) - imagebackend.disk.extend(self.QCOW2_BASE, self.SIZE, use_cow=True) + image = imgmodel.LocalFileImage(self.QCOW2_BASE, + imgmodel.FORMAT_QCOW2) + imagebackend.disk.extend(image, self.SIZE) os.path.exists(self.PATH).AndReturn(True) self.mox.ReplayAll() diff --git a/nova/virt/disk/api.py b/nova/virt/disk/api.py index 83b530878290..7595ffffd5c6 100644 --- a/nova/virt/disk/api.py +++ b/nova/virt/disk/api.py @@ -173,23 +173,28 @@ def get_disk_size(path): return images.qemu_img_info(path).virtual_size -def extend(image, size, use_cow=False): +def extend(image, size): """Increase image to size. - :param image: path to disk image file + :param image: instance of nova.virt.image.model.Image :param size: image size in bytes - :param use_cow: whether the disk is a qcow2 file """ - if not can_resize_image(image, size): + + # Currently can only resize FS in local images + if not isinstance(image, imgmodel.LocalImage): return - utils.execute('qemu-img', 'resize', image, size) + if not can_resize_image(image.path, size): + return - if use_cow and not CONF.resize_fs_using_block_device: + utils.execute('qemu-img', 'resize', image.path, size) + + if (image.format != imgmodel.FORMAT_RAW and + not CONF.resize_fs_using_block_device): return # if we can't access the filesystem, we can't do anything more - if not is_image_extendable(image, use_cow): + if not is_image_extendable(image): return def safe_resize2fs(dev, run_as_root=False, finally_call=lambda: None): @@ -202,23 +207,24 @@ def extend(image, size, use_cow=False): finally_call() # NOTE(vish): attempts to resize filesystem - if use_cow: + if image.format != imgmodel.FORMAT_RAW: # in case of non-raw disks we can't just resize the image, but # rather the mounted device instead mounter = mount.Mount.instance_for_format( - imgmodel.LocalFileImage(image, - imgmodel.FORMAT_QCOW2), - None, None) + image, None, None) if mounter.get_dev(): safe_resize2fs(mounter.device, run_as_root=True, finally_call=mounter.unget_dev) else: - safe_resize2fs(image) + safe_resize2fs(image.path) def can_resize_image(image, size): - """Check whether we can resize the container image file.""" + """Check whether we can resize the container image file. + :param image: path to local image file + :param size: the image size in bytes + """ LOG.debug('Checking if we can resize image %(image)s. ' 'size=%(size)s', {'image': image, 'size': size}) @@ -231,18 +237,18 @@ def can_resize_image(image, size): return True -def is_image_extendable(image, use_cow=False): +def is_image_extendable(image): """Check whether we can extend the image.""" - LOG.debug('Checking if we can extend filesystem inside %(image)s. ' - 'CoW=%(use_cow)s', {'image': image, 'use_cow': use_cow}) + LOG.debug('Checking if we can extend filesystem inside %(image)s.', + {'image': image}) - # Check the image is unpartitioned - if use_cow: + # For anything except a local raw file we must + # go via the VFS layer + if (not isinstance(image, imgmodel.LocalImage) or + image.format != imgmodel.FORMAT_RAW): fs = None try: - fs = vfs.VFS.instance_for_image( - imgmodel.LocalFileImage(image, imgmodel.FORMAT_QCOW2), - None) + fs = vfs.VFS.instance_for_image(image, None) fs.setup(mount=False) if fs.get_image_fs() in SUPPORTED_FS_TO_EXTEND: return True @@ -263,7 +269,7 @@ def is_image_extendable(image, use_cow=False): else: # For raw, we can directly inspect the file system try: - utils.execute('e2label', image) + utils.execute('e2label', image.file) except processutils.ProcessExecutionError as e: LOG.debug('Unable to determine label for image %(image)s with ' 'error %(error)s. Cannot resize.', @@ -279,25 +285,18 @@ class _DiskImage(object): tmp_prefix = 'openstack-disk-mount-tmp' - def __init__(self, image, partition=None, use_cow=False, mount_dir=None): + def __init__(self, image, partition=None, mount_dir=None): """Create a new _DiskImage object instance - :param image: the path to the disk image file + :param image: instance of nova.virt.image.model.Image :param partition: the partition number within the image - :param use_cow: whether the disk is in qcow2 format :param mount_dir: the directory to mount the image on """ # These passed to each mounter self.partition = partition self.mount_dir = mount_dir - - if use_cow: - self.image = imgmodel.LocalFileImage(image, - imgmodel.FORMAT_QCOW2) - else: - self.image = imgmodel.LocalFileImage(image, - imgmodel.FORMAT_RAW) + self.image = image # Internal self._mkdir = False @@ -383,17 +382,16 @@ class _DiskImage(object): # Public module functions def inject_data(image, key=None, net=None, metadata=None, admin_password=None, - files=None, partition=None, use_cow=False, mandatory=()): + files=None, partition=None, mandatory=()): """Inject the specified items into a disk image. - :param image: the local file path + :param image: instance of nova.virt.image.model.Image :param key: the SSH public key to inject :param net: the network configuration to inject :param metadata: the user metadata to inject :param admin_password: the root password to set :param files: the files to copy into the image :param partition: the partition number to access - :param use_cow: whether the image is in qcow2 format :param mandatory: the list of parameters which must not fail to inject If an item name is not specified in the MANDATORY iterable, then a warning @@ -409,15 +407,11 @@ def inject_data(image, key=None, net=None, metadata=None, admin_password=None, """ LOG.debug("Inject data image=%(image)s key=%(key)s net=%(net)s " "metadata=%(metadata)s admin_password= " - "files=%(files)s partition=%(partition)s use_cow=%(use_cow)s", + "files=%(files)s partition=%(partition)s", {'image': image, 'key': key, 'net': net, 'metadata': metadata, - 'files': files, 'partition': partition, 'use_cow': use_cow}) - fmt = imgmodel.FORMAT_RAW - if use_cow: - fmt = imgmodel.FORMAT_QCOW2 + 'files': files, 'partition': partition}) try: - fs = vfs.VFS.instance_for_image( - imgmodel.LocalFileImage(image, fmt), partition) + fs = vfs.VFS.instance_for_image(image, partition) fs.setup() except Exception as e: # If a mandatory item is passed to this function, @@ -437,15 +431,18 @@ def inject_data(image, key=None, net=None, metadata=None, admin_password=None, fs.teardown() -def setup_container(image, container_dir, use_cow=False): +def setup_container(image, container_dir): """Setup the LXC container. + :param image: instance of nova.virt.image.model.Image + :param container_dir: directory to mount the image at + It will mount the loopback image to the container directory in order to create the root filesystem for the container. Returns path of image device which is mounted to the container directory. """ - img = _DiskImage(image=image, use_cow=use_cow, mount_dir=container_dir) + img = _DiskImage(image=image, mount_dir=container_dir) dev = img.mount() if dev is None: LOG.error(_LE("Failed to mount container filesystem '%(image)s' " diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 72b122f88b9d..c1461d3022c7 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -88,6 +88,7 @@ from nova.virt.disk.vfs import guestfs from nova.virt import driver from nova.virt import firewall from nova.virt import hardware +from nova.virt.image import model as imgmodel from nova.virt.libvirt import blockinfo from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import dmcrypt @@ -2734,10 +2735,14 @@ class LibvirtDriver(driver.ComputeDriver): injection_image.path, instance=instance) return try: - disk.inject_data(injection_image.path, + fmt = imgmodel.FORMAT_RAW + if CONF.use_cow_images: + fmt = imgmodel.FORMAT_QCOW2 + image = imgmodel.LocalFileImage(injection_image.path, + fmt) + disk.inject_data(image, key, net, metadata, admin_pass, files, partition=target_partition, - use_cow=CONF.use_cow_images, mandatory=('files',)) except Exception as e: with excutils.save_and_reraise_exception(): @@ -4301,9 +4306,12 @@ class LibvirtDriver(driver.ComputeDriver): container_dir = os.path.join(inst_path, 'rootfs') fileutils.ensure_tree(container_dir) - rootfs_dev = disk.setup_container(disk_path, - container_dir=container_dir, - use_cow=use_cow) + fmt = imgmodel.FORMAT_RAW + if use_cow: + fmt = imgmodel.FORMAT_QCOW2 + image = imgmodel.LocalFileImage(disk_path, fmt) + rootfs_dev = disk.setup_container(image, + container_dir=container_dir) try: # Save rootfs device to disconnect it when deleting the instance @@ -6415,32 +6423,40 @@ class LibvirtDriver(driver.ComputeDriver): '-O', 'raw', path, path_raw) utils.execute('mv', path_raw, path) - def _disk_resize(self, info, size): + def _disk_resize(self, image, size): """Attempts to resize a disk to size + :param image: an instance of nova.virt.image.model.Image + Attempts to resize a disk by checking the capabilities and preparing the format, then calling disk.api.extend. Note: Currently only support disk extend. """ + + if not isinstance(image, imgmodel.LocalFileImage): + LOG.debug("Skipping resize of non-local image") + return + # If we have a non partitioned image that we can extend # then ensure we're in 'raw' format so we can extend file system. - fmt, org = [info['type']] * 2 - pth = info['path'] - if (size and fmt == 'qcow2' and - disk.can_resize_image(pth, size) and - disk.is_image_extendable(pth, use_cow=True)): - self._disk_qcow2_to_raw(pth) - fmt = 'raw' + converted = False + if (size and + image.format == imgmodel.FORMAT_QCOW2 and + disk.can_resize_image(image.path, size) and + disk.is_image_extendable(image)): + self._disk_qcow2_to_raw(image.path) + converted = True + image = imgmodel.LocalFileImage(image.path, + imgmodel.FORMAT_RAW) if size: - use_cow = fmt == 'qcow2' - disk.extend(pth, size, use_cow=use_cow) + disk.extend(image, size) - if fmt != org: + if converted: # back to qcow2 (no backing_file though) so that snapshot # will be available - self._disk_raw_to_qcow2(pth) + self._disk_raw_to_qcow2(image.path) def finish_migration(self, context, migration, instance, disk_info, network_info, image_meta, resize_instance, @@ -6452,7 +6468,9 @@ class LibvirtDriver(driver.ComputeDriver): for info in disk_info: size = self._disk_size_from_instance(instance, info) if resize_instance: - self._disk_resize(info, size) + image = imgmodel.LocalFileImage(info['path'], + info['type']) + self._disk_resize(image, size) if info['type'] == 'raw' and CONF.use_cow_images: self._disk_raw_to_qcow2(info['path']) diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py index ee42f8939a16..725b67ac5f97 100644 --- a/nova/virt/libvirt/imagebackend.py +++ b/nova/virt/libvirt/imagebackend.py @@ -35,6 +35,7 @@ from nova import keymgr from nova.openstack.common import fileutils from nova import utils from nova.virt.disk import api as disk +from nova.virt.image import model as imgmodel from nova.virt import images from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import dmcrypt @@ -425,8 +426,9 @@ class Raw(Image): libvirt_utils.copy_image(base, target) if size: # class Raw is misnamed, format may not be 'raw' in all cases - use_cow = self.driver_format == 'qcow2' - disk.extend(target, size, use_cow=use_cow) + image = imgmodel.LocalFileImage(target, + self.driver_format) + disk.extend(image, size) generating = 'image_id' not in kwargs if generating: @@ -473,7 +475,8 @@ class Qcow2(Image): # This would be keyed on a 'preallocate_images' setting. libvirt_utils.create_cow_image(base, target) if size: - disk.extend(target, size, use_cow=True) + image = imgmodel.LocalFileImage(target, imgmodel.FORMAT_QCOW2) + disk.extend(image, size) # Download the unmodified base image unless we already have a copy. if not os.path.exists(base): @@ -502,7 +505,9 @@ class Qcow2(Image): if not os.path.exists(legacy_base): with fileutils.remove_path_on_error(legacy_base): libvirt_utils.copy_image(base, legacy_base) - disk.extend(legacy_base, legacy_backing_size, use_cow=True) + image = imgmodel.LocalFileImage(legacy_base, + imgmodel.FORMAT_QCOW2) + disk.extend(image, legacy_backing_size) if not os.path.exists(self.path): with fileutils.remove_path_on_error(self.path):