Merge lp:~soren/nova/twisted-web-s3-server into lp:~hudson-openstack/nova/trunk

Proposed by Soren Hansen
Status: Merged
Approved by: Monty Taylor
Approved revision: no longer in the revision history of the source branch.
Merged at revision: 146
Proposed branch: lp:~soren/nova/twisted-web-s3-server
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 618 lines (+225/-259)
7 files modified
bin/nova-objectstore (+13/-14)
debian/control (+1/-1)
debian/nova-objectstore.install (+0/-1)
debian/nova-objectstore.links (+0/-1)
debian/nova-objectstore.nginx.conf (+0/-17)
nova/flags.py (+0/-1)
nova/objectstore/handler.py (+211/-224)
To merge this branch: bzr merge lp:~soren/nova/twisted-web-s3-server
Reviewer Review Type Date Requested Status
justinsb (community) Needs Fixing
Vish Ishaya (community) Approve
Review via email: mp+30206@code.launchpad.net

Commit message

Replace tornado objectstore with twisted web.

Description of the change

This branch replaces the tornado driven objectstore with a twisted web based one.

To post a comment you must log in.
Revision history for this message
Vish Ishaya (vishvananda) wrote :

Looks good aside from the merge errors. Have you tested to make sure running instances still works with the twisted version?

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

Merge errors? I don't see them, sorry.

Yes, I've tested this and it works great.

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

Oh, wow, yeah, I see them now. *blush* I'll fix.

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

Uh... I don't know what's going on, but I seriously don't see those merge problems when I check out the code.

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

checkout newest lp:nova and merge it into your branch and you should see the merge errors

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

Apparantly, it's intentional. It shows the merge conflicts that would appear when merging with the target branch. Kind of cool, kind of surprising :) I've merged with the trunk and pushed again. Please re-review.

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

lgtm

review: Approve
lp:~soren/nova/twisted-web-s3-server updated
146. By Soren Hansen

Replace tornado objectstore with twisted web.

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

Bug in line 537?
images = [i for i in image.Image.all() if i.is_authorized(self.context)]

Shouldn't this be...
images = [i for i in image.Image.all() if i.is_authorized(request.context)]

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

Inherited from the old code, but this is probably the time to clean it up:

1) If the Authorization header is not set, this throws a 500 error, where it should raise exception.NotAuthorized (401?)

Presumably the except clause needs to be broader than "except exception.Error, ex:"

2) Should we be using the 'modern' exception syntax "exception exception.Error as ex"? http://www.python.org/dev/peps/pep-3110/

3) There's a pretty big FIXME in there "# FIXME: check signature here!" - presumably we should open a bug for that...

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

Missing call to request.finish() in render_GET in ImageResource

        request.write(json.dumps([i.metadata for i in images]))
