Complete volume attachment until volume become to 'in-use'

1. Move the wait volume state logic from nova to cinder
2. Wait volume state to be "in-use", then complete volume attachment
3. Change volume sync code to not sync "attaching" or "detaching" state
as if sync code change such state, then volume attach/detach will be
broken



Closes-Bug: #1402467
Change-Id: I3640afe9ad4aba8788a932d05e94f55a3b8d6333
This commit is contained in:
terryyao 2014-12-15 09:45:29 +08:00
parent 3699879307
commit 9b9d3d05a7
5 changed files with 100 additions and 67 deletions

View File

@ -1,5 +1,5 @@
from __future__ import absolute_import
# Copyright 2013 IBM Corp.
# Copyright 2013, 2014 IBM Corp.
import logging
import sys
@ -301,3 +301,16 @@ class PowerVCDriver(VolumeDriver):
return name
else:
return volume.id
def attach_volume(self, context, volume, instance_uuid, host_name,
mountpoint):
"""Callback for volume attached to instance or host."""
# wait for volume to be attached
self._service.attach_volume(context, volume, instance_uuid, host_name,
mountpoint)
def detach_volume(self, context, volume):
"""Callback for volume detached."""
# wait for volume to be detached
self._service.detach_volume(context, volume)

View File

@ -1,5 +1,5 @@
from __future__ import absolute_import
# Copyright 2013 IBM Corp.
# Copyright 2013, 2014 IBM Corp.
import httplib
@ -270,3 +270,62 @@ class PowerVCService(object):
def list_storage_providers(self):
return PowerVCService._client.storage_providers.list()
def attach_volume(self, context, volume, instance_uuid, host_name,
mountpoint):
"""Callback for volume attached to instance or host."""
# wait for volume to be attached, the volume-attach operation is done
# in nova
pvc_volume_id = None
for metaDataItem in volume.volume_metadata:
if metaDataItem.key == constants.LOCAL_PVC_PREFIX + 'id':
pvc_volume_id = metaDataItem.value
break
else:
LOG.warning('Fail to get pvc_id %s' % volume_id)
raise exceptions.BadRequest
LOG.debug('wait until PVC volume %s status to in-use', pvc_volume_id)
FILPC = loopingcall.FixedIntervalLoopingCall
timer = FILPC(self._wait_for_state_change,
pvc_volume_id,
constants.STATUS_AVAILABLE,
constants.STATUS_INUSE,
constants.STATUS_ATTACHING)
try:
timer.start(interval=10).wait()
except:
LOG.error("volume %s attaching failed, ", volume.id)
# when attached failed raise exception
raise exception.CinderException('Volume %s attaching failed',
volume.id)
def detach_volume(self, context, volume):
"""Callback for volume detach to instance or host."""
# wait for volume to be detached, the volume-detach operation is done
# in nova
pvc_volume_id = None
for metaDataItem in volume.volume_metadata:
if metaDataItem.key == constants.LOCAL_PVC_PREFIX + 'id':
pvc_volume_id = metaDataItem.value
break
else:
LOG.warning('Fail to get pvc_id %s' % volume_id)
raise exceptions.BadRequest
LOG.debug('wait until PVC volume %s status to available', pvc_volume_id)
FILPC = loopingcall.FixedIntervalLoopingCall
timer = FILPC(self._wait_for_state_change,
pvc_volume_id,
constants.STATUS_INUSE,
constants.STATUS_AVAILABLE,
constants.STATUS_DETACHING)
try:
timer.start(interval=10).wait()
except:
LOG.error("volume %s detaching failed, ", volume.id)
# when attached failed raise exception
raise exception.CinderException('Volume %s detaching failed',
volume.id)

View File

@ -52,6 +52,9 @@ STATUS_AVAILABLE = 'available'
STATUS_ERROR = 'error'
STATUS_CREATING = 'creating'
STATUS_DELETING = 'deleting'
STATUS_INUSE = 'in-use'
STATUS_ATTACHING = 'attaching'
STATUS_DETACHING = 'detaching'
# multi-backends configuration option for PowerVCDriver
BACKEND_POWERVCDRIVER = "powervcdriver"

