blob: 5937811503714556c9a99cdefdd3450d8ffc8fda [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
David Kranz6cdcfb62012-09-06 11:02:58 -040020import nose
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060021
Jay Pipesf38eaac2012-06-21 13:37:35 -040022from tempest.common.utils.data_utils import rand_name, parse_image_id
Brian Waldon738cd632011-12-12 18:45:09 -050023import tempest.config
Jay Pipes13b479b2012-06-11 14:52:27 -040024from tempest import exceptions
Brian Waldon738cd632011-12-12 18:45:09 -050025from tempest import openstack
Dan Smithe7316bb2012-08-14 12:35:34 -070026from tempest.tests.compute import base
Jay Pipesf38eaac2012-06-21 13:37:35 -040027from tempest.tests import compute
Jay Pipes7f757632011-12-02 15:53:32 -050028
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060029
Dan Smithe7316bb2012-08-14 12:35:34 -070030class ImagesTestBase(object):
Rohit Karajgiea462ae2012-05-27 21:23:21 -070031
32 def tearDown(self):
33 """Terminate test instances created after a test is executed"""
34 for server in self.servers:
35 resp, body = self.servers_client.delete_server(server['id'])
36 if resp['status'] == '204':
37 self.servers.remove(server)
38 self.servers_client.wait_for_server_termination(server['id'])
39
40 for image_id in self.image_ids:
41 self.client.delete_image(image_id)
42 self.image_ids.remove(image_id)
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060043
Daryl Wallecked8bef32011-12-05 23:02:08 -060044 @attr(type='smoke')
Jay Pipesf38eaac2012-06-21 13:37:35 -040045 @unittest.skipUnless(compute.CREATE_IMAGE_ENABLED,
Brian Waldon738cd632011-12-12 18:45:09 -050046 'Environment unable to create images.')
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060047 def test_create_delete_image(self):
48 """An image for the provided server should be created"""
49 server_name = rand_name('server')
50 resp, server = self.servers_client.create_server(server_name,
51 self.image_ref,
52 self.flavor_ref)
53 self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
54
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080055 # Create a new image
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060056 name = rand_name('image')
Daryl Wallecked8bef32011-12-05 23:02:08 -060057 meta = {'image_type': 'test'}
58 resp, body = self.client.create_image(server['id'], name, meta)
Jay Pipesf38eaac2012-06-21 13:37:35 -040059 image_id = parse_image_id(resp['location'])
Daryl Walleck416af922011-11-22 22:28:33 -060060 self.client.wait_for_image_resp_code(image_id, 200)
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060061 self.client.wait_for_image_status(image_id, 'ACTIVE')
62
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080063 # Verify the image was created correctly
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060064 resp, image = self.client.get_image(image_id)
65 self.assertEqual(name, image['name'])
Daryl Wallecked8bef32011-12-05 23:02:08 -060066 self.assertEqual('test', image['metadata']['image_type'])
67
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080068 # Verify minRAM and minDisk values are the same as the original image
Daryl Wallecked8bef32011-12-05 23:02:08 -060069 resp, original_image = self.client.get_image(self.image_ref)
70 self.assertEqual(original_image['minRam'], image['minRam'])
71 self.assertEqual(original_image['minDisk'], image['minDisk'])
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060072
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080073 # Teardown
Daryl Walleck73a9e7a2011-11-15 17:43:31 -060074 self.client.delete_image(image['id'])
75 self.servers_client.delete_server(server['id'])
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080076
77 @attr(type='negative')
78 def test_create_image_from_deleted_server(self):
Jay Pipes04b70812012-01-11 10:53:24 -050079 """An image should not be created if the server instance is removed """
Ravikumar Venkatesan94d81172012-01-09 21:53:14 -080080 server_name = rand_name('server')
81 resp, server = self.servers_client.create_server(server_name,
82 self.image_ref,
83 self.flavor_ref)
84 self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
85
86 # Delete server before trying to create server
87 self.servers_client.delete_server(server['id'])
88
89 try:
90 # Create a new image after server is deleted
91 name = rand_name('image')
92 meta = {'image_type': 'test'}
93 resp, body = self.client.create_image(server['id'], name, meta)
94
95 except:
96 pass
97
98 else:
Jay Pipesf38eaac2012-06-21 13:37:35 -040099 image_id = parse_image_id(resp['location'])
Daryl Walleckade49f92012-01-16 23:14:26 -0600100 self.client.wait_for_image_resp_code(image_id, 200)
101 self.client.wait_for_image_status(image_id, 'ACTIVE')
Jay Pipes04b70812012-01-11 10:53:24 -0500102 self.client.delete_image(image_id)
Daryl Walleckade49f92012-01-16 23:14:26 -0600103 self.fail("Should not create snapshot from deleted instance!")
rajalakshmi-ganesan32f8db62012-05-18 19:13:40 +0530104
105 @attr(type='negative')
106 def test_create_image_from_invalid_server(self):
107 """An image should not be created with invalid server id"""
108 try:
109 # Create a new image with invalid server id
110 name = rand_name('image')
111 meta = {'image_type': 'test'}
112 resp = {}
113 resp['status'] = None
114 resp, body = self.client.create_image('!@#$%^&*()', name, meta)
115
116 except exceptions.NotFound:
117 pass
118
119 finally:
Zhongyue Luoe471d6e2012-09-17 17:02:43 +0800120 if (resp['status'] is not None):
Jay Pipesf38eaac2012-06-21 13:37:35 -0400121 image_id = parse_image_id(resp['location'])
rajalakshmi-ganesan32f8db62012-05-18 19:13:40 +0530122 resp, _ = self.client.delete_image(image_id)
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800123 self.fail("An image should not be created "
124 "with invalid server id")
rajalakshmi-ganesan32f8db62012-05-18 19:13:40 +0530125
126 @attr(type='negative')
Jay Pipesf38eaac2012-06-21 13:37:35 -0400127 @unittest.skipUnless(compute.MULTI_USER, 'Second user not configured')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700128 def test_create_image_for_server_in_another_tenant(self):
129 """Creating image of another tenant's server should be return error"""
130 server = self.create_server()
131
132 snapshot_name = rand_name('test-snap-')
133 self.assertRaises(exceptions.NotFound, self.alt_client.create_image,
134 server['id'], snapshot_name)
135
136 @attr(type='negative')
137 def test_create_image_when_server_is_building(self):
138 """Return error when creating an image of a server that is building"""
139 server_name = rand_name('test-vm-')
140 resp, server = self.servers_client.create_server(server_name,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800141 self.image_ref,
142 self.flavor_ref)
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700143 self.servers.append(server)
144 snapshot_name = rand_name('test-snap-')
145 self.assertRaises(exceptions.Duplicate, self.client.create_image,
146 server['id'], snapshot_name)
147
David Kranz0312b692012-08-21 17:51:17 -0400148 @unittest.skip("Until Bug 1039739 is fixed")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700149 @attr(type='negative')
150 def test_create_image_when_server_is_rebooting(self):
151 """Return error when creating an image of server that is rebooting"""
152 server = self.create_server()
153 self.servers_client.reboot(server['id'], 'HARD')
154
155 snapshot_name = rand_name('test-snap-')
156 self.assertRaises(exceptions.Duplicate, self.client.create_image,
157 server['id'], snapshot_name)
158
159 @attr(type='negative')
160 def test_create_image_when_server_is_terminating(self):
161 """Return an error when creating image of server that is terminating"""
162 server = self.create_server()
163 self.servers_client.delete_server(server['id'])
164
165 snapshot_name = rand_name('test-snap-')
166 self.assertRaises(exceptions.Duplicate, self.client.create_image,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800167 server['id'], snapshot_name)
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700168
169 @attr(type='negative')
170 def test_create_second_image_when_first_image_is_being_saved(self):
171 """Disallow creating another image when first image is being saved"""
172 server = self.create_server()
173
174 try:
175 # Create first snapshot
176 snapshot_name = rand_name('test-snap-')
177 resp, body = self.client.create_image(server['id'], snapshot_name)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400178 image_id = parse_image_id(resp['location'])
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700179 self.image_ids.append(image_id)
180
181 # Create second snapshot
182 alt_snapshot_name = rand_name('test-snap-')
183 self.client.create_image(server['id'], alt_snapshot_name)
184 except exceptions.Duplicate:
185 pass
186
187 else:
188 self.fail("Should allow creating an image when another image of"
189 "the server is still being saved")
190
191 @attr(type='negative')
192 @unittest.skip("Until Bug 1004564 is fixed")
193 def test_create_image_specify_name_over_256_chars(self):
194 """Return an error if snapshot name over 256 characters is passed"""
195 server = self.create_server()
196
197 try:
198 snapshot_name = rand_name('a' * 260)
199 self.assertRaises(exceptions.BadRequest, self.client.create_image,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800200 server['id'], snapshot_name)
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700201 except:
202 self.fail("Should return 400 Bad Request if image name is over 256"
203 " characters")
204
205 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700206 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)
Joe Gordonf26620f2012-09-13 12:34:35 -0700211 self.assertRaises(exceptions.NotFound, self.client.create_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700212 test_uuid, snapshot_name)
213 except:
Joe Gordonf26620f2012-09-13 12:34:35 -0700214 self.fail("Should return 404 Not Found if server uuid is 35"
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700215 " characters or less")
216
217 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700218 def test_create_image_specify_uuid_37_characters_or_more(self):
219 """Return an error if Image ID passed is 37 characters or more"""
220 try:
221 snapshot_name = rand_name('test-snap-')
222 test_uuid = ('a' * 37)
Joe Gordonf26620f2012-09-13 12:34:35 -0700223 self.assertRaises(exceptions.NotFound, self.client.create_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700224 test_uuid, snapshot_name)
225 except:
Joe Gordonf26620f2012-09-13 12:34:35 -0700226 self.fail("Should return 404 Not Found if server uuid is 37"
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700227 " characters or more")
228
229 @attr(type='negative')
230 @unittest.skip("Until Bug 1006725 is fixed")
231 def test_create_image_specify_multibyte_character_image_name(self):
232 """Return an error if the image name has multi-byte characters"""
233 server = self.create_server()
234
235 try:
236 snapshot_name = rand_name('\xef\xbb\xbf')
237 self.assertRaises(exceptions.BadRequest,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800238 self.client.create_image, server['id'],
239 snapshot_name)
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700240 except:
241 self.fail("Should return 400 Bad Request if multi byte characters"
242 " are used for image name")
243
244 @attr(type='negative')
245 @unittest.skip("Until Bug 1005423 is fixed")
246 def test_create_image_specify_invalid_metadata(self):
247 """Return an error when creating image with invalid metadata"""
248 server = self.create_server()
249
250 try:
251 snapshot_name = rand_name('test-snap-')
252 meta = {'': ''}
253 self.assertRaises(exceptions.BadRequest, self.client.create_image,
254 server['id'], snapshot_name, meta)
255
256 except:
257 self.fail("Should raise 400 Bad Request if meta data is invalid")
258
259 @attr(type='negative')
260 @unittest.skip("Until Bug 1005423 is fixed")
261 def test_create_image_specify_metadata_over_limits(self):
262 """Return an error when creating image with meta data over 256 chars"""
263 server = self.create_server()
264
265 try:
266 snapshot_name = rand_name('test-snap-')
267 meta = {'a' * 260: 'b' * 260}
268 self.assertRaises(exceptions.OverLimit, self.client.create_image,
269 server['id'], snapshot_name, meta)
270
271 except:
272 self.fail("Should raise 413 Over Limit if meta data was too long")
273
274 @attr(type='negative')
rajalakshmi-ganesan32f8db62012-05-18 19:13:40 +0530275 def test_delete_image_with_invalid_image_id(self):
276 """An image should not be deleted with invalid image id"""
277 try:
278 # Delete an image with invalid image id
279 resp, _ = self.client.delete_image('!@$%^&*()')
280
281 except exceptions.NotFound:
282 pass
283
284 else:
Zhongyue Luoe0884a32012-09-25 17:24:17 +0800285 self.fail("DELETE image request should rasie NotFound exception "
286 "when requested with invalid image")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700287
288 @attr(type='negative')
289 def test_delete_non_existent_image(self):
290 """Return an error while trying to delete a non-existent image"""
291
292 non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
293 self.assertRaises(exceptions.NotFound, self.client.delete_image,
294 non_existent_image_id)
295
296 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700297 def test_delete_image_blank_id(self):
298 """Return an error while trying to delete an image with blank Id"""
299
300 try:
David Kranz28e35c52012-07-10 10:14:38 -0400301 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700302 '')
303 except:
David Kranz28e35c52012-07-10 10:14:38 -0400304 self.fail("Did not return HTTP 404 NotFound for blank image id")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700305
306 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700307 def test_delete_image_non_hex_string_id(self):
308 """Return an error while trying to delete an image with non hex id"""
309
310 image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
311 try:
David Kranz28e35c52012-07-10 10:14:38 -0400312 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800313 image_id)
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700314 except:
David Kranz28e35c52012-07-10 10:14:38 -0400315 self.fail("Did not return HTTP 404 NotFound for non hex image")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700316
317 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700318 def test_delete_image_negative_image_id(self):
319 """Return an error while trying to delete an image with negative id"""
320
321 try:
David Kranz28e35c52012-07-10 10:14:38 -0400322 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700323 -1)
324 except:
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800325 self.fail("Did not return HTTP 404 NotFound for negative image id")
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700326
327 @attr(type='negative')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700328 def test_delete_image_id_is_over_35_character_limit(self):
329 """Return an error while trying to delete image with id over limit"""
330
331 try:
David Kranz28e35c52012-07-10 10:14:38 -0400332 self.assertRaises(exceptions.NotFound, self.client.delete_image,
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700333 '11a22b9-120q-5555-cc11-00ab112223gj-3fac')
334 except:
David Kranz28e35c52012-07-10 10:14:38 -0400335 self.fail("Did not return HTTP 404 NotFound for image id that "
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700336 "exceeds 35 character ID length limit")
337
338 @attr(type='negative')
Jay Pipesf38eaac2012-06-21 13:37:35 -0400339 @unittest.skipUnless(compute.MULTI_USER, 'Second user not configured')
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700340 def test_delete_image_of_another_tenant(self):
341 """Return an error while trying to delete another tenant's image"""
342
343 server = self.create_server()
344
345 snapshot_name = rand_name('test-snap-')
346 resp, body = self.client.create_image(server['id'], snapshot_name)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400347 image_id = parse_image_id(resp['location'])
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700348 self.image_ids.append(image_id)
349 self.client.wait_for_image_resp_code(image_id, 200)
350 self.client.wait_for_image_status(image_id, 'ACTIVE')
351
352 # Delete image
353 self.assertRaises(exceptions.NotFound,
Zhongyue Luo79d8d362012-09-25 13:49:27 +0800354 self.alt_client.delete_image, image_id)
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700355
356 @attr(type='negative')
357 def test_delete_image_that_is_not_yet_active(self):
358 """Return an error while trying to delete an active that is creating"""
359
360 server = self.create_server()
361
362 snapshot_name = rand_name('test-snap-')
363 resp, body = self.client.create_image(server['id'], snapshot_name)
Jay Pipesf38eaac2012-06-21 13:37:35 -0400364 image_id = parse_image_id(resp['location'])
Rohit Karajgiea462ae2012-05-27 21:23:21 -0700365 self.image_ids.append(image_id)
366
367 # Do not wait, attempt to delete the image, ensure it's successful
368 resp, body = self.client.delete_image(image_id)
369 self.assertEqual('204', resp['status'])
370 self.image_ids.remove(image_id)
371
372 self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
Dan Smithe7316bb2012-08-14 12:35:34 -0700373
374
375class ImagesTestJSON(base.BaseComputeTestJSON,
376 ImagesTestBase):
377 def tearDown(self):
378 ImagesTestBase.tearDown(self)
379
380 @classmethod
381 def setUpClass(cls):
David Kranz6cdcfb62012-09-06 11:02:58 -0400382 raise nose.SkipTest("Until Bug 1046870 is fixed")
Dan Smithe7316bb2012-08-14 12:35:34 -0700383 super(ImagesTestJSON, cls).setUpClass()
384 cls.client = cls.images_client
385 cls.servers_client = cls.servers_client
386
387 cls.image_ids = []
388
389 if compute.MULTI_USER:
390 if cls.config.compute.allow_tenant_isolation:
391 creds = cls._get_isolated_creds()
392 username, tenant_name, password = creds
393 cls.alt_manager = openstack.Manager(username=username,
394 password=password,
395 tenant_name=tenant_name)
396 else:
397 # Use the alt_XXX credentials in the config file
398 cls.alt_manager = openstack.AltManager()
399 cls.alt_client = cls.alt_manager.images_client
400
401
402class ImagesTestXML(base.BaseComputeTestXML,
403 ImagesTestBase):
404 def tearDown(self):
405 ImagesTestBase.tearDown(self)
406
407 @classmethod
408 def setUpClass(cls):
David Kranz6cdcfb62012-09-06 11:02:58 -0400409 raise nose.SkipTest("Until Bug 1046870 is fixed")
Dan Smithe7316bb2012-08-14 12:35:34 -0700410 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