Use microversion 2.60 when attaching a multiattach volume

Multiattach capable volumes can only be attached with microversion
2.60 or later, so this checks if the volume being attached is
multiattach capable and if microversion 2.60 is available, use it.

Part of blueprint multi-attach-volume

Closes-Bug: #1751564

Change-Id: If708e3edb05cff6e93cfc3dfccb91b1441dcd181
This commit is contained in:
Matt Riedemann 2018-02-25 14:38:43 -05:00
parent f545272f12
commit 1e2dfc4bb4
5 changed files with 87 additions and 9 deletions

View File

@ -90,7 +90,8 @@ class Volume(BaseCinderAPIResourceWrapper):
'snapshot_id', 'source_volid', 'attachments', 'tenant_name',
'consistencygroup_id', 'os-vol-host-attr:host',
'os-vol-tenant-attr:tenant_id', 'metadata',
'volume_image_metadata', 'encrypted', 'transfer']
'volume_image_metadata', 'encrypted', 'transfer',
'multiattach']
@property
def is_bootable(self):

View File

@ -32,7 +32,8 @@ MICROVERSION_FEATURES = {
"instance_description": ["2.19", "2.42"],
"remote_console_mks": ["2.8", "2.53"],
"servergroup_soft_policies": ["2.15", "2.60"],
"servergroup_user_info": ["2.13", "2.60"]
"servergroup_user_info": ["2.13", "2.60"],
"multiattach": ["2.60"]
},
"cinder": {
"consistency_groups": ["2.0", "3.10"],

View File

@ -69,6 +69,10 @@ def is_feature_available(request, features):
return bool(get_microversion(request, features))
class VolumeMultiattachNotSupported(horizon_exceptions.HorizonException):
status_code = 400
class VNCConsole(base.APIDictWrapper):
"""Wrapper for the "console" dictionary.
@ -797,9 +801,20 @@ def get_password(request, instance_id, private_key=None):
@profiler.trace
def instance_volume_attach(request, volume_id, instance_id, device):
return novaclient(request).volumes.create_server_volume(instance_id,
volume_id,
device)
from openstack_dashboard.api import cinder
# If we have a multiattach volume, we need to use microversion>=2.60.
volume = cinder.volume_get(request, volume_id)
if volume.multiattach:
version = get_microversion(request, 'multiattach')
if version:
client = novaclient(request, version)
else:
raise VolumeMultiattachNotSupported(
_('Multiattach volumes are not yet supported.'))
else:
client = novaclient(request)
return client.volumes.create_server_volume(
instance_id, volume_id, device)
@profiler.trace

View File

@ -17,6 +17,7 @@ from django.template.defaultfilters import filesizeformat
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.debug import sensitive_variables
import six
from horizon import exceptions
from horizon import forms
@ -230,11 +231,15 @@ class AttachVolume(forms.SelfHandlingForm):
"inst": instance_id,
"dev": attach.device}
messages.info(request, message)
except Exception:
except Exception as ex:
redirect = reverse('horizon:project:instances:index')
exceptions.handle(request,
_('Unable to attach volume.'),
redirect=redirect)
if isinstance(ex, api.nova.VolumeMultiattachNotSupported):
# Use the specific error from the specific message.
msg = six.text_type(ex)
else:
# Use a generic error message.
msg = _('Unable to attach volume.')
exceptions.handle(request, msg, redirect=redirect)
return True

View File

@ -5165,6 +5165,62 @@ class ConsoleManagerTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
self.mock_instance_volume_attach.assert_called_once_with(
helpers.IsHttpRequest(), volume.id, server.id, str(None))
@mock.patch.object(api.cinder, 'volume_list')
@mock.patch.object(api.cinder, 'volume_get')
@mock.patch.object(api.nova, 'get_microversion', return_value='2.60')
@mock.patch.object(api.nova, 'novaclient')
def test_volume_attach_post_multiattach(
self, mock_client, mock_get_microversion, mock_volume_get,
mock_volume_list):
# Tests that a multiattach volume must be attached with compute API
# microversion 2.60 and the feature is supported.
server = self.servers.first()
volumes = self.cinder_volumes.list()
volume = volumes[1]
volume.multiattach = True
mock_volume_list.return_value = volumes
mock_volume_get.return_value = volume
form_data = {"volume": volume.id,
"instance_id": server.id,
"device": None}
url = reverse('horizon:project:instances:attach_volume',
args=[server.id])
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
mock_client.assert_called_once_with(mock.ANY, '2.60')
@mock.patch.object(api.cinder, 'volume_list')
@mock.patch.object(api.cinder, 'volume_get')
@mock.patch.object(api.nova, 'get_microversion', return_value=None)
@mock.patch.object(api.nova, 'novaclient')
def test_volume_attach_post_multiattach_feature_not_available(
self, mock_client, mock_get_microversion, mock_volume_get,
mock_volume_list):
# Tests that a multiattach volume must be attached with compute API
# microversion 2.60 and the feature is not available.
server = self.servers.first()
volumes = self.cinder_volumes.list()
volume = volumes[1]
volume.multiattach = True
mock_volume_list.return_value = volumes
mock_volume_get.return_value = volume
form_data = {"volume": volume.id,
"instance_id": server.id,
"device": None}
url = reverse('horizon:project:instances:attach_volume',
args=[server.id])
self.client.post(url, form_data)
# TODO(mriedem): Assert the actual error from the response but
# the test helpers don't seem to handle this case.
mock_client.assert_not_called()
@helpers.create_mocks({api.nova: ('instance_volumes_list',)})
def test_volume_detach_get(self):
server = self.servers.first()