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.