From 86e1960f99d3163eaead4968cf67fdbf43d8ba76 Mon Sep 17 00:00:00 2001 From: shutingm Date: Mon, 27 May 2019 11:56:05 +0800 Subject: [PATCH] Allow to select multiattach volume that has been attached When one multiattach enable volume is attached to one instance, it can be only attached to another instance by cli. This patch allows users to do so from horizon Part of blueprint multi-attach-volume Change-Id: Ic65d84d00a9113b7ee71f06b9c41cd09dcaa1325 Co-Authored-By: Vishal Manchanda --- .../dashboards/project/instances/forms.py | 11 ++-- .../dashboards/project/instances/tests.py | 53 +++++++++++++++++++ ...ached-volume-support-3d32cde6f296cdd9.yaml | 15 ++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/multi-attached-volume-support-3d32cde6f296cdd9.yaml diff --git a/openstack_dashboard/dashboards/project/instances/forms.py b/openstack_dashboard/dashboards/project/instances/forms.py index 9e9e244a98..5c8fe1ead7 100644 --- a/openstack_dashboard/dashboards/project/instances/forms.py +++ b/openstack_dashboard/dashboards/project/instances/forms.py @@ -194,15 +194,18 @@ class AttachVolume(forms.SelfHandlingForm): "select a device name.")) instance_id = forms.CharField(widget=forms.HiddenInput()) - def __init__(self, *args, **kwargs): - super(AttachVolume, self).__init__(*args, **kwargs) + def __init__(self, request, *args, **kwargs): + super(AttachVolume, self).__init__(request, *args, **kwargs) # Populate volume choices volume_list = kwargs.get('initial', {}).get("volume_list", []) volumes = [] for volume in volume_list: # Only show volumes that aren't attached to an instance already - if not volume.attachments: + # Or those with multiattach enabled + if (not volume.attachments or + (getattr(volume, 'multiattach', False)) and + api.nova.get_microversion(request, 'multiattach')): volumes.append( (volume.id, '%(name)s (%(id)s)' % {"name": volume.name, "id": volume.id})) @@ -239,7 +242,7 @@ class AttachVolume(forms.SelfHandlingForm): msg = six.text_type(ex) else: # Use a generic error message. - msg = _('Unable to attach volume.') + msg = _('Unable to attach volume: %s') % ex exceptions.handle(request, msg, redirect=redirect) return True diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 824acf6cbb..ecc2b45f72 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -30,6 +30,7 @@ from django.test.utils import override_settings from django.urls import reverse from django.utils.http import urlencode import mock +from novaclient import api_versions import six from horizon import exceptions @@ -5665,6 +5666,58 @@ class ConsoleManagerTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase): # the test helpers don't seem to handle this case. mock_client.assert_not_called() + @helpers.create_mocks({ + api.cinder: ('volume_list', + 'volume_get',), + api.nova: ('get_microversion',), + api._nova: ('novaclient',), + }) + def test_multiattach_volume_attach_to_multple_server(self): + # Tests that a multiattach volume must be attached with compute API + # microversion 2.60 and the feature is not available. + server1 = self.servers.list()[0] + server2 = self.servers.list()[1] + volumes = self.cinder_volumes.list() + volume = volumes[1] + volume.multiattach = True + self.mock_volume_list.return_value = volumes + self.mock_volume_get.return_value = volume + self.mock_get_microversion.return_value = api_versions.APIVersion( + '2.60') + + form_data = {"volume": volume.id, + "instance_id": server1.id, + "device": None} + + url = reverse('horizon:project:instances:attach_volume', + args=[server1.id]) + + s1 = self.client.post(url, form_data) + self.assertNoFormErrors(s1) + + form_data = {"volume": volume.id, + "instance_id": server2.id, + "device": None} + + url = reverse('horizon:project:instances:attach_volume', + args=[server2.id]) + + s2 = self.client.post(url, form_data) + self.assertNoFormErrors(s2) + self.mock_volume_list.assert_has_calls([ + mock.call(helpers.IsHttpRequest()), + mock.call(helpers.IsHttpRequest())]) + self.assertEqual(self.mock_volume_list.call_count, 2) + self.mock_volume_get.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), volume.id) + ]) + self.assertEqual(self.mock_volume_get.call_count, 2) + self.mock_get_microversion.assert_has_calls([ + mock.call(helpers.IsHttpRequest(), 'multiattach') + + ]) + self.assertEqual(self.mock_get_microversion.call_count, 2) + @helpers.create_mocks({api.nova: ('instance_volumes_list',)}) def test_volume_detach_get(self): server = self.servers.first() diff --git a/releasenotes/notes/multi-attached-volume-support-3d32cde6f296cdd9.yaml b/releasenotes/notes/multi-attached-volume-support-3d32cde6f296cdd9.yaml new file mode 100644 index 0000000000..21fd9a4a25 --- /dev/null +++ b/releasenotes/notes/multi-attached-volume-support-3d32cde6f296cdd9.yaml @@ -0,0 +1,15 @@ +--- +features: + - | + [:blueprint:`multi-Attached-volume-support`] + Horizon now support Multi-Attached volume. + User is now able to attach a volume to multiple + instances. The ability to attach a volume to + multiple host/servers requires that the volume + type includes an extra-spec capability setting of + multiattach= True. Horizon automatically detects + and enable multi-attach-volume feature. + + API restrictions: + Multiattach capable volumes can only be attached + with nova API microversion 2.60 or later.