RBD: Implement volume extension
The RBD connector doesn't implement the extend_volume method and raise NotImplementedError when called. This patch implements the method for all types of connected volumes: - File descriptor - OpenStack locally attached - Non OpenStack locally attached Closes-Bug: #1884554 Related-Bug: #1883720 Change-Id: I4bbf3778210d05c6934289116a85a8f344bed818
This commit is contained in:
parent
7d995b6cf6
commit
29752fb54b
|
@ -384,5 +384,45 @@ class RBDConnector(base.BaseLinuxConnector):
|
|||
return self._check_valid_device(path)
|
||||
|
||||
def extend_volume(self, connection_properties):
|
||||
# TODO(walter-boring): is this possible?
|
||||
raise NotImplementedError
|
||||
"""Refresh local volume view and return current size in bytes."""
|
||||
# Nothing to do, RBD attached volumes are automatically refreshed, but
|
||||
# we need to return the new size for compatibility
|
||||
do_local_attach = connection_properties.get('do_local_attach',
|
||||
self.do_local_attach)
|
||||
|
||||
if not do_local_attach:
|
||||
handle = self._get_rbd_handle(connection_properties)
|
||||
try:
|
||||
# Handles should return absolute position on seek, but the RBD
|
||||
# wrapper doesn't, so we need to call tell afterwards
|
||||
handle.seek(0, 2)
|
||||
return handle.tell()
|
||||
finally:
|
||||
fileutils.delete_if_exists(handle.rbd_conf)
|
||||
handle.close()
|
||||
|
||||
# Create config file when we do the attach on the host and not the VM
|
||||
conf = self.create_non_openstack_config(connection_properties)
|
||||
|
||||
try:
|
||||
device_path = self._find_root_device(connection_properties, conf)
|
||||
finally:
|
||||
# If we have generated the config file we need to remove it
|
||||
if conf:
|
||||
try:
|
||||
rbd_privsep.delete_if_exists(conf)
|
||||
except Exception as exc:
|
||||
LOG.warning(_('Could not remove config file %(filename)s: '
|
||||
'%(exc)s'), {'filename': conf, 'exc': exc})
|
||||
|
||||
if not device_path:
|
||||
msg = _('Cannot extend non mapped device.')
|
||||
raise exception.BrickException(msg=msg)
|
||||
|
||||
device_name = os.path.basename(device_path) # ie: rbd0
|
||||
device_number = device_name[3:] # ie: 0
|
||||
# Get size from /sys/devices/rbd/0/size instead of
|
||||
# /sys/class/block/rbd0/size because the latter isn't updated
|
||||
with open('/sys/devices/rbd/' + device_number + '/size') as f:
|
||||
size_bytes = f.read().strip()
|
||||
return int(size_bytes)
|
||||
|
|
|
@ -419,12 +419,72 @@ class RBDConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_execute.called_once_with(*show_cmd, root_helper=None,
|
||||
run_as_root=True)
|
||||
|
||||
def test_extend_volume(self):
|
||||
rbd_connector = rbd.RBDConnector(None)
|
||||
self.assertRaises(NotImplementedError,
|
||||
rbd_connector.extend_volume,
|
||||
@mock.patch('oslo_utils.fileutils.delete_if_exists')
|
||||
@mock.patch.object(rbd.RBDConnector, '_get_rbd_handle')
|
||||
def test_extend_volume_handle(self, mock_handle, mock_delete):
|
||||
connector = rbd.RBDConnector(None)
|
||||
res = connector.extend_volume(self.connection_properties)
|
||||
|
||||
mock_handle.assert_called_once_with(self.connection_properties)
|
||||
mock_handle.return_value.seek.assert_called_once_with(0, 2)
|
||||
mock_handle.return_value.tell.assert_called_once_with()
|
||||
self.assertIs(mock_handle().tell(), res)
|
||||
mock_delete.assert_called_once_with(mock_handle().rbd_conf)
|
||||
mock_handle.return_value.close.assert_called_once_with()
|
||||
|
||||
@mock.patch('oslo_utils.fileutils.delete_if_exists')
|
||||
@mock.patch.object(rbd.RBDConnector, '_get_rbd_handle')
|
||||
def test_extend_volume_handle_fail(self, mock_handle, mock_delete):
|
||||
mock_handle.return_value.seek.side_effect = ValueError
|
||||
connector = rbd.RBDConnector(None)
|
||||
|
||||
self.assertRaises(ValueError, connector.extend_volume,
|
||||
self.connection_properties)
|
||||
|
||||
mock_handle.assert_called_once_with(self.connection_properties)
|
||||
mock_handle.return_value.seek.assert_called_once_with(0, 2)
|
||||
mock_handle().tell.assert_not_called()
|
||||
mock_delete.assert_called_once_with(mock_handle.return_value.rbd_conf)
|
||||
mock_handle.return_value.close.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(rbd, 'open')
|
||||
@mock.patch('os_brick.privileged.rbd.delete_if_exists')
|
||||
@mock.patch.object(rbd.RBDConnector, '_find_root_device')
|
||||
@mock.patch.object(rbd.RBDConnector, 'create_non_openstack_config')
|
||||
def test_extend_volume_block(self, mock_config, mock_find, mock_delete,
|
||||
mock_open):
|
||||
mock_find.return_value = '/dev/rbd1'
|
||||
file_handle = mock_open.return_value.__enter__.return_value
|
||||
file_handle.read.return_value = '123456789'
|
||||
connector = rbd.RBDConnector(None, do_local_attach=True)
|
||||
|
||||
res = connector.extend_volume(self.connection_properties)
|
||||
|
||||
mock_config.assert_called_once_with(self.connection_properties)
|
||||
mock_find.assert_called_once_with(self.connection_properties,
|
||||
mock_config.return_value)
|
||||
mock_delete.assert_called_once_with(mock_config.return_value)
|
||||
mock_open.assert_called_once_with('/sys/devices/rbd/1/size')
|
||||
file_handle.read.assert_called_once_with()
|
||||
self.assertEqual(123456789, res)
|
||||
|
||||
@mock.patch.object(rbd, 'open')
|
||||
@mock.patch('os_brick.privileged.rbd.delete_if_exists')
|
||||
@mock.patch.object(rbd.RBDConnector, '_find_root_device')
|
||||
@mock.patch.object(rbd.RBDConnector, 'create_non_openstack_config')
|
||||
def test_extend_volume_no_device_local(self, mock_config, mock_find,
|
||||
mock_delete, mock_open):
|
||||
mock_find.return_value = None
|
||||
connector = rbd.RBDConnector(None, do_local_attach=True)
|
||||
self.assertRaises(exception.BrickException, connector.extend_volume,
|
||||
self.connection_properties)
|
||||
|
||||
mock_config.assert_called_once_with(self.connection_properties)
|
||||
mock_find.assert_called_once_with(self.connection_properties,
|
||||
mock_config.return_value)
|
||||
mock_delete.assert_called_once_with(mock_config.return_value)
|
||||
mock_open.assert_not_called()
|
||||
|
||||
def test__get_rbd_args(self):
|
||||
res = rbd.RBDConnector._get_rbd_args(self.connection_properties, None)
|
||||
expected = ['--id', self.user,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
Implement the extend_volume method for the RBD connector.
|
||||
(Bug #1884554).
|
Loading…
Reference in New Issue