blob: f83e62c52d63045c7143d502b971a28fb7b9a39d [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."""
Dan Smithc18d8c62012-07-02 08:09:26 -070031
Attila Fazekas19044d52013-02-16 07:35:06 +010032 @classmethod
Emily Hugenbruch8284a342014-12-11 22:04:55 +000033 def skip_checks(cls):
Matt Riedemann039740a2018-01-02 15:47:40 -050034 super(BaseAttachVolumeTest, cls).skip_checks()
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000035 if not CONF.service_available.cinder:
Matthew Treinish4c412922013-07-16 15:27:42 -040036 skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
37 raise cls.skipException(skip_msg)
Dan Smithc18d8c62012-07-02 08:09:26 -070038
Emily Hugenbruch8284a342014-12-11 22:04:55 +000039 @classmethod
40 def setup_credentials(cls):
41 cls.prepare_instance_network()
Matt Riedemann039740a2018-01-02 15:47:40 -050042 super(BaseAttachVolumeTest, cls).setup_credentials()
Emily Hugenbruch8284a342014-12-11 22:04:55 +000043
44 @classmethod
45 def resource_setup(cls):
Matt Riedemann039740a2018-01-02 15:47:40 -050046 super(BaseAttachVolumeTest, cls).resource_setup()
Emily Hugenbruch8284a342014-12-11 22:04:55 +000047 cls.device = CONF.compute.volume_device_name
48
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020049 def _create_server(self):
Dan Smithc18d8c62012-07-02 08:09:26 -070050 # Start a server and wait for it to become ready
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010051 validation_resources = self.get_test_validation_resources(
52 self.os_primary)
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020053 server = self.create_test_server(
Joseph Lanouxffe09dd2015-03-18 16:45:33 +000054 validatable=True,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010055 validation_resources=validation_resources,
Joseph Lanouxffe09dd2015-03-18 16:45:33 +000056 wait_until='ACTIVE',
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020057 adminPass=self.image_ssh_password)
Kevin_Zheng7a547df2017-04-27 18:00:13 +080058 self.addCleanup(self.delete_server, server['id'])
Dan Smithc18d8c62012-07-02 08:09:26 -070059 # Record addresses so that we can ssh later
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020060 server['addresses'] = self.servers_client.list_addresses(
61 server['id'])['addresses']
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010062 return server, validation_resources
Dan Smithc18d8c62012-07-02 08:09:26 -070063
Matt Riedemann039740a2018-01-02 15:47:40 -050064
65class AttachVolumeTestJSON(BaseAttachVolumeTest):
66
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -080067 @decorators.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
Matt Riedemann99683152019-02-14 14:35:38 -050068 # This test is conditionally marked slow if SSH validation is enabled.
69 @decorators.attr(type='slow', condition=CONF.validation.run_validation)
Dan Smithc18d8c62012-07-02 08:09:26 -070070 def test_attach_detach_volume(self):
Sean Dague4dd2c0b2013-01-03 17:50:28 -050071 # Stop and Start a server with an attached volume, ensuring that
72 # the volume remains attached.
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()
87
Benny Kopilov5c5f7d82016-09-13 14:19:53 +030088 volume = self.create_volume()
zhufl2c9d9622018-08-30 16:03:07 +080089
90 # NOTE: As of the 12.0.0 Liberty release, the Nova libvirt driver
91 # no longer honors a user-supplied device name, in that case
92 # CONF.compute.volume_device_name must be set the equal value as
93 # the libvirt auto-assigned one
zhufl36f0a972017-02-28 15:43:33 +080094 attachment = self.attach_volume(server, volume,
95 device=('/dev/%s' % self.device))
ivan-zhu2f54b282013-03-11 16:39:25 +080096
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020097 self.servers_client.stop_server(server['id'])
98 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000099 'SHUTOFF')
Dan Smithc18d8c62012-07-02 08:09:26 -0700100
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200101 self.servers_client.start_server(server['id'])
102 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000103 'ACTIVE')
Dan Smithc18d8c62012-07-02 08:09:26 -0700104
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100105 if CONF.validation.run_validation:
Evgeny Antyshev4894a912016-11-21 12:17:18 +0000106 disks = linux_client.get_disks()
107 device_name_to_match = '\n' + self.device + ' '
108 self.assertIn(device_name_to_match, disks)
Dan Smithc18d8c62012-07-02 08:09:26 -0700109
zhufl36f0a972017-02-28 15:43:33 +0800110 self.servers_client.detach_volume(server['id'], attachment['volumeId'])
111 waiters.wait_for_volume_resource_status(
112 self.volumes_client, attachment['volumeId'], 'available')
113
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200114 self.servers_client.stop_server(server['id'])
115 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000116 'SHUTOFF')
Dan Smithc18d8c62012-07-02 08:09:26 -0700117
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200118 self.servers_client.start_server(server['id'])
119 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000120 'ACTIVE')
Dan Smithc18d8c62012-07-02 08:09:26 -0700121
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100122 if CONF.validation.run_validation:
Evgeny Antyshev4894a912016-11-21 12:17:18 +0000123 disks = linux_client.get_disks()
124 self.assertNotIn(device_name_to_match, disks)
Dan Smith1ced8422012-08-16 10:35:19 -0700125
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800126 @decorators.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
Ghanshyam5c2a5582014-04-14 17:16:57 +0900127 def test_list_get_volume_attachments(self):
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300128 # List volume attachment of the server
afazekas5d066232019-03-25 18:35:36 +0100129 server, validation_resources = self._create_server()
zhufl16dd62c2017-05-03 15:46:16 +0800130 volume_1st = self.create_volume()
zhufl2c9d9622018-08-30 16:03:07 +0800131 attachment_1st = self.attach_volume(server, volume_1st)
David Kranz3ebc7212015-02-10 12:19:19 -0500132 body = self.servers_client.list_volume_attachments(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200133 server['id'])['volumeAttachments']
Ghanshyam5c2a5582014-04-14 17:16:57 +0900134 self.assertEqual(1, len(body))
zhufl16dd62c2017-05-03 15:46:16 +0800135 self.assertIn(attachment_1st, body)
Ghanshyam5c2a5582014-04-14 17:16:57 +0900136
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300137 # Get volume attachment of the server
Ken'ichi Ohmichi277d1882015-11-20 00:44:06 +0000138 body = self.servers_client.show_volume_attachment(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200139 server['id'],
zhufl16dd62c2017-05-03 15:46:16 +0800140 attachment_1st['id'])['volumeAttachment']
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200141 self.assertEqual(server['id'], body['serverId'])
zhufl16dd62c2017-05-03 15:46:16 +0800142 self.assertEqual(volume_1st['id'], body['volumeId'])
143 self.assertEqual(attachment_1st['id'], body['id'])
lanoux2746ba02016-03-16 17:41:01 +0900144
zhufl16dd62c2017-05-03 15:46:16 +0800145 # attach one more volume to server
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800146 volume_2nd = self.create_volume()
zhufl36f0a972017-02-28 15:43:33 +0800147 attachment_2nd = self.attach_volume(server, volume_2nd)
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800148 body = self.servers_client.list_volume_attachments(
149 server['id'])['volumeAttachments']
150 self.assertEqual(2, len(body))
151
afazekas5d066232019-03-25 18:35:36 +0100152 if CONF.validation.run_validation:
153 linux_client = remote_client.RemoteClient(
154 self.get_server_ip(server, validation_resources),
155 self.image_ssh_user,
156 self.image_ssh_password,
157 validation_resources['keypair']['private_key'],
158 server=server,
159 servers_client=self.servers_client)
160 linux_client.validate_authentication()
161
zhufl16dd62c2017-05-03 15:46:16 +0800162 for attachment in [attachment_1st, attachment_2nd]:
163 body = self.servers_client.show_volume_attachment(
164 server['id'], attachment['id'])['volumeAttachment']
165 self.assertEqual(server['id'], body['serverId'])
166 self.assertEqual(attachment['volumeId'], body['volumeId'])
167 self.assertEqual(attachment['id'], body['id'])
Steve Noyes5026c502017-08-10 11:12:31 -0400168 self.servers_client.detach_volume(server['id'],
169 attachment['volumeId'])
170 waiters.wait_for_volume_resource_status(
171 self.volumes_client, attachment['volumeId'], 'available')
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800172
lanoux2746ba02016-03-16 17:41:01 +0900173
Matt Riedemann039740a2018-01-02 15:47:40 -0500174class AttachVolumeShelveTestJSON(BaseAttachVolumeTest):
lanoux2746ba02016-03-16 17:41:01 +0900175 """Testing volume with shelved instance.
176
177 This test checks the attaching and detaching volumes from
Tianbiao Qi0d1d24e2016-09-28 14:17:12 +0800178 a shelved or shelved offload instance.
Matt Riedemannb5720532018-09-19 16:02:05 -0400179
180 Note that these are uncommon scenarios until blueprint detach-boot-volume
181 is implemented in the compute service.
lanoux2746ba02016-03-16 17:41:01 +0900182 """
183
184 min_microversion = '2.20'
185 max_microversion = 'latest'
186
zhufl11289db2017-08-29 10:59:39 +0800187 @classmethod
188 def skip_checks(cls):
189 super(AttachVolumeShelveTestJSON, cls).skip_checks()
190 if not CONF.compute_feature_enabled.shelve:
191 raise cls.skipException('Shelve is not available.')
192
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100193 def _count_volumes(self, server, validation_resources):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200194 # Count number of volumes on an instance
195 volumes = 0
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100196 if CONF.validation.run_validation:
197 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100198 self.get_server_ip(server, validation_resources),
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100199 self.image_ssh_user,
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200200 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100201 validation_resources['keypair']['private_key'],
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200202 server=server,
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100203 servers_client=self.servers_client)
lanoux2746ba02016-03-16 17:41:01 +0900204
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200205 command = 'grep -c -E [vs]d.$ /proc/partitions'
206 volumes = int(linux_client.exec_command(command).strip())
207 return volumes
208
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100209 def _shelve_server(self, server, validation_resources):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200210 # NOTE(andreaf) If we are going to shelve a server, we should
211 # check first whether the server is ssh-able. Otherwise we
212 # won't be able to distinguish failures introduced by shelve
213 # from pre-existing ones. Also it's good to wait for cloud-init
214 # to be done and sshd server to be running before shelving to
215 # avoid breaking the VM
216 if CONF.validation.run_validation:
217 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100218 self.get_server_ip(server, validation_resources),
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200219 self.image_ssh_user,
220 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100221 validation_resources['keypair']['private_key'],
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200222 server=server,
223 servers_client=self.servers_client)
224 linux_client.validate_authentication()
225
226 # If validation went ok, or it was skipped, shelve the server
227 compute.shelve_server(self.servers_client, server['id'])
228
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100229 def _unshelve_server_and_check_volumes(self, server,
230 validation_resources,
231 number_of_volumes):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200232 # Unshelve the instance and check that there are expected volumes
233 self.servers_client.unshelve_server(server['id'])
234 waiters.wait_for_server_status(self.servers_client,
235 server['id'],
236 'ACTIVE')
237 if CONF.validation.run_validation:
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100238 counted_volumes = self._count_volumes(
239 server, validation_resources)
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200240 self.assertEqual(number_of_volumes, counted_volumes)
lanoux2746ba02016-03-16 17:41:01 +0900241
Matt Riedemannb5720532018-09-19 16:02:05 -0400242 # NOTE(mriedem): Marked as slow since this is an uncommon scenario until
243 # attach/detach root volume is supported in nova, and it's slow.
244 @decorators.attr(type='slow')
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800245 @decorators.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
lanoux2746ba02016-03-16 17:41:01 +0900246 def test_attach_volume_shelved_or_offload_server(self):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200247 # Create server, count number of volumes on it, shelve
248 # server and attach pre-created volume to shelved server
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100249 server, validation_resources = self._create_server()
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300250 volume = self.create_volume()
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100251 num_vol = self._count_volumes(server, validation_resources)
252 self._shelve_server(server, validation_resources)
zhufl2c9d9622018-08-30 16:03:07 +0800253 attachment = self.attach_volume(server, volume)
lanoux2746ba02016-03-16 17:41:01 +0900254
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200255 # Unshelve the instance and check that attached volume exists
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100256 self._unshelve_server_and_check_volumes(
257 server, validation_resources, num_vol + 1)
lanoux2746ba02016-03-16 17:41:01 +0900258
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300259 # Get volume attachment of the server
lanoux2746ba02016-03-16 17:41:01 +0900260 volume_attachment = self.servers_client.show_volume_attachment(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200261 server['id'],
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300262 attachment['id'])['volumeAttachment']
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200263 self.assertEqual(server['id'], volume_attachment['serverId'])
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300264 self.assertEqual(attachment['id'], volume_attachment['id'])
lanoux2746ba02016-03-16 17:41:01 +0900265 # Check the mountpoint is not None after unshelve server even in
266 # case of shelved_offloaded.
267 self.assertIsNotNone(volume_attachment['device'])
268
Matt Riedemannb5720532018-09-19 16:02:05 -0400269 # NOTE(mriedem): Marked as slow since this is an uncommon scenario until
270 # attach/detach root volume is supported in nova, and it's slow.
271 @decorators.attr(type='slow')
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800272 @decorators.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
lanoux2746ba02016-03-16 17:41:01 +0900273 def test_detach_volume_shelved_or_offload_server(self):
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300274 # Count number of volumes on instance, shelve
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200275 # server and attach pre-created volume to shelved server
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100276 server, validation_resources = self._create_server()
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300277 volume = self.create_volume()
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100278 num_vol = self._count_volumes(server, validation_resources)
279 self._shelve_server(server, validation_resources)
zhufl36f0a972017-02-28 15:43:33 +0800280
281 # Attach and then detach the volume
zhufl2c9d9622018-08-30 16:03:07 +0800282 self.attach_volume(server, volume)
zhufl36f0a972017-02-28 15:43:33 +0800283 self.servers_client.detach_volume(server['id'], volume['id'])
284 waiters.wait_for_volume_resource_status(self.volumes_client,
285 volume['id'], 'available')
lanoux2746ba02016-03-16 17:41:01 +0900286
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200287 # Unshelve the instance and check that we have the expected number of
288 # volume(s)
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100289 self._unshelve_server_and_check_volumes(
290 server, validation_resources, num_vol)
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800291
292
293class AttachVolumeMultiAttachTest(BaseAttachVolumeTest):
294 min_microversion = '2.60'
295 max_microversion = 'latest'
296
297 @classmethod
298 def skip_checks(cls):
299 super(AttachVolumeMultiAttachTest, cls).skip_checks()
300 if not CONF.compute_feature_enabled.volume_multiattach:
301 raise cls.skipException('Volume multi-attach is not available.')
302
303 def _attach_volume_to_servers(self, volume, servers):
304 """Attaches the given volume to the list of servers.
305
306 :param volume: The multiattach volume to use.
307 :param servers: list of server instances on which the volume will be
308 attached
309 :returns: dict of server ID to volumeAttachment dict entries
310 """
311 attachments = {}
312 for server in servers:
313 # map the server id to the volume attachment
314 attachments[server['id']] = self.attach_volume(server, volume)
315 # NOTE(mriedem): In the case of multi-attach, after the first
316 # attach the volume will be in-use. On the second attach, nova will
317 # 'reserve' the volume which puts it back into 'attaching' status
318 # and then the volume shouldn't go back to in-use until the compute
319 # actually attaches the server to the volume.
320 return attachments
321
322 def _detach_multiattach_volume(self, volume_id, server_id):
323 """Detaches a multiattach volume from the given server.
324
325 Depending on the number of attachments the volume has, this method
326 will wait for the volume to go to back to 'in-use' status if there are
327 more attachments or 'available' state if there are no more attachments.
328 """
329 # Count the number of attachments before starting the detach.
330 volume = self.volumes_client.show_volume(volume_id)['volume']
331 attachments = volume['attachments']
332 wait_status = 'in-use' if len(attachments) > 1 else 'available'
333 # Now detach the volume from the given server.
334 self.servers_client.detach_volume(server_id, volume_id)
335 # Now wait for the volume status to change.
336 waiters.wait_for_volume_resource_status(
337 self.volumes_client, volume_id, wait_status)
338
339 def _create_multiattach_volume(self, bootable=False):
340 kwargs = {}
341 if bootable:
342 kwargs['image_ref'] = CONF.compute.image_ref
343 return self.create_volume(multiattach=True, **kwargs)
344
345 def _create_and_multiattach(self):
346 """Creates two server instances and a volume and attaches to both.
347
348 :returns: A three-item tuple of the list of created servers,
349 the created volume, and dict of server ID to volumeAttachment
350 dict entries
351 """
352 servers = []
353 for x in range(2):
354 name = 'multiattach-server-%i' % x
355 servers.append(self.create_test_server(name=name))
356
357 # Now wait for the servers to be ACTIVE.
358 for server in servers:
359 waiters.wait_for_server_status(self.servers_client, server['id'],
360 'ACTIVE')
361
362 volume = self._create_multiattach_volume()
363
364 # Attach the volume to the servers
365 attachments = self._attach_volume_to_servers(volume, servers)
366 return servers, volume, attachments
367
368 @decorators.idempotent_id('8d5853f7-56e7-4988-9b0c-48cea3c7049a')
369 def test_list_get_volume_attachments_multiattach(self):
370 # Attach a single volume to two servers.
371 servers, volume, attachments = self._create_and_multiattach()
372
373 # List attachments from the volume and make sure the server uuids
374 # are in that list.
375 vol_attachments = self.volumes_client.show_volume(
376 volume['id'])['volume']['attachments']
377 attached_server_ids = [attachment['server_id']
378 for attachment in vol_attachments]
379 self.assertEqual(2, len(attached_server_ids))
380
381 # List Volume attachment of the servers
382 for server in servers:
383 self.assertIn(server['id'], attached_server_ids)
384 vol_attachments = self.servers_client.list_volume_attachments(
385 server['id'])['volumeAttachments']
386 self.assertEqual(1, len(vol_attachments))
387 attachment = attachments[server['id']]
388 self.assertDictEqual(attachment, vol_attachments[0])
389 # Detach the volume from this server.
390 self._detach_multiattach_volume(volume['id'], server['id'])
391
392 def _boot_from_multiattach_volume(self):
393 """Boots a server from a multiattach volume.
394
395 The volume will not be deleted when the server is deleted.
396
397 :returns: 2-item tuple of (server, volume)
398 """
399 volume = self._create_multiattach_volume(bootable=True)
400 # Now create a server from the bootable volume.
401 bdm = [{
402 'uuid': volume['id'],
403 'source_type': 'volume',
404 'destination_type': 'volume',
405 'boot_index': 0,
406 'delete_on_termination': False}]
407 server = self.create_test_server(
408 image_id='', block_device_mapping_v2=bdm, wait_until='ACTIVE')
409 # Assert the volume is attached to the server.
410 attachments = self.servers_client.list_volume_attachments(
411 server['id'])['volumeAttachments']
412 self.assertEqual(1, len(attachments))
413 self.assertEqual(volume['id'], attachments[0]['volumeId'])
414 return server, volume
415
416 @decorators.idempotent_id('65e33aa2-185b-44c8-b22e-e524973ed625')
417 def test_boot_from_multiattach_volume(self):
418 """Simple test to boot an instance from a multiattach volume."""
419 self._boot_from_multiattach_volume()
420
421 @utils.services('image')
422 @decorators.idempotent_id('885ac48a-2d7a-40c5-ae8b-1993882d724c')
423 def test_snapshot_volume_backed_multiattach(self):
424 """Boots a server from a multiattach volume and snapshots the server.
425
426 Creating the snapshot of the server will also create a snapshot of
427 the volume.
428 """
429 server, volume = self._boot_from_multiattach_volume()
430 # Create a snapshot of the server (and volume implicitly).
431 self.create_image_from_server(
432 server['id'], name='multiattach-snapshot',
433 wait_until='active', wait_for_server=True)
434 # TODO(mriedem): Make sure the volume snapshot exists. This requires
435 # adding the volume snapshots client to BaseV2ComputeTest.
436 # Delete the server, wait for it to be gone, and make sure the volume
437 # still exists.
438 self.servers_client.delete_server(server['id'])
439 waiters.wait_for_server_termination(self.servers_client, server['id'])
440 # Delete the volume and cascade the delete of the volume snapshot.
441 self.volumes_client.delete_volume(volume['id'], cascade=True)
442 # Now we have to wait for the volume to be gone otherwise the normal
443 # teardown will fail since it will race with our call and the snapshot
444 # might still exist.
445 self.volumes_client.wait_for_resource_deletion(volume['id'])
446
Steve Noyes83a14122018-01-11 16:12:40 -0500447 @decorators.idempotent_id('f01c7169-a124-4fc7-ae60-5e380e247c9c')
448 @testtools.skipUnless(CONF.compute_feature_enabled.resize,
449 'Resize not available.')
450 def test_resize_server_with_multiattached_volume(self):
451 # Attach a single volume to multiple servers, then resize the servers
452 servers, volume, _ = self._create_and_multiattach()
453
454 for server in servers:
455 self.resize_server(server['id'], self.flavor_ref_alt)
456
457 for server in servers:
458 self._detach_multiattach_volume(volume['id'], server['id'])
459
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800460 # TODO(mriedem): Might be interesting to create a bootable multiattach
461 # volume with delete_on_termination=True, create server1 from the
462 # volume, then attach it to server2, and then delete server1 in which
463 # case the volume won't be deleted because it's still attached to
464 # server2 and make sure the volume is still attached to server2.