Merged trunk. Fixed new references to UserManager
This commit is contained in:
commit
8ef16679e0
1
.bzrignore
Normal file
1
.bzrignore
Normal file
@ -0,0 +1 @@
|
||||
run_tests.err.log
|
@ -18,23 +18,27 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
dhcpleasor.py
|
||||
nova-dhcpbridge
|
||||
|
||||
Handle lease database updates from DHCP servers.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
#TODO(joshua): there is concern that the user dnsmasq runs under will not
|
||||
# have nova in the path. This should be verified and if it is
|
||||
# not true the ugly line below can be removed
|
||||
sys.path.append(os.path.abspath(os.path.join(__file__, "../../")))
|
||||
|
||||
logging.debug(sys.path)
|
||||
import getopt
|
||||
from os import environ
|
||||
from nova import rpc
|
||||
from nova import flags
|
||||
from nova import rpc
|
||||
from nova import utils
|
||||
from nova.compute import linux_net
|
||||
from nova.compute import network
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
@ -63,11 +67,12 @@ def init_leases(interface):
|
||||
return res
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
interface = environ.get('DNSMASQ_INTERFACE', 'br0')
|
||||
if int(environ.get('TESTING', '0')):
|
||||
def main():
|
||||
flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile)
|
||||
utils.default_flagfile(flagfile)
|
||||
argv = FLAGS(sys.argv)
|
||||
interface = os.environ.get('DNSMASQ_INTERFACE', 'br0')
|
||||
if int(os.environ.get('TESTING', '0')):
|
||||
FLAGS.fake_rabbit = True
|
||||
FLAGS.redis_db = 8
|
||||
FLAGS.network_size = 32
|
@ -18,33 +18,32 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Tornado daemon for nova objectstore. Supports S3 API.
|
||||
Twisted daemon for nova objectstore. Supports S3 API.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from tornado import httpserver
|
||||
from tornado import ioloop
|
||||
|
||||
from nova import flags
|
||||
from nova import server
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova import twistd
|
||||
from nova.objectstore import handler
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
def main(argv):
|
||||
def main():
|
||||
# FIXME: if this log statement isn't here, no logging
|
||||
# appears from other files and app won't start daemonized
|
||||
logging.debug('Started HTTP server on %s' % (FLAGS.s3_internal_port))
|
||||
app = handler.Application(manager.AuthManager())
|
||||
server = httpserver.HTTPServer(app)
|
||||
server.listen(FLAGS.s3_internal_port)
|
||||
ioloop.IOLoop.instance().start()
|
||||
|
||||
logging.debug('Started HTTP server on %s' % (FLAGS.s3_port))
|
||||
app = handler.get_application()
|
||||
print app
|
||||
return app
|
||||
|
||||
# NOTE(soren): Stolen from nova-compute
|
||||
if __name__ == '__main__':
|
||||
twistd.serve(__file__)
|
||||
|
||||
if __name__ == '__builtin__':
|
||||
utils.default_flagfile()
|
||||
server.serve('nova-objectstore', main)
|
||||
application = main()
|
||||
|
2
debian/control
vendored
2
debian/control
vendored
@ -91,7 +91,7 @@ Description: Nova Cloud Computing - API frontend
|
||||
|
||||
Package: nova-objectstore
|
||||
Architecture: all
|
||||
Depends: nova-common (= ${binary:Version}), nginx, ${python:Depends}, ${misc:Depends}
|
||||
Depends: nova-common (= ${binary:Version}), ${python:Depends}, ${misc:Depends}
|
||||
Description: Nova Cloud Computing - object store
|
||||
Nova is a cloud computing fabric controller (the main part of an IaaS
|
||||
system) built to match the popular AWS EC2 and S3 APIs. It is written in
|
||||
|
3
debian/nova-api.conf
vendored
3
debian/nova-api.conf
vendored
@ -1,5 +1,6 @@
|
||||
--daemonize=1
|
||||
--ca_path=/var/lib/nova/CA
|
||||
--keys_path=/var/lib/nova/keys
|
||||
--networks_path=/var/lib/nova/networks
|
||||
--dhcpbridge_flagfile=/etc/nova/nova-dhcpbridge.conf
|
||||
--fake_users=1
|
||||
--datastore_path=/var/lib/nova/keeper
|
||||
|
1
debian/nova-api.install
vendored
1
debian/nova-api.install
vendored
@ -1,2 +1,3 @@
|
||||
bin/nova-api usr/bin
|
||||
debian/nova-api.conf etc/nova
|
||||
debian/nova-dhcpbridge.conf etc/nova
|
||||
|
2
debian/nova-compute.conf
vendored
2
debian/nova-compute.conf
vendored
@ -1,8 +1,6 @@
|
||||
--ca_path=/var/lib/nova/CA
|
||||
--keys_path=/var/lib/nova/keys
|
||||
--datastore_path=/var/lib/nova/keeper
|
||||
--instances_path=/var/lib/nova/instances
|
||||
--networks_path=/var/lib/nova/networks
|
||||
--simple_network_template=/usr/share/nova/interfaces.template
|
||||
--libvirt_xml_template=/usr/share/nova/libvirt.xml.template
|
||||
--vpn_client_template=/usr/share/nova/client.ovpn.template
|
||||
|
2
debian/nova-dhcp.conf
vendored
Normal file
2
debian/nova-dhcp.conf
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
--networks_path=/var/lib/nova/networks
|
||||
--fake_users=1
|
1
debian/nova-objectstore.conf
vendored
1
debian/nova-objectstore.conf
vendored
@ -1,7 +1,6 @@
|
||||
--daemonize=1
|
||||
--ca_path=/var/lib/nova/CA
|
||||
--keys_path=/var/lib/nova/keys
|
||||
--datastore_path=/var/lib/nova/keeper
|
||||
--fake_users=1
|
||||
--images_path=/var/lib/nova/images
|
||||
--buckets_path=/var/lib/nova/buckets
|
||||
|
1
debian/nova-objectstore.install
vendored
1
debian/nova-objectstore.install
vendored
@ -1,3 +1,2 @@
|
||||
bin/nova-objectstore usr/bin
|
||||
debian/nova-objectstore.conf etc/nova
|
||||
debian/nova-objectstore.nginx.conf etc/nginx/sites-available
|
||||
|
1
debian/nova-objectstore.links
vendored
1
debian/nova-objectstore.links
vendored
@ -1 +0,0 @@
|
||||
/etc/nginx/sites-available/nova-objectstore.nginx.conf /etc/nginx/sites-enabled/nova-objectstore.nginx.conf
|
17
debian/nova-objectstore.nginx.conf
vendored
17
debian/nova-objectstore.nginx.conf
vendored
@ -1,17 +0,0 @@
|
||||
server {
|
||||
listen 3333 default;
|
||||
server_name localhost;
|
||||
client_max_body_size 10m;
|
||||
|
||||
access_log /var/log/nginx/localhost.access.log;
|
||||
|
||||
location ~ /_images/.+ {
|
||||
root /var/lib/nova/images;
|
||||
rewrite ^/_images/(.*)$ /$1 break;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3334/;
|
||||
}
|
||||
}
|
||||
|
1
debian/nova-volume.conf
vendored
1
debian/nova-volume.conf
vendored
@ -1,7 +1,6 @@
|
||||
--daemonize=1
|
||||
--ca_path=/var/lib/nova/CA
|
||||
--keys_path=/var/lib/nova/keys
|
||||
--datastore_path=/var/lib/nova/keeper
|
||||
--fake_users=1
|
||||
--images_path=/var/lib/nova/images
|
||||
--buckets_path=/var/lib/nova/buckets
|
||||
|
@ -28,18 +28,22 @@ from nova import utils
|
||||
from nova import flags
|
||||
FLAGS=flags.FLAGS
|
||||
|
||||
def execute(cmd):
|
||||
flags.DEFINE_string('dhcpbridge_flagfile',
|
||||
'/etc/nova-dhcpbridge.conf',
|
||||
'location of flagfile for dhcpbridge')
|
||||
|
||||
def execute(cmd, addl_env=None):
|
||||
if FLAGS.fake_network:
|
||||
logging.debug("FAKE NET: %s" % cmd)
|
||||
return "fake", 0
|
||||
else:
|
||||
return nova.utils.execute(cmd)
|
||||
return utils.execute(cmd, addl_env=addl_env)
|
||||
|
||||
def runthis(desc, cmd):
|
||||
if FLAGS.fake_network:
|
||||
return execute(cmd)
|
||||
else:
|
||||
return nova.utils.runthis(desc,cmd)
|
||||
return utils.runthis(desc,cmd)
|
||||
|
||||
def Popen(cmd):
|
||||
if FLAGS.fake_network:
|
||||
@ -61,7 +65,7 @@ def remove_rule(cmd):
|
||||
|
||||
def bind_public_ip(ip, interface):
|
||||
runthis("Binding IP to interface: %s", "sudo ip addr add %s dev %s" % (ip, interface))
|
||||
|
||||
|
||||
def unbind_public_ip(ip, interface):
|
||||
runthis("Binding IP to interface: %s", "sudo ip addr del %s dev %s" % (ip, interface))
|
||||
|
||||
@ -99,7 +103,7 @@ def dnsmasq_cmd(net):
|
||||
' --except-interface=lo',
|
||||
' --dhcp-range=%s,static,600s' % (net.dhcp_range_start),
|
||||
' --dhcp-hostsfile=%s' % dhcp_file(net['vlan'], 'conf'),
|
||||
' --dhcp-script=%s' % bin_file('dhcpleasor.py'),
|
||||
' --dhcp-script=%s' % bin_file('nova-dhcpbridge'),
|
||||
' --leasefile-ro']
|
||||
return ''.join(cmd)
|
||||
|
||||
@ -139,7 +143,9 @@ def start_dnsmasq(network):
|
||||
if os.path.exists(lease_file):
|
||||
os.unlink(lease_file)
|
||||
|
||||
Popen(dnsmasq_cmd(network).split(" "))
|
||||
# FLAGFILE in env
|
||||
env = {'FLAGFILE' : FLAGS.dhcpbridge_flagfile}
|
||||
execute(dnsmasq_cmd(network), addl_env=env)
|
||||
|
||||
def stop_dnsmasq(network):
|
||||
""" stops the dnsmasq instance for a given network """
|
||||
|
@ -251,7 +251,7 @@ class BaseNetwork(datastore.BasicModel):
|
||||
raise compute_exception.NoMoreAddresses("Project %s with network %s" %
|
||||
(project_id, str(self.network)))
|
||||
|
||||
def lease_ip(self, ip_str):
|
||||
def lease_ip(self, ip_str):
|
||||
logging.debug("Leasing allocated IP %s" % (ip_str))
|
||||
|
||||
def release_ip(self, ip_str):
|
||||
@ -566,10 +566,10 @@ def allocate_ip(user_id, project_id, mac):
|
||||
|
||||
def deallocate_ip(address):
|
||||
return get_network_by_address(address).deallocate_ip(address)
|
||||
|
||||
|
||||
def release_ip(address):
|
||||
return get_network_by_address(address).release_ip(address)
|
||||
|
||||
|
||||
def lease_ip(address):
|
||||
return get_network_by_address(address).lease_ip(address)
|
||||
|
||||
|
@ -37,7 +37,6 @@ DEFINE_bool = DEFINE_bool
|
||||
# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39
|
||||
|
||||
DEFINE_integer('s3_port', 3333, 's3 port')
|
||||
DEFINE_integer('s3_internal_port', 3334, 's3 port')
|
||||
DEFINE_string('s3_host', '127.0.0.1', 's3 host')
|
||||
#DEFINE_string('cloud_topic', 'cloud', 'the topic clouds listen on')
|
||||
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
|
||||
|
@ -1,10 +1,11 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2009 Facebook
|
||||
# Copyright 2010 OpenStack LLC.
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2009 Facebook
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
@ -37,15 +38,21 @@ S3 client with this module::
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import json
|
||||
import multiprocessing
|
||||
from tornado import escape, web
|
||||
import os
|
||||
from tornado import escape
|
||||
import urllib
|
||||
|
||||
from twisted.application import internet, service
|
||||
from twisted.web.resource import Resource
|
||||
from twisted.web import server, static
|
||||
|
||||
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova.auth import manager
|
||||
from nova.endpoint import api
|
||||
from nova.objectstore import bucket
|
||||
from nova.objectstore import image
|
||||
@ -53,241 +60,217 @@ from nova.objectstore import image
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
def render_xml(request, value):
|
||||
assert isinstance(value, dict) and len(value) == 1
|
||||
request.setHeader("Content-Type", "application/xml; charset=UTF-8")
|
||||
|
||||
def catch_nova_exceptions(target):
|
||||
# FIXME: find a way to wrap all handlers in the web.Application.__init__ ?
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return target(*args, **kwargs)
|
||||
except exception.NotFound:
|
||||
raise web.HTTPError(404)
|
||||
except exception.NotAuthorized:
|
||||
raise web.HTTPError(403)
|
||||
name = value.keys()[0]
|
||||
request.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||
request.write('<' + escape.utf8(name) +
|
||||
' xmlns="http://doc.s3.amazonaws.com/2006-03-01">')
|
||||
_render_parts(value.values()[0], request.write)
|
||||
request.write('</' + escape.utf8(name) + '>')
|
||||
request.finish()
|
||||
|
||||
return wrapper
|
||||
def finish(request, content=None):
|
||||
if content:
|
||||
request.write(content)
|
||||
request.finish()
|
||||
|
||||
def _render_parts(value, write_cb):
|
||||
if isinstance(value, basestring):
|
||||
write_cb(escape.xhtml_escape(value))
|
||||
elif isinstance(value, int) or isinstance(value, long):
|
||||
write_cb(str(value))
|
||||
elif isinstance(value, datetime.datetime):
|
||||
write_cb(value.strftime("%Y-%m-%dT%H:%M:%S.000Z"))
|
||||
elif isinstance(value, dict):
|
||||
for name, subvalue in value.iteritems():
|
||||
if not isinstance(subvalue, list):
|
||||
subvalue = [subvalue]
|
||||
for subsubvalue in subvalue:
|
||||
write_cb('<' + escape.utf8(name) + '>')
|
||||
_render_parts(subsubvalue, write_cb)
|
||||
write_cb('</' + escape.utf8(name) + '>')
|
||||
else:
|
||||
raise Exception("Unknown S3 value type %r", value)
|
||||
|
||||
class Application(web.Application):
|
||||
def get_argument(request, key, default_value):
|
||||
if key in request.args:
|
||||
return request.args[key][0]
|
||||
return default_value
|
||||
|
||||
def get_context(request):
|
||||
try:
|
||||
# Authorization Header format: 'AWS <access>:<secret>'
|
||||
access, sep, secret = request.getHeader('Authorization').split(' ')[1].rpartition(':')
|
||||
(user, project) = manager.AuthManager().authenticate(access,
|
||||
secret,
|
||||
{},
|
||||
request.method,
|
||||
request.host,
|
||||
request.uri,
|
||||
False)
|
||||
# FIXME: check signature here!
|
||||
return api.APIRequestContext(None, user, project)
|
||||
except exception.Error, ex:
|
||||
logging.debug("Authentication Failure: %s" % ex)
|
||||
raise exception.NotAuthorized
|
||||
|
||||
class S3(Resource):
|
||||
"""Implementation of an S3-like storage server based on local files."""
|
||||
def __init__(self, user_manager):
|
||||
web.Application.__init__(self, [
|
||||
(r"/", RootHandler),
|
||||
(r"/_images/(.+)", ImageDownloadHandler),
|
||||
(r"/_images/", ImageHandler),
|
||||
(r"/([^/]+)/(.+)", ObjectHandler),
|
||||
(r"/([^/]+)/", BucketHandler),
|
||||
])
|
||||
self.buckets_path = os.path.abspath(FLAGS.buckets_path)
|
||||
self.images_path = os.path.abspath(FLAGS.images_path)
|
||||
def getChild(self, name, request):
|
||||
request.context = get_context(request)
|
||||
|
||||
if not os.path.exists(self.buckets_path):
|
||||
raise Exception("buckets_path does not exist")
|
||||
if not os.path.exists(self.images_path):
|
||||
raise Exception("images_path does not exist")
|
||||
self.user_manager = user_manager
|
||||
|
||||
|
||||
class BaseRequestHandler(web.RequestHandler):
|
||||
SUPPORTED_METHODS = ("PUT", "GET", "DELETE", "HEAD")
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
if not hasattr(self, '_context'):
|
||||
try:
|
||||
# Authorization Header format: 'AWS <access>:<secret>'
|
||||
access, sep, secret = self.request.headers['Authorization'].split(' ')[1].rpartition(':')
|
||||
(user, project) = self.application.user_manager.authenticate(access, secret, {}, self.request.method, self.request.host, self.request.path, False)
|
||||
# FIXME: check signature here!
|
||||
self._context = api.APIRequestContext(self, user, project)
|
||||
except exception.Error, ex:
|
||||
logging.debug("Authentication Failure: %s" % ex)
|
||||
raise web.HTTPError(403)
|
||||
return self._context
|
||||
|
||||
def render_xml(self, value):
|
||||
assert isinstance(value, dict) and len(value) == 1
|
||||
self.set_header("Content-Type", "application/xml; charset=UTF-8")
|
||||
name = value.keys()[0]
|
||||
parts = []
|
||||
parts.append('<' + escape.utf8(name) +
|
||||
' xmlns="http://doc.s3.amazonaws.com/2006-03-01">')
|
||||
self._render_parts(value.values()[0], parts)
|
||||
parts.append('</' + escape.utf8(name) + '>')
|
||||
self.finish('<?xml version="1.0" encoding="UTF-8"?>\n' +
|
||||
''.join(parts))
|
||||
|
||||
def _render_parts(self, value, parts=[]):
|
||||
if isinstance(value, basestring):
|
||||
parts.append(escape.xhtml_escape(value))
|
||||
elif isinstance(value, int) or isinstance(value, long):
|
||||
parts.append(str(value))
|
||||
elif isinstance(value, datetime.datetime):
|
||||
parts.append(value.strftime("%Y-%m-%dT%H:%M:%S.000Z"))
|
||||
elif isinstance(value, dict):
|
||||
for name, subvalue in value.iteritems():
|
||||
if not isinstance(subvalue, list):
|
||||
subvalue = [subvalue]
|
||||
for subsubvalue in subvalue:
|
||||
parts.append('<' + escape.utf8(name) + '>')
|
||||
self._render_parts(subsubvalue, parts)
|
||||
parts.append('</' + escape.utf8(name) + '>')
|
||||
if name == '':
|
||||
return self
|
||||
elif name == '_images':
|
||||
return ImageResource()
|
||||
else:
|
||||
raise Exception("Unknown S3 value type %r", value)
|
||||
return BucketResource(name)
|
||||
|
||||
def head(self, *args, **kwargs):
|
||||
return self.get(*args, **kwargs)
|
||||
def render_GET(self, request):
|
||||
buckets = [b for b in bucket.Bucket.all() if b.is_authorized(request.context)]
|
||||
|
||||
|
||||
class RootHandler(BaseRequestHandler):
|
||||
def get(self):
|
||||
buckets = [b for b in bucket.Bucket.all() if b.is_authorized(self.context)]
|
||||
|
||||
self.render_xml({"ListAllMyBucketsResult": {
|
||||
render_xml(request, {"ListAllMyBucketsResult": {
|
||||
"Buckets": {"Bucket": [b.metadata for b in buckets]},
|
||||
}})
|
||||
return server.NOT_DONE_YET
|
||||
|
||||
class BucketResource(Resource):
|
||||
def __init__(self, name):
|
||||
Resource.__init__(self)
|
||||
self.name = name
|
||||
|
||||
class BucketHandler(BaseRequestHandler):
|
||||
@catch_nova_exceptions
|
||||
def get(self, bucket_name):
|
||||
logging.debug("List keys for bucket %s" % (bucket_name))
|
||||
def getChild(self, name, request):
|
||||
if name == '':
|
||||
return self
|
||||
else:
|
||||
return ObjectResource(bucket.Bucket(self.name), name)
|
||||
|
||||
bucket_object = bucket.Bucket(bucket_name)
|
||||
def render_GET(self, request):
|
||||
logging.debug("List keys for bucket %s" % (self.name))
|
||||
|
||||
if not bucket_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
bucket_object = bucket.Bucket(self.name)
|
||||
|
||||
prefix = self.get_argument("prefix", u"")
|
||||
marker = self.get_argument("marker", u"")
|
||||
max_keys = int(self.get_argument("max-keys", 1000))
|
||||
terse = int(self.get_argument("terse", 0))
|
||||
if not bucket_object.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
prefix = get_argument(request, "prefix", u"")
|
||||
marker = get_argument(request, "marker", u"")
|
||||
max_keys = int(get_argument(request, "max-keys", 1000))
|
||||
terse = int(get_argument(request, "terse", 0))
|
||||
|
||||
results = bucket_object.list_keys(prefix=prefix, marker=marker, max_keys=max_keys, terse=terse)
|
||||
self.render_xml({"ListBucketResult": results})
|
||||
render_xml(request, {"ListBucketResult": results})
|
||||
return server.NOT_DONE_YET
|
||||
|
||||
@catch_nova_exceptions
|
||||
def put(self, bucket_name):
|
||||
logging.debug("Creating bucket %s" % (bucket_name))
|
||||
bucket.Bucket.create(bucket_name, self.context)
|
||||
self.finish()
|
||||
def render_PUT(self, request):
|
||||
logging.debug("Creating bucket %s" % (self.name))
|
||||
try:
|
||||
print 'user is %s' % request.context
|
||||
except Exception, e:
|
||||
logging.exception(e)
|
||||
logging.debug("calling bucket.Bucket.create(%r, %r)" % (self.name, request.context))
|
||||
bucket.Bucket.create(self.name, request.context)
|
||||
return ''
|
||||
|
||||
@catch_nova_exceptions
|
||||
def delete(self, bucket_name):
|
||||
logging.debug("Deleting bucket %s" % (bucket_name))
|
||||
bucket_object = bucket.Bucket(bucket_name)
|
||||
def render_DELETE(self, request):
|
||||
logging.debug("Deleting bucket %s" % (self.name))
|
||||
bucket_object = bucket.Bucket(self.name)
|
||||
|
||||
if not bucket_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
if not bucket_object.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
bucket_object.delete()
|
||||
self.set_status(204)
|
||||
self.finish()
|
||||
request.setResponseCode(204)
|
||||
return ''
|
||||
|
||||
|
||||
class ObjectHandler(BaseRequestHandler):
|
||||
@catch_nova_exceptions
|
||||
def get(self, bucket_name, object_name):
|
||||
logging.debug("Getting object: %s / %s" % (bucket_name, object_name))
|
||||
class ObjectResource(Resource):
|
||||
def __init__(self, bucket, name):
|
||||
Resource.__init__(self)
|
||||
self.bucket = bucket
|
||||
self.name = name
|
||||
|
||||
bucket_object = bucket.Bucket(bucket_name)
|
||||
def render_GET(self, request):
|
||||
logging.debug("Getting object: %s / %s" % (self.bucket.name, self.name))
|
||||
|
||||
if not bucket_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
if not self.bucket.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
obj = bucket_object[urllib.unquote(object_name)]
|
||||
self.set_header("Content-Type", "application/unknown")
|
||||
self.set_header("Last-Modified", datetime.datetime.utcfromtimestamp(obj.mtime))
|
||||
self.set_header("Etag", '"' + obj.md5 + '"')
|
||||
self.finish(obj.read())
|
||||
obj = self.bucket[urllib.unquote(self.name)]
|
||||
request.setHeader("Content-Type", "application/unknown")
|
||||
request.setHeader("Last-Modified", datetime.datetime.utcfromtimestamp(obj.mtime))
|
||||
request.setHeader("Etag", '"' + obj.md5 + '"')
|
||||
return static.File(obj.path).render_GET(request)
|
||||
|
||||
@catch_nova_exceptions
|
||||
def put(self, bucket_name, object_name):
|
||||
logging.debug("Putting object: %s / %s" % (bucket_name, object_name))
|
||||
bucket_object = bucket.Bucket(bucket_name)
|
||||
def render_PUT(self, request):
|
||||
logging.debug("Putting object: %s / %s" % (self.bucket.name, self.name))
|
||||
|
||||
if not bucket_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
if not self.bucket.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
key = urllib.unquote(object_name)
|
||||
bucket_object[key] = self.request.body
|
||||
self.set_header("Etag", '"' + bucket_object[key].md5 + '"')
|
||||
self.finish()
|
||||
key = urllib.unquote(self.name)
|
||||
request.content.seek(0, 0)
|
||||
self.bucket[key] = request.content.read()
|
||||
request.setHeader("Etag", '"' + self.bucket[key].md5 + '"')
|
||||
finish(request)
|
||||
return server.NOT_DONE_YET
|
||||
|
||||
@catch_nova_exceptions
|
||||
def delete(self, bucket_name, object_name):
|
||||
logging.debug("Deleting object: %s / %s" % (bucket_name, object_name))
|
||||
bucket_object = bucket.Bucket(bucket_name)
|
||||
def render_DELETE(self, request):
|
||||
logging.debug("Deleting object: %s / %s" % (self.bucket.name, self.name))
|
||||
|
||||
if not bucket_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
if not self.bucket.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
del bucket_object[urllib.unquote(object_name)]
|
||||
self.set_status(204)
|
||||
self.finish()
|
||||
del self.bucket[urllib.unquote(self.name)]
|
||||
request.setResponseCode(204)
|
||||
return ''
|
||||
|
||||
class ImageResource(Resource):
|
||||
isLeaf = True
|
||||
|
||||
class ImageDownloadHandler(BaseRequestHandler):
|
||||
SUPPORTED_METHODS = ("GET", )
|
||||
def getChild(self, name, request):
|
||||
if name == '':
|
||||
return self
|
||||
else:
|
||||
request.setHeader("Content-Type", "application/octet-stream")
|
||||
img = image.Image(name)
|
||||
return static.File(img.image_path)
|
||||
|
||||
@catch_nova_exceptions
|
||||
def get(self, image_id):
|
||||
""" send the decrypted image file
|
||||
|
||||
streaming content through python is slow and should only be used
|
||||
in development mode. You should serve files via a web server
|
||||
in production.
|
||||
"""
|
||||
|
||||
self.set_header("Content-Type", "application/octet-stream")
|
||||
|
||||
READ_SIZE = 64*1024
|
||||
|
||||
img = image.Image(image_id)
|
||||
with open(img.image_path, 'rb') as fp:
|
||||
s = fp.read(READ_SIZE)
|
||||
while s:
|
||||
self.write(s)
|
||||
s = fp.read(READ_SIZE)
|
||||
|
||||
self.finish()
|
||||
|
||||
class ImageHandler(BaseRequestHandler):
|
||||
SUPPORTED_METHODS = ("POST", "PUT", "GET", "DELETE")
|
||||
|
||||
@catch_nova_exceptions
|
||||
def get(self):
|
||||
def render_GET(self, request):
|
||||
""" returns a json listing of all images
|
||||
that a user has permissions to see """
|
||||
|
||||
images = [i for i in image.Image.all() if i.is_authorized(self.context)]
|
||||
|
||||
self.finish(json.dumps([i.metadata for i in images]))
|
||||
request.write(json.dumps([i.metadata for i in images]))
|
||||
return server.NOT_DONE_YET
|
||||
|
||||
@catch_nova_exceptions
|
||||
def put(self):
|
||||
def render_PUT(self, request):
|
||||
""" create a new registered image """
|
||||
|
||||
image_id = self.get_argument('image_id', u'')
|
||||
image_location = self.get_argument('image_location', u'')
|
||||
image_id = get_argument(request, 'image_id', u'')
|
||||
image_location = get_argument(request, 'image_location', u'')
|
||||
|
||||
image_path = os.path.join(FLAGS.images_path, image_id)
|
||||
if not image_path.startswith(FLAGS.images_path) or \
|
||||
os.path.exists(image_path):
|
||||
raise web.HTTPError(403)
|
||||
raise exception.NotAuthorized
|
||||
|
||||
bucket_object = bucket.Bucket(image_location.split("/")[0])
|
||||
manifest = image_location[len(image_location.split('/')[0])+1:]
|
||||
|
||||
if not bucket_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
if not bucket_object.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
p = multiprocessing.Process(target=image.Image.register_aws_image,
|
||||
args=(image_id, image_location, self.context))
|
||||
args=(image_id, image_location, request.context))
|
||||
p.start()
|
||||
self.finish()
|
||||
return ''
|
||||
|
||||
@catch_nova_exceptions
|
||||
def post(self):
|
||||
def render_POST(self, request):
|
||||
""" update image attributes: public/private """
|
||||
|
||||
image_id = self.get_argument('image_id', u'')
|
||||
@ -295,22 +278,30 @@ class ImageHandler(BaseRequestHandler):
|
||||
|
||||
image_object = image.Image(image_id)
|
||||
|
||||
if not image_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
if not image.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
image_object.set_public(operation=='add')
|
||||
|
||||
self.finish()
|
||||
return ''
|
||||
|
||||
@catch_nova_exceptions
|
||||
def delete(self):
|
||||
def render_DELETE(self, request):
|
||||
""" delete a registered image """
|
||||
image_id = self.get_argument("image_id", u"")
|
||||
image_object = image.Image(image_id)
|
||||
|
||||
if not image_object.is_authorized(self.context):
|
||||
raise web.HTTPError(403)
|
||||
if not image.is_authorized(request.context):
|
||||
raise exception.NotAuthorized
|
||||
|
||||
image_object.delete()
|
||||
|
||||
self.set_status(204)
|
||||
request.setResponseCode(204)
|
||||
return ''
|
||||
|
||||
def get_application():
|
||||
root = S3()
|
||||
factory = server.Site(root)
|
||||
application = service.Application("objectstore")
|
||||
objectStoreService = internet.TCPServer(FLAGS.s3_port, factory)
|
||||
objectStoreService.setServiceParent(application)
|
||||
return application
|
||||
|
@ -110,6 +110,7 @@ class TopicConsumer(Consumer):
|
||||
self.queue = topic
|
||||
self.routing_key = topic
|
||||
self.exchange = FLAGS.control_exchange
|
||||
self.durable = False
|
||||
super(TopicConsumer, self).__init__(connection=connection)
|
||||
|
||||
|
||||
@ -195,7 +196,10 @@ def call(topic, msg):
|
||||
conn = Connection.instance()
|
||||
d = defer.Deferred()
|
||||
consumer = DirectConsumer(connection=conn, msg_id=msg_id)
|
||||
consumer.register_callback(lambda data, message: d.callback(data))
|
||||
def deferred_receive(data, message):
|
||||
message.ack()
|
||||
d.callback(data)
|
||||
consumer.register_callback(deferred_receive)
|
||||
injected = consumer.attach_to_tornado()
|
||||
|
||||
# clean up after the injected listened and return x
|
||||
@ -233,7 +237,8 @@ def send_message(topic, message, wait=True):
|
||||
exchange=msg_id,
|
||||
auto_delete=True,
|
||||
exchange_type="direct",
|
||||
routing_key=msg_id)
|
||||
routing_key=msg_id,
|
||||
durable=False)
|
||||
consumer.register_callback(generic_response)
|
||||
|
||||
publisher = messaging.Publisher(connection=Connection.instance(),
|
||||
|
@ -19,16 +19,15 @@
|
||||
import IPy
|
||||
import os
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova import exception
|
||||
from nova.compute.exception import NoMoreAddresses
|
||||
from nova.compute import network
|
||||
from nova.auth import manager
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.compute import network
|
||||
from nova.compute.exception import NoMoreAddresses
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
class NetworkTestCase(test.TrialTestCase):
|
||||
def setUp(self):
|
||||
@ -180,14 +179,20 @@ def binpath(script):
|
||||
|
||||
class FakeDNSMasq(object):
|
||||
def issue_ip(self, mac, ip, hostname, interface):
|
||||
cmd = "%s add %s %s %s" % (binpath('dhcpleasor.py'), mac, ip, hostname)
|
||||
env = {'DNSMASQ_INTERFACE': interface, 'TESTING' : '1'}
|
||||
cmd = "%s add %s %s %s" % (binpath('nova-dhcpbridge'),
|
||||
mac, ip, hostname)
|
||||
env = {'DNSMASQ_INTERFACE': interface,
|
||||
'TESTING' : '1',
|
||||
'FLAGFILE' : FLAGS.dhcpbridge_flagfile}
|
||||
(out, err) = utils.execute(cmd, addl_env=env)
|
||||
logging.debug("ISSUE_IP: %s, %s " % (out, err))
|
||||
|
||||
def release_ip(self, mac, ip, hostname, interface):
|
||||
cmd = "%s del %s %s %s" % (binpath('dhcpleasor.py'), mac, ip, hostname)
|
||||
env = {'DNSMASQ_INTERFACE': interface, 'TESTING' : '1'}
|
||||
cmd = "%s del %s %s %s" % (binpath('nova-dhcpbridge'),
|
||||
mac, ip, hostname)
|
||||
env = {'DNSMASQ_INTERFACE': interface,
|
||||
'TESTING' : '1',
|
||||
'FLAGFILE' : FLAGS.dhcpbridge_flagfile}
|
||||
(out, err) = utils.execute(cmd, addl_env=env)
|
||||
logging.debug("RELEASE_IP: %s, %s " % (out, err))
|
||||
|
||||
|
@ -39,6 +39,7 @@ Due to our use of multiprocessing it we frequently get some ignorable
|
||||
|
||||
"""
|
||||
import __main__
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
@ -66,6 +67,9 @@ FLAGS = flags.FLAGS
|
||||
flags.DEFINE_bool('flush_db', True,
|
||||
'Flush the database before running fake tests')
|
||||
|
||||
flags.DEFINE_string('tests_stderr', 'run_tests.err.log',
|
||||
'Path to where to pipe STDERR during test runs. Default = "run_tests.err.log"')
|
||||
|
||||
if __name__ == '__main__':
|
||||
OptionsClass = twistd.WrapTwistedOptions(trial_script.Options)
|
||||
config = OptionsClass()
|
||||
@ -85,6 +89,11 @@ if __name__ == '__main__':
|
||||
else:
|
||||
from nova.tests.real_flags import *
|
||||
|
||||
# Establish redirect for STDERR
|
||||
sys.stderr.flush()
|
||||
err = open(FLAGS.tests_stderr, 'w+', 0)
|
||||
os.dup2(err.fileno(), sys.stderr.fileno())
|
||||
|
||||
if len(argv) == 1 and len(config['tests']) == 0:
|
||||
# If no tests were specified run the ones imported in this file
|
||||
# NOTE(termie): "tests" is not a flag, just some Trial related stuff
|
||||
|
Loading…
x
Reference in New Issue
Block a user