Merge "Extend volume for libvirt network volumes (RBD)"

This commit is contained in:
Zuul 2019-03-07 00:52:10 +00:00 committed by Gerrit Code Review
commit d3254af0fe
21 changed files with 181 additions and 39 deletions

View File

@ -50,6 +50,7 @@ from oslo_service import periodic_task
from oslo_utils import excutils
from oslo_utils import strutils
from oslo_utils import timeutils
from oslo_utils import units
import six
from six.moves import range
@ -8298,7 +8299,8 @@ class ComputeManager(manager.Manager):
try:
self.driver.extend_volume(connection_info,
instance)
instance,
bdm.volume_size * units.Gi)
except Exception as ex:
LOG.warning('Extend volume failed, '
'volume_id=%(volume_id)s, reason: %(msg)s',

View File

@ -2956,6 +2956,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
def test_extend_volume(self):
inst_obj = objects.Instance(id=3, uuid=uuids.instance)
connection_info = {'foo': 'bar'}
new_size = 20
bdm = objects.BlockDeviceMapping(
source_type='volume',
destination_type='volume',
@ -2973,13 +2974,13 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
def do_test(bdm_save, bdm_get_by_vol_and_inst, extend_volume,
volume_api):
bdm_get_by_vol_and_inst.return_value = bdm
volume_api.get.return_value = {'size': 20}
volume_api.get.return_value = {'size': new_size}
self.compute.extend_volume(
self.context, inst_obj, uuids.volume_id)
bdm_save.assert_called_once_with()
extend_volume.assert_called_once_with(
connection_info, inst_obj)
connection_info, inst_obj, new_size * pow(1024, 3))
do_test()

View File

@ -7964,9 +7964,11 @@ class LibvirtConnTestCase(test.NoDBTestCase,
for state in (power_state.RUNNING, power_state.PAUSED):
guest.get_power_state = mock.Mock(return_value=state)
drvr.extend_volume(connection_info, instance)
drvr.extend_volume(connection_info,
instance, new_size_in_kb * 1024)
drvr._extend_volume.assert_called_with(connection_info,
instance)
instance,
new_size_in_kb * 1024)
guest.get_block_device.assert_called_with('/fake')
block_device.resize.assert_called_with(20480)
@ -7979,7 +7981,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
connection_info = {'driver_volume_type': 'fake'}
self.assertRaises(exception.ExtendVolumeNotSupported,
drvr.extend_volume,
connection_info, instance)
connection_info, instance, 0)
def test_extend_volume_disk_not_found(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -7998,7 +8000,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
drvr._host.get_guest = mock.Mock(return_value=guest)
drvr._extend_volume = mock.Mock(return_value=new_size_in_kb)
drvr.extend_volume(connection_info, instance)
drvr.extend_volume(connection_info, instance, new_size_in_kb * 1024)
def test_extend_volume_with_instance_not_found(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -8013,7 +8015,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
connection_info = {'driver_volume_type': 'fake'}
self.assertRaises(exception.InstanceNotFound,
drvr.extend_volume,
connection_info, instance)
connection_info, instance, 0)
def test_extend_volume_with_libvirt_error(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -8038,7 +8040,80 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self.assertRaises(fakelibvirt.libvirtError,
drvr.extend_volume,
connection_info, instance)
connection_info, instance, new_size_in_kb * 1024)
def test_extend_volume_with_no_device_path_attribute(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
instance = objects.Instance(**self.test_instance)
connection_info = {
'serial': '58a84f6d-3f0c-4e19-a0af-eb657b790657',
'driver_volume_type': 'fake',
'data': {'cluster_name': 'fake',
'auth_enabled': False,
'volume_id': '58a84f6d-3f0c-4e19-a0af-eb657b790657',
'access_mode': 'rw'}
}
new_size_in_kb = 20 * 1024 * 1024
guest = mock.Mock(spec='nova.virt.libvirt.guest.Guest')
# block_device
block_device = mock.Mock(
spec='nova.virt.libvirt.guest.BlockDevice')
block_device.resize = mock.Mock()
disk = mock.Mock(
spec='nova.virt.libvirt.config.LibvirtConfigGuestDisk',
serial='58a84f6d-3f0c-4e19-a0af-eb657b790657',
target_dev='vdb')
guest.get_block_device = mock.Mock(return_value=block_device)
guest.get_all_disks = mock.Mock(return_value=[disk])
drvr._host.get_guest = mock.Mock(return_value=guest)
drvr._extend_volume = mock.Mock(return_value=new_size_in_kb)
for state in (power_state.RUNNING, power_state.PAUSED):
guest.get_power_state = mock.Mock(return_value=state)
drvr.extend_volume(connection_info, instance,
new_size_in_kb * 1024)
drvr._extend_volume.assert_called_with(connection_info,
instance,
new_size_in_kb * 1024)
guest.get_block_device.assert_called_with('vdb')
block_device.resize.assert_called_with(20480)
def test_extend_volume_no_disk_found_by_serial(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
instance = objects.Instance(**self.test_instance)
connection_info = {
'serial': '58a84f6d-3f0c-4e19-a0af-eb657b790657',
'driver_volume_type': 'fake',
'data': {'cluster_name': 'fake',
'auth_enabled': False,
'volume_id': '58a84f6d-3f0c-4e19-a0af-eb657b790657',
'access_mode': 'rw'}
}
new_size_in_kb = 20 * 1024 * 1024
guest = mock.Mock(spec='nova.virt.libvirt.guest.Guest')
# block_device
block_device = mock.Mock(
spec='nova.virt.libvirt.guest.BlockDevice')
block_device.resize = mock.Mock()
disk = mock.Mock(
spec='nova.virt.libvirt.config.LibvirtConfigGuestDisk',
serial='12345678-abcd-abcd-abcd-0123456789012',
target_dev='vdb')
guest.get_block_device = mock.Mock(return_value=block_device)
guest.get_all_disks = mock.Mock(return_value=[disk])
drvr._host.get_guest = mock.Mock(return_value=guest)
drvr._extend_volume = mock.Mock(return_value=new_size_in_kb)
guest.get_power_state = mock.Mock(return_value=power_state.RUNNING)
self.assertRaises(
exception.VolumeNotFound,
drvr.extend_volume,
connection_info,
instance,
new_size_in_kb * 1024
)
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')

View File

@ -68,13 +68,16 @@ class LibvirtFibreChannelVolumeDriverTestCase(
def test_extend_volume(self):
device_path = '/dev/fake-dev'
connection_info = {'data': {'device_path': device_path}}
requested_size = 1
libvirt_driver = fibrechannel.LibvirtFibreChannelVolumeDriver(
self.fake_host)
libvirt_driver.connector.extend_volume = mock.MagicMock(return_value=1)
libvirt_driver.connector.extend_volume = mock.MagicMock(
return_value=requested_size)
new_size = libvirt_driver.extend_volume(connection_info,
mock.sentinel.instance)
mock.sentinel.instance,
requested_size)
self.assertEqual(1, new_size)
self.assertEqual(requested_size, new_size)
libvirt_driver.connector.extend_volume.assert_called_once_with(
connection_info['data'])

View File

@ -64,12 +64,15 @@ class LibvirtISCSIVolumeDriverTestCase(
def test_extend_volume(self):
device_path = '/dev/fake-dev'
connection_info = {'data': {'device_path': device_path}}
requested_size = 1
libvirt_driver = iscsi.LibvirtISCSIVolumeDriver(self.fake_host)
libvirt_driver.connector.extend_volume = mock.MagicMock(return_value=1)
libvirt_driver.connector.extend_volume = mock.MagicMock(
return_value=requested_size)
new_size = libvirt_driver.extend_volume(connection_info,
mock.sentinel.instance)
mock.sentinel.instance,
requested_size)
self.assertEqual(1, new_size)
self.assertEqual(requested_size, new_size)
libvirt_driver.connector.extend_volume.assert_called_once_with(
connection_info['data'])

View File

@ -241,3 +241,16 @@ class LibvirtNetVolumeDriverTestCase(
tree.find('./auth/secret').get('uuid'))
libvirt_driver.disconnect_volume(connection_info,
mock.sentinel.instance)
def test_extend_volume(self):
device_path = '/dev/fake-dev'
connection_info = {'data': {'device_path': device_path}}
requested_size = 20 * pow(1024, 3) # 20GiB
libvirt_driver = net.LibvirtNetVolumeDriver(self.fake_host)
new_size = libvirt_driver.extend_volume(connection_info,
mock.sentinel.instance,
requested_size)
self.assertEqual(requested_size, new_size)

View File

@ -63,4 +63,5 @@ class LibvirtScaleIOVolumeDriverTestCase(
'extend_volume',
side_effect=brick_extend_vol):
self.assertEqual(extended_vol_size,
sio.extend_volume(conn, mock.sentinel.instance))
sio.extend_volume(conn, mock.sentinel.instance,
extended_vol_size))

View File

@ -127,32 +127,34 @@ class LibvirtStorPoolVolumeDriverTestCase(
ci_1 = self.conn_info('1')
ci_2 = self.conn_info('2')
rs_1 = ci_1['data']['real_size']
rs_2 = ci_2['data']['real_size']
self.assertRaises(MockStorPoolExc,
libvirt_driver.extend_volume,
ci_1, mock.sentinel.instance)
ci_1, mock.sentinel.instance, rs_1)
self.assertRaises(MockStorPoolExc,
libvirt_driver.extend_volume,
ci_2, mock.sentinel.instance)
ci_2, mock.sentinel.instance, rs_2)
libvirt_driver.connect_volume(ci_1, mock.sentinel.instance)
self.assertStorpoolAttached(('1',))
ns_1 = libvirt_driver.extend_volume(ci_1, mock.sentinel.instance)
ns_1 = libvirt_driver.extend_volume(ci_1, mock.sentinel.instance, rs_1)
self.assertEqual(ci_1['data']['real_size'], ns_1)
self.assertRaises(MockStorPoolExc,
libvirt_driver.extend_volume,
ci_2, mock.sentinel.instance)
ci_2, mock.sentinel.instance, rs_2)
libvirt_driver.connect_volume(ci_2, mock.sentinel.instance)
self.assertStorpoolAttached(('1', '2'))
ns_1 = libvirt_driver.extend_volume(ci_1, mock.sentinel.instance)
ns_1 = libvirt_driver.extend_volume(ci_1, mock.sentinel.instance, rs_1)
self.assertEqual(ci_1['data']['real_size'], ns_1)
ns_2 = libvirt_driver.extend_volume(ci_2, mock.sentinel.instance)
ns_2 = libvirt_driver.extend_volume(ci_2, mock.sentinel.instance, rs_2)
self.assertEqual(ci_2['data']['real_size'], ns_2)
self.assertRaises(MockStorPoolExc,
@ -168,7 +170,7 @@ class LibvirtStorPoolVolumeDriverTestCase(
self.assertRaises(MockStorPoolExc,
libvirt_driver.extend_volume,
ci_1, mock.sentinel.instance)
ci_1, mock.sentinel.instance, rs_1)
libvirt_driver.disconnect_volume(ci_2, mock.sentinel.instance)
self.assertDictEqual({}, test_attached)

View File

@ -592,7 +592,7 @@ class TestPowerVMDriver(test.NoDBTestCase):
@mock.patch('nova.virt.powervm.volume.fcvscsi.FCVscsiVolumeAdapter')
def test_extend_volume(self, mock_vscsi_adpt):
mock_bdm = self._fake_bdms()['block_device_mapping'][0]
self.drv.extend_volume(mock_bdm.get('connection_info'), self.inst)
self.drv.extend_volume(mock_bdm.get('connection_info'), self.inst, 0)
mock_vscsi_adpt.return_value.extend_volume.assert_called_once_with()
def test_vol_drv_iter(self):

View File

@ -557,13 +557,15 @@ class ComputeDriver(object):
"""
raise NotImplementedError()
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the disk attached to the instance.
:param dict connection_info:
The connection for the extended volume.
:param nova.objects.instance.Instance instance:
The instance whose volume gets extended.
:param int requested_size
The requested new size of the volume in bytes
:return: None
"""

View File

@ -340,7 +340,7 @@ class FakeDriver(driver.ComputeDriver):
self._mounts[instance_name] = {}
self._mounts[instance_name][mountpoint] = new_connection_info
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the disk attached to the instance."""
pass

View File

@ -1325,9 +1325,10 @@ class LibvirtDriver(driver.ComputeDriver):
driver_block_device.get_volume_id(connection_info),
instance=instance)
def _extend_volume(self, connection_info, instance):
def _extend_volume(self, connection_info, instance, requested_size):
vol_driver = self._get_volume_driver(connection_info)
return vol_driver.extend_volume(connection_info, instance)
return vol_driver.extend_volume(connection_info, instance,
requested_size)
def _use_native_luks(self, encryption=None):
"""Is LUKS the required provider and native QEMU LUKS available
@ -1677,9 +1678,10 @@ class LibvirtDriver(driver.ComputeDriver):
self._disconnect_volume(context, connection_info, instance,
encryption=encryption)
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
try:
new_size = self._extend_volume(connection_info, instance)
new_size = self._extend_volume(connection_info, instance,
requested_size)
except NotImplementedError:
raise exception.ExtendVolumeNotSupported()
@ -1690,7 +1692,22 @@ class LibvirtDriver(driver.ComputeDriver):
state = guest.get_power_state(self._host)
active_state = state in (power_state.RUNNING, power_state.PAUSED)
if active_state:
disk_path = connection_info['data']['device_path']
if 'device_path' in connection_info['data']:
disk_path = connection_info['data']['device_path']
else:
# Some drivers (eg. net) don't put the device_path
# into the connection_info. Match disks by their serial
# number instead
volume_id = driver_block_device.get_volume_id(
connection_info)
disk = next(iter([
d for d in guest.get_all_disks()
if d.serial == volume_id
]), None)
if not disk:
raise exception.VolumeNotFound(volume_id=volume_id)
disk_path = disk.target_dev
LOG.debug('resizing block device %(dev)s to %(size)u kb',
{'dev': disk_path, 'size': new_size})
dev = guest.get_block_device(disk_path)

View File

@ -75,7 +75,7 @@ class LibvirtFibreChannelVolumeDriver(libvirt_volume.LibvirtBaseVolumeDriver):
super(LibvirtFibreChannelVolumeDriver,
self).disconnect_volume(connection_info, instance)
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the volume."""
LOG.debug("calling os-brick to extend FC Volume", instance=instance)
new_size = self.connector.extend_volume(connection_info['data'])

View File

@ -80,7 +80,7 @@ class LibvirtISCSIVolumeDriver(libvirt_volume.LibvirtBaseVolumeDriver):
super(LibvirtISCSIVolumeDriver,
self).disconnect_volume(connection_info, instance)
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the volume."""
LOG.debug("calling os-brick to extend iSCSI Volume", instance=instance)
new_size = self.connector.extend_volume(connection_info['data'])

View File

@ -135,3 +135,9 @@ class LibvirtNetVolumeDriver(libvirt_volume.LibvirtBaseVolumeDriver):
super(LibvirtNetVolumeDriver,
self).disconnect_volume(connection_info, instance)
self._delete_secret_by_name(connection_info)
def extend_volume(self, connection_info, instance, requested_size):
# There is nothing to do for network volumes. Cinder already extended
# the volume and there is no local block device which needs to be
# refreshed.
return requested_size

View File

@ -53,7 +53,7 @@ class LibvirtNVMEVolumeDriver(libvirt_volume.LibvirtVolumeDriver):
super(LibvirtNVMEVolumeDriver,
self).disconnect_volume(connection_info, instance)
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the volume."""
LOG.debug("calling os-brick to extend NVMe Volume", instance=instance)
new_size = self.connector.extend_volume(connection_info['data'])

View File

@ -62,7 +62,7 @@ class LibvirtScaleIOVolumeDriver(libvirt_volume.LibvirtBaseVolumeDriver):
super(LibvirtScaleIOVolumeDriver, self).disconnect_volume(
connection_info, instance)
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
LOG.debug("calling os-brick to extend ScaleIO Volume",
instance=instance)
new_size = self.connector.extend_volume(connection_info['data'])

View File

@ -46,7 +46,7 @@ class LibvirtStorPoolVolumeDriver(libvirt_volume.LibvirtVolumeDriver):
self.connector.disconnect_volume(connection_info['data'], None)
LOG.debug("Detached StorPool volume", instance=instance)
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the volume."""
LOG.debug("Extending StorPool volume %s",
connection_info['data']['volume'], instance=instance)

View File

@ -132,8 +132,19 @@ class LibvirtBaseVolumeDriver(object):
"""Disconnect the volume."""
pass
def extend_volume(self, connection_info, instance):
"""Extend the volume."""
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the volume.
:param: connection_info: connection information about the volume
that has been extended.
:param: instance: instance connected to the newly extended volume.
:param: requested_size: new extended size (in bytes) for the volume to
be extended.
:returns: the new size to use when resizing the disk in QEMU.
Note: the requested_size parameter is not used by all volume drivers
"""
raise NotImplementedError()

View File

@ -635,12 +635,13 @@ class PowerVMDriver(driver.ComputeDriver):
# Run the flow
tf_base.run(flow, instance=instance)
def extend_volume(self, connection_info, instance):
def extend_volume(self, connection_info, instance, requested_size):
"""Extend the disk attached to the instance.
:param dict connection_info: The connection for the extended volume.
:param nova.objects.instance.Instance instance:
The instance whose volume gets extended.
:param int requested_size: The requested new volume size in bytes.
:return: None
"""

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds support for extending RBD attached volumes using the libvirt network
volume driver.