+ request.finish()
        return server.NOT_DONE_YET

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/nova-objectstore'
--- bin/nova-objectstore 2010-07-15 23:13:48 +0000
+++ bin/nova-objectstore 2010-07-18 18:28:46 +0000
@@ -18,33 +18,32 @@
18# under the License.18# under the License.
1919
20"""20"""
21 Tornado daemon for nova objectstore. Supports S3 API. 21 Twisted daemon for nova objectstore. Supports S3 API.
22"""22"""
2323
24import logging24import logging
25from tornado import httpserver
26from tornado import ioloop
2725
28from nova import flags26from nova import flags
29from nova import server
30from nova import utils27from nova import utils
31from nova.auth import users28from nova import twistd
32from nova.objectstore import handler29from nova.objectstore import handler
3330
3431
35FLAGS = flags.FLAGS32FLAGS = flags.FLAGS
3633
3734
38def main(argv):35def main():
39 # FIXME: if this log statement isn't here, no logging36 # FIXME: if this log statement isn't here, no logging
40 # appears from other files and app won't start daemonized37 # appears from other files and app won't start daemonized
41 logging.debug('Started HTTP server on %s' % (FLAGS.s3_internal_port))38 logging.debug('Started HTTP server on %s' % (FLAGS.s3_port))
42 app = handler.Application(users.UserManager())39 app = handler.get_application()
43 server = httpserver.HTTPServer(app)40 print app
44 server.listen(FLAGS.s3_internal_port)41 return app
45 ioloop.IOLoop.instance().start()42
4643# NOTE(soren): Stolen from nova-compute
47
48if __name__ == '__main__':44if __name__ == '__main__':
45 twistd.serve(__file__)
46
47if __name__ == '__builtin__':
49 utils.default_flagfile()48 utils.default_flagfile()
50 server.serve('nova-objectstore', main)49 application = main()
5150
=== modified file 'debian/control'
--- debian/control 2010-07-15 15:52:11 +0000
+++ debian/control 2010-07-18 18:28:46 +0000
@@ -91,7 +91,7 @@
9191
92Package: nova-objectstore92Package: nova-objectstore
93Architecture: all93Architecture: all
94Depends: nova-common (= ${binary:Version}), nginx, ${python:Depends}, ${misc:Depends}94Depends: nova-common (= ${binary:Version}), ${python:Depends}, ${misc:Depends}
95Description: Nova Cloud Computing - object store95Description: Nova Cloud Computing - object store
96 Nova is a cloud computing fabric controller (the main part of an IaaS96 Nova is a cloud computing fabric controller (the main part of an IaaS
97 system) built to match the popular AWS EC2 and S3 APIs. It is written in97 system) built to match the popular AWS EC2 and S3 APIs. It is written in
9898
=== modified file 'debian/nova-objectstore.install'
--- debian/nova-objectstore.install 2010-07-12 15:39:01 +0000
+++ debian/nova-objectstore.install 2010-07-18 18:28:46 +0000
@@ -1,3 +1,2 @@
1bin/nova-objectstore usr/bin1bin/nova-objectstore usr/bin
2debian/nova-objectstore.conf etc/nova2debian/nova-objectstore.conf etc/nova
3debian/nova-objectstore.nginx.conf etc/nginx/sites-available
43
=== removed file 'debian/nova-objectstore.links'
--- debian/nova-objectstore.links 2010-07-06 11:34:08 +0000
+++ debian/nova-objectstore.links 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
1/etc/nginx/sites-available/nova-objectstore.nginx.conf /etc/nginx/sites-enabled/nova-objectstore.nginx.conf
20
=== removed file 'debian/nova-objectstore.nginx.conf'
--- debian/nova-objectstore.nginx.conf 2010-07-06 11:34:08 +0000
+++ debian/nova-objectstore.nginx.conf 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
1server {
2 listen 3333 default;
3 server_name localhost;
4 client_max_body_size 10m;
5
6 access_log /var/log/nginx/localhost.access.log;
7
8 location ~ /_images/.+ {
9 root /var/lib/nova/images;
10 rewrite ^/_images/(.*)$ /$1 break;
11 }
12
13 location / {
14 proxy_pass http://localhost:3334/;
15 }
16}
17
180
=== modified file 'nova/flags.py'
--- nova/flags.py 2010-07-15 16:07:40 +0000
+++ nova/flags.py 2010-07-18 18:28:46 +0000
@@ -37,7 +37,6 @@
37# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#3937# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39
3838
39DEFINE_integer('s3_port', 3333, 's3 port')39DEFINE_integer('s3_port', 3333, 's3 port')
40DEFINE_integer('s3_internal_port', 3334, 's3 port')
41DEFINE_string('s3_host', '127.0.0.1', 's3 host')40DEFINE_string('s3_host', '127.0.0.1', 's3 host')
42#DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')41#DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
43DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')42DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
4443
=== modified file 'nova/objectstore/handler.py'
--- nova/objectstore/handler.py 2010-07-15 23:13:48 +0000
+++ nova/objectstore/handler.py 2010-07-18 18:28:46 +0000
@@ -1,10 +1,11 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=41# vim: tabstop=4 shiftwidth=4 softtabstop=4
22#
3# Copyright 2010 United States Government as represented by the3# Copyright 2010 OpenStack LLC.
4# Administrator of the National Aeronautics and Space Administration.4# Copyright 2010 United States Government as represented by the
5# All Rights Reserved.5# Administrator of the National Aeronautics and Space Administration.
6#6# All Rights Reserved.
7# Copyright 2009 Facebook7#
8# Copyright 2009 Facebook
8#9#
9# Licensed under the Apache License, Version 2.0 (the "License"); you may10# Licensed under the Apache License, Version 2.0 (the "License"); you may
10# not use this file except in compliance with the License. You may obtain11# not use this file except in compliance with the License. You may obtain
@@ -37,15 +38,21 @@
37"""38"""
3839
39import datetime40import datetime
40import os41import logging
41import json42import json
42import logging
43import multiprocessing43import multiprocessing
44from tornado import escape, web44import os
45from tornado import escape
45import urllib46import urllib
4647
48from twisted.application import internet, service
49from twisted.web.resource import Resource
50from twisted.web import server, static
51
52
47from nova import exception53from nova import exception
48from nova import flags54from nova import flags
55from nova.auth import users
49from nova.endpoint import api56from nova.endpoint import api
50from nova.objectstore import bucket57from nova.objectstore import bucket
51from nova.objectstore import image58from nova.objectstore import image
@@ -53,241 +60,213 @@
5360
54FLAGS = flags.FLAGS61FLAGS = flags.FLAGS
5562
5663def render_xml(request, value):
57def catch_nova_exceptions(target):64 assert isinstance(value, dict) and len(value) == 1
58 # FIXME: find a way to wrap all handlers in the web.Application.__init__ ?65 request.setHeader("Content-Type", "application/xml; charset=UTF-8")
59 def wrapper(*args, **kwargs):66
60 try:67 name = value.keys()[0]
61 return target(*args, **kwargs)68 request.write('<?xml version="1.0" encoding="UTF-8"?>\n')
62 except exception.NotFound:69 request.write('<' + escape.utf8(name) +
63 raise web.HTTPError(404)70 ' xmlns="http://doc.s3.amazonaws.com/2006-03-01">')
64 except exception.NotAuthorized:71 _render_parts(value.values()[0], request.write)
65 raise web.HTTPError(403)72 request.write('</' + escape.utf8(name) + '>')
6673 request.finish()
67 return wrapper74
6875def finish(request, content=None):
6976 if content:
70class Application(web.Application):77 request.write(content)
78 request.finish()
79
80def _render_parts(value, write_cb):
81 if isinstance(value, basestring):
82 write_cb(escape.xhtml_escape(value))
83 elif isinstance(value, int) or isinstance(value, long):
84 write_cb(str(value))
85 elif isinstance(value, datetime.datetime):
86 write_cb(value.strftime("%Y-%m-%dT%H:%M:%S.000Z"))
87 elif isinstance(value, dict):
88 for name, subvalue in value.iteritems():
89 if not isinstance(subvalue, list):
90 subvalue = [subvalue]
91 for subsubvalue in subvalue:
92 write_cb('<' + escape.utf8(name) + '>')
93 _render_parts(subsubvalue, write_cb)
94 write_cb('</' + escape.utf8(name) + '>')
95 else:
96 raise Exception("Unknown S3 value type %r", value)
97
98def get_argument(request, key, default_value):
99 if key in request.args:
100 return request.args[key][0]
101 return default_value
102
103def get_context(request):
104 try:
105 # Authorization Header format: 'AWS <access>:<secret>'
106 access, sep, secret = request.getHeader('Authorization').split(' ')[1].rpartition(':')
107 um = users.UserManager.instance()
108 print 'um %s' % um
109 (user, project) = um.authenticate(access, secret, {}, request.method, request.host, request.uri, False)
110 # FIXME: check signature here!
111 return api.APIRequestContext(None, user, project)
112 except exception.Error, ex:
113 logging.debug("Authentication Failure: %s" % ex)
114 raise exception.NotAuthorized
115
116class S3(Resource):
71 """Implementation of an S3-like storage server based on local files."""117 """Implementation of an S3-like storage server based on local files."""
72 def __init__(self, user_manager):118 def getChild(self, name, request):
73 web.Application.__init__(self, [119 request.context = get_context(request)
74 (r"/", RootHandler),120
75 (r"/_images/(.+)", ImageDownloadHandler),121 if name == '':
76 (r"/_images/", ImageHandler),122 return self
77 (r"/([^/]+)/(.+)", ObjectHandler),123 elif name == '_images':
78 (r"/([^/]+)/", BucketHandler),124 return ImageResource()
79 ])
80 self.buckets_path = os.path.abspath(FLAGS.buckets_path)
81 self.images_path = os.path.abspath(FLAGS.images_path)
82
83 if not os.path.exists(self.buckets_path):
84 raise Exception("buckets_path does not exist")
85 if not os.path.exists(self.images_path):
86 raise Exception("images_path does not exist")
87 self.user_manager = user_manager
88
89
90class BaseRequestHandler(web.RequestHandler):
91 SUPPORTED_METHODS = ("PUT", "GET", "DELETE", "HEAD")
92
93 @property
94 def context(self):
95 if not hasattr(self, '_context'):
96 try:
97 # Authorization Header format: 'AWS <access>:<secret>'
98 access, sep, secret = self.request.headers['Authorization'].split(' ')[1].rpartition(':')
99 (user, project) = self.application.user_manager.authenticate(access, secret, {}, self.request.method, self.request.host, self.request.path, False)
100 # FIXME: check signature here!
101 self._context = api.APIRequestContext(self, user, project)
102 except exception.Error, ex:
103 logging.debug("Authentication Failure: %s" % ex)
104 raise web.HTTPError(403)
105 return self._context
106
107 def render_xml(self, value):
108 assert isinstance(value, dict) and len(value) == 1
109 self.set_header("Content-Type", "application/xml; charset=UTF-8")
110 name = value.keys()[0]
111 parts = []
112 parts.append('<' + escape.utf8(name) +
113 ' xmlns="http://doc.s3.amazonaws.com/2006-03-01">')
114 self._render_parts(value.values()[0], parts)
115 parts.append('</' + escape.utf8(name) + '>')
116 self.finish('<?xml version="1.0" encoding="UTF-8"?>\n' +
117 ''.join(parts))
118
119 def _render_parts(self, value, parts=[]):
120 if isinstance(value, basestring):
121 parts.append(escape.xhtml_escape(value))
122 elif isinstance(value, int) or isinstance(value, long):
123 parts.append(str(value))
124 elif isinstance(value, datetime.datetime):
125 parts.append(value.strftime("%Y-%m-%dT%H:%M:%S.000Z"))
126 elif isinstance(value, dict):
127 for name, subvalue in value.iteritems():
128 if not isinstance(subvalue, list):
129 subvalue = [subvalue]
130 for subsubvalue in subvalue:
131 parts.append('<' + escape.utf8(name) + '>')
132 self._render_parts(subsubvalue, parts)
133 parts.append('</' + escape.utf8(name) + '>')
134 else:125 else:
135 raise Exception("Unknown S3 value type %r", value)126 return BucketResource(name)
136127
137 def head(self, *args, **kwargs):128 def render_GET(self, request):
138 return self.get(*args, **kwargs)129 buckets = [b for b in bucket.Bucket.all() if b.is_authorized(request.context)]
139130
140131 render_xml(request, {"ListAllMyBucketsResult": {
141class RootHandler(BaseRequestHandler):
142 def get(self):
143 buckets = [b for b in bucket.Bucket.all() if b.is_authorized(self.context)]
144
145 self.render_xml({"ListAllMyBucketsResult": {
146 "Buckets": {"Bucket": [b.metadata for b in buckets]},132 "Buckets": {"Bucket": [b.metadata for b in buckets]},
147 }})133 }})
148134 return server.NOT_DONE_YET
149135
150class BucketHandler(BaseRequestHandler):136class BucketResource(Resource):
151 @catch_nova_exceptions137 def __init__(self, name):
152 def get(self, bucket_name):138 Resource.__init__(self)
153 logging.debug("List keys for bucket %s" % (bucket_name))139 self.name = name
154140
155 bucket_object = bucket.Bucket(bucket_name)141 def getChild(self, name, request):
156142 if name == '':
157 if not bucket_object.is_authorized(self.context):143 return self
158 raise web.HTTPError(403)144 else:
159145 return ObjectResource(bucket.Bucket(self.name), name)
160 prefix = self.get_argument("prefix", u"")146
161 marker = self.get_argument("marker", u"")147 def render_GET(self, request):
162 max_keys = int(self.get_argument("max-keys", 1000))148 logging.debug("List keys for bucket %s" % (self.name))
163 terse = int(self.get_argument("terse", 0))149
150 bucket_object = bucket.Bucket(self.name)
151
152 if not bucket_object.is_authorized(request.context):
153 raise exception.NotAuthorized
154
155 prefix = get_argument(request, "prefix", u"")
156 marker = get_argument(request, "marker", u"")
157 max_keys = int(get_argument(request, "max-keys", 1000))
158 terse = int(get_argument(request, "terse", 0))
164159
165 results = bucket_object.list_keys(prefix=prefix, marker=marker, max_keys=max_keys, terse=terse)160 results = bucket_object.list_keys(prefix=prefix, marker=marker, max_keys=max_keys, terse=terse)
166 self.render_xml({"ListBucketResult": results})161 render_xml(request, {"ListBucketResult": results})
167162 return server.NOT_DONE_YET
168 @catch_nova_exceptions163
169 def put(self, bucket_name):164 def render_PUT(self, request):
170 logging.debug("Creating bucket %s" % (bucket_name))165 logging.debug("Creating bucket %s" % (self.name))
171 bucket.Bucket.create(bucket_name, self.context)166 try:
172 self.finish()167 print 'user is %s' % request.context
173168 except Exception, e:
174 @catch_nova_exceptions169 logging.exception(e)
175 def delete(self, bucket_name):170 logging.debug("calling bucket.Bucket.create(%r, %r)" % (self.name, request.context))
176 logging.debug("Deleting bucket %s" % (bucket_name))171 bucket.Bucket.create(self.name, request.context)
177 bucket_object = bucket.Bucket(bucket_name)172 return ''
178173
179 if not bucket_object.is_authorized(self.context):174 def render_DELETE(self, request):
180 raise web.HTTPError(403)175 logging.debug("Deleting bucket %s" % (self.name))
176 bucket_object = bucket.Bucket(self.name)
177
178 if not bucket_object.is_authorized(request.context):
179 raise exception.NotAuthorized
181180
182 bucket_object.delete()181 bucket_object.delete()
183 self.set_status(204)182 request.setResponseCode(204)
184 self.finish()183 return ''
185184
186185
187class ObjectHandler(BaseRequestHandler):186class ObjectResource(Resource):
188 @catch_nova_exceptions187 def __init__(self, bucket, name):
189 def get(self, bucket_name, object_name):188 Resource.__init__(self)
190 logging.debug("Getting object: %s / %s" % (bucket_name, object_name))189 self.bucket = bucket
191190 self.name = name
192 bucket_object = bucket.Bucket(bucket_name)191
193192 def render_GET(self, request):
194 if not bucket_object.is_authorized(self.context):193 logging.debug("Getting object: %s / %s" % (self.bucket.name, self.name))
195 raise web.HTTPError(403)194
196195 if not self.bucket.is_authorized(request.context):
197 obj = bucket_object[urllib.unquote(object_name)]196 raise exception.NotAuthorized
198 self.set_header("Content-Type", "application/unknown")197
199 self.set_header("Last-Modified", datetime.datetime.utcfromtimestamp(obj.mtime))198 obj = self.bucket[urllib.unquote(self.name)]
200 self.set_header("Etag", '"' + obj.md5 + '"')199 request.setHeader("Content-Type", "application/unknown")
201 self.finish(obj.read())200 request.setHeader("Last-Modified", datetime.datetime.utcfromtimestamp(obj.mtime))
202201 request.setHeader("Etag", '"' + obj.md5 + '"')
203 @catch_nova_exceptions202 return static.File(obj.path).render_GET(request)
204 def put(self, bucket_name, object_name):203
205 logging.debug("Putting object: %s / %s" % (bucket_name, object_name))204 def render_PUT(self, request):
206 bucket_object = bucket.Bucket(bucket_name)205 logging.debug("Putting object: %s / %s" % (self.bucket.name, self.name))
207206
208 if not bucket_object.is_authorized(self.context):207 if not self.bucket.is_authorized(request.context):
209 raise web.HTTPError(403)208 raise exception.NotAuthorized
210209
211 key = urllib.unquote(object_name)210 key = urllib.unquote(self.name)
212 bucket_object[key] = self.request.body211 request.content.seek(0, 0)
213 self.set_header("Etag", '"' + bucket_object[key].md5 + '"')212 self.bucket[key] = request.content.read()
214 self.finish()213 request.setHeader("Etag", '"' + self.bucket[key].md5 + '"')
215214 finish(request)
216 @catch_nova_exceptions215 return server.NOT_DONE_YET
217 def delete(self, bucket_name, object_name):216
218 logging.debug("Deleting object: %s / %s" % (bucket_name, object_name))217 def render_DELETE(self, request):
219 bucket_object = bucket.Bucket(bucket_name)218 logging.debug("Deleting object: %s / %s" % (self.bucket.name, self.name))
220219
221 if not bucket_object.is_authorized(self.context):220 if not self.bucket.is_authorized(request.context):
222 raise web.HTTPError(403)221 raise exception.NotAuthorized
223222
224 del bucket_object[urllib.unquote(object_name)]223 del self.bucket[urllib.unquote(self.name)]
225 self.set_status(204)224 request.setResponseCode(204)
226 self.finish()225 return ''
227226
228227class ImageResource(Resource):
229class ImageDownloadHandler(BaseRequestHandler):228 isLeaf = True
230 SUPPORTED_METHODS = ("GET", )229
231230 def getChild(self, name, request):
232 @catch_nova_exceptions231 if name == '':
233 def get(self, image_id):232 return self
234 """ send the decrypted image file233 else:
235234 request.setHeader("Content-Type", "application/octet-stream")
236 streaming content through python is slow and should only be used235 img = image.Image(name)
237 in development mode. You should serve files via a web server236 return static.File(img.image_path)
238 in production.237
239 """238 def render_GET(self, request):
240
241 self.set_header("Content-Type", "application/octet-stream")
242
243 READ_SIZE = 64*1024
244
245 img = image.Image(image_id)
246 with open(img.image_path, 'rb') as fp:
247 s = fp.read(READ_SIZE)
248 while s:
249 self.write(s)
250 s = fp.read(READ_SIZE)
251
252 self.finish()
253
254class ImageHandler(BaseRequestHandler):
255 SUPPORTED_METHODS = ("POST", "PUT", "GET", "DELETE")
256
257 @catch_nova_exceptions
258 def get(self):
259 """ returns a json listing of all images239 """ returns a json listing of all images
260 that a user has permissions to see """240 that a user has permissions to see """
261241
262 images = [i for i in image.Image.all() if i.is_authorized(self.context)]242 images = [i for i in image.Image.all() if i.is_authorized(self.context)]
263243
264 self.finish(json.dumps([i.metadata for i in images]))244 request.write(json.dumps([i.metadata for i in images]))
245 return server.NOT_DONE_YET
265246
266 @catch_nova_exceptions247 def render_PUT(self, request):
267 def put(self):
268 """ create a new registered image """248 """ create a new registered image """
269249
270 image_id = self.get_argument('image_id', u'')250 image_id = get_argument(request, 'image_id', u'')
271 image_location = self.get_argument('image_location', u'')251 image_location = get_argument(request, 'image_location', u'')
272252
273 image_path = os.path.join(FLAGS.images_path, image_id)253 image_path = os.path.join(FLAGS.images_path, image_id)
274 if not image_path.startswith(FLAGS.images_path) or \254 if not image_path.startswith(FLAGS.images_path) or \
275 os.path.exists(image_path):255 os.path.exists(image_path):
276 raise web.HTTPError(403)256 raise exception.NotAuthorized
277257
278 bucket_object = bucket.Bucket(image_location.split("/")[0])258 bucket_object = bucket.Bucket(image_location.split("/")[0])
279 manifest = image_location[len(image_location.split('/')[0])+1:]259 manifest = image_location[len(image_location.split('/')[0])+1:]
280260
281 if not bucket_object.is_authorized(self.context):261 if not bucket_object.is_authorized(request.context):
282 raise web.HTTPError(403)262 raise exception.NotAuthorized
283263
284 p = multiprocessing.Process(target=image.Image.register_aws_image,264 p = multiprocessing.Process(target=image.Image.register_aws_image,
285 args=(image_id, image_location, self.context))265 args=(image_id, image_location, request.context))
286 p.start()266 p.start()
287 self.finish()267 return ''
288268
289 @catch_nova_exceptions269 def render_POST(self, request):
290 def post(self):
291 """ update image attributes: public/private """270 """ update image attributes: public/private """
292271
293 image_id = self.get_argument('image_id', u'')272 image_id = self.get_argument('image_id', u'')
@@ -295,22 +274,30 @@
295274
296 image_object = image.Image(image_id)275 image_object = image.Image(image_id)
297276
298 if not image_object.is_authorized(self.context):277 if not image.is_authorized(request.context):
299 raise web.HTTPError(403)278 raise exception.NotAuthorized
300279
301 image_object.set_public(operation=='add')280 image_object.set_public(operation=='add')
302281
303 self.finish()282 return ''
304283
305 @catch_nova_exceptions284 def render_DELETE(self, request):
306 def delete(self):
307 """ delete a registered image """285 """ delete a registered image """
308 image_id = self.get_argument("image_id", u"")286 image_id = self.get_argument("image_id", u"")
309 image_object = image.Image(image_id)287 image_object = image.Image(image_id)
310288
311 if not image_object.is_authorized(self.context):289 if not image.is_authorized(request.context):
312 raise web.HTTPError(403)290 raise exception.NotAuthorized
313291
314 image_object.delete()292 image_object.delete()
315293
316 self.set_status(204)294 request.setResponseCode(204)
295 return ''
296
297def get_application():
298 root = S3()
299 factory = server.Site(root)
300 application = service.Application("objectstore")
301 objectStoreService = internet.TCPServer(FLAGS.s3_port, factory)
302 objectStoreService.setServiceParent(application)
303 return application