Merge lp:~vishvananda/nova/keystone-migration-final into lp:~hudson-openstack/nova/trunk

Proposed by Vish Ishaya
Status: Merged
Approved by: Jesse Andrews
Approved revision: 1432
Merged at revision: 1478
Proposed branch: lp:~vishvananda/nova/keystone-migration-final
Merge into: lp:~hudson-openstack/nova/trunk
Prerequisite: lp:~vishvananda/nova/keystone-migration-ec2
Diff against target: 553 lines (+119/-166)
12 files modified
bin/nova-manage (+10/-9)
etc/nova/api-paste.ini (+15/-3)
nova/api/auth.py (+1/-0)
nova/api/ec2/__init__.py (+21/-0)
nova/api/ec2/admin.py (+3/-1)
nova/api/openstack/auth.py (+49/-3)
nova/auth/manager.py (+3/-0)
nova/cloudpipe/pipelib.py (+3/-6)
nova/tests/api/openstack/test_server_actions.py (+0/-8)
nova/tests/integrated/integrated_helpers.py (+13/-96)
nova/tests/integrated/test_login.py (+0/-39)
nova/tests/integrated/test_servers.py (+1/-1)
To merge this branch: bzr merge lp:~vishvananda/nova/keystone-migration-final
Reviewer Review Type Date Requested Status
Jesse Andrews (community) Approve
termie (community) Approve
Brian Waldon (community) Needs Fixing
Jay Pipes (community) Approve
Review via email: mp+71977@code.launchpad.net

Description of the change

This branch does the final tear out of AuthManager from the main code. The NoAuth middlewares (active by default) allow a user to specify any user and project id through headers (os_api) or access key (ec2_api).

The plan is to leave the auth manager code in but mention that it is deprecated. There is a sample paste config in ini to still allow old auth. Immediately after the diablo release we can tear out all of the Auth related code and not support the deprecated auth anymore.

To post a comment you must log in.
Revision history for this message
Jesse Andrews (anotherjesse) wrote :

Perhaps a silly idea:

Make it so instantiating the auth manager raises an exception unless a flag saying: use_deprecated_auth has been set?

Revision history for this message
Jay Pipes (jaypipes) wrote :

Nice stuff, Vish. I likee.

review: Approve
Revision history for this message
Brian Waldon (bcwaldon) wrote :

This is pretty awesome, Vish. Only problem is the comment on line 188.

review: Needs Fixing
1429. By Vish Ishaya

merged trunk

1430. By Vish Ishaya

change NoAuth to actually use a tenant and user

1431. By Vish Ishaya

fix comment

Revision history for this message
Vish Ishaya (vishvananda) wrote :
Revision history for this message
termie (termie) wrote :

180 + def __call__(self, req):
181 + if 'X-Auth-Token' not in req.headers:
182 + os_url = req.url
183 + version = common.get_version_from_href(os_url)
184 + user_id = req.headers.get('X-Auth-User', 'admin')
185 + project_id = req.headers.get('X-Auth-Project-Id', 'admin')
186 + if version == '1.1':
187 + os_url += '/' + project_id
188 + res = webob.Response()
189 + res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id)
190 + res.headers['X-Server-Management-Url'] = os_url
191 + res.headers['X-Storage-Url'] = ''
192 + res.headers['X-CDN-Management-Url'] = ''
193 + res.content_type = 'text/plain'
194 + res.status = '204'
195 + return res
196 + else:
197 + token = req.headers['X-Auth-Token']
198 + user_id, _sep, project_id = token.partition(':')

Just a nit, but the 'else' isn't needed, the first half will return if it succeeds, so you can just de-indent the else stuff

Looks good otherwise

review: Approve
Revision history for this message
Vish Ishaya (vishvananda) wrote :

Good point, termie, Fixed.

1432. By Vish Ishaya

unindented per review, added a note about auth v2

