Use the correct attachment id for multiattach.
A volume attached to multiple servers has multiple attachment structures
that are distinguished by their "attachment_id" field, not the "id" one.
So when setting up a waiter watching for an attachment being removed,
the "attachment_id" field must be passed. Thus, when attaching a volume,
it is necessary to wait until the attachment shows up in the volume's
data and then use the "attachment_id" field of that record to pass to
the waiter invoked at tear-down time.
Change-Id: I9ab9f786b23061dd3a6a3482ab9739ba504d2bc0
Closes-Bug: 1894724
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 74570ce..8b847fc 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -566,17 +566,19 @@
# the state of the volume to change to available. This is so we don't
# error out when trying to delete the volume during teardown.
if volume['multiattach']:
+ att = waiters.wait_for_volume_attachment_create(
+ self.volumes_client, volume['id'], server['id'])
self.addCleanup(waiters.wait_for_volume_attachment_remove,
self.volumes_client, volume['id'],
- attachment['id'])
+ att['attachment_id'])
else:
self.addCleanup(waiters.wait_for_volume_resource_status,
self.volumes_client, volume['id'], 'available')
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ volume['id'], 'in-use')
# Ignore 404s on detach in case the server is deleted or the volume
# is already detached.
self.addCleanup(self._detach_volume, server, volume)
- waiters.wait_for_volume_resource_status(self.volumes_client,
- volume['id'], 'in-use')
return attachment
def create_volume_snapshot(self, volume_id, name=None, description=None,
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 14790d6..fc25914 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -223,6 +223,25 @@
resource_name, resource_id, status, time.time() - start)
+def wait_for_volume_attachment_create(client, volume_id, server_id):
+ """Waits for a volume attachment to be created at a given volume."""
+ start = int(time.time())
+ while True:
+ attachments = client.show_volume(volume_id)['volume']['attachments']
+ found = [a for a in attachments if a['server_id'] == server_id]
+ if found:
+ LOG.info('Attachment %s created for volume %s to server %s after '
+ 'waiting for %f seconds', found[0]['attachment_id'],
+ volume_id, server_id, time.time() - start)
+ return found[0]
+ time.sleep(client.build_interval)
+ if int(time.time()) - start >= client.build_timeout:
+ message = ('Failed to attach volume %s to server %s '
+ 'within the required time (%s s).' %
+ (volume_id, server_id, client.build_timeout))
+ raise lib_exc.TimeoutException(message)
+
+
def wait_for_volume_attachment_remove(client, volume_id, attachment_id):
"""Waits for a volume attachment to be removed from a given volume."""
start = int(time.time())
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index 32d6498..73924bd 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -234,6 +234,29 @@
mock.call(volume_id)])
mock_sleep.assert_called_once_with(1)
+ def test_wait_for_volume_attachment_create(self):
+ vol_detached = {'volume': {'attachments': []}}
+ vol_attached = {'volume': {'attachments': [
+ {'id': uuids.volume_id,
+ 'attachment_id': uuids.attachment_id,
+ 'server_id': uuids.server_id,
+ 'volume_id': uuids.volume_id}]}}
+ show_volume = mock.MagicMock(side_effect=[
+ vol_detached, vol_detached, vol_attached])
+ client = mock.Mock(spec=volumes_client.VolumesClient,
+ build_interval=1,
+ build_timeout=5,
+ show_volume=show_volume)
+ self.patch('time.time')
+ self.patch('time.sleep')
+ att = waiters.wait_for_volume_attachment_create(
+ client, uuids.volume_id, uuids.server_id)
+ assert att == vol_attached['volume']['attachments'][0]
+ # Assert that show volume is called until the attachment is removed.
+ show_volume.assert_has_calls([mock.call(uuids.volume_id),
+ mock.call(uuids.volume_id),
+ mock.call(uuids.volume_id)])
+
def test_wait_for_volume_attachment(self):
vol_detached = {'volume': {'attachments': []}}
vol_attached = {'volume': {'attachments': [