nova/nova/tests/functional/test_multiattach.py
Ildiko Vancsa 7e6ae9afd9 [api] Allow multi-attach in compute api
This change introduces a new microversion which must be used
to create a server from a multiattach volume or attach a multiattach
volume to an existing server instance.

Attaching a multiattach volume to a shelved offloaded instance is not
supported since an instance in that state does not have a compute host
so we can't tell if the compute would support the multiattach volume
or not. This is consistent with the tagged attach validation with 2.49.

When creating a server from a multiattach volume, we'll check to see
if all computes in all cells are upgraded to the point of even supporting
the compute side changes, otherwise the server create request fails with
a 409. We do this because we don't know which compute node the scheduler
will pick and we don't have any compute capability filtering in the
scheduler for multiattach volumes (that may be a future improvement).

Similarly, when attaching a multiattach volume to an existing instance,
if the compute isn't new enough to support multiattach or the virt
driver simply doesn't support the capability, a 409 response is returned.
Presumably, operators will use AZs/aggregates to organize which hosts
support multiattach if they have a mixed hypervisor deployment, or will
simply disable multiattach support via Cinder policy.

The unit tests are covering error conditions with the new flow. A new
functional scenario test is added for happy path testing of the new boot
from multiattach volume flow and attaching a multiattach volume to more
than one instance.

Tempest integration testing for multiattach is added in change
I80c20914c03d7371e798ca3567c37307a0d54aaa.

Devstack support for multiattach is added in change
I46b7eabf6a28f230666f6933a087f73cb4408348.

Co-Authored-By: Matt Riedemann <mriedem.os@gmail.com>

Implements: blueprint multi-attach-volume
Change-Id: I02120ef8767c3f9c9497bff67101e57e204ed6f4
2018-01-22 10:45:13 -05:00

81 lines
3.7 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova.tests import fixtures as nova_fixtures
from nova.tests.functional import integrated_helpers
class TestMultiattachVolumes(integrated_helpers._IntegratedTestBase,
integrated_helpers.InstanceHelperMixin):
"""Functional tests for creating a server from a multiattach volume
and attaching a multiattach volume to a server.
Uses the CinderFixtureNewAttachFlow fixture with a specific volume ID
to represent a multiattach volume.
"""
# These are all used in _IntegratedTestBase.
USE_NEUTRON = True
api_major_version = 'v2.1'
microversion = '2.60'
_image_ref_parameter = 'imageRef'
_flavor_ref_parameter = 'flavorRef'
def setUp(self):
# Everything has been upgraded to the latest code to support
# multiattach.
self.useFixture(nova_fixtures.AllServicesCurrent())
super(TestMultiattachVolumes, self).setUp()
self.useFixture(nova_fixtures.CinderFixtureNewAttachFlow(self))
self.useFixture(nova_fixtures.NeutronFixture(self))
def test_boot_from_volume_and_attach_to_second_server(self):
"""This scenario creates a server from the multiattach volume, waits
for it to be ACTIVE, and then attaches the volume to another server.
"""
volume_id = nova_fixtures.CinderFixtureNewAttachFlow.MULTIATTACH_VOL
create_req = self._build_server(flavor_id='1', image='')
create_req['networks'] = 'none'
create_req['block_device_mapping_v2'] = [{
'uuid': volume_id,
'source_type': 'volume',
'destination_type': 'volume',
'delete_on_termination': False,
'boot_index': 0
}]
server = self.api.post_server({'server': create_req})
self._wait_for_state_change(self.api, server, 'ACTIVE')
# Make sure the volume is attached to the first server.
attachments = self.api.api_get(
'/servers/%s/os-volume_attachments' % server['id']).body[
'volumeAttachments']
self.assertEqual(1, len(attachments))
self.assertEqual(server['id'], attachments[0]['serverId'])
self.assertEqual(volume_id, attachments[0]['volumeId'])
# Now create a second server and attach the same volume to that.
create_req = self._build_server(
flavor_id='1', image='155d900f-4e14-4e4c-a73d-069cbf4541e6')
create_req['networks'] = 'none'
server2 = self.api.post_server({'server': create_req})
self._wait_for_state_change(self.api, server2, 'ACTIVE')
# Attach the volume to the second server.
self.api.api_post('/servers/%s/os-volume_attachments' % server2['id'],
{'volumeAttachment': {'volumeId': volume_id}})
# Make sure the volume is attached to the second server.
attachments = self.api.api_get(
'/servers/%s/os-volume_attachments' % server2['id']).body[
'volumeAttachments']
self.assertEqual(1, len(attachments))
self.assertEqual(server2['id'], attachments[0]['serverId'])
self.assertEqual(volume_id, attachments[0]['volumeId'])