blob: eff1beedacf6864bd3214e956414ddce84e609c4 [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')
Dan Smithc18d8c62012-07-02 08:09:26 -070068 def test_attach_detach_volume(self):
Sean Dague4dd2c0b2013-01-03 17:50:28 -050069 # Stop and Start a server with an attached volume, ensuring that
70 # the volume remains attached.
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010071 server, validation_resources = self._create_server()
Andrea Frittolicec44942017-03-24 14:44:19 +000072
73 # NOTE(andreaf) Create one remote client used throughout the test.
74 if CONF.validation.run_validation:
75 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010076 self.get_server_ip(server, validation_resources),
Andrea Frittolicec44942017-03-24 14:44:19 +000077 self.image_ssh_user,
78 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +010079 validation_resources['keypair']['private_key'],
Andrea Frittolicec44942017-03-24 14:44:19 +000080 server=server,
81 servers_client=self.servers_client)
82 # NOTE(andreaf) We need to ensure the ssh key has been
83 # injected in the guest before we power cycle
84 linux_client.validate_authentication()
85
Benny Kopilov5c5f7d82016-09-13 14:19:53 +030086 volume = self.create_volume()
zhufl2c9d9622018-08-30 16:03:07 +080087
88 # NOTE: As of the 12.0.0 Liberty release, the Nova libvirt driver
89 # no longer honors a user-supplied device name, in that case
90 # CONF.compute.volume_device_name must be set the equal value as
91 # the libvirt auto-assigned one
zhufl36f0a972017-02-28 15:43:33 +080092 attachment = self.attach_volume(server, volume,
93 device=('/dev/%s' % self.device))
ivan-zhu2f54b282013-03-11 16:39:25 +080094
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020095 self.servers_client.stop_server(server['id'])
96 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000097 'SHUTOFF')
Dan Smithc18d8c62012-07-02 08:09:26 -070098
Fabian Zimmermannbbef2762016-06-23 14:05:33 +020099 self.servers_client.start_server(server['id'])
100 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000101 'ACTIVE')
Dan Smithc18d8c62012-07-02 08:09:26 -0700102
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100103 if CONF.validation.run_validation:
Evgeny Antyshev4894a912016-11-21 12:17:18 +0000104 disks = linux_client.get_disks()
105 device_name_to_match = '\n' + self.device + ' '
106 self.assertIn(device_name_to_match, disks)
Dan Smithc18d8c62012-07-02 08:09:26 -0700107
zhufl36f0a972017-02-28 15:43:33 +0800108 self.servers_client.detach_volume(server['id'], attachment['volumeId'])
109 waiters.wait_for_volume_resource_status(
110 self.volumes_client, attachment['volumeId'], 'available')
111
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200112 self.servers_client.stop_server(server['id'])
113 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000114 'SHUTOFF')
Dan Smithc18d8c62012-07-02 08:09:26 -0700115
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200116 self.servers_client.start_server(server['id'])
117 waiters.wait_for_server_status(self.servers_client, server['id'],
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000118 'ACTIVE')
Dan Smithc18d8c62012-07-02 08:09:26 -0700119
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100120 if CONF.validation.run_validation:
Evgeny Antyshev4894a912016-11-21 12:17:18 +0000121 disks = linux_client.get_disks()
122 self.assertNotIn(device_name_to_match, disks)
Dan Smith1ced8422012-08-16 10:35:19 -0700123
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800124 @decorators.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
Ghanshyam5c2a5582014-04-14 17:16:57 +0900125 def test_list_get_volume_attachments(self):
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300126 # List volume attachment of the server
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100127 server, _ = self._create_server()
zhufl16dd62c2017-05-03 15:46:16 +0800128 volume_1st = self.create_volume()
zhufl2c9d9622018-08-30 16:03:07 +0800129 attachment_1st = self.attach_volume(server, volume_1st)
David Kranz3ebc7212015-02-10 12:19:19 -0500130 body = self.servers_client.list_volume_attachments(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200131 server['id'])['volumeAttachments']
Ghanshyam5c2a5582014-04-14 17:16:57 +0900132 self.assertEqual(1, len(body))
zhufl16dd62c2017-05-03 15:46:16 +0800133 self.assertIn(attachment_1st, body)
Ghanshyam5c2a5582014-04-14 17:16:57 +0900134
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300135 # Get volume attachment of the server
Ken'ichi Ohmichi277d1882015-11-20 00:44:06 +0000136 body = self.servers_client.show_volume_attachment(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200137 server['id'],
zhufl16dd62c2017-05-03 15:46:16 +0800138 attachment_1st['id'])['volumeAttachment']
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200139 self.assertEqual(server['id'], body['serverId'])
zhufl16dd62c2017-05-03 15:46:16 +0800140 self.assertEqual(volume_1st['id'], body['volumeId'])
141 self.assertEqual(attachment_1st['id'], body['id'])
lanoux2746ba02016-03-16 17:41:01 +0900142
zhufl16dd62c2017-05-03 15:46:16 +0800143 # attach one more volume to server
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800144 volume_2nd = self.create_volume()
zhufl36f0a972017-02-28 15:43:33 +0800145 attachment_2nd = self.attach_volume(server, volume_2nd)
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800146 body = self.servers_client.list_volume_attachments(
147 server['id'])['volumeAttachments']
148 self.assertEqual(2, len(body))
149
zhufl16dd62c2017-05-03 15:46:16 +0800150 for attachment in [attachment_1st, attachment_2nd]:
151 body = self.servers_client.show_volume_attachment(
152 server['id'], attachment['id'])['volumeAttachment']
153 self.assertEqual(server['id'], body['serverId'])
154 self.assertEqual(attachment['volumeId'], body['volumeId'])
155 self.assertEqual(attachment['id'], body['id'])
Steve Noyes5026c502017-08-10 11:12:31 -0400156 self.servers_client.detach_volume(server['id'],
157 attachment['volumeId'])
158 waiters.wait_for_volume_resource_status(
159 self.volumes_client, attachment['volumeId'], 'available')
Ken'ichi Ohmichi09139282016-12-01 14:36:22 -0800160
lanoux2746ba02016-03-16 17:41:01 +0900161
Matt Riedemann039740a2018-01-02 15:47:40 -0500162class AttachVolumeShelveTestJSON(BaseAttachVolumeTest):
lanoux2746ba02016-03-16 17:41:01 +0900163 """Testing volume with shelved instance.
164
165 This test checks the attaching and detaching volumes from
Tianbiao Qi0d1d24e2016-09-28 14:17:12 +0800166 a shelved or shelved offload instance.
lanoux2746ba02016-03-16 17:41:01 +0900167 """
168
169 min_microversion = '2.20'
170 max_microversion = 'latest'
171
zhufl11289db2017-08-29 10:59:39 +0800172 @classmethod
173 def skip_checks(cls):
174 super(AttachVolumeShelveTestJSON, cls).skip_checks()
175 if not CONF.compute_feature_enabled.shelve:
176 raise cls.skipException('Shelve is not available.')
177
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100178 def _count_volumes(self, server, validation_resources):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200179 # Count number of volumes on an instance
180 volumes = 0
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100181 if CONF.validation.run_validation:
182 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100183 self.get_server_ip(server, validation_resources),
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100184 self.image_ssh_user,
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200185 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100186 validation_resources['keypair']['private_key'],
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200187 server=server,
Andrea Frittoli (andreaf)3f5aa982016-07-08 12:10:36 +0100188 servers_client=self.servers_client)
lanoux2746ba02016-03-16 17:41:01 +0900189
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200190 command = 'grep -c -E [vs]d.$ /proc/partitions'
191 volumes = int(linux_client.exec_command(command).strip())
192 return volumes
193
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100194 def _shelve_server(self, server, validation_resources):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200195 # NOTE(andreaf) If we are going to shelve a server, we should
196 # check first whether the server is ssh-able. Otherwise we
197 # won't be able to distinguish failures introduced by shelve
198 # from pre-existing ones. Also it's good to wait for cloud-init
199 # to be done and sshd server to be running before shelving to
200 # avoid breaking the VM
201 if CONF.validation.run_validation:
202 linux_client = remote_client.RemoteClient(
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100203 self.get_server_ip(server, validation_resources),
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200204 self.image_ssh_user,
205 self.image_ssh_password,
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100206 validation_resources['keypair']['private_key'],
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200207 server=server,
208 servers_client=self.servers_client)
209 linux_client.validate_authentication()
210
211 # If validation went ok, or it was skipped, shelve the server
212 compute.shelve_server(self.servers_client, server['id'])
213
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100214 def _unshelve_server_and_check_volumes(self, server,
215 validation_resources,
216 number_of_volumes):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200217 # Unshelve the instance and check that there are expected volumes
218 self.servers_client.unshelve_server(server['id'])
219 waiters.wait_for_server_status(self.servers_client,
220 server['id'],
221 'ACTIVE')
222 if CONF.validation.run_validation:
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100223 counted_volumes = self._count_volumes(
224 server, validation_resources)
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200225 self.assertEqual(number_of_volumes, counted_volumes)
lanoux2746ba02016-03-16 17:41:01 +0900226
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800227 @decorators.idempotent_id('13a940b6-3474-4c3c-b03f-29b89112bfee')
lanoux2746ba02016-03-16 17:41:01 +0900228 def test_attach_volume_shelved_or_offload_server(self):
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200229 # Create server, count number of volumes on it, shelve
230 # server and attach pre-created volume to shelved server
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100231 server, validation_resources = self._create_server()
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300232 volume = self.create_volume()
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100233 num_vol = self._count_volumes(server, validation_resources)
234 self._shelve_server(server, validation_resources)
zhufl2c9d9622018-08-30 16:03:07 +0800235 attachment = self.attach_volume(server, volume)
lanoux2746ba02016-03-16 17:41:01 +0900236
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200237 # Unshelve the instance and check that attached volume exists
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100238 self._unshelve_server_and_check_volumes(
239 server, validation_resources, num_vol + 1)
lanoux2746ba02016-03-16 17:41:01 +0900240
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300241 # Get volume attachment of the server
lanoux2746ba02016-03-16 17:41:01 +0900242 volume_attachment = self.servers_client.show_volume_attachment(
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200243 server['id'],
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300244 attachment['id'])['volumeAttachment']
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200245 self.assertEqual(server['id'], volume_attachment['serverId'])
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300246 self.assertEqual(attachment['id'], volume_attachment['id'])
lanoux2746ba02016-03-16 17:41:01 +0900247 # Check the mountpoint is not None after unshelve server even in
248 # case of shelved_offloaded.
249 self.assertIsNotNone(volume_attachment['device'])
250
Ken'ichi Ohmichi6c92edf2017-01-27 17:32:10 -0800251 @decorators.idempotent_id('b54e86dd-a070-49c4-9c07-59ae6dae15aa')
lanoux2746ba02016-03-16 17:41:01 +0900252 def test_detach_volume_shelved_or_offload_server(self):
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300253 # Count number of volumes on instance, shelve
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200254 # server and attach pre-created volume to shelved server
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100255 server, validation_resources = self._create_server()
Benny Kopilov5c5f7d82016-09-13 14:19:53 +0300256 volume = self.create_volume()
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100257 num_vol = self._count_volumes(server, validation_resources)
258 self._shelve_server(server, validation_resources)
zhufl36f0a972017-02-28 15:43:33 +0800259
260 # Attach and then detach the volume
zhufl2c9d9622018-08-30 16:03:07 +0800261 self.attach_volume(server, volume)
zhufl36f0a972017-02-28 15:43:33 +0800262 self.servers_client.detach_volume(server['id'], volume['id'])
263 waiters.wait_for_volume_resource_status(self.volumes_client,
264 volume['id'], 'available')
lanoux2746ba02016-03-16 17:41:01 +0900265
Fabian Zimmermannbbef2762016-06-23 14:05:33 +0200266 # Unshelve the instance and check that we have the expected number of
267 # volume(s)
Andrea Frittoli9f416dd2017-08-10 15:38:00 +0100268 self._unshelve_server_and_check_volumes(
269 server, validation_resources, num_vol)
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800270
271
272class AttachVolumeMultiAttachTest(BaseAttachVolumeTest):
273 min_microversion = '2.60'
274 max_microversion = 'latest'
275
276 @classmethod
277 def skip_checks(cls):
278 super(AttachVolumeMultiAttachTest, cls).skip_checks()
279 if not CONF.compute_feature_enabled.volume_multiattach:
280 raise cls.skipException('Volume multi-attach is not available.')
281
282 def _attach_volume_to_servers(self, volume, servers):
283 """Attaches the given volume to the list of servers.
284
285 :param volume: The multiattach volume to use.
286 :param servers: list of server instances on which the volume will be
287 attached
288 :returns: dict of server ID to volumeAttachment dict entries
289 """
290 attachments = {}
291 for server in servers:
292 # map the server id to the volume attachment
293 attachments[server['id']] = self.attach_volume(server, volume)
294 # NOTE(mriedem): In the case of multi-attach, after the first
295 # attach the volume will be in-use. On the second attach, nova will
296 # 'reserve' the volume which puts it back into 'attaching' status
297 # and then the volume shouldn't go back to in-use until the compute
298 # actually attaches the server to the volume.
299 return attachments
300
301 def _detach_multiattach_volume(self, volume_id, server_id):
302 """Detaches a multiattach volume from the given server.
303
304 Depending on the number of attachments the volume has, this method
305 will wait for the volume to go to back to 'in-use' status if there are
306 more attachments or 'available' state if there are no more attachments.
307 """
308 # Count the number of attachments before starting the detach.
309 volume = self.volumes_client.show_volume(volume_id)['volume']
310 attachments = volume['attachments']
311 wait_status = 'in-use' if len(attachments) > 1 else 'available'
312 # Now detach the volume from the given server.
313 self.servers_client.detach_volume(server_id, volume_id)
314 # Now wait for the volume status to change.
315 waiters.wait_for_volume_resource_status(
316 self.volumes_client, volume_id, wait_status)
317
318 def _create_multiattach_volume(self, bootable=False):
319 kwargs = {}
320 if bootable:
321 kwargs['image_ref'] = CONF.compute.image_ref
322 return self.create_volume(multiattach=True, **kwargs)
323
324 def _create_and_multiattach(self):
325 """Creates two server instances and a volume and attaches to both.
326
327 :returns: A three-item tuple of the list of created servers,
328 the created volume, and dict of server ID to volumeAttachment
329 dict entries
330 """
331 servers = []
332 for x in range(2):
333 name = 'multiattach-server-%i' % x
334 servers.append(self.create_test_server(name=name))
335
336 # Now wait for the servers to be ACTIVE.
337 for server in servers:
338 waiters.wait_for_server_status(self.servers_client, server['id'],
339 'ACTIVE')
340
341 volume = self._create_multiattach_volume()
342
343 # Attach the volume to the servers
344 attachments = self._attach_volume_to_servers(volume, servers)
345 return servers, volume, attachments
346
347 @decorators.idempotent_id('8d5853f7-56e7-4988-9b0c-48cea3c7049a')
348 def test_list_get_volume_attachments_multiattach(self):
349 # Attach a single volume to two servers.
350 servers, volume, attachments = self._create_and_multiattach()
351
352 # List attachments from the volume and make sure the server uuids
353 # are in that list.
354 vol_attachments = self.volumes_client.show_volume(
355 volume['id'])['volume']['attachments']
356 attached_server_ids = [attachment['server_id']
357 for attachment in vol_attachments]
358 self.assertEqual(2, len(attached_server_ids))
359
360 # List Volume attachment of the servers
361 for server in servers:
362 self.assertIn(server['id'], attached_server_ids)
363 vol_attachments = self.servers_client.list_volume_attachments(
364 server['id'])['volumeAttachments']
365 self.assertEqual(1, len(vol_attachments))
366 attachment = attachments[server['id']]
367 self.assertDictEqual(attachment, vol_attachments[0])
368 # Detach the volume from this server.
369 self._detach_multiattach_volume(volume['id'], server['id'])
370
371 def _boot_from_multiattach_volume(self):
372 """Boots a server from a multiattach volume.
373
374 The volume will not be deleted when the server is deleted.
375
376 :returns: 2-item tuple of (server, volume)
377 """
378 volume = self._create_multiattach_volume(bootable=True)
379 # Now create a server from the bootable volume.
380 bdm = [{
381 'uuid': volume['id'],
382 'source_type': 'volume',
383 'destination_type': 'volume',
384 'boot_index': 0,
385 'delete_on_termination': False}]
386 server = self.create_test_server(
387 image_id='', block_device_mapping_v2=bdm, wait_until='ACTIVE')
388 # Assert the volume is attached to the server.
389 attachments = self.servers_client.list_volume_attachments(
390 server['id'])['volumeAttachments']
391 self.assertEqual(1, len(attachments))
392 self.assertEqual(volume['id'], attachments[0]['volumeId'])
393 return server, volume
394
395 @decorators.idempotent_id('65e33aa2-185b-44c8-b22e-e524973ed625')
396 def test_boot_from_multiattach_volume(self):
397 """Simple test to boot an instance from a multiattach volume."""
398 self._boot_from_multiattach_volume()
399
400 @utils.services('image')
401 @decorators.idempotent_id('885ac48a-2d7a-40c5-ae8b-1993882d724c')
402 def test_snapshot_volume_backed_multiattach(self):
403 """Boots a server from a multiattach volume and snapshots the server.
404
405 Creating the snapshot of the server will also create a snapshot of
406 the volume.
407 """
408 server, volume = self._boot_from_multiattach_volume()
409 # Create a snapshot of the server (and volume implicitly).
410 self.create_image_from_server(
411 server['id'], name='multiattach-snapshot',
412 wait_until='active', wait_for_server=True)
413 # TODO(mriedem): Make sure the volume snapshot exists. This requires
414 # adding the volume snapshots client to BaseV2ComputeTest.
415 # Delete the server, wait for it to be gone, and make sure the volume
416 # still exists.
417 self.servers_client.delete_server(server['id'])
418 waiters.wait_for_server_termination(self.servers_client, server['id'])
419 # Delete the volume and cascade the delete of the volume snapshot.
420 self.volumes_client.delete_volume(volume['id'], cascade=True)
421 # Now we have to wait for the volume to be gone otherwise the normal
422 # teardown will fail since it will race with our call and the snapshot
423 # might still exist.
424 self.volumes_client.wait_for_resource_deletion(volume['id'])
425
Steve Noyes83a14122018-01-11 16:12:40 -0500426 @decorators.idempotent_id('f01c7169-a124-4fc7-ae60-5e380e247c9c')
427 @testtools.skipUnless(CONF.compute_feature_enabled.resize,
428 'Resize not available.')
429 def test_resize_server_with_multiattached_volume(self):
430 # Attach a single volume to multiple servers, then resize the servers
431 servers, volume, _ = self._create_and_multiattach()
432
433 for server in servers:
434 self.resize_server(server['id'], self.flavor_ref_alt)
435
436 for server in servers:
437 self._detach_multiattach_volume(volume['id'], server['id'])
438
Matt Riedemann81fa9b62016-01-14 13:04:38 -0800439 # TODO(mriedem): Might be interesting to create a bootable multiattach
440 # volume with delete_on_termination=True, create server1 from the
441 # volume, then attach it to server2, and then delete server1 in which
442 # case the volume won't be deleted because it's still attached to
443 # server2 and make sure the volume is still attached to server2.