View File

@ -938,6 +938,19 @@ class PowerVCCinderManager(service.Service):
return found
def _is_intermediate_state(self, local_volume=None, pvc_volume=None):
intermediate_statuses = [constants.STATUS_ATTACHING,
constants.STATUS_DETACHING]
if local_volume:
local_volume_state = local_volume.get('status')
if local_volume_state in intermediate_statuses:
return True
if pvc_volume:
pvc_volume_state = pvc_volume.get('status')
if pvc_volume_state in intermediate_statuses:
return True
return False
def _sync_existing_volume(self, context, local_volume, pvc_volume):
ret = False
if local_volume is None or pvc_volume is None:
@ -949,6 +962,12 @@ class PowerVCCinderManager(service.Service):
" Skipping volume sync."))
return ret
if self._is_intermediate_state(local_volume, pvc_volume):
LOG.info('volume can not be synced as local or pvc volume state in'
'intermediate state: %s, '
'%s' % (local_volume.get('status'), pvc_volume))
return
values = self._get_values_from_volume(context,
pvc_volume,
local_volume)
@ -992,6 +1011,10 @@ class PowerVCCinderManager(service.Service):
LOG.debug("Volume is None, cannot insert it")
return
if self._is_intermediate_state(pvc_volume=volume):
LOG.info('pvc volume is in a intermediate status, ignore to insert:'
' %s' % volume)
return
volume_info = volume
volume_type = volume_info.get('volume_type')
volume_display_name = volume_info.get('display_name')

View File

@ -427,38 +427,6 @@ class PowerVCService(object):
LOG.warning(_("VolumeId missing in detaching volume"))
self._volumes.delete_server_volume(server_id, volume_id)
def _wait_for_detach(server_id, volume_id):
"""
This method is used to call at an interval until the volume is
detached from the server.
"""
try:
volume = self._volumes.get_server_volume(server_id, volume_id)
except exceptions.NotFound:
LOG.info(
_("Detach the volume on instance %s successfully.")
% server_id)
raise loopingcall.LoopingCallDone(True)
if volume:
if self.try_time > self.max_tries:
LOG.info(_("Volume %s failed to detach.") % volume_id)
# There is no VolumeDetachFailed like exception defined
raise loopingcall.LoopingCallDone(True)
else:
LOG.debug(_("Looping call to check detach of volume %s.")
% volume_id)
self.try_time += 1
else:
raise loopingcall.LoopingCallDone(True)
self.try_time = 0
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_detach,
server_id,
volume_id)
return timer.start(self.longrun_loop_interval,
self.longrun_initial_delay).wait()
def power_off(self, instance):
"""Power off the specified instance."""
server_instance = self._get_server(instance)
@ -1056,15 +1024,6 @@ class PowerVCService(object):
volume_id,
mountpoint)
self.try_time = 0
timer = loopingcall.\
FixedIntervalLoopingCall(self._check_attachment_of_instance,
server_id,
volume_id)
return timer.start(self.longrun_loop_interval,
self.longrun_initial_delay).wait()
def list_attachments_of_instance(self, server_id):
"""
Lists the volume attachments for the specified server.
@ -1074,30 +1033,6 @@ class PowerVCService(object):
return response
def _check_attachment_of_instance(self, server_id, volume_id):
"""
Check whether the specified volume has been attached to
the specified instance.
"""
self.try_time = self.try_time + 1
try:
attachments = self.list_attachments_of_instance(server_id)
except exceptions:
LOG.warning(_("Fail to get the attachments of the server:\
VM %s.") % server_id)
raise exceptions.BadRequest
for attachment in attachments:
get_volume_id = getattr(attachment, 'volumeId', '')
if get_volume_id == volume_id:
LOG.debug(_("The attach_volume operation of the server:\
VM %s is completed") % server_id)
raise loopingcall.LoopingCallDone(True)
if self.try_time > self.max_tries:
raise exception.VolumeUnattached
def _get_pvc_volume_id(self, local_id):
"""
The method get_pvc_volume_id is used to get the PowerVC volume id