Files
cinder/cinder/api/v2/views/volumes.py
T
Steve Noyes a95c9e5668 Fix attachments on volume show when volume is attaching
With the new v3 cinder attach flow, nova calls attachment_update
and then attachment_complete. During that time the volume
has 'attaching' status. Because of that status, cinder show volume
will show all the attachments as empty, even if the volume has
attachments. Currently this will only happen during migration,
but when multi-attach is working, this will be a general issue.

The change is to the show volume flow. It will no longer look at the
volume attach status, but only at the individual attachment status's.
Any attachment that has status of attached will be returned by show.

(This will only be an issue in Queens when the new multi-attach flow
is used. There is no need for this change in Pike.)

Partially Implements: blueprint cinder-new-attach-apis
Change-Id: I8a2bff5a668ec58ee80c192cb72326f2b3599c39
Closes-Bug: 1713521
2017-09-06 09:14:04 -04:00

168 lines
6.9 KiB
Python

# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from cinder.api import common
from cinder import group as group_api
from cinder.objects import fields
from cinder.volume import group_types
class ViewBuilder(common.ViewBuilder):
"""Model a server API response as a python dictionary."""
_collection_name = "volumes"
def __init__(self):
"""Initialize view builder."""
super(ViewBuilder, self).__init__()
def summary_list(self, request, volumes, volume_count=None):
"""Show a list of volumes without many details."""
return self._list_view(self.summary, request, volumes,
volume_count)
def detail_list(self, request, volumes, volume_count=None):
"""Detailed view of a list of volumes."""
return self._list_view(self.detail, request, volumes,
volume_count,
self._collection_name + '/detail')
def summary(self, request, volume):
"""Generic, non-detailed view of a volume."""
return {
'volume': {
'id': volume['id'],
'name': volume['display_name'],
'links': self._get_links(request,
volume['id']),
},
}
def _get_volume_status(self, volume):
# NOTE(wanghao): for fixing bug 1504007, we introduce 'managing',
# 'error_managing' and 'error_managing_deleting' status into managing
# process, but still expose 'creating' and 'error' and 'deleting'
# status to user for API compatibility.
status_map = {
'managing': 'creating',
'error_managing': 'error',
'error_managing_deleting': 'deleting',
}
vol_status = volume.get('status')
return status_map.get(vol_status, vol_status)
def detail(self, request, volume):
"""Detailed view of a single volume."""
volume_ref = {
'volume': {
'id': volume.get('id'),
'status': self._get_volume_status(volume),
'size': volume.get('size'),
'availability_zone': volume.get('availability_zone'),
'created_at': volume.get('created_at'),
'updated_at': volume.get('updated_at'),
'attachments': self._get_attachments(volume),
'name': volume.get('display_name'),
'description': volume.get('display_description'),
'volume_type': self._get_volume_type(volume),
'snapshot_id': volume.get('snapshot_id'),
'source_volid': volume.get('source_volid'),
'metadata': self._get_volume_metadata(volume),
'links': self._get_links(request, volume['id']),
'user_id': volume.get('user_id'),
'bootable': six.text_type(volume.get('bootable')).lower(),
'encrypted': self._is_volume_encrypted(volume),
'replication_status': volume.get('replication_status'),
'consistencygroup_id': volume.get('consistencygroup_id'),
'multiattach': volume.get('multiattach'),
}
}
if request.environ['cinder.context'].is_admin:
volume_ref['volume']['migration_status'] = (
volume.get('migration_status'))
# NOTE(xyang): Display group_id as consistencygroup_id in detailed
# view of the volume if group is converted from cg.
group_id = volume.get('group_id')
if group_id is not None:
# Not found exception will be handled at the wsgi level
ctxt = request.environ['cinder.context']
grp = group_api.API().get(ctxt, group_id)
cgsnap_type = group_types.get_default_cgsnapshot_type()
if grp.group_type_id == cgsnap_type['id']:
volume_ref['volume']['consistencygroup_id'] = group_id
return volume_ref
def _is_volume_encrypted(self, volume):
"""Determine if volume is encrypted."""
return volume.get('encryption_key_id') is not None
def _get_attachments(self, volume):
"""Retrieve the attachments of the volume object."""
attachments = []
for attachment in volume.volume_attachment:
if (attachment.get('attach_status') ==
fields.VolumeAttachStatus.ATTACHED):
a = {'id': attachment.get('volume_id'),
'attachment_id': attachment.get('id'),
'volume_id': attachment.get('volume_id'),
'server_id': attachment.get('instance_uuid'),
'host_name': attachment.get('attached_host'),
'device': attachment.get('mountpoint'),
'attached_at': attachment.get('attach_time'),
}
attachments.append(a)
return attachments
def _get_volume_metadata(self, volume):
"""Retrieve the metadata of the volume object."""
return volume.metadata
def _get_volume_type(self, volume):
"""Retrieve the type the volume object."""
if volume['volume_type_id'] and volume.get('volume_type'):
return volume['volume_type']['name']
else:
return volume['volume_type_id']
def _list_view(self, func, request, volumes, volume_count,
coll_name=_collection_name):
"""Provide a view for a list of volumes.
:param func: Function used to format the volume data
:param request: API request
:param volumes: List of volumes in dictionary format
:param volume_count: Length of the original list of volumes
:param coll_name: Name of collection, used to generate the next link
for a pagination query
:returns: Volume data in dictionary format
"""
volumes_list = [func(request, volume)['volume'] for volume in volumes]
volumes_links = self._get_collection_links(request,
volumes,
coll_name,
volume_count)
volumes_dict = dict(volumes=volumes_list)
if volumes_links:
volumes_dict['volumes_links'] = volumes_links
return volumes_dict