blob: c969a0cf1dc8973cce902f7b0ea65fd1ee8a77ec [file] [log] [blame]
Jay Pipes13b479b2012-06-11 14:52:27 -04001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2012 OpenStack, LLC
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060018from nose.plugins.attrib import attr
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060019import unittest2 as unittest
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060020
Jay Pipesf38eaac2012-06-21 13:37:35 -040021from tempest.common.utils.data_utils import rand_name, parse_image_id
Brian Waldon738cd632011-12-12 18:45:09 -050022import tempest.config
Jay Pipes13b479b2012-06-11 14:52:27 -040023from tempest import exceptions
Brian Waldon738cd632011-12-12 18:45:09 -050024from tempest import openstack
Dan Smithe7316bb2012-08-14 12:35:34 -070025from tempest.tests.compute import base
Jay Pipesf38eaac2012-06-21 13:37:35 -040026from tempest.tests import compute
Jay Pipes7f757632011-12-02 15:53:32 -050027
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060028
Dan Smithe7316bb2012-08-14 12:35:34 -070029class ImagesTestBase(object):
Rohit Karajgiea462ae2012-05-27 21:23:21 -070030
31 def tearDown(self):
32 """Terminate test instances created after a test is executed"""
33 for server in self.servers:
34 resp, body = self.servers_client.delete_server(server['id'])
35 if resp['status'] == '204':
36 self.servers.remove(server)
37 self.servers_client.wait_for_server_termination(server['id'])
38
39 for image_id in self.image_ids:
40 self.client.delete_image(image_id)
41 self.image_ids.remove(image_id)
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060042
Daryl Wallecked8bef32011-12-05 23:02:08 -060043 @attr(type='smoke')
Jay Pipesf38eaac2012-06-21 13:37:35 -040044 @unittest.skipUnless(compute.CREATE_IMAGE_ENABLED,
Brian Waldon738cd632011-12-12 18:45:09 -050045 'Environment unable to create images.')
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060046 def test_create_delete_image(self):
47 """An image for the provided server should be created"""
48 server_name = rand_name('server')
49 resp, server = self.servers_client.create_server(server_name,
50 self.image_ref,
51 self.flavor_ref)
52 self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
53
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080054 # Create a new image
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060055 name = rand_name('image')
Daryl Wallecked8bef32011-12-05 23:02:08 -060056 meta = {'image_type': 'test'}
57 resp, body = self.client.create_image(server['id'], name, meta)
Jay Pipesf38eaac2012-06-21 13:37:35 -040058 image_id = parse_image_id(resp['location'])
Daryl Walleck416af922011-11-22 22:28:33 -060059 self.client.wait_for_image_resp_code(image_id, 200)
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060060 self.client.wait_for_image_status(image_id, 'ACTIVE')
61
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080062 # Verify the image was created correctly
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060063 resp, image = self.client.get_image(image_id)
64 self.assertEqual(name, image['name'])
Daryl Wallecked8bef32011-12-05 23:02:08 -060065 self.assertEqual('test', image['metadata']['image_type'])
66
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080067 # Verify minRAM and minDisk values are the same as the original image
Daryl Wallecked8bef32011-12-05 23:02:08 -060068 resp, original_image = self.client.get_image(self.image_ref)
69 self.assertEqual(original_image['minRam'], image['minRam'])
70 self.assertEqual(original_image['minDisk'], image['minDisk'])
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060071
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080072 # Teardown
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060073 self.client.delete_image(image['id'])
74 self.servers_client.delete_server(server['id'])
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080075
76 @attr(type='negative')
77 def test_create_image_from_deleted_server(self):
Jay Pipes04b70812012-01-11 10:53:24 -050078 """An image should not be created if the server instance is removed """
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080079 server_name = rand_name('server')
80 resp, server = self.servers_client.create_server(server_name,
81 self.image_ref,
82 self.flavor_ref)
83 self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
84
85 # Delete server before trying to create server
86 self.servers_client.delete_server(server['id'])
87
88 try:
89 # Create a new image after server is deleted
90 name = rand_name('image')
91 meta = {'image_type': 'test'}
92 resp, body = self.client.create_image(server['id'], name, meta)
93
94 except:
95 pass
96
97 else:
Jay Pipesf38eaac2012-06-21 13:37:35 -040098 image_id = parse_image_id(resp['location'])
Daryl Walleckade49f92012-01-16 23:14:26 -060099 self.client.wait_for_image_resp_code(image_id, 200)
100 self.client.wait_for_image_status(image_id, 'ACTIVE')
Jay Pipes04b70812012-01-11 10:53:24 -0500101 self.client.delete_image(image_id)
Daryl Walleckade49f92012-01-16 23:14:26 -0600102 self.fail("Should not create snapshot from deleted instance!")
rajalakshmi-ganesan32f8db62012-05-18 19:13:40 +0530103
104 @attr(type='negative')
105 def test_create_image_from_invalid_server(self):
106 """An image should not be created with invalid server id"""
107 try:
108 # Create a new image with invalid server id
109 name = rand_name('image')
110 meta = {'image_type': 'test'}
111 resp = {}
112 resp['status'] = None
113 resp, body = self.client.create_image('!@#$%^&*()', name, meta)
114
115 except exceptions.NotFound:
116 pass
117
118 finally:
119 if (resp['status'] != None):
Jay Pipesf38eaac2012-06-21 13:37:35 -0400120 image_id = parse_image_id(resp['location'])
rajalakshmi-ganesan32f8db62012-05-18 19:13:40 +0530121 resp, _ = self.client.delete_image(image_id)
122 self.fail("An image should not be created"
123 " with invalid server id")
124
125 @attr(type='negative')
Jay Pipesf38eaac2012-06-21 13:37:35 -0400126 @unittest.skipUnless(compute.MULTI_USER, 'Second user not configured')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700127 def test_create_image_for_server_in_another_tenant(self):
128 """Creating image of another tenant's server should be return error"""
129 server = self.create_server()
130
131 snapshot_name = rand_name('test-snap-')
132 self.assertRaises(exceptions.NotFound, self.alt_client.create_image,
133 server['id'], snapshot_name)
134
135 @attr(type='negative')
136 def test_create_image_when_server_is_building(self):
137 """Return error when creating an image of a server that is building"""
138 server_name = rand_name('test-vm-')
139 resp, server = self.servers_client.create_server(server_name,
140 self.image_ref,
141 self.flavor_ref)
142 self.servers.append(server)
143 snapshot_name = rand_name('test-snap-')
144 self.assertRaises(exceptions.Duplicate, self.client.create_image,
145 server['id'], snapshot_name)
146
David Kranz0312b692012-08-21 17:51:17 -0400147 @unittest.skip("Until Bug 1039739 is fixed")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700148 @attr(type='negative')
149 def test_create_image_when_server_is_rebooting(self):
150 """Return error when creating an image of server that is rebooting"""
151 server = self.create_server()
152 self.servers_client.reboot(server['id'], 'HARD')
153
154 snapshot_name = rand_name('test-snap-')
155 self.assertRaises(exceptions.Duplicate, self.client.create_image,
156 server['id'], snapshot_name)
157
158 @attr(type='negative')
159 def test_create_image_when_server_is_terminating(self):
160 """Return an error when creating image of server that is terminating"""
161 server = self.create_server()
162 self.servers_client.delete_server(server['id'])
163
164 snapshot_name = rand_name('test-snap-')
165 self.assertRaises(exceptions.Duplicate, self.client.create_image,
166 server['id'], snapshot_name)
167
168 @attr(type='negative')
169 def test_create_second_image_when_first_image_is_being_saved(self):
170 """Disallow creating another image when first image is being saved"""
171 server = self.create_server()
172
173 try:
174 # Create first snapshot
175 snapshot_name = rand_name('test-snap-')
176 resp, body = self.client.create_image(server['id'], snapshot_name)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400177 image_id = parse_image_id(resp['location'])
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700178 self.image_ids.append(image_id)
179
180 # Create second snapshot
181 alt_snapshot_name = rand_name('test-snap-')
182 self.client.create_image(server['id'], alt_snapshot_name)
183 except exceptions.Duplicate:
184 pass
185
186 else:
187 self.fail("Should allow creating an image when another image of"
188 "the server is still being saved")
189
190 @attr(type='negative')
191 @unittest.skip("Until Bug 1004564 is fixed")
192 def test_create_image_specify_name_over_256_chars(self):
193 """Return an error if snapshot name over 256 characters is passed"""
194 server = self.create_server()
195
196 try:
197 snapshot_name = rand_name('a' * 260)
198 self.assertRaises(exceptions.BadRequest, self.client.create_image,
199 server['id'], snapshot_name)
200 except:
201 self.fail("Should return 400 Bad Request if image name is over 256"
202 " characters")
203
204 @attr(type='negative')
205 @unittest.skip("Until Bug 1005397 is fixed")
206 def test_create_image_specify_uuid_35_characters_or_less(self):
207 """Return an error if Image ID passed is 35 characters or less"""
208 try:
209 snapshot_name = rand_name('test-snap-')
210 test_uuid = ('a' * 35)
211 self.assertRaises(exceptions.BadRequest, self.client.create_image,
212 test_uuid, snapshot_name)
213 except:
214 self.fail("Should return 400 Bad Request if server uuid is 35"
215 " characters or less")
216
217 @attr(type='negative')
218 @unittest.skip("Until Bug 1005397 is fixed")
219 def test_create_image_specify_uuid_37_characters_or_more(self):
220 """Return an error if Image ID passed is 37 characters or more"""
221 try:
222 snapshot_name = rand_name('test-snap-')
223 test_uuid = ('a' * 37)
224 self.assertRaises(exceptions.BadRequest, self.client.create_image,
225 test_uuid, snapshot_name)
226 except:
227 self.fail("Should return 400 Bad Request if server uuid is 37"
228 " characters or more")
229
230 @attr(type='negative')
231 @unittest.skip("Until Bug 1006725 is fixed")
232 def test_create_image_specify_multibyte_character_image_name(self):
233 """Return an error if the image name has multi-byte characters"""
234 server = self.create_server()
235
236 try:
237 snapshot_name = rand_name('\xef\xbb\xbf')
238 self.assertRaises(exceptions.BadRequest,
239 self.client.create_image, server['id'],
240 snapshot_name)
241 except:
242 self.fail("Should return 400 Bad Request if multi byte characters"
243 " are used for image name")
244
245 @attr(type='negative')
246 @unittest.skip("Until Bug 1005423 is fixed")
247 def test_create_image_specify_invalid_metadata(self):
248 """Return an error when creating image with invalid metadata"""
249 server = self.create_server()
250
251 try:
252 snapshot_name = rand_name('test-snap-')
253 meta = {'': ''}
254 self.assertRaises(exceptions.BadRequest, self.client.create_image,
255 server['id'], snapshot_name, meta)
256
257 except:
258 self.fail("Should raise 400 Bad Request if meta data is invalid")
259
260 @attr(type='negative')
261 @unittest.skip("Until Bug 1005423 is fixed")
262 def test_create_image_specify_metadata_over_limits(self):
263 """Return an error when creating image with meta data over 256 chars"""
264 server = self.create_server()
265
266 try:
267 snapshot_name = rand_name('test-snap-')
268 meta = {'a' * 260: 'b' * 260}
269 self.assertRaises(exceptions.OverLimit, self.client.create_image,
270 server['id'], snapshot_name, meta)
271
272 except:
273 self.fail("Should raise 413 Over Limit if meta data was too long")
274
275 @attr(type='negative')
rajalakshmi-ganesan32f8db62012-05-18 19:13:40 +0530276 def test_delete_image_with_invalid_image_id(self):
277 """An image should not be deleted with invalid image id"""
278 try:
279 # Delete an image with invalid image id
280 resp, _ = self.client.delete_image('!@$%^&*()')
281
282 except exceptions.NotFound:
283 pass
284
285 else:
286 self.fail("DELETE image request should rasie NotFound exception"
287 "when requested with invalid image")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700288
289 @attr(type='negative')
290 def test_delete_non_existent_image(self):
291 """Return an error while trying to delete a non-existent image"""
292
293 non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
294 self.assertRaises(exceptions.NotFound, self.client.delete_image,
295 non_existent_image_id)
296
297 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700298 def test_delete_image_blank_id(self):
299 """Return an error while trying to delete an image with blank Id"""
300
301 try:
David Kranz28e35c52012-07-10 10:14:38 -0400302 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700303 '')
304 except:
David Kranz28e35c52012-07-10 10:14:38 -0400305 self.fail("Did not return HTTP 404 NotFound for blank image id")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700306
307 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700308 def test_delete_image_non_hex_string_id(self):
309 """Return an error while trying to delete an image with non hex id"""
310
311 image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
312 try:
David Kranz28e35c52012-07-10 10:14:38 -0400313 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700314 image_id)
315 except:
David Kranz28e35c52012-07-10 10:14:38 -0400316 self.fail("Did not return HTTP 404 NotFound for non hex image")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700317
318 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700319 def test_delete_image_negative_image_id(self):
320 """Return an error while trying to delete an image with negative id"""
321
322 try:
David Kranz28e35c52012-07-10 10:14:38 -0400323 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700324 -1)
325 except:
David Kranz28e35c52012-07-10 10:14:38 -0400326 self.fail("Did not return HTTP 404 NotFound for negative image "
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700327 "id")
328
329 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700330 def test_delete_image_id_is_over_35_character_limit(self):
331 """Return an error while trying to delete image with id over limit"""
332
333 try:
David Kranz28e35c52012-07-10 10:14:38 -0400334 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700335 '11a22b9-120q-5555-cc11-00ab112223gj-3fac')
336 except:
David Kranz28e35c52012-07-10 10:14:38 -0400337 self.fail("Did not return HTTP 404 NotFound for image id that "
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700338 "exceeds 35 character ID length limit")
339
340 @attr(type='negative')
Jay Pipesf38eaac2012-06-21 13:37:35 -0400341 @unittest.skipUnless(compute.MULTI_USER, 'Second user not configured')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700342 def test_delete_image_of_another_tenant(self):
343 """Return an error while trying to delete another tenant's image"""
344
345 server = self.create_server()
346
347 snapshot_name = rand_name('test-snap-')
348 resp, body = self.client.create_image(server['id'], snapshot_name)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400349 image_id = parse_image_id(resp['location'])
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700350 self.image_ids.append(image_id)
351 self.client.wait_for_image_resp_code(image_id, 200)
352 self.client.wait_for_image_status(image_id, 'ACTIVE')
353
354 # Delete image
355 self.assertRaises(exceptions.NotFound,
356 self.alt_client.delete_image, image_id)
357
358 @attr(type='negative')
359 def test_delete_image_that_is_not_yet_active(self):
360 """Return an error while trying to delete an active that is creating"""
361
362 server = self.create_server()
363
364 snapshot_name = rand_name('test-snap-')
365 resp, body = self.client.create_image(server['id'], snapshot_name)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400366 image_id = parse_image_id(resp['location'])
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700367 self.image_ids.append(image_id)
368
369 # Do not wait, attempt to delete the image, ensure it's successful
370 resp, body = self.client.delete_image(image_id)
371 self.assertEqual('204', resp['status'])
372 self.image_ids.remove(image_id)
373
374 self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
Dan Smithe7316bb2012-08-14 12:35:34 -0700375
376
377class ImagesTestJSON(base.BaseComputeTestJSON,
378 ImagesTestBase):
379 def tearDown(self):
380 ImagesTestBase.tearDown(self)
381
382 @classmethod
383 def setUpClass(cls):
384 super(ImagesTestJSON, cls).setUpClass()
385 cls.client = cls.images_client
386 cls.servers_client = cls.servers_client
387
388 cls.image_ids = []
389
390 if compute.MULTI_USER:
391 if cls.config.compute.allow_tenant_isolation:
392 creds = cls._get_isolated_creds()
393 username, tenant_name, password = creds
394 cls.alt_manager = openstack.Manager(username=username,
395 password=password,
396 tenant_name=tenant_name)
397 else:
398 # Use the alt_XXX credentials in the config file
399 cls.alt_manager = openstack.AltManager()
400 cls.alt_client = cls.alt_manager.images_client
401
402
403class ImagesTestXML(base.BaseComputeTestXML,
404 ImagesTestBase):
405 def tearDown(self):
406 ImagesTestBase.tearDown(self)
407
408 @classmethod
409 def setUpClass(cls):
410 super(ImagesTestXML, cls).setUpClass()
411 cls.client = cls.images_client
412 cls.servers_client = cls.servers_client
413
414 cls.image_ids = []
415
416 if compute.MULTI_USER:
417 if cls.config.compute.allow_tenant_isolation:
418 creds = cls._get_isolated_creds()
419 username, tenant_name, password = creds
420 cls.alt_manager = openstack.Manager(username=username,
421 password=password,
422 tenant_name=tenant_name)
423 else:
424 # Use the alt_XXX credentials in the config file
425 cls.alt_manager = openstack.AltManager()
426 cls.alt_client = cls.alt_manager.images_client