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:
parent
f545272f12
commit
1e2dfc4bb4
|
@ -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):
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue