Add support for file-backed ephemeral disk

Adds support for file-backed ephemeral disk as a localdisk replacement
for lvm.
Supports both RAW and QCOW2 files. Resizing a file-backed instance is
not currently supported.

partially-implements: bp file-io-driver

Change-Id: I1fd499a776cc2e05246c1fc453151aa7bd712dc8
This commit is contained in:
Taylor Jakobson 2017-05-03 13:15:04 -05:00
parent fecc5ec0db
commit 20f3a9a387
3 changed files with 49 additions and 7 deletions

View File

@ -63,7 +63,10 @@ Two disk implementations exist currently.
physical disks local to their system. Images will be cached on the same
volume group as the VMs. The cached images will be periodically cleaned up
by the Nova imagecache manager, at a rate determined by the ``nova.conf``
setting: image_cache_manager_interval.
setting: image_cache_manager_interval. Also supports file-backed ephemeral
storage, which is specified by using the ``QCOW VG - default`` volume group.
Note: Resizing instances with file-backed ephemeral is not currently
supported.
* Shared Storage Pool - utilizes PowerVM's distributed storage. As such this
implementation allows operators to make use of live migration capabilities.

View File

@ -30,6 +30,7 @@ from nova_powervm.tests.virt.powervm import fixtures as fx
from nova_powervm.virt.powervm.disk import driver as disk_dvr
from nova_powervm.virt.powervm.disk import localdisk as ld
from nova_powervm.virt.powervm import exception as npvmex
from nova_powervm.virt.powervm import vm
class TestLocalDisk(test.TestCase):
@ -126,7 +127,8 @@ class TestLocalDisk(test.TestCase):
self.apt, 'vios-uuid', self.vg_uuid, mock_it2f.return_value,
'i_3e865d14_8c1e', powervm.TEST_IMAGE1.size,
d_size=powervm.TEST_IMAGE1.size,
upload_type=tsk_stor.UploadType.IO_STREAM)
upload_type=tsk_stor.UploadType.IO_STREAM,
file_format=powervm.TEST_IMAGE1.disk_format)
mock_it2f.assert_called_once_with(mock_img_api.return_value)
mock_img_api.assert_called_once_with('ctx', powervm.TEST_IMAGE1.id)
@ -246,10 +248,13 @@ class TestLocalDisk(test.TestCase):
# to match the FeedTask.
local = self.get_ls(self.apt)
inst = mock.Mock(uuid=fx.FAKE_INST_UUID)
lpar_uuid = vm.get_pvm_uuid(inst)
mock_disk = mock.Mock()
# As initialized above, remove_maps returns True to trigger update.
local.connect_disk(inst, mock.Mock(), stg_ftsk=None)
local.connect_disk(inst, mock_disk, stg_ftsk=None)
self.assertEqual(1, mock_add_map.call_count)
mock_build_map.assert_called_once_with(
'host_uuid', self.vio_to_vg, lpar_uuid, mock_disk)
mock_add_map.assert_called_once_with(feed[0], 'fake_map')
self.assertEqual(1, self.vio_to_vg.update.call_count)
@ -304,7 +309,6 @@ class TestLocalDisk(test.TestCase):
@mock.patch('pypowervm.wrappers.storage.VG', autospec=True)
def test_extend_disk_not_found(self, mock_vg):
local = self.get_ls(self.apt)
inst = mock.Mock()
inst.name = 'Name Of Instance'
inst.uuid = 'd5065c2c-ac43-3fa6-af32-ea84a3960291'
@ -325,6 +329,26 @@ class TestLocalDisk(test.TestCase):
self.assertEqual(1, resp.update.call_count)
self.assertEqual(vdisk.capacity, 1000)
@mock.patch('pypowervm.wrappers.storage.VG', autospec=True)
def test_extend_disk_file_format(self, mock_vg):
local = self.get_ls(self.apt)
inst = mock.Mock()
inst.name = 'Name Of Instance'
inst.uuid = 'd5065c2c-ac43-3fa6-af32-ea84a3960291'
vdisk = mock.Mock(name='vdisk')
vdisk.configure_mock(name='/path/to/b_Name_Of__d506',
backstore_type=pvm_stor.BackStoreType.USER_QCOW,
file_format=pvm_stor.FileFormatType.QCOW2)
resp = mock.Mock(name='response')
resp.virtual_disks = [vdisk]
mock_vg.get.return_value = resp
self.assertRaises(nova_exc.ResizeError, local.extend_disk,
inst, dict(type='boot'), 10)
vdisk.file_format = pvm_stor.FileFormatType.RAW
self.assertRaises(nova_exc.ResizeError, local.extend_disk,
inst, dict(type='boot'), 10)
def _bld_mocks_for_instance_disk(self):
inst = mock.Mock()
inst.name = 'Name Of Instance'

View File

@ -33,6 +33,7 @@ from nova_powervm import conf as cfg
from nova_powervm.virt.powervm.disk import driver as disk_dvr
from nova_powervm.virt.powervm.disk import imagecache
from nova_powervm.virt.powervm import exception as npvmex
from nova_powervm.virt.powervm.i18n import _
from nova_powervm.virt.powervm.i18n import _LE
from nova_powervm.virt.powervm.i18n import _LI
from nova_powervm.virt.powervm import vm
@ -247,7 +248,8 @@ class LocalStorage(disk_dvr.DiskAdapter):
disk_dvr.IterableToFileAdapter(
IMAGE_API.download(context, image_meta.id)), cache_name,
image_meta.size, d_size=image_meta.size,
upload_type=tsk_stg.UploadType.IO_STREAM)[0]
upload_type=tsk_stg.UploadType.IO_STREAM,
file_format=image_meta.disk_format)[0]
return image.udid
def connect_disk(self, instance, disk_info, stg_ftsk=None):
@ -285,6 +287,17 @@ class LocalStorage(disk_dvr.DiskAdapter):
if stg_ftsk.name == 'localdisk':
stg_ftsk.execute()
def _validate_resizable(self, vdisk):
"""Validates that VDisk supports resizing
:param vdisk: The VDisk to be resized
:raise ResizeError: If resizing is not supported for the given VDisk.
"""
if (vdisk.backstore_type == pvm_stg.BackStoreType.USER_QCOW):
raise nova_exc.ResizeError(
reason=_("Resizing file-backed instances is not currently"
"supported."))
def extend_disk(self, instance, disk_info, size):
"""Extends the disk.
@ -299,7 +312,8 @@ class LocalStorage(disk_dvr.DiskAdapter):
vdisks = vg_wrap.virtual_disks
disk_found = None
for vdisk in vdisks:
if vdisk.name == vol_name:
# Vdisk name can be either disk_name or /path/to/disk_name
if vdisk.name.split('/')[-1] == vol_name.split('/')[-1]:
disk_found = vdisk
break
@ -308,6 +322,7 @@ class LocalStorage(disk_dvr.DiskAdapter):
instance=instance)
raise nova_exc.DiskNotFound(
location=self.vg_name + '/' + vol_name)
self._validate_resizable(disk_found)
# Set the new size
disk_found.capacity = size