diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 10a38511dad2..8bb5e2285566 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -52,6 +52,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 @@ -8048,7 +8049,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', diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index ca0b9ca4c7e1..8544b9d666af 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -2965,6 +2965,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', @@ -2982,13 +2983,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() diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index f9a1ac31731a..85292a1b50b0 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -7997,9 +7997,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) @@ -8012,7 +8014,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) @@ -8031,7 +8033,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) @@ -8046,7 +8048,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) @@ -8071,7 +8073,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') diff --git a/nova/tests/unit/virt/libvirt/volume/test_fibrechannel.py b/nova/tests/unit/virt/libvirt/volume/test_fibrechannel.py index 4289ac5aefb6..89a59f2f1abc 100644 --- a/nova/tests/unit/virt/libvirt/volume/test_fibrechannel.py +++ b/nova/tests/unit/virt/libvirt/volume/test_fibrechannel.py @@ -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']) diff --git a/nova/tests/unit/virt/libvirt/volume/test_iscsi.py b/nova/tests/unit/virt/libvirt/volume/test_iscsi.py index 2991ef1ac9b1..f8a64abea5f6 100644 --- a/nova/tests/unit/virt/libvirt/volume/test_iscsi.py +++ b/nova/tests/unit/virt/libvirt/volume/test_iscsi.py @@ -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']) diff --git a/nova/tests/unit/virt/libvirt/volume/test_net.py b/nova/tests/unit/virt/libvirt/volume/test_net.py index 640c1709ded8..bea90a24dcfd 100644 --- a/nova/tests/unit/virt/libvirt/volume/test_net.py +++ b/nova/tests/unit/virt/libvirt/volume/test_net.py @@ -240,3 +240,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) diff --git a/nova/tests/unit/virt/libvirt/volume/test_scaleio.py b/nova/tests/unit/virt/libvirt/volume/test_scaleio.py index 1c3aa23cdce5..bf42779b186d 100644 --- a/nova/tests/unit/virt/libvirt/volume/test_scaleio.py +++ b/nova/tests/unit/virt/libvirt/volume/test_scaleio.py @@ -71,4 +71,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)) diff --git a/nova/tests/unit/virt/libvirt/volume/test_storpool.py b/nova/tests/unit/virt/libvirt/volume/test_storpool.py index 96dd4894e3b2..d9df648f52aa 100644 --- a/nova/tests/unit/virt/libvirt/volume/test_storpool.py +++ b/nova/tests/unit/virt/libvirt/volume/test_storpool.py @@ -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) diff --git a/nova/tests/unit/virt/powervm/test_driver.py b/nova/tests/unit/virt/powervm/test_driver.py index 9f3cd9d8c736..b307c3b76714 100644 --- a/nova/tests/unit/virt/powervm/test_driver.py +++ b/nova/tests/unit/virt/powervm/test_driver.py @@ -587,7 +587,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): diff --git a/nova/virt/driver.py b/nova/virt/driver.py index ff4748cc72c0..819d7dfde8d5 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -522,13 +522,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 """ diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 26a06eabdb30..488817edcc7e 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -326,7 +326,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 diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 8668087b65bc..158b64751091 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -1266,9 +1266,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 @@ -1618,9 +1619,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() @@ -1631,7 +1633,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) diff --git a/nova/virt/libvirt/volume/fibrechannel.py b/nova/virt/libvirt/volume/fibrechannel.py index 8a304cd79d06..7a0cf5e843d4 100644 --- a/nova/virt/libvirt/volume/fibrechannel.py +++ b/nova/virt/libvirt/volume/fibrechannel.py @@ -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']) diff --git a/nova/virt/libvirt/volume/iscsi.py b/nova/virt/libvirt/volume/iscsi.py index 6a6d6f100615..9eb08b0a9063 100644 --- a/nova/virt/libvirt/volume/iscsi.py +++ b/nova/virt/libvirt/volume/iscsi.py @@ -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']) diff --git a/nova/virt/libvirt/volume/net.py b/nova/virt/libvirt/volume/net.py index 3b432dea54c1..8faa61a67857 100644 --- a/nova/virt/libvirt/volume/net.py +++ b/nova/virt/libvirt/volume/net.py @@ -132,3 +132,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 diff --git a/nova/virt/libvirt/volume/nvme.py b/nova/virt/libvirt/volume/nvme.py index 8d20ab10d5ec..4b4e701ed9aa 100644 --- a/nova/virt/libvirt/volume/nvme.py +++ b/nova/virt/libvirt/volume/nvme.py @@ -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']) diff --git a/nova/virt/libvirt/volume/scaleio.py b/nova/virt/libvirt/volume/scaleio.py index 4195bc1096e2..2fa2ea3fcc54 100644 --- a/nova/virt/libvirt/volume/scaleio.py +++ b/nova/virt/libvirt/volume/scaleio.py @@ -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']) diff --git a/nova/virt/libvirt/volume/storpool.py b/nova/virt/libvirt/volume/storpool.py index a99589a6ca58..cc950186cab3 100644 --- a/nova/virt/libvirt/volume/storpool.py +++ b/nova/virt/libvirt/volume/storpool.py @@ -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) diff --git a/nova/virt/libvirt/volume/volume.py b/nova/virt/libvirt/volume/volume.py index aaffaa074c75..114887445efd 100644 --- a/nova/virt/libvirt/volume/volume.py +++ b/nova/virt/libvirt/volume/volume.py @@ -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() diff --git a/nova/virt/powervm/driver.py b/nova/virt/powervm/driver.py index a153257ee721..d8f62954eaf6 100644 --- a/nova/virt/powervm/driver.py +++ b/nova/virt/powervm/driver.py @@ -634,12 +634,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 """ diff --git a/releasenotes/notes/bp-extend-in-use-rbd-volumes-8f334ce2a06ee247.yaml b/releasenotes/notes/bp-extend-in-use-rbd-volumes-8f334ce2a06ee247.yaml new file mode 100644 index 000000000000..4e81eb66656b --- /dev/null +++ b/releasenotes/notes/bp-extend-in-use-rbd-volumes-8f334ce2a06ee247.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for extending RBD attached volumes using the libvirt network + volume driver.