Merge lp:~justin-fathomdb/nova/test-openstack-api-servers into lp:~hudson-openstack/nova/trunk

Proposed by justinsb
Status: Work in progress
Proposed branch: lp:~justin-fathomdb/nova/test-openstack-api-servers
Merge into: lp:~hudson-openstack/nova/trunk
Prerequisite: lp:~justin-fathomdb/nova/justinsb-openstack-api-volumes
Diff against target: 657 lines (+458/-29)
7 files modified
nova/api/openstack/ratelimiting/__init__.py (+13/-5)
nova/image/fake.py (+108/-0)
nova/tests/integrated/api/client.py (+40/-0)
nova/tests/integrated/integrated_helpers.py (+77/-22)
nova/tests/integrated/test_keys.py (+2/-1)
nova/tests/integrated/test_servers.py (+216/-0)
nova/tests/integrated/test_volumes.py (+2/-1)
To merge this branch: bzr merge lp:~justin-fathomdb/nova/test-openstack-api-servers
Reviewer Review Type Date Requested Status
Nova Core security contacts Pending
Review via email: mp+51089@code.launchpad.net

Description of the change

Basic unit testing of the OpenStack API for /servers.

To post a comment you must log in.
Revision history for this message
Rick Harris (rconradharris) wrote :
Download full text (3.2 KiB)

Overall this looks good.

The known_bugs addition looks pretty nifty (but I worry a bit about the added complexity of it). The fake image service looks like a really nice addition.

Below are a few femto-nits:

658 + # Not supported till Python 2.7 / 3.1
659 + #@unittest.skipIf(
660 + # 'server_create_metadata_throws_unhashable_type' in KNOWN_BUGS,
661 + # 'KNOWN_BUG:server_create_metadata_throws_unhashable_type')

If we're going to go this route, perhaps we could roll our own decorator which the TestRunner can use to skip the relevant tests. This would make adoption of this under 2.6 just as easy as 2.7+.

Something along the lines of:

@tests_known_bug("my_known_bug")
def test_my_known_bug(self):
    self.assert_(False)

586 +# print driver.LoggingVolumeDriver.all_logs()
587 +#
588 +#
589 +# create_actions = driver.LoggingVolumeDriver.logs_like(
590 +# 'create_volume',
591 +# id=created_volume_id)
592 +# print create_actions
593 +# self.assertEquals(1, len(create_actions))
594 +# create_action = create_actions[0]
595 +# self.assertEquals(create_action['id'], created_server_id)
596 +# self.assertEquals(create_action['availability_zone'], 'nova')
597 +# self.assertEquals(create_action['size'], 1)
598 +#
599 +# export_actions = driver.LoggingVolumeDriver.logs_like(
600 +# 'create_export',
601 +# id=created_server_id)
602 +# self.assertEquals(1, len(export_actions))
603 +# export_action = export_actions[0]
604 +# self.assertEquals(export_action['id'], created_server_id)
605 +# self.assertEquals(export_action['availability_zone'], 'nova')
606 +#
607 +# delete_actions = driver.LoggingVolumeDriver.logs_like(
608 +# 'delete_volume',
609 +# id=created_server_id)
610 +# self.assertEquals(1, len(delete_actions))
611 +# delete_action = export_actions[0]
612 +# self.assertEquals(delete_action['id'], created_server_id)

Was this left in on purpose? If so, it probably should have a TODO with it.

> 509 + def test_get_servers(self):
> 510 + """Simple check that listing servers works"""
> 511 + servers = self.api.get_servers()
> 512 + for server in servers:
> 513 + print("server: %s" % server)

Can we put an assert in here? Perhaps just a assertNotRaises or something.

> 334 + raise exception.Error("No way to create an image through API??")

This line (and a few others) need i18n treatment, the _("blah").

> 628 + #if found_server['status'] != 'deleting':
> 629 + # break

Left in by accident, or does that need a TODO/FIXME?

> 129 + raise exception.NotFound

