Merge "Remove TaskRunner from Volume resources"
This commit is contained in:
commit
6cf94a3ece
heat
engine
tests
@ -22,6 +22,7 @@ from heat.common.i18n import _
|
|||||||
from heat.common.i18n import _LI
|
from heat.common.i18n import _LI
|
||||||
from heat.engine.clients import client_plugin
|
from heat.engine.clients import client_plugin
|
||||||
from heat.engine import constraints
|
from heat.engine import constraints
|
||||||
|
from heat.engine import resource
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -127,6 +128,87 @@ class CinderClientPlugin(client_plugin.ClientPlugin):
|
|||||||
return (isinstance(ex, exceptions.ClientException) and
|
return (isinstance(ex, exceptions.ClientException) and
|
||||||
ex.code == 409)
|
ex.code == 409)
|
||||||
|
|
||||||
|
def check_detach_volume_complete(self, vol_id):
|
||||||
|
try:
|
||||||
|
vol = self.client().volumes.get(vol_id)
|
||||||
|
except Exception as ex:
|
||||||
|
self.ignore_not_found(ex)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if vol.status in ('in-use', 'detaching'):
|
||||||
|
LOG.debug('%s - volume still in use' % vol_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
LOG.debug('Volume %(id)s - status: %(status)s' % {
|
||||||
|
'id': vol.id, 'status': vol.status})
|
||||||
|
|
||||||
|
if vol.status not in ('available', 'deleting'):
|
||||||
|
LOG.debug("Detachment failed - volume %(vol)s "
|
||||||
|
"is in %(status)s status" % {"vol": vol.id,
|
||||||
|
"status": vol.status})
|
||||||
|
raise resource.ResourceUnknownStatus(
|
||||||
|
resource_status=vol.status,
|
||||||
|
result=_('Volume detachment failed'))
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_attach_volume_complete(self, vol_id):
|
||||||
|
vol = self.client().volumes.get(vol_id)
|
||||||
|
if vol.status in ('available', 'attaching'):
|
||||||
|
LOG.debug("Volume %(id)s is being attached - "
|
||||||
|
"volume status: %(status)s" % {'id': vol_id,
|
||||||
|
'status': vol.status})
|
||||||
|
return False
|
||||||
|
|
||||||
|
if vol.status != 'in-use':
|
||||||
|
LOG.debug("Attachment failed - volume %(vol)s is "
|
||||||
|
"in %(status)s status" % {"vol": vol_id,
|
||||||
|
"status": vol.status})
|
||||||
|
raise resource.ResourceUnknownStatus(
|
||||||
|
resource_status=vol.status,
|
||||||
|
result=_('Volume attachment failed'))
|
||||||
|
|
||||||
|
LOG.info(_LI('Attaching volume %(id)s complete'), {'id': vol_id})
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE(pshchelo): these Volume*Progress classes are simple key-value storages
|
||||||
|
# meant to be passed between handle_<action> and check_<action>_complete,
|
||||||
|
# being mutated during subsequent check_<action>_complete calls.
|
||||||
|
class VolumeDetachProgress(object):
|
||||||
|
def __init__(self, srv_id, vol_id, attach_id, val=False):
|
||||||
|
self.called = val
|
||||||
|
self.cinder_complete = val
|
||||||
|
self.nova_complete = val
|
||||||
|
self.srv_id = srv_id
|
||||||
|
self.vol_id = vol_id
|
||||||
|
self.attach_id = attach_id
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeAttachProgress(object):
|
||||||
|
def __init__(self, srv_id, vol_id, device, val=False):
|
||||||
|
self.called = val
|
||||||
|
self.complete = val
|
||||||
|
self.srv_id = srv_id
|
||||||
|
self.vol_id = vol_id
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeDeleteProgress(object):
|
||||||
|
def __init__(self, val=False):
|
||||||
|
self.backup = {'called': val,
|
||||||
|
'complete': val}
|
||||||
|
self.delete = {'called': val,
|
||||||
|
'complete': val}
|
||||||
|
self.backup_id = None
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeResizeProgress(object):
|
||||||
|
def __init__(self, val=False, size=None):
|
||||||
|
self.called = val
|
||||||
|
self.complete = val
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
|
||||||
class VolumeConstraint(constraints.BaseCustomConstraint):
|
class VolumeConstraint(constraints.BaseCustomConstraint):
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ from six.moves.urllib import parse as urlparse
|
|||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
|
from heat.common.i18n import _LI
|
||||||
from heat.common.i18n import _LW
|
from heat.common.i18n import _LW
|
||||||
from heat.engine.clients import client_plugin
|
from heat.engine.clients import client_plugin
|
||||||
from heat.engine import constraints
|
from heat.engine import constraints
|
||||||
@ -495,6 +496,55 @@ echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
|
|||||||
|
|
||||||
return net_id
|
return net_id
|
||||||
|
|
||||||
|
def attach_volume(self, server_id, volume_id, device):
|
||||||
|
try:
|
||||||
|
va = self.client().volumes.create_server_volume(
|
||||||
|
server_id=server_id,
|
||||||
|
volume_id=volume_id,
|
||||||
|
device=device)
|
||||||
|
except Exception as ex:
|
||||||
|
if self.is_client_exception(ex):
|
||||||
|
raise exception.Error(_(
|
||||||
|
"Failed to attach volume %(vol)s to server %(srv)s "
|
||||||
|
"- %(err)s") % {'vol': volume_id,
|
||||||
|
'srv': server_id,
|
||||||
|
'err': ex})
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return va.id
|
||||||
|
|
||||||
|
def detach_volume(self, server_id, attach_id):
|
||||||
|
# detach the volume using volume_attachment
|
||||||
|
try:
|
||||||
|
self.client().volumes.delete_server_volume(server_id, attach_id)
|
||||||
|
except Exception as ex:
|
||||||
|
if not (self.is_not_found(ex)
|
||||||
|
or self.is_bad_request(ex)):
|
||||||
|
raise exception.Error(
|
||||||
|
_("Could not detach attachment %(att)s "
|
||||||
|
"from server %(srv)s.") % {'srv': server_id,
|
||||||
|
'att': attach_id})
|
||||||
|
|
||||||
|
def check_detach_volume_complete(self, server_id, attach_id):
|
||||||
|
"""Check that nova server lost attachment.
|
||||||
|
|
||||||
|
This check is needed for immediate reattachment when updating:
|
||||||
|
there might be some time between cinder marking volume as 'available'
|
||||||
|
and nova removing attachment from its own objects, so we
|
||||||
|
check that nova already knows that the volume is detached.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.client().volumes.get_server_volume(server_id, attach_id)
|
||||||
|
except Exception as ex:
|
||||||
|
self.ignore_not_found(ex)
|
||||||
|
LOG.info(_LI("Volume %(vol)s is detached from server %(srv)s"),
|
||||||
|
{'vol': attach_id, 'srv': server_id})
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
LOG.debug("Server %(srv)s still has attachment %(att)s." % {
|
||||||
|
'att': attach_id, 'srv': server_id})
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ServerConstraint(constraints.BaseCustomConstraint):
|
class ServerConstraint(constraints.BaseCustomConstraint):
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ from heat.common import exception
|
|||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
from heat.common.i18n import _LI
|
from heat.common.i18n import _LI
|
||||||
from heat.engine import attributes
|
from heat.engine import attributes
|
||||||
|
from heat.engine.clients.os import cinder as heat_cinder
|
||||||
from heat.engine import constraints
|
from heat.engine import constraints
|
||||||
from heat.engine import properties
|
from heat.engine import properties
|
||||||
|
from heat.engine import resource
|
||||||
from heat.engine.resources import volume_base as vb
|
from heat.engine.resources import volume_base as vb
|
||||||
from heat.engine import scheduler
|
|
||||||
from heat.engine import support
|
from heat.engine import support
|
||||||
from heat.engine import volume_tasks as vol_task
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -236,10 +236,41 @@ class CinderVolume(vb.BaseVolume):
|
|||||||
|
|
||||||
return vol_id
|
return vol_id
|
||||||
|
|
||||||
|
def _extend_volume(self, new_size):
|
||||||
|
try:
|
||||||
|
self.client().volumes.extend(self.resource_id, new_size)
|
||||||
|
except Exception as ex:
|
||||||
|
if self.client_plugin().is_client_exception(ex):
|
||||||
|
raise exception.Error(_(
|
||||||
|
"Failed to extend volume %(vol)s - %(err)s") % {
|
||||||
|
'vol': self.resource_id, 'err': str(ex)})
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _check_extend_volume_complete(self):
|
||||||
|
vol = self.client().volumes.get(self.resource_id)
|
||||||
|
if vol.status == 'extending':
|
||||||
|
LOG.debug("Volume %s is being extended" % vol.id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if vol.status != 'available':
|
||||||
|
LOG.info(_LI("Resize failed: Volume %(vol)s "
|
||||||
|
"is in %(status)s state."),
|
||||||
|
{'vol': vol.id, 'status': vol.status})
|
||||||
|
raise resource.ResourceUnknownStatus(
|
||||||
|
resource_status=vol.status,
|
||||||
|
result=_('Volume resize failed'))
|
||||||
|
|
||||||
|
LOG.info(_LI('Volume %(id)s resize complete'), {'id': vol.id})
|
||||||
|
return True
|
||||||
|
|
||||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||||
vol = None
|
vol = None
|
||||||
checkers = []
|
|
||||||
cinder = self.client()
|
cinder = self.client()
|
||||||
|
prg_resize = None
|
||||||
|
prg_attach = None
|
||||||
|
prg_detach = None
|
||||||
# update the name and description for cinder volume
|
# update the name and description for cinder volume
|
||||||
if self.NAME in prop_diff or self.DESCRIPTION in prop_diff:
|
if self.NAME in prop_diff or self.DESCRIPTION in prop_diff:
|
||||||
vol = cinder.volumes.get(self.resource_id)
|
vol = cinder.volumes.get(self.resource_id)
|
||||||
@ -268,6 +299,10 @@ class CinderVolume(vb.BaseVolume):
|
|||||||
vol = cinder.volumes.get(self.resource_id)
|
vol = cinder.volumes.get(self.resource_id)
|
||||||
new_vol_type = prop_diff.get(self.VOLUME_TYPE)
|
new_vol_type = prop_diff.get(self.VOLUME_TYPE)
|
||||||
cinder.volumes.retype(vol, new_vol_type, 'never')
|
cinder.volumes.retype(vol, new_vol_type, 'never')
|
||||||
|
# update read_only access mode
|
||||||
|
if self.READ_ONLY in prop_diff:
|
||||||
|
flag = prop_diff.get(self.READ_ONLY)
|
||||||
|
cinder.volumes.update_readonly_flag(self.resource_id, flag)
|
||||||
# extend volume size
|
# extend volume size
|
||||||
if self.SIZE in prop_diff:
|
if self.SIZE in prop_diff:
|
||||||
if not vol:
|
if not vol:
|
||||||
@ -278,6 +313,7 @@ class CinderVolume(vb.BaseVolume):
|
|||||||
raise exception.NotSupported(feature=_("Shrinking volume"))
|
raise exception.NotSupported(feature=_("Shrinking volume"))
|
||||||
|
|
||||||
elif new_size > vol.size:
|
elif new_size > vol.size:
|
||||||
|
prg_resize = heat_cinder.VolumeResizeProgress(size=new_size)
|
||||||
if vol.attachments:
|
if vol.attachments:
|
||||||
# NOTE(pshchelo):
|
# NOTE(pshchelo):
|
||||||
# this relies on current behavior of cinder attachments,
|
# this relies on current behavior of cinder attachments,
|
||||||
@ -289,35 +325,59 @@ class CinderVolume(vb.BaseVolume):
|
|||||||
server_id = vol.attachments[0]['server_id']
|
server_id = vol.attachments[0]['server_id']
|
||||||
device = vol.attachments[0]['device']
|
device = vol.attachments[0]['device']
|
||||||
attachment_id = vol.attachments[0]['id']
|
attachment_id = vol.attachments[0]['id']
|
||||||
detach_task = vol_task.VolumeDetachTask(
|
prg_detach = heat_cinder.VolumeDetachProgress(
|
||||||
self.stack, server_id, attachment_id)
|
server_id, vol.id, attachment_id)
|
||||||
checkers.append(scheduler.TaskRunner(detach_task))
|
prg_attach = heat_cinder.VolumeAttachProgress(
|
||||||
extend_task = vol_task.VolumeExtendTask(
|
server_id, vol.id, device)
|
||||||
self.stack, vol.id, new_size)
|
|
||||||
checkers.append(scheduler.TaskRunner(extend_task))
|
|
||||||
attach_task = vol_task.VolumeAttachTask(
|
|
||||||
self.stack, server_id, vol.id, device)
|
|
||||||
checkers.append(scheduler.TaskRunner(attach_task))
|
|
||||||
|
|
||||||
else:
|
return prg_detach, prg_resize, prg_attach
|
||||||
extend_task = vol_task.VolumeExtendTask(
|
|
||||||
self.stack, vol.id, new_size)
|
|
||||||
checkers.append(scheduler.TaskRunner(extend_task))
|
|
||||||
# update read_only access mode
|
|
||||||
if self.READ_ONLY in prop_diff:
|
|
||||||
flag = prop_diff.get(self.READ_ONLY)
|
|
||||||
cinder.volumes.update_readonly_flag(self.resource_id, flag)
|
|
||||||
|
|
||||||
if checkers:
|
def _detach_volume_to_complete(self, prg_detach):
|
||||||
checkers[0].start()
|
if not prg_detach.called:
|
||||||
return checkers
|
self.client_plugin('nova').detach_volume(prg_detach.srv_id,
|
||||||
|
prg_detach.attach_id)
|
||||||
|
prg_detach.called = True
|
||||||
|
return False
|
||||||
|
if not prg_detach.cinder_complete:
|
||||||
|
cinder_complete_res = self.client_plugin(
|
||||||
|
).check_detach_volume_complete(prg_detach.vol_id)
|
||||||
|
prg_detach.cinder_complete = cinder_complete_res
|
||||||
|
return False
|
||||||
|
if not prg_detach.nova_complete:
|
||||||
|
prg_detach.nova_complete = self.client_plugin(
|
||||||
|
'nova').check_detach_volume_complete(prg_detach.srv_id,
|
||||||
|
prg_detach.attach_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _attach_volume_to_complete(self, prg_attach):
|
||||||
|
if not prg_attach.called:
|
||||||
|
prg_attach.called = self.client_plugin('nova').attach_volume(
|
||||||
|
prg_attach.srv_id, prg_attach.vol_id, prg_attach.device)
|
||||||
|
return False
|
||||||
|
if not prg_attach.complete:
|
||||||
|
prg_attach.complete = self.client_plugin(
|
||||||
|
).check_attach_volume_complete(prg_attach.vol_id)
|
||||||
|
return prg_attach.complete
|
||||||
|
|
||||||
def check_update_complete(self, checkers):
|
def check_update_complete(self, checkers):
|
||||||
for checker in checkers:
|
prg_detach, prg_resize, prg_attach = checkers
|
||||||
if not checker.started():
|
if not prg_resize:
|
||||||
checker.start()
|
return True
|
||||||
if not checker.step():
|
# detach volume
|
||||||
|
if prg_detach:
|
||||||
|
if not prg_detach.nova_complete:
|
||||||
|
self._detach_volume_to_complete(prg_detach)
|
||||||
return False
|
return False
|
||||||
|
# resize volume
|
||||||
|
if not prg_resize.called:
|
||||||
|
prg_resize.called = self._extend_volume(prg_resize.size)
|
||||||
|
return False
|
||||||
|
if not prg_resize.complete:
|
||||||
|
prg_resize.complete = self._check_extend_volume_complete()
|
||||||
|
return prg_resize.complete and not prg_attach
|
||||||
|
# reattach volume back
|
||||||
|
if prg_attach:
|
||||||
|
return self._attach_volume_to_complete(prg_attach)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def handle_snapshot(self):
|
def handle_snapshot(self):
|
||||||
@ -334,24 +394,25 @@ class CinderVolume(vb.BaseVolume):
|
|||||||
raise exception.Error(backup.fail_reason)
|
raise exception.Error(backup.fail_reason)
|
||||||
|
|
||||||
def handle_delete_snapshot(self, snapshot):
|
def handle_delete_snapshot(self, snapshot):
|
||||||
backup_id = snapshot['resource_data'].get('backup_id')
|
backup_id = snapshot['resource_data']['backup_id']
|
||||||
|
try:
|
||||||
|
self.client().backups.delete(backup_id)
|
||||||
|
except Exception as ex:
|
||||||
|
self.client_plugin().ignore_not_found(ex)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return backup_id
|
||||||
|
|
||||||
def delete():
|
def check_delete_snapshot_complete(self, backup_id):
|
||||||
cinder = self.client()
|
if not backup_id:
|
||||||
try:
|
return True
|
||||||
cinder.backups.delete(backup_id)
|
try:
|
||||||
while True:
|
self.client().backups.get(backup_id)
|
||||||
yield
|
except Exception as ex:
|
||||||
cinder.backups.get(backup_id)
|
self.client_plugin().ignore_not_found(ex)
|
||||||
except Exception as ex:
|
return True
|
||||||
self.client_plugin().ignore_not_found(ex)
|
else:
|
||||||
|
return False
|
||||||
delete_task = scheduler.TaskRunner(delete)
|
|
||||||
delete_task.start()
|
|
||||||
return delete_task
|
|
||||||
|
|
||||||
def check_delete_snapshot_complete(self, delete_task):
|
|
||||||
return delete_task.step()
|
|
||||||
|
|
||||||
def _build_exclusive_options(self):
|
def _build_exclusive_options(self):
|
||||||
exclusive_options = []
|
exclusive_options = []
|
||||||
@ -463,13 +524,21 @@ class CinderVolumeAttachment(vb.BaseVolumeAttachment):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||||
checkers = []
|
prg_attach = None
|
||||||
|
prg_detach = None
|
||||||
if prop_diff:
|
if prop_diff:
|
||||||
# Even though some combinations of changed properties
|
# Even though some combinations of changed properties
|
||||||
# could be updated in UpdateReplace manner,
|
# could be updated in UpdateReplace manner,
|
||||||
# we still first detach the old resource so that
|
# we still first detach the old resource so that
|
||||||
# self.resource_id is not replaced prematurely
|
# self.resource_id is not replaced prematurely
|
||||||
volume_id = self.properties[self.VOLUME_ID]
|
volume_id = self.properties[self.VOLUME_ID]
|
||||||
|
server_id = self._stored_properties_data.get(self.INSTANCE_ID)
|
||||||
|
self.client_plugin('nova').detach_volume(server_id,
|
||||||
|
self.resource_id)
|
||||||
|
prg_detach = heat_cinder.VolumeDetachProgress(
|
||||||
|
server_id, volume_id, self.resource_id)
|
||||||
|
prg_detach.called = True
|
||||||
|
|
||||||
if self.VOLUME_ID in prop_diff:
|
if self.VOLUME_ID in prop_diff:
|
||||||
volume_id = prop_diff.get(self.VOLUME_ID)
|
volume_id = prop_diff.get(self.VOLUME_ID)
|
||||||
|
|
||||||
@ -477,29 +546,36 @@ class CinderVolumeAttachment(vb.BaseVolumeAttachment):
|
|||||||
if self.DEVICE in prop_diff:
|
if self.DEVICE in prop_diff:
|
||||||
device = prop_diff.get(self.DEVICE)
|
device = prop_diff.get(self.DEVICE)
|
||||||
|
|
||||||
server_id = self._stored_properties_data.get(self.INSTANCE_ID)
|
|
||||||
detach_task = vol_task.VolumeDetachTask(
|
|
||||||
self.stack, server_id, self.resource_id)
|
|
||||||
checkers.append(scheduler.TaskRunner(detach_task))
|
|
||||||
|
|
||||||
if self.INSTANCE_ID in prop_diff:
|
if self.INSTANCE_ID in prop_diff:
|
||||||
server_id = prop_diff.get(self.INSTANCE_ID)
|
server_id = prop_diff.get(self.INSTANCE_ID)
|
||||||
attach_task = vol_task.VolumeAttachTask(
|
prg_attach = heat_cinder.VolumeAttachProgress(
|
||||||
self.stack, server_id, volume_id, device)
|
server_id, volume_id, device)
|
||||||
|
|
||||||
checkers.append(scheduler.TaskRunner(attach_task))
|
return prg_detach, prg_attach
|
||||||
|
|
||||||
if checkers:
|
|
||||||
checkers[0].start()
|
|
||||||
return checkers
|
|
||||||
|
|
||||||
def check_update_complete(self, checkers):
|
def check_update_complete(self, checkers):
|
||||||
for checker in checkers:
|
prg_detach, prg_attach = checkers
|
||||||
if not checker.started():
|
if not (prg_detach and prg_attach):
|
||||||
checker.start()
|
return True
|
||||||
if not checker.step():
|
if not prg_detach.cinder_complete:
|
||||||
return False
|
prg_detach.cinder_complete = self.client_plugin(
|
||||||
self.resource_id_set(checkers[-1]._task.attachment_id)
|
).check_detach_volume_complete(prg_detach.vol_id)
|
||||||
|
return False
|
||||||
|
if not prg_detach.nova_complete:
|
||||||
|
prg_detach.nova_complete = self.client_plugin(
|
||||||
|
'nova').check_detach_volume_complete(prg_detach.srv_id,
|
||||||
|
self.resource_id)
|
||||||
|
return False
|
||||||
|
if not prg_attach.called:
|
||||||
|
prg_attach.called = self.client_plugin('nova').attach_volume(
|
||||||
|
prg_attach.srv_id, prg_attach.vol_id, prg_attach.device)
|
||||||
|
return False
|
||||||
|
if not prg_attach.complete:
|
||||||
|
prg_attach.complete = self.client_plugin(
|
||||||
|
).check_attach_volume_complete(prg_attach.vol_id)
|
||||||
|
if prg_attach.complete:
|
||||||
|
self.resource_id_set(prg_attach.called)
|
||||||
|
return prg_attach.complete
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,9 +13,8 @@
|
|||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
|
from heat.engine.clients.os import cinder as heat_cinder
|
||||||
from heat.engine import resource
|
from heat.engine import resource
|
||||||
from heat.engine import scheduler
|
|
||||||
from heat.engine import volume_tasks as vol_task
|
|
||||||
|
|
||||||
|
|
||||||
class BaseVolume(resource.Resource):
|
class BaseVolume(resource.Resource):
|
||||||
@ -82,55 +81,80 @@ class BaseVolume(resource.Resource):
|
|||||||
]
|
]
|
||||||
self._verify_check_conditions(checks)
|
self._verify_check_conditions(checks)
|
||||||
|
|
||||||
def _backup(self):
|
def handle_snapshot_delete(self, state):
|
||||||
cinder = self.client()
|
backup = state not in ((self.CREATE, self.FAILED),
|
||||||
backup = cinder.backups.create(self.resource_id)
|
(self.UPDATE, self.FAILED))
|
||||||
while backup.status == 'creating':
|
progress = heat_cinder.VolumeDeleteProgress()
|
||||||
yield
|
progress.backup['called'] = not backup
|
||||||
backup = cinder.backups.get(backup.id)
|
progress.backup['complete'] = not backup
|
||||||
if backup.status != 'available':
|
return progress
|
||||||
|
|
||||||
|
def handle_delete(self):
|
||||||
|
if self.resource_id is None:
|
||||||
|
return heat_cinder.VolumeDeleteProgress(True)
|
||||||
|
progress = heat_cinder.VolumeDeleteProgress()
|
||||||
|
progress.backup['called'] = True
|
||||||
|
progress.backup['complete'] = True
|
||||||
|
return progress
|
||||||
|
|
||||||
|
def _create_backup(self):
|
||||||
|
backup = self.client().backups.create(self.resource_id)
|
||||||
|
return backup.id
|
||||||
|
|
||||||
|
def _check_create_backup_complete(self, prg):
|
||||||
|
backup = self.client().backups.get(prg.backup_id)
|
||||||
|
if backup.status == 'creating':
|
||||||
|
return False
|
||||||
|
if backup.status == 'available':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
raise resource.ResourceUnknownStatus(
|
raise resource.ResourceUnknownStatus(
|
||||||
resource_status=backup.status,
|
resource_status=backup.status,
|
||||||
result=_('Volume backup failed'))
|
result=_('Volume backup failed'))
|
||||||
|
|
||||||
@scheduler.wrappertask
|
def _delete_volume(self):
|
||||||
def _delete(self, backup=False):
|
try:
|
||||||
if self.resource_id is not None:
|
|
||||||
cinder = self.client()
|
cinder = self.client()
|
||||||
|
vol = cinder.volumes.get(self.resource_id)
|
||||||
|
if vol.status == 'in-use':
|
||||||
|
raise exception.Error(_('Volume in use'))
|
||||||
|
# if the volume is already in deleting status,
|
||||||
|
# just wait for the deletion to complete
|
||||||
|
if vol.status != 'deleting':
|
||||||
|
cinder.volumes.delete(self.resource_id)
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
except Exception as ex:
|
||||||
|
self.client_plugin().ignore_not_found(ex)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_delete_complete(self, prg):
|
||||||
|
if not prg.backup['called']:
|
||||||
|
prg.backup_id = self._create_backup()
|
||||||
|
prg.backup['called'] = True
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not prg.backup['complete']:
|
||||||
|
prg.backup['complete'] = self._check_create_backup_complete(prg)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not prg.delete['called']:
|
||||||
|
prg.delete['complete'] = self._delete_volume()
|
||||||
|
prg.delete['called'] = True
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not prg.delete['complete']:
|
||||||
try:
|
try:
|
||||||
vol = cinder.volumes.get(self.resource_id)
|
self.client().volumes.get(self.resource_id)
|
||||||
|
|
||||||
if backup:
|
|
||||||
yield self._backup()
|
|
||||||
vol = cinder.volumes.get(self.resource_id)
|
|
||||||
|
|
||||||
if vol.status == 'in-use':
|
|
||||||
raise exception.Error(_('Volume in use'))
|
|
||||||
# if the volume is already in deleting status,
|
|
||||||
# just wait for the deletion to complete
|
|
||||||
if vol.status != 'deleting':
|
|
||||||
cinder.volumes.delete(self.resource_id)
|
|
||||||
while True:
|
|
||||||
yield
|
|
||||||
vol = cinder.volumes.get(self.resource_id)
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.client_plugin().ignore_not_found(ex)
|
self.client_plugin().ignore_not_found(ex)
|
||||||
|
prg.delete['complete'] = True
|
||||||
def handle_snapshot_delete(self, state):
|
return True
|
||||||
backup = state not in ((self.CREATE, self.FAILED),
|
else:
|
||||||
(self.UPDATE, self.FAILED))
|
return False
|
||||||
|
return True
|
||||||
delete_task = scheduler.TaskRunner(self._delete, backup=backup)
|
|
||||||
delete_task.start()
|
|
||||||
return delete_task
|
|
||||||
|
|
||||||
def handle_delete(self):
|
|
||||||
delete_task = scheduler.TaskRunner(self._delete)
|
|
||||||
delete_task.start()
|
|
||||||
return delete_task
|
|
||||||
|
|
||||||
def check_delete_complete(self, delete_task):
|
|
||||||
return delete_task.step()
|
|
||||||
|
|
||||||
|
|
||||||
class BaseVolumeAttachment(resource.Resource):
|
class BaseVolumeAttachment(resource.Resource):
|
||||||
@ -138,31 +162,41 @@ class BaseVolumeAttachment(resource.Resource):
|
|||||||
Base Volume Attachment Manager.
|
Base Volume Attachment Manager.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
default_client_name = 'cinder'
|
||||||
|
|
||||||
def handle_create(self):
|
def handle_create(self):
|
||||||
server_id = self.properties[self.INSTANCE_ID]
|
server_id = self.properties[self.INSTANCE_ID]
|
||||||
volume_id = self.properties[self.VOLUME_ID]
|
volume_id = self.properties[self.VOLUME_ID]
|
||||||
dev = self.properties[self.DEVICE]
|
dev = self.properties[self.DEVICE]
|
||||||
|
|
||||||
attach_task = vol_task.VolumeAttachTask(
|
attach_id = self.client_plugin('nova').attach_volume(
|
||||||
self.stack, server_id, volume_id, dev)
|
server_id, volume_id, dev)
|
||||||
attach_runner = scheduler.TaskRunner(attach_task)
|
|
||||||
|
|
||||||
attach_runner.start()
|
self.resource_id_set(attach_id)
|
||||||
|
|
||||||
self.resource_id_set(attach_task.attachment_id)
|
return volume_id
|
||||||
|
|
||||||
return attach_runner
|
def check_create_complete(self, volume_id):
|
||||||
|
return self.client_plugin().check_attach_volume_complete(volume_id)
|
||||||
def check_create_complete(self, attach_runner):
|
|
||||||
return attach_runner.step()
|
|
||||||
|
|
||||||
def handle_delete(self):
|
def handle_delete(self):
|
||||||
server_id = self.properties[self.INSTANCE_ID]
|
server_id = self.properties[self.INSTANCE_ID]
|
||||||
detach_task = vol_task.VolumeDetachTask(
|
vol_id = self.properties[self.VOLUME_ID]
|
||||||
self.stack, server_id, self.resource_id)
|
self.client_plugin('nova').detach_volume(server_id,
|
||||||
detach_runner = scheduler.TaskRunner(detach_task)
|
self.resource_id)
|
||||||
detach_runner.start()
|
prg = heat_cinder.VolumeDetachProgress(
|
||||||
return detach_runner
|
server_id, vol_id, self.resource_id)
|
||||||
|
prg.called = True
|
||||||
|
return prg
|
||||||
|
|
||||||
def check_delete_complete(self, detach_runner):
|
def check_delete_complete(self, prg):
|
||||||
return detach_runner.step()
|
if not prg.cinder_complete:
|
||||||
|
prg.cinder_complete = self.client_plugin(
|
||||||
|
).check_detach_volume_complete(prg.vol_id)
|
||||||
|
return False
|
||||||
|
if not prg.nova_complete:
|
||||||
|
prg.nova_complete = self.client_plugin(
|
||||||
|
'nova').check_detach_volume_complete(prg.srv_id,
|
||||||
|
prg.attach_id)
|
||||||
|
return prg.nova_complete
|
||||||
|
return True
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from heat.common import exception
|
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
from heat.common.i18n import _LI
|
from heat.common.i18n import _LI
|
||||||
from heat.engine import resource
|
from heat.engine import resource
|
||||||
@ -21,56 +20,6 @@ from heat.engine import resource
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class VolumeExtendTask(object):
|
|
||||||
"""A task to resize volume using Cinder API."""
|
|
||||||
|
|
||||||
def __init__(self, stack, volume_id, size):
|
|
||||||
self.clients = stack.clients
|
|
||||||
self.volume_id = volume_id
|
|
||||||
self.size = size
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return _("Resizing volume %(vol)s to size %(size)i") % {
|
|
||||||
'vol': self.volume_id, 'size': self.size}
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "%s(%s +-> %i)" % (type(self).__name__, self.volume_id,
|
|
||||||
self.size)
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
LOG.debug(str(self))
|
|
||||||
|
|
||||||
cinder = self.clients.client('cinder').volumes
|
|
||||||
vol = cinder.get(self.volume_id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
cinder.extend(self.volume_id, self.size)
|
|
||||||
except Exception as ex:
|
|
||||||
if self.clients.client_plugin('cinder').is_client_exception(ex):
|
|
||||||
raise exception.Error(_(
|
|
||||||
"Failed to extend volume %(vol)s - %(err)s") % {
|
|
||||||
'vol': vol.id, 'err': ex})
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
vol = cinder.get(self.volume_id)
|
|
||||||
while vol.status == 'extending':
|
|
||||||
LOG.debug("Volume %s is being extended" % self.volume_id)
|
|
||||||
yield
|
|
||||||
vol = cinder.get(self.volume_id)
|
|
||||||
|
|
||||||
if vol.status != 'available':
|
|
||||||
LOG.info(_LI("Resize failed: Volume %(vol)s is in %(status)s "
|
|
||||||
"state."), {'vol': vol.id, 'status': vol.status})
|
|
||||||
raise resource.ResourceUnknownStatus(
|
|
||||||
resource_status=vol.status,
|
|
||||||
result=_('Volume resize failed'))
|
|
||||||
|
|
||||||
LOG.info(_LI('%s - complete'), str(self))
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachTask(object):
|
class VolumeAttachTask(object):
|
||||||
"""A task for attaching a volume to a Nova server."""
|
"""A task for attaching a volume to a Nova server."""
|
||||||
|
|
||||||
@ -128,102 +77,3 @@ class VolumeAttachTask(object):
|
|||||||
result=_('Volume attachment failed'))
|
result=_('Volume attachment failed'))
|
||||||
|
|
||||||
LOG.info(_LI('%s - complete'), str(self))
|
LOG.info(_LI('%s - complete'), str(self))
|
||||||
|
|
||||||
|
|
||||||
class VolumeDetachTask(object):
|
|
||||||
"""A task for detaching a volume from a Nova server."""
|
|
||||||
|
|
||||||
def __init__(self, stack, server_id, attachment_id):
|
|
||||||
"""
|
|
||||||
Initialise with the stack (for obtaining the clients), and the IDs of
|
|
||||||
the server and volume.
|
|
||||||
"""
|
|
||||||
self.clients = stack.clients
|
|
||||||
self.server_id = server_id
|
|
||||||
self.attachment_id = attachment_id
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""Return a human-readable string description of the task."""
|
|
||||||
return _('Removing attachment %(att)s from Instance %(srv)s') % {
|
|
||||||
'att': self.attachment_id, 'srv': self.server_id}
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return a brief string description of the task."""
|
|
||||||
return '%s(%s -/> %s)' % (type(self).__name__,
|
|
||||||
self.attachment_id,
|
|
||||||
self.server_id)
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
"""Return a co-routine which runs the task."""
|
|
||||||
LOG.debug(str(self))
|
|
||||||
|
|
||||||
nova_plugin = self.clients.client_plugin('nova')
|
|
||||||
cinder_plugin = self.clients.client_plugin('cinder')
|
|
||||||
server_api = self.clients.client('nova').volumes
|
|
||||||
cinder = self.clients.client('cinder')
|
|
||||||
# get reference to the volume while it is attached
|
|
||||||
try:
|
|
||||||
nova_vol = server_api.get_server_volume(self.server_id,
|
|
||||||
self.attachment_id)
|
|
||||||
vol = cinder.volumes.get(nova_vol.id)
|
|
||||||
except Exception as ex:
|
|
||||||
if (cinder_plugin.is_not_found(ex) or
|
|
||||||
nova_plugin.is_not_found(ex) or
|
|
||||||
nova_plugin.is_bad_request(ex)):
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
if vol.status == 'deleting':
|
|
||||||
return
|
|
||||||
|
|
||||||
# detach the volume using volume_attachment
|
|
||||||
try:
|
|
||||||
server_api.delete_server_volume(self.server_id, self.attachment_id)
|
|
||||||
except Exception as ex:
|
|
||||||
if nova_plugin.is_not_found(ex) or nova_plugin.is_bad_request(ex):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
try:
|
|
||||||
while vol.status in ('in-use', 'detaching'):
|
|
||||||
LOG.debug('%s - volume still in use' % str(self))
|
|
||||||
yield
|
|
||||||
vol = cinder.volumes.get(nova_vol.id)
|
|
||||||
|
|
||||||
LOG.info(_LI('%(name)s - status: %(status)s'),
|
|
||||||
{'name': str(self), 'status': vol.status})
|
|
||||||
if vol.status not in ['available', 'deleting']:
|
|
||||||
LOG.info(_LI("Detachment failed - volume %(vol)s "
|
|
||||||
"is in %(status)s status"),
|
|
||||||
{"vol": vol.id,
|
|
||||||
"status": vol.status})
|
|
||||||
raise resource.ResourceUnknownStatus(
|
|
||||||
resource_status=vol.status,
|
|
||||||
result=_('Volume detachment failed'))
|
|
||||||
|
|
||||||
except Exception as ex:
|
|
||||||
cinder_plugin.ignore_not_found(ex)
|
|
||||||
|
|
||||||
# The next check is needed for immediate reattachment when updating:
|
|
||||||
# there might be some time between cinder marking volume as 'available'
|
|
||||||
# and nova removing attachment from its own objects, so we
|
|
||||||
# check that nova already knows that the volume is detached
|
|
||||||
def server_has_attachment(server_id, attachment_id):
|
|
||||||
try:
|
|
||||||
server_api.get_server_volume(server_id, attachment_id)
|
|
||||||
except Exception as ex:
|
|
||||||
nova_plugin.ignore_not_found(ex)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
while server_has_attachment(self.server_id, self.attachment_id):
|
|
||||||
LOG.info(_LI("Server %(srv)s still has attachment %(att)s."),
|
|
||||||
{'att': self.attachment_id, 'srv': self.server_id})
|
|
||||||
yield
|
|
||||||
|
|
||||||
LOG.info(_LI("Volume %(vol)s is detached from server %(srv)s"),
|
|
||||||
{'vol': vol.id, 'srv': self.server_id})
|
|
||||||
|
@ -308,10 +308,13 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
|||||||
self._mock_create_server_volume_script(fva)
|
self._mock_create_server_volume_script(fva)
|
||||||
self.stub_VolumeConstraint_validate()
|
self.stub_VolumeConstraint_validate()
|
||||||
# delete script
|
# delete script
|
||||||
self.fc.volumes.get_server_volume(u'WikiDatabase',
|
self.fc.volumes.delete_server_volume(u'WikiDatabase',
|
||||||
'vol-123').AndReturn(fva)
|
'vol-123').AndReturn(None)
|
||||||
self.cinder_fc.volumes.get(fva.id).AndRaise(
|
self.cinder_fc.volumes.get(fva.id).AndRaise(
|
||||||
cinder_exp.NotFound('Not found'))
|
cinder_exp.NotFound('Not found'))
|
||||||
|
self.fc.volumes.get_server_volume(u'WikiDatabase', 'vol-123'
|
||||||
|
).AndRaise(
|
||||||
|
fakes_nova.fake_exception())
|
||||||
|
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -333,9 +336,12 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
|||||||
self._mock_create_server_volume_script(fva)
|
self._mock_create_server_volume_script(fva)
|
||||||
self.stub_VolumeConstraint_validate()
|
self.stub_VolumeConstraint_validate()
|
||||||
# delete script
|
# delete script
|
||||||
self.fc.volumes.get_server_volume(u'WikiDatabase',
|
self.fc.volumes.delete_server_volume(u'WikiDatabase',
|
||||||
'vol-123').AndReturn(fva)
|
'vol-123').AndReturn(None)
|
||||||
self.cinder_fc.volumes.get(fva.id).AndReturn(fva)
|
self.cinder_fc.volumes.get(fva.id).AndReturn(fva)
|
||||||
|
self.fc.volumes.get_server_volume(u'WikiDatabase', 'vol-123'
|
||||||
|
).AndRaise(
|
||||||
|
fakes_nova.fake_exception())
|
||||||
|
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -395,11 +401,8 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
|||||||
self.stub_VolumeConstraint_validate()
|
self.stub_VolumeConstraint_validate()
|
||||||
# delete script
|
# delete script
|
||||||
fva = vt_base.FakeVolume('in-use')
|
fva = vt_base.FakeVolume('in-use')
|
||||||
self.fc.volumes.get_server_volume(u'WikiDatabase',
|
|
||||||
'vol-123').AndReturn(fva)
|
|
||||||
self.cinder_fc.volumes.get(fva.id).AndReturn(fva)
|
|
||||||
self.fc.volumes.delete_server_volume(
|
self.fc.volumes.delete_server_volume(
|
||||||
'WikiDatabase', 'vol-123').MultipleTimes().AndReturn(None)
|
'WikiDatabase', 'vol-123').AndReturn(None)
|
||||||
self.cinder_fc.volumes.get(fva.id).AndReturn(
|
self.cinder_fc.volumes.get(fva.id).AndReturn(
|
||||||
vt_base.FakeVolume('error', id=fva.id))
|
vt_base.FakeVolume('error', id=fva.id))
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
@ -444,13 +447,9 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
|||||||
fv = self._mock_create_volume(vt_base.FakeVolume('creating'),
|
fv = self._mock_create_volume(vt_base.FakeVolume('creating'),
|
||||||
stack_name)
|
stack_name)
|
||||||
|
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(
|
|
||||||
vt_base.FakeVolume('available'))
|
|
||||||
self.cinder_fc.volumes.delete(fv.id).AndReturn(True)
|
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(
|
self.cinder_fc.volumes.get(fv.id).AndReturn(
|
||||||
vt_base.FakeVolume('deleting'))
|
vt_base.FakeVolume('deleting'))
|
||||||
self.cinder_fc.volumes.get(fv.id).AndRaise(
|
|
||||||
cinder_exp.NotFound('Not found'))
|
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
stack = utils.parse_stack(self.t, stack_name=stack_name)
|
stack = utils.parse_stack(self.t, stack_name=stack_name)
|
||||||
@ -533,8 +532,10 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
|||||||
|
|
||||||
# snapshot script
|
# snapshot script
|
||||||
self.m.StubOutWithMock(self.cinder_fc.backups, 'create')
|
self.m.StubOutWithMock(self.cinder_fc.backups, 'create')
|
||||||
self.cinder_fc.backups.create(fv.id).AndReturn(
|
self.m.StubOutWithMock(self.cinder_fc.backups, 'get')
|
||||||
vt_base.FakeBackup('available'))
|
fb = vt_base.FakeBackup('available')
|
||||||
|
self.cinder_fc.backups.create(fv.id).AndReturn(fb)
|
||||||
|
self.cinder_fc.backups.get(fb.id).AndReturn(fb)
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
||||||
self._mock_delete_volume(fv)
|
self._mock_delete_volume(fv)
|
||||||
|
|
||||||
@ -555,10 +556,11 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
|||||||
stack_name)
|
stack_name)
|
||||||
|
|
||||||
# snapshot script
|
# snapshot script
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
|
||||||
self.m.StubOutWithMock(self.cinder_fc.backups, 'create')
|
self.m.StubOutWithMock(self.cinder_fc.backups, 'create')
|
||||||
|
self.m.StubOutWithMock(self.cinder_fc.backups, 'get')
|
||||||
fb = vt_base.FakeBackup('error')
|
fb = vt_base.FakeBackup('error')
|
||||||
self.cinder_fc.backups.create(fv.id).AndReturn(fb)
|
self.cinder_fc.backups.create(fv.id).AndReturn(fb)
|
||||||
|
self.cinder_fc.backups.get(fb.id).AndReturn(fb)
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
self.t['Resources']['DataVolume']['DeletionPolicy'] = 'Snapshot'
|
self.t['Resources']['DataVolume']['DeletionPolicy'] = 'Snapshot'
|
||||||
|
@ -337,7 +337,6 @@ class CinderVolumeTest(vt_base.BaseVolumeTest):
|
|||||||
fv = vt_base.FakeVolume('available',
|
fv = vt_base.FakeVolume('available',
|
||||||
size=1, attachments=[])
|
size=1, attachments=[])
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
|
||||||
self.cinder_fc.volumes.extend(fv.id, 2)
|
self.cinder_fc.volumes.extend(fv.id, 2)
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(
|
self.cinder_fc.volumes.get(fv.id).AndReturn(
|
||||||
vt_base.FakeVolume('extending'))
|
vt_base.FakeVolume('extending'))
|
||||||
@ -371,7 +370,6 @@ class CinderVolumeTest(vt_base.BaseVolumeTest):
|
|||||||
fv = vt_base.FakeVolume('available',
|
fv = vt_base.FakeVolume('available',
|
||||||
size=1, attachments=[])
|
size=1, attachments=[])
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
|
||||||
self.cinder_fc.volumes.extend(fv.id, 2).AndRaise(
|
self.cinder_fc.volumes.extend(fv.id, 2).AndRaise(
|
||||||
cinder_exp.OverLimit(413))
|
cinder_exp.OverLimit(413))
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
@ -400,7 +398,6 @@ class CinderVolumeTest(vt_base.BaseVolumeTest):
|
|||||||
fv = vt_base.FakeVolume('available',
|
fv = vt_base.FakeVolume('available',
|
||||||
size=1, attachments=[])
|
size=1, attachments=[])
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(fv)
|
|
||||||
self.cinder_fc.volumes.extend(fv.id, 2)
|
self.cinder_fc.volumes.extend(fv.id, 2)
|
||||||
self.cinder_fc.volumes.get(fv.id).AndReturn(
|
self.cinder_fc.volumes.get(fv.id).AndReturn(
|
||||||
vt_base.FakeVolume('extending'))
|
vt_base.FakeVolume('extending'))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user