summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTodd Willey <todd@rubidine.com>2010-06-20 20:18:56 -0400
committerTodd Willey <todd@rubidine.com>2010-06-20 20:18:56 -0400
commit79964253e910dd2884de56138bbe6fa75a3bf283 (patch)
tree441b260650fbad85f033aa593d69ae3d274966b3
parentd6c78d600091f476e360371df033be7eda1b749b (diff)
downloadnova-79964253e910dd2884de56138bbe6fa75a3bf283.tar.gz
nova-79964253e910dd2884de56138bbe6fa75a3bf283.tar.xz
nova-79964253e910dd2884de56138bbe6fa75a3bf283.zip
git checkpoint commit post-wsgi
-rwxr-xr-xbin/nova-rsapi41
-rw-r--r--nova/endpoint/rackspace.py253
-rw-r--r--nova/endpoint/wsgi.py23
3 files changed, 88 insertions, 229 deletions
diff --git a/bin/nova-rsapi b/bin/nova-rsapi
index cd2f285cf..24b469f75 100755
--- a/bin/nova-rsapi
+++ b/bin/nova-rsapi
@@ -19,10 +19,9 @@
"""
import logging
+from wsgiref import simple_server
from nova import vendor
-from tornado import httpserver
-import tornado.web
from tornado import ioloop
from nova import flags
@@ -31,34 +30,28 @@ from nova import server
from nova import utils
from nova.auth import users
from nova.endpoint import rackspace
-from nova.endpoint import wsgi_wrapper
-FLAGS = flags.FLAGS
-flags.DEFINE_integer('cc_port', 8773, 'api server port')
-def application(_args):
- """ Wrap WSGI application as tornado """
- wsgi_wrapper.Wrap(rackspace.Api)
+FLAGS = flags.FLAGS
+flags.DEFINE_integer('cc_port', 8773, 'cloud controller port')
def main(_argv):
user_manager = users.UserManager()
- controllers = {
- 'Servers': rackspace.ServersController()
- }
- _app = rackspace.RackspaceAPIServerApplication(user_manager, controllers)
-
+ api_instance = rackspace.Api(user_manager)
conn = rpc.Connection.instance()
- consumer = rpc.AdapterConsumer(connection=conn,
+ rpc_consumer = rpc.AdapterConsumer(connection=conn,
topic=FLAGS.cloud_topic,
- proxy=controllers['Servers'])
-
- io_inst = ioloop.IOLoop.instance()
- _injected = consumer.attach_to_tornado(io_inst)
-
- http_server = httpserver.HTTPServer(_app)
- http_server.listen(FLAGS.cc_port)
- logging.debug('Started HTTP server on %s', FLAGS.cc_port)
- io_inst.start()
-
+ proxy=api_instance)
+
+# TODO: fire rpc response listener (without attach to tornado)
+# io_inst = ioloop.IOLoop.instance()
+# _injected = consumer.attach_to_tornado(io_inst)
+
+ http_server = simple_server.WSGIServer(('0.0.0.0', FLAGS.cc_port), simple_server.WSGIRequestHandler)
+ http_server.set_app(api_instance.handler)
+ logging.debug('Started HTTP server on port %i' % FLAGS.cc_port)
+ while True:
+ http_server.handle_request()
+# io_inst.start()
if __name__ == '__main__':
utils.default_flagfile()
diff --git a/nova/endpoint/rackspace.py b/nova/endpoint/rackspace.py
index c48285657..7438e3bd3 100644
--- a/nova/endpoint/rackspace.py
+++ b/nova/endpoint/rackspace.py
@@ -36,6 +36,7 @@ from nova import exception
from nova.auth import users
from nova.compute import model
from nova.compute import network
+from nova.endpoint import wsgi
from nova.endpoint import images
from nova.volume import storage
@@ -53,216 +54,58 @@ def _gen_key(user_id, key_name):
return {'private_key': private_key, 'fingerprint': fingerprint}
-class ServersController(object):
- """ ServersController provides the critical dispatch between
- inbound API calls through the endpoint and messages
- sent to the other nodes.
-"""
- def __init__(self):
- self.instdir = model.InstanceDirectory()
- self.network = network.PublicNetworkController()
- self.setup()
-
- @property
- def instances(self):
- """ All instances in the system, as dicts """
- for instance in self.instdir.all:
- yield {instance['instance_id']: instance}
-
- @property
- def volumes(self):
- """ returns a list of all volumes """
- for volume_id in datastore.Redis.instance().smembers("volumes"):
- volume = storage.Volume(volume_id=volume_id)
- yield volume
-
- def __str__(self):
- return 'ServersController'
-
- def setup(self):
- """ Ensure the keychains and folders exist. """
- # Create keys folder, if it doesn't exist
- if not os.path.exists(FLAGS.keys_path):
- os.makedirs(os.path.abspath(FLAGS.keys_path))
- # Gen root CA, if we don't have one
- root_ca_path = os.path.join(FLAGS.ca_path, FLAGS.ca_file)
- if not os.path.exists(root_ca_path):
- start = os.getcwd()
- os.chdir(FLAGS.ca_path)
- utils.runthis("Generating root CA: %s", "sh genrootca.sh")
- os.chdir(start)
- # TODO: Do this with M2Crypto instead
-
- def get_instance_by_ip(self, ip):
- return self.instdir.by_ip(ip)
+class Api(object):
- def get_metadata(self, ip):
- i = self.get_instance_by_ip(ip)
- if i is None:
- return None
- if i['key_name']:
- keys = {
- '0': {
- '_name': i['key_name'],
- 'openssh-key': i['key_data']
- }
- }
- else:
- keys = ''
- data = {
- 'user-data': base64.b64decode(i['user_data']),
- 'meta-data': {
- 'ami-id': i['image_id'],
- 'ami-launch-index': i['ami_launch_index'],
- 'ami-manifest-path': 'FIXME', # image property
- 'block-device-mapping': { # TODO: replace with real data
- 'ami': 'sda1',
- 'ephemeral0': 'sda2',
- 'root': '/dev/sda1',
- 'swap': 'sda3'
- },
- 'hostname': i['private_dns_name'], # is this public sometimes?
- 'instance-action': 'none',
- 'instance-id': i['instance_id'],
- 'instance-type': i.get('instance_type', ''),
- 'local-hostname': i['private_dns_name'],
- 'local-ipv4': i['private_dns_name'], # TODO: switch to IP
- 'kernel-id': i.get('kernel_id', ''),
- 'placement': {
- 'availaibility-zone': i.get('availability_zone', 'nova'),
- },
- 'public-hostname': i.get('dns_name', ''),
- 'public-ipv4': i.get('dns_name', ''), # TODO: switch to IP
- 'public-keys' : keys,
- 'ramdisk-id': i.get('ramdisk_id', ''),
- 'reservation-id': i['reservation_id'],
- 'security-groups': i.get('groups', '')
- }
+ def __init__(self, rpc_mechanism):
+ self.controllers = {
+ "v1.0": RackspaceAuthenticationApi(),
+ "server": RackspaceCloudServerApi()
}
- if False: # TODO: store ancestor ids
- data['ancestor-ami-ids'] = []
- if i.get('product_codes', None):
- data['product-codes'] = i['product_codes']
- return data
-
- def update_state(self, topic, value):
- """ accepts status reports from the queue and consolidates them """
- # TODO(jmc): if an instance has disappeared from
- # the node, call instance_death
- if topic == "instances":
- return defer.succeed(True)
- aggregate_state = getattr(self, topic)
- node_name = value.keys()[0]
- items = value[node_name]
- logging.debug("Updating %s state for %s" % (topic, node_name))
- for item_id in items.keys():
- if (aggregate_state.has_key('pending') and
- aggregate_state['pending'].has_key(item_id)):
- del aggregate_state['pending'][item_id]
- aggregate_state[node_name] = items
- return defer.succeed(True)
-
-class RackspaceAPIServerApplication(tornado.web.Application):
- def __init__(self, user_manager, controllers):
- tornado.web.Application.__init__(self, [
- (r'/servers/?(.*)', RackspaceServerRequestHandler),
- ], pool=multiprocessing.Pool(4))
- self.user_manager = user_manager
- self.controllers = controllers
-
-class RackspaceServerRequestHandler(tornado.web.RequestHandler):
-
- def get(self, controller_name):
- self.execute(controller_name)
-
- @tornado.web.asynchronous
- def execute(self, controller_name):
- controller = self.application.controllers['Servers']
-
- # Obtain the appropriate controller for this request.
-# try:
-# controller = self.application.controllers[controller_name]
-# except KeyError:
-# self._error('unhandled', 'no controller named %s' % controller_name)
-# return
-#
- args = self.request.arguments
- logging.error("ARGS: %s" % args)
-
- # Read request signature.
- try:
- signature = args.pop('Signature')[0]
- except:
- raise tornado.web.HTTPError(400)
-
- # Make a copy of args for authentication and signature verification.
- auth_params = {}
- for key, value in args.items():
- auth_params[key] = value[0]
-
- # Get requested action and remove authentication args for final request.
- try:
- action = args.pop('Action')[0]
- access = args.pop('AWSAccessKeyId')[0]
- args.pop('SignatureMethod')
- args.pop('SignatureVersion')
- args.pop('Version')
- args.pop('Timestamp')
- except:
- raise tornado.web.HTTPError(400)
-
- # Authenticate the request.
- try:
- (user, project) = users.UserManager.instance().authenticate(
- access,
- signature,
- auth_params,
- self.request.method,
- self.request.host,
- self.request.path
- )
-
- except exception.Error, ex:
- logging.debug("Authentication Failure: %s" % ex)
- raise tornado.web.HTTPError(403)
-
- _log.debug('action: %s' % action)
-
- for key, value in args.items():
- _log.debug('arg: %s\t\tval: %s' % (key, value))
-
- request = APIRequest(controller, action)
- context = APIRequestContext(self, user, project)
- d = request.send(context, **args)
- # d.addCallback(utils.debug)
+ self.rpc_mechanism = rpc_mechanism
+
+ def handler(self, environ, responder):
+ logging.error("*** %s" % environ)
+ controller, path = wsgi.Util.route(environ['PATH_INFO'], self.controllers)
+ if not controller:
+ raise Exception("Missing Controller")
+ rv = controller.process(path, environ)
+ if type(rv) is tuple:
+ responder(rv[0], rv[1])
+ rv = rv[2]
+ else:
+ responder("200 OK", [])
+ return rv
+
+class RackspaceApiEndpoint(object):
+ def process(self, path, env):
+ if len(path) == 0:
+ return self.index(env)
+
+ action = path.pop(0)
+ if hasattr(self, action):
+ method = getattr(self, action)
+ return method(path, env)
+ else:
+ raise Exception("Missing method %s" % path[0])
- # TODO: Wrap response in AWS XML format
- d.addCallbacks(self._write_callback, self._error_callback)
- def _write_callback(self, data):
- self.set_header('Content-Type', 'text/xml')
- self.write(data)
- self.finish()
+class RackspaceAuthenticationApi(RackspaceApiEndpoint):
- def _error_callback(self, failure):
- try:
- failure.raiseException()
- except exception.ApiError as ex:
- self._error(type(ex).__name__ + "." + ex.code, ex.message)
- # TODO(vish): do something more useful with unknown exceptions
- except Exception as ex:
- self._error(type(ex).__name__, str(ex))
- raise
+ def index(self, env):
+ response = '204 No Content'
+ headers = [
+ ('X-Server-Management-Url', 'http://localhost:8773/server'),
+ ('X-Storage-Url', 'http://localhost:8773/server'),
+ ('X-CDN-Managment-Url', 'http://localhost:8773/server'),
+ ]
+ body = ""
+ return (response, headers, body)
+
- def post(self, controller_name):
- self.execute(controller_name)
+class RackspaceCloudServerApi(object):
- def _error(self, code, message):
- self._status_code = 400
- self.set_header('Content-Type', 'text/xml')
- self.write('<?xml version="1.0"?>\n')
- self.write('<Response><Errors><Error><Code>%s</Code>'
- '<Message>%s</Message></Error></Errors>'
- '<RequestID>?</RequestID></Response>' % (code, message))
- self.finish()
+ def index(self):
+ return "IDX"
+ def list(self, args):
+ return "%s" % args
diff --git a/nova/endpoint/wsgi.py b/nova/endpoint/wsgi.py
new file mode 100644
index 000000000..e0817fd0e
--- /dev/null
+++ b/nova/endpoint/wsgi.py
@@ -0,0 +1,23 @@
+
+'''
+Utility methods for working with WSGI servers
+'''
+
+class Util(object):
+
+ @staticmethod
+ def route(reqstr, controllers):
+ if len(reqstr) == 0:
+ return Util.select_root_controller(controllers), []
+ parts = [x for x in reqstr.split("/") if len(x) > 0]
+ if len(parts) == 0:
+ return Util.select_root_controller(controllers), []
+ return controllers[parts[0]], parts[1:]
+
+ @staticmethod
+ def select_root_controller(controllers):
+ if '' in controllers:
+ return controllers['']
+ else:
+ return None
+