Might be helpful to pass in a string with the Exception which includes the Id of the image not found. Something like

  raise exception.NotFound(_("Image %(image_id)s not found) % locals())

> 489 +import time

Should be moved up to top section of imports (per HACKING)

> 641 + #TODO(justinsb): This is FUBAR

Agree wit...

Read more...

Revision history for this message
Soren Hansen (soren) wrote :

What's the status of this? There are concrete comments here, but the branch hasn't been touched for more than a week. Perhaps just set it back to Work in Progress?

Revision history for this message
justinsb (justin-fathomdb) wrote :

Soren: The branch has got unmerged pre-reqs. Here be dragons.

Revision history for this message
justinsb (justin-fathomdb) wrote :

Dragons slayed. There are still unmerged pre-reqs though, so reviewers please focus on those first!

Unmerged revisions

732. By justinsb

PEP8 fun

731. By justinsb

Finally figured out how to work around the latest flip-flops on what the image service should be returning

730. By justinsb

More changing of print -> LOG.debug

729. By justinsb

Removed known_bugs based on community opinion. Commented out the test cases that otherwise won't work.

728. By justinsb

Merged with head, fixing up bazaar's pathetic attempts at conflict resolution.

727. By justinsb

PEP8 fix

726. By justinsb

Need spurious import to pull in flags

725. By justinsb

Scale up rate limits for tests massively, to effectively turn them off

724. By justinsb

More careful checking of servers detail vs non-detail views

723. By justinsb

Add integrated unit tests for server metadata. Also add KNOWN_BUGS list, so that we can test bugs separately from fixing them

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/api/openstack/ratelimiting/__init__.py'
2--- nova/api/openstack/ratelimiting/__init__.py 2011-01-06 18:57:48 +0000
3+++ nova/api/openstack/ratelimiting/__init__.py 2011-03-08 02:57:47 +0000
4@@ -23,9 +23,15 @@
5 import webob.dec
6 import webob.exc
7
8+from nova import flags
9 from nova import wsgi
10 from nova.api.openstack import faults
11
12+
13+FLAGS = flags.FLAGS
14+flags.DEFINE_integer('rate_limit_multiplier', 1,
15+ 'Factor by which to scale up rate-limits')
16+
17 # Convenience constants for the limits dictionary passed to Limiter().
18 PER_SECOND = 1
19 PER_MINUTE = 60
20@@ -44,14 +50,16 @@
21 at the given host+port to keep rate counters.
22 """
23 if not service_host:
24+ scale = FLAGS.rate_limit_multiplier
25+
26 #TODO(gundlach): These limits were based on limitations of Cloud
27 #Servers. We should revisit them in Nova.
28 self.limiter = Limiter(limits={
29- 'DELETE': (100, PER_MINUTE),
30- 'PUT': (10, PER_MINUTE),
31- 'POST': (10, PER_MINUTE),
32- 'POST servers': (50, PER_DAY),
33- 'GET changes-since': (3, PER_MINUTE),
34+ 'DELETE': (100 * scale, PER_MINUTE),
35+ 'PUT': (10 * scale, PER_MINUTE),
36+ 'POST': (10 * scale, PER_MINUTE),
37+ 'POST servers': (50 * scale, PER_DAY),
38+ 'GET changes-since': (3 * scale, PER_MINUTE),
39 })
40 else:
41 self.limiter = WSGIAppProxy(service_host)
42
43=== added file 'nova/image/fake.py'
44--- nova/image/fake.py 1970-01-01 00:00:00 +0000
45+++ nova/image/fake.py 2011-03-08 02:57:47 +0000
46@@ -0,0 +1,108 @@
47+# vim: tabstop=4 shiftwidth=4 softtabstop=4
48+
49+# Copyright 2011 Justin Santa Barbara
50+# All Rights Reserved.
51+#
52+# Licensed under the Apache License, Version 2.0 (the "License"); you may
53+# not use this file except in compliance with the License. You may obtain
54+# a copy of the License at
55+#
56+# http://www.apache.org/licenses/LICENSE-2.0
57+#
58+# Unless required by applicable law or agreed to in writing, software
59+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
60+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
61+# License for the specific language governing permissions and limitations
62+# under the License.
63+"""Implementation of an fake image service"""
64+
65+from nova import exception
66+from nova import flags
67+from nova import log as logging
68+from nova.image import service
69+
70+
71+LOG = logging.getLogger('nova.image.fake')
72+
73+FLAGS = flags.FLAGS
74+
75+
76+class MockImageService(service.BaseImageService):
77+ """Mock (fake) image service for unit testing"""
78+
79+ def __init__(self):
80+ self.images = {}
81+ # NOTE(justinsb): The OpenStack API can't upload an image???
82+ # So, make sure we've got one..
83+ image = {'id': '123456',
84+ 'status': 'active',
85+ 'type': 'machine',
86+ 'properties': {'kernel_id': FLAGS.null_kernel,
87+ 'ramdisk_id': FLAGS.null_kernel,
88+ }
89+ }
90+ self.create(None, image)
91+ super(MockImageService, self).__init__()
92+
93+ def index(self, context):
94+ """Returns list of images"""
95+ return self.images.values()
96+
97+ def detail(self, context):
98+ """Return list of detailed image information"""
99+ return self.images.values()
100+
101+ def show(self, context, image_id):
102+ """
103+ Returns a dict containing image data for the given opaque image id.
104+ """
105+ image_id = int(image_id)
106+ image = self.images.get(image_id)
107+ if image:
108+ return image
109+ LOG.warn("Unable to find image id %s. Have images: %s",
110+ image_id, self.images)
111+ raise exception.NotFound
112+
113+ def create(self, context, data):
114+ """
115+ Store the image data and return the new image id.
116+
117+ :raises AlreadyExists if the image already exist.
118+
119+ """
120+ image_id = int(data['id'])
121+ if self.images.get(image_id):
122+ #TODO(justinsb): Where is this AlreadyExists exception??
123+ raise exception.Error("AlreadyExists")
124+
125+ self.images[image_id] = data
126+
127+ def update(self, context, image_id, data):
128+ """Replace the contents of the given image with the new data.
129+
130+ :raises NotFound if the image does not exist.
131+
132+ """
133+ image_id = int(image_id)
134+ if not self.images.get(image_id):
135+ raise exception.NotFound
136+ self.images[image_id] = data
137+
138+ def delete(self, context, image_id):
139+ """
140+ Delete the given image.
141+
142+ :raises NotFound if the image does not exist.
143+
144+ """
145+ image_id = int(image_id)
146+ removed = self.images.pop(image_id, None)
147+ if not removed:
148+ raise exception.NotFound
149+
150+ def delete_all(self):
151+ """
152+ Clears out all images
153+ """
154+ self.images.clear()
155
156=== modified file 'nova/tests/integrated/api/client.py'
157--- nova/tests/integrated/api/client.py 2011-03-08 02:57:47 +0000
158+++ nova/tests/integrated/api/client.py 2011-03-08 02:57:47 +0000
159@@ -88,6 +88,9 @@
160 if parsed_url.query:
161 relative_url = relative_url + parsed_url.query
162 LOG.info(_("Doing %(method)s on %(relative_url)s") % locals())
163+ if body:
164+ LOG.info(_("Body: %s") % body)
165+
166 conn.request(method, relative_url, body, headers)
167 response = conn.getresponse()
168 return response
169@@ -182,3 +185,40 @@
170
171 def delete_volume(self, volume_id):
172 return self.api_delete('/volumes/%s' % volume_id)
173+
174+ def get_server(self, server_id):
175+ return self.api_get('/servers/%s' % server_id)['server']
176+
177+ def get_servers(self, detail=True):
178+ rel_url = '/servers/detail' if detail else '/servers'
179+ return self.api_get(rel_url)['servers']
180+
181+ def post_server(self, server):
182+ return self.api_post('/servers', server)['server']
183+
184+ def delete_server(self, server_id):
185+ return self.api_delete('/servers/%s' % server_id)
186+
187+ def get_image(self, image_id):
188+ return self.api_get('/images/%s' % image_id)['image']
189+
190+ def get_images_detail(self):
191+ return self.api_get('/images/detail')['images']
192+
193+ def post_image(self, image):
194+ return self.api_post('/images', image)['image']
195+
196+ def delete_image(self, image_id):
197+ return self.api_delete('/images/%s' % image_id)
198+
199+ def get_flavor(self, flavor_id):
200+ return self.api_get('/flavors/%s' % flavor_id)['flavor']
201+
202+ def get_flavors_detail(self):
203+ return self.api_get('/flavors/detail')['flavors']
204+
205+ def post_flavor(self, flavor):
206+ return self.api_post('/flavors', flavor)['flavor']
207+
208+ def delete_flavor(self, flavor_id):
209+ return self.api_delete('/flavors/%s' % flavor_id)
210
211=== modified file 'nova/tests/integrated/integrated_helpers.py'
212--- nova/tests/integrated/integrated_helpers.py 2011-03-08 02:57:47 +0000
213+++ nova/tests/integrated/integrated_helpers.py 2011-03-08 02:57:47 +0000
214@@ -26,29 +26,89 @@
215 from nova import flags
216 from nova import service
217 from nova.api import openstack # For FLAGS.allow_testing_api
218+from nova.api.openstack import ratelimiting # For FLAGS.rate_limit_multiplier
219 from nova.auth import manager
220 from nova.exception import Error
221 from nova.log import logging
222 from nova.tests.integrated.api import client
223
224+
225 FLAGS = flags.FLAGS
226
227
228 def generate_random_alphanumeric(length):
229- """Creates a random string of specified length"""
230+ """Creates a random alphanumeric string of specified length"""
231 return ''.join(random.choice(string.ascii_uppercase + string.digits)
232 for _x in range(length))
233
234
235-def generate_new_element(items, prefix):
236+def generate_random_numeric(length):
237+ """Creates a random numeric string of specified length"""
238+ return ''.join(random.choice(string.digits)
239+ for _x in range(length))
240+
241+
242+def generate_new_element(items, prefix, numeric=False):
243 """Creates a random string with prefix, that is not in 'items' list"""
244 while True:
245- candidate = prefix + generate_random_alphanumeric(8)
246+ if numeric:
247+ candidate = prefix + generate_random_numeric(8)
248+ else:
249+ candidate = prefix + generate_random_alphanumeric(8)
250 if not candidate in items:
251 return candidate
252 print "Random collision on %s" % candidate
253
254
255+class TestUser(object):
256+ def __init__(self, name, secret, auth_url):
257+ self.name = name
258+ self.secret = secret
259+
260+ if not auth_url:
261+ raise exception.Error("auth_url is required")
262+ self.openstack_api = client.TestOpenStackClient(self.name,
263+ self.secret,
264+ auth_url)
265+
266+ self.key_pairs = None
267+
268+ def get_key_pair(self, create=False):
269+ if self.key_pairs is None:
270+ self.key_pairs = self.openstack_api.get_keys_detail()
271+ if create and not self.key_pairs:
272+ key = {'key': {'name': 'key0'}}
273+ created_key = self.openstack_api.post_key(key)
274+ self.key_pairs.append(created_key)
275+
276+ if self.key_pairs:
277+ return self.key_pairs[0]
278+
279+ return None
280+
281+ def get_unused_server_name(self, create=False):
282+ servers = self.openstack_api.get_servers()
283+ server_names = [server['name'] for server in servers]
284+ return generate_new_element(server_names, 'server')
285+
286+ def get_invalid_image(self):
287+ images = self.openstack_api.get_images_detail()
288+ image_ids = [image['id'] for image in images]
289+ return generate_new_element(image_ids, '', numeric=True)
290+
291+ def get_valid_image(self, create=False):
292+ images = self.openstack_api.get_images_detail()
293+ if create and not images:
294+ # TODO(justinsb): No way to create an image through API???
295+ #created_image = self.openstack_api.post_image(image)
296+ #images.append(created_image)
297+ raise exception.Error("No way to create an image through API??")
298+
299+ if images:
300+ return images[0]
301+ return None
302+
303+
304 class IntegratedUnitTestContext(object):
305 __INSTANCE = None
306
307@@ -62,20 +122,17 @@
308 self.saved_flags = {}
309 self.services = []
310 self.auth_url = None
311- self.auth_user = None
312- self.auth_key = None
313 self.project_name = None
314- self.openstack_api = None
315
316 self.setup()
317
318 def setup(self):
319 self._configure_flags()
320
321+ self._start_services()
322+
323 self._create_test_user()
324
325- self._start_services()
326-
327 def _configure_flags(self):
328 self._overwrite_flag('allow_testing_api', True)
329 if not FLAGS.allow_testing_api:
330@@ -88,6 +145,11 @@
331 self._overwrite_flag('fake_rabbit', False)
332 self._overwrite_flag('volume_driver',
333 'nova.volume.driver.LoggingVolumeDriver')
334+ self._overwrite_flag('image_service',
335+ 'nova.image.fake.MockImageService')
336+ self._overwrite_flag('connection_type', 'fake')
337+ # Scale up rate limits by 10000x; effectively turning them off
338+ self._overwrite_flag('rate_limit_multiplier', 10000)
339
340 def _overwrite_flag(self, flag_key, flag_value):
341 self.saved_flags[flag_key] = FLAGS[flag_key].value
342@@ -100,11 +162,11 @@
343 self.saved_flags.clear()
344
345 def _create_test_user(self):
346- self.auth_user, self.auth_key = self._create_unittest_user()
347+ self.test_user = self._create_unittest_user()
348
349 # No way to currently pass this through the OpenStack API
350 self.project_name = 'openstack'
351- self._configure_project(self.project_name)
352+ self._configure_project(self.project_name, self.test_user)
353
354 def _start_services(self):
355 # WSGI shutdown broken :-(
356@@ -114,10 +176,6 @@
357 self._start_volume_service()
358 self._start_scheduler_service()
359
360- self.openstack_api = client.TestOpenStackClient(self.auth_user,
361- self.auth_key,
362- self.auth_url)
363-
364 def cleanup(self):
365 for service in self.services:
366 service.kill()
367@@ -127,7 +185,7 @@
368 # self.wsgi_server.terminate()
369 # self.wsgi_server = None
370 self._restore_flags()
371- self.openstack_api = None
372+ self.test_user = None
373
374 def _create_unittest_user(self):
375 users = self.auth_manager.get_users()
376@@ -139,18 +197,18 @@
377 auth_key = auth_name
378
379 self.auth_manager.create_user(auth_name, auth_name, auth_key, False)
380- return auth_name, auth_key
381+ return TestUser(auth_name, auth_key, self.auth_url)
382
383- def _configure_project(self, project_name):
384+ def _configure_project(self, project_name, user):
385 projects = self.auth_manager.get_projects()
386 project_names = [project.name for project in projects]
387 if not project_name in project_names:
388 project = self.auth_manager.create_project(project_name,
389- self.auth_user,
390+ user.name,
391 description=None,
392 member_users=None)
393 else:
394- self.auth_manager.add_to_project(self.auth_user, project_name)
395+ self.auth_manager.add_to_project(user.name, project_name)
396
397 def _start_api_service(self):
398 api_service = service.ApiService.create()
399@@ -179,9 +237,6 @@
400 self.services.append(scheduler_service)
401 return scheduler_service
402
403- def get_openstack_api(self):
404- return self.openstack_api
405-
406 # WSGI shutdown broken :-(
407 #@staticmethod
408 #def get():
409
410=== modified file 'nova/tests/integrated/test_keys.py'
411--- nova/tests/integrated/test_keys.py 2011-03-08 02:57:47 +0000
412+++ nova/tests/integrated/test_keys.py 2011-03-08 02:57:47 +0000
413@@ -28,7 +28,8 @@
414 class KeysTest(unittest.TestCase):
415 def setUp(self):
416 context = integrated_helpers.IntegratedUnitTestContext.startup()
417- self.api = context.get_openstack_api()
418+ self.user = context.test_user
419+ self.api = self.user.openstack_api
420
421 def tearDown(self):
422 integrated_helpers.IntegratedUnitTestContext.shutdown()
423
424=== added file 'nova/tests/integrated/test_servers.py'
425--- nova/tests/integrated/test_servers.py 1970-01-01 00:00:00 +0000
426+++ nova/tests/integrated/test_servers.py 2011-03-08 02:57:47 +0000
427@@ -0,0 +1,216 @@
428+# vim: tabstop=4 shiftwidth=4 softtabstop=4
429+
430+# Copyright 2011 Justin Santa Barbara
431+# All Rights Reserved.
432+#
433+# Licensed under the Apache License, Version 2.0 (the "License"); you may
434+# not use this file except in compliance with the License. You may obtain
435+# a copy of the License at
436+#
437+# http://www.apache.org/licenses/LICENSE-2.0
438+#
439+# Unless required by applicable law or agreed to in writing, software
440+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
441+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
442+# License for the specific language governing permissions and limitations
443+# under the License.
444+
445+import unittest
446+
447+from nova import flags
448+from nova import test # For the flags
449+from nova.log import logging
450+from nova.tests.integrated import integrated_helpers
451+from nova.tests.integrated.api import client
452+import time
453+
454+
455+LOG = logging.getLogger('nova.tests.integrated')
456+
457+FLAGS = flags.FLAGS
458+FLAGS.verbose = True
459+
460+
461+class ServersTest(unittest.TestCase):
462+ def setUp(self):
463+ context = integrated_helpers.IntegratedUnitTestContext.startup()
464+ self.user = context.test_user
465+ self.api = self.user.openstack_api
466+
467+ # Make sure we have a key. API defaults to using this key
468+ self.key = self.user.get_key_pair(create=True)
469+
470+ def tearDown(self):
471+ integrated_helpers.IntegratedUnitTestContext.shutdown()
472+
473+ def test_get_servers(self):
474+ """Simple check that listing servers works"""
475+ servers = self.api.get_servers()
476+ for server in servers:
477+ LOG.debug("server: %s" % server)
478+
479+ def test_create_and_delete_server(self):
480+ """Creates and deletes a server"""
481+
482+ # Create server
483+
484+ # Build the server data gradually, checking errors along the way
485+ server = {}
486+ good_server = self._build_minimal_create_server_request()
487+
488+ post = {'server': server}
489+
490+ # Without an imageId, this throws 500.
491+ # TODO(justinsb): Check whatever the spec says should be thrown here
492+ self.assertRaises(client.OpenstackApiException,
493+ self.api.post_server, post)
494+
495+ # With an invalid imageId, this throws 500.
496+ server['imageId'] = self.user.get_invalid_image()
497+ # TODO(justinsb): Check whatever the spec says should be thrown here
498+ self.assertRaises(client.OpenstackApiException,
499+ self.api.post_server, post)
500+
501+ # Add a valid imageId
502+ server['imageId'] = good_server['imageId']
503+
504+ # Without flavorId, this throws 500
505+ # TODO(justinsb): Check whatever the spec says should be thrown here
506+ self.assertRaises(client.OpenstackApiException,
507+ self.api.post_server, post)
508+
509+ # Set a valid flavorId
510+ server['flavorId'] = good_server['flavorId']
511+
512+ # Without a name, this throws 500
513+ # TODO(justinsb): Check whatever the spec says should be thrown here
514+ self.assertRaises(client.OpenstackApiException,
515+ self.api.post_server, post)
516+
517+ # Set a valid server name
518+ server['name'] = good_server['name']
519+
520+ created_server = self.api.post_server(post)
521+ LOG.debug("created_server: %s" % created_server)
522+ self.assertTrue(created_server['id'])
523+ created_server_id = created_server['id']
524+
525+ # Check it's there
526+ found_server = self.api.get_server(created_server_id)
527+ self.assertEqual(created_server_id, found_server['id'])
528+
529+ # It should also be in the all-servers list
530+ servers = self.api.get_servers()
531+ server_ids = [server['id'] for server in servers]
532+ self.assertTrue(created_server_id in server_ids)
533+
534+ # Wait (briefly) for creation
535+ retries = 0
536+ while found_server['status'] == 'build':
537+ LOG.debug("found server: %s" % found_server)
538+ time.sleep(1)
539+ found_server = self.api.get_server(created_server_id)
540+ retries = retries + 1
541+ if retries > 5:
542+ break
543+
544+ # It should be available...
545+ # TODO(justinsb): Mock doesn't yet do this...
546+ #self.assertEqual('available', found_server['status'])
547+
548+ self._delete_server(created_server_id)
549+
550+ def _delete_server(self, server_id):
551+ # Delete the server
552+ self.api.delete_server(server_id)
553+
554+ # Wait (briefly) for deletion
555+ for _retries in range(5):
556+ try:
557+ found_server = self.api.get_server(server_id)
558+ except client.OpenstackApiNotFoundException:
559+ found_server = None
560+ LOG.debug("Got 404, proceeding")
561+ break
562+
563+ LOG.debug("Found_server=%s" % found_server)
564+
565+ # TODO(justinsb): Mock doesn't yet do accurate state changes
566+ #if found_server['status'] != 'deleting':
567+ # break
568+ time.sleep(1)
569+
570+ # Should be gone
571+ self.assertFalse(found_server)
572+
573+ def _build_minimal_create_server_request(self):
574+ server = {}
575+
576+ image = self.user.get_valid_image(create=True)
577+ image_id = image['id']
578+
579+ #TODO(justinsb): This is FUBAR
580+ image_id = abs(hash(image_id))
581+
582+ # We now have a valid imageId
583+ server['imageId'] = image_id
584+
585+ # Set a valid flavorId
586+ flavor = self.api.get_flavors_detail()[0]
587+ LOG.debug("Using flavor: %s" % flavor)
588+ server['flavorId'] = flavor['id']
589+
590+ # Set a valid server name
591+ server_name = self.user.get_unused_server_name()
592+ server['name'] = server_name
593+
594+ return server
595+
596+# TODO(justinsb): Enable this unit test when the metadata bug is fixed
597+# def test_create_server_with_metadata(self):
598+# """Creates a server with metadata"""
599+#
600+# # Build the server data gradually, checking errors along the way
601+# server = self._build_minimal_create_server_request()
602+#
603+# for metadata_count in range(30):
604+# metadata = {}
605+# for i in range(metadata_count):
606+# metadata['key_%s' % i] = 'value_%s' % i
607+# server['metadata'] = metadata
608+#
609+# post = {'server': server}
610+# created_server = self.api.post_server(post)
611+# LOG.debug("created_server: %s" % created_server)
612+# self.assertTrue(created_server['id'])
613+# created_server_id = created_server['id']
614+# # Reenable when bug fixed
615+# # self.assertEqual(metadata, created_server.get('metadata'))
616+#
617+# # Check it's there
618+# found_server = self.api.get_server(created_server_id)
619+# self.assertEqual(created_server_id, found_server['id'])
620+# self.assertEqual(metadata, found_server.get('metadata'))
621+#
622+# # The server should also be in the all-servers details list
623+# servers = self.api.get_servers(detail=True)
624+# server_map = dict((server['id'], server) for server in servers)
625+# found_server = server_map.get(created_server_id)
626+# self.assertTrue(found_server)
627+# # Details do include metadata
628+# self.assertEqual(metadata, found_server.get('metadata'))
629+#
630+# # The server should also be in the all-servers summary list
631+# servers = self.api.get_servers(detail=False)
632+# server_map = dict((server['id'], server) for server in servers)
633+# found_server = server_map.get(created_server_id)
634+# self.assertTrue(found_server)
635+# # Summary should not include metadata
636+# self.assertFalse(found_server.get('metadata'))
637+#
638+# # Cleanup
639+# self._delete_server(created_server_id)
640+
641+
642+if __name__ == "__main__":
643+ unittest.main()
644
645=== modified file 'nova/tests/integrated/test_volumes.py'
646--- nova/tests/integrated/test_volumes.py 2011-03-08 02:57:47 +0000
647+++ nova/tests/integrated/test_volumes.py 2011-03-08 02:57:47 +0000
648@@ -31,7 +31,8 @@
649 class VolumesTest(unittest.TestCase):
650 def setUp(self):
651 context = integrated_helpers.IntegratedUnitTestContext.startup()
652- self.api = context.get_openstack_api()
653+ self.user = context.test_user
654+ self.api = self.user.openstack_api
655
656 def tearDown(self):
657 integrated_helpers.IntegratedUnitTestContext.shutdown()