Revision history for this message
Jesse Andrews (anotherjesse) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/nova-manage'
2--- bin/nova-manage 2011-08-23 02:05:35 +0000
3+++ bin/nova-manage 2011-08-23 02:26:29 +0000
4@@ -134,7 +134,7 @@
5 help='Project name')
6 def list(self, project=None):
7 """Print a listing of the VPN data for one or all projects."""
8-
9+ print "WARNING: This method only works with deprecated auth"
10 print "%-12s\t" % 'project',
11 print "%-20s\t" % 'ip:port',
12 print "%-20s\t" % 'private_ip',
13@@ -170,17 +170,22 @@
14
15 def spawn(self):
16 """Run all VPNs."""
17+ print "WARNING: This method only works with deprecated auth"
18 for p in reversed(self.manager.get_projects()):
19 if not self._vpn_for(p.id):
20 print 'spawning %s' % p.id
21- self.pipe.launch_vpn_instance(p.id)
22+ self.pipe.launch_vpn_instance(p.id, p.project_manager_id)
23 time.sleep(10)
24
25 @args('--project', dest="project_id", metavar='<Project name>',
26 help='Project name')
27- def run(self, project_id):
28- """Start the VPN for a given project."""
29- self.pipe.launch_vpn_instance(project_id)
30+ @args('--user', dest="user_id", metavar='<user name>', help='User name')
31+ def run(self, project_id, user_id):
32+ """Start the VPN for a given project and user."""
33+ if not user_id:
34+ print "WARNING: This method only works with deprecated auth"
35+ user_id = self.manager.get_project(project_id).project_manager_id
36+ self.pipe.launch_vpn_instance(project_id, user_id)
37
38 @args('--project', dest="project_id", metavar='<Project name>',
39 help='Project name')
40@@ -195,10 +200,6 @@
41 """
42 # TODO(tr3buchet): perhaps this shouldn't update all networks
43 # associated with a project in the future
44- project = self.manager.get_project(project_id)
45- if not project:
46- print 'No project %s' % (project_id)
47- return
48 admin_context = context.get_admin_context()
49 networks = db.project_get_networks(admin_context, project_id)
50 for network in networks:
51
52=== modified file 'etc/nova/api-paste.ini'
53--- etc/nova/api-paste.ini 2011-08-19 19:02:02 +0000
54+++ etc/nova/api-paste.ini 2011-08-23 02:26:29 +0000
55@@ -19,7 +19,9 @@
56 /1.0: ec2metadata
57
58 [pipeline:ec2cloud]
59-pipeline = logrequest authenticate cloudrequest authorizer ec2executor
60+pipeline = logrequest ec2noauth cloudrequest authorizer ec2executor
61+# NOTE(vish): use the following pipeline for deprecated auth
62+#pipeline = logrequest authenticate cloudrequest authorizer ec2executor
63 # NOTE(vish): use the following pipeline for keystone
64 # pipeline = logrequest totoken authtoken keystonecontext cloudrequest authorizer ec2executor
65
66@@ -41,6 +43,9 @@
67 [filter:totoken]
68 paste.filter_factory = nova.api.ec2:ToToken.factory
69
70+[filter:ec2noauth]
71+paste.filter_factory = nova.api.ec2:NoAuth.factory
72+
73 [filter:authenticate]
74 paste.filter_factory = nova.api.ec2:Authenticate.factory
75
76@@ -75,12 +80,16 @@
77 /v1.1: openstackapi11
78
79 [pipeline:openstackapi10]
80-pipeline = faultwrap auth ratelimit osapiapp10
81+pipeline = faultwrap noauth ratelimit osapiapp10
82+# NOTE(vish): use the following pipeline for deprecated auth
83+# pipeline = faultwrap auth ratelimit osapiapp10
84 # NOTE(vish): use the following pipeline for keystone
85 #pipeline = faultwrap authtoken keystonecontext ratelimit osapiapp10
86
87 [pipeline:openstackapi11]
88-pipeline = faultwrap auth ratelimit extensions osapiapp11
89+pipeline = faultwrap noauth ratelimit extensions osapiapp11
90+# NOTE(vish): use the following pipeline for deprecated auth
91+# pipeline = faultwrap auth ratelimit extensions osapiapp11
92 # NOTE(vish): use the following pipeline for keystone
93 # pipeline = faultwrap authtoken keystonecontext ratelimit extensions osapiapp11
94
95@@ -90,6 +99,9 @@
96 [filter:auth]
97 paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory
98
99+[filter:noauth]
100+paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory
101+
102 [filter:ratelimit]
103 paste.filter_factory = nova.api.openstack.limits:RateLimitingMiddleware.factory
104
105
106=== modified file 'nova/api/auth.py'
107--- nova/api/auth.py 2011-08-12 18:28:47 +0000
108+++ nova/api/auth.py 2011-08-23 02:26:29 +0000
109@@ -62,6 +62,7 @@
110 req.headers.get('X_STORAGE_TOKEN'))
111
112 # Build a context, including the auth_token...
113+ remote_address = getattr(req, 'remote_address', '127.0.0.1')
114 remote_address = req.remote_addr
115 if FLAGS.use_forwarded_for:
116 remote_address = req.headers.get('X-Forwarded-For', remote_address)
117
118=== modified file 'nova/api/ec2/__init__.py'
119--- nova/api/ec2/__init__.py 2011-08-19 19:02:02 +0000
120+++ nova/api/ec2/__init__.py 2011-08-23 02:26:29 +0000
121@@ -183,6 +183,27 @@
122 return self.application
123
124
125+class NoAuth(wsgi.Middleware):
126+ """Add user:project as 'nova.context' to WSGI environ."""
127+
128+ @webob.dec.wsgify(RequestClass=wsgi.Request)
129+ def __call__(self, req):
130+ if 'AWSAccessKeyId' not in req.params:
131+ raise webob.exc.HTTPBadRequest()
132+ user_id, _sep, project_id = req.params['AWSAccessKeyId'].partition(':')
133+ project_id = project_id or user_id
134+ remote_address = getattr(req, 'remote_address', '127.0.0.1')
135+ if FLAGS.use_forwarded_for:
136+ remote_address = req.headers.get('X-Forwarded-For', remote_address)
137+ ctx = context.RequestContext(user_id,
138+ project_id,
139+ is_admin=True,
140+ remote_address=remote_address)
141+
142+ req.environ['nova.context'] = ctx
143+ return self.application
144+
145+
146 class Authenticate(wsgi.Middleware):
147 """Authenticate an EC2 request and add 'nova.context' to WSGI environ."""
148
149
150=== modified file 'nova/api/ec2/admin.py'
151--- nova/api/ec2/admin.py 2011-06-25 19:38:07 +0000
152+++ nova/api/ec2/admin.py 2011-08-23 02:26:29 +0000
153@@ -283,8 +283,10 @@
154 # NOTE(vish) import delayed because of __init__.py
155 from nova.cloudpipe import pipelib
156 pipe = pipelib.CloudPipe()
157+ proj = manager.AuthManager().get_project(project)
158+ user_id = proj.project_manager_id
159 try:
160- pipe.launch_vpn_instance(project)
161+ pipe.launch_vpn_instance(project, user_id)
162 except db.NoMoreNetworks:
163 raise exception.ApiError("Unable to claim IP for VPN instance"
164 ", ensure it isn't running, and try "
165
166=== modified file 'nova/api/openstack/auth.py'
167--- nova/api/openstack/auth.py 2011-08-17 16:37:50 +0000
168+++ nova/api/openstack/auth.py 2011-08-23 02:26:29 +0000
169@@ -33,6 +33,46 @@
170
171 LOG = logging.getLogger('nova.api.openstack')
172 FLAGS = flags.FLAGS
173+flags.DECLARE('use_forwarded_for', 'nova.api.auth')
174+
175+
176+class NoAuthMiddleware(wsgi.Middleware):
177+ """Return a fake token if one isn't specified."""
178+
179+ @webob.dec.wsgify(RequestClass=wsgi.Request)
180+ def __call__(self, req):
181+ if 'X-Auth-Token' not in req.headers:
182+ os_url = req.url
183+ version = common.get_version_from_href(os_url)
184+ user_id = req.headers.get('X-Auth-User', 'admin')
185+ project_id = req.headers.get('X-Auth-Project-Id', 'admin')
186+ if version == '1.1':
187+ os_url += '/' + project_id
188+ res = webob.Response()
189+ # NOTE(vish): This is expecting and returning Auth(1.1), whereas
190+ # keystone uses 2.0 auth. We should probably allow
191+ # 2.0 auth here as well.
192+ res.headers['X-Auth-Token'] = '%s:%s' % (user_id, project_id)
193+ res.headers['X-Server-Management-Url'] = os_url
194+ res.headers['X-Storage-Url'] = ''
195+ res.headers['X-CDN-Management-Url'] = ''
196+ res.content_type = 'text/plain'
197+ res.status = '204'
198+ return res
199+
200+ token = req.headers['X-Auth-Token']
201+ user_id, _sep, project_id = token.partition(':')
202+ project_id = project_id or user_id
203+ remote_address = getattr(req, 'remote_address', '127.0.0.1')
204+ if FLAGS.use_forwarded_for:
205+ remote_address = req.headers.get('X-Forwarded-For', remote_address)
206+ ctx = context.RequestContext(user_id,
207+ project_id,
208+ is_admin=True,
209+ remote_address=remote_address)
210+
211+ req.environ['nova.context'] = ctx
212+ return self.application
213
214
215 class AuthMiddleware(wsgi.Middleware):
216@@ -85,9 +125,15 @@
217 project_id = projects[0].id
218
219 is_admin = self.auth.is_admin(user_id)
220- req.environ['nova.context'] = context.RequestContext(user_id,
221- project_id,
222- is_admin)
223+ remote_address = getattr(req, 'remote_address', '127.0.0.1')
224+ if FLAGS.use_forwarded_for:
225+ remote_address = req.headers.get('X-Forwarded-For', remote_address)
226+ ctx = context.RequestContext(user_id,
227+ project_id,
228+ is_admin=is_admin,
229+ remote_address=remote_address)
230+ req.environ['nova.context'] = ctx
231+
232 if not is_admin and not self.auth.is_project_member(user_id,
233 project_id):
234 msg = _("%(user_id)s must be an admin or a "
235
236=== modified file 'nova/auth/manager.py'
237--- nova/auth/manager.py 2011-07-22 22:04:52 +0000
238+++ nova/auth/manager.py 2011-08-23 02:26:29 +0000
239@@ -17,6 +17,9 @@
240 # under the License.
241
242 """
243+WARNING: This code is deprecated and will be removed.
244+Keystone is the recommended solution for auth management.
245+
246 Nova authentication management
247 """
248
249
250=== modified file 'nova/cloudpipe/pipelib.py'
251--- nova/cloudpipe/pipelib.py 2011-08-04 19:30:39 +0000
252+++ nova/cloudpipe/pipelib.py 2011-08-23 02:26:29 +0000
253@@ -34,7 +34,6 @@
254 from nova import flags
255 from nova import log as logging
256 from nova import utils
257-from nova.auth import manager
258 # TODO(eday): Eventually changes these to something not ec2-specific
259 from nova.api.ec2 import cloud
260
261@@ -57,7 +56,6 @@
262 class CloudPipe(object):
263 def __init__(self):
264 self.controller = cloud.CloudController()
265- self.manager = manager.AuthManager()
266
267 def get_encoded_zip(self, project_id):
268 # Make a payload.zip
269@@ -93,11 +91,10 @@
270 zippy.close()
271 return encoded
272
273- def launch_vpn_instance(self, project_id):
274+ def launch_vpn_instance(self, project_id, user_id):
275 LOG.debug(_("Launching VPN for %s") % (project_id))
276- project = self.manager.get_project(project_id)
277- ctxt = context.RequestContext(user=project.project_manager_id,
278- project=project.id)
279+ ctxt = context.RequestContext(user_id=user_id,
280+ project_id=project_id)
281 key_name = self.setup_key_pair(ctxt)
282 group_name = self.setup_security_group(ctxt)
283
284
285=== modified file 'nova/tests/api/openstack/test_server_actions.py'
286--- nova/tests/api/openstack/test_server_actions.py 2011-08-19 02:24:31 +0000
287+++ nova/tests/api/openstack/test_server_actions.py 2011-08-23 02:26:29 +0000
288@@ -1,17 +1,13 @@
289 import base64
290 import json
291-import unittest
292-from xml.dom import minidom
293
294 import stubout
295 import webob
296
297 from nova import context
298-from nova import db
299 from nova import utils
300 from nova import flags
301 from nova.api.openstack import create_instance_helper
302-from nova.compute import instance_types
303 from nova.compute import power_state
304 import nova.db.api
305 from nova import test
306@@ -103,8 +99,6 @@
307 super(ServerActionsTest, self).setUp()
308 self.flags(verbose=True)
309 self.stubs = stubout.StubOutForTesting()
310- fakes.FakeAuthManager.reset_fake_data()
311- fakes.FakeAuthDatabase.data = {}
312 fakes.stub_out_auth(self.stubs)
313 self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id)
314 self.stubs.Set(nova.db.api, 'instance_update', instance_update)
315@@ -468,8 +462,6 @@
316 self.maxDiff = None
317 super(ServerActionsTestV11, self).setUp()
318 self.stubs = stubout.StubOutForTesting()
319- fakes.FakeAuthManager.reset_fake_data()
320- fakes.FakeAuthDatabase.data = {}
321 fakes.stub_out_auth(self.stubs)
322 self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id)
323 self.stubs.Set(nova.db.api, 'instance_update', instance_update)
324
325=== modified file 'nova/tests/integrated/integrated_helpers.py'
326--- nova/tests/integrated/integrated_helpers.py 2011-08-04 19:07:07 +0000
327+++ nova/tests/integrated/integrated_helpers.py 2011-08-23 02:26:29 +0000
328@@ -22,10 +22,8 @@
329 import random
330 import string
331
332-from nova import exception
333 from nova import service
334 from nova import test # For the flags
335-from nova.auth import manager
336 import nova.image.glance
337 from nova.log import logging
338 from nova.tests.integrated.api import client
339@@ -58,90 +56,6 @@
340 LOG.debug("Random collision on %s" % candidate)
341
342
343-class TestUser(object):
344- def __init__(self, name, secret, auth_url):
345- self.name = name
346- self.secret = secret
347- self.auth_url = auth_url
348-
349- if not auth_url:
350- raise exception.Error("auth_url is required")
351- self.openstack_api = client.TestOpenStackClient(self.name,
352- self.secret,
353- self.auth_url)
354-
355- def get_unused_server_name(self):
356- servers = self.openstack_api.get_servers()
357- server_names = [server['name'] for server in servers]
358- return generate_new_element(server_names, 'server')
359-
360- def get_invalid_image(self):
361- images = self.openstack_api.get_images()
362- image_ids = [image['id'] for image in images]
363- return generate_new_element(image_ids, '', numeric=True)
364-
365- def get_valid_image(self, create=False):
366- images = self.openstack_api.get_images()
367- if create and not images:
368- # TODO(justinsb): No way currently to create an image through API
369- #created_image = self.openstack_api.post_image(image)
370- #images.append(created_image)
371- raise exception.Error("No way to create an image through API")
372-
373- if images:
374- return images[0]
375- return None
376-
377-
378-class IntegratedUnitTestContext(object):
379- def __init__(self, auth_url):
380- self.auth_manager = manager.AuthManager()
381-
382- self.auth_url = auth_url
383- self.project_name = None
384-
385- self.test_user = None
386-
387- self.setup()
388-
389- def setup(self):
390- self._create_test_user()
391-
392- def _create_test_user(self):
393- self.test_user = self._create_unittest_user()
394-
395- # No way to currently pass this through the OpenStack API
396- self.project_name = 'openstack'
397- self._configure_project(self.project_name, self.test_user)
398-
399- def cleanup(self):
400- self.test_user = None
401-
402- def _create_unittest_user(self):
403- users = self.auth_manager.get_users()
404- user_names = [user.name for user in users]
405- auth_name = generate_new_element(user_names, 'unittest_user_')
406- auth_key = generate_random_alphanumeric(16)
407-
408- # Right now there's a bug where auth_name and auth_key are reversed
409- # bug732907
410- auth_key = auth_name
411-
412- self.auth_manager.create_user(auth_name, auth_name, auth_key, False)
413- return TestUser(auth_name, auth_key, self.auth_url)
414-
415- def _configure_project(self, project_name, user):
416- projects = self.auth_manager.get_projects()
417- project_names = [project.name for project in projects]
418- if not project_name in project_names:
419- project = self.auth_manager.create_project(project_name,
420- user.name,
421- description=None,
422- member_users=None)
423- else:
424- self.auth_manager.add_to_project(user.name, project_name)
425-
426-
427 class _IntegratedTestBase(test.TestCase):
428 def setUp(self):
429 super(_IntegratedTestBase, self).setUp()
430@@ -163,10 +77,7 @@
431
432 self._start_api_service()
433
434- self.context = IntegratedUnitTestContext(self.auth_url)
435-
436- self.user = self.context.test_user
437- self.api = self.user.openstack_api
438+ self.api = client.TestOpenStackClient('fake', 'fake', self.auth_url)
439
440 def _start_api_service(self):
441 osapi = service.WSGIService("osapi")
442@@ -174,10 +85,6 @@
443 self.auth_url = 'http://%s:%s/v1.1' % (osapi.host, osapi.port)
444 LOG.warn(self.auth_url)
445
446- def tearDown(self):
447- self.context.cleanup()
448- super(_IntegratedTestBase, self).tearDown()
449-
450 def _get_flags(self):
451 """An opportunity to setup flags, before the services are started."""
452 f = {}
453@@ -190,10 +97,20 @@
454 f['fake_network'] = True
455 return f
456
457+ def get_unused_server_name(self):
458+ servers = self.api.get_servers()
459+ server_names = [server['name'] for server in servers]
460+ return generate_new_element(server_names, 'server')
461+
462+ def get_invalid_image(self):
463+ images = self.api.get_images()
464+ image_ids = [image['id'] for image in images]
465+ return generate_new_element(image_ids, '', numeric=True)
466+
467 def _build_minimal_create_server_request(self):
468 server = {}
469
470- image = self.user.get_valid_image(create=True)
471+ image = self.api.get_images()[0]
472 LOG.debug("Image: %s" % image)
473
474 if 'imageRef' in image:
475@@ -211,7 +128,7 @@
476 server['flavorRef'] = 'http://fake.server/%s' % flavor['id']
477
478 # Set a valid server name
479- server_name = self.user.get_unused_server_name()
480+ server_name = self.get_unused_server_name()
481 server['name'] = server_name
482
483 return server
484
485=== modified file 'nova/tests/integrated/test_login.py'
486--- nova/tests/integrated/test_login.py 2011-08-16 19:11:32 +0000
487+++ nova/tests/integrated/test_login.py 2011-08-23 02:26:29 +0000
488@@ -15,11 +15,9 @@
489 # License for the specific language governing permissions and limitations
490 # under the License.
491
492-import unittest
493
494 from nova.log import logging
495 from nova.tests.integrated import integrated_helpers
496-from nova.tests.integrated.api import client
497
498
499 LOG = logging.getLogger('nova.tests.integrated')
500@@ -31,40 +29,3 @@
501 flavors = self.api.get_flavors()
502 for flavor in flavors:
503 LOG.debug(_("flavor: %s") % flavor)
504-
505- def test_bad_login_password(self):
506- """Test that I get a 401 with a bad username."""
507- bad_credentials_api = client.TestOpenStackClient(self.user.name,
508- "notso_password",
509- self.user.auth_url)
510-
511- self.assertRaises(client.OpenStackApiAuthenticationException,
512- bad_credentials_api.get_flavors)
513-
514- def test_bad_login_username(self):
515- """Test that I get a 401 with a bad password."""
516- bad_credentials_api = client.TestOpenStackClient("notso_username",
517- self.user.secret,
518- self.user.auth_url)
519-
520- self.assertRaises(client.OpenStackApiAuthenticationException,
521- bad_credentials_api.get_flavors)
522-
523- def test_bad_login_both_bad(self):
524- """Test that I get a 401 with both bad username and bad password."""
525- bad_credentials_api = client.TestOpenStackClient("notso_username",
526- "notso_password",
527- self.user.auth_url)
528-
529- self.assertRaises(client.OpenStackApiAuthenticationException,
530- bad_credentials_api.get_flavors)
531-
532- def test_good_login_bad_project(self):
533- """Test that I get a 401 with valid user/pass but bad project"""
534- self.api.project_id = 'openstackBAD'
535-
536- self.assertRaises(client.OpenStackApiAuthorizationException,
537- self.api.get_flavors)
538-
539-if __name__ == "__main__":
540- unittest.main()
541
542=== modified file 'nova/tests/integrated/test_servers.py'
543--- nova/tests/integrated/test_servers.py 2011-08-19 17:08:46 +0000
544+++ nova/tests/integrated/test_servers.py 2011-08-23 02:26:29 +0000
545@@ -51,7 +51,7 @@
546 self.api.post_server, post)
547
548 # With an invalid imageRef, this throws 500.
549- server['imageRef'] = self.user.get_invalid_image()
550+ server['imageRef'] = self.get_invalid_image()
551 # TODO(justinsb): Check whatever the spec says should be thrown here
552 self.assertRaises(client.OpenStackApiException,
553 self.api.post_server, post)