Support online volume extension
Cinder supports extending volumes in use since API microversion 3.42. Check the supported microversion and use this feature if supported, instead of detaching the volume. Story: 2011229 Task: 51086 Change-Id: If67bdd2bc7a055d9b0c22772528f6ab08697f486
This commit is contained in:
parent
0b84e5be0d
commit
e26f779d92
heat
engine/resources/openstack/cinder
tests
releasenotes/notes
@ -27,6 +27,8 @@ from heat.engine import translation
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CINDER_MICROVERSIONS = (MICROVERSION_EXTEND_INUSE,) = ('3.42',)
|
||||||
|
|
||||||
|
|
||||||
class CinderVolume(vb.BaseVolume, sh.SchedulerHintsMixin):
|
class CinderVolume(vb.BaseVolume, sh.SchedulerHintsMixin):
|
||||||
"""A resource that implements Cinder volumes.
|
"""A resource that implements Cinder volumes.
|
||||||
@ -369,6 +371,9 @@ class CinderVolume(vb.BaseVolume, sh.SchedulerHintsMixin):
|
|||||||
def _ready_to_extend_volume(self):
|
def _ready_to_extend_volume(self):
|
||||||
vol = self.client().volumes.get(self.resource_id)
|
vol = self.client().volumes.get(self.resource_id)
|
||||||
expected_status = ('available',)
|
expected_status = ('available',)
|
||||||
|
if self.client_plugin().is_version_supported(
|
||||||
|
MICROVERSION_EXTEND_INUSE):
|
||||||
|
expected_status = ('available', 'in-use')
|
||||||
if vol.status in expected_status:
|
if vol.status in expected_status:
|
||||||
LOG.debug("Volume %s is ready to extend.", vol.id)
|
LOG.debug("Volume %s is ready to extend.", vol.id)
|
||||||
return True
|
return True
|
||||||
@ -381,6 +386,9 @@ class CinderVolume(vb.BaseVolume, sh.SchedulerHintsMixin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
expected_status = ('available',)
|
expected_status = ('available',)
|
||||||
|
if self.client_plugin().is_version_supported(
|
||||||
|
MICROVERSION_EXTEND_INUSE):
|
||||||
|
expected_status = ('available', 'in-use')
|
||||||
if vol.status not in expected_status:
|
if vol.status not in expected_status:
|
||||||
LOG.info("Resize failed: Volume %(vol)s "
|
LOG.info("Resize failed: Volume %(vol)s "
|
||||||
"is in %(status)s state.",
|
"is in %(status)s state.",
|
||||||
@ -490,6 +498,8 @@ class CinderVolume(vb.BaseVolume, sh.SchedulerHintsMixin):
|
|||||||
|
|
||||||
elif new_size > vol.size:
|
elif new_size > vol.size:
|
||||||
prg_resize = progress.VolumeResizeProgress(size=new_size)
|
prg_resize = progress.VolumeResizeProgress(size=new_size)
|
||||||
|
if not self.client_plugin().is_version_supported(
|
||||||
|
MICROVERSION_EXTEND_INUSE):
|
||||||
prg_detach, prg_attach = self._detach_attach_progress(vol)
|
prg_detach, prg_attach = self._detach_attach_progress(vol)
|
||||||
|
|
||||||
return prg_restore, prg_detach, prg_resize, prg_access, prg_attach
|
return prg_restore, prg_detach, prg_resize, prg_access, prg_attach
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
import copy
|
import copy
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from cinderclient import api_versions
|
|
||||||
from cinderclient import exceptions as cinder_exp
|
from cinderclient import exceptions as cinder_exp
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
@ -625,7 +624,7 @@ class VolumeTest(vt_base.VolumeTestCase):
|
|||||||
self.create_volume(self.t, stack, 'DataVolume', no_create=True)
|
self.create_volume(self.t, stack, 'DataVolume', no_create=True)
|
||||||
|
|
||||||
cinder.CinderClientPlugin._create.assert_called_once_with(
|
cinder.CinderClientPlugin._create.assert_called_once_with(
|
||||||
version=api_versions.MAX_VERSION)
|
version='3.0')
|
||||||
self.m_restore.assert_called_once_with('backup-123')
|
self.m_restore.assert_called_once_with('backup-123')
|
||||||
self.cinder_fc.volumes.get.assert_called_with('vol-123')
|
self.cinder_fc.volumes.get.assert_called_with('vol-123')
|
||||||
self.cinder_fc.volumes.update.assert_called_once_with(
|
self.cinder_fc.volumes.update.assert_called_once_with(
|
||||||
@ -654,7 +653,7 @@ class VolumeTest(vt_base.VolumeTestCase):
|
|||||||
str(ex))
|
str(ex))
|
||||||
|
|
||||||
cinder.CinderClientPlugin._create.assert_called_once_with(
|
cinder.CinderClientPlugin._create.assert_called_once_with(
|
||||||
version=api_versions.MAX_VERSION)
|
version='3.0')
|
||||||
self.m_restore.assert_called_once_with('backup-123')
|
self.m_restore.assert_called_once_with('backup-123')
|
||||||
self.cinder_fc.volumes.update.assert_called_once_with(
|
self.cinder_fc.volumes.update.assert_called_once_with(
|
||||||
fv.id, description=vol_name, name=vol_name)
|
fv.id, description=vol_name, name=vol_name)
|
||||||
|
@ -449,6 +449,36 @@ class CinderVolumeTest(vt_base.VolumeTestCase):
|
|||||||
self.stack_name = 'test_cvolume_extend_att_stack'
|
self.stack_name = 'test_cvolume_extend_att_stack'
|
||||||
self._update_if_attached(self.stack_name)
|
self._update_if_attached(self.stack_name)
|
||||||
|
|
||||||
|
def test_cinder_volume_extend_attached_live(self):
|
||||||
|
self.patchobject(cinder.CinderClientPlugin, 'get_max_microversion',
|
||||||
|
return_value='3.42')
|
||||||
|
self.stack_name = 'test_cvolume_extend_live_att_stack'
|
||||||
|
|
||||||
|
fv = vt_base.FakeVolume('available',
|
||||||
|
size=1, attachments=[])
|
||||||
|
self._mock_create_volume(vt_base.FakeVolume('creating'),
|
||||||
|
self.stack_name,
|
||||||
|
extra_get_mocks=[
|
||||||
|
fv, vt_base.FakeVolume('extending'),
|
||||||
|
vt_base.FakeVolume('extending'),
|
||||||
|
vt_base.FakeVolume('in-use')])
|
||||||
|
|
||||||
|
stack = utils.parse_stack(self.t, stack_name=self.stack_name)
|
||||||
|
|
||||||
|
rsrc = self.create_volume(self.t, stack, 'volume')
|
||||||
|
|
||||||
|
props = copy.deepcopy(rsrc.properties.data)
|
||||||
|
props['size'] = 2
|
||||||
|
after = rsrc_defn.ResourceDefinition(rsrc.name, rsrc.type(), props)
|
||||||
|
|
||||||
|
update_task = scheduler.TaskRunner(rsrc.update, after)
|
||||||
|
self.assertIsNone(update_task())
|
||||||
|
|
||||||
|
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
|
||||||
|
self.cinder_fc.volumes.extend.assert_called_once_with(fv.id, 2)
|
||||||
|
self.fc.volumes.get_server_volume.assert_not_called()
|
||||||
|
self.fc.volumes.delete_server_volume.assert_not_called()
|
||||||
|
|
||||||
def test_cinder_volume_extend_created_from_backup_with_same_size(self):
|
def test_cinder_volume_extend_created_from_backup_with_same_size(self):
|
||||||
self.stack_name = 'test_cvolume_extend_snapsht_stack'
|
self.stack_name = 'test_cvolume_extend_snapsht_stack'
|
||||||
|
|
||||||
@ -1386,6 +1416,11 @@ class CinderVolumeTest(vt_base.VolumeTestCase):
|
|||||||
self._mock_create_volume(vt_base.FakeVolume('creating'),
|
self._mock_create_volume(vt_base.FakeVolume('creating'),
|
||||||
self.stack_name,
|
self.stack_name,
|
||||||
extra_get_mocks=[
|
extra_get_mocks=[
|
||||||
|
vt_base.FakeVolume('extending'),
|
||||||
|
vt_base.FakeVolume('reserved'),
|
||||||
|
vt_base.FakeVolume('in-use'),
|
||||||
|
vt_base.FakeVolume('available'),
|
||||||
|
vt_base.FakeVolume('creating'),
|
||||||
vt_base.FakeVolume('extending'),
|
vt_base.FakeVolume('extending'),
|
||||||
vt_base.FakeVolume('reserved'),
|
vt_base.FakeVolume('reserved'),
|
||||||
vt_base.FakeVolume('in-use'),
|
vt_base.FakeVolume('in-use'),
|
||||||
@ -1401,6 +1436,14 @@ class CinderVolumeTest(vt_base.VolumeTestCase):
|
|||||||
self.assertEqual(False, rsrc._ready_to_extend_volume())
|
self.assertEqual(False, rsrc._ready_to_extend_volume())
|
||||||
self.assertEqual(True, rsrc._ready_to_extend_volume())
|
self.assertEqual(True, rsrc._ready_to_extend_volume())
|
||||||
|
|
||||||
|
self.patchobject(cinder.CinderClientPlugin, 'get_max_microversion',
|
||||||
|
return_value='3.42')
|
||||||
|
self.assertEqual(False, rsrc._ready_to_extend_volume())
|
||||||
|
self.assertEqual(False, rsrc._ready_to_extend_volume())
|
||||||
|
self.assertEqual(False, rsrc._ready_to_extend_volume())
|
||||||
|
self.assertEqual(True, rsrc._ready_to_extend_volume())
|
||||||
|
self.assertEqual(True, rsrc._ready_to_extend_volume())
|
||||||
|
|
||||||
def test_try_detach_volume_if_server_was_temporarily_in_error(self):
|
def test_try_detach_volume_if_server_was_temporarily_in_error(self):
|
||||||
self.stack_name = 'test_cvolume_detach_server_in_error_stack'
|
self.stack_name = 'test_cvolume_detach_server_in_error_stack'
|
||||||
fva = vt_base.FakeVolume('in-use')
|
fva = vt_base.FakeVolume('in-use')
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from cinderclient import api_versions
|
|
||||||
from cinderclient.v3 import client as cinderclient
|
from cinderclient.v3 import client as cinderclient
|
||||||
|
|
||||||
from heat.engine.clients.os import cinder
|
from heat.engine.clients.os import cinder
|
||||||
@ -34,7 +33,7 @@ class VolumeTestCase(common.HeatTestCase):
|
|||||||
self.cinder_fc = cinderclient.Client('username', 'password')
|
self.cinder_fc = cinderclient.Client('username', 'password')
|
||||||
self.cinder_fc.volume_api_version = 3
|
self.cinder_fc.volume_api_version = 3
|
||||||
self.patchobject(cinder.CinderClientPlugin, 'get_max_microversion',
|
self.patchobject(cinder.CinderClientPlugin, 'get_max_microversion',
|
||||||
return_value=api_versions.MAX_VERSION)
|
return_value='3.0')
|
||||||
self.patchobject(cinder.CinderClientPlugin, '_create',
|
self.patchobject(cinder.CinderClientPlugin, '_create',
|
||||||
return_value=self.cinder_fc)
|
return_value=self.cinder_fc)
|
||||||
self.patchobject(nova.NovaClientPlugin, 'client',
|
self.patchobject(nova.NovaClientPlugin, 'client',
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The ``OS::Cinder::Volume`` resource type now supports extending volumes
|
||||||
|
in use. Note that this requires that Cinder supports API microversion
|
||||||
|
3.42 or later.
|
Loading…
Reference in New Issue
Block a user