Merge "Use microversion 2.60 when attaching a multiattach volume"
This commit is contained in:
@@ -90,7 +90,8 @@ class Volume(BaseCinderAPIResourceWrapper):
|
|||||||
'snapshot_id', 'source_volid', 'attachments', 'tenant_name',
|
'snapshot_id', 'source_volid', 'attachments', 'tenant_name',
|
||||||
'consistencygroup_id', 'os-vol-host-attr:host',
|
'consistencygroup_id', 'os-vol-host-attr:host',
|
||||||
'os-vol-tenant-attr:tenant_id', 'metadata',
|
'os-vol-tenant-attr:tenant_id', 'metadata',
|
||||||
'volume_image_metadata', 'encrypted', 'transfer']
|
'volume_image_metadata', 'encrypted', 'transfer',
|
||||||
|
'multiattach']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_bootable(self):
|
def is_bootable(self):
|
||||||
|
@@ -32,7 +32,8 @@ MICROVERSION_FEATURES = {
|
|||||||
"instance_description": ["2.19", "2.42"],
|
"instance_description": ["2.19", "2.42"],
|
||||||
"remote_console_mks": ["2.8", "2.53"],
|
"remote_console_mks": ["2.8", "2.53"],
|
||||||
"servergroup_soft_policies": ["2.15", "2.60"],
|
"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": {
|
"cinder": {
|
||||||
"consistency_groups": ["2.0", "3.10"],
|
"consistency_groups": ["2.0", "3.10"],
|
||||||
|
@@ -70,6 +70,10 @@ def is_feature_available(request, features):
|
|||||||
return bool(get_microversion(request, features))
|
return bool(get_microversion(request, features))
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeMultiattachNotSupported(horizon_exceptions.HorizonException):
|
||||||
|
status_code = 400
|
||||||
|
|
||||||
|
|
||||||
class VNCConsole(base.APIDictWrapper):
|
class VNCConsole(base.APIDictWrapper):
|
||||||
"""Wrapper for the "console" dictionary.
|
"""Wrapper for the "console" dictionary.
|
||||||
|
|
||||||
@@ -792,9 +796,20 @@ def get_password(request, instance_id, private_key=None):
|
|||||||
|
|
||||||
@profiler.trace
|
@profiler.trace
|
||||||
def instance_volume_attach(request, volume_id, instance_id, device):
|
def instance_volume_attach(request, volume_id, instance_id, device):
|
||||||
return novaclient(request).volumes.create_server_volume(instance_id,
|
from openstack_dashboard.api import cinder
|
||||||
volume_id,
|
# If we have a multiattach volume, we need to use microversion>=2.60.
|
||||||
device)
|
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
|
@profiler.trace
|
||||||
|
@@ -18,6 +18,7 @@ from django.urls import reverse
|
|||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.decorators.debug import sensitive_variables
|
from django.views.decorators.debug import sensitive_variables
|
||||||
|
import six
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
@@ -231,11 +232,15 @@ class AttachVolume(forms.SelfHandlingForm):
|
|||||||
"inst": instance_id,
|
"inst": instance_id,
|
||||||
"dev": attach.device}
|
"dev": attach.device}
|
||||||
messages.info(request, message)
|
messages.info(request, message)
|
||||||
except Exception:
|
except Exception as ex:
|
||||||
redirect = reverse('horizon:project:instances:index')
|
redirect = reverse('horizon:project:instances:index')
|
||||||
exceptions.handle(request,
|
if isinstance(ex, api.nova.VolumeMultiattachNotSupported):
|
||||||
_('Unable to attach volume.'),
|
# Use the specific error from the specific message.
|
||||||
redirect=redirect)
|
msg = six.text_type(ex)
|
||||||
|
else:
|
||||||
|
# Use a generic error message.
|
||||||
|
msg = _('Unable to attach volume.')
|
||||||
|
exceptions.handle(request, msg, redirect=redirect)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@@ -5215,6 +5215,62 @@ class ConsoleManagerTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
|||||||
self.mock_instance_volume_attach.assert_called_once_with(
|
self.mock_instance_volume_attach.assert_called_once_with(
|
||||||
helpers.IsHttpRequest(), volume.id, server.id, str(None))
|
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',)})
|
@helpers.create_mocks({api.nova: ('instance_volumes_list',)})
|
||||||
def test_volume_detach_get(self):
|
def test_volume_detach_get(self):
|
||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
|
Reference in New Issue
Block a user