Implement extend_share() method in Generic driver
- Add implementation for extend_share() method - Add appropriate unit tests Partially implements bp share-extend-api Change-Id: I81c4278a8caa75a2b475eaf5538a5ae6d3ed18d5
This commit is contained in:
parent
dfed9c66a8
commit
72728025a9
@ -83,6 +83,9 @@ Known restrictions
|
|||||||
- Juno version does not use security services data provided with share-network.
|
- Juno version does not use security services data provided with share-network.
|
||||||
These data will be just ignored.
|
These data will be just ignored.
|
||||||
|
|
||||||
|
- Liberty version adds a share extend capability. Share access will be briefly
|
||||||
|
interrupted during an extend operation.
|
||||||
|
|
||||||
The :mod:`manila.share.drivers.generic` Module
|
The :mod:`manila.share.drivers.generic` Module
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ share_opts = [
|
|||||||
cfg.IntOpt('max_time_to_create_volume',
|
cfg.IntOpt('max_time_to_create_volume',
|
||||||
default=180,
|
default=180,
|
||||||
help="Maximum time to wait for creating cinder volume."),
|
help="Maximum time to wait for creating cinder volume."),
|
||||||
|
cfg.IntOpt('max_time_to_extend_volume',
|
||||||
|
default=180,
|
||||||
|
help="Maximum time to wait for extending cinder volume."),
|
||||||
cfg.IntOpt('max_time_to_attach',
|
cfg.IntOpt('max_time_to_attach',
|
||||||
default=120,
|
default=120,
|
||||||
help="Maximum time to wait for attaching cinder volume."),
|
help="Maximum time to wait for attaching cinder volume."),
|
||||||
@ -466,19 +469,29 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
|||||||
self.private_storage.update(
|
self.private_storage.update(
|
||||||
share['id'], {'volume_id': volume['id']})
|
share['id'], {'volume_id': volume['id']})
|
||||||
|
|
||||||
|
msg_error = _('Failed to create volume')
|
||||||
|
msg_timeout = (
|
||||||
|
_('Volume has not been created in %ss. Giving up') %
|
||||||
|
self.configuration.max_time_to_create_volume
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._wait_for_available_volume(
|
||||||
|
volume, self.configuration.max_time_to_create_volume,
|
||||||
|
msg_error=msg_error, msg_timeout=msg_timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
def _wait_for_available_volume(self, volume, timeout,
|
||||||
|
msg_error, msg_timeout):
|
||||||
t = time.time()
|
t = time.time()
|
||||||
while time.time() - t < self.configuration.max_time_to_create_volume:
|
while time.time() - t < timeout:
|
||||||
if volume['status'] == const.STATUS_AVAILABLE:
|
if volume['status'] == const.STATUS_AVAILABLE:
|
||||||
break
|
break
|
||||||
if volume['status'] == const.STATUS_ERROR:
|
if volume['status'] == const.STATUS_ERROR:
|
||||||
raise exception.ManilaException(_('Failed to create volume'))
|
raise exception.ManilaException(msg_error)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
volume = self.volume_api.get(context, volume['id'])
|
volume = self.volume_api.get(self.admin_context, volume['id'])
|
||||||
else:
|
else:
|
||||||
raise exception.ManilaException(
|
raise exception.ManilaException(msg_timeout)
|
||||||
_('Volume have not been created '
|
|
||||||
'in %ss. Giving up') %
|
|
||||||
self.configuration.max_time_to_create_volume)
|
|
||||||
|
|
||||||
return volume
|
return volume
|
||||||
|
|
||||||
@ -528,6 +541,44 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
|||||||
share['name'])
|
share['name'])
|
||||||
return location
|
return location
|
||||||
|
|
||||||
|
@ensure_server
|
||||||
|
def extend_share(self, share, new_size, share_server=None):
|
||||||
|
server_details = share_server['backend_details']
|
||||||
|
|
||||||
|
self._unmount_device(share, server_details)
|
||||||
|
self._detach_volume(self.admin_context, share, server_details)
|
||||||
|
|
||||||
|
volume = self._get_volume(self.admin_context, share['id'])
|
||||||
|
volume = self._extend_volume(self.admin_context, volume, new_size)
|
||||||
|
|
||||||
|
volume = self._attach_volume(
|
||||||
|
self.admin_context,
|
||||||
|
share,
|
||||||
|
server_details['instance_id'],
|
||||||
|
volume)
|
||||||
|
self._resize_filesystem(server_details, volume)
|
||||||
|
self._mount_device(share, server_details, volume)
|
||||||
|
|
||||||
|
def _extend_volume(self, context, volume, new_size):
|
||||||
|
self.volume_api.extend(context, volume['id'], new_size)
|
||||||
|
|
||||||
|
msg_error = _('Failed to extend volume %s') % volume['id']
|
||||||
|
msg_timeout = (
|
||||||
|
_('Volume has not been extended in %ss. Giving up') %
|
||||||
|
self.configuration.max_time_to_extend_volume
|
||||||
|
)
|
||||||
|
return self._wait_for_available_volume(
|
||||||
|
volume, self.configuration.max_time_to_extend_volume,
|
||||||
|
msg_error=msg_error, msg_timeout=msg_timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
def _resize_filesystem(self, server_details, volume):
|
||||||
|
"""Resize filesystem of provided volume."""
|
||||||
|
check_command = ['sudo', 'fsck', '-pf', volume['mountpoint']]
|
||||||
|
self._ssh_exec(server_details, check_command)
|
||||||
|
command = ['sudo', 'resize2fs', volume['mountpoint']]
|
||||||
|
self._ssh_exec(server_details, command)
|
||||||
|
|
||||||
def _is_share_server_active(self, context, share_server):
|
def _is_share_server_active(self, context, share_server):
|
||||||
"""Check if the share server is active."""
|
"""Check if the share server is active."""
|
||||||
has_active_share_server = (
|
has_active_share_server = (
|
||||||
|
@ -61,6 +61,9 @@ class API(object):
|
|||||||
def create(self, *args, **kwargs):
|
def create(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def extend(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
def get_all(self, search_opts):
|
def get_all(self, search_opts):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -642,6 +642,31 @@ class GenericShareDriverTestCase(test.TestCase):
|
|||||||
self._context,
|
self._context,
|
||||||
self.share)
|
self.share)
|
||||||
|
|
||||||
|
def test_wait_for_available_volume(self):
|
||||||
|
fake_volume = {'status': 'creating', 'id': 'fake'}
|
||||||
|
fake_available_volume = {'status': 'available', 'id': 'fake'}
|
||||||
|
self.mock_object(self._driver.volume_api, 'get',
|
||||||
|
mock.Mock(return_value=fake_available_volume))
|
||||||
|
|
||||||
|
actual_result = self._driver._wait_for_available_volume(
|
||||||
|
fake_volume, 5, "error", "timeout")
|
||||||
|
|
||||||
|
self.assertEqual(fake_available_volume, actual_result)
|
||||||
|
self._driver.volume_api.get.assert_called_once_with(
|
||||||
|
mock.ANY, fake_volume['id'])
|
||||||
|
|
||||||
|
@ddt.data(mock.Mock(return_value={'status': 'creating', 'id': 'fake'}),
|
||||||
|
mock.Mock(return_value={'status': 'error', 'id': 'fake'}))
|
||||||
|
def test_wait_for_available_volume_invalid(self, volume_get_mock):
|
||||||
|
fake_volume = {'status': 'creating', 'id': 'fake'}
|
||||||
|
self.mock_object(self._driver.volume_api, 'get', volume_get_mock)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exception.ManilaException,
|
||||||
|
self._driver._wait_for_available_volume,
|
||||||
|
fake_volume, 1, "error", "timeout"
|
||||||
|
)
|
||||||
|
|
||||||
def test_deallocate_container(self):
|
def test_deallocate_container(self):
|
||||||
fake_vol = fake_volume.FakeVolume()
|
fake_vol = fake_volume.FakeVolume()
|
||||||
self.mock_object(self._driver, '_get_volume',
|
self.mock_object(self._driver, '_get_volume',
|
||||||
@ -1269,6 +1294,72 @@ class GenericShareDriverTestCase(test.TestCase):
|
|||||||
self._driver._get_mounted_share_size,
|
self._driver._get_mounted_share_size,
|
||||||
'/fake/path', {})
|
'/fake/path', {})
|
||||||
|
|
||||||
|
def test_extend_share(self):
|
||||||
|
fake_volume = "fake"
|
||||||
|
fake_share = {'id': 'fake'}
|
||||||
|
new_size = 123
|
||||||
|
srv_details = self.server['backend_details']
|
||||||
|
self.mock_object(
|
||||||
|
self._driver.service_instance_manager,
|
||||||
|
'get_common_server',
|
||||||
|
mock.Mock(return_value=self.server)
|
||||||
|
)
|
||||||
|
self.mock_object(self._driver, '_unmount_device')
|
||||||
|
self.mock_object(self._driver, '_detach_volume')
|
||||||
|
self.mock_object(self._driver, '_extend_volume')
|
||||||
|
self.mock_object(self._driver, '_attach_volume')
|
||||||
|
self.mock_object(self._driver, '_mount_device')
|
||||||
|
self.mock_object(self._driver, '_resize_filesystem')
|
||||||
|
self.mock_object(
|
||||||
|
self._driver, '_get_volume',
|
||||||
|
mock.Mock(return_value=fake_volume)
|
||||||
|
)
|
||||||
|
CONF.set_default('driver_handles_share_servers', False)
|
||||||
|
|
||||||
|
self._driver.extend_share(fake_share, new_size)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
self._driver.service_instance_manager.get_common_server.called)
|
||||||
|
self._driver._unmount_device.assert_called_once_with(
|
||||||
|
fake_share, srv_details)
|
||||||
|
self._driver._detach_volume.assert_called_once_with(
|
||||||
|
mock.ANY, fake_share, srv_details)
|
||||||
|
self._driver._get_volume.assert_called_once_with(
|
||||||
|
mock.ANY, fake_share['id'])
|
||||||
|
self._driver._extend_volume.assert_called_once_with(
|
||||||
|
mock.ANY, fake_volume, new_size)
|
||||||
|
self._driver._attach_volume.assert_called_once_with(
|
||||||
|
mock.ANY, fake_share, srv_details['instance_id'], mock.ANY)
|
||||||
|
self.assertTrue(self._driver._resize_filesystem.called)
|
||||||
|
|
||||||
|
def test_extend_volume(self):
|
||||||
|
fake_volume = {'id': 'fake'}
|
||||||
|
new_size = 123
|
||||||
|
self.mock_object(self._driver.volume_api, 'extend')
|
||||||
|
self.mock_object(self._driver, '_wait_for_available_volume')
|
||||||
|
|
||||||
|
self._driver._extend_volume(self._context, fake_volume, new_size)
|
||||||
|
|
||||||
|
self._driver.volume_api.extend.assert_called_once_with(
|
||||||
|
self._context, fake_volume['id'], new_size
|
||||||
|
)
|
||||||
|
self._driver._wait_for_available_volume.assert_called_once_with(
|
||||||
|
fake_volume, mock.ANY, msg_timeout=mock.ANY, msg_error=mock.ANY
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_resize_filesystem(self):
|
||||||
|
fake_server_details = {'fake': 'fake'}
|
||||||
|
fake_volume = {'mountpoint': '/dev/fake'}
|
||||||
|
self.mock_object(self._driver, '_ssh_exec')
|
||||||
|
|
||||||
|
self._driver._resize_filesystem(fake_server_details, fake_volume)
|
||||||
|
|
||||||
|
self._driver._ssh_exec.assert_any_call(
|
||||||
|
fake_server_details, ['sudo', 'fsck', '-pf', '/dev/fake'])
|
||||||
|
self._driver._ssh_exec.assert_any_call(
|
||||||
|
fake_server_details, ['sudo', 'resize2fs', '/dev/fake'])
|
||||||
|
self.assertEqual(2, self._driver._ssh_exec.call_count)
|
||||||
|
|
||||||
|
|
||||||
@generic.ensure_server
|
@generic.ensure_server
|
||||||
def fake(driver_instance, context, share_server=None):
|
def fake(driver_instance, context, share_server=None):
|
||||||
|
@ -312,6 +312,10 @@ class API(base.Base):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise exception.ManilaException(e.message)
|
raise exception.ManilaException(e.message)
|
||||||
|
|
||||||
|
@translate_volume_exception
|
||||||
|
def extend(self, context, volume_id, new_size):
|
||||||
|
cinderclient(context).volumes.extend(volume_id, new_size)
|
||||||
|
|
||||||
@translate_volume_exception
|
@translate_volume_exception
|
||||||
def delete(self, context, volume_id):
|
def delete(self, context, volume_id):
|
||||||
cinderclient(context).volumes.delete(volume_id)
|
cinderclient(context).volumes.delete(volume_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user