blob: d85e4f77477417a0c71d14d330f47e575695f358 [file] [log] [blame]
Matt Riedemannbc8dbd32013-08-02 14:02:12 -07001# Copyright 2013 IBM Corp.
Dan Smithc18d8c62012-07-02 08:09:26 -07002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Steve Noyes83a14122018-01-11 16:12:40 -050016import testtools
17
Sean Dague1937d092013-05-17 16:36:38 -040018from tempest.api.compute import base
lanoux2746ba02016-03-16 17:41:01 +090019from tempest.common import compute
Matt Riedemann81fa9b62016-01-14 13:04:38 -080020from tempest.common import utils
Masayuki Igawa209fd502014-02-17 14:46:43 +090021from tempest.common.utils.linux import remote_client
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000022from tempest.common import waiters
Sean Dague86bd8422013-12-20 09:56:44 -050023from tempest import config
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -080024from tempest.lib import decorators
Dan Smithc18d8c62012-07-02 08:09:26 -070025
Sean Dague86bd8422013-12-20 09:56:44 -050026CONF = config.CONF
27
Dan Smithc18d8c62012-07-02 08:09:26 -070028
Matt Riedemann039740a2018-01-02 15:47:40 -050029class BaseAttachVolumeTest(base.BaseV2ComputeTest):
30 """Base class for the attach volume tests in this module."""
Eric Fried1f546532020-01-14 17:11:58 -060031 create_default_network = True
Dan Smithc18d8c62012-07-02 08:09:26 -070032
Attila Fazekas19044d52013-02-16 07:35:06 +010033 @classmethod
Emily Hugenbruch8284a342014-12-11 22:04:55 +000034 def skip_checks(cls):
Matt Riedemann039740a2018-01-02 15:47:40 -050035 super(BaseAttachVolumeTest, cls).skip_checks()
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000036 if not CONF.service_available.cinder:
Matthew Treinish4c412922013-07-16 15:27:42 -040037 skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
38 raise cls.skipException(skip_msg)
Dan Smithc18d8c62012-07-02 08:09:26 -070039
Emily Hugenbruch8284a342014-12-11 22:04:55 +000040 @classmethod
41 def setup_credentials(cls):
42 cls.prepare_instance_network()
Matt Riedemann039740a2018-01-02 15:47:40 -050043 super(BaseAttachVolumeTest, cls).setup_credentials()
Emily Hugenbruch8284a342014-12-11 22:04:55 +000044
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020045 def _create_server(self):
Dan Smithc18d8c62012-07-02 08:09:26 -070046 # Start a server and wait for it to become ready
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010047 validation_resources = self.get_test_validation_resources(
48 self.os_primary)
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020049 server = self.create_test_server(
Joseph Lanouxffe09dd2015-03-18 16:45:33 +000050 validatable=True,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010051 validation_resources=validation_resources,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +000052 wait_until='ACTIVE',
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020053 adminPass=self.image_ssh_password)
Kevin_Zheng7a547df2017-04-27 18:00:13 +080054 self.addCleanup(self.delete_server, server['id'])
Dan Smithc18d8c62012-07-02 08:09:26 -070055 # Record addresses so that we can ssh later
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020056 server['addresses'] = self.servers_client.list_addresses(
57 server['id'])['addresses']
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010058 return server, validation_resources
Dan Smithc18d8c62012-07-02 08:09:26 -070059
Matt Riedemann039740a2018-01-02 15:47:40 -050060
61class AttachVolumeTestJSON(BaseAttachVolumeTest):
zhuflb5603bc2020-05-27 09:18:24 +080062 """Test attaching volume to server"""
Matt Riedemann039740a2018-01-02 15:47:40 -050063
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -080064 @decorators.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
Matt Riedemann99683152019-02-14 14:35:38 -050065 # This test is conditionally marked slow if SSH validation is enabled.
66 @decorators.attr(type='slow', condition=CONF.validation.run_validation)
Dan Smithc18d8c62012-07-02 08:09:26 -070067 def test_attach_detach_volume(self):
zhuflb5603bc2020-05-27 09:18:24 +080068 """Test attaching and detaching volume from server
69
70 Stop and Start a server with an attached volume, ensuring that
71 the volume remains attached.
72 """
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010073 server, validation_resources = self._create_server()
Andrea Frittolicec44942017-03-24 14:44:19 +000074
75 # NOTE(andreaf) Create one remote client used throughout the test.
76 if CONF.validation.run_validation:
77 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010078 self.get_server_ip(server, validation_resources),
Andrea Frittolicec44942017-03-24 14:44:19 +000079 self.image_ssh_user,
80 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010081 validation_resources['keypair']['private_key'],
Andrea Frittolicec44942017-03-24 14:44:19 +000082 server=server,
83 servers_client=self.servers_client)
84 # NOTE(andreaf) We need to ensure the ssh key has been
85 # injected in the guest before we power cycle
86 linux_client.validate_authentication()
Paras Babbar4b45f9e2019-12-11 16:51:57 -050087 disks_before_attach = linux_client.list_disks()
Andrea Frittolicec44942017-03-24 14:44:19 +000088
Benny Kopilov5c5f7d82016-09-13 14:19:53 +030089 volume = self.create_volume()
zhufl2c9d9622018-08-30 16:03:07 +080090
91 # NOTE: As of the 12.0.0 Liberty release, the Nova libvirt driver
Paras Babbarf1185652019-11-15 16:55:45 -050092 # no longer honors a user-supplied device name, and there can be
93 # a mismatch between libvirt provide disk name and actual disk name
94 # on instance, hence we no longer validate this test with the supplied
95 # device name rather we count number of disk before attach
96 # detach to validate the testcase.
97
98 attachment = self.attach_volume(server, volume)
ivan-zhu2f54b282013-03-11 16:39:25 +080099
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200100 self.servers_client.stop_server(server['id'])
101 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000102 'SHUTOFF')
Dan Smithc18d8c62012-07-02 08:09:26 -0700103
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200104 self.servers_client.start_server(server['id'])
105 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000106 'ACTIVE')
Dan Smithc18d8c62012-07-02 08:09:26 -0700107
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100108 if CONF.validation.run_validation:
Paras Babbar4b45f9e2019-12-11 16:51:57 -0500109 disks_after_attach = linux_client.list_disks()
110 self.assertGreater(
111 len(disks_after_attach),
112 len(disks_before_attach))
Dan Smithc18d8c62012-07-02 08:09:26 -0700113
zhufl36f0a972017-02-28 15:43:33 +0800114 self.servers_client.detach_volume(server['id'], attachment['volumeId'])
115 waiters.wait_for_volume_resource_status(
116 self.volumes_client, attachment['volumeId'], 'available')
117
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200118 self.servers_client.stop_server(server['id'])
119 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000120 'SHUTOFF')
Dan Smithc18d8c62012-07-02 08:09:26 -0700121
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200122 self.servers_client.start_server(server['id'])
123 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000124 'ACTIVE')
Dan Smithc18d8c62012-07-02 08:09:26 -0700125
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100126 if CONF.validation.run_validation:
Paras Babbar4b45f9e2019-12-11 16:51:57 -0500127 disks_after_detach = linux_client.list_disks()
128 self.assertEqual(len(disks_before_attach), len(disks_after_detach))
Dan Smith1ced8422012-08-16 10:35:19 -0700129
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800130 @decorators.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
Ghanshyam5c2a5582014-04-14 17:16:57 +0900131 def test_list_get_volume_attachments(self):
zhuflb5603bc2020-05-27 09:18:24 +0800132 """Test listing and getting volume attachments
133
134 First we attach one volume to the server, check listing and getting
135 the volume attachment of the server. Then we attach another volume to
136 the server, check listing and getting the volume attachments of the
137 server. Finally we detach the volumes from the server one by one.
138 """
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300139 # List volume attachment of the server
afazekas5d066232019-03-25 18:35:36 +0100140 server, validation_resources = self._create_server()
zhufl16dd62c2017-05-03 15:46:16 +0800141 volume_1st = self.create_volume()
zhufl2c9d9622018-08-30 16:03:07 +0800142 attachment_1st = self.attach_volume(server, volume_1st)
David Kranz3ebc7212015-02-10 12:19:19 -0500143 body = self.servers_client.list_volume_attachments(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200144 server['id'])['volumeAttachments']
Ghanshyam5c2a5582014-04-14 17:16:57 +0900145 self.assertEqual(1, len(body))
zhufl16dd62c2017-05-03 15:46:16 +0800146 self.assertIn(attachment_1st, body)
Ghanshyam5c2a5582014-04-14 17:16:57 +0900147
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300148 # Get volume attachment of the server
Ken'ichi Ohmichi277d1882015-11-20 00:44:06 +0000149 body = self.servers_client.show_volume_attachment(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200150 server['id'],
zhufl16dd62c2017-05-03 15:46:16 +0800151 attachment_1st['id'])['volumeAttachment']
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200152 self.assertEqual(server['id'], body['serverId'])
zhufl16dd62c2017-05-03 15:46:16 +0800153 self.assertEqual(volume_1st['id'], body['volumeId'])
154 self.assertEqual(attachment_1st['id'], body['id'])
lanoux2746ba02016-03-16 17:41:01 +0900155
zhufl16dd62c2017-05-03 15:46:16 +0800156 # attach one more volume to server
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800157 volume_2nd = self.create_volume()
zhufl36f0a972017-02-28 15:43:33 +0800158 attachment_2nd = self.attach_volume(server, volume_2nd)
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800159 body = self.servers_client.list_volume_attachments(
160 server['id'])['volumeAttachments']
161 self.assertEqual(2, len(body))
162
afazekas5d066232019-03-25 18:35:36 +0100163 if CONF.validation.run_validation:
164 linux_client = remote_client.RemoteClient(
165 self.get_server_ip(server, validation_resources),
166 self.image_ssh_user,
167 self.image_ssh_password,
168 validation_resources['keypair']['private_key'],
169 server=server,
170 servers_client=self.servers_client)
171 linux_client.validate_authentication()
172
zhufl16dd62c2017-05-03 15:46:16 +0800173 for attachment in [attachment_1st, attachment_2nd]:
174 body = self.servers_client.show_volume_attachment(
175 server['id'], attachment['id'])['volumeAttachment']
176 self.assertEqual(server['id'], body['serverId'])
177 self.assertEqual(attachment['volumeId'], body['volumeId'])
178 self.assertEqual(attachment['id'], body['id'])
Steve Noyes5026c502017-08-10 11:12:31 -0400179 self.servers_client.detach_volume(server['id'],
180 attachment['volumeId'])
181 waiters.wait_for_volume_resource_status(
182 self.volumes_client, attachment['volumeId'], 'available')
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800183
lanoux2746ba02016-03-16 17:41:01 +0900184
Matt Riedemann039740a2018-01-02 15:47:40 -0500185class AttachVolumeShelveTestJSON(BaseAttachVolumeTest):
lanoux2746ba02016-03-16 17:41:01 +0900186 """Testing volume with shelved instance.
187
188 This test checks the attaching and detaching volumes from
Tianbiao Qi0d1d24e2016-09-28 14:17:12 +0800189 a shelved or shelved offload instance.
Matt Riedemannb5720532018-09-19 16:02:05 -0400190
191 Note that these are uncommon scenarios until blueprint detach-boot-volume
192 is implemented in the compute service.
lanoux2746ba02016-03-16 17:41:01 +0900193 """
194
195 min_microversion = '2.20'
196 max_microversion = 'latest'
197
zhufl11289db2017-08-29 10:59:39 +0800198 @classmethod
199 def skip_checks(cls):
200 super(AttachVolumeShelveTestJSON, cls).skip_checks()
201 if not CONF.compute_feature_enabled.shelve:
202 raise cls.skipException('Shelve is not available.')
203
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100204 def _count_volumes(self, server, validation_resources):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200205 # Count number of volumes on an instance
206 volumes = 0
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100207 if CONF.validation.run_validation:
208 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100209 self.get_server_ip(server, validation_resources),
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100210 self.image_ssh_user,
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200211 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100212 validation_resources['keypair']['private_key'],
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200213 server=server,
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100214 servers_client=self.servers_client)
lanoux2746ba02016-03-16 17:41:01 +0900215
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200216 command = 'grep -c -E [vs]d.$ /proc/partitions'
217 volumes = int(linux_client.exec_command(command).strip())
218 return volumes
219
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100220 def _shelve_server(self, server, validation_resources):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200221 # NOTE(andreaf) If we are going to shelve a server, we should
222 # check first whether the server is ssh-able. Otherwise we
223 # won't be able to distinguish failures introduced by shelve
224 # from pre-existing ones. Also it's good to wait for cloud-init
225 # to be done and sshd server to be running before shelving to
226 # avoid breaking the VM
227 if CONF.validation.run_validation:
228 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100229 self.get_server_ip(server, validation_resources),
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200230 self.image_ssh_user,
231 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100232 validation_resources['keypair']['private_key'],
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200233 server=server,
234 servers_client=self.servers_client)
235 linux_client.validate_authentication()
236
237 # If validation went ok, or it was skipped, shelve the server
238 compute.shelve_server(self.servers_client, server['id'])
239
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100240 def _unshelve_server_and_check_volumes(self, server,
241 validation_resources,
242 number_of_volumes):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200243 # Unshelve the instance and check that there are expected volumes
244 self.servers_client.unshelve_server(server['id'])
245 waiters.wait_for_server_status(self.servers_client,
246 server['id'],
247 'ACTIVE')
248 if CONF.validation.run_validation:
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100249 counted_volumes = self._count_volumes(
250 server, validation_resources)
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200251 self.assertEqual(number_of_volumes, counted_volumes)
lanoux2746ba02016-03-16 17:41:01 +0900252
Matt Riedemannb5720532018-09-19 16:02:05 -0400253 # NOTE(mriedem): Marked as slow since this is an uncommon scenario until
254 # attach/detach root volume is supported in nova, and it's slow.
255 @decorators.attr(type='slow')
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800256 @decorators.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
lanoux2746ba02016-03-16 17:41:01 +0900257 def test_attach_volume_shelved_or_offload_server(self):
zhuflb5603bc2020-05-27 09:18:24 +0800258 """Test attaching volume to shelved server
259
260 Create server, count number of volumes on it, shelve
261 server and attach pre-created volume to shelved server, then
262 unshelve the server and check that attached volume exists.
263 """
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100264 server, validation_resources = self._create_server()
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300265 volume = self.create_volume()
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100266 num_vol = self._count_volumes(server, validation_resources)
267 self._shelve_server(server, validation_resources)
zhufl2c9d9622018-08-30 16:03:07 +0800268 attachment = self.attach_volume(server, volume)
lanoux2746ba02016-03-16 17:41:01 +0900269
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200270 # Unshelve the instance and check that attached volume exists
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100271 self._unshelve_server_and_check_volumes(
272 server, validation_resources, num_vol + 1)
lanoux2746ba02016-03-16 17:41:01 +0900273
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300274 # Get volume attachment of the server
lanoux2746ba02016-03-16 17:41:01 +0900275 volume_attachment = self.servers_client.show_volume_attachment(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200276 server['id'],
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300277 attachment['id'])['volumeAttachment']
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200278 self.assertEqual(server['id'], volume_attachment['serverId'])
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300279 self.assertEqual(attachment['id'], volume_attachment['id'])
lanoux2746ba02016-03-16 17:41:01 +0900280 # Check the mountpoint is not None after unshelve server even in
281 # case of shelved_offloaded.
282 self.assertIsNotNone(volume_attachment['device'])
283
Matt Riedemannb5720532018-09-19 16:02:05 -0400284 # NOTE(mriedem): Marked as slow since this is an uncommon scenario until
285 # attach/detach root volume is supported in nova, and it's slow.
286 @decorators.attr(type='slow')
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800287 @decorators.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
lanoux2746ba02016-03-16 17:41:01 +0900288 def test_detach_volume_shelved_or_offload_server(self):
zhuflb5603bc2020-05-27 09:18:24 +0800289 """Test detaching volume from shelved server
290
291 Count number of volumes on server, shelve server and attach
292 pre-created volume to shelved server, then detach the volume, unshelve
293 the instance and check that we have the expected number of volume(s).
294 """
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100295 server, validation_resources = self._create_server()
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300296 volume = self.create_volume()
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100297 num_vol = self._count_volumes(server, validation_resources)
298 self._shelve_server(server, validation_resources)
zhufl36f0a972017-02-28 15:43:33 +0800299
300 # Attach and then detach the volume
zhufl2c9d9622018-08-30 16:03:07 +0800301 self.attach_volume(server, volume)
zhufl36f0a972017-02-28 15:43:33 +0800302 self.servers_client.detach_volume(server['id'], volume['id'])
303 waiters.wait_for_volume_resource_status(self.volumes_client,
304 volume['id'], 'available')
lanoux2746ba02016-03-16 17:41:01 +0900305
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200306 # Unshelve the instance and check that we have the expected number of
307 # volume(s)
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100308 self._unshelve_server_and_check_volumes(
309 server, validation_resources, num_vol)
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800310
311
312class AttachVolumeMultiAttachTest(BaseAttachVolumeTest):
zhuflb5603bc2020-05-27 09:18:24 +0800313 """Test attaching one volume to multiple servers
314
315 Test attaching one volume to multiple servers with compute
316 microversion greater than 2.59.
317 """
318
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800319 min_microversion = '2.60'
320 max_microversion = 'latest'
321
322 @classmethod
323 def skip_checks(cls):
324 super(AttachVolumeMultiAttachTest, cls).skip_checks()
325 if not CONF.compute_feature_enabled.volume_multiattach:
326 raise cls.skipException('Volume multi-attach is not available.')
327
328 def _attach_volume_to_servers(self, volume, servers):
329 """Attaches the given volume to the list of servers.
330
331 :param volume: The multiattach volume to use.
332 :param servers: list of server instances on which the volume will be
333 attached
334 :returns: dict of server ID to volumeAttachment dict entries
335 """
336 attachments = {}
337 for server in servers:
338 # map the server id to the volume attachment
339 attachments[server['id']] = self.attach_volume(server, volume)
340 # NOTE(mriedem): In the case of multi-attach, after the first
341 # attach the volume will be in-use. On the second attach, nova will
342 # 'reserve' the volume which puts it back into 'attaching' status
343 # and then the volume shouldn't go back to in-use until the compute
344 # actually attaches the server to the volume.
345 return attachments
346
347 def _detach_multiattach_volume(self, volume_id, server_id):
348 """Detaches a multiattach volume from the given server.
349
350 Depending on the number of attachments the volume has, this method
351 will wait for the volume to go to back to 'in-use' status if there are
352 more attachments or 'available' state if there are no more attachments.
353 """
354 # Count the number of attachments before starting the detach.
355 volume = self.volumes_client.show_volume(volume_id)['volume']
356 attachments = volume['attachments']
357 wait_status = 'in-use' if len(attachments) > 1 else 'available'
358 # Now detach the volume from the given server.
359 self.servers_client.detach_volume(server_id, volume_id)
360 # Now wait for the volume status to change.
361 waiters.wait_for_volume_resource_status(
362 self.volumes_client, volume_id, wait_status)
363
364 def _create_multiattach_volume(self, bootable=False):
365 kwargs = {}
366 if bootable:
367 kwargs['image_ref'] = CONF.compute.image_ref
368 return self.create_volume(multiattach=True, **kwargs)
369
370 def _create_and_multiattach(self):
371 """Creates two server instances and a volume and attaches to both.
372
373 :returns: A three-item tuple of the list of created servers,
374 the created volume, and dict of server ID to volumeAttachment
375 dict entries
376 """
377 servers = []
378 for x in range(2):
379 name = 'multiattach-server-%i' % x
380 servers.append(self.create_test_server(name=name))
381
382 # Now wait for the servers to be ACTIVE.
383 for server in servers:
384 waiters.wait_for_server_status(self.servers_client, server['id'],
385 'ACTIVE')
386
387 volume = self._create_multiattach_volume()
388
389 # Attach the volume to the servers
390 attachments = self._attach_volume_to_servers(volume, servers)
391 return servers, volume, attachments
392
393 @decorators.idempotent_id('8d5853f7-56e7-4988-9b0c-48cea3c7049a')
394 def test_list_get_volume_attachments_multiattach(self):
zhuflb5603bc2020-05-27 09:18:24 +0800395 """Test listing and getting multiattached volume attachments
396
397 Attach a single volume to two servers, list attachments from the
398 volume and make sure the server uuids are in the list, then detach
399 the volume from servers one by one.
400 """
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800401 # Attach a single volume to two servers.
402 servers, volume, attachments = self._create_and_multiattach()
403
404 # List attachments from the volume and make sure the server uuids
405 # are in that list.
406 vol_attachments = self.volumes_client.show_volume(
407 volume['id'])['volume']['attachments']
408 attached_server_ids = [attachment['server_id']
409 for attachment in vol_attachments]
410 self.assertEqual(2, len(attached_server_ids))
411
412 # List Volume attachment of the servers
413 for server in servers:
414 self.assertIn(server['id'], attached_server_ids)
415 vol_attachments = self.servers_client.list_volume_attachments(
416 server['id'])['volumeAttachments']
417 self.assertEqual(1, len(vol_attachments))
418 attachment = attachments[server['id']]
419 self.assertDictEqual(attachment, vol_attachments[0])
420 # Detach the volume from this server.
421 self._detach_multiattach_volume(volume['id'], server['id'])
422
423 def _boot_from_multiattach_volume(self):
424 """Boots a server from a multiattach volume.
425
426 The volume will not be deleted when the server is deleted.
427
428 :returns: 2-item tuple of (server, volume)
429 """
430 volume = self._create_multiattach_volume(bootable=True)
431 # Now create a server from the bootable volume.
432 bdm = [{
433 'uuid': volume['id'],
434 'source_type': 'volume',
435 'destination_type': 'volume',
436 'boot_index': 0,
437 'delete_on_termination': False}]
438 server = self.create_test_server(
439 image_id='', block_device_mapping_v2=bdm, wait_until='ACTIVE')
440 # Assert the volume is attached to the server.
441 attachments = self.servers_client.list_volume_attachments(
442 server['id'])['volumeAttachments']
443 self.assertEqual(1, len(attachments))
444 self.assertEqual(volume['id'], attachments[0]['volumeId'])
445 return server, volume
446
447 @decorators.idempotent_id('65e33aa2-185b-44c8-b22e-e524973ed625')
448 def test_boot_from_multiattach_volume(self):
449 """Simple test to boot an instance from a multiattach volume."""
450 self._boot_from_multiattach_volume()
451
452 @utils.services('image')
453 @decorators.idempotent_id('885ac48a-2d7a-40c5-ae8b-1993882d724c')
454 def test_snapshot_volume_backed_multiattach(self):
455 """Boots a server from a multiattach volume and snapshots the server.
456
457 Creating the snapshot of the server will also create a snapshot of
458 the volume.
459 """
460 server, volume = self._boot_from_multiattach_volume()
461 # Create a snapshot of the server (and volume implicitly).
462 self.create_image_from_server(
463 server['id'], name='multiattach-snapshot',
464 wait_until='active', wait_for_server=True)
465 # TODO(mriedem): Make sure the volume snapshot exists. This requires
466 # adding the volume snapshots client to BaseV2ComputeTest.
467 # Delete the server, wait for it to be gone, and make sure the volume
468 # still exists.
469 self.servers_client.delete_server(server['id'])
470 waiters.wait_for_server_termination(self.servers_client, server['id'])
471 # Delete the volume and cascade the delete of the volume snapshot.
472 self.volumes_client.delete_volume(volume['id'], cascade=True)
473 # Now we have to wait for the volume to be gone otherwise the normal
474 # teardown will fail since it will race with our call and the snapshot
475 # might still exist.
476 self.volumes_client.wait_for_resource_deletion(volume['id'])
477
Steve Noyes83a14122018-01-11 16:12:40 -0500478 @decorators.idempotent_id('f01c7169-a124-4fc7-ae60-5e380e247c9c')
479 @testtools.skipUnless(CONF.compute_feature_enabled.resize,
480 'Resize not available.')
481 def test_resize_server_with_multiattached_volume(self):
zhuflb5603bc2020-05-27 09:18:24 +0800482 """Test resizing servers with multiattached volume
483
484 Attach a single volume to multiple servers, then resize the servers
485 """
Steve Noyes83a14122018-01-11 16:12:40 -0500486 servers, volume, _ = self._create_and_multiattach()
487
488 for server in servers:
489 self.resize_server(server['id'], self.flavor_ref_alt)
490
491 for server in servers:
492 self._detach_multiattach_volume(volume['id'], server['id'])
493
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800494 # TODO(mriedem): Might be interesting to create a bootable multiattach
495 # volume with delete_on_termination=True, create server1 from the
496 # volume, then attach it to server2, and then delete server1 in which
497 # case the volume won't be deleted because it's still attached to
498 # server2 and make sure the volume is still attached